Fix code style

This commit is contained in:
Art4 2025-01-13 13:31:54 +00:00
parent d4697a17a3
commit 0e59dba914
102 changed files with 3038 additions and 2764 deletions

View file

@ -131,10 +131,10 @@ function item_insert(int $uid, array $request, bool $preview, string $return_pat
$post = DI::contentItem()->initializePost($post); $post = DI::contentItem()->initializePost($post);
$post['edit'] = null; $post['edit'] = null;
$post['post-type'] = $request['post_type'] ?? ''; $post['post-type'] = $request['post_type'] ?? '';
$post['wall'] = $request['wall'] ?? true; $post['wall'] = $request['wall'] ?? true;
$post['pubmail'] = $request['pubmail_enable'] ?? false; $post['pubmail'] = $request['pubmail_enable'] ?? false;
$post['created'] = $request['created_at'] ?? DateTimeFormat::utcNow(); $post['created'] = $request['created_at'] ?? DateTimeFormat::utcNow();
$post['edited'] = $post['changed'] = $post['commented'] = $post['created']; $post['edited'] = $post['changed'] = $post['commented'] = $post['created'];
$post['app'] = ''; $post['app'] = '';
$post['inform'] = ''; $post['inform'] = '';
@ -167,15 +167,15 @@ function item_insert(int $uid, array $request, bool $preview, string $return_pat
DI::logger()->info('Public item stored for user', ['uri-id' => $toplevel_item['uri-id'], 'uid' => $post['uid'], 'stored' => $stored]); DI::logger()->info('Public item stored for user', ['uri-id' => $toplevel_item['uri-id'], 'uid' => $post['uid'], 'stored' => $stored]);
} }
$post['parent'] = $toplevel_item['id']; $post['parent'] = $toplevel_item['id'];
$post['gravity'] = Item::GRAVITY_COMMENT; $post['gravity'] = Item::GRAVITY_COMMENT;
$post['thr-parent'] = $parent_item['uri']; $post['thr-parent'] = $parent_item['uri'];
$post['wall'] = $toplevel_item['wall']; $post['wall'] = $toplevel_item['wall'];
} else { } else {
$parent_item = []; $parent_item = [];
$post['parent'] = 0; $post['parent'] = 0;
$post['gravity'] = Item::GRAVITY_PARENT; $post['gravity'] = Item::GRAVITY_PARENT;
$post['thr-parent'] = $post['uri']; $post['thr-parent'] = $post['uri'];
} }
$post = DI::contentItem()->getACL($post, $parent_item, $request); $post = DI::contentItem()->getACL($post, $parent_item, $request);

View file

@ -114,8 +114,8 @@ function photos_post()
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.')); throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
} }
$can_post = false; $can_post = false;
$visitor = 0; $visitor = 0;
$page_owner_uid = intval($user['uid']); $page_owner_uid = intval($user['uid']);
$community_page = in_array($user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN]); $community_page = in_array($user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN]);
@ -124,8 +124,8 @@ function photos_post()
$can_post = true; $can_post = true;
} elseif ($community_page && !empty(DI::userSession()->getRemoteContactID($page_owner_uid))) { } elseif ($community_page && !empty(DI::userSession()->getRemoteContactID($page_owner_uid))) {
$contact_id = DI::userSession()->getRemoteContactID($page_owner_uid); $contact_id = DI::userSession()->getRemoteContactID($page_owner_uid);
$can_post = true; $can_post = true;
$visitor = $contact_id; $visitor = $contact_id;
} }
if (!$can_post) { if (!$can_post) {
@ -141,7 +141,7 @@ function photos_post()
System::exit(); System::exit();
} }
$aclFormatter = DI::aclFormatter(); $aclFormatter = DI::aclFormatter();
$str_contact_allow = isset($_REQUEST['contact_allow']) ? $aclFormatter->toString($_REQUEST['contact_allow']) : $owner_record['allow_cid'] ?? ''; $str_contact_allow = isset($_REQUEST['contact_allow']) ? $aclFormatter->toString($_REQUEST['contact_allow']) : $owner_record['allow_cid'] ?? '';
$str_circle_allow = isset($_REQUEST['circle_allow']) ? $aclFormatter->toString($_REQUEST['circle_allow']) : $owner_record['allow_gid'] ?? ''; $str_circle_allow = isset($_REQUEST['circle_allow']) ? $aclFormatter->toString($_REQUEST['circle_allow']) : $owner_record['allow_gid'] ?? '';
$str_contact_deny = isset($_REQUEST['contact_deny']) ? $aclFormatter->toString($_REQUEST['contact_deny']) : $owner_record['deny_cid'] ?? ''; $str_contact_deny = isset($_REQUEST['contact_deny']) ? $aclFormatter->toString($_REQUEST['contact_deny']) : $owner_record['deny_cid'] ?? '';
@ -151,7 +151,7 @@ function photos_post()
if ($visibility === 'public') { if ($visibility === 'public') {
// The ACL selector introduced in version 2019.12 sends ACL input data even when the Public visibility is selected // The ACL selector introduced in version 2019.12 sends ACL input data even when the Public visibility is selected
$str_contact_allow = $str_circle_allow = $str_contact_deny = $str_circle_deny = ''; $str_contact_allow = $str_circle_allow = $str_contact_deny = $str_circle_deny = '';
} else if ($visibility === 'custom') { } elseif ($visibility === 'custom') {
// Since we know from the visibility parameter the item should be private, we have to prevent the empty ACL // Since we know from the visibility parameter the item should be private, we have to prevent the empty ACL
// case that would make it public. So we always append the author's contact id to the allowed contacts. // case that would make it public. So we always append the author's contact id to the allowed contacts.
// See https://github.com/friendica/friendica/issues/9672 // See https://github.com/friendica/friendica/issues/9672
@ -317,7 +317,7 @@ function photos_post()
if (DBA::isResult($photos)) { if (DBA::isResult($photos)) {
$photo = $photos[0]; $photo = $photos[0];
$ext = Images::getExtensionByMimeType($photo['type']); $ext = Images::getExtensionByMimeType($photo['type']);
Photo::update( Photo::update(
['desc' => $desc, 'album' => $albname, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow, 'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny], ['desc' => $desc, 'album' => $albname, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow, 'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny],
['resource-id' => $resource_id, 'uid' => $page_owner_uid] ['resource-id' => $resource_id, 'uid' => $page_owner_uid]
@ -332,9 +332,9 @@ function photos_post()
if (DBA::isResult($photos) && !$item_id) { if (DBA::isResult($photos) && !$item_id) {
// Create item container // Create item container
$title = ''; $title = '';
$uri = Item::newURI(); $uri = Item::newURI();
$arr = []; $arr = [];
$arr['guid'] = System::createUUID(); $arr['guid'] = System::createUUID();
$arr['uid'] = $page_owner_uid; $arr['uid'] = $page_owner_uid;
$arr['uri'] = $uri; $arr['uri'] = $uri;
@ -356,7 +356,7 @@ function photos_post()
$arr['visible'] = 0; $arr['visible'] = 0;
$arr['origin'] = 1; $arr['origin'] = 1;
$arr['body'] = Images::getBBCodeByResource($photo['resource-id'], $user['nickname'], $photo['scale'], $ext); $arr['body'] = Images::getBBCodeByResource($photo['resource-id'], $user['nickname'], $photo['scale'], $ext);
$item_id = Item::insert($arr); $item_id = Item::insert($arr);
} }
@ -370,7 +370,7 @@ function photos_post()
} }
if (strlen($rawtags)) { if (strlen($rawtags)) {
$inform = ''; $inform = '';
// if the new tag doesn't have a namespace specifier (@foo or #foo) give it a hashtag // if the new tag doesn't have a namespace specifier (@foo or #foo) give it a hashtag
$x = substr($rawtags, 0, 1); $x = substr($rawtags, 0, 1);
@ -379,13 +379,13 @@ function photos_post()
} }
$taginfo = []; $taginfo = [];
$tags = BBCode::getTags($rawtags); $tags = BBCode::getTags($rawtags);
if (count($tags)) { if (count($tags)) {
foreach ($tags as $tag) { foreach ($tags as $tag) {
if (strpos($tag, '@') === 0) { if (strpos($tag, '@') === 0) {
$profile = ''; $profile = '';
$name = substr($tag, 1); $name = substr($tag, 1);
$contact = Contact::getByURL($name); $contact = Contact::getByURL($name);
if (empty($contact)) { if (empty($contact)) {
$newname = $name; $newname = $name;
@ -453,7 +453,7 @@ function photos_post()
} }
$newinform .= $inform; $newinform .= $inform;
$fields = ['inform' => $newinform, 'edited' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()]; $fields = ['inform' => $newinform, 'edited' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()];
$condition = ['id' => $item_id]; $condition = ['id' => $item_id];
Item::update($fields, $condition); Item::update($fields, $condition);
@ -552,7 +552,7 @@ function photos_content()
$datum = null; $datum = null;
if (DI::args()->getArgc() > 3) { if (DI::args()->getArgc() > 3) {
$datatype = DI::args()->getArgv()[2]; $datatype = DI::args()->getArgv()[2];
$datum = DI::args()->getArgv()[3]; $datum = DI::args()->getArgv()[3];
} elseif ((DI::args()->getArgc() > 2) && (DI::args()->getArgv()[2] === 'upload')) { } elseif ((DI::args()->getArgc() > 2) && (DI::args()->getArgv()[2] === 'upload')) {
$datatype = 'upload'; $datatype = 'upload';
} else { } else {
@ -582,12 +582,12 @@ function photos_content()
$can_post = true; $can_post = true;
} elseif ($community_page && !empty(DI::userSession()->getRemoteContactID($owner_uid))) { } elseif ($community_page && !empty(DI::userSession()->getRemoteContactID($owner_uid))) {
$contact_id = DI::userSession()->getRemoteContactID($owner_uid); $contact_id = DI::userSession()->getRemoteContactID($owner_uid);
$contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => $owner_uid, 'blocked' => false, 'pending' => false]); $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => $owner_uid, 'blocked' => false, 'pending' => false]);
if (DBA::isResult($contact)) { if (DBA::isResult($contact)) {
$can_post = true; $can_post = true;
$remote_contact = true; $remote_contact = true;
$visitor = $contact_id; $visitor = $contact_id;
} }
} }
@ -643,14 +643,14 @@ function photos_content()
$uploader = ''; $uploader = '';
$ret = [ $ret = [
'post_url' => 'profile/' . $user['nickname'] . '/photos', 'post_url' => 'profile/' . $user['nickname'] . '/photos',
'addon_text' => $uploader, 'addon_text' => $uploader,
'default_upload' => true 'default_upload' => true
]; ];
Hook::callAll('photo_upload_form', $ret); Hook::callAll('photo_upload_form', $ret);
$default_upload_box = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_box.tpl'), []); $default_upload_box = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_box.tpl'), []);
$default_upload_submit = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_submit.tpl'), [ $default_upload_submit = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_submit.tpl'), [
'$submit' => DI::l10n()->t('Submit'), '$submit' => DI::l10n()->t('Submit'),
]); ]);
@ -675,22 +675,22 @@ function photos_content()
$aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML(DI::page(), DI::userSession()->getLocalUserId())); $aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML(DI::page(), DI::userSession()->getLocalUserId()));
$o .= Renderer::replaceMacros($tpl, [ $o .= Renderer::replaceMacros($tpl, [
'$pagename' => DI::l10n()->t('Upload Photos'), '$pagename' => DI::l10n()->t('Upload Photos'),
'$sessid' => session_id(), '$sessid' => session_id(),
'$usage' => $usage_message, '$usage' => $usage_message,
'$nickname' => $user['nickname'], '$nickname' => $user['nickname'],
'$newalbum' => DI::l10n()->t('New album name: '), '$newalbum' => DI::l10n()->t('New album name: '),
'$existalbumtext' => DI::l10n()->t('or select existing album:'), '$existalbumtext' => DI::l10n()->t('or select existing album:'),
'$nosharetext' => DI::l10n()->t('Do not show a status post for this upload'), '$nosharetext' => DI::l10n()->t('Do not show a status post for this upload'),
'$albumselect' => $albumselect, '$albumselect' => $albumselect,
'$selname' => $selname, '$selname' => $selname,
'$permissions' => DI::l10n()->t('Permissions'), '$permissions' => DI::l10n()->t('Permissions'),
'$aclselect' => $aclselect_e, '$aclselect' => $aclselect_e,
'$lockstate' => ACL::getLockstateForUserId(DI::userSession()->getLocalUserId()) ? 'lock' : 'unlock', '$lockstate' => ACL::getLockstateForUserId(DI::userSession()->getLocalUserId()) ? 'lock' : 'unlock',
'$alt_uploader' => $ret['addon_text'], '$alt_uploader' => $ret['addon_text'],
'$default_upload_box' => ($ret['default_upload'] ? $default_upload_box : ''), '$default_upload_box' => ($ret['default_upload'] ? $default_upload_box : ''),
'$default_upload_submit' => ($ret['default_upload'] ? $default_upload_submit : ''), '$default_upload_submit' => ($ret['default_upload'] ? $default_upload_submit : ''),
'$uploadurl' => $ret['post_url'], '$uploadurl' => $ret['post_url'],
// ACL permissions box // ACL permissions box
'$return_path' => DI::args()->getQueryString(), '$return_path' => DI::args()->getQueryString(),
@ -712,7 +712,7 @@ function photos_content()
} }
$total = 0; $total = 0;
$r = DBA::toArray(DBA::p( $r = DBA::toArray(DBA::p(
"SELECT `resource-id`, MAX(`scale`) AS `scale` FROM `photo` WHERE `uid` = ? AND `album` = ? "SELECT `resource-id`, MAX(`scale`) AS `scale` FROM `photo` WHERE `uid` = ? AND `album` = ?
AND `scale` <= 4 $sql_extra GROUP BY `resource-id`", AND `scale` <= 4 $sql_extra GROUP BY `resource-id`",
$owner_uid, $owner_uid,
@ -748,7 +748,7 @@ function photos_content()
$drop_url = DI::args()->getQueryString(); $drop_url = DI::args()->getQueryString();
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [ return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
'$l10n' => [ '$l10n' => [
'message' => DI::l10n()->t('Do you really want to delete this photo album and all its photos?'), 'message' => DI::l10n()->t('Do you really want to delete this photo album and all its photos?'),
'confirm' => DI::l10n()->t('Delete Album'), 'confirm' => DI::l10n()->t('Delete Album'),
'cancel' => DI::l10n()->t('Cancel'), 'cancel' => DI::l10n()->t('Cancel'),
@ -768,11 +768,11 @@ function photos_content()
$album_e = $album; $album_e = $album;
$o .= Renderer::replaceMacros($edit_tpl, [ $o .= Renderer::replaceMacros($edit_tpl, [
'$nametext' => DI::l10n()->t('New album name: '), '$nametext' => DI::l10n()->t('New album name: '),
'$nickname' => $user['nickname'], '$nickname' => $user['nickname'],
'$album' => $album_e, '$album' => $album_e,
'$hexalbum' => bin2hex($album), '$hexalbum' => bin2hex($album),
'$submit' => DI::l10n()->t('Submit'), '$submit' => DI::l10n()->t('Submit'),
'$dropsubmit' => DI::l10n()->t('Delete Album') '$dropsubmit' => DI::l10n()->t('Delete Album')
]); ]);
} }
@ -782,7 +782,7 @@ function photos_content()
} }
if ($order_field === 'created') { if ($order_field === 'created') {
$order = [DI::l10n()->t('Show Newest First'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album), 'oldest']; $order = [DI::l10n()->t('Show Newest First'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album), 'oldest'];
} else { } else {
$order = [DI::l10n()->t('Show Oldest First'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album) . '?order=created', 'newest']; $order = [DI::l10n()->t('Show Oldest First'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album) . '?order=created', 'newest'];
} }
@ -798,7 +798,7 @@ function photos_content()
$ext = Images::getExtensionByMimeType($rr['type']); $ext = Images::getExtensionByMimeType($rr['type']);
$imgalt_e = $rr['filename']; $imgalt_e = $rr['filename'];
$desc_e = $rr['desc']; $desc_e = $rr['desc'];
$photos[] = [ $photos[] = [
'id' => $rr['id'], 'id' => $rr['id'],
@ -817,13 +817,13 @@ function photos_content()
$tpl = Renderer::getMarkupTemplate('photo_album.tpl'); $tpl = Renderer::getMarkupTemplate('photo_album.tpl');
$o .= Renderer::replaceMacros($tpl, [ $o .= Renderer::replaceMacros($tpl, [
'$photos' => $photos, '$photos' => $photos,
'$album' => $album, '$album' => $album,
'$can_post' => $can_post, '$can_post' => $can_post,
'$upload' => [DI::l10n()->t('Upload New Photos'), 'photos/' . $user['nickname'] . '/upload/' . bin2hex($album)], '$upload' => [DI::l10n()->t('Upload New Photos'), 'photos/' . $user['nickname'] . '/upload/' . bin2hex($album)],
'$order' => $order, '$order' => $order,
'$edit' => $edit, '$edit' => $edit,
'$drop' => $drop, '$drop' => $drop,
'$paginate' => $pager->renderFull($total), '$paginate' => $pager->renderFull($total),
]); ]);
@ -848,7 +848,7 @@ function photos_content()
$drop_url = DI::args()->getQueryString(); $drop_url = DI::args()->getQueryString();
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [ return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
'$l10n' => [ '$l10n' => [
'message' => DI::l10n()->t('Do you really want to delete this photo?'), 'message' => DI::l10n()->t('Do you really want to delete this photo?'),
'confirm' => DI::l10n()->t('Delete Photo'), 'confirm' => DI::l10n()->t('Delete Photo'),
'cancel' => DI::l10n()->t('Cancel'), 'cancel' => DI::l10n()->t('Cancel'),
@ -950,8 +950,8 @@ function photos_content()
if ($cmd === 'edit') { if ($cmd === 'edit') {
$tools['view'] = ['photos/' . $user['nickname'] . '/image/' . $datum, DI::l10n()->t('View photo')]; $tools['view'] = ['photos/' . $user['nickname'] . '/image/' . $datum, DI::l10n()->t('View photo')];
} else { } else {
$tools['edit'] = ['photos/' . $user['nickname'] . '/image/' . $datum . '/edit', DI::l10n()->t('Edit photo')]; $tools['edit'] = ['photos/' . $user['nickname'] . '/image/' . $datum . '/edit', DI::l10n()->t('Edit photo')];
$tools['delete'] = ['photos/' . $user['nickname'] . '/image/' . $datum . '/drop', DI::l10n()->t('Delete photo')]; $tools['delete'] = ['photos/' . $user['nickname'] . '/image/' . $datum . '/drop', DI::l10n()->t('Delete photo')];
$tools['profile'] = ['settings/profile/photo/crop/' . $ph[0]['resource-id'], DI::l10n()->t('Use as profile photo')]; $tools['profile'] = ['settings/profile/photo/crop/' . $ph[0]['resource-id'], DI::l10n()->t('Use as profile photo')];
} }
@ -973,9 +973,9 @@ function photos_content()
'filename' => $hires['filename'], 'filename' => $hires['filename'],
]; ];
$map = null; $map = null;
$link_item = []; $link_item = [];
$total = 0; $total = 0;
// Do we have an item for this photo? // Do we have an item for this photo?
@ -989,12 +989,12 @@ function photos_content()
if (!empty($link_item['parent']) && !empty($link_item['uid'])) { if (!empty($link_item['parent']) && !empty($link_item['uid'])) {
$condition = ["`parent` = ? AND `gravity` = ?", $link_item['parent'], Item::GRAVITY_COMMENT]; $condition = ["`parent` = ? AND `gravity` = ?", $link_item['parent'], Item::GRAVITY_COMMENT];
$total = Post::count($condition); $total = Post::count($condition);
$pager = new Pager(DI::l10n(), DI::args()->getQueryString()); $pager = new Pager(DI::l10n(), DI::args()->getQueryString());
$params = ['order' => ['id'], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]]; $params = ['order' => ['id'], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
$items = Post::toArray(Post::selectForUser($link_item['uid'], array_merge(Item::ITEM_FIELDLIST, ['author-alias']), $condition, $params)); $items = Post::toArray(Post::selectForUser($link_item['uid'], array_merge(Item::ITEM_FIELDLIST, ['author-alias']), $condition, $params));
if (DI::userSession()->getLocalUserId() == $link_item['uid']) { if (DI::userSession()->getLocalUserId() == $link_item['uid']) {
Item::update(['unseen' => false], ['parent' => $link_item['parent']]); Item::update(['unseen' => false], ['parent' => $link_item['parent']]);
@ -1021,51 +1021,51 @@ function photos_content()
$tags = ['title' => DI::l10n()->t('Tags: '), 'tags' => $tag_arr]; $tags = ['title' => DI::l10n()->t('Tags: '), 'tags' => $tag_arr];
if ($cmd === 'edit') { if ($cmd === 'edit') {
$tags['removeanyurl'] = 'post/' . $link_item['id'] . '/tag/remove?return=' . urlencode(DI::args()->getCommand()); $tags['removeanyurl'] = 'post/' . $link_item['id'] . '/tag/remove?return=' . urlencode(DI::args()->getCommand());
$tags['removetitle'] = DI::l10n()->t('[Select tags to remove]'); $tags['removetitle'] = DI::l10n()->t('[Select tags to remove]');
} }
} }
$edit = Null; $edit = null;
if ($cmd === 'edit' && $can_post) { if ($cmd === 'edit' && $can_post) {
$edit_tpl = Renderer::getMarkupTemplate('photo_edit.tpl'); $edit_tpl = Renderer::getMarkupTemplate('photo_edit.tpl');
$album_e = $ph[0]['album']; $album_e = $ph[0]['album'];
$caption_e = $ph[0]['desc']; $caption_e = $ph[0]['desc'];
$aclselect_e = ACL::getFullSelectorHTML(DI::page(), DI::userSession()->getLocalUserId(), false, ACL::getDefaultUserPermissions($ph[0])); $aclselect_e = ACL::getFullSelectorHTML(DI::page(), DI::userSession()->getLocalUserId(), false, ACL::getDefaultUserPermissions($ph[0]));
$edit = Renderer::replaceMacros($edit_tpl, [ $edit = Renderer::replaceMacros($edit_tpl, [
'$id' => $ph[0]['id'], '$id' => $ph[0]['id'],
'$album' => ['albname', DI::l10n()->t('New album name'), $album_e, ''], '$album' => ['albname', DI::l10n()->t('New album name'), $album_e, ''],
'$caption' => ['desc', DI::l10n()->t('Caption'), $caption_e, ''], '$caption' => ['desc', DI::l10n()->t('Caption'), $caption_e, ''],
'$tags' => ['newtag', DI::l10n()->t('Add a Tag'), "", DI::l10n()->t('Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping')], '$tags' => ['newtag', DI::l10n()->t('Add a Tag'), "", DI::l10n()->t('Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping')],
'$rotate_none' => ['rotate', DI::l10n()->t('Do not rotate'), 0, '', true], '$rotate_none' => ['rotate', DI::l10n()->t('Do not rotate'), 0, '', true],
'$rotate_cw' => ['rotate', DI::l10n()->t("Rotate CW \x28right\x29"), 1, ''], '$rotate_cw' => ['rotate', DI::l10n()->t("Rotate CW \x28right\x29"), 1, ''],
'$rotate_ccw' => ['rotate', DI::l10n()->t("Rotate CCW \x28left\x29"), 2, ''], '$rotate_ccw' => ['rotate', DI::l10n()->t("Rotate CCW \x28left\x29"), 2, ''],
'$nickname' => $user['nickname'], '$nickname' => $user['nickname'],
'$resource_id' => $ph[0]['resource-id'], '$resource_id' => $ph[0]['resource-id'],
'$permissions' => DI::l10n()->t('Permissions'), '$permissions' => DI::l10n()->t('Permissions'),
'$aclselect' => $aclselect_e, '$aclselect' => $aclselect_e,
'$item_id' => $link_item['id'] ?? 0, '$item_id' => $link_item['id'] ?? 0,
'$submit' => DI::l10n()->t('Submit'), '$submit' => DI::l10n()->t('Submit'),
'$delete' => DI::l10n()->t('Delete Photo'), '$delete' => DI::l10n()->t('Delete Photo'),
// ACL permissions box // ACL permissions box
'$return_path' => DI::args()->getQueryString(), '$return_path' => DI::args()->getQueryString(),
]); ]);
} }
$like = ''; $like = '';
$dislike = ''; $dislike = '';
$likebuttons = ''; $likebuttons = '';
$comments = ''; $comments = '';
$paginate = ''; $paginate = '';
if (!empty($link_item['id']) && !empty($link_item['uri'])) { if (!empty($link_item['id']) && !empty($link_item['uri'])) {
$cmnt_tpl = Renderer::getMarkupTemplate('comment_item.tpl'); $cmnt_tpl = Renderer::getMarkupTemplate('comment_item.tpl');
$tpl = Renderer::getMarkupTemplate('photo_item.tpl'); $tpl = Renderer::getMarkupTemplate('photo_item.tpl');
$return_path = DI::args()->getCommand(); $return_path = DI::args()->getCommand();
if (!DBA::isResult($items)) { if (!DBA::isResult($items)) {
@ -1076,25 +1076,25 @@ function photos_content()
*/ */
$qcomment = null; $qcomment = null;
if (Addon::isEnabled('qcomment')) { if (Addon::isEnabled('qcomment')) {
$words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words'); $words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words');
$qcomment = $words ? explode("\n", $words) : []; $qcomment = $words ? explode("\n", $words) : [];
} }
$comments .= Renderer::replaceMacros($cmnt_tpl, [ $comments .= Renderer::replaceMacros($cmnt_tpl, [
'$return_path' => '', '$return_path' => '',
'$jsreload' => $return_path, '$jsreload' => $return_path,
'$id' => $link_item['id'], '$id' => $link_item['id'],
'$parent' => $link_item['id'], '$parent' => $link_item['id'],
'$profile_uid' => $owner_uid, '$profile_uid' => $owner_uid,
'$mylink' => $contact['url'], '$mylink' => $contact['url'],
'$mytitle' => DI::l10n()->t('This is you'), '$mytitle' => DI::l10n()->t('This is you'),
'$myphoto' => $contact['thumb'], '$myphoto' => $contact['thumb'],
'$comment' => DI::l10n()->t('Comment'), '$comment' => DI::l10n()->t('Comment'),
'$submit' => DI::l10n()->t('Submit'), '$submit' => DI::l10n()->t('Submit'),
'$preview' => DI::l10n()->t('Preview'), '$preview' => DI::l10n()->t('Preview'),
'$loading' => DI::l10n()->t('Loading...'), '$loading' => DI::l10n()->t('Loading...'),
'$qcomment' => $qcomment, '$qcomment' => $qcomment,
'$rand_num' => Crypto::randomDigits(12), '$rand_num' => Crypto::randomDigits(12),
]); ]);
} }
} }
@ -1132,29 +1132,29 @@ function photos_content()
*/ */
$qcomment = null; $qcomment = null;
if (Addon::isEnabled('qcomment')) { if (Addon::isEnabled('qcomment')) {
$words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words'); $words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words');
$qcomment = $words ? explode("\n", $words) : []; $qcomment = $words ? explode("\n", $words) : [];
} }
$comments .= Renderer::replaceMacros($cmnt_tpl, [ $comments .= Renderer::replaceMacros($cmnt_tpl, [
'$return_path' => '', '$return_path' => '',
'$jsreload' => $return_path, '$jsreload' => $return_path,
'$id' => $link_item['id'], '$id' => $link_item['id'],
'$parent' => $link_item['id'], '$parent' => $link_item['id'],
'$profile_uid' => $owner_uid, '$profile_uid' => $owner_uid,
'$mylink' => $contact['url'], '$mylink' => $contact['url'],
'$mytitle' => DI::l10n()->t('This is you'), '$mytitle' => DI::l10n()->t('This is you'),
'$myphoto' => $contact['thumb'], '$myphoto' => $contact['thumb'],
'$comment' => DI::l10n()->t('Comment'), '$comment' => DI::l10n()->t('Comment'),
'$submit' => DI::l10n()->t('Submit'), '$submit' => DI::l10n()->t('Submit'),
'$preview' => DI::l10n()->t('Preview'), '$preview' => DI::l10n()->t('Preview'),
'$qcomment' => $qcomment, '$qcomment' => $qcomment,
'$rand_num' => Crypto::randomDigits(12), '$rand_num' => Crypto::randomDigits(12),
]); ]);
} }
foreach ($items as $item) { foreach ($items as $item) {
$comment = ''; $comment = '';
$template = $tpl; $template = $tpl;
$activity = DI::activity(); $activity = DI::activity();
@ -1181,7 +1181,7 @@ function photos_content()
} }
$dropping = (($item['contact-id'] == $contact_id) || ($item['uid'] == DI::userSession()->getLocalUserId())); $dropping = (($item['contact-id'] == $contact_id) || ($item['uid'] == DI::userSession()->getLocalUserId()));
$drop = [ $drop = [
'dropping' => $dropping, 'dropping' => $dropping,
'pagedrop' => false, 'pagedrop' => false,
'select' => DI::l10n()->t('Select'), 'select' => DI::l10n()->t('Select'),
@ -1189,7 +1189,7 @@ function photos_content()
]; ];
$title_e = $item['title']; $title_e = $item['title'];
$body_e = BBCode::convertForUriId($item['uri-id'], $item['body']); $body_e = BBCode::convertForUriId($item['uri-id'], $item['body']);
$comments .= Renderer::replaceMacros($template, [ $comments .= Renderer::replaceMacros($template, [
'$id' => $item['id'], '$id' => $item['id'],
@ -1212,24 +1212,24 @@ function photos_content()
*/ */
$qcomment = null; $qcomment = null;
if (Addon::isEnabled('qcomment')) { if (Addon::isEnabled('qcomment')) {
$words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words'); $words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words');
$qcomment = $words ? explode("\n", $words) : []; $qcomment = $words ? explode("\n", $words) : [];
} }
$comments .= Renderer::replaceMacros($cmnt_tpl, [ $comments .= Renderer::replaceMacros($cmnt_tpl, [
'$return_path' => '', '$return_path' => '',
'$jsreload' => $return_path, '$jsreload' => $return_path,
'$id' => $item['id'], '$id' => $item['id'],
'$parent' => $item['parent'], '$parent' => $item['parent'],
'$profile_uid' => $owner_uid, '$profile_uid' => $owner_uid,
'$mylink' => $contact['url'], '$mylink' => $contact['url'],
'$mytitle' => DI::l10n()->t('This is you'), '$mytitle' => DI::l10n()->t('This is you'),
'$myphoto' => $contact['thumb'], '$myphoto' => $contact['thumb'],
'$comment' => DI::l10n()->t('Comment'), '$comment' => DI::l10n()->t('Comment'),
'$submit' => DI::l10n()->t('Submit'), '$submit' => DI::l10n()->t('Submit'),
'$preview' => DI::l10n()->t('Preview'), '$preview' => DI::l10n()->t('Preview'),
'$qcomment' => $qcomment, '$qcomment' => $qcomment,
'$rand_num' => Crypto::randomDigits(12), '$rand_num' => Crypto::randomDigits(12),
]); ]);
} }
} }
@ -1243,17 +1243,17 @@ function photos_content()
} }
if ($cmd === 'view' && ($can_post || Security::canWriteToUserWall($owner_uid))) { if ($cmd === 'view' && ($can_post || Security::canWriteToUserWall($owner_uid))) {
$like_tpl = Renderer::getMarkupTemplate('like_noshare.tpl'); $like_tpl = Renderer::getMarkupTemplate('like_noshare.tpl');
$likebuttons = Renderer::replaceMacros($like_tpl, [ $likebuttons = Renderer::replaceMacros($like_tpl, [
'$id' => $link_item['id'], '$id' => $link_item['id'],
'$like' => DI::l10n()->t('Like'), '$like' => DI::l10n()->t('Like'),
'$like_title' => DI::l10n()->t('I like this (toggle)'), '$like_title' => DI::l10n()->t('I like this (toggle)'),
'$dislike' => DI::l10n()->t('Dislike'), '$dislike' => DI::l10n()->t('Dislike'),
'$wait' => DI::l10n()->t('Please wait'), '$wait' => DI::l10n()->t('Please wait'),
'$dislike_title' => DI::l10n()->t('I don\'t like this (toggle)'), '$dislike_title' => DI::l10n()->t('I don\'t like this (toggle)'),
'$hide_dislike' => DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'hide_dislike'), '$hide_dislike' => DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'hide_dislike'),
'$responses' => $responses, '$responses' => $responses,
'$return_path' => DI::args()->getQueryString(), '$return_path' => DI::args()->getQueryString(),
]); ]);
} }
@ -1262,22 +1262,22 @@ function photos_content()
$photo_tpl = Renderer::getMarkupTemplate('photo_view.tpl'); $photo_tpl = Renderer::getMarkupTemplate('photo_view.tpl');
$o .= Renderer::replaceMacros($photo_tpl, [ $o .= Renderer::replaceMacros($photo_tpl, [
'$id' => $ph[0]['id'], '$id' => $ph[0]['id'],
'$album' => [$album_link, $ph[0]['album']], '$album' => [$album_link, $ph[0]['album']],
'$tools' => $tools, '$tools' => $tools,
'$photo' => $photo, '$photo' => $photo,
'$prevlink' => $prevlink, '$prevlink' => $prevlink,
'$nextlink' => $nextlink, '$nextlink' => $nextlink,
'$desc' => $ph[0]['desc'], '$desc' => $ph[0]['desc'],
'$tags' => $tags, '$tags' => $tags,
'$edit' => $edit, '$edit' => $edit,
'$map' => $map, '$map' => $map,
'$map_text' => DI::l10n()->t('Map'), '$map_text' => DI::l10n()->t('Map'),
'$likebuttons' => $likebuttons, '$likebuttons' => $likebuttons,
'$like' => $like, '$like' => $like,
'$dislike' => $dislike, '$dislike' => $dislike,
'$comments' => $comments, '$comments' => $comments,
'$paginate' => $paginate, '$paginate' => $paginate,
]); ]);
DI::page()['htmlhead'] .= "\n" . '<meta name="twitter:card" content="summary_large_image" />' . "\n"; DI::page()['htmlhead'] .= "\n" . '<meta name="twitter:card" content="summary_large_image" />' . "\n";

View file

@ -81,7 +81,7 @@ class Page implements ArrayAccess
public function __construct(string $basepath) public function __construct(string $basepath)
{ {
$this->timestamp = microtime(true); $this->timestamp = microtime(true);
$this->basePath = $basepath; $this->basePath = $basepath;
} }
public function setLogging(string $method, string $module, string $command) public function setLogging(string $method, string $module, string $command)
@ -445,7 +445,7 @@ class Page implements ArrayAccess
$this->initContent($response, $mode); $this->initContent($response, $mode);
// Load current theme info after module has been initialized as theme could have been set in module // Load current theme info after module has been initialized as theme could have been set in module
$currentTheme = $appHelper->getCurrentTheme(); $currentTheme = $appHelper->getCurrentTheme();
$theme_info_file = 'view/theme/' . $currentTheme . '/theme.php'; $theme_info_file = 'view/theme/' . $currentTheme . '/theme.php';
if (file_exists($theme_info_file)) { if (file_exists($theme_info_file)) {
require_once $theme_info_file; require_once $theme_info_file;
@ -479,7 +479,7 @@ class Page implements ArrayAccess
// Add the navigation (menu) template // Add the navigation (menu) template
if ($moduleName != 'install' && $moduleName != 'maintenance') { if ($moduleName != 'install' && $moduleName != 'maintenance') {
$this->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('nav_head.tpl'), []); $this->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('nav_head.tpl'), []);
$this->page['nav'] = $nav->getHtml(); $this->page['nav'] = $nav->getHtml();
} }
// Build the page - now that we have all the components // Build the page - now that we have all the components
@ -512,7 +512,7 @@ class Page implements ArrayAccess
} }
} }
$page = $this->page; $page = $this->page;
// add and escape some common but crucial content for direct "echo" in HTML (security) // add and escape some common but crucial content for direct "echo" in HTML (security)
$page['title'] = htmlspecialchars($page['title'] ?? ''); $page['title'] = htmlspecialchars($page['title'] ?? '');

View file

@ -215,7 +215,7 @@ class Avatar
} }
$avatarpath = parse_url(self::baseUrl(), PHP_URL_PATH); $avatarpath = parse_url(self::baseUrl(), PHP_URL_PATH);
$pos = strpos($parts['path'], $avatarpath); $pos = strpos($parts['path'], $avatarpath);
if ($pos !== 0) { if ($pos !== 0) {
return ''; return '';
} }

View file

@ -116,8 +116,8 @@ class Item
public function determineCategoriesTerms(array $item, int $uid = 0): array public function determineCategoriesTerms(array $item, int $uid = 0): array
{ {
$categories = []; $categories = [];
$folders = []; $folders = [];
$first = true; $first = true;
$uid = $item['uid'] ?: $uid; $uid = $item['uid'] ?: $uid;
@ -132,11 +132,11 @@ class Item
$url = '#'; $url = '#';
} }
$categories[] = [ $categories[] = [
'name' => $savedFolderName, 'name' => $savedFolderName,
'url' => $url, 'url' => $url,
'removeurl' => $this->userSession->getLocalUserId() == $uid ? 'filerm/' . $item['id'] . '?cat=' . rawurlencode($savedFolderName) : '', 'removeurl' => $this->userSession->getLocalUserId() == $uid ? 'filerm/' . $item['id'] . '?cat=' . rawurlencode($savedFolderName) : '',
'first' => $first, 'first' => $first,
'last' => false 'last' => false
]; ];
$first = false; $first = false;
} }
@ -148,11 +148,11 @@ class Item
if ($this->userSession->getLocalUserId() == $uid) { if ($this->userSession->getLocalUserId() == $uid) {
foreach (Post\Category::getArrayByURIId($item['uri-id'], $uid, Post\Category::FILE) as $savedFolderName) { foreach (Post\Category::getArrayByURIId($item['uri-id'], $uid, Post\Category::FILE) as $savedFolderName) {
$folders[] = [ $folders[] = [
'name' => $savedFolderName, 'name' => $savedFolderName,
'url' => "#", 'url' => "#",
'removeurl' => $this->userSession->getLocalUserId() == $uid ? 'filerm/' . $item['id'] . '?term=' . rawurlencode($savedFolderName) : '', 'removeurl' => $this->userSession->getLocalUserId() == $uid ? 'filerm/' . $item['id'] . '?term=' . rawurlencode($savedFolderName) : '',
'first' => $first, 'first' => $first,
'last' => false 'last' => false
]; ];
$first = false; $first = false;
} }
@ -197,55 +197,55 @@ class Item
// Sometimes the tag detection doesn't seem to work right // Sometimes the tag detection doesn't seem to work right
// This is some workaround // This is some workaround
$nameparts = explode(' ', $name); $nameparts = explode(' ', $name);
$name = $nameparts[0]; $name = $nameparts[0];
// Try to detect the contact in various ways // Try to detect the contact in various ways
if (strpos($name, 'http://') || strpos($name, '@')) { if (strpos($name, 'http://') || strpos($name, '@')) {
$contact = Contact::getByURLForUser($name, $profile_uid); $contact = Contact::getByURLForUser($name, $profile_uid);
} else { } else {
$contact = false; $contact = false;
$fields = ['id', 'url', 'nick', 'name', 'alias', 'network', 'forum', 'prv']; $fields = ['id', 'url', 'nick', 'name', 'alias', 'network', 'forum', 'prv'];
if (strrpos($name, '+')) { if (strrpos($name, '+')) {
// Is it in format @nick+number? // Is it in format @nick+number?
$tagcid = intval(substr($name, strrpos($name, '+') + 1)); $tagcid = intval(substr($name, strrpos($name, '+') + 1));
$contact = DBA::selectFirst('contact', $fields, ['id' => $tagcid, 'uid' => $profile_uid]); $contact = DBA::selectFirst('contact', $fields, ['id' => $tagcid, 'uid' => $profile_uid]);
} }
// select someone by nick in the current network // select someone by nick in the current network
if (!DBA::isResult($contact) && ($network != '')) { if (!DBA::isResult($contact) && ($network != '')) {
$condition = ['nick' => $name, 'network' => $network, 'uid' => $profile_uid]; $condition = ['nick' => $name, 'network' => $network, 'uid' => $profile_uid];
$contact = DBA::selectFirst('contact', $fields, $condition); $contact = DBA::selectFirst('contact', $fields, $condition);
} }
// select someone by attag in the current network // select someone by attag in the current network
if (!DBA::isResult($contact) && ($network != '')) { if (!DBA::isResult($contact) && ($network != '')) {
$condition = ['attag' => $name, 'network' => $network, 'uid' => $profile_uid]; $condition = ['attag' => $name, 'network' => $network, 'uid' => $profile_uid];
$contact = DBA::selectFirst('contact', $fields, $condition); $contact = DBA::selectFirst('contact', $fields, $condition);
} }
//select someone by name in the current network //select someone by name in the current network
if (!DBA::isResult($contact) && ($network != '')) { if (!DBA::isResult($contact) && ($network != '')) {
$condition = ['name' => $name, 'network' => $network, 'uid' => $profile_uid]; $condition = ['name' => $name, 'network' => $network, 'uid' => $profile_uid];
$contact = DBA::selectFirst('contact', $fields, $condition); $contact = DBA::selectFirst('contact', $fields, $condition);
} }
// select someone by nick in any network // select someone by nick in any network
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
$condition = ['nick' => $name, 'uid' => $profile_uid]; $condition = ['nick' => $name, 'uid' => $profile_uid];
$contact = DBA::selectFirst('contact', $fields, $condition); $contact = DBA::selectFirst('contact', $fields, $condition);
} }
// select someone by attag in any network // select someone by attag in any network
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
$condition = ['attag' => $name, 'uid' => $profile_uid]; $condition = ['attag' => $name, 'uid' => $profile_uid];
$contact = DBA::selectFirst('contact', $fields, $condition); $contact = DBA::selectFirst('contact', $fields, $condition);
} }
// select someone by name in any network // select someone by name in any network
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
$condition = ['name' => $name, 'uid' => $profile_uid]; $condition = ['name' => $name, 'uid' => $profile_uid];
$contact = DBA::selectFirst('contact', $fields, $condition); $contact = DBA::selectFirst('contact', $fields, $condition);
} }
} }
@ -262,8 +262,8 @@ class Item
$replaced = true; $replaced = true;
// create profile link // create profile link
$profile = str_replace(',', '%2c', $profile); $profile = str_replace(',', '%2c', $profile);
$newtag = $tag_type . '[url=' . $profile . ']' . $newname . '[/url]'; $newtag = $tag_type . '[url=' . $profile . ']' . $newname . '[/url]';
$body = str_replace($tag_type . $name, $newtag, $body); $body = str_replace($tag_type . $name, $newtag, $body);
} }
} }
@ -303,7 +303,7 @@ class Item
'url' => $item['author-link'], 'url' => $item['author-link'],
'alias' => $item['author-alias'], 'alias' => $item['author-alias'],
]; ];
$author = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $item['author-name'] . '[/url]'; $author = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $item['author-name'] . '[/url]';
$author_arr = [ $author_arr = [
'uid' => 0, 'uid' => 0,
@ -312,7 +312,7 @@ class Item
'url' => $obj['author-link'], 'url' => $obj['author-link'],
'alias' => $obj['author-alias'], 'alias' => $obj['author-alias'],
]; ];
$objauthor = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $obj['author-name'] . '[/url]'; $objauthor = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $obj['author-name'] . '[/url]';
switch ($obj['verb']) { switch ($obj['verb']) {
case Activity::POST: case Activity::POST:
@ -340,7 +340,7 @@ class Item
$parsedobj = XML::parseString($xmlhead . $item['object']); $parsedobj = XML::parseString($xmlhead . $item['object']);
$tag = sprintf('#[url=%s]%s[/url]', $parsedobj->id, $parsedobj->content); $tag = sprintf('#[url=%s]%s[/url]', $parsedobj->id, $parsedobj->content);
$item['body'] = $this->l10n->t('%1$s tagged %2$s\'s %3$s with %4$s', $author, $objauthor, $plink, $tag); $item['body'] = $this->l10n->t('%1$s tagged %2$s\'s %3$s with %4$s', $author, $objauthor, $plink, $tag);
} }
} }
@ -358,7 +358,7 @@ class Item
public function photoMenu(array $item, string $formSecurityToken): string public function photoMenu(array $item, string $formSecurityToken): string
{ {
$this->profiler->startRecording('rendering'); $this->profiler->startRecording('rendering');
$sub_link = $contact_url = $pm_url = $status_link = ''; $sub_link = $contact_url = $pm_url = $status_link = '';
$photos_link = $posts_link = $block_link = $ignore_link = $collapse_link = $ignoreserver_link = ''; $photos_link = $posts_link = $block_link = $ignore_link = $collapse_link = $ignoreserver_link = '';
if ($this->userSession->getLocalUserId() && $this->userSession->getLocalUserId() == $item['uid'] && $item['gravity'] == ItemModel::GRAVITY_PARENT && !$item['self'] && !$item['mention']) { if ($this->userSession->getLocalUserId() && $this->userSession->getLocalUserId() == $item['uid'] && $item['gravity'] == ItemModel::GRAVITY_PARENT && !$item['self'] && !$item['mention']) {
@ -379,16 +379,16 @@ class Item
$profile_link = $profile_link . '?' . http_build_query(['url' => $item['author-link'] . '/profile']); $profile_link = $profile_link . '?' . http_build_query(['url' => $item['author-link'] . '/profile']);
} }
$cid = 0; $cid = 0;
$pcid = $item['author-id']; $pcid = $item['author-id'];
$network = ''; $network = '';
$rel = 0; $rel = 0;
$condition = ['uid' => $this->userSession->getLocalUserId(), 'uri-id' => $item['author-uri-id']]; $condition = ['uid' => $this->userSession->getLocalUserId(), 'uri-id' => $item['author-uri-id']];
$contact = DBA::selectFirst('contact', ['id', 'network', 'rel'], $condition); $contact = DBA::selectFirst('contact', ['id', 'network', 'rel'], $condition);
if (DBA::isResult($contact)) { if (DBA::isResult($contact)) {
$cid = $contact['id']; $cid = $contact['id'];
$network = $contact['network']; $network = $contact['network'];
$rel = $contact['rel']; $rel = $contact['rel'];
} }
if (!empty($pcid)) { if (!empty($pcid)) {
@ -415,16 +415,16 @@ class Item
if ($this->userSession->getLocalUserId()) { if ($this->userSession->getLocalUserId()) {
$menu = [ $menu = [
$this->l10n->t('Follow Thread') => $sub_link, $this->l10n->t('Follow Thread') => $sub_link,
$this->l10n->t('View Status') => $status_link, $this->l10n->t('View Status') => $status_link,
$this->l10n->t('View Profile') => $profile_link, $this->l10n->t('View Profile') => $profile_link,
$this->l10n->t('View Photos') => $photos_link, $this->l10n->t('View Photos') => $photos_link,
$this->l10n->t('Network Posts') => $posts_link, $this->l10n->t('Network Posts') => $posts_link,
$this->l10n->t('View Contact') => $contact_url, $this->l10n->t('View Contact') => $contact_url,
$this->l10n->t('Send PM') => $pm_url, $this->l10n->t('Send PM') => $pm_url,
$this->l10n->t('Block') => $block_link, $this->l10n->t('Block') => $block_link,
$this->l10n->t('Ignore') => $ignore_link, $this->l10n->t('Ignore') => $ignore_link,
$this->l10n->t('Collapse') => $collapse_link, $this->l10n->t('Collapse') => $collapse_link,
$this->l10n->t("Ignore %s server", $authorBaseUri->getHost()) => $ignoreserver_link, $this->l10n->t("Ignore %s server", $authorBaseUri->getHost()) => $ignoreserver_link,
]; ];
@ -517,13 +517,13 @@ class Item
if (!empty($contact['prv']) || ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION])) { if (!empty($contact['prv']) || ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION])) {
$private_group = $contact['prv']; $private_group = $contact['prv'];
$only_to_group = ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]); $only_to_group = ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]);
$private_id = $contact['id']; $private_id = $contact['id'];
$group_contact = $contact; $group_contact = $contact;
DI::logger()->info('Private group or exclusive mention', ['url' => $tag[2], 'mention' => $tag[1]]); DI::logger()->info('Private group or exclusive mention', ['url' => $tag[2], 'mention' => $tag[1]]);
} elseif ($item['allow_cid'] == '<' . $contact['id'] . '>') { } elseif ($item['allow_cid'] == '<' . $contact['id'] . '>') {
$private_group = false; $private_group = false;
$only_to_group = true; $only_to_group = true;
$private_id = $contact['id']; $private_id = $contact['id'];
$group_contact = $contact; $group_contact = $contact;
DI::logger()->info('Public group', ['url' => $tag[2], 'mention' => $tag[1]]); DI::logger()->info('Public group', ['url' => $tag[2], 'mention' => $tag[1]]);
} else { } else {
@ -561,7 +561,7 @@ class Item
} elseif ($setPermissions) { } elseif ($setPermissions) {
if (empty($receivers)) { if (empty($receivers)) {
// For security reasons direct posts without any receiver will be posts to yourself // For security reasons direct posts without any receiver will be posts to yourself
$self = Contact::selectFirst(['id'], ['uid' => $item['uid'], 'self' => true]); $self = Contact::selectFirst(['id'], ['uid' => $item['uid'], 'self' => true]);
$receivers[] = $self['id']; $receivers[] = $self['id'];
} }
@ -605,9 +605,9 @@ class Item
$owner_updated = ''; $owner_updated = '';
$owner_thumb = $item['contact-avatar']; $owner_thumb = $item['contact-avatar'];
} else { } else {
$owner_avatar = $item['owner-id']; $owner_avatar = $item['owner-id'];
$owner_updated = $item['owner-updated']; $owner_updated = $item['owner-updated'];
$owner_thumb = $item['owner-avatar']; $owner_thumb = $item['owner-avatar'];
} }
if (empty($owner_thumb) || Photo::isPhotoURI($owner_thumb)) { if (empty($owner_thumb) || Photo::isPhotoURI($owner_thumb)) {
@ -634,7 +634,7 @@ class Item
return $body; return $body;
} }
$fields = ['uri-id', 'uri', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink', 'network', 'quote-uri-id']; $fields = ['uri-id', 'uri', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink', 'network', 'quote-uri-id'];
$shared_item = Post::selectFirst($fields, ['uri-id' => $item['quote-uri-id'], 'uid' => [$item['uid'], 0], 'private' => [ItemModel::PUBLIC, ItemModel::UNLISTED]]); $shared_item = Post::selectFirst($fields, ['uri-id' => $item['quote-uri-id'], 'uid' => [$item['uid'], 0], 'private' => [ItemModel::PUBLIC, ItemModel::UNLISTED]]);
if (!DBA::isResult($shared_item)) { if (!DBA::isResult($shared_item)) {
DI::logger()->notice('Post does not exist.', ['uri-id' => $item['quote-uri-id'], 'uid' => $item['uid']]); DI::logger()->notice('Post does not exist.', ['uri-id' => $item['quote-uri-id'], 'uid' => $item['uid']]);
@ -649,7 +649,7 @@ class Item
*/ */
private function createSharedPostByGuid(string $guid, bool $add_media): string private function createSharedPostByGuid(string $guid, bool $add_media): string
{ {
$fields = ['uri-id', 'uri', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink', 'network']; $fields = ['uri-id', 'uri', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink', 'network'];
$shared_item = Post::selectFirst($fields, ['guid' => $guid, 'uid' => 0, 'private' => [ItemModel::PUBLIC, ItemModel::UNLISTED]]); $shared_item = Post::selectFirst($fields, ['guid' => $guid, 'uid' => 0, 'private' => [ItemModel::PUBLIC, ItemModel::UNLISTED]]);
if (!DBA::isResult($shared_item)) { if (!DBA::isResult($shared_item)) {
@ -800,14 +800,14 @@ class Item
public function storeAttachmentFromRequest(array $request): string public function storeAttachmentFromRequest(array $request): string
{ {
$attachment_type = $request['attachment_type'] ?? ''; $attachment_type = $request['attachment_type'] ?? '';
$attachment_title = $request['attachment_title'] ?? ''; $attachment_title = $request['attachment_title'] ?? '';
$attachment_text = $request['attachment_text'] ?? ''; $attachment_text = $request['attachment_text'] ?? '';
$attachment_url = hex2bin($request['attachment_url'] ?? ''); $attachment_url = hex2bin($request['attachment_url'] ?? '');
$attachment_img_src = hex2bin($request['attachment_img_src'] ?? ''); $attachment_img_src = hex2bin($request['attachment_img_src'] ?? '');
$attachment_img_width = $request['attachment_img_width'] ?? 0; $attachment_img_width = $request['attachment_img_width'] ?? 0;
$attachment_img_height = $request['attachment_img_height'] ?? 0; $attachment_img_height = $request['attachment_img_height'] ?? 0;
// Fetch the basic attachment data // Fetch the basic attachment data
@ -815,10 +815,10 @@ class Item
unset($attachment['keywords']); unset($attachment['keywords']);
// Overwrite the basic data with possible changes from the frontend // Overwrite the basic data with possible changes from the frontend
$attachment['type'] = $attachment_type; $attachment['type'] = $attachment_type;
$attachment['title'] = $attachment_title; $attachment['title'] = $attachment_title;
$attachment['text'] = $attachment_text; $attachment['text'] = $attachment_text;
$attachment['url'] = $attachment_url; $attachment['url'] = $attachment_url;
if (!empty($attachment_img_src)) { if (!empty($attachment_img_src)) {
$attachment['images'] = [ $attachment['images'] = [
@ -842,7 +842,7 @@ class Item
$filedas = FileTag::fileToArray($post['file']); $filedas = FileTag::fileToArray($post['file']);
} }
$list_array = explode(',', trim($category)); $list_array = explode(',', trim($category));
$post['file'] = FileTag::arrayToFile($list_array, 'category'); $post['file'] = FileTag::arrayToFile($list_array, 'category');
if (!empty($filedas) && is_array($filedas)) { if (!empty($filedas) && is_array($filedas)) {
@ -858,8 +858,8 @@ class Item
if ($toplevel_item) { if ($toplevel_item) {
$post['allow_cid'] = $toplevel_item['allow_cid'] ?? ''; $post['allow_cid'] = $toplevel_item['allow_cid'] ?? '';
$post['allow_gid'] = $toplevel_item['allow_gid'] ?? ''; $post['allow_gid'] = $toplevel_item['allow_gid'] ?? '';
$post['deny_cid'] = $toplevel_item['deny_cid'] ?? ''; $post['deny_cid'] = $toplevel_item['deny_cid'] ?? '';
$post['deny_gid'] = $toplevel_item['deny_gid'] ?? ''; $post['deny_gid'] = $toplevel_item['deny_gid'] ?? '';
$post['private'] = $toplevel_item['private']; $post['private'] = $toplevel_item['private'];
return $post; return $post;
} }
@ -878,7 +878,7 @@ class Item
if ($visibility === 'public') { if ($visibility === 'public') {
// The ACL selector introduced in version 2019.12 sends ACL input data even when the Public visibility is selected // The ACL selector introduced in version 2019.12 sends ACL input data even when the Public visibility is selected
$post['allow_cid'] = $post['allow_gid'] = $post['deny_cid'] = $post['deny_gid'] = ''; $post['allow_cid'] = $post['allow_gid'] = $post['deny_cid'] = $post['deny_gid'] = '';
} else if ($visibility === 'custom') { } elseif ($visibility === 'custom') {
// Since we know from the visibility parameter the item should be private, we have to prevent the empty ACL // Since we know from the visibility parameter the item should be private, we have to prevent the empty ACL
// case that would make it public. So we always append the author's contact id to the allowed contacts. // case that would make it public. So we always append the author's contact id to the allowed contacts.
// See https://github.com/friendica/friendica/issues/9672 // See https://github.com/friendica/friendica/issues/9672
@ -907,7 +907,7 @@ class Item
if ((preg_match_all("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", $post['body'], $match, PREG_SET_ORDER) || !empty($data['type'])) if ((preg_match_all("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", $post['body'], $match, PREG_SET_ORDER) || !empty($data['type']))
&& ($post['post-type'] != ItemModel::PT_PERSONAL_NOTE) && ($post['post-type'] != ItemModel::PT_PERSONAL_NOTE)
) { ) {
$post['post-type'] = ItemModel::PT_PAGE; $post['post-type'] = ItemModel::PT_PAGE;
$post['object-type'] = Activity\ObjectType::BOOKMARK; $post['object-type'] = Activity\ObjectType::BOOKMARK;
} }
@ -925,10 +925,10 @@ class Item
$post['direction'] = Conversation::PUSH; $post['direction'] = Conversation::PUSH;
$post['received'] = DateTimeFormat::utcNow(); $post['received'] = DateTimeFormat::utcNow();
$post['origin'] = true; $post['origin'] = true;
$post['wall'] = $post['wall'] ?? true; $post['wall'] = $post['wall'] ?? true;
$post['guid'] = $post['guid'] ?? System::createUUID(); $post['guid'] = $post['guid'] ?? System::createUUID();
$post['verb'] = $post['verb'] ?? Activity::POST; $post['verb'] = $post['verb'] ?? Activity::POST;
$post['uri'] = $post['uri'] ?? ItemModel::newURI($post['guid']); $post['uri'] = $post['uri'] ?? ItemModel::newURI($post['guid']);
$post['thr-parent'] = $post['thr-parent'] ?? $post['uri']; $post['thr-parent'] = $post['thr-parent'] ?? $post['uri'];
if (empty($post['gravity'])) { if (empty($post['gravity'])) {
@ -989,7 +989,7 @@ class Item
// Convert links with empty descriptions to links without an explicit description // Convert links with empty descriptions to links without an explicit description
$post['body'] = trim(preg_replace('#\[url=([^\]]*?)\]\[/url\]#ism', '[url]$1[/url]', $post['body'])); $post['body'] = trim(preg_replace('#\[url=([^\]]*?)\]\[/url\]#ism', '[url]$1[/url]', $post['body']));
$post['body'] = $this->bbCodeVideo->transform($post['body']); $post['body'] = $this->bbCodeVideo->transform($post['body']);
$post = $this->setObjectType($post); $post = $this->setObjectType($post);
// Personal notes must never be altered to a group post. // Personal notes must never be altered to a group post.
if ($post['post-type'] != ItemModel::PT_PERSONAL_NOTE) { if ($post['post-type'] != ItemModel::PT_PERSONAL_NOTE) {
@ -1083,7 +1083,7 @@ class Item
} }
if (($expire_interval > 0) && !empty($created)) { if (($expire_interval > 0) && !empty($created)) {
$expire_date = time() - ($expire_interval * 86400); $expire_date = time() - ($expire_interval * 86400);
$created_date = strtotime($created); $created_date = strtotime($created);
if ($created_date < $expire_date) { if ($created_date < $expire_date) {
DI::logger()->notice('Item created before expiration interval.', ['created' => date('c', $created_date), 'expired' => date('c', $expire_date)]); DI::logger()->notice('Item created before expiration interval.', ['created' => date('c', $created_date), 'expired' => date('c', $expire_date)]);

View file

@ -59,7 +59,7 @@ class PageInfo
$body = substr_replace($body, "\n[bookmark=" . $data['url'] . ']' . $linkTitle . "[/bookmark]\n", $existingAttachmentPos, 0); $body = substr_replace($body, "\n[bookmark=" . $data['url'] . ']' . $linkTitle . "[/bookmark]\n", $existingAttachmentPos, 0);
} else { } else {
$footer = self::getFooterFromData($data, $no_photos); $footer = self::getFooterFromData($data, $no_photos);
$body = self::stripTrailingUrlFromBody($body, $data['url']); $body = self::stripTrailingUrlFromBody($body, $data['url']);
$body .= "\n" . $footer; $body .= "\n" . $footer;
} }
@ -215,8 +215,11 @@ class PageInfo
$taglist = []; $taglist = [];
foreach ($data['keywords'] as $keyword) { foreach ($data['keywords'] as $keyword) {
$hashtag = str_replace([' ', '+', '/', '.', '#', "'"], $hashtag = str_replace(
['', '', '', '', '', ''], $keyword); [' ', '+', '/', '.', '#', "'"],
['', '', '', '', '', ''],
$keyword
);
$taglist[] = $hashtag; $taglist[] = $hashtag;
} }
@ -270,7 +273,7 @@ class PageInfo
protected static function stripTrailingUrlFromBody(string $body, string $url): string protected static function stripTrailingUrlFromBody(string $body, string $url): string
{ {
$quotedUrl = preg_quote($url, '#'); $quotedUrl = preg_quote($url, '#');
$body = preg_replace_callback("#(?: $body = preg_replace_callback("#(?:
\[url]$quotedUrl\[/url]| \[url]$quotedUrl\[/url]|
\[url=$quotedUrl]$quotedUrl\[/url]| \[url=$quotedUrl]$quotedUrl\[/url]|
\[url=$quotedUrl]([^[]*?)\[/url]| \[url=$quotedUrl]([^[]*?)\[/url]|

View file

@ -130,10 +130,11 @@ class BBCode
break; break;
case 'title': case 'title':
$value = self::toPlaintext(html_entity_decode($value, ENT_QUOTES, 'UTF-8')); $value = self::toPlaintext(html_entity_decode($value, ENT_QUOTES, 'UTF-8'));
$value = html_entity_decode($value, ENT_QUOTES, 'UTF-8'); $value = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
$data['title'] = self::escapeContent($value); $data['title'] = self::escapeContent($value);
// no break
default: default:
$data[$field] = html_entity_decode($value, ENT_QUOTES, 'UTF-8'); $data[$field] = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
break; break;
@ -289,7 +290,7 @@ class BBCode
// Remove all unneeded white space // Remove all unneeded white space
do { do {
$oldtext = $text; $oldtext = $text;
$text = str_replace([' ', "\n", "\r", '"'], ' ', $text); $text = str_replace([' ', "\n", "\r", '"'], ' ', $text);
} while ($oldtext != $text); } while ($oldtext != $text);
return trim($text); return trim($text);
@ -330,12 +331,12 @@ class BBCode
DI::logger()->info('the total body length exceeds the limit', ['maxlen' => $maxlen, 'body_len' => strlen($body)]); DI::logger()->info('the total body length exceeds the limit', ['maxlen' => $maxlen, 'body_len' => strlen($body)]);
$orig_body = $body; $orig_body = $body;
$new_body = ''; $new_body = '';
$textlen = 0; $textlen = 0;
$img_start = strpos($orig_body, '[img'); $img_start = strpos($orig_body, '[img');
$img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
$img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false); $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false);
while (($img_st_close !== false) && ($img_end !== false)) { while (($img_st_close !== false) && ($img_end !== false)) {
$img_st_close++; // make it point to AFTER the closing bracket $img_st_close++; // make it point to AFTER the closing bracket
@ -349,7 +350,7 @@ class BBCode
if ($textlen < $maxlen) { if ($textlen < $maxlen) {
DI::logger()->debug('the limit happens before an embedded image'); DI::logger()->debug('the limit happens before an embedded image');
$new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen); $new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
$textlen = $maxlen; $textlen = $maxlen;
} }
} else { } else {
$new_body = $new_body . substr($orig_body, 0, $img_start); $new_body = $new_body . substr($orig_body, 0, $img_start);
@ -363,7 +364,7 @@ class BBCode
if ($textlen < $maxlen) { if ($textlen < $maxlen) {
DI::logger()->debug('the limit happens before the end of a non-embedded image'); DI::logger()->debug('the limit happens before the end of a non-embedded image');
$new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen); $new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
$textlen = $maxlen; $textlen = $maxlen;
} }
} else { } else {
$new_body = $new_body . substr($orig_body, 0, $img_end); $new_body = $new_body . substr($orig_body, 0, $img_end);
@ -377,9 +378,9 @@ class BBCode
$orig_body = ''; $orig_body = '';
} }
$img_start = strpos($orig_body, '[img'); $img_start = strpos($orig_body, '[img');
$img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
$img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false); $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false);
} }
if (($textlen + strlen($orig_body)) > $maxlen) { if (($textlen + strlen($orig_body)) > $maxlen) {
@ -433,7 +434,7 @@ class BBCode
if (((strpos($data['text'], '[img=') !== false) || (strpos($data['text'], '[img]') !== false) || DI::config()->get('system', 'always_show_preview')) && !empty($data['image'])) { if (((strpos($data['text'], '[img=') !== false) || (strpos($data['text'], '[img]') !== false) || DI::config()->get('system', 'always_show_preview')) && !empty($data['image'])) {
$data['preview'] = $data['image']; $data['preview'] = $data['image'];
$data['image'] = ''; $data['image'] = '';
} }
$return = ''; $return = '';
@ -506,11 +507,11 @@ class BBCode
} }
$title = htmlentities($data['title'] ?? '', ENT_QUOTES, 'UTF-8', false); $title = htmlentities($data['title'] ?? '', ENT_QUOTES, 'UTF-8', false);
$text = htmlentities($data['text'], ENT_QUOTES, 'UTF-8', false); $text = htmlentities($data['text'], ENT_QUOTES, 'UTF-8', false);
if ($plaintext || (($title != '') && strstr($text, $title))) { if ($plaintext || (($title != '') && strstr($text, $title))) {
$data['title'] = $data['url']; $data['title'] = $data['url'];
} elseif (($text != '') && strstr($title, $text)) { } elseif (($text != '') && strstr($title, $text)) {
$data['text'] = $data['title']; $data['text'] = $data['title'];
$data['title'] = $data['url']; $data['title'] = $data['url'];
} }
@ -584,11 +585,11 @@ class BBCode
$res = [ $res = [
'start' => [ 'start' => [
'open' => $start_open, 'open' => $start_open,
'close' => $start_close 'close' => $start_close
], ],
'end' => [ 'end' => [
'open' => $end_open, 'open' => $end_open,
'close' => $end_open + strlen('[/' . $name . ']') 'close' => $end_open + strlen('[/' . $name . ']')
], ],
]; ];
@ -614,17 +615,17 @@ class BBCode
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$occurrences = 0; $occurrences = 0;
$pos = self::getTagPosition($text, $name, $occurrences); $pos = self::getTagPosition($text, $name, $occurrences);
while ($pos !== false && $occurrences++ < 1000) { while ($pos !== false && $occurrences++ < 1000) {
$start = substr($text, 0, $pos['start']['open']); $start = substr($text, 0, $pos['start']['open']);
$subject = substr($text, $pos['start']['open'], $pos['end']['close'] - $pos['start']['open']); $subject = substr($text, $pos['start']['open'], $pos['end']['close'] - $pos['start']['open']);
$end = substr($text, $pos['end']['close']); $end = substr($text, $pos['end']['close']);
if ($end === false) { if ($end === false) {
$end = ''; $end = '';
} }
$subject = preg_replace($pattern, $replace, $subject); $subject = preg_replace($pattern, $replace, $subject);
$text = $start . $subject . $end; $text = $start . $subject . $end;
$pos = self::getTagPosition($text, $name, $occurrences); $pos = self::getTagPosition($text, $name, $occurrences);
} }
@ -636,13 +637,13 @@ class BBCode
private static function extractImagesFromItemBody(string $body): array private static function extractImagesFromItemBody(string $body): array
{ {
$saved_image = []; $saved_image = [];
$orig_body = $body; $orig_body = $body;
$new_body = ''; $new_body = '';
$cnt = 0; $cnt = 0;
$img_start = strpos($orig_body, '[img'); $img_start = strpos($orig_body, '[img');
$img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
$img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false); $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false);
while (($img_st_close !== false) && ($img_end !== false)) { while (($img_st_close !== false) && ($img_end !== false)) {
$img_st_close++; // make it point to AFTER the closing bracket $img_st_close++; // make it point to AFTER the closing bracket
$img_end += $img_start; $img_end += $img_start;
@ -650,7 +651,7 @@ class BBCode
if (!strcmp(substr($orig_body, $img_start + $img_st_close, 5), 'data:')) { if (!strcmp(substr($orig_body, $img_start + $img_st_close, 5), 'data:')) {
// This is an embedded image // This is an embedded image
$saved_image[$cnt] = substr($orig_body, $img_start + $img_st_close, $img_end - ($img_start + $img_st_close)); $saved_image[$cnt] = substr($orig_body, $img_start + $img_st_close, $img_end - ($img_start + $img_st_close));
$new_body = $new_body . substr($orig_body, 0, $img_start) . '[$#saved_image' . $cnt . '#$]'; $new_body = $new_body . substr($orig_body, 0, $img_start) . '[$#saved_image' . $cnt . '#$]';
$cnt++; $cnt++;
} else { } else {
@ -664,9 +665,9 @@ class BBCode
$orig_body = ''; $orig_body = '';
} }
$img_start = strpos($orig_body, '[img'); $img_start = strpos($orig_body, '[img');
$img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
$img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false); $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false);
} }
$new_body = $new_body . $orig_body; $new_body = $new_body . $orig_body;
@ -736,7 +737,7 @@ class BBCode
$attributes = self::extractShareAttributes($matches[2]); $attributes = self::extractShareAttributes($matches[2]);
$attributes['comment'] = trim($matches[1]); $attributes['comment'] = trim($matches[1]);
$attributes['shared'] = trim($matches[3]); $attributes['shared'] = trim($matches[3]);
DI::profiler()->stopRecording(); DI::profiler()->stopRecording();
return $attributes; return $attributes;
@ -796,13 +797,13 @@ class BBCode
function ($match) use ($callback, $uriid) { function ($match) use ($callback, $uriid) {
$attributes = self::extractShareAttributes($match[2]); $attributes = self::extractShareAttributes($match[2]);
$author_contact = Contact::getByURL($attributes['profile'], false, ['id', 'url', 'addr', 'name', 'micro']); $author_contact = Contact::getByURL($attributes['profile'], false, ['id', 'url', 'addr', 'name', 'micro']);
$author_contact['url'] = ($author_contact['url'] ?? $attributes['profile']); $author_contact['url'] = ($author_contact['url'] ?? $attributes['profile']);
$author_contact['addr'] = ($author_contact['addr'] ?? ''); $author_contact['addr'] = ($author_contact['addr'] ?? '');
$attributes['author'] = ($author_contact['name'] ?? '') ?: $attributes['author']; $attributes['author'] = ($author_contact['name'] ?? '') ?: $attributes['author'];
$attributes['avatar'] = ($author_contact['micro'] ?? '') ?: $attributes['avatar']; $attributes['avatar'] = ($author_contact['micro'] ?? '') ?: $attributes['avatar'];
$attributes['profile'] = ($author_contact['url'] ?? '') ?: $attributes['profile']; $attributes['profile'] = ($author_contact['url'] ?? '') ?: $attributes['profile'];
if (!empty($author_contact['id'])) { if (!empty($author_contact['id'])) {
$attributes['avatar'] = Contact::getAvatarUrlForId($author_contact['id'], Proxy::SIZE_THUMB); $attributes['avatar'] = Contact::getAvatarUrlForId($author_contact['id'], Proxy::SIZE_THUMB);
@ -831,7 +832,7 @@ class BBCode
"/\[[zi]mg(.*?)\]([^\[\]]*)\[\/[zi]mg\]/ism", "/\[[zi]mg(.*?)\]([^\[\]]*)\[\/[zi]mg\]/ism",
function ($match) use ($simplehtml, $uriid) { function ($match) use ($simplehtml, $uriid) {
$attribute_string = $match[1]; $attribute_string = $match[1];
$attributes = []; $attributes = [];
foreach (['alt', 'width', 'height'] as $field) { foreach (['alt', 'width', 'height'] as $field) {
preg_match("/$field=(['\"])(.+?)\\1/ism", $attribute_string, $matches); preg_match("/$field=(['\"])(.+?)\\1/ism", $attribute_string, $matches);
$attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8'); $attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8');
@ -907,7 +908,7 @@ class BBCode
break; break;
case self::ACTIVITYPUB: case self::ACTIVITYPUB:
$author = '@<span class="vcard"><a href="' . $author_contact['url'] . '" class="url u-url mention" title="' . $author_contact['addr'] . '"><span class="fn nickname mention">' . $author_contact['addr'] . '</span></a>:</span>'; $author = '@<span class="vcard"><a href="' . $author_contact['url'] . '" class="url u-url mention" title="' . $author_contact['addr'] . '"><span class="fn nickname mention">' . $author_contact['addr'] . '</span></a>:</span>';
$text = '<div><a href="' . $attributes['link'] . '">' . html_entity_decode('&#x2672;', ENT_QUOTES, 'UTF-8') . '</a> ' . $author . '<blockquote>' . $content . '</blockquote></div>' . "\n"; $text = '<div><a href="' . $attributes['link'] . '">' . html_entity_decode('&#x2672;', ENT_QUOTES, 'UTF-8') . '</a> ' . $author . '<blockquote>' . $content . '</blockquote></div>' . "\n";
break; break;
default: default:
$text = ($is_quote_share ? "\n" : ''); $text = ($is_quote_share ? "\n" : '');
@ -916,7 +917,7 @@ class BBCode
$network = $contact['network'] ?? Protocol::PHANTOM; $network = $contact['network'] ?? Protocol::PHANTOM;
$gsid = ContactSelector::getServerIdForProfile($attributes['profile']); $gsid = ContactSelector::getServerIdForProfile($attributes['profile']);
$tpl = Renderer::getMarkupTemplate('shared_content.tpl'); $tpl = Renderer::getMarkupTemplate('shared_content.tpl');
$text .= self::SHARED_ANCHOR . Renderer::replaceMacros($tpl, [ $text .= self::SHARED_ANCHOR . Renderer::replaceMacros($tpl, [
'$profile' => $attributes['profile'], '$profile' => $attributes['profile'],
'$avatar' => $attributes['avatar'], '$avatar' => $attributes['avatar'],
@ -938,7 +939,7 @@ class BBCode
private static function removePictureLinksCallback(array $match): string private static function removePictureLinksCallback(array $match): string
{ {
$cache_key = 'remove:' . $match[1]; $cache_key = 'remove:' . $match[1];
$text = DI::cache()->get($cache_key); $text = DI::cache()->get($cache_key);
if (is_null($text)) { if (is_null($text)) {
$curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout'), HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]); $curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout'), HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]);
@ -963,7 +964,7 @@ class BBCode
$doc = new DOMDocument(); $doc = new DOMDocument();
@$doc->loadHTML($body); @$doc->loadHTML($body);
$xpath = new DOMXPath($doc); $xpath = new DOMXPath($doc);
$list = $xpath->query('//meta[@name]'); $list = $xpath->query('//meta[@name]');
foreach ($list as $node) { foreach ($list as $node) {
$attr = []; $attr = [];
@ -1034,7 +1035,7 @@ class BBCode
} }
$cache_key = 'clean:' . $match[1]; $cache_key = 'clean:' . $match[1];
$text = DI::cache()->get($cache_key); $text = DI::cache()->get($cache_key);
if (!is_null($text)) { if (!is_null($text)) {
return $text; return $text;
} }
@ -1066,7 +1067,7 @@ class BBCode
$doc = new DOMDocument(); $doc = new DOMDocument();
@$doc->loadHTML($body); @$doc->loadHTML($body);
$xpath = new DOMXPath($doc); $xpath = new DOMXPath($doc);
$list = $xpath->query('//meta[@name]'); $list = $xpath->query('//meta[@name]');
foreach ($list as $node) { foreach ($list as $node) {
$attr = []; $attr = [];
if ($node->attributes->length) { if ($node->attributes->length) {
@ -1134,7 +1135,7 @@ class BBCode
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$regexp = "/([@!])\[url\=([^\[\]]*)\].*?\[\/url\]/ism"; $regexp = "/([@!])\[url\=([^\[\]]*)\].*?\[\/url\]/ism";
$body = preg_replace_callback($regexp, [self::class, 'mentionCallback'], $body); $body = preg_replace_callback($regexp, [self::class, 'mentionCallback'], $body);
DI::profiler()->stopRecording(); DI::profiler()->stopRecording();
return $body; return $body;
} }
@ -1170,7 +1171,7 @@ class BBCode
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$regexp = "/([@!])\[url\=([^\[\]]*)\].*?\[\/url\]/ism"; $regexp = "/([@!])\[url\=([^\[\]]*)\].*?\[\/url\]/ism";
$body = preg_replace_callback($regexp, [self::class, 'mentionToAddrCallback'], $body); $body = preg_replace_callback($regexp, [self::class, 'mentionToAddrCallback'], $body);
DI::profiler()->stopRecording(); DI::profiler()->stopRecording();
return $body; return $body;
} }
@ -1310,7 +1311,7 @@ class BBCode
* $match[2] = $title or absent * $match[2] = $title or absent
*/ */
$try_oembed_callback = function (array $match) use ($uriid) { $try_oembed_callback = function (array $match) use ($uriid) {
$url = $match[1]; $url = $match[1];
$title = $match[2] ?? ''; $title = $match[2] ?? '';
try { try {
@ -1325,7 +1326,7 @@ class BBCode
// Extract the private images which use data urls since preg has issues with // Extract the private images which use data urls since preg has issues with
// large data sizes. Stash them away while we do bbcode conversion, and then put them back // large data sizes. Stash them away while we do bbcode conversion, and then put them back
// in after we've done all the regex matching. We cannot use any preg functions to do this. // in after we've done all the regex matching. We cannot use any preg functions to do this.
$extracted = self::extractImagesFromItemBody($text); $extracted = self::extractImagesFromItemBody($text);
$saved_image = $extracted['images']; $saved_image = $extracted['images'];
// General clean up of the content, for example unneeded blanks and new lines // General clean up of the content, for example unneeded blanks and new lines
@ -1474,13 +1475,13 @@ class BBCode
]; ];
do { do {
$oldtext = $text; $oldtext = $text;
$text = str_replace($search, $replace, $text); $text = str_replace($search, $replace, $text);
} while ($oldtext != $text); } while ($oldtext != $text);
// Replace these here only once // Replace these here only once
$search = ["\n[table]", "[/table]\n"]; $search = ["\n[table]", "[/table]\n"];
$replace = ["[table]", "[/table]"]; $replace = ["[table]", "[/table]"];
$text = str_replace($search, $replace, $text); $text = str_replace($search, $replace, $text);
// Trim new lines regardless of the system.remove_multiplicated_lines config value // Trim new lines regardless of the system.remove_multiplicated_lines config value
$text = trim($text, "\n"); $text = trim($text, "\n");
@ -1497,7 +1498,7 @@ class BBCode
]; ];
do { do {
$oldtext = $text; $oldtext = $text;
$text = str_replace($search, $replace, $text); $text = str_replace($search, $replace, $text);
} while ($oldtext != $text); } while ($oldtext != $text);
} }
@ -1634,7 +1635,7 @@ class BBCode
} }
$elements = [ $elements = [
'del' => 's', 'ins' => 'em', 'kbd' => 'code', 'mark' => 'strong', 'del' => 's', 'ins' => 'em', 'kbd' => 'code', 'mark' => 'strong',
'samp' => 'code', 'u' => 'em', 'var' => 'em' 'samp' => 'code', 'u' => 'em', 'var' => 'em'
]; ];
foreach ($elements as $bbcode => $html) { foreach ($elements as $bbcode => $html) {
@ -1749,7 +1750,7 @@ class BBCode
// handle nested quotes // handle nested quotes
$endlessloop = 0; $endlessloop = 0;
while ((strpos($text, "[/spoiler]") !== false) && (strpos($text, "[spoiler=") !== false) && (++$endlessloop < 20)) { while ((strpos($text, "[/spoiler]") !== false) && (strpos($text, "[spoiler=") !== false) && (++$endlessloop < 20)) {
$text = preg_replace( $text = preg_replace(
"/\[spoiler=[\"\']*(.*?)[\"\']*\](.*?)\[\/spoiler\]/ism", "/\[spoiler=[\"\']*(.*?)[\"\']*\](.*?)\[\/spoiler\]/ism",
'<details class="spoiler"><summary>$1</summary>$2</details>', '<details class="spoiler"><summary>$1</summary>$2</details>',
@ -1795,7 +1796,7 @@ class BBCode
// handle nested quotes // handle nested quotes
$endlessloop = 0; $endlessloop = 0;
while ((strpos($text, "[/quote]") !== false) && (strpos($text, "[quote=") !== false) && (++$endlessloop < 20)) { while ((strpos($text, "[/quote]") !== false) && (strpos($text, "[quote=") !== false) && (++$endlessloop < 20)) {
$text = preg_replace( $text = preg_replace(
"/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism", "/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism",
"<p><strong class=" . '"author"' . ">" . $t_wrote . "</strong></p><blockquote>$2</blockquote>", "<p><strong class=" . '"author"' . ">" . $t_wrote . "</strong></p><blockquote>$2</blockquote>",
@ -1829,7 +1830,7 @@ class BBCode
"/\[[iz]mg\=(.*?)\](.*?)\[\/[iz]mg\]/ism", "/\[[iz]mg\=(.*?)\](.*?)\[\/[iz]mg\]/ism",
function ($matches) use ($simple_html, $uriid) { function ($matches) use ($simple_html, $uriid) {
$matches[1] = self::proxyUrl($matches[1], $simple_html, $uriid); $matches[1] = self::proxyUrl($matches[1], $simple_html, $uriid);
$alt = htmlspecialchars($matches[2], ENT_COMPAT); $alt = htmlspecialchars($matches[2], ENT_COMPAT);
// Fix for Markdown problems with Diaspora, see issue #12701 // Fix for Markdown problems with Diaspora, see issue #12701
if (($simple_html != self::DIASPORA) || strpos($matches[2], '"') === false) { if (($simple_html != self::DIASPORA) || strpos($matches[2], '"') === false) {
return '<img src="' . $matches[1] . '" alt="' . $alt . '" title="' . $alt . '" class="' . (empty($alt) ? 'empty-description' : 'has-alt-description') . '">'; return '<img src="' . $matches[1] . '" alt="' . $alt . '" title="' . $alt . '" class="' . (empty($alt) ? 'empty-description' : 'has-alt-description') . '">';
@ -2043,7 +2044,7 @@ class BBCode
// Server independent link to posts and comments // Server independent link to posts and comments
// See issue: https://github.com/diaspora/diaspora_federation/issues/75 // See issue: https://github.com/diaspora/diaspora_federation/issues/75
$expression = "=diaspora://.*?/post/([0-9A-Za-z\-_@.:]{15,254}[0-9A-Za-z])=ism"; $expression = "=diaspora://.*?/post/([0-9A-Za-z\-_@.:]{15,254}[0-9A-Za-z])=ism";
$text = preg_replace($expression, DI::baseUrl() . "/display/$1", $text); $text = preg_replace($expression, DI::baseUrl() . "/display/$1", $text);
/* Tag conversion /* Tag conversion
* Supports: * Supports:
@ -2218,7 +2219,7 @@ class BBCode
}); });
$regex = '#<([^>]*?)(href)="(?!' . implode('|', $allowed_link_protocols) . ')(.*?)"(.*?)>#ism'; $regex = '#<([^>]*?)(href)="(?!' . implode('|', $allowed_link_protocols) . ')(.*?)"(.*?)>#ism';
$text = preg_replace($regex, '<$1$2="javascript:void(0)"$4 data-original-href="$3" class="invalid-href" title="' . DI::l10n()->t('Invalid link protocol') . '">', $text); $text = preg_replace($regex, '<$1$2="javascript:void(0)"$4 data-original-href="$3" class="invalid-href" title="' . DI::l10n()->t('Invalid link protocol') . '">', $text);
return $text; return $text;
} }
@ -2318,7 +2319,7 @@ class BBCode
* Transform #tags, strip off the [url] and replace spaces with underscore * Transform #tags, strip off the [url] and replace spaces with underscore
*/ */
$url_search_string = "^\[\]"; $url_search_string = "^\[\]";
$text = preg_replace_callback( $text = preg_replace_callback(
"/#\[url\=([$url_search_string]*)\](.*?)\[\/url\]/i", "/#\[url\=([$url_search_string]*)\](.*?)\[\/url\]/i",
function ($matches) { function ($matches) {
return '#' . str_replace(' ', '_', $matches[2]); return '#' . str_replace(' ', '_', $matches[2]);
@ -2367,7 +2368,7 @@ class BBCode
if ($for_diaspora) { if ($for_diaspora) {
$url_search_string = "^\[\]"; $url_search_string = "^\[\]";
$text = preg_replace_callback( $text = preg_replace_callback(
"/([@!])\[(.*?)\]\(([$url_search_string]*?)\)/ism", "/([@!])\[(.*?)\]\(([$url_search_string]*?)\)/ism",
[self::class, 'bbCodeMention2DiasporaCallback'], [self::class, 'bbCodeMention2DiasporaCallback'],
$text $text
@ -2571,7 +2572,7 @@ class BBCode
$header .= "' message_id='" . str_replace(["'", "[", "]"], ["&#x27;", "&#x5B;", "&#x5D;"], $uri); $header .= "' message_id='" . str_replace(["'", "[", "]"], ["&#x27;", "&#x5B;", "&#x5D;"], $uri);
} }
$header .= "']"; $header .= "']";
DI::profiler()->stopRecording(); DI::profiler()->stopRecording();
return $header; return $header;

View file

@ -24,10 +24,11 @@ class Markdown
* @param string $baseuri Optional. Prepend anchor links with this URL * @param string $baseuri Optional. Prepend anchor links with this URL
* @return string * @return string
*/ */
public static function convert($text, $hardwrap = true, $baseuri = null) { public static function convert($text, $hardwrap = true, $baseuri = null)
{
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$MarkdownParser = new MarkdownParser(); $MarkdownParser = new MarkdownParser();
$MarkdownParser->code_class_prefix = 'language-'; $MarkdownParser->code_class_prefix = 'language-';
$MarkdownParser->hard_wrap = $hardwrap; $MarkdownParser->hard_wrap = $hardwrap;
$MarkdownParser->hashtag_protection = true; $MarkdownParser->hashtag_protection = true;
@ -121,10 +122,10 @@ class Markdown
//$s = preg_replace("/([^\]\=]|^)(https?\:\/\/)(vimeo|youtu|www\.youtube|soundcloud)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url=$2$3$4]$2$3$4[/url]',$s); //$s = preg_replace("/([^\]\=]|^)(https?\:\/\/)(vimeo|youtu|www\.youtube|soundcloud)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url=$2$3$4]$2$3$4[/url]',$s);
$s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s); $s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s);
$s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/www.youtube.com\/watch\?v\=(.*?)\].*?\[\/url\]/ism' , '[youtube]$1[/youtube]', 'url', $s); $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/www.youtube.com\/watch\?v\=(.*?)\].*?\[\/url\]/ism', '[youtube]$1[/youtube]', 'url', $s);
$s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/www.youtube.com\/shorts\/(.*?)\].*?\[\/url\]/ism' , '[youtube]$1[/youtube]', 'url', $s); $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/www.youtube.com\/shorts\/(.*?)\].*?\[\/url\]/ism', '[youtube]$1[/youtube]', 'url', $s);
$s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/url\]/ism' , '[vimeo]$2[/vimeo]' , 'url', $s); $s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/url\]/ism', '[vimeo]$2[/vimeo]', 'url', $s);
$s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/vimeo.com\/([0-9]+)\](.*?)\[\/url\]/ism' , '[vimeo]$1[/vimeo]' , 'url', $s); $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/vimeo.com\/([0-9]+)\](.*?)\[\/url\]/ism', '[vimeo]$1[/vimeo]', 'url', $s);
// remove duplicate adjacent code tags // remove duplicate adjacent code tags
$s = preg_replace('/(\[code\])+(.*?)(\[\/code\])+/ism', '[code]$2[/code]', $s); $s = preg_replace('/(\[code\])+(.*?)(\[\/code\])+/ism', '[code]$2[/code]', $s);

View file

@ -39,11 +39,11 @@ class VCard
$contact_url = Contact::getProfileLink($contact); $contact_url = Contact::getProfileLink($contact);
if ($contact['network'] != '') { if ($contact['network'] != '') {
$network_link = Strings::formatNetworkName($contact['network'], $contact_url); $network_link = Strings::formatNetworkName($contact['network'], $contact_url);
$network_svg = ContactSelector::networkToSVG($contact['network'], $contact['gsid'], '', DI::userSession()->getLocalUserId()); $network_svg = ContactSelector::networkToSVG($contact['network'], $contact['gsid'], '', DI::userSession()->getLocalUserId());
} else { } else {
$network_link = ''; $network_link = '';
$network_svg = ''; $network_svg = '';
} }
$follow_link = ''; $follow_link = '';
@ -53,7 +53,7 @@ class VCard
$mention_link = ''; $mention_link = '';
$showgroup_link = ''; $showgroup_link = '';
$photo = Contact::getPhoto($contact); $photo = Contact::getPhoto($contact);
if (DI::userSession()->getLocalUserId()) { if (DI::userSession()->getLocalUserId()) {
if (Contact\User::isIsBlocked($contact['id'], DI::userSession()->getLocalUserId())) { if (Contact\User::isIsBlocked($contact['id'], DI::userSession()->getLocalUserId())) {
@ -68,8 +68,8 @@ class VCard
} else { } else {
$pcontact = Contact::selectFirst([], ['uid' => DI::userSession()->getLocalUserId(), 'uri-id' => $contact['uri-id'], 'deleted' => false]); $pcontact = Contact::selectFirst([], ['uid' => DI::userSession()->getLocalUserId(), 'uri-id' => $contact['uri-id'], 'deleted' => false]);
$id = $pcontact['id'] ?? $contact['id']; $id = $pcontact['id'] ?? $contact['id'];
$rel = $pcontact['rel'] ?? Contact::NOTHING; $rel = $pcontact['rel'] ?? Contact::NOTHING;
$pending = $pcontact['pending'] ?? false; $pending = $pcontact['pending'] ?? false;
if (!empty($pcontact) && in_array($pcontact['network'], [Protocol::MAIL, Protocol::FEED])) { if (!empty($pcontact) && in_array($pcontact['network'], [Protocol::MAIL, Protocol::FEED])) {
@ -91,8 +91,8 @@ class VCard
if ($contact['contact-type'] == Contact::TYPE_COMMUNITY) { if ($contact['contact-type'] == Contact::TYPE_COMMUNITY) {
if (!$hide_mention) { if (!$hide_mention) {
$mention_label = DI::l10n()->t('Post to group'); $mention_label = DI::l10n()->t('Post to group');
$mention_link = 'compose/0?body=!' . $contact['addr']; $mention_link = 'compose/0?body=!' . $contact['addr'];
} }
$showgroup_link = 'contact/' . $id . '/conversations'; $showgroup_link = 'contact/' . $id . '/conversations';
} elseif (!$hide_mention) { } elseif (!$hide_mention) {

View file

@ -40,12 +40,12 @@ class Addon
public static function getAvailableList(): array public static function getAvailableList(): array
{ {
$addons = []; $addons = [];
$files = glob('addon/*/'); $files = glob('addon/*/');
if (is_array($files)) { if (is_array($files)) {
foreach ($files as $file) { foreach ($files as $file) {
if (is_dir($file)) { if (is_dir($file)) {
list($tmp, $addon) = array_map('trim', explode('/', $file)); list($tmp, $addon) = array_map('trim', explode('/', $file));
$info = self::getInfo($addon); $info = self::getInfo($addon);
if (DI::config()->get('system', 'show_unsupported_addons') if (DI::config()->get('system', 'show_unsupported_addons')
|| strtolower($info['status']) != 'unsupported' || strtolower($info['status']) != 'unsupported'
@ -70,7 +70,7 @@ class Addon
public static function getAdminList(): array public static function getAdminList(): array
{ {
$addons_admin = []; $addons_admin = [];
$addons = array_filter(DI::config()->get('addons') ?? []); $addons = array_filter(DI::config()->get('addons') ?? []);
ksort($addons); ksort($addons);
foreach ($addons as $name => $data) { foreach ($addons as $name => $data) {
@ -79,8 +79,8 @@ class Addon
} }
$addons_admin[$name] = [ $addons_admin[$name] = [
'url' => 'admin/addons/' . $name, 'url' => 'admin/addons/' . $name,
'name' => $name, 'name' => $name,
'class' => 'addon' 'class' => 'addon'
]; ];
} }
@ -160,7 +160,7 @@ class Addon
DI::config()->set('addons', $addon, [ DI::config()->set('addons', $addon, [
'last_update' => $t, 'last_update' => $t,
'admin' => function_exists($addon . '_addon_admin'), 'admin' => function_exists($addon . '_addon_admin'),
]); ]);
if (!self::isEnabled($addon)) { if (!self::isEnabled($addon)) {
@ -182,7 +182,7 @@ class Addon
$addons = array_filter(DI::config()->get('addons') ?? []); $addons = array_filter(DI::config()->get('addons') ?? []);
foreach ($addons as $name => $data) { foreach ($addons as $name => $data) {
$addonname = Strings::sanitizeFilePathItem(trim($name)); $addonname = Strings::sanitizeFilePathItem(trim($name));
$addon_file_path = 'addon/' . $addonname . '/' . $addonname . '.php'; $addon_file_path = 'addon/' . $addonname . '/' . $addonname . '.php';
if (file_exists($addon_file_path) && $data['last_update'] == filemtime($addon_file_path)) { if (file_exists($addon_file_path) && $data['last_update'] == filemtime($addon_file_path)) {
// Addon unmodified, skipping // Addon unmodified, skipping
@ -218,12 +218,12 @@ class Addon
$addon = Strings::sanitizeFilePathItem($addon); $addon = Strings::sanitizeFilePathItem($addon);
$info = [ $info = [
'name' => $addon, 'name' => $addon,
'description' => "", 'description' => "",
'author' => [], 'author' => [],
'maintainer' => [], 'maintainer' => [],
'version' => "", 'version' => "",
'status' => "" 'status' => ""
]; ];
if (!is_file("addon/$addon/$addon.php")) { if (!is_file("addon/$addon/$addon.php")) {
@ -247,7 +247,7 @@ class Addon
} }
list($type, $v) = $addon_info; list($type, $v) = $addon_info;
$type = strtolower($type); $type = strtolower($type);
if ($type == "author" || $type == "maintainer") { if ($type == "author" || $type == "maintainer") {
$r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m);
if ($r) { if ($r) {
@ -302,7 +302,7 @@ class Addon
public static function getVisibleList(): array public static function getVisibleList(): array
{ {
$visible_addons = []; $visible_addons = [];
$addons = array_filter(DI::config()->get('addons') ?? []); $addons = array_filter(DI::config()->get('addons') ?? []);
foreach ($addons as $name => $data) { foreach ($addons as $name => $data) {
$visible_addons[] = $name; $visible_addons[] = $name;

View file

@ -57,7 +57,7 @@ class Protocol
const XMPP = 'xmpp'; // XMPP const XMPP = 'xmpp'; // XMPP
const ZOT = 'zot!'; // Zot! const ZOT = 'zot!'; // Zot!
const PHANTOM = 'unkn'; // Place holder const PHANTOM = 'unkn'; // Place holder
/** /**
* Returns whether the provided protocol supports following * Returns whether the provided protocol supports following
@ -74,7 +74,7 @@ class Protocol
$hook_data = [ $hook_data = [
'protocol' => $protocol, 'protocol' => $protocol,
'result' => null 'result' => null
]; ];
Hook::callAll('support_follow', $hook_data); Hook::callAll('support_follow', $hook_data);
@ -96,7 +96,7 @@ class Protocol
$hook_data = [ $hook_data = [
'protocol' => $protocol, 'protocol' => $protocol,
'result' => null 'result' => null
]; ];
Hook::callAll('support_revoke_follow', $hook_data); Hook::callAll('support_revoke_follow', $hook_data);
@ -242,8 +242,8 @@ class Protocol
// Catch-all hook for connector addons // Catch-all hook for connector addons
$hook_data = [ $hook_data = [
'contact' => $contact, 'contact' => $contact,
'uid' => $uid, 'uid' => $uid,
'result' => null, 'result' => null,
]; ];
Hook::callAll('block', $hook_data); Hook::callAll('block', $hook_data);
@ -281,8 +281,8 @@ class Protocol
// Catch-all hook for connector addons // Catch-all hook for connector addons
$hook_data = [ $hook_data = [
'contact' => $contact, 'contact' => $contact,
'uid' => $uid, 'uid' => $uid,
'result' => null, 'result' => null,
]; ];
Hook::callAll('unblock', $hook_data); Hook::callAll('unblock', $hook_data);
@ -309,7 +309,7 @@ class Protocol
$hook_data = [ $hook_data = [
'protocol' => $protocol, 'protocol' => $protocol,
'result' => null 'result' => null
]; ];
Hook::callAll('support_probe', $hook_data); Hook::callAll('support_probe', $hook_data);

View file

@ -117,15 +117,15 @@ class Search
$results = json_decode($resultJson, true); $results = json_decode($resultJson, true);
$resultList = new ResultList( $resultList = new ResultList(
($results['page'] ?? 0) ?: 1, ($results['page'] ?? 0) ?: 1,
$results['count'] ?? 0, $results['count'] ?? 0,
($results['itemsperpage'] ?? 0) ?: 30 ($results['itemsperpage'] ?? 0) ?: 30
); );
$profiles = $results['profiles'] ?? []; $profiles = $results['profiles'] ?? [];
foreach ($profiles as $profile) { foreach ($profiles as $profile) {
$profile_url = $profile['profile_url'] ?? ''; $profile_url = $profile['profile_url'] ?? '';
$contactDetails = Contact::getByURLForUser($profile_url, DI::userSession()->getLocalUserId()); $contactDetails = Contact::getByURLForUser($profile_url, DI::userSession()->getLocalUserId());
$result = new ContactResult( $result = new ContactResult(
@ -137,7 +137,7 @@ class Search
Protocol::DFRN, Protocol::DFRN,
$contactDetails['cid'] ?? 0, $contactDetails['cid'] ?? 0,
$contactDetails['zid'] ?? 0, $contactDetails['zid'] ?? 0,
$profile['tags'] ?? '' $profile['tags'] ?? ''
); );
$resultList->addResult($result); $resultList->addResult($result);
@ -231,7 +231,7 @@ class Search
// Converting Directory Search results into contact-looking records // Converting Directory Search results into contact-looking records
$return = array_map(function ($result) { $return = array_map(function ($result) {
static $contactType = [ static $contactType = [
'People' => Contact::TYPE_PERSON, 'People' => Contact::TYPE_PERSON,
// Kept for backward compatibility // Kept for backward compatibility
'Forum' => Contact::TYPE_COMMUNITY, 'Forum' => Contact::TYPE_COMMUNITY,
'Group' => Contact::TYPE_COMMUNITY, 'Group' => Contact::TYPE_COMMUNITY,

View file

@ -20,7 +20,7 @@ class Theme
{ {
$allowed_themes_str = DI::config()->get('system', 'allowed_themes'); $allowed_themes_str = DI::config()->get('system', 'allowed_themes');
$allowed_themes_raw = explode(',', str_replace(' ', '', $allowed_themes_str)); $allowed_themes_raw = explode(',', str_replace(' ', '', $allowed_themes_str));
$allowed_themes = []; $allowed_themes = [];
if (count($allowed_themes_raw)) { if (count($allowed_themes_raw)) {
foreach ($allowed_themes_raw as $theme) { foreach ($allowed_themes_raw as $theme) {
$theme = Strings::sanitizeFilePathItem(trim($theme)); $theme = Strings::sanitizeFilePathItem(trim($theme));
@ -58,14 +58,14 @@ class Theme
$theme = Strings::sanitizeFilePathItem($theme); $theme = Strings::sanitizeFilePathItem($theme);
$info = [ $info = [
'name' => $theme, 'name' => $theme,
'description' => "", 'description' => "",
'author' => [], 'author' => [],
'maintainer' => [], 'maintainer' => [],
'version' => "", 'version' => "",
'credits' => "", 'credits' => "",
'experimental' => file_exists("view/theme/$theme/experimental"), 'experimental' => file_exists("view/theme/$theme/experimental"),
'unsupported' => file_exists("view/theme/$theme/unsupported") 'unsupported' => file_exists("view/theme/$theme/unsupported")
]; ];
if (!is_file("view/theme/$theme/theme.php")) { if (!is_file("view/theme/$theme/theme.php")) {
@ -84,7 +84,7 @@ class Theme
$comment_line = trim($comment_line, "\t\n\r */"); $comment_line = trim($comment_line, "\t\n\r */");
if (strpos($comment_line, ':') !== false) { if (strpos($comment_line, ':') !== false) {
list($key, $value) = array_map("trim", explode(":", $comment_line, 2)); list($key, $value) = array_map("trim", explode(":", $comment_line, 2));
$key = strtolower($key); $key = strtolower($key);
if ($key == "author") { if ($key == "author") {
$result = preg_match("|([^<]+)<([^>]+)>|", $value, $matches); $result = preg_match("|([^<]+)<([^>]+)>|", $value, $matches);
if ($result) { if ($result) {
@ -153,7 +153,7 @@ class Theme
} }
$allowed_themes = Theme::getAllowedList(); $allowed_themes = Theme::getAllowedList();
$key = array_search($theme, $allowed_themes); $key = array_search($theme, $allowed_themes);
if ($key !== false) { if ($key !== false) {
unset($allowed_themes[$key]); unset($allowed_themes[$key]);
Theme::setAllowedList($allowed_themes); Theme::setAllowedList($allowed_themes);
@ -185,7 +185,7 @@ class Theme
$func(); $func();
} }
$allowed_themes = Theme::getAllowedList(); $allowed_themes = Theme::getAllowedList();
$allowed_themes[] = $theme; $allowed_themes[] = $theme;
Theme::setAllowedList($allowed_themes); Theme::setAllowedList($allowed_themes);
@ -267,7 +267,7 @@ class Theme
{ {
$theme = Strings::sanitizeFilePathItem($theme); $theme = Strings::sanitizeFilePathItem($theme);
$appHelper = DI::appHelper(); $appHelper = DI::appHelper();
$base_theme = $appHelper->getThemeInfoValue('extends') ?? ''; $base_theme = $appHelper->getThemeInfoValue('extends') ?? '';
if (file_exists("view/theme/$theme/config.php")) { if (file_exists("view/theme/$theme/config.php")) {

View file

@ -169,7 +169,7 @@ class Update
if ($build != DB_UPDATE_VERSION || $force) { if ($build != DB_UPDATE_VERSION || $force) {
require_once 'update.php'; require_once 'update.php';
$stored = intval($build); $stored = intval($build);
$current = intval(DB_UPDATE_VERSION); $current = intval(DB_UPDATE_VERSION);
if ($stored < $current || $force) { if ($stored < $current || $force) {
DI::config()->reload(); DI::config()->reload();
@ -203,8 +203,11 @@ class Update
// run the pre_update_nnnn functions in update.php // run the pre_update_nnnn functions in update.php
for ($version = $stored + 1; $version <= $current; $version++) { for ($version = $stored + 1; $version <= $current; $version++) {
DI::logger()->notice('Execute pre update.', ['version' => $version]); DI::logger()->notice('Execute pre update.', ['version' => $version]);
DI::config()->set('system', 'maintenance_reason', DI::l10n()->t('%s: executing pre update %d', DI::config()->set('system', 'maintenance_reason', DI::l10n()->t(
DateTimeFormat::utcNow() . ' ' . date('e'), $version)); '%s: executing pre update %d',
DateTimeFormat::utcNow() . ' ' . date('e'),
$version
));
$r = self::runUpdateFunction($version, 'pre_update', $sendMail); $r = self::runUpdateFunction($version, 'pre_update', $sendMail);
if (!$r) { if (!$r) {
DI::logger()->warning('Pre update failed', ['version' => $version]); DI::logger()->warning('Pre update failed', ['version' => $version]);
@ -245,8 +248,11 @@ class Update
// run the update_nnnn functions in update.php // run the update_nnnn functions in update.php
for ($version = $stored + 1; $version <= $current; $version++) { for ($version = $stored + 1; $version <= $current; $version++) {
DI::logger()->notice('Execute post update.', ['version' => $version]); DI::logger()->notice('Execute post update.', ['version' => $version]);
DI::config()->set('system', 'maintenance_reason', DI::l10n()->t('%s: executing post update %d', DI::config()->set('system', 'maintenance_reason', DI::l10n()->t(
DateTimeFormat::utcNow() . ' ' . date('e'), $version)); '%s: executing post update %d',
DateTimeFormat::utcNow() . ' ' . date('e'),
$version
));
$r = self::runUpdateFunction($version, 'update', $sendMail); $r = self::runUpdateFunction($version, 'update', $sendMail);
if (!$r) { if (!$r) {
DI::logger()->warning('Post update failed', ['version' => $version]); DI::logger()->warning('Post update failed', ['version' => $version]);
@ -359,13 +365,15 @@ class Update
foreach($adminEmails as $admin) { foreach($adminEmails as $admin) {
$l10n = DI::l10n()->withLang($admin['language'] ?: 'en'); $l10n = DI::l10n()->withLang($admin['language'] ?: 'en');
$preamble = Strings::deindent($l10n->t(" $preamble = Strings::deindent($l10n->t(
"
The friendica developers released update %s recently, The friendica developers released update %s recently,
but when I tried to install it, something went terribly wrong. but when I tried to install it, something went terribly wrong.
This needs to be fixed soon and I can't do it alone. Please contact a This needs to be fixed soon and I can't do it alone. Please contact a
friendica developer if you can not help me on your own. My database might be invalid.", friendica developer if you can not help me on your own. My database might be invalid.",
$update_id)); $update_id
$body = $l10n->t('The error message is\n[pre]%s[/pre]', $error_message); ));
$body = $l10n->t('The error message is\n[pre]%s[/pre]', $error_message);
$email = DI::emailer() $email = DI::emailer()
->newSystemMail() ->newSystemMail()
@ -391,9 +399,12 @@ class Update
foreach(User::getAdminListForEmailing(['uid', 'language', 'email']) as $admin) { foreach(User::getAdminListForEmailing(['uid', 'language', 'email']) as $admin) {
$l10n = DI::l10n()->withLang($admin['language'] ?: 'en'); $l10n = DI::l10n()->withLang($admin['language'] ?: 'en');
$preamble = Strings::deindent($l10n->t(' $preamble = Strings::deindent($l10n->t(
'
The friendica database was successfully updated from %s to %s.', The friendica database was successfully updated from %s to %s.',
$from_build, $to_build)); $from_build,
$to_build
));
$email = DI::emailer() $email = DI::emailer()
->newSystemMail() ->newSystemMail()

View file

@ -112,7 +112,10 @@ class Cron
} elseif ($entry['priority'] != Worker::PRIORITY_CRITICAL) { } elseif ($entry['priority'] != Worker::PRIORITY_CRITICAL) {
$new_priority = Worker::PRIORITY_NEGLIGIBLE; $new_priority = Worker::PRIORITY_NEGLIGIBLE;
} }
DBA::update('workerqueue', ['executed' => DBA::NULL_DATETIME, 'created' => DateTimeFormat::utcNow(), 'priority' => $new_priority, 'pid' => 0], ['id' => $entry["id"]] DBA::update(
'workerqueue',
['executed' => DBA::NULL_DATETIME, 'created' => DateTimeFormat::utcNow(), 'priority' => $new_priority, 'pid' => 0],
['id' => $entry["id"]]
); );
} else { } else {
DI::logger()->info('Process runtime is okay', ['duration' => number_format($duration, 3), 'max' => $max_duration, 'id' => $entry["id"], 'pid' => $entry["pid"], 'command' => $command]); DI::logger()->info('Process runtime is okay', ['duration' => number_format($duration, 3), 'max' => $max_duration, 'id' => $entry["id"], 'pid' => $entry["pid"], 'command' => $command]);

View file

@ -73,8 +73,11 @@ class DBStructure
'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item', 'challenge', 'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item', 'challenge',
'auth_codes', 'tokens', 'clients', 'profile_check', 'host', 'conversation', 'fcontact', 'addon', 'push_subscriber']; 'auth_codes', 'tokens', 'clients', 'profile_check', 'host', 'conversation', 'fcontact', 'addon', 'push_subscriber'];
$tables = DBA::selectToArray('INFORMATION_SCHEMA.TABLES', ['TABLE_NAME'], $tables = DBA::selectToArray(
['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']); 'INFORMATION_SCHEMA.TABLES',
['TABLE_NAME'],
['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']
);
if (empty($tables)) { if (empty($tables)) {
echo DI::l10n()->t('No unused tables found.'); echo DI::l10n()->t('No unused tables found.');
@ -143,8 +146,11 @@ class DBStructure
*/ */
private static function printUpdateError(string $message): string private static function printUpdateError(string $message): string
{ {
echo DI::l10n()->t("\nError %d occurred during database update:\n%s\n", echo DI::l10n()->t(
DBA::errorNo(), DBA::errorMessage()); "\nError %d occurred during database update:\n%s\n",
DBA::errorNo(),
DBA::errorMessage()
);
return DI::l10n()->t('Errors encountered performing database changes: ') . $message . '<br />'; return DI::l10n()->t('Errors encountered performing database changes: ') . $message . '<br />';
} }
@ -522,30 +528,36 @@ class DBStructure
// This query doesn't seem to be executable as a prepared statement // This query doesn't seem to be executable as a prepared statement
$indexes = DBA::toArray(DBA::p("SHOW INDEX FROM " . DBA::quoteIdentifier($table))); $indexes = DBA::toArray(DBA::p("SHOW INDEX FROM " . DBA::quoteIdentifier($table)));
$fields = DBA::selectToArray('INFORMATION_SCHEMA.COLUMNS', $fields = DBA::selectToArray(
'INFORMATION_SCHEMA.COLUMNS',
['COLUMN_NAME', 'COLUMN_TYPE', 'IS_NULLABLE', 'COLUMN_DEFAULT', 'EXTRA', ['COLUMN_NAME', 'COLUMN_TYPE', 'IS_NULLABLE', 'COLUMN_DEFAULT', 'EXTRA',
'COLUMN_KEY', 'COLLATION_NAME', 'COLUMN_COMMENT'], 'COLUMN_KEY', 'COLLATION_NAME', 'COLUMN_COMMENT'],
["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?", ["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?",
DBA::databaseName(), $table]); DBA::databaseName(), $table]
);
$foreign_keys = DBA::selectToArray('INFORMATION_SCHEMA.KEY_COLUMN_USAGE', $foreign_keys = DBA::selectToArray(
'INFORMATION_SCHEMA.KEY_COLUMN_USAGE',
['COLUMN_NAME', 'CONSTRAINT_NAME', 'REFERENCED_TABLE_NAME', 'REFERENCED_COLUMN_NAME'], ['COLUMN_NAME', 'CONSTRAINT_NAME', 'REFERENCED_TABLE_NAME', 'REFERENCED_COLUMN_NAME'],
["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `REFERENCED_TABLE_SCHEMA` IS NOT NULL", ["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `REFERENCED_TABLE_SCHEMA` IS NOT NULL",
DBA::databaseName(), $table]); DBA::databaseName(), $table]
);
$table_status = DBA::selectFirst('INFORMATION_SCHEMA.TABLES', $table_status = DBA::selectFirst(
'INFORMATION_SCHEMA.TABLES',
['ENGINE', 'TABLE_COLLATION', 'TABLE_COMMENT'], ['ENGINE', 'TABLE_COLLATION', 'TABLE_COMMENT'],
["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?", ["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?",
DBA::databaseName(), $table]); DBA::databaseName(), $table]
);
$fielddata = []; $fielddata = [];
$indexdata = []; $indexdata = [];
$foreigndata = []; $foreigndata = [];
if (DBA::isResult($foreign_keys)) { if (DBA::isResult($foreign_keys)) {
foreach ($foreign_keys as $foreign_key) { foreach ($foreign_keys as $foreign_key) {
$parameters = ['foreign' => [$foreign_key['REFERENCED_TABLE_NAME'] => $foreign_key['REFERENCED_COLUMN_NAME']]]; $parameters = ['foreign' => [$foreign_key['REFERENCED_TABLE_NAME'] => $foreign_key['REFERENCED_COLUMN_NAME']]];
$constraint = self::getConstraintName($table, $foreign_key['COLUMN_NAME'], $parameters); $constraint = self::getConstraintName($table, $foreign_key['COLUMN_NAME'], $parameters);
$foreigndata[$constraint] = $foreign_key; $foreigndata[$constraint] = $foreign_key;
} }
} }
@ -573,8 +585,8 @@ class DBStructure
$fielddata = []; $fielddata = [];
if (DBA::isResult($fields)) { if (DBA::isResult($fields)) {
foreach ($fields as $field) { foreach ($fields as $field) {
$search = ['tinyint(1)', 'tinyint(3) unsigned', 'tinyint(4)', 'smallint(5) unsigned', 'smallint(6)', 'mediumint(8) unsigned', 'mediumint(9)', 'bigint(20)', 'int(10) unsigned', 'int(11)']; $search = ['tinyint(1)', 'tinyint(3) unsigned', 'tinyint(4)', 'smallint(5) unsigned', 'smallint(6)', 'mediumint(8) unsigned', 'mediumint(9)', 'bigint(20)', 'int(10) unsigned', 'int(11)'];
$replace = ['boolean', 'tinyint unsigned', 'tinyint', 'smallint unsigned', 'smallint', 'mediumint unsigned', 'mediumint', 'bigint', 'int unsigned', 'int']; $replace = ['boolean', 'tinyint unsigned', 'tinyint', 'smallint unsigned', 'smallint', 'mediumint unsigned', 'mediumint', 'bigint', 'int unsigned', 'int'];
$field['COLUMN_TYPE'] = str_replace($search, $replace, $field['COLUMN_TYPE']); $field['COLUMN_TYPE'] = str_replace($search, $replace, $field['COLUMN_TYPE']);
$fielddata[$field['COLUMN_NAME']]['type'] = $field['COLUMN_TYPE']; $fielddata[$field['COLUMN_NAME']]['type'] = $field['COLUMN_TYPE'];
@ -596,13 +608,13 @@ class DBStructure
} }
$fielddata[$field['COLUMN_NAME']]['Collation'] = $field['COLLATION_NAME']; $fielddata[$field['COLUMN_NAME']]['Collation'] = $field['COLLATION_NAME'];
$fielddata[$field['COLUMN_NAME']]['comment'] = $field['COLUMN_COMMENT']; $fielddata[$field['COLUMN_NAME']]['comment'] = $field['COLUMN_COMMENT'];
} }
} }
return [ return [
'fields' => $fielddata, 'fields' => $fielddata,
'indexes' => $indexdata, 'indexes' => $indexdata,
'foreign_keys' => $foreigndata, 'foreign_keys' => $foreigndata,
'table_status' => $table_status 'table_status' => $table_status
]; ];
@ -731,9 +743,11 @@ class DBStructure
*/ */
public static function existsForeignKeyForField(string $table, string $field): bool public static function existsForeignKeyForField(string $table, string $field): bool
{ {
return DBA::exists('INFORMATION_SCHEMA.KEY_COLUMN_USAGE', return DBA::exists(
'INFORMATION_SCHEMA.KEY_COLUMN_USAGE',
["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ? AND `REFERENCED_TABLE_SCHEMA` IS NOT NULL", ["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ? AND `REFERENCED_TABLE_SCHEMA` IS NOT NULL",
DBA::databaseName(), $table, $field]); DBA::databaseName(), $table, $field]
);
} }
/** /**
@ -806,8 +820,8 @@ class DBStructure
if (self::existsTable('user') && !DBA::exists('user', ['uid' => 0])) { if (self::existsTable('user') && !DBA::exists('user', ['uid' => 0])) {
$user = [ $user = [
'verified' => true, 'verified' => true,
'page-flags' => User::PAGE_FLAGS_SOAPBOX, 'page-flags' => User::PAGE_FLAGS_SOAPBOX,
'account-type' => User::ACCOUNT_TYPE_RELAY, 'account-type' => User::ACCOUNT_TYPE_RELAY,
]; ];
DBA::insert('user', $user); DBA::insert('user', $user);
@ -883,7 +897,7 @@ class DBStructure
$permission = ''; $permission = '';
} }
$fields = ['id' => $set['psid'], 'uid' => $set['uid'], 'allow_cid' => $permission, $fields = ['id' => $set['psid'], 'uid' => $set['uid'], 'allow_cid' => $permission,
'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']; 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => ''];
DBA::insert('permissionset', $fields, Database::INSERT_IGNORE); DBA::insert('permissionset', $fields, Database::INSERT_IGNORE);
} }
DBA::close($sets); DBA::close($sets);
@ -913,7 +927,7 @@ class DBStructure
$isUpdate = false; $isUpdate = false;
$processes = DBA::select('information_schema.processlist', ['info'], [ $processes = DBA::select('information_schema.processlist', ['info'], [
'db' => DBA::databaseName(), 'db' => DBA::databaseName(),
'command' => ['Query', 'Execute'] 'command' => ['Query', 'Execute']
]); ]);

View file

@ -35,7 +35,7 @@ use GuzzleHttp\Psr7\Uri;
class PostUpdate class PostUpdate
{ {
// Needed for the helper function to read from the legacy term table // Needed for the helper function to read from the legacy term table
const OBJECT_TYPE_POST = 1; const OBJECT_TYPE_POST = 1;
const VERSION = 1550; const VERSION = 1550;
@ -138,7 +138,7 @@ class PostUpdate
} }
$max_item_delivery_data = DBA::selectFirst('item-delivery-data', ['iid'], ['queue_count > 0 OR queue_done > 0'], ['order' => ['iid']]); $max_item_delivery_data = DBA::selectFirst('item-delivery-data', ['iid'], ['queue_count > 0 OR queue_done > 0'], ['order' => ['iid']]);
$max_iid = $max_item_delivery_data['iid'] ?? 0; $max_iid = $max_item_delivery_data['iid'] ?? 0;
DI::logger()->info('Start update1297 with max iid: ' . $max_iid); DI::logger()->info('Start update1297 with max iid: ' . $max_iid);
@ -174,12 +174,19 @@ class PostUpdate
DI::logger()->info('Start'); DI::logger()->info('Start');
$contacts = DBA::p("SELECT `nurl`, `uid` FROM `contact` $contacts = DBA::p(
"SELECT `nurl`, `uid` FROM `contact`
WHERE EXISTS (SELECT `nurl` FROM `contact` AS `c2` WHERE EXISTS (SELECT `nurl` FROM `contact` AS `c2`
WHERE `c2`.`nurl` = `contact`.`nurl` AND `c2`.`id` != `contact`.`id` AND `c2`.`uid` = `contact`.`uid` AND `c2`.`network` IN (?, ?, ?) AND NOT `deleted`) WHERE `c2`.`nurl` = `contact`.`nurl` AND `c2`.`id` != `contact`.`id` AND `c2`.`uid` = `contact`.`uid` AND `c2`.`network` IN (?, ?, ?) AND NOT `deleted`)
AND (`network` IN (?, ?, ?) OR (`uid` = ?)) AND NOT `deleted` GROUP BY `nurl`, `uid`", AND (`network` IN (?, ?, ?) OR (`uid` = ?)) AND NOT `deleted` GROUP BY `nurl`, `uid`",
Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB, Protocol::DIASPORA,
Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB, 0); Protocol::OSTATUS,
Protocol::ACTIVITYPUB,
Protocol::DIASPORA,
Protocol::OSTATUS,
Protocol::ACTIVITYPUB,
0
);
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
DI::logger()->info('Remove duplicates', ['nurl' => $contact['nurl'], 'uid' => $contact['uid']]); DI::logger()->info('Remove duplicates', ['nurl' => $contact['nurl'], 'uid' => $contact['uid']]);
@ -216,11 +223,11 @@ class PostUpdate
DI::logger()->info('Start', ['item' => $id]); DI::logger()->info('Start', ['item' => $id]);
$start_id = $id; $start_id = $id;
$rows = 0; $rows = 0;
$condition = ["`id` > ?", $id]; $condition = ["`id` > ?", $id];
$params = ['order' => ['id'], 'limit' => 10000]; $params = ['order' => ['id'], 'limit' => 10000];
$items = DBA::select('item', ['id', 'uri-id', 'uid'], $condition, $params); $items = DBA::select('item', ['id', 'uri-id', 'uid'], $condition, $params);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]); DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
@ -331,12 +338,18 @@ class PostUpdate
$rows = 0; $rows = 0;
$terms = DBA::p("SELECT `term`.`tid`, `item`.`uri-id`, `term`.`type`, `term`.`term`, `term`.`url`, `item-content`.`body` $terms = DBA::p(
"SELECT `term`.`tid`, `item`.`uri-id`, `term`.`type`, `term`.`term`, `term`.`url`, `item-content`.`body`
FROM `term` FROM `term`
INNER JOIN `item` ON `item`.`id` = `term`.`oid` INNER JOIN `item` ON `item`.`id` = `term`.`oid`
INNER JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id` INNER JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id`
WHERE term.type IN (?, ?, ?, ?) AND `tid` >= ? ORDER BY `tid` LIMIT 100000", WHERE term.type IN (?, ?, ?, ?) AND `tid` >= ? ORDER BY `tid` LIMIT 100000",
Tag::HASHTAG, Tag::MENTION, Tag::EXCLUSIVE_MENTION, Tag::IMPLICIT_MENTION, $id); Tag::HASHTAG,
Tag::MENTION,
Tag::EXCLUSIVE_MENTION,
Tag::IMPLICIT_MENTION,
$id
);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]); DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
@ -345,17 +358,17 @@ class PostUpdate
while ($term = DBA::fetch($terms)) { while ($term = DBA::fetch($terms)) {
if (($term['type'] == Tag::MENTION) && !empty($term['url']) && !strstr($term['body'], $term['url'])) { if (($term['type'] == Tag::MENTION) && !empty($term['url']) && !strstr($term['body'], $term['url'])) {
$condition = ['nurl' => Strings::normaliseLink($term['url']), 'uid' => 0, 'deleted' => false]; $condition = ['nurl' => Strings::normaliseLink($term['url']), 'uid' => 0, 'deleted' => false];
$contact = DBA::selectFirst('contact', ['url', 'alias'], $condition, ['order' => ['id']]); $contact = DBA::selectFirst('contact', ['url', 'alias'], $condition, ['order' => ['id']]);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
$ssl_url = str_replace('http://', 'https://', $term['url']); $ssl_url = str_replace('http://', 'https://', $term['url']);
$condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $term['url'], Strings::normaliseLink($term['url']), $ssl_url, 0]; $condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $term['url'], Strings::normaliseLink($term['url']), $ssl_url, 0];
$contact = DBA::selectFirst('contact', ['url', 'alias'], $condition, ['order' => ['id']]); $contact = DBA::selectFirst('contact', ['url', 'alias'], $condition, ['order' => ['id']]);
} }
if (DBA::isResult($contact) && (!strstr($term['body'], $contact['url']) && (empty($contact['alias']) || !strstr($term['body'], $contact['alias'])))) { if (DBA::isResult($contact) && (!strstr($term['body'], $contact['url']) && (empty($contact['alias']) || !strstr($term['body'], $contact['alias'])))) {
$term['type'] = Tag::IMPLICIT_MENTION; $term['type'] = Tag::IMPLICIT_MENTION;
} }
} }
Tag::store($term['uri-id'], $term['type'], $term['term'], $term['url']); Tag::store($term['uri-id'], $term['type'], $term['term'], $term['url']);
@ -454,7 +467,7 @@ class PostUpdate
$file_text = ''; $file_text = '';
$condition = ['otype' => self::OBJECT_TYPE_POST, 'oid' => $item_id, 'type' => [Category::FILE, Category::CATEGORY]]; $condition = ['otype' => self::OBJECT_TYPE_POST, 'oid' => $item_id, 'type' => [Category::FILE, Category::CATEGORY]];
$tags = DBA::selectToArray('term', ['type', 'term', 'url'], $condition); $tags = DBA::selectToArray('term', ['type', 'term', 'url'], $condition);
foreach ($tags as $tag) { foreach ($tags as $tag) {
if ($tag['type'] == Category::CATEGORY) { if ($tag['type'] == Category::CATEGORY) {
$file_text .= '<' . $tag['term'] . '>'; $file_text .= '<' . $tag['term'] . '>';
@ -490,9 +503,12 @@ class PostUpdate
$rows = 0; $rows = 0;
$terms = DBA::select('term', ['oid'], $terms = DBA::select(
'term',
['oid'],
["`type` IN (?, ?) AND `oid` >= ?", Category::CATEGORY, Category::FILE, $id], ["`type` IN (?, ?) AND `oid` >= ?", Category::CATEGORY, Category::FILE, $id],
['order' => ['oid'], 'limit' => 1000, 'group_by' => ['oid']]); ['order' => ['oid'], 'limit' => 1000, 'group_by' => ['oid']]
);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]); DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
@ -557,7 +573,7 @@ class PostUpdate
DI::logger()->info('Start', ['item' => $id]); DI::logger()->info('Start', ['item' => $id]);
$start_id = $id; $start_id = $id;
$rows = 0; $rows = 0;
$items = DBA::p("SELECT `item`.`id`, `item`.`verb` AS `item-verb`, `item-content`.`verb`, `item-activity`.`activity` $items = DBA::p("SELECT `item`.`id`, `item`.`verb` AS `item-verb`, `item-content`.`verb`, `item-activity`.`activity`
FROM `item` LEFT JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id` FROM `item` LEFT JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id`
@ -570,7 +586,7 @@ class PostUpdate
} }
while ($item = DBA::fetch($items)) { while ($item = DBA::fetch($items)) {
$id = $item['id']; $id = $item['id'];
$verb = $item['item-verb']; $verb = $item['item-verb'];
if (empty($verb)) { if (empty($verb)) {
$verb = $item['verb']; $verb = $item['verb'];
@ -618,11 +634,11 @@ class PostUpdate
DI::logger()->info('Start', ['contact' => $id]); DI::logger()->info('Start', ['contact' => $id]);
$start_id = $id; $start_id = $id;
$rows = 0; $rows = 0;
$condition = ["`id` > ? AND `gsid` IS NULL AND `baseurl` != '' AND NOT `baseurl` IS NULL", $id]; $condition = ["`id` > ? AND `gsid` IS NULL AND `baseurl` != '' AND NOT `baseurl` IS NULL", $id];
$params = ['order' => ['id'], 'limit' => 10000]; $params = ['order' => ['id'], 'limit' => 10000];
$contacts = DBA::select('contact', ['id', 'baseurl'], $condition, $params); $contacts = DBA::select('contact', ['id', 'baseurl'], $condition, $params);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]); DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
@ -632,9 +648,11 @@ class PostUpdate
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
$id = $contact['id']; $id = $contact['id'];
DBA::update('contact', DBA::update(
'contact',
['gsid' => GServer::getID($contact['baseurl'], true), 'baseurl' => GServer::cleanURL($contact['baseurl'])], ['gsid' => GServer::getID($contact['baseurl'], true), 'baseurl' => GServer::cleanURL($contact['baseurl'])],
['id' => $contact['id']]); ['id' => $contact['id']]
);
++$rows; ++$rows;
} }
@ -671,10 +689,10 @@ class PostUpdate
DI::logger()->info('Start', ['apcontact' => $id]); DI::logger()->info('Start', ['apcontact' => $id]);
$start_id = $id; $start_id = $id;
$rows = 0; $rows = 0;
$condition = ["`url` > ? AND `gsid` IS NULL AND `baseurl` != '' AND NOT `baseurl` IS NULL", $id]; $condition = ["`url` > ? AND `gsid` IS NULL AND `baseurl` != '' AND NOT `baseurl` IS NULL", $id];
$params = ['order' => ['url'], 'limit' => 10000]; $params = ['order' => ['url'], 'limit' => 10000];
$apcontacts = DBA::select('apcontact', ['url', 'baseurl'], $condition, $params); $apcontacts = DBA::select('apcontact', ['url', 'baseurl'], $condition, $params);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
@ -685,9 +703,11 @@ class PostUpdate
while ($apcontact = DBA::fetch($apcontacts)) { while ($apcontact = DBA::fetch($apcontacts)) {
$id = $apcontact['url']; $id = $apcontact['url'];
DBA::update('apcontact', DBA::update(
'apcontact',
['gsid' => GServer::getID($apcontact['baseurl'], true), 'baseurl' => GServer::cleanURL($apcontact['baseurl'])], ['gsid' => GServer::getID($apcontact['baseurl'], true), 'baseurl' => GServer::cleanURL($apcontact['baseurl'])],
['url' => $apcontact['url']]); ['url' => $apcontact['url']]
);
++$rows; ++$rows;
} }
@ -723,7 +743,7 @@ class PostUpdate
DI::logger()->info('Start'); DI::logger()->info('Start');
$deleted = 0; $deleted = 0;
$avatar = [4 => 'photo', 5 => 'thumb', 6 => 'micro']; $avatar = [4 => 'photo', 5 => 'thumb', 6 => 'micro'];
$photos = DBA::select('photo', ['id', 'contact-id', 'resource-id', 'scale'], ["`contact-id` != ? AND `album` = ?", 0, Photo::CONTACT_PHOTOS]); $photos = DBA::select('photo', ['id', 'contact-id', 'resource-id', 'scale'], ["`contact-id` != ? AND `album` = ?", 0, Photo::CONTACT_PHOTOS]);
while ($photo = DBA::fetch($photos)) { while ($photo = DBA::fetch($photos)) {
@ -764,7 +784,7 @@ class PostUpdate
$condition = ["`hash` IS NULL"]; $condition = ["`hash` IS NULL"];
DI::logger()->info('Start', ['rest' => DBA::count('photo', $condition)]); DI::logger()->info('Start', ['rest' => DBA::count('photo', $condition)]);
$rows = 0; $rows = 0;
$photos = DBA::select('photo', [], $condition, ['limit' => 100]); $photos = DBA::select('photo', [], $condition, ['limit' => 100]);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
@ -814,7 +834,7 @@ class PostUpdate
$condition = ["`extid` != ? AND EXISTS(SELECT `id` FROM `post-user` WHERE `uri-id` = `item`.`uri-id` AND `uid` = `item`.`uid` AND `external-id` IS NULL)", '']; $condition = ["`extid` != ? AND EXISTS(SELECT `id` FROM `post-user` WHERE `uri-id` = `item`.`uri-id` AND `uid` = `item`.`uid` AND `external-id` IS NULL)", ''];
DI::logger()->info('Start', ['rest' => DBA::count('item', $condition)]); DI::logger()->info('Start', ['rest' => DBA::count('item', $condition)]);
$rows = 0; $rows = 0;
$items = DBA::select('item', ['uri-id', 'uid', 'extid'], $condition, ['order' => ['id'], 'limit' => 10000]); $items = DBA::select('item', ['uri-id', 'uid', 'extid'], $condition, ['order' => ['id'], 'limit' => 10000]);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
@ -856,7 +876,7 @@ class PostUpdate
$condition = ["`uri-id` IS NULL"]; $condition = ["`uri-id` IS NULL"];
DI::logger()->info('Start', ['rest' => DBA::count('contact', $condition)]); DI::logger()->info('Start', ['rest' => DBA::count('contact', $condition)]);
$rows = 0; $rows = 0;
$contacts = DBA::select('contact', ['id', 'url'], $condition, ['limit' => 1000]); $contacts = DBA::select('contact', ['id', 'url'], $condition, ['limit' => 1000]);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
@ -903,7 +923,7 @@ class PostUpdate
$condition = ["`uri-id` IS NULL"]; $condition = ["`uri-id` IS NULL"];
DI::logger()->info('Start', ['rest' => DBA::count('fcontact', $condition)]); DI::logger()->info('Start', ['rest' => DBA::count('fcontact', $condition)]);
$rows = 0; $rows = 0;
$fcontacts = DBA::select('fcontact', ['id', 'url', 'guid'], $condition, ['limit' => 1000]); $fcontacts = DBA::select('fcontact', ['id', 'url', 'guid'], $condition, ['limit' => 1000]);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
@ -950,7 +970,7 @@ class PostUpdate
$condition = ["`uri-id` IS NULL"]; $condition = ["`uri-id` IS NULL"];
DI::logger()->info('Start', ['rest' => DBA::count('apcontact', $condition)]); DI::logger()->info('Start', ['rest' => DBA::count('apcontact', $condition)]);
$rows = 0; $rows = 0;
$apcontacts = DBA::select('apcontact', ['url', 'uuid'], $condition, ['limit' => 1000]); $apcontacts = DBA::select('apcontact', ['url', 'uuid'], $condition, ['limit' => 1000]);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
@ -997,7 +1017,7 @@ class PostUpdate
$condition = ["`uri-id` IS NULL"]; $condition = ["`uri-id` IS NULL"];
DI::logger()->info('Start', ['rest' => DBA::count('event', $condition)]); DI::logger()->info('Start', ['rest' => DBA::count('event', $condition)]);
$rows = 0; $rows = 0;
$events = DBA::select('event', ['id', 'uri', 'guid'], $condition, ['limit' => 1000]); $events = DBA::select('event', ['id', 'uri', 'guid'], $condition, ['limit' => 1000]);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
@ -1053,10 +1073,14 @@ class PostUpdate
$rows = 0; $rows = 0;
$received = ''; $received = '';
$conversations = DBA::p("SELECT `post-view`.`uri-id`, `conversation`.`source`, `conversation`.`received` FROM `conversation` $conversations = DBA::p(
"SELECT `post-view`.`uri-id`, `conversation`.`source`, `conversation`.`received` FROM `conversation`
INNER JOIN `post-view` ON `post-view`.`uri` = `conversation`.`item-uri` INNER JOIN `post-view` ON `post-view`.`uri` = `conversation`.`item-uri`
WHERE NOT `source` IS NULL AND `conversation`.`protocol` = ? AND `uri-id` > ? LIMIT ?", WHERE NOT `source` IS NULL AND `conversation`.`protocol` = ? AND `uri-id` > ? LIMIT ?",
Conversation::PARCEL_ACTIVITYPUB, $id, 1000); Conversation::PARCEL_ACTIVITYPUB,
$id,
1000
);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]); DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
@ -1199,11 +1223,11 @@ class PostUpdate
DI::logger()->info('Start', ['contact' => $id]); DI::logger()->info('Start', ['contact' => $id]);
$start_id = $id; $start_id = $id;
$rows = 0; $rows = 0;
$condition = ["`id` > ? AND `gsid` IS NULL AND `network` = ?", $id, Protocol::DIASPORA]; $condition = ["`id` > ? AND `gsid` IS NULL AND `network` = ?", $id, Protocol::DIASPORA];
$params = ['order' => ['id'], 'limit' => 10000]; $params = ['order' => ['id'], 'limit' => 10000];
$contacts = DBA::select('contact', ['id', 'url'], $condition, $params); $contacts = DBA::select('contact', ['id', 'url'], $condition, $params);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]); DI::logger()->error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
@ -1217,9 +1241,11 @@ class PostUpdate
unset($parts['path']); unset($parts['path']);
$server = (string)Uri::fromParts($parts); $server = (string)Uri::fromParts($parts);
DBA::update('contact', DBA::update(
'contact',
['gsid' => GServer::getID($server, true), 'baseurl' => GServer::cleanURL($server)], ['gsid' => GServer::getID($server, true), 'baseurl' => GServer::cleanURL($server)],
['id' => $contact['id']]); ['id' => $contact['id']]
);
++$rows; ++$rows;
} }
@ -1256,10 +1282,10 @@ class PostUpdate
DI::logger()->info('Start', ['apcontact' => $id]); DI::logger()->info('Start', ['apcontact' => $id]);
$start_id = $id; $start_id = $id;
$rows = 0; $rows = 0;
$condition = ["`url` > ? AND NOT `gsid` IS NULL", $id]; $condition = ["`url` > ? AND NOT `gsid` IS NULL", $id];
$params = ['order' => ['url'], 'limit' => 10000]; $params = ['order' => ['url'], 'limit' => 10000];
$apcontacts = DBA::select('apcontact', ['url', 'gsid', 'sharedinbox', 'inbox'], $condition, $params); $apcontacts = DBA::select('apcontact', ['url', 'gsid', 'sharedinbox', 'inbox'], $condition, $params);
if (DBA::errorNo() != 0) { if (DBA::errorNo() != 0) {
@ -1310,7 +1336,7 @@ class PostUpdate
$id = (int)(DI::keyValue()->get('post_update_version_1544_id') ?? 0); $id = (int)(DI::keyValue()->get('post_update_version_1544_id') ?? 0);
if ($id == 0) { if ($id == 0) {
$post = Post::selectFirstPost(['uri-id'], [], ['order' => ['uri-id' => true]]); $post = Post::selectFirstPost(['uri-id'], [], ['order' => ['uri-id' => true]]);
$id = (int)($post['uri-id'] ?? 0); $id = (int)($post['uri-id'] ?? 0);
} }
DI::logger()->info('Start', ['uri-id' => $id]); DI::logger()->info('Start', ['uri-id' => $id]);
@ -1375,7 +1401,7 @@ class PostUpdate
$id = (int)(DI::keyValue()->get('post_update_version_1550_id') ?? 0); $id = (int)(DI::keyValue()->get('post_update_version_1550_id') ?? 0);
if ($id == 0) { if ($id == 0) {
$post = Post::selectFirstPost(['uri-id'], [], ['order' => ['uri-id' => true]]); $post = Post::selectFirstPost(['uri-id'], [], ['order' => ['uri-id' => true]]);
$id = (int)($post['uri-id'] ?? 0); $id = (int)($post['uri-id'] ?? 0);
} }
DI::logger()->info('Start', ['uri-id' => $id]); DI::logger()->info('Start', ['uri-id' => $id]);

View file

@ -221,8 +221,8 @@ class Status extends BaseFactory
$application = new \Friendica\Object\Api\Mastodon\Application($item['app'] ?: $platform); $application = new \Friendica\Object\Api\Mastodon\Application($item['app'] ?: $platform);
$mentions = $this->mstdnMentionFactory->createFromUriId($uriId)->getArrayCopy(); $mentions = $this->mstdnMentionFactory->createFromUriId($uriId)->getArrayCopy();
$tags = $this->mstdnTagFactory->createFromUriId($uriId); $tags = $this->mstdnTagFactory->createFromUriId($uriId);
if ($item['has-media']) { if ($item['has-media']) {
$card = $this->mstdnCardFactory->createFromUriId($uriId); $card = $this->mstdnCardFactory->createFromUriId($uriId);
$attachments = $this->mstdnAttachmentFactory->createFromUriId($uriId); $attachments = $this->mstdnAttachmentFactory->createFromUriId($uriId);
@ -349,7 +349,7 @@ class Status extends BaseFactory
$quote_id = $media['media-uri-id']; $quote_id = $media['media-uri-id'];
} else { } else {
$shared_item = Post::selectFirst(['uri-id'], ['plink' => $media[0]['url'], 'uid' => [$uid, 0]]); $shared_item = Post::selectFirst(['uri-id'], ['plink' => $media[0]['url'], 'uid' => [$uid, 0]]);
$quote_id = $shared_item['uri-id'] ?? 0; $quote_id = $shared_item['uri-id'] ?? 0;
} }
} }
} else { } else {

View file

@ -13,7 +13,6 @@ use Friendica\Core\Protocol;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Item;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Network\Probe; use Friendica\Network\Probe;
use Friendica\Protocol\ActivityNamespace; use Friendica\Protocol\ActivityNamespace;
@ -153,7 +152,7 @@ class APContact
// Detect multiple fast repeating request to the same address // Detect multiple fast repeating request to the same address
// See https://github.com/friendica/friendica/issues/9303 // See https://github.com/friendica/friendica/issues/9303
$cachekey = 'apcontact:' . ItemURI::getIdByURI($url); $cachekey = 'apcontact:' . ItemURI::getIdByURI($url);
$result = DI::cache()->get($cachekey); $result = DI::cache()->get($cachekey);
if (!is_null($result)) { if (!is_null($result)) {
DI::logger()->info('Multiple requests for the address', ['url' => $url, 'update' => $update, 'result' => $result]); DI::logger()->info('Multiple requests for the address', ['url' => $url, 'update' => $update, 'result' => $result]);
if (!empty($fetched_contact)) { if (!empty($fetched_contact)) {
@ -165,7 +164,7 @@ class APContact
if (DI::baseUrl()->isLocalUrl($url) && ($local_uid = User::getIdForURL($url))) { if (DI::baseUrl()->isLocalUrl($url) && ($local_uid = User::getIdForURL($url))) {
try { try {
$data = Transmitter::getProfile($local_uid); $data = Transmitter::getProfile($local_uid);
$local_owner = User::getOwnerDataById($local_uid); $local_owner = User::getOwnerDataById($local_uid);
} catch(HTTPException\NotFoundException $e) { } catch(HTTPException\NotFoundException $e) {
$data = null; $data = null;
@ -177,11 +176,11 @@ class APContact
try { try {
$curlResult = HTTPSignature::fetchRaw($url); $curlResult = HTTPSignature::fetchRaw($url);
$failed = empty($curlResult->getBodyString()) || $failed = empty($curlResult->getBodyString()) ||
(!$curlResult->isSuccess() && ($curlResult->getReturnCode() != 410)); (!$curlResult->isSuccess() && ($curlResult->getReturnCode() != 410));
if (!$failed) { if (!$failed) {
$data = json_decode($curlResult->getBodyString(), true); $data = json_decode($curlResult->getBodyString(), true);
$failed = empty($data) || !is_array($data); $failed = empty($data) || !is_array($data);
} }
@ -206,13 +205,13 @@ class APContact
return $fetched_contact; return $fetched_contact;
} }
$apcontact['url'] = $compacted['@id']; $apcontact['url'] = $compacted['@id'];
$apcontact['uuid'] = JsonLD::fetchElement($compacted, 'diaspora:guid', '@value'); $apcontact['uuid'] = JsonLD::fetchElement($compacted, 'diaspora:guid', '@value');
$apcontact['type'] = str_replace('as:', '', JsonLD::fetchElement($compacted, '@type')); $apcontact['type'] = str_replace('as:', '', JsonLD::fetchElement($compacted, '@type'));
$apcontact['following'] = JsonLD::fetchElement($compacted, 'as:following', '@id'); $apcontact['following'] = JsonLD::fetchElement($compacted, 'as:following', '@id');
$apcontact['followers'] = JsonLD::fetchElement($compacted, 'as:followers', '@id'); $apcontact['followers'] = JsonLD::fetchElement($compacted, 'as:followers', '@id');
$apcontact['inbox'] = (JsonLD::fetchElement($compacted, 'ldp:inbox', '@id') ?? ''); $apcontact['inbox'] = (JsonLD::fetchElement($compacted, 'ldp:inbox', '@id') ?? '');
$apcontact['outbox'] = JsonLD::fetchElement($compacted, 'as:outbox', '@id'); $apcontact['outbox'] = JsonLD::fetchElement($compacted, 'as:outbox', '@id');
$apcontact['sharedinbox'] = ''; $apcontact['sharedinbox'] = '';
if (!empty($compacted['as:endpoints'])) { if (!empty($compacted['as:endpoints'])) {
@ -303,12 +302,12 @@ class APContact
} }
} }
$apcontact['manually-approve'] = (int)JsonLD::fetchElement($compacted, 'as:manuallyApprovesFollowers'); $apcontact['manually-approve'] = (int)JsonLD::fetchElement($compacted, 'as:manuallyApprovesFollowers');
$apcontact['posting-restricted'] = (int)JsonLD::fetchElement($compacted, 'lemmy:postingRestrictedToMods'); $apcontact['posting-restricted'] = (int)JsonLD::fetchElement($compacted, 'lemmy:postingRestrictedToMods');
$apcontact['suspended'] = (int)JsonLD::fetchElement($compacted, 'toot:suspended'); $apcontact['suspended'] = (int)JsonLD::fetchElement($compacted, 'toot:suspended');
if (!empty($compacted['as:generator'])) { if (!empty($compacted['as:generator'])) {
$apcontact['baseurl'] = JsonLD::fetchElement($compacted['as:generator'], 'as:url', '@id'); $apcontact['baseurl'] = JsonLD::fetchElement($compacted['as:generator'], 'as:url', '@id');
$apcontact['generator'] = JsonLD::fetchElement($compacted['as:generator'], 'as:name', '@value'); $apcontact['generator'] = JsonLD::fetchElement($compacted['as:generator'], 'as:name', '@value');
} }
@ -348,7 +347,7 @@ class APContact
if (!empty($local_owner)) { if (!empty($local_owner)) {
$statuses_count = self::getStatusesCount($local_owner); $statuses_count = self::getStatusesCount($local_owner);
} else { } else {
$outbox = HTTPSignature::fetch($apcontact['outbox']); $outbox = HTTPSignature::fetch($apcontact['outbox']);
$statuses_count = $outbox['totalItems'] ?? 0; $statuses_count = $outbox['totalItems'] ?? 0;
} }
if (!empty($statuses_count)) { if (!empty($statuses_count)) {

View file

@ -23,7 +23,7 @@ use Friendica\Protocol\ActivityPub;
class Circle class Circle
{ {
const FOLLOWERS = '~'; const FOLLOWERS = '~';
const MUTUALS = '&'; const MUTUALS = '&';
/** /**
* Fetches circle record by user id and maybe includes deleted circles as well * Fetches circle record by user id and maybe includes deleted circles as well
@ -163,7 +163,8 @@ class Circle
*/ */
public static function countUnseen(int $uid) public static function countUnseen(int $uid)
{ {
$stmt = DBA::p("SELECT `circle`.`id`, `circle`.`name`, $stmt = DBA::p(
"SELECT `circle`.`id`, `circle`.`name`,
(SELECT COUNT(*) FROM `post-user` (SELECT COUNT(*) FROM `post-user`
WHERE `uid` = ? WHERE `uid` = ?
AND `unseen` AND `unseen`
@ -230,15 +231,15 @@ class Circle
if ($user['def_gid'] == $gid) { if ($user['def_gid'] == $gid) {
$user['def_gid'] = 0; $user['def_gid'] = 0;
$change = true; $change = true;
} }
if (strpos($user['allow_gid'], '<' . $gid . '>') !== false) { if (strpos($user['allow_gid'], '<' . $gid . '>') !== false) {
$user['allow_gid'] = str_replace('<' . $gid . '>', '', $user['allow_gid']); $user['allow_gid'] = str_replace('<' . $gid . '>', '', $user['allow_gid']);
$change = true; $change = true;
} }
if (strpos($user['deny_gid'], '<' . $gid . '>') !== false) { if (strpos($user['deny_gid'], '<' . $gid . '>') !== false) {
$user['deny_gid'] = str_replace('<' . $gid . '>', '', $user['deny_gid']); $user['deny_gid'] = str_replace('<' . $gid . '>', '', $user['deny_gid']);
$change = true; $change = true;
} }
if ($change) { if ($change) {
@ -410,13 +411,13 @@ class Circle
if ($key !== false) { if ($key !== false) {
if ($expand_followers) { if ($expand_followers) {
$followers = Contact::selectToArray(['id'], [ $followers = Contact::selectToArray(['id'], [
'uid' => $uid, 'uid' => $uid,
'rel' => [Contact::FOLLOWER, Contact::FRIEND], 'rel' => [Contact::FOLLOWER, Contact::FRIEND],
'network' => $networks, 'network' => $networks,
'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON, Contact::TYPE_NEWS, Contact::TYPE_ORGANISATION], 'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON, Contact::TYPE_NEWS, Contact::TYPE_ORGANISATION],
'archive' => false, 'archive' => false,
'pending' => false, 'pending' => false,
'blocked' => false, 'blocked' => false,
]); ]);
foreach ($followers as $follower) { foreach ($followers as $follower) {
@ -431,13 +432,13 @@ class Circle
$key = array_search(self::MUTUALS, $circle_ids); $key = array_search(self::MUTUALS, $circle_ids);
if ($key !== false) { if ($key !== false) {
$mutuals = Contact::selectToArray(['id'], [ $mutuals = Contact::selectToArray(['id'], [
'uid' => $uid, 'uid' => $uid,
'rel' => [Contact::FRIEND], 'rel' => [Contact::FRIEND],
'network' => $networks, 'network' => $networks,
'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON], 'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON],
'archive' => false, 'archive' => false,
'pending' => false, 'pending' => false,
'blocked' => false, 'blocked' => false,
]); ]);
foreach ($mutuals as $mutual) { foreach ($mutuals as $mutual) {
@ -478,8 +479,8 @@ class Circle
{ {
$display_circles = [ $display_circles = [
[ [
'name' => '', 'name' => '',
'id' => '0', 'id' => '0',
'selected' => '' 'selected' => ''
] ]
]; ];
@ -487,8 +488,8 @@ class Circle
$stmt = DBA::select('group', [], ['deleted' => false, 'uid' => $uid, 'cid' => null], ['order' => ['name']]); $stmt = DBA::select('group', [], ['deleted' => false, 'uid' => $uid, 'cid' => null], ['order' => ['name']]);
while ($circle = DBA::fetch($stmt)) { while ($circle = DBA::fetch($stmt)) {
$display_circles[] = [ $display_circles[] = [
'name' => $circle['name'], 'name' => $circle['name'],
'id' => $circle['id'], 'id' => $circle['id'],
'selected' => $gid == $circle['id'] ? 'true' : '' 'selected' => $gid == $circle['id'] ? 'true' : ''
]; ];
} }
@ -497,8 +498,8 @@ class Circle
DI::logger()->info('Got circles', $display_circles); DI::logger()->info('Got circles', $display_circles);
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('circle_selection.tpl'), [ $o = Renderer::replaceMacros(Renderer::getMarkupTemplate('circle_selection.tpl'), [
'$id' => $id, '$id' => $id,
'$label' => $label, '$label' => $label,
'$circles' => $display_circles '$circles' => $display_circles
]); ]);
return $o; return $o;
@ -526,10 +527,10 @@ class Circle
$display_circles = [ $display_circles = [
[ [
'text' => DI::l10n()->t('Everybody'), 'text' => DI::l10n()->t('Everybody'),
'id' => 0, 'id' => 0,
'selected' => (($circle_id === 'everyone') ? 'circle-selected' : ''), 'selected' => (($circle_id === 'everyone') ? 'circle-selected' : ''),
'href' => $every, 'href' => $every,
] ]
]; ];
@ -544,7 +545,7 @@ class Circle
if ($editmode == 'full') { if ($editmode == 'full') {
$circleedit = [ $circleedit = [
'href' => 'circle/' . $circle['id'], 'href' => 'circle/' . $circle['id'],
'title' => DI::l10n()->t('edit'), 'title' => DI::l10n()->t('edit'),
]; ];
} else { } else {
@ -552,23 +553,23 @@ class Circle
} }
if ($each == 'circle') { if ($each == 'circle') {
$networks = Widget::unavailableNetworks(); $networks = Widget::unavailableNetworks();
$sql_values = array_merge([$circle['id']], $networks); $sql_values = array_merge([$circle['id']], $networks);
$condition = ["`circle-id` = ? AND NOT `contact-network` IN (" . substr(str_repeat("?, ", count($networks)), 0, -2) . ")"]; $condition = ["`circle-id` = ? AND NOT `contact-network` IN (" . substr(str_repeat("?, ", count($networks)), 0, -2) . ")"];
$condition = array_merge($condition, $sql_values); $condition = array_merge($condition, $sql_values);
$count = DBA::count('circle-member-view', $condition); $count = DBA::count('circle-member-view', $condition);
$circle_name = sprintf('%s (%d)', $circle['name'], $count); $circle_name = sprintf('%s (%d)', $circle['name'], $count);
} else { } else {
$circle_name = $circle['name']; $circle_name = $circle['name'];
} }
$display_circles[] = [ $display_circles[] = [
'id' => $circle['id'], 'id' => $circle['id'],
'cid' => $cid, 'cid' => $cid,
'text' => $circle_name, 'text' => $circle_name,
'href' => $each . '/' . $circle['id'], 'href' => $each . '/' . $circle['id'],
'edit' => $circleedit, 'edit' => $circleedit,
'selected' => $selected, 'selected' => $selected,
'ismember' => in_array($circle['id'], $member_of), 'ismember' => in_array($circle['id'], $member_of),
]; ];
@ -581,18 +582,18 @@ class Circle
} }
$tpl = Renderer::getMarkupTemplate('circle_side.tpl'); $tpl = Renderer::getMarkupTemplate('circle_side.tpl');
$o = Renderer::replaceMacros($tpl, [ $o = Renderer::replaceMacros($tpl, [
'$add' => DI::l10n()->t('add'), '$add' => DI::l10n()->t('add'),
'$title' => DI::l10n()->t('Circles'), '$title' => DI::l10n()->t('Circles'),
'$circles' => $display_circles, '$circles' => $display_circles,
'$new_circle' => $editmode == 'extended' || $editmode == 'full' ? 1 : '', '$new_circle' => $editmode == 'extended' || $editmode == 'full' ? 1 : '',
'$circle_page' => 'circle/', '$circle_page' => 'circle/',
'$edittext' => DI::l10n()->t('Edit circle'), '$edittext' => DI::l10n()->t('Edit circle'),
'$uncircled' => $every === 'contact' ? DI::l10n()->t('Contacts not in any circle') : '', '$uncircled' => $every === 'contact' ? DI::l10n()->t('Contacts not in any circle') : '',
'$uncircled_selected' => (($circle_id === 'none') ? 'circle-selected' : ''), '$uncircled_selected' => (($circle_id === 'none') ? 'circle-selected' : ''),
'$createtext' => DI::l10n()->t('Create a new circle'), '$createtext' => DI::l10n()->t('Create a new circle'),
'$create_circle' => DI::l10n()->t('Circle Name: '), '$create_circle' => DI::l10n()->t('Circle Name: '),
'$edit_circles_text' => DI::l10n()->t('Edit circles'), '$edit_circles_text' => DI::l10n()->t('Edit circles'),
'$form_security_token' => BaseModule::getFormSecurityToken('circle_edit'), '$form_security_token' => BaseModule::getFormSecurityToken('circle_edit'),
]); ]);

View file

@ -74,12 +74,12 @@ class Contact
* This will only be assigned to contacts, not to user accounts * This will only be assigned to contacts, not to user accounts
* @{ * @{
*/ */
const TYPE_UNKNOWN = -1; const TYPE_UNKNOWN = -1;
const TYPE_PERSON = User::ACCOUNT_TYPE_PERSON; const TYPE_PERSON = User::ACCOUNT_TYPE_PERSON;
const TYPE_ORGANISATION = User::ACCOUNT_TYPE_ORGANISATION; const TYPE_ORGANISATION = User::ACCOUNT_TYPE_ORGANISATION;
const TYPE_NEWS = User::ACCOUNT_TYPE_NEWS; const TYPE_NEWS = User::ACCOUNT_TYPE_NEWS;
const TYPE_COMMUNITY = User::ACCOUNT_TYPE_COMMUNITY; const TYPE_COMMUNITY = User::ACCOUNT_TYPE_COMMUNITY;
const TYPE_RELAY = User::ACCOUNT_TYPE_RELAY; const TYPE_RELAY = User::ACCOUNT_TYPE_RELAY;
/** /**
* @} * @}
*/ */
@ -335,7 +335,7 @@ class Contact
if (!empty($fields)) { if (!empty($fields)) {
foreach (['id', 'next-update', 'network', 'local-data'] as $internal) { foreach (['id', 'next-update', 'network', 'local-data'] as $internal) {
if (!in_array($internal, $fields)) { if (!in_array($internal, $fields)) {
$fields[] = $internal; $fields[] = $internal;
$removal[] = $internal; $removal[] = $internal;
} }
} }
@ -353,9 +353,9 @@ class Contact
// Then the alias (which could be anything) // Then the alias (which could be anything)
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
// The link could be provided as http although we stored it as https // The link could be provided as http although we stored it as https
$ssl_url = str_replace('http://', 'https://', $url); $ssl_url = str_replace('http://', 'https://', $url);
$condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $url, Strings::normaliseLink($url), $ssl_url, $uid]; $condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $url, Strings::normaliseLink($url), $ssl_url, $uid];
$contact = DBA::selectFirst('contact', $fields, $condition, $options); $contact = DBA::selectFirst('contact', $fields, $condition, $options);
} }
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
@ -837,7 +837,7 @@ class Contact
} }
$fields = ['uid', 'username', 'nickname', 'page-flags', 'account-type', 'prvkey', 'pubkey']; $fields = ['uid', 'username', 'nickname', 'page-flags', 'account-type', 'prvkey', 'pubkey'];
$user = DBA::selectFirst('user', $fields, ['uid' => $uid, 'verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false]); $user = DBA::selectFirst('user', $fields, ['uid' => $uid, 'verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false]);
if (!DBA::isResult($user)) { if (!DBA::isResult($user)) {
return false; return false;
} }
@ -868,11 +868,11 @@ class Contact
'network' => Protocol::DFRN, 'network' => Protocol::DFRN,
'url' => $url, 'url' => $url,
// it seems as if ported accounts can have wrong values, so we make sure that now everything is fine. // it seems as if ported accounts can have wrong values, so we make sure that now everything is fine.
'nurl' => Strings::normaliseLink($url), 'nurl' => Strings::normaliseLink($url),
'uri-id' => ItemURI::getIdByURI($url), 'uri-id' => ItemURI::getIdByURI($url),
'addr' => $user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3), 'addr' => $user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3),
'notify' => DI::baseUrl() . '/dfrn_notify/' . $user['nickname'], 'notify' => DI::baseUrl() . '/dfrn_notify/' . $user['nickname'],
'poll' => DI::baseUrl() . '/feed/' . $user['nickname'], 'poll' => DI::baseUrl() . '/feed/' . $user['nickname'],
]; ];
$avatar = Photo::selectFirst(['resource-id', 'type'], ['uid' => $uid, 'profile' => true]); $avatar = Photo::selectFirst(['resource-id', 'type'], ['uid' => $uid, 'profile' => true]);
@ -897,14 +897,14 @@ class Contact
$fields['micro'] = self::getDefaultAvatar($fields, Proxy::SIZE_MICRO); $fields['micro'] = self::getDefaultAvatar($fields, Proxy::SIZE_MICRO);
} }
$fields['avatar'] = User::getAvatarUrl($user); $fields['avatar'] = User::getAvatarUrl($user);
$fields['header'] = User::getBannerUrl($user); $fields['header'] = User::getBannerUrl($user);
$fields['forum'] = in_array($user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN]); $fields['forum'] = in_array($user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN]);
$fields['prv'] = $user['page-flags'] == User::PAGE_FLAGS_PRVGROUP; $fields['prv'] = $user['page-flags'] == User::PAGE_FLAGS_PRVGROUP;
$fields['unsearchable'] = !$profile['net-publish']; $fields['unsearchable'] = !$profile['net-publish'];
$fields['manually-approve'] = in_array($user['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP, User::PAGE_FLAGS_COMM_MAN]); $fields['manually-approve'] = in_array($user['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP, User::PAGE_FLAGS_COMM_MAN]);
$fields['baseurl'] = DI::baseUrl(); $fields['baseurl'] = DI::baseUrl();
$fields['gsid'] = GServer::getID($fields['baseurl'], true); $fields['gsid'] = GServer::getID($fields['baseurl'], true);
$update = false; $update = false;
@ -1083,7 +1083,7 @@ class Contact
public static function markForArchival(array $contact) public static function markForArchival(array $contact)
{ {
if ((!isset($contact['uri-id']) || !isset($contact['url']) || !isset($contact['archive']) || !isset($contact['self']) || !isset($contact['term-date'])) && !empty($contact['id'])) { if ((!isset($contact['uri-id']) || !isset($contact['url']) || !isset($contact['archive']) || !isset($contact['self']) || !isset($contact['term-date'])) && !empty($contact['id'])) {
$fields = ['id', 'uri-id', 'url', 'archive', 'self', 'term-date']; $fields = ['id', 'uri-id', 'url', 'archive', 'self', 'term-date'];
$contact = DBA::selectFirst('contact', $fields, ['id' => $contact['id']]); $contact = DBA::selectFirst('contact', $fields, ['id' => $contact['id']]);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
return; return;
@ -1135,7 +1135,7 @@ class Contact
{ {
// Always unarchive the relay contact entry // Always unarchive the relay contact entry
if (!empty($contact['batch']) && !empty($contact['term-date']) && ($contact['term-date'] > DBA::NULL_DATETIME)) { if (!empty($contact['batch']) && !empty($contact['term-date']) && ($contact['term-date'] > DBA::NULL_DATETIME)) {
$fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false, 'unsearchable' => true]; $fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false, 'unsearchable' => true];
$condition = ['uid' => 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY]; $condition = ['uid' => 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY];
if (!DBA::exists('contact', array_merge($condition, $fields))) { if (!DBA::exists('contact', array_merge($condition, $fields))) {
self::update($fields, $condition); self::update($fields, $condition);
@ -1149,7 +1149,7 @@ class Contact
} }
if ((!isset($contact['url']) || !isset($contact['uri-id'])) && !empty($contact['id'])) { if ((!isset($contact['url']) || !isset($contact['uri-id'])) && !empty($contact['id'])) {
$fields = ['id', 'uri-id', 'url', 'batch', 'term-date']; $fields = ['id', 'uri-id', 'url', 'batch', 'term-date'];
$contact = DBA::selectFirst('contact', $fields, ['id' => $contact['id']]); $contact = DBA::selectFirst('contact', $fields, ['id' => $contact['id']]);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
return; return;
@ -1205,11 +1205,11 @@ class Contact
if ($contact['contact-type'] == Contact::TYPE_COMMUNITY) { if ($contact['contact-type'] == Contact::TYPE_COMMUNITY) {
$mention_label = DI::l10n()->t('Post to group'); $mention_label = DI::l10n()->t('Post to group');
$mention_url = 'compose/0?body=!' . $contact['addr']; $mention_url = 'compose/0?body=!' . $contact['addr'];
$network_label = DI::l10n()->t('View group'); $network_label = DI::l10n()->t('View group');
} else { } else {
$mention_label = DI::l10n()->t('Mention'); $mention_label = DI::l10n()->t('Mention');
$mention_url = 'compose/0?body=@' . $contact['addr']; $mention_url = 'compose/0?body=@' . $contact['addr'];
$network_label = DI::l10n()->t('Network Posts'); $network_label = DI::l10n()->t('Network Posts');
} }
$network_url = 'contact/' . $contact['id'] . '/conversations'; $network_url = 'contact/' . $contact['id'] . '/conversations';
@ -1369,10 +1369,10 @@ class Contact
if (DBA::isResult($personal_contact) && !Probe::isProbable($personal_contact['network'])) { if (DBA::isResult($personal_contact) && !Probe::isProbable($personal_contact['network'])) {
DI::logger()->info('Take contact data from personal contact', ['url' => $url, 'update' => $update, 'contact' => $personal_contact]); DI::logger()->info('Take contact data from personal contact', ['url' => $url, 'update' => $update, 'contact' => $personal_contact]);
$data = $personal_contact; $data = $personal_contact;
$data['photo'] = $personal_contact['avatar']; $data['photo'] = $personal_contact['avatar'];
$data['account-type'] = $personal_contact['contact-type']; $data['account-type'] = $personal_contact['contact-type'];
$data['hide'] = $personal_contact['unsearchable']; $data['hide'] = $personal_contact['unsearchable'];
unset($data['avatar']); unset($data['avatar']);
unset($data['contact-type']); unset($data['contact-type']);
unset($data['unsearchable']); unset($data['unsearchable']);
@ -1404,17 +1404,17 @@ class Contact
if (!$contact_id) { if (!$contact_id) {
// We only insert the basic data. The rest will be done in "updateFromProbeArray" // We only insert the basic data. The rest will be done in "updateFromProbeArray"
$fields = [ $fields = [
'uid' => $uid, 'uid' => $uid,
'url' => $data['url'], 'url' => $data['url'],
'baseurl' => $data['baseurl'] ?? '', 'baseurl' => $data['baseurl'] ?? '',
'nurl' => Strings::normaliseLink($data['url']), 'nurl' => Strings::normaliseLink($data['url']),
'network' => $data['network'], 'network' => $data['network'],
'created' => DateTimeFormat::utcNow(), 'created' => DateTimeFormat::utcNow(),
'rel' => self::SHARING, 'rel' => self::SHARING,
'writable' => 1, 'writable' => 1,
'blocked' => 0, 'blocked' => 0,
'readonly' => 0, 'readonly' => 0,
'pending' => 0, 'pending' => 0,
]; ];
$condition = ['nurl' => Strings::normaliseLink($data['url']), 'uid' => $uid, 'deleted' => false]; $condition = ['nurl' => Strings::normaliseLink($data['url']), 'uid' => $uid, 'deleted' => false];
@ -1627,13 +1627,13 @@ class Contact
if (DI::pConfig()->get($uid, 'system', 'infinite_scroll')) { if (DI::pConfig()->get($uid, 'system', 'infinite_scroll')) {
$tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl'); $tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl');
$o = Renderer::replaceMacros($tpl, ['$reload_uri' => DI::args()->getQueryString()]); $o = Renderer::replaceMacros($tpl, ['$reload_uri' => DI::args()->getQueryString()]);
} else { } else {
$o = ''; $o = '';
} }
$fields = array_merge(Item::DISPLAY_FIELDLIST, ['featured']); $fields = array_merge(Item::DISPLAY_FIELDLIST, ['featured']);
$items = Post::toArray(Post::selectForUser($uid, $fields, $condition, $params)); $items = Post::toArray(Post::selectForUser($uid, $fields, $condition, $params));
$o .= DI::conversation()->render($items, ConversationContent::MODE_CONTACT_POSTS); $o .= DI::conversation()->render($items, ConversationContent::MODE_CONTACT_POSTS);
@ -1690,7 +1690,7 @@ class Contact
if (DI::pConfig()->get($uid, 'system', 'infinite_scroll')) { if (DI::pConfig()->get($uid, 'system', 'infinite_scroll')) {
$tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl'); $tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl');
$o = Renderer::replaceMacros($tpl, ['$reload_uri' => DI::args()->getQueryString()]); $o = Renderer::replaceMacros($tpl, ['$reload_uri' => DI::args()->getQueryString()]);
} else { } else {
$o = ''; $o = '';
} }
@ -1706,7 +1706,7 @@ class Contact
$sql2 = "SELECT `thr-parent-id` AS `uri-id`, `created` FROM `post-user-view` WHERE " . array_shift($condition2); $sql2 = "SELECT `thr-parent-id` AS `uri-id`, `created` FROM `post-user-view` WHERE " . array_shift($condition2);
$union = array_merge($condition1, $condition2); $union = array_merge($condition1, $condition2);
$sql = $sql1 . " UNION " . $sql2; $sql = $sql1 . " UNION " . $sql2;
$sql .= " ORDER BY `created` DESC LIMIT ?, ?"; $sql .= " ORDER BY `created` DESC LIMIT ?, ?";
$union = array_merge($union, [$pager->getStart(), $pager->getItemsPerPage()]); $union = array_merge($union, [$pager->getStart(), $pager->getItemsPerPage()]);
@ -1715,7 +1715,7 @@ class Contact
if (empty($last_created) && ($pager->getStart() == 0)) { if (empty($last_created) && ($pager->getStart() == 0)) {
$fields = ['uri-id', 'thr-parent-id', 'gravity', 'author-id', 'created']; $fields = ['uri-id', 'thr-parent-id', 'gravity', 'author-id', 'created'];
$pinned = Post\Collection::selectToArrayForContact($cid, Post\Collection::FEATURED, $fields); $pinned = Post\Collection::selectToArrayForContact($cid, Post\Collection::FEATURED, $fields);
$items = array_merge($items, $pinned); $items = array_merge($items, $pinned);
} }
$o .= DI::conversation()->render($items, ConversationContent::MODE_CONTACTS, $update, false, 'pinned_created', $uid); $o .= DI::conversation()->render($items, ConversationContent::MODE_CONTACTS, $update, false, 'pinned_created', $uid);
@ -1906,9 +1906,9 @@ class Contact
*/ */
private static function checkAvatarCacheByArray(array $contact, bool $no_update = false): array private static function checkAvatarCacheByArray(array $contact, bool $no_update = false): array
{ {
$update = false; $update = false;
$contact_fields = []; $contact_fields = [];
$fields = ['photo', 'thumb', 'micro']; $fields = ['photo', 'thumb', 'micro'];
foreach ($fields as $field) { foreach ($fields as $field) {
if (isset($contact[$field])) { if (isset($contact[$field])) {
$contact_fields[] = $field; $contact_fields[] = $field;
@ -1964,7 +1964,7 @@ class Contact
if (!empty($contact['gsid'])) { if (!empty($contact['gsid'])) {
// Use default banners for certain platforms // Use default banners for certain platforms
$gserver = DBA::selectFirst('gserver', ['platform'], ['id' => $contact['gsid']]); $gserver = DBA::selectFirst('gserver', ['platform'], ['id' => $contact['gsid']]);
$platform = strtolower($gserver['platform'] ?? ''); $platform = strtolower($gserver['platform'] ?? '');
} else { } else {
$platform = ''; $platform = '';
@ -2009,18 +2009,18 @@ class Contact
switch ($size) { switch ($size) {
case Proxy::SIZE_MICRO: case Proxy::SIZE_MICRO:
$avatar['size'] = 48; $avatar['size'] = 48;
$default = self::DEFAULT_AVATAR_MICRO; $default = self::DEFAULT_AVATAR_MICRO;
break; break;
case Proxy::SIZE_THUMB: case Proxy::SIZE_THUMB:
$avatar['size'] = 80; $avatar['size'] = 80;
$default = self::DEFAULT_AVATAR_THUMB; $default = self::DEFAULT_AVATAR_THUMB;
break; break;
case Proxy::SIZE_SMALL: case Proxy::SIZE_SMALL:
default: default:
$avatar['size'] = 300; $avatar['size'] = 300;
$default = self::DEFAULT_AVATAR_PHOTO; $default = self::DEFAULT_AVATAR_PHOTO;
break; break;
} }
@ -2029,14 +2029,14 @@ class Contact
$type = Contact::TYPE_PERSON; $type = Contact::TYPE_PERSON;
if (!empty($contact['id'])) { if (!empty($contact['id'])) {
$account = DBA::selectFirst('account-user-view', ['platform', 'contact-type'], ['id' => $contact['id']]); $account = DBA::selectFirst('account-user-view', ['platform', 'contact-type'], ['id' => $contact['id']]);
$platform = $account['platform'] ?? ''; $platform = $account['platform'] ?? '';
$type = $account['contact-type'] ?? Contact::TYPE_PERSON; $type = $account['contact-type'] ?? Contact::TYPE_PERSON;
} }
if (empty($platform) && !empty($contact['uri-id'])) { if (empty($platform) && !empty($contact['uri-id'])) {
$account = DBA::selectFirst('account-user-view', ['platform', 'contact-type'], ['uri-id' => $contact['uri-id']]); $account = DBA::selectFirst('account-user-view', ['platform', 'contact-type'], ['uri-id' => $contact['uri-id']]);
$platform = $account['platform'] ?? ''; $platform = $account['platform'] ?? '';
$type = $account['contact-type'] ?? Contact::TYPE_PERSON; $type = $account['contact-type'] ?? Contact::TYPE_PERSON;
} }
@ -2146,7 +2146,7 @@ class Contact
return DI::baseUrl() . $default; return DI::baseUrl() . $default;
} }
$avatar['url'] = ''; $avatar['url'] = '';
$avatar['success'] = false; $avatar['success'] = false;
Hook::callAll('avatar_lookup', $avatar); Hook::callAll('avatar_lookup', $avatar);
@ -2174,7 +2174,7 @@ class Contact
if (empty($updated)) { if (empty($updated)) {
$account = DBA::selectFirst('account-user-view', ['updated', 'guid'], ['id' => $cid]); $account = DBA::selectFirst('account-user-view', ['updated', 'guid'], ['id' => $cid]);
$updated = $account['updated'] ?? ''; $updated = $account['updated'] ?? '';
$guid = $account['guid'] ?? ''; $guid = $account['guid'] ?? '';
} }
$guid = urlencode($guid); $guid = urlencode($guid);
@ -2242,7 +2242,7 @@ class Contact
if (empty($updated) || empty($guid)) { if (empty($updated) || empty($guid)) {
$account = DBA::selectFirst('account-user-view', ['updated', 'guid'], ['id' => $cid]); $account = DBA::selectFirst('account-user-view', ['updated', 'guid'], ['id' => $cid]);
$updated = $account['updated'] ?? ''; $updated = $account['updated'] ?? '';
$guid = $account['guid'] ?? ''; $guid = $account['guid'] ?? '';
} }
$guid = urlencode($guid); $guid = urlencode($guid);
@ -2373,11 +2373,11 @@ class Contact
if ($default_avatar && Proxy::isLocalImage($avatar)) { if ($default_avatar && Proxy::isLocalImage($avatar)) {
$fields = [ $fields = [
'avatar' => $avatar, 'avatar' => $avatar,
'avatar-date' => DateTimeFormat::utcNow(), 'avatar-date' => DateTimeFormat::utcNow(),
'photo' => $avatar, 'photo' => $avatar,
'thumb' => self::getDefaultAvatar($contact, Proxy::SIZE_THUMB), 'thumb' => self::getDefaultAvatar($contact, Proxy::SIZE_THUMB),
'micro' => self::getDefaultAvatar($contact, Proxy::SIZE_MICRO) 'micro' => self::getDefaultAvatar($contact, Proxy::SIZE_MICRO)
]; ];
DI::logger()->debug('Use default avatar', ['id' => $cid, 'uid' => $uid]); DI::logger()->debug('Use default avatar', ['id' => $cid, 'uid' => $uid]);
} }
@ -2416,11 +2416,11 @@ class Contact
$photos = Photo::importProfilePhoto($avatar, $uid, $cid, true); $photos = Photo::importProfilePhoto($avatar, $uid, $cid, true);
if ($photos) { if ($photos) {
$fields = [ $fields = [
'avatar' => $avatar, 'avatar' => $avatar,
'photo' => $photos[0], 'photo' => $photos[0],
'thumb' => $photos[1], 'thumb' => $photos[1],
'micro' => $photos[2], 'micro' => $photos[2],
'blurhash' => $photos[3], 'blurhash' => $photos[3],
'avatar-date' => DateTimeFormat::utcNow(), 'avatar-date' => DateTimeFormat::utcNow(),
]; ];
$update = true; $update = true;
@ -2473,7 +2473,7 @@ class Contact
{ {
// Update contact data for all users // Update contact data for all users
$condition = ['self' => false, 'nurl' => Strings::normaliseLink($url)]; $condition = ['self' => false, 'nurl' => Strings::normaliseLink($url)];
$contacts = DBA::select('contact', ['id', 'uid'], $condition); $contacts = DBA::select('contact', ['id', 'uid'], $condition);
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
DI::logger()->info('Deleting contact', ['id' => $contact['id'], 'uid' => $contact['uid'], 'url' => $url]); DI::logger()->info('Deleting contact', ['id' => $contact['id'], 'uid' => $contact['uid'], 'url' => $url]);
self::remove($contact['id']); self::remove($contact['id']);
@ -2600,7 +2600,7 @@ class Contact
public static function removeDuplicates(string $nurl, int $uid) public static function removeDuplicates(string $nurl, int $uid)
{ {
$condition = ['nurl' => $nurl, 'uid' => $uid, 'self' => false, 'deleted' => false, 'network' => Protocol::FEDERATED]; $condition = ['nurl' => $nurl, 'uid' => $uid, 'self' => false, 'deleted' => false, 'network' => Protocol::FEDERATED];
$count = DBA::count('contact', $condition); $count = DBA::count('contact', $condition);
if ($count <= 1) { if ($count <= 1) {
return false; return false;
} }
@ -2615,7 +2615,7 @@ class Contact
DI::logger()->info('Found duplicates', ['count' => $count, 'first' => $first, 'uid' => $uid, 'nurl' => $nurl]); DI::logger()->info('Found duplicates', ['count' => $count, 'first' => $first, 'uid' => $uid, 'nurl' => $nurl]);
// Find all duplicates // Find all duplicates
$condition = ["`nurl` = ? AND `uid` = ? AND `id` != ? AND NOT `self` AND NOT `deleted`", $nurl, $uid, $first]; $condition = ["`nurl` = ? AND `uid` = ? AND `id` != ? AND NOT `self` AND NOT `deleted`", $nurl, $uid, $first];
$duplicates = DBA::select('contact', ['id', 'network'], $condition); $duplicates = DBA::select('contact', ['id', 'network'], $condition);
while ($duplicate = DBA::fetch($duplicates)) { while ($duplicate = DBA::fetch($duplicates)) {
if (!in_array($duplicate['network'], Protocol::FEDERATED)) { if (!in_array($duplicate['network'], Protocol::FEDERATED)) {
@ -2686,7 +2686,7 @@ class Contact
$data = Probe::uri($contact['url'], $network, $contact['uid']); $data = Probe::uri($contact['url'], $network, $contact['uid']);
if (in_array($data['network'], Protocol::FEDERATED) && (parse_url($data['url'], PHP_URL_SCHEME) == 'http')) { if (in_array($data['network'], Protocol::FEDERATED) && (parse_url($data['url'], PHP_URL_SCHEME) == 'http')) {
$ssl_url = str_replace('http://', 'https://', $contact['url']); $ssl_url = str_replace('http://', 'https://', $contact['url']);
$ssl_data = Probe::uri($ssl_url, $network, $contact['uid']); $ssl_data = Probe::uri($ssl_url, $network, $contact['uid']);
if (($ssl_data['network'] == $data['network']) && (parse_url($ssl_data['url'], PHP_URL_SCHEME) != 'http')) { if (($ssl_data['network'] == $data['network']) && (parse_url($ssl_data['url'], PHP_URL_SCHEME) != 'http')) {
$data = $ssl_data; $data = $ssl_data;
@ -2883,12 +2883,12 @@ class Contact
} }
if (isset($ret['account-type']) && is_int($ret['account-type'])) { if (isset($ret['account-type']) && is_int($ret['account-type'])) {
$ret['forum'] = false; $ret['forum'] = false;
$ret['prv'] = false; $ret['prv'] = false;
$ret['contact-type'] = $ret['account-type']; $ret['contact-type'] = $ret['account-type'];
if (($ret['contact-type'] == User::ACCOUNT_TYPE_COMMUNITY) && isset($ret['manually-approve'])) { if (($ret['contact-type'] == User::ACCOUNT_TYPE_COMMUNITY) && isset($ret['manually-approve'])) {
$ret['forum'] = (bool)!$ret['manually-approve']; $ret['forum'] = (bool)!$ret['manually-approve'];
$ret['prv'] = (bool)!$ret['forum']; $ret['prv'] = (bool)!$ret['forum'];
} }
} }
@ -2907,7 +2907,7 @@ class Contact
} }
$update = false; $update = false;
$guid = ($ret['guid'] ?? '') ?: Item::guidFromUri($ret['url'], $ret['baseurl'] ?? $ret['alias'] ?? ''); $guid = ($ret['guid'] ?? '') ?: Item::guidFromUri($ret['url'], $ret['baseurl'] ?? $ret['alias'] ?? '');
// make sure to not overwrite existing values with blank entries except some technical fields // make sure to not overwrite existing values with blank entries except some technical fields
$keep = ['batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'baseurl']; $keep = ['batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'baseurl'];
@ -2974,7 +2974,7 @@ class Contact
} }
if (($uid == 0) || in_array($ret['network'], [Protocol::DFRN, Protocol::DIASPORA, Protocol::ACTIVITYPUB])) { if (($uid == 0) || in_array($ret['network'], [Protocol::DFRN, Protocol::DIASPORA, Protocol::ACTIVITYPUB])) {
$ret['last-update'] = $updated; $ret['last-update'] = $updated;
$ret['success_update'] = $updated; $ret['success_update'] = $updated;
} }
@ -3106,10 +3106,10 @@ class Contact
if (!empty($arr['contact']['name'])) { if (!empty($arr['contact']['name'])) {
$probed = false; $probed = false;
$ret = $arr['contact']; $ret = $arr['contact'];
} else { } else {
$probed = true; $probed = true;
$ret = Probe::uri($url, $network, $uid); $ret = Probe::uri($url, $network, $uid);
// Ensure that the public contact exists // Ensure that the public contact exists
if ($ret['network'] != Protocol::PHANTOM) { if ($ret['network'] != Protocol::PHANTOM) {
@ -3124,7 +3124,7 @@ class Contact
// check if we already have a contact // check if we already have a contact
$condition = ['uid' => $uid, 'nurl' => Strings::normaliseLink($ret['url']), 'deleted' => false]; $condition = ['uid' => $uid, 'nurl' => Strings::normaliseLink($ret['url']), 'deleted' => false];
$contact = DBA::selectFirst('contact', ['id', 'rel', 'url', 'pending', 'hub-verify'], $condition); $contact = DBA::selectFirst('contact', ['id', 'rel', 'url', 'pending', 'hub-verify'], $condition);
$protocol = self::getProtocol($ret['url'], $ret['network']); $protocol = self::getProtocol($ret['url'], $ret['network']);
@ -3203,7 +3203,7 @@ class Contact
'nick' => $ret['nick'], 'nick' => $ret['nick'],
'network' => $ret['network'], 'network' => $ret['network'],
'baseurl' => $ret['baseurl'], 'baseurl' => $ret['baseurl'],
'gsid' => $ret['gsid'] ?? null, 'gsid' => $ret['gsid'] ?? null,
'contact-type' => $ret['account-type'] ?? self::TYPE_PERSON, 'contact-type' => $ret['account-type'] ?? self::TYPE_PERSON,
'protocol' => $protocol, 'protocol' => $protocol,
'pubkey' => $ret['pubkey'], 'pubkey' => $ret['pubkey'],
@ -3223,7 +3223,7 @@ class Contact
return $result; return $result;
} }
$contact_id = $contact['id']; $contact_id = $contact['id'];
$result['cid'] = $contact_id; $result['cid'] = $contact_id;
if ($contact['contact-type'] == self::TYPE_COMMUNITY) { if ($contact['contact-type'] == self::TYPE_COMMUNITY) {
@ -3268,7 +3268,7 @@ class Contact
return false; return false;
} }
$fields = ['id', 'url', 'name', 'nick', 'avatar', 'photo', 'network', 'blocked', 'baseurl']; $fields = ['id', 'url', 'name', 'nick', 'avatar', 'photo', 'network', 'blocked', 'baseurl'];
$pub_contact = DBA::selectFirst('contact', $fields, ['id' => $datarray['author-id']]); $pub_contact = DBA::selectFirst('contact', $fields, ['id' => $datarray['author-id']]);
if (!DBA::isResult($pub_contact)) { if (!DBA::isResult($pub_contact)) {
// Should never happen // Should never happen
@ -3280,10 +3280,10 @@ class Contact
return false; return false;
} }
$url = ($datarray['author-link'] ?? '') ?: $pub_contact['url']; $url = ($datarray['author-link'] ?? '') ?: $pub_contact['url'];
$name = $pub_contact['name']; $name = $pub_contact['name'];
$photo = ($pub_contact['avatar'] ?? '') ?: $pub_contact["photo"]; $photo = ($pub_contact['avatar'] ?? '') ?: $pub_contact["photo"];
$nick = $pub_contact['nick']; $nick = $pub_contact['nick'];
$network = $pub_contact['network']; $network = $pub_contact['network'];
// Ensure that we don't create a new contact when there already is one // Ensure that we don't create a new contact when there already is one
@ -3361,7 +3361,7 @@ class Contact
/// @TODO Encapsulate this into a function/method /// @TODO Encapsulate this into a function/method
$fields = ['uid', 'username', 'email', 'page-flags', 'notify-flags', 'language']; $fields = ['uid', 'username', 'email', 'page-flags', 'notify-flags', 'language'];
$user = DBA::selectFirst('user', $fields, ['uid' => $importer['uid']]); $user = DBA::selectFirst('user', $fields, ['uid' => $importer['uid']]);
if (DBA::isResult($user) && !in_array($user['page-flags'], [User::PAGE_FLAGS_SOAPBOX, User::PAGE_FLAGS_FREELOVE, User::PAGE_FLAGS_COMMUNITY])) { if (DBA::isResult($user) && !in_array($user['page-flags'], [User::PAGE_FLAGS_SOAPBOX, User::PAGE_FLAGS_FREELOVE, User::PAGE_FLAGS_COMMUNITY])) {
// create notification // create notification
if (is_array($contact_record)) { if (is_array($contact_record)) {
@ -3395,7 +3395,7 @@ class Contact
} }
$condition = ['uid' => $importer['uid'], 'url' => $url, 'pending' => true]; $condition = ['uid' => $importer['uid'], 'url' => $url, 'pending' => true];
$fields = ['pending' => false]; $fields = ['pending' => false];
if ($user['page-flags'] == User::PAGE_FLAGS_FREELOVE) { if ($user['page-flags'] == User::PAGE_FLAGS_FREELOVE) {
$fields['rel'] = self::FRIEND; $fields['rel'] = self::FRIEND;
} }
@ -3500,7 +3500,7 @@ class Contact
DBA::update( DBA::update(
'contact', 'contact',
['bdyear' => substr($nextbd, 0, 4), 'bd' => $nextbd], ['bdyear' => substr($nextbd, 0, 4), 'bd' => $nextbd],
['id' => $contact['id']] ['id' => $contact['id']]
); );
} }
} }
@ -3655,9 +3655,9 @@ class Contact
*/ */
public static function isGroup(int $contactid): bool public static function isGroup(int $contactid): bool
{ {
$fields = ['contact-type']; $fields = ['contact-type'];
$condition = ['id' => $contactid]; $condition = ['id' => $contactid];
$contact = DBA::selectFirst('contact', $fields, $condition); $contact = DBA::selectFirst('contact', $fields, $condition);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
return false; return false;
} }
@ -3675,7 +3675,7 @@ class Contact
public static function canReceivePrivateMessages(array $contact): bool public static function canReceivePrivateMessages(array $contact): bool
{ {
$protocol = $contact['network'] ?? $contact['protocol'] ?? Protocol::PHANTOM; $protocol = $contact['network'] ?? $contact['protocol'] ?? Protocol::PHANTOM;
$self = $contact['self'] ?? false; $self = $contact['self'] ?? false;
return in_array($protocol, [Protocol::DFRN, Protocol::DIASPORA, Protocol::ACTIVITYPUB]) && !$self; return in_array($protocol, [Protocol::DFRN, Protocol::DIASPORA, Protocol::ACTIVITYPUB]) && !$self;
} }
@ -3706,12 +3706,12 @@ class Contact
} }
$condition = [ $condition = [
'network' => $networks, 'network' => $networks,
'server-failed' => false, 'server-failed' => false,
'failed' => false, 'failed' => false,
'deleted' => false, 'deleted' => false,
'unsearchable' => false, 'unsearchable' => false,
'uid' => $uid 'uid' => $uid
]; ];
if (!$show_blocked) { if (!$show_blocked) {
@ -3755,10 +3755,10 @@ class Contact
*/ */
public static function addByUrls(array $urls): array public static function addByUrls(array $urls): array
{ {
$added = 0; $added = 0;
$updated = 0; $updated = 0;
$unchanged = 0; $unchanged = 0;
$count = 0; $count = 0;
foreach ($urls as $url) { foreach ($urls as $url) {
if (empty($url) || !is_string($url)) { if (empty($url) || !is_string($url)) {

View file

@ -98,7 +98,7 @@ class Relation
$uid = User::getIdForURL($url); $uid = User::getIdForURL($url);
if (!empty($uid)) { if (!empty($uid)) {
DI::logger()->info('Fetch the followers/followings locally', ['url' => $url]); DI::logger()->info('Fetch the followers/followings locally', ['url' => $url]);
$followers = self::getContacts($uid, [Contact::FOLLOWER, Contact::FRIEND]); $followers = self::getContacts($uid, [Contact::FOLLOWER, Contact::FRIEND]);
$followings = self::getContacts($uid, [Contact::SHARING, Contact::FRIEND]); $followings = self::getContacts($uid, [Contact::SHARING, Contact::FRIEND]);
} elseif (!Contact::isLocal($url)) { } elseif (!Contact::isLocal($url)) {
DI::logger()->info('Fetch the followers/followings by polling the endpoints', ['url' => $url]); DI::logger()->info('Fetch the followers/followings by polling the endpoints', ['url' => $url]);
@ -117,7 +117,7 @@ class Relation
} }
} else { } else {
DI::logger()->warning('Contact seems to be local but could not be found here', ['url' => $url]); DI::logger()->warning('Contact seems to be local but could not be found here', ['url' => $url]);
$followers = []; $followers = [];
$followings = []; $followings = [];
} }
@ -158,7 +158,7 @@ class Relation
} }
$contacts = array_unique($contacts); $contacts = array_unique($contacts);
$follower_counter = 0; $follower_counter = 0;
$following_counter = 0; $following_counter = 0;
DI::logger()->info('Discover contacts', ['id' => $target, 'url' => $url, 'contacts' => count($contacts)]); DI::logger()->info('Discover contacts', ['id' => $target, 'url' => $url, 'contacts' => count($contacts)]);
@ -199,7 +199,7 @@ class Relation
*/ */
private static function getContacts(int $uid, array $rel, bool $only_ap = true): array private static function getContacts(int $uid, array $rel, bool $only_ap = true): array
{ {
$list = []; $list = [];
$profile = Profile::getByUID($uid); $profile = Profile::getByUID($uid);
if (!empty($profile['hide-friends'])) { if (!empty($profile['hide-friends'])) {
return $list; return $list;
@ -300,7 +300,7 @@ class Relation
* @param integer $uid * @param integer $uid
* @return boolean * @return boolean
*/ */
static public function areSuggestionsOutdated(int $uid): bool public static function areSuggestionsOutdated(int $uid): bool
{ {
return DI::pConfig()->get($uid, 'suggestion', 'last_update') + 3600 < time(); return DI::pConfig()->get($uid, 'suggestion', 'last_update') + 3600 < time();
} }
@ -311,7 +311,7 @@ class Relation
* @param integer $uid * @param integer $uid
* @return void * @return void
*/ */
static public function updateCachedSuggestions(int $uid) public static function updateCachedSuggestions(int $uid)
{ {
if (!self::areSuggestionsOutdated($uid)) { if (!self::areSuggestionsOutdated($uid)) {
return; return;
@ -334,11 +334,11 @@ class Relation
* @param int $limit optional, default 80 * @param int $limit optional, default 80
* @return array * @return array
*/ */
static public function getCachedSuggestions(int $uid, int $start = 0, int $limit = 80): array public static function getCachedSuggestions(int $uid, int $start = 0, int $limit = 80): array
{ {
$condition = ["`uid` = ? AND `uri-id` IN (SELECT `uri-id` FROM `account-suggestion` WHERE NOT `ignore` AND `uid` = ?)", 0, $uid]; $condition = ["`uid` = ? AND `uri-id` IN (SELECT `uri-id` FROM `account-suggestion` WHERE NOT `ignore` AND `uid` = ?)", 0, $uid];
$params = ['limit' => [$start, $limit]]; $params = ['limit' => [$start, $limit]];
$cached = DBA::selectToArray('contact', [], $condition, $params); $cached = DBA::selectToArray('contact', [], $condition, $params);
if (!empty($cached)) { if (!empty($cached)) {
return $cached; return $cached;
@ -355,15 +355,15 @@ class Relation
* @param int $limit optional, default 80 * @param int $limit optional, default 80
* @return array * @return array
*/ */
static public function getSuggestions(int $uid, int $start = 0, int $limit = 80): array public static function getSuggestions(int $uid, int $start = 0, int $limit = 80): array
{ {
if ($uid == 0) { if ($uid == 0) {
return []; return [];
} }
$cid = Contact::getPublicIdByUserId($uid); $cid = Contact::getPublicIdByUserId($uid);
$totallimit = $start + $limit; $totallimit = $start + $limit;
$contacts = []; $contacts = [];
DI::logger()->info('Collecting suggestions', ['uid' => $uid, 'cid' => $cid, 'start' => $start, 'limit' => $limit]); DI::logger()->info('Collecting suggestions', ['uid' => $uid, 'cid' => $cid, 'start' => $start, 'limit' => $limit]);
@ -371,17 +371,21 @@ class Relation
// The query returns contacts where contacts interacted with whom the given user follows. // The query returns contacts where contacts interacted with whom the given user follows.
// Contacts who already are in the user's contact table are ignored. // Contacts who already are in the user's contact table are ignored.
$results = DBA::select('contact', [], ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN $results = DBA::select(
'contact',
[],
["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN
(SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ?) (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ?)
AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN
(SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?))) AND `id` = `cid`) (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?))) AND `id` = `cid`)
AND NOT `hidden` AND `network` IN (?, ?, ?) AND NOT `hidden` AND `network` IN (?, ?, ?)
AND NOT `uri-id` IN (SELECT `uri-id` FROM `account-suggestion` WHERE `uri-id` = `contact`.`uri-id` AND `uid` = ?)", AND NOT `uri-id` IN (SELECT `uri-id` FROM `account-suggestion` WHERE `uri-id` = `contact`.`uri-id` AND `uid` = ?)",
$cid, $cid,
0, 0,
$uid, Contact::FRIEND, Contact::SHARING, $uid, Contact::FRIEND, Contact::SHARING,
Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $uid Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $uid
], [ ],
[
'order' => ['last-item' => true], 'order' => ['last-item' => true],
'limit' => $totallimit, 'limit' => $totallimit,
] ]
@ -401,15 +405,17 @@ class Relation
// The query returns contacts where contacts interacted with whom also interacted with the given user. // The query returns contacts where contacts interacted with whom also interacted with the given user.
// Contacts who already are in the user's contact table are ignored. // Contacts who already are in the user's contact table are ignored.
$results = DBA::select('contact', [], $results = DBA::select(
'contact',
[],
["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN
(SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ?) (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ?)
AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN
(SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?))) AND `id` = `cid`) (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?))) AND `id` = `cid`)
AND NOT `hidden` AND `network` IN (?, ?, ?) AND NOT `hidden` AND `network` IN (?, ?, ?)
AND NOT `uri-id` IN (SELECT `uri-id` FROM `account-suggestion` WHERE `uri-id` = `contact`.`uri-id` AND `uid` = ?)", AND NOT `uri-id` IN (SELECT `uri-id` FROM `account-suggestion` WHERE `uri-id` = `contact`.`uri-id` AND `uid` = ?)",
$cid, 0, $uid, Contact::FRIEND, Contact::SHARING, $cid, 0, $uid, Contact::FRIEND, Contact::SHARING,
Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $uid], Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $uid],
['order' => ['last-item' => true], 'limit' => $totallimit] ['order' => ['last-item' => true], 'limit' => $totallimit]
); );
@ -425,12 +431,14 @@ class Relation
} }
// The query returns contacts that follow the given user but aren't followed by that user. // The query returns contacts that follow the given user but aren't followed by that user.
$results = DBA::select('contact', [], $results = DBA::select(
'contact',
[],
["`nurl` IN (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` = ?) ["`nurl` IN (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` = ?)
AND NOT `hidden` AND `uid` = ? AND `network` IN (?, ?, ?) AND NOT `hidden` AND `uid` = ? AND `network` IN (?, ?, ?)
AND NOT `uri-id` IN (SELECT `uri-id` FROM `account-suggestion` WHERE `uri-id` = `contact`.`uri-id` AND `uid` = ?)", AND NOT `uri-id` IN (SELECT `uri-id` FROM `account-suggestion` WHERE `uri-id` = `contact`.`uri-id` AND `uid` = ?)",
$uid, Contact::FOLLOWER, 0, $uid, Contact::FOLLOWER, 0,
Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $uid], Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $uid],
['order' => ['last-item' => true], 'limit' => $totallimit] ['order' => ['last-item' => true], 'limit' => $totallimit]
); );
@ -446,12 +454,14 @@ class Relation
} }
// The query returns any contact that isn't followed by that user. // The query returns any contact that isn't followed by that user.
$results = DBA::select('contact', [], $results = DBA::select(
'contact',
[],
["NOT `nurl` IN (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?) AND `nurl` = `nurl`) ["NOT `nurl` IN (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?) AND `nurl` = `nurl`)
AND NOT `hidden` AND `uid` = ? AND `network` IN (?, ?, ?) AND NOT `hidden` AND `uid` = ? AND `network` IN (?, ?, ?)
AND NOT `uri-id` IN (SELECT `uri-id` FROM `account-suggestion` WHERE `uri-id` = `contact`.`uri-id` AND `uid` = ?)", AND NOT `uri-id` IN (SELECT `uri-id` FROM `account-suggestion` WHERE `uri-id` = `contact`.`uri-id` AND `uid` = ?)",
$uid, Contact::FRIEND, Contact::SHARING, 0, $uid, Contact::FRIEND, Contact::SHARING, 0,
Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $uid], Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $uid],
['order' => ['last-item' => true], 'limit' => $totallimit] ['order' => ['last-item' => true], 'limit' => $totallimit]
); );
@ -476,7 +486,7 @@ class Relation
public static function countFollows(int $cid, array $condition = []): int public static function countFollows(int $cid, array $condition = []): int
{ {
$condition = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]); $condition = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]);
$sql = "SELECT COUNT(*) AS `total` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition); $sql = "SELECT COUNT(*) AS `total` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition);
$result = DBA::fetchFirst($sql, $condition); $result = DBA::fetchFirst($sql, $condition);
return $result['total'] ?? 0; return $result['total'] ?? 0;
@ -495,7 +505,7 @@ class Relation
public static function listFollows(int $cid, array $condition = [], int $count = 30, int $offset = 0) public static function listFollows(int $cid, array $condition = [], int $count = 30, int $offset = 0)
{ {
$condition = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]); $condition = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]);
$sql = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition); $sql = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition);
if ($count > 0) { if ($count > 0) {
$sql .= " LIMIT ?, ?"; $sql .= " LIMIT ?, ?";
$condition = array_merge($condition, [$offset, $count]); $condition = array_merge($condition, [$offset, $count]);
@ -514,7 +524,7 @@ class Relation
public static function countFollowers(int $cid, array $condition = []) public static function countFollowers(int $cid, array $condition = [])
{ {
$condition = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]); $condition = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]);
$sql = "SELECT COUNT(*) AS `total` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition); $sql = "SELECT COUNT(*) AS `total` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition);
$result = DBA::fetchFirst($sql, $condition); $result = DBA::fetchFirst($sql, $condition);
return $result['total'] ?? 0; return $result['total'] ?? 0;
@ -533,7 +543,7 @@ class Relation
public static function listFollowers(int $cid, array $condition = [], int $count = 30, int $offset = 0) public static function listFollowers(int $cid, array $condition = [], int $count = 30, int $offset = 0)
{ {
$condition = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]); $condition = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]);
$sql = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition); $sql = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition);
if ($count > 0) { if ($count > 0) {
$sql .= " LIMIT ?, ?"; $sql .= " LIMIT ?, ?";
$condition = array_merge($condition, [$offset, $count]); $condition = array_merge($condition, [$offset, $count]);
@ -553,13 +563,13 @@ class Relation
{ {
$condition1 = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]); $condition1 = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]);
$condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]); $condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]);
$sql1 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition1); $sql1 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition1);
$sql2 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition2); $sql2 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition2);
$union = array_merge($condition1, $condition2); $union = array_merge($condition1, $condition2);
$sql = $sql1 . " INTERSECT " . $sql2; $sql = $sql1 . " INTERSECT " . $sql2;
$contacts = 0; $contacts = 0;
$query = DBA::p($sql, $union); $query = DBA::p($sql, $union);
while (DBA::fetch($query)) { while (DBA::fetch($query)) {
$contacts++; $contacts++;
} }
@ -582,10 +592,10 @@ class Relation
{ {
$condition1 = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]); $condition1 = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]);
$condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]); $condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]);
$sql1 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition1); $sql1 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition1);
$sql2 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition2); $sql2 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition2);
$union = array_merge($condition1, $condition2); $union = array_merge($condition1, $condition2);
$sql = $sql1 . " INTERSECT " . $sql2; $sql = $sql1 . " INTERSECT " . $sql2;
if ($count > 0) { if ($count > 0) {
$sql .= " LIMIT ?, ?"; $sql .= " LIMIT ?, ?";
$union = array_merge($union, [$offset, $count]); $union = array_merge($union, [$offset, $count]);
@ -605,13 +615,13 @@ class Relation
{ {
$condition1 = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]); $condition1 = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]);
$condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]); $condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]);
$sql1 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition1); $sql1 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition1);
$sql2 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " .array_shift($condition2); $sql2 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " .array_shift($condition2);
$union = array_merge($condition1, $condition2); $union = array_merge($condition1, $condition2);
$sql = $sql1 . " UNION " . $sql2; $sql = $sql1 . " UNION " . $sql2;
$contacts = 0; $contacts = 0;
$query = DBA::p($sql, $union); $query = DBA::p($sql, $union);
while (DBA::fetch($query)) { while (DBA::fetch($query)) {
$contacts++; $contacts++;
} }
@ -634,10 +644,10 @@ class Relation
{ {
$condition1 = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]); $condition1 = DBA::mergeConditions($condition, ["`cid` = ? and `follows`", $cid]);
$condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]); $condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ? and `follows`", $cid]);
$sql1 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition1); $sql1 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `relation-cid` WHERE " . array_shift($condition1);
$sql2 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " .array_shift($condition2); $sql2 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " .array_shift($condition2);
$union = array_merge($condition1, $condition2); $union = array_merge($condition1, $condition2);
$sql = $sql1 . " UNION " . $sql2; $sql = $sql1 . " UNION " . $sql2;
if ($count > 0) { if ($count > 0) {
$sql .= " LIMIT ?, ?"; $sql .= " LIMIT ?, ?";
$union = array_merge($union, [$offset, $count]); $union = array_merge($union, [$offset, $count]);
@ -659,13 +669,13 @@ class Relation
{ {
$condition1 = DBA::mergeConditions($condition, ["`relation-cid` = ?", $sourceId]); $condition1 = DBA::mergeConditions($condition, ["`relation-cid` = ?", $sourceId]);
$condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ?", $targetId]); $condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ?", $targetId]);
$sql1 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition1); $sql1 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition1);
$sql2 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " .array_shift($condition2); $sql2 = "SELECT `contact`.`id` FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " .array_shift($condition2);
$union = array_merge($condition1, $condition2); $union = array_merge($condition1, $condition2);
$sql = $sql1 . " INTERSECT " . $sql2; $sql = $sql1 . " INTERSECT " . $sql2;
$contacts = 0; $contacts = 0;
$query = DBA::p($sql, $union); $query = DBA::p($sql, $union);
while (DBA::fetch($query)) { while (DBA::fetch($query)) {
$contacts++; $contacts++;
} }
@ -690,10 +700,10 @@ class Relation
{ {
$condition1 = DBA::mergeConditions($condition, ["`relation-cid` = ?", $sourceId]); $condition1 = DBA::mergeConditions($condition, ["`relation-cid` = ?", $sourceId]);
$condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ?", $targetId]); $condition2 = DBA::mergeConditions($condition, ["`relation-cid` = ?", $targetId]);
$sql1 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition1); $sql1 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " . array_shift($condition1);
$sql2 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " .array_shift($condition2); $sql2 = "SELECT `contact`.* FROM `contact-relation` INNER JOIN `contact` ON `contact`.`id` = `cid` WHERE " .array_shift($condition2);
$union = array_merge($condition1, $condition2); $union = array_merge($condition1, $condition2);
$sql = $sql1 . " INTERSECT " . $sql2; $sql = $sql1 . " INTERSECT " . $sql2;
if ($count > 0) { if ($count > 0) {
$sql .= " LIMIT ?, ?"; $sql .= " LIMIT ?, ?";
$union = array_merge($union, [$offset, $count]); $union = array_merge($union, [$offset, $count]);
@ -712,10 +722,11 @@ class Relation
*/ */
public static function countCommonFollows(int $sourceId, int $targetId, array $condition = []): int public static function countCommonFollows(int $sourceId, int $targetId, array $condition = []): int
{ {
$condition = DBA::mergeConditions($condition, $condition = DBA::mergeConditions(
$condition,
['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)
AND `id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)', AND `id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)',
$sourceId, $targetId] $sourceId, $targetId]
); );
return DI::dba()->count('contact', $condition); return DI::dba()->count('contact', $condition);
@ -735,13 +746,17 @@ class Relation
*/ */
public static function listCommonFollows(int $sourceId, int $targetId, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) public static function listCommonFollows(int $sourceId, int $targetId, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false)
{ {
$condition = DBA::mergeConditions($condition, $condition = DBA::mergeConditions(
$condition,
["`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) ["`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)
AND `id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)", AND `id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)",
$sourceId, $targetId] $sourceId, $targetId]
); );
return DI::dba()->selectToArray('contact', [], $condition, return DI::dba()->selectToArray(
'contact',
[],
$condition,
['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']] ['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']]
); );
} }
@ -757,10 +772,11 @@ class Relation
*/ */
public static function countCommonFollowers(int $sourceId, int $targetId, array $condition = []): int public static function countCommonFollowers(int $sourceId, int $targetId, array $condition = []): int
{ {
$condition = DBA::mergeConditions($condition, $condition = DBA::mergeConditions(
$condition,
["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`) ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)
AND `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)", AND `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)",
$sourceId, $targetId] $sourceId, $targetId]
); );
return DI::dba()->count('contact', $condition); return DI::dba()->count('contact', $condition);
@ -780,13 +796,17 @@ class Relation
*/ */
public static function listCommonFollowers(int $sourceId, int $targetId, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) public static function listCommonFollowers(int $sourceId, int $targetId, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false)
{ {
$condition = DBA::mergeConditions($condition, $condition = DBA::mergeConditions(
$condition,
["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`) ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)
AND `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)", AND `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)",
$sourceId, $targetId] $sourceId, $targetId]
); );
return DI::dba()->selectToArray('contact', [], $condition, return DI::dba()->selectToArray(
'contact',
[],
$condition,
['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']] ['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']]
); );
} }
@ -799,78 +819,156 @@ class Relation
*/ */
public static function calculateInteractionScore(int $uid) public static function calculateInteractionScore(int $uid)
{ {
$days = DI::config()->get('channel', 'interaction_score_days'); $days = DI::config()->get('channel', 'interaction_score_days');
$contact_id = Contact::getPublicIdByUserId($uid); $contact_id = Contact::getPublicIdByUserId($uid);
DI::logger()->debug('Calculation - start', ['uid' => $uid, 'cid' => $contact_id, 'days' => $days]); DI::logger()->debug('Calculation - start', ['uid' => $uid, 'cid' => $contact_id, 'days' => $days]);
$follow = Verb::getID(Activity::FOLLOW); $follow = Verb::getID(Activity::FOLLOW);
$view = Verb::getID(Activity::VIEW); $view = Verb::getID(Activity::VIEW);
$read = Verb::getID(Activity::READ); $read = Verb::getID(Activity::READ);
DBA::update('contact-relation', ['score' => 0, 'relation-score' => 0, 'thread-score' => 0, 'relation-thread-score' => 0], ['relation-cid' => $contact_id]); DBA::update('contact-relation', ['score' => 0, 'relation-score' => 0, 'thread-score' => 0, 'relation-thread-score' => 0], ['relation-cid' => $contact_id]);
$total = DBA::fetchFirst("SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)", $total = DBA::fetchFirst(
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read); "SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)",
$contact_id,
DateTimeFormat::utc('now - ' . $days . ' day'),
$uid,
$contact_id,
$follow,
$view,
$read
);
DI::logger()->debug('Calculate relation-score', ['uid' => $uid, 'total' => $total['activity']]); DI::logger()->debug('Calculate relation-score', ['uid' => $uid, 'total' => $total['activity']]);
$interactions = DBA::p("SELECT `post`.`author-id`, count(*) AS `activity`, EXISTS(SELECT `pid` FROM `account-user-view` WHERE `pid` = `post`.`author-id` AND `uid` = ? AND `rel` IN (?, ?)) AS `follows` $interactions = DBA::p(
"SELECT `post`.`author-id`, count(*) AS `activity`, EXISTS(SELECT `pid` FROM `account-user-view` WHERE `pid` = `post`.`author-id` AND `uid` = ? AND `rel` IN (?, ?)) AS `follows`
FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`", FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`",
$uid, Contact::SHARING, Contact::FRIEND, $contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read); $uid,
Contact::SHARING,
Contact::FRIEND,
$contact_id,
DateTimeFormat::utc('now - ' . $days . ' day'),
$uid,
$contact_id,
$follow,
$view,
$read
);
while ($interaction = DBA::fetch($interactions)) { while ($interaction = DBA::fetch($interactions)) {
$score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535); $score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535);
DBA::update('contact-relation', ['relation-score' => $score, 'follows' => $interaction['follows']], ['relation-cid' => $contact_id, 'cid' => $interaction['author-id']]); DBA::update('contact-relation', ['relation-score' => $score, 'follows' => $interaction['follows']], ['relation-cid' => $contact_id, 'cid' => $interaction['author-id']]);
} }
DBA::close($interactions); DBA::close($interactions);
$total = DBA::fetchFirst("SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)", $total = DBA::fetchFirst(
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read); "SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)",
$contact_id,
DateTimeFormat::utc('now - ' . $days . ' day'),
$uid,
$contact_id,
$follow,
$view,
$read
);
DI::logger()->debug('Calculate relation-thread-score', ['uid' => $uid, 'total' => $total['activity']]); DI::logger()->debug('Calculate relation-thread-score', ['uid' => $uid, 'total' => $total['activity']]);
$interactions = DBA::p("SELECT `post`.`author-id`, count(*) AS `activity`, EXISTS(SELECT `pid` FROM `account-user-view` WHERE `pid` = `post`.`author-id` AND `uid` = ? AND `rel` IN (?, ?)) AS `follows` $interactions = DBA::p(
"SELECT `post`.`author-id`, count(*) AS `activity`, EXISTS(SELECT `pid` FROM `account-user-view` WHERE `pid` = `post`.`author-id` AND `uid` = ? AND `rel` IN (?, ?)) AS `follows`
FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`", FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`",
$uid, Contact::SHARING, Contact::FRIEND, $contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read); $uid,
Contact::SHARING,
Contact::FRIEND,
$contact_id,
DateTimeFormat::utc('now - ' . $days . ' day'),
$uid,
$contact_id,
$follow,
$view,
$read
);
while ($interaction = DBA::fetch($interactions)) { while ($interaction = DBA::fetch($interactions)) {
$score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535); $score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535);
DBA::update('contact-relation', ['relation-thread-score' => $score, 'follows' => !empty($interaction['follows'])], ['relation-cid' => $contact_id, 'cid' => $interaction['author-id']]); DBA::update('contact-relation', ['relation-thread-score' => $score, 'follows' => !empty($interaction['follows'])], ['relation-cid' => $contact_id, 'cid' => $interaction['author-id']]);
} }
DBA::close($interactions); DBA::close($interactions);
$total = DBA::fetchFirst("SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)", $total = DBA::fetchFirst(
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read); "SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)",
$contact_id,
DateTimeFormat::utc('now - ' . $days . ' day'),
$uid,
$contact_id,
$follow,
$view,
$read
);
DI::logger()->debug('Calculate score', ['uid' => $uid, 'total' => $total['activity']]); DI::logger()->debug('Calculate score', ['uid' => $uid, 'total' => $total['activity']]);
$interactions = DBA::p("SELECT `post`.`author-id`, count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`", $interactions = DBA::p(
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read); "SELECT `post`.`author-id`, count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`",
$contact_id,
DateTimeFormat::utc('now - ' . $days . ' day'),
$uid,
$contact_id,
$follow,
$view,
$read
);
while ($interaction = DBA::fetch($interactions)) { while ($interaction = DBA::fetch($interactions)) {
$score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535); $score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535);
DBA::update('contact-relation', ['score' => $score], ['relation-cid' => $contact_id, 'cid' => $interaction['author-id']]); DBA::update('contact-relation', ['score' => $score], ['relation-cid' => $contact_id, 'cid' => $interaction['author-id']]);
} }
DBA::close($interactions); DBA::close($interactions);
$total = DBA::fetchFirst("SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)", $total = DBA::fetchFirst(
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read); "SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)",
$contact_id,
DateTimeFormat::utc('now - ' . $days . ' day'),
$uid,
$contact_id,
$follow,
$view,
$read
);
DI::logger()->debug('Calculate thread-score', ['uid' => $uid, 'total' => $total['activity']]); DI::logger()->debug('Calculate thread-score', ['uid' => $uid, 'total' => $total['activity']]);
$interactions = DBA::p("SELECT `post`.`author-id`, count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`", $interactions = DBA::p(
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read); "SELECT `post`.`author-id`, count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`",
$contact_id,
DateTimeFormat::utc('now - ' . $days . ' day'),
$uid,
$contact_id,
$follow,
$view,
$read
);
while ($interaction = DBA::fetch($interactions)) { while ($interaction = DBA::fetch($interactions)) {
$score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535); $score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535);
DBA::update('contact-relation', ['thread-score' => $score], ['relation-cid' => $contact_id, 'cid' => $interaction['author-id']]); DBA::update('contact-relation', ['thread-score' => $score], ['relation-cid' => $contact_id, 'cid' => $interaction['author-id']]);
} }
DBA::close($interactions); DBA::close($interactions);
$total = DBA::fetchFirst("SELECT count(*) AS `posts` FROM `post-thread-user` WHERE EXISTS(SELECT `cid` FROM `contact-relation` WHERE `cid` = `post-thread-user`.`author-id` AND `relation-cid` = ? AND `follows`) AND `uid` = ? AND `created` > ?", $total = DBA::fetchFirst(
$contact_id, $uid, DateTimeFormat::utc('now - ' . $days . ' day')); "SELECT count(*) AS `posts` FROM `post-thread-user` WHERE EXISTS(SELECT `cid` FROM `contact-relation` WHERE `cid` = `post-thread-user`.`author-id` AND `relation-cid` = ? AND `follows`) AND `uid` = ? AND `created` > ?",
$contact_id,
$uid,
DateTimeFormat::utc('now - ' . $days . ' day')
);
DI::logger()->debug('Calculate post-score', ['uid' => $uid, 'total' => $total['posts']]); DI::logger()->debug('Calculate post-score', ['uid' => $uid, 'total' => $total['posts']]);
$posts = DBA::p("SELECT `author-id`, count(*) AS `posts` FROM `post-thread-user` WHERE EXISTS(SELECT `cid` FROM `contact-relation` WHERE `cid` = `post-thread-user`.`author-id` AND `relation-cid` = ? AND `follows`) AND `uid` = ? AND `created` > ? GROUP BY `author-id`", $posts = DBA::p(
$contact_id, $uid, DateTimeFormat::utc('now - ' . $days . ' day')); "SELECT `author-id`, count(*) AS `posts` FROM `post-thread-user` WHERE EXISTS(SELECT `cid` FROM `contact-relation` WHERE `cid` = `post-thread-user`.`author-id` AND `relation-cid` = ? AND `follows`) AND `uid` = ? AND `created` > ? GROUP BY `author-id`",
$contact_id,
$uid,
DateTimeFormat::utc('now - ' . $days . ' day')
);
while ($post = DBA::fetch($posts)) { while ($post = DBA::fetch($posts)) {
$score = min((int)(($post['posts'] / $total['posts']) * 65535), 65535); $score = min((int)(($post['posts'] / $total['posts']) * 65535), 65535);
DBA::update('contact-relation', ['post-score' => $score], ['relation-cid' => $contact_id, 'cid' => $post['author-id']]); DBA::update('contact-relation', ['post-score' => $score], ['relation-cid' => $contact_id, 'cid' => $post['author-id']]);

View file

@ -56,9 +56,9 @@ class User
return false; return false;
} }
$fields = self::preparedFields($contact); $fields = self::preparedFields($contact);
$fields['cid'] = $pcid; $fields['cid'] = $pcid;
$fields['uid'] = $contact['uid']; $fields['uid'] = $contact['uid'];
$fields['uri-id'] = $contact['uri-id']; $fields['uri-id'] = $contact['uri-id'];
$ret = DBA::insert('user-contact', $fields, Database::INSERT_UPDATE); $ret = DBA::insert('user-contact', $fields, Database::INSERT_UPDATE);
@ -89,7 +89,7 @@ class User
continue; continue;
} }
$update_fields['cid'] = $contact['pid']; $update_fields['cid'] = $contact['pid'];
$ret = DBA::update('user-contact', $update_fields, ['uri-id' => $contact['uri-id'], 'uid' => $contact['uid']], true); $ret = DBA::update('user-contact', $update_fields, ['uri-id' => $contact['uri-id'], 'uid' => $contact['uid']], true);
DI::logger()->info('Updated user contact', ['uid' => $contact['uid'], 'id' => $contact['pid'], 'uri-id' => $contact['uri-id'], 'ret' => $ret]); DI::logger()->info('Updated user contact', ['uid' => $contact['uid'], 'id' => $contact['pid'], 'uri-id' => $contact['uri-id'], 'ret' => $ret]);
} }

View file

@ -29,7 +29,6 @@ use Friendica\Util\XML;
*/ */
class Event class Event
{ {
public static function getHTML(array $event, bool $simple = false, int $uriid = 0): string public static function getHTML(array $event, bool $simple = false, int $uriid = 0): string
{ {
if (empty($event)) { if (empty($event)) {
@ -232,28 +231,28 @@ class Event
*/ */
public static function store(array $arr): int public static function store(array $arr): int
{ {
$guid = $arr['guid'] ?? '' ?: System::createUUID(); $guid = $arr['guid'] ?? '' ?: System::createUUID();
$uri = $arr['uri'] ?? '' ?: Item::newURI($guid); $uri = $arr['uri'] ?? '' ?: Item::newURI($guid);
$event = [ $event = [
'id' => intval($arr['id'] ?? 0), 'id' => intval($arr['id'] ?? 0),
'uid' => intval($arr['uid'] ?? 0), 'uid' => intval($arr['uid'] ?? 0),
'cid' => intval($arr['cid'] ?? 0), 'cid' => intval($arr['cid'] ?? 0),
'guid' => $guid, 'guid' => $guid,
'uri' => $uri, 'uri' => $uri,
'uri-id' => ItemURI::insert(['uri' => $uri, 'guid' => $guid]), 'uri-id' => ItemURI::insert(['uri' => $uri, 'guid' => $guid]),
'type' => ($arr['type'] ?? '') ?: 'event', 'type' => ($arr['type'] ?? '') ?: 'event',
'summary' => $arr['summary'] ?? '', 'summary' => $arr['summary'] ?? '',
'desc' => $arr['desc'] ?? '', 'desc' => $arr['desc'] ?? '',
'location' => $arr['location'] ?? '', 'location' => $arr['location'] ?? '',
'allow_cid' => $arr['allow_cid'] ?? '', 'allow_cid' => $arr['allow_cid'] ?? '',
'allow_gid' => $arr['allow_gid'] ?? '', 'allow_gid' => $arr['allow_gid'] ?? '',
'deny_cid' => $arr['deny_cid'] ?? '', 'deny_cid' => $arr['deny_cid'] ?? '',
'deny_gid' => $arr['deny_gid'] ?? '', 'deny_gid' => $arr['deny_gid'] ?? '',
'nofinish' => intval($arr['nofinish'] ?? (!empty($arr['start']) && empty($arr['finish']))), 'nofinish' => intval($arr['nofinish'] ?? (!empty($arr['start']) && empty($arr['finish']))),
'created' => DateTimeFormat::utc(($arr['created'] ?? '') ?: 'now'), 'created' => DateTimeFormat::utc(($arr['created'] ?? '') ?: 'now'),
'edited' => DateTimeFormat::utc(($arr['edited'] ?? '') ?: 'now'), 'edited' => DateTimeFormat::utc(($arr['edited'] ?? '') ?: 'now'),
'start' => DateTimeFormat::utc(($arr['start'] ?? '') ?: DBA::NULL_DATETIME), 'start' => DateTimeFormat::utc(($arr['start'] ?? '') ?: DBA::NULL_DATETIME),
'finish' => DateTimeFormat::utc(($arr['finish'] ?? '') ?: DBA::NULL_DATETIME), 'finish' => DateTimeFormat::utc(($arr['finish'] ?? '') ?: DBA::NULL_DATETIME),
]; ];
@ -357,7 +356,7 @@ class Event
$item['body'] = self::getBBCode($event); $item['body'] = self::getBBCode($event);
$item['event-id'] = $event['id']; $item['event-id'] = $event['id'];
$item['object'] = '<object><type>' . XML::escape(Activity\ObjectType::EVENT) . '</type><title></title><id>' . XML::escape($event['uri']) . '</id>'; $item['object'] = '<object><type>' . XML::escape(Activity\ObjectType::EVENT) . '</type><title></title><id>' . XML::escape($event['uri']) . '</id>';
$item['object'] .= '<content>' . XML::escape(self::getBBCode($event)) . '</content>'; $item['object'] .= '<content>' . XML::escape(self::getBBCode($event)) . '</content>';
$item['object'] .= '</object>' . "\n"; $item['object'] .= '</object>' . "\n";
@ -375,13 +374,13 @@ class Event
return $item; return $item;
} }
$item['post-type'] = Item::PT_EVENT; $item['post-type'] = Item::PT_EVENT;
$item['title'] = ''; $item['title'] = '';
$item['object-type'] = Activity\ObjectType::EVENT; $item['object-type'] = Activity\ObjectType::EVENT;
$item['body'] = self::getBBCode($event); $item['body'] = self::getBBCode($event);
$item['event-id'] = $event_id; $item['event-id'] = $event_id;
$item['object'] = '<object><type>' . XML::escape(Activity\ObjectType::EVENT) . '</type><title></title><id>' . XML::escape($event['uri']) . '</id>'; $item['object'] = '<object><type>' . XML::escape(Activity\ObjectType::EVENT) . '</type><title></title><id>' . XML::escape($event['uri']) . '</id>';
$item['object'] .= '<content>' . XML::escape(self::getBBCode($event)) . '</content>'; $item['object'] .= '<content>' . XML::escape(self::getBBCode($event)) . '</content>';
$item['object'] .= '</object>' . "\n"; $item['object'] .= '</object>' . "\n";
@ -398,7 +397,7 @@ class Event
{ {
// First day of the week (0 = Sunday). // First day of the week (0 = Sunday).
$firstDay = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'calendar', 'first_day_of_week') ?? 0; $firstDay = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'calendar', 'first_day_of_week') ?? 0;
$defaultView = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'calendar', 'defaultView') ?? 'month'; $defaultView = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'calendar', 'defaultView') ?? 'month';
return [ return [
'firstDay' => $firstDay, 'firstDay' => $firstDay,
@ -649,12 +648,12 @@ class Event
if (DI::userSession()->getLocalUserId() && DI::userSession()->getLocalUserId() == $event['uid'] && $event['type'] == 'event') { if (DI::userSession()->getLocalUserId() && DI::userSession()->getLocalUserId() == $event['uid'] && $event['type'] == 'event') {
$edit = !$event['cid'] ? ['calendar/event/edit/' . $event['id'], DI::l10n()->t('Edit event'), '', ''] : null; $edit = !$event['cid'] ? ['calendar/event/edit/' . $event['id'], DI::l10n()->t('Edit event'), '', ''] : null;
$copy = !$event['cid'] ? ['calendar/event/copy/' . $event['id'], DI::l10n()->t('Duplicate event'), '', ''] : null; $copy = !$event['cid'] ? ['calendar/event/copy/' . $event['id'], DI::l10n()->t('Duplicate event'), '', ''] : null;
$drop = ['calendar/api/delete/' . $event['id'], DI::l10n()->t('Delete event'), '', '']; $drop = ['calendar/api/delete/' . $event['id'], DI::l10n()->t('Delete event'), '', ''];
} }
$title = strip_tags(BBCode::convertForUriId($event['uri-id'], $event['summary'])); $title = strip_tags(BBCode::convertForUriId($event['uri-id'], $event['summary']));
if (!$title) { if (!$title) {
[$title, $_trash] = explode("<br", BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['desc'])), BBCode::TWITTER_API); list($title, $_trash) = explode("<br", BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['desc'])), BBCode::TWITTER_API);
} }
$event['author-link'] = Contact::magicLink($event['author-link']); $event['author-link'] = Contact::magicLink($event['author-link']);
@ -694,7 +693,7 @@ class Event
} }
switch ($format) { switch ($format) {
// Format the exported data as a CSV file. // Format the exported data as a CSV file.
case "csv": case "csv":
$o .= '"Subject", "Start Date", "Start Time", "Description", "End Date", "End Time", "Location"' . PHP_EOL; $o .= '"Subject", "Start Date", "Start Time", "Description", "End Date", "End Time", "Location"' . PHP_EOL;
@ -744,21 +743,21 @@ class Event
$tmp = $event['summary']; $tmp = $event['summary'];
$tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp); $tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp);
$tmp = addcslashes($tmp, ',;'); $tmp = addcslashes($tmp, ',;');
$o .= 'SUMMARY:' . $tmp . PHP_EOL; $o .= 'SUMMARY:' . $tmp . PHP_EOL;
} }
if ($event['desc']) { if ($event['desc']) {
$tmp = $event['desc']; $tmp = $event['desc'];
$tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp); $tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp);
$tmp = addcslashes($tmp, ',;'); $tmp = addcslashes($tmp, ',;');
$o .= 'DESCRIPTION:' . $tmp . PHP_EOL; $o .= 'DESCRIPTION:' . $tmp . PHP_EOL;
} }
if ($event['location']) { if ($event['location']) {
$tmp = $event['location']; $tmp = $event['location'];
$tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp); $tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp);
$tmp = addcslashes($tmp, ',;'); $tmp = addcslashes($tmp, ',;');
$o .= 'LOCATION:' . $tmp . PHP_EOL; $o .= 'LOCATION:' . $tmp . PHP_EOL;
} }
$o .= 'END:VEVENT' . PHP_EOL; $o .= 'END:VEVENT' . PHP_EOL;
@ -912,7 +911,7 @@ class Event
} }
// Construct the profile link (magic-auth). // Construct the profile link (magic-auth).
$author = [ $author = [
'uid' => 0, 'uid' => 0,
'id' => $item['author-id'], 'id' => $item['author-id'],
'network' => $item['author-network'], 'network' => $item['author-network'],

View file

@ -39,44 +39,44 @@ use Psr\Http\Message\UriInterface;
class GServer class GServer
{ {
// Directory types // Directory types
const DT_NONE = 0; const DT_NONE = 0;
const DT_POCO = 1; const DT_POCO = 1;
const DT_MASTODON = 2; const DT_MASTODON = 2;
// Methods to detect server types // Methods to detect server types
// Non endpoint specific methods // Non endpoint specific methods
const DETECT_MANUAL = 0; const DETECT_MANUAL = 0;
const DETECT_HEADER = 1; const DETECT_HEADER = 1;
const DETECT_BODY = 2; const DETECT_BODY = 2;
const DETECT_HOST_META = 3; const DETECT_HOST_META = 3;
const DETECT_CONTACTS = 4; const DETECT_CONTACTS = 4;
const DETECT_AP_ACTOR = 5; const DETECT_AP_ACTOR = 5;
const DETECT_AP_COLLECTION = 6; const DETECT_AP_COLLECTION = 6;
const DETECT_UNSPECIFIC = [self::DETECT_MANUAL, self::DETECT_HEADER, self::DETECT_BODY, self::DETECT_HOST_META, self::DETECT_CONTACTS, self::DETECT_AP_ACTOR]; const DETECT_UNSPECIFIC = [self::DETECT_MANUAL, self::DETECT_HEADER, self::DETECT_BODY, self::DETECT_HOST_META, self::DETECT_CONTACTS, self::DETECT_AP_ACTOR];
// Implementation specific endpoints // Implementation specific endpoints
// @todo Possibly add Lemmy detection via the endpoint /api/v3/site // @todo Possibly add Lemmy detection via the endpoint /api/v3/site
const DETECT_FRIENDIKA = 10; const DETECT_FRIENDIKA = 10;
const DETECT_FRIENDICA = 11; const DETECT_FRIENDICA = 11;
const DETECT_STATUSNET = 12; const DETECT_STATUSNET = 12;
const DETECT_GNUSOCIAL = 13; const DETECT_GNUSOCIAL = 13;
const DETECT_CONFIG_JSON = 14; // Statusnet, GNU Social, Older Hubzilla/Redmatrix const DETECT_CONFIG_JSON = 14; // Statusnet, GNU Social, Older Hubzilla/Redmatrix
const DETECT_SITEINFO_JSON = 15; // Newer Hubzilla const DETECT_SITEINFO_JSON = 15; // Newer Hubzilla
const DETECT_MASTODON_API = 16; const DETECT_MASTODON_API = 16;
const DETECT_STATUS_PHP = 17; // Nextcloud const DETECT_STATUS_PHP = 17; // Nextcloud
const DETECT_V1_CONFIG = 18; const DETECT_V1_CONFIG = 18;
const DETECT_SYSTEM_ACTOR = 20; // Mistpark, Osada, Roadhouse, Zap const DETECT_SYSTEM_ACTOR = 20; // Mistpark, Osada, Roadhouse, Zap
const DETECT_THREADS = 21; const DETECT_THREADS = 21;
// Standardized endpoints // Standardized endpoints
const DETECT_STATISTICS_JSON = 100; const DETECT_STATISTICS_JSON = 100;
const DETECT_NODEINFO_10 = 101; // Nodeinfo Version 1.0 const DETECT_NODEINFO_10 = 101; // Nodeinfo Version 1.0
const DETECT_NODEINFO_20 = 102; // Nodeinfo Version 2.0 const DETECT_NODEINFO_20 = 102; // Nodeinfo Version 2.0
const DETECT_NODEINFO2_10 = 103; // Nodeinfo2 Version 1.0 const DETECT_NODEINFO2_10 = 103; // Nodeinfo2 Version 1.0
const DETECT_NODEINFO_21 = 104; // Nodeinfo Version 2.1 const DETECT_NODEINFO_21 = 104; // Nodeinfo Version 2.1
const DETECT_NODEINFO_22 = 105; // Nodeinfo Version 2.2 const DETECT_NODEINFO_22 = 105; // Nodeinfo Version 2.2
/** /**
* Check for the existence of a server and adds it in the background if not existant * Check for the existence of a server and adds it in the background if not existant
@ -343,7 +343,7 @@ class GServer
$gserver = DBA::selectFirst('gserver', [], ['nurl' => Strings::normaliseLink($server_url)]); $gserver = DBA::selectFirst('gserver', [], ['nurl' => Strings::normaliseLink($server_url)]);
if (DBA::isResult($gserver)) { if (DBA::isResult($gserver)) {
if ($gserver['created'] <= DBA::NULL_DATETIME) { if ($gserver['created'] <= DBA::NULL_DATETIME) {
$fields = ['created' => DateTimeFormat::utcNow()]; $fields = ['created' => DateTimeFormat::utcNow()];
$condition = ['nurl' => Strings::normaliseLink($server_url)]; $condition = ['nurl' => Strings::normaliseLink($server_url)];
self::update($fields, $condition); self::update($fields, $condition);
} }
@ -450,9 +450,11 @@ class GServer
$gserver = DBA::selectFirst('gserver', [], ['nurl' => $nurl]); $gserver = DBA::selectFirst('gserver', [], ['nurl' => $nurl]);
if (DBA::isResult($gserver)) { if (DBA::isResult($gserver)) {
$next_update = self::getNextUpdateDate(false, $gserver['created'], $gserver['last_contact']); $next_update = self::getNextUpdateDate(false, $gserver['created'], $gserver['last_contact']);
self::update(['url' => $url, 'failed' => true, 'blocked' => Network::isUrlBlocked($url), 'last_failure' => DateTimeFormat::utcNow(), self::update(
'next_contact' => $next_update, 'network' => Protocol::PHANTOM, 'detection-method' => null], ['url' => $url, 'failed' => true, 'blocked' => Network::isUrlBlocked($url), 'last_failure' => DateTimeFormat::utcNow(),
['nurl' => $nurl]); 'next_contact' => $next_update, 'network' => Protocol::PHANTOM, 'detection-method' => null],
['nurl' => $nurl]
);
DI::logger()->info('Set failed status for existing server', ['url' => $url]); DI::logger()->info('Set failed status for existing server', ['url' => $url]);
if (self::isDefunct($gserver)) { if (self::isDefunct($gserver)) {
self::archiveContacts($gserver['id']); self::archiveContacts($gserver['id']);
@ -461,8 +463,8 @@ class GServer
} }
self::insert(['url' => $url, 'nurl' => $nurl, self::insert(['url' => $url, 'nurl' => $nurl,
'network' => Protocol::PHANTOM, 'created' => DateTimeFormat::utcNow(), 'network' => Protocol::PHANTOM, 'created' => DateTimeFormat::utcNow(),
'failed' => true, 'last_failure' => DateTimeFormat::utcNow()]); 'failed' => true, 'last_failure' => DateTimeFormat::utcNow()]);
DI::logger()->info('Set failed status for new server', ['url' => $url]); DI::logger()->info('Set failed status for new server', ['url' => $url]);
} }
@ -592,7 +594,7 @@ class GServer
if ((parse_url($url, PHP_URL_HOST) == parse_url($valid_url, PHP_URL_HOST)) && if ((parse_url($url, PHP_URL_HOST) == parse_url($valid_url, PHP_URL_HOST)) &&
(parse_url($url, PHP_URL_PATH) == parse_url($valid_url, PHP_URL_PATH)) && (parse_url($url, PHP_URL_PATH) == parse_url($valid_url, PHP_URL_PATH)) &&
(parse_url($url, PHP_URL_SCHEME) != parse_url($valid_url, PHP_URL_SCHEME))) { (parse_url($url, PHP_URL_SCHEME) != parse_url($valid_url, PHP_URL_SCHEME))) {
$url = $valid_url; $url = $valid_url;
} }
$in_webroot = empty(parse_url($url, PHP_URL_PATH)); $in_webroot = empty(parse_url($url, PHP_URL_PATH));
@ -627,10 +629,10 @@ class GServer
if ($serverdata['network'] == Protocol::PHANTOM) { if ($serverdata['network'] == Protocol::PHANTOM) {
if ($in_webroot) { if ($in_webroot) {
// Fetch the landing page, possibly it reveals some data // Fetch the landing page, possibly it reveals some data
$accept = 'application/activity+json,application/ld+json,application/json,*/*;q=0.9'; $accept = 'application/activity+json,application/ld+json,application/json,*/*;q=0.9';
$curlResult = DI::httpClient()->get($url, $accept, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]); $curlResult = DI::httpClient()->get($url, $accept, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess() && $curlResult->getReturnCode() == '406') { if (!$curlResult->isSuccess() && $curlResult->getReturnCode() == '406') {
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]); $curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
$html_fetched = true; $html_fetched = true;
} else { } else {
$html_fetched = false; $html_fetched = false;
@ -639,8 +641,8 @@ class GServer
if ($curlResult->isSuccess()) { if ($curlResult->isSuccess()) {
$json = json_decode($curlResult->getBodyString(), true); $json = json_decode($curlResult->getBodyString(), true);
if (!empty($json) && is_array($json)) { if (!empty($json) && is_array($json)) {
$data = self::fetchDataFromSystemActor($json, $serverdata); $data = self::fetchDataFromSystemActor($json, $serverdata);
$serverdata = $data['server']; $serverdata = $data['server'];
$systemactor = $data['actor']; $systemactor = $data['actor'];
if (!$html_fetched && !in_array($serverdata['detection-method'], [self::DETECT_SYSTEM_ACTOR, self::DETECT_AP_COLLECTION])) { if (!$html_fetched && !in_array($serverdata['detection-method'], [self::DETECT_SYSTEM_ACTOR, self::DETECT_AP_COLLECTION])) {
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]); $curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
@ -739,7 +741,7 @@ class GServer
return false; return false;
} }
$serverdata['url'] = $url; $serverdata['url'] = $url;
$serverdata['nurl'] = Strings::normaliseLink($url); $serverdata['nurl'] = Strings::normaliseLink($url);
// We have to prevent an endless loop here. // We have to prevent an endless loop here.
@ -803,10 +805,10 @@ class GServer
$gserver = DBA::selectFirst('gserver', ['network'], ['nurl' => Strings::normaliseLink($url)]); $gserver = DBA::selectFirst('gserver', ['network'], ['nurl' => Strings::normaliseLink($url)]);
if (!DBA::isResult($gserver)) { if (!DBA::isResult($gserver)) {
$serverdata['created'] = DateTimeFormat::utcNow(); $serverdata['created'] = DateTimeFormat::utcNow();
$ret = self::insert($serverdata); $ret = self::insert($serverdata);
$id = DBA::lastInsertId(); $id = DBA::lastInsertId();
} else { } else {
$ret = self::update($serverdata, ['nurl' => $serverdata['nurl']]); $ret = self::update($serverdata, ['nurl' => $serverdata['nurl']]);
$gserver = DBA::selectFirst('gserver', ['id'], ['nurl' => $serverdata['nurl']]); $gserver = DBA::selectFirst('gserver', ['id'], ['nurl' => $serverdata['nurl']]);
if (DBA::isResult($gserver)) { if (DBA::isResult($gserver)) {
$id = $gserver['id']; $id = $gserver['id'];
@ -816,8 +818,8 @@ class GServer
// Count the number of known contacts from this server // Count the number of known contacts from this server
if (!empty($id) && !in_array($serverdata['network'], [Protocol::PHANTOM, Protocol::FEED])) { if (!empty($id) && !in_array($serverdata['network'], [Protocol::PHANTOM, Protocol::FEED])) {
$apcontacts = DBA::count('apcontact', ['gsid' => $id]); $apcontacts = DBA::count('apcontact', ['gsid' => $id]);
$contacts = DBA::count('contact', ['uid' => 0, 'gsid' => $id, 'failed' => false]); $contacts = DBA::count('contact', ['uid' => 0, 'gsid' => $id, 'failed' => false]);
$max_users = max($apcontacts, $contacts); $max_users = max($apcontacts, $contacts);
if ($max_users > $serverdata['registered-users']) { if ($max_users > $serverdata['registered-users']) {
DI::logger()->info('Update registered users', ['id' => $id, 'url' => $serverdata['nurl'], 'registered-users' => $max_users]); DI::logger()->info('Update registered users', ['id' => $id, 'url' => $serverdata['nurl'], 'registered-users' => $max_users]);
self::update(['registered-users' => $max_users], ['id' => $id]); self::update(['registered-users' => $max_users], ['id' => $id]);
@ -846,7 +848,7 @@ class GServer
if (!empty($systemactor)) { if (!empty($systemactor)) {
$contact = Contact::getByURL($systemactor, true, ['gsid', 'baseurl', 'id', 'network', 'url', 'name']); $contact = Contact::getByURL($systemactor, true, ['gsid', 'baseurl', 'id', 'network', 'url', 'name']);
DI::logger()->debug('Fetched system actor', ['url' => $url, 'gsid' => $id, 'contact' => $contact]); DI::logger()->debug('Fetched system actor', ['url' => $url, 'gsid' => $id, 'contact' => $contact]);
} }
return $ret; return $ret;
@ -879,9 +881,9 @@ class GServer
$data['subscribe'] = (bool)($data['subscribe'] ?? false); $data['subscribe'] = (bool)($data['subscribe'] ?? false);
if (!$data['subscribe'] || empty($data['scope']) || !in_array(strtolower($data['scope']), ['all', 'tags'])) { if (!$data['subscribe'] || empty($data['scope']) || !in_array(strtolower($data['scope']), ['all', 'tags'])) {
$data['scope'] = ''; $data['scope'] = '';
$data['subscribe'] = false; $data['subscribe'] = false;
$data['tags'] = []; $data['tags'] = [];
} }
$gserver = DBA::selectFirst('gserver', ['id', 'url', 'network', 'relay-subscribe', 'relay-scope'], ['nurl' => Strings::normaliseLink($server_url)]); $gserver = DBA::selectFirst('gserver', ['id', 'url', 'network', 'relay-subscribe', 'relay-scope'], ['nurl' => Strings::normaliseLink($server_url)]);
@ -975,13 +977,13 @@ class GServer
return $serverdata; return $serverdata;
} }
$valid = false; $valid = false;
$old_serverdata = $serverdata; $old_serverdata = $serverdata;
$serverdata['detection-method'] = self::DETECT_STATISTICS_JSON; $serverdata['detection-method'] = self::DETECT_STATISTICS_JSON;
if (!empty($data['version'])) { if (!empty($data['version'])) {
$valid = true; $valid = true;
$serverdata['version'] = $data['version']; $serverdata['version'] = $data['version'];
// Version numbers on statistics.json are presented with additional info, e.g.: // Version numbers on statistics.json are presented with additional info, e.g.:
// 0.6.3.0-p1702cc1c, 0.6.99.0-p1b9ab160 or 3.4.3-2-1191. // 0.6.3.0-p1702cc1c, 0.6.99.0-p1b9ab160 or 3.4.3-2-1191.
@ -989,12 +991,12 @@ class GServer
} }
if (!empty($data['name'])) { if (!empty($data['name'])) {
$valid = true; $valid = true;
$serverdata['site_name'] = $data['name']; $serverdata['site_name'] = $data['name'];
} }
if (!empty($data['network'])) { if (!empty($data['network'])) {
$valid = true; $valid = true;
$serverdata['platform'] = strtolower($data['network']); $serverdata['platform'] = strtolower($data['network']);
if ($serverdata['platform'] == 'diaspora') { if ($serverdata['platform'] == 'diaspora') {
@ -1009,22 +1011,22 @@ class GServer
} }
if (!empty($data['total_users'])) { if (!empty($data['total_users'])) {
$valid = true; $valid = true;
$serverdata['registered-users'] = max($data['total_users'], 1); $serverdata['registered-users'] = max($data['total_users'], 1);
} }
if (!empty($data['active_users_monthly'])) { if (!empty($data['active_users_monthly'])) {
$valid = true; $valid = true;
$serverdata['active-month-users'] = max($data['active_users_monthly'], 0); $serverdata['active-month-users'] = max($data['active_users_monthly'], 0);
} }
if (!empty($data['active_users_halfyear'])) { if (!empty($data['active_users_halfyear'])) {
$valid = true; $valid = true;
$serverdata['active-halfyear-users'] = max($data['active_users_halfyear'], 0); $serverdata['active-halfyear-users'] = max($data['active_users_halfyear'], 0);
} }
if (!empty($data['local_posts'])) { if (!empty($data['local_posts'])) {
$valid = true; $valid = true;
$serverdata['local-posts'] = max($data['local_posts'], 0); $serverdata['local-posts'] = max($data['local_posts'], 0);
} }
@ -1065,8 +1067,8 @@ class GServer
return []; return [];
} }
$nodeinfo1_url = ''; $nodeinfo1_url = '';
$nodeinfo2_url = ''; $nodeinfo2_url = '';
$detection_method = self::DETECT_MANUAL; $detection_method = self::DETECT_MANUAL;
foreach ($nodeinfo['links'] as $link) { foreach ($nodeinfo['links'] as $link) {
@ -1078,13 +1080,13 @@ class GServer
if ($link['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/1.0') { if ($link['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/1.0') {
$nodeinfo1_url = Network::addBasePath($link['href'], $httpResult->getUrl()); $nodeinfo1_url = Network::addBasePath($link['href'], $httpResult->getUrl());
} elseif (($detection_method < self::DETECT_NODEINFO_20) && ($link['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/2.0')) { } elseif (($detection_method < self::DETECT_NODEINFO_20) && ($link['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/2.0')) {
$nodeinfo2_url = Network::addBasePath($link['href'], $httpResult->getUrl()); $nodeinfo2_url = Network::addBasePath($link['href'], $httpResult->getUrl());
$detection_method = self::DETECT_NODEINFO_20; $detection_method = self::DETECT_NODEINFO_20;
} elseif (($detection_method < self::DETECT_NODEINFO_21) && ($link['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/2.1')) { } elseif (($detection_method < self::DETECT_NODEINFO_21) && ($link['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/2.1')) {
$nodeinfo2_url = Network::addBasePath($link['href'], $httpResult->getUrl()); $nodeinfo2_url = Network::addBasePath($link['href'], $httpResult->getUrl());
$detection_method = self::DETECT_NODEINFO_21; $detection_method = self::DETECT_NODEINFO_21;
} elseif (($detection_method < self::DETECT_NODEINFO_22) && ($link['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/2.2')) { } elseif (($detection_method < self::DETECT_NODEINFO_22) && ($link['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/2.2')) {
$nodeinfo2_url = Network::addBasePath($link['href'], $httpResult->getUrl()); $nodeinfo2_url = Network::addBasePath($link['href'], $httpResult->getUrl());
$detection_method = self::DETECT_NODEINFO_22; $detection_method = self::DETECT_NODEINFO_22;
} }
} }
@ -1132,7 +1134,7 @@ class GServer
$server = [ $server = [
'detection-method' => self::DETECT_NODEINFO_10, 'detection-method' => self::DETECT_NODEINFO_10,
'register_policy' => Register::CLOSED 'register_policy' => Register::CLOSED
]; ];
if (!empty($nodeinfo['openRegistrations'])) { if (!empty($nodeinfo['openRegistrations'])) {
@ -1232,8 +1234,8 @@ class GServer
$server = [ $server = [
'detection-method' => $detection_method, 'detection-method' => $detection_method,
'register_policy' => Register::CLOSED, 'register_policy' => Register::CLOSED,
'platform' => 'unknown', 'platform' => 'unknown',
]; ];
if (!empty($nodeinfo['openRegistrations'])) { if (!empty($nodeinfo['openRegistrations'])) {
@ -1363,7 +1365,7 @@ class GServer
$server = [ $server = [
'detection-method' => self::DETECT_NODEINFO2_10, 'detection-method' => self::DETECT_NODEINFO2_10,
'register_policy' => Register::CLOSED 'register_policy' => Register::CLOSED
]; ];
if (!empty($nodeinfo['openRegistrations'])) { if (!empty($nodeinfo['openRegistrations'])) {
@ -1471,7 +1473,7 @@ class GServer
if (!empty($data['platform'])) { if (!empty($data['platform'])) {
$serverdata['platform'] = strtolower($data['platform']); $serverdata['platform'] = strtolower($data['platform']);
$serverdata['version'] = $data['version'] ?? 'N/A'; $serverdata['version'] = $data['version'] ?? 'N/A';
} }
if (!empty($data['plugins'])) { if (!empty($data['plugins'])) {
@ -1547,17 +1549,17 @@ class GServer
$actor = JsonLD::compact($data, false); $actor = JsonLD::compact($data, false);
if (in_array(JsonLD::fetchElement($actor, '@type'), ActivityPub\Receiver::ACCOUNT_TYPES)) { if (in_array(JsonLD::fetchElement($actor, '@type'), ActivityPub\Receiver::ACCOUNT_TYPES)) {
$serverdata['network'] = Protocol::ACTIVITYPUB; $serverdata['network'] = Protocol::ACTIVITYPUB;
$serverdata['site_name'] = JsonLD::fetchElement($actor, 'as:name', '@value'); $serverdata['site_name'] = JsonLD::fetchElement($actor, 'as:name', '@value');
$serverdata['info'] = JsonLD::fetchElement($actor, 'as:summary', '@value'); $serverdata['info'] = JsonLD::fetchElement($actor, 'as:summary', '@value');
if (self::isNomad($actor)) { if (self::isNomad($actor)) {
$serverdata['platform'] = self::getNomadName($actor['@id']); $serverdata['platform'] = self::getNomadName($actor['@id']);
$serverdata['version'] = self::getNomadVersion($actor['@id']); $serverdata['version'] = self::getNomadVersion($actor['@id']);
$serverdata['detection-method'] = self::DETECT_SYSTEM_ACTOR; $serverdata['detection-method'] = self::DETECT_SYSTEM_ACTOR;
} elseif (!empty($actor['as:generator'])) { } elseif (!empty($actor['as:generator'])) {
$generator = explode(' ', JsonLD::fetchElement($actor['as:generator'], 'as:name', '@value')); $generator = explode(' ', JsonLD::fetchElement($actor['as:generator'], 'as:name', '@value'));
$serverdata['platform'] = strtolower(array_shift($generator)); $serverdata['platform'] = strtolower(array_shift($generator));
$serverdata['version'] = self::getNomadVersion($actor['@id']); $serverdata['version'] = self::getNomadVersion($actor['@id']);
$serverdata['detection-method'] = self::DETECT_SYSTEM_ACTOR; $serverdata['detection-method'] = self::DETECT_SYSTEM_ACTOR;
} else { } else {
$serverdata['detection-method'] = self::DETECT_AP_ACTOR; $serverdata['detection-method'] = self::DETECT_AP_ACTOR;
@ -1565,8 +1567,8 @@ class GServer
return ['server' => $serverdata, 'actor' => $actor['@id']]; return ['server' => $serverdata, 'actor' => $actor['@id']];
} elseif ((JsonLD::fetchElement($actor, '@type') == 'as:Collection')) { } elseif ((JsonLD::fetchElement($actor, '@type') == 'as:Collection')) {
// By now only Ktistec seems to provide collections this way // By now only Ktistec seems to provide collections this way
$serverdata['platform'] = 'ktistec'; $serverdata['platform'] = 'ktistec';
$serverdata['network'] = Protocol::ACTIVITYPUB; $serverdata['network'] = Protocol::ACTIVITYPUB;
$serverdata['detection-method'] = self::DETECT_AP_COLLECTION; $serverdata['detection-method'] = self::DETECT_AP_COLLECTION;
$actors = JsonLD::fetchElementArray($actor, 'as:items'); $actors = JsonLD::fetchElementArray($actor, 'as:items');
@ -1610,7 +1612,7 @@ class GServer
*/ */
private static function getNomadName(string $url): string private static function getNomadName(string $url): string
{ {
$name = 'nomad'; $name = 'nomad';
$curlResult = DI::httpClient()->get($url . '/manifest', 'application/manifest+json', [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]); $curlResult = DI::httpClient()->get($url . '/manifest', 'application/manifest+json', [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess() || ($curlResult->getBodyString() == '')) { if (!$curlResult->isSuccess() || ($curlResult->getBodyString() == '')) {
if ($curlResult->getReturnCode() == 418) { if ($curlResult->getReturnCode() == 418) {
@ -1729,7 +1731,7 @@ class GServer
private static function validHostMeta(string $url): bool private static function validHostMeta(string $url): bool
{ {
$xrd_timeout = DI::config()->get('system', 'xrd_timeout'); $xrd_timeout = DI::config()->get('system', 'xrd_timeout');
$curlResult = DI::httpClient()->get($url . Probe::HOST_META, HttpClientAccept::XRD_XML, [HttpClientOptions::TIMEOUT => $xrd_timeout, HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]); $curlResult = DI::httpClient()->get($url . Probe::HOST_META, HttpClientAccept::XRD_XML, [HttpClientOptions::TIMEOUT => $xrd_timeout, HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess()) { if (!$curlResult->isSuccess()) {
return false; return false;
} }
@ -1828,10 +1830,10 @@ class GServer
} }
if (!empty($data['totalResults'])) { if (!empty($data['totalResults'])) {
$registeredUsers = $serverdata['registered-users'] ?? 0; $registeredUsers = $serverdata['registered-users'] ?? 0;
$serverdata['registered-users'] = max($data['totalResults'], $registeredUsers, 1); $serverdata['registered-users'] = max($data['totalResults'], $registeredUsers, 1);
$serverdata['directory-type'] = self::DT_POCO; $serverdata['directory-type'] = self::DT_POCO;
$serverdata['poco'] = $url . '/poco'; $serverdata['poco'] = $url . '/poco';
} }
return $serverdata; return $serverdata;
@ -1886,8 +1888,8 @@ class GServer
if (!empty($data['instance']) && !empty($data['serverVersion'])) { if (!empty($data['instance']) && !empty($data['serverVersion'])) {
$serverdata['platform'] = 'peertube'; $serverdata['platform'] = 'peertube';
$serverdata['version'] = $data['serverVersion']; $serverdata['version'] = $data['serverVersion'];
$serverdata['network'] = Protocol::ACTIVITYPUB; $serverdata['network'] = Protocol::ACTIVITYPUB;
if (!empty($data['instance']['name'])) { if (!empty($data['instance']['name'])) {
$serverdata['site_name'] = $data['instance']['name']; $serverdata['site_name'] = $data['instance']['name'];
@ -1934,7 +1936,7 @@ class GServer
if (!empty($data['version'])) { if (!empty($data['version'])) {
$serverdata['platform'] = 'nextcloud'; $serverdata['platform'] = 'nextcloud';
$serverdata['version'] = $data['version']; $serverdata['version'] = $data['version'];
if ($validHostMeta) { if ($validHostMeta) {
$serverdata['network'] = Protocol::ACTIVITYPUB; $serverdata['network'] = Protocol::ACTIVITYPUB;
@ -2012,9 +2014,9 @@ class GServer
if (!empty($data['version'])) { if (!empty($data['version'])) {
$serverdata['platform'] = 'mastodon'; $serverdata['platform'] = 'mastodon';
$serverdata['version'] = $data['version'] ?? ''; $serverdata['version'] = $data['version'] ?? '';
$serverdata['network'] = Protocol::ACTIVITYPUB; $serverdata['network'] = Protocol::ACTIVITYPUB;
$valid = true; $valid = true;
} }
if (!empty($data['title'])) { if (!empty($data['title'])) {
@ -2023,8 +2025,8 @@ class GServer
if (!empty($data['title']) && empty($serverdata['platform']) && ($serverdata['network'] == Protocol::PHANTOM)) { if (!empty($data['title']) && empty($serverdata['platform']) && ($serverdata['network'] == Protocol::PHANTOM)) {
$serverdata['platform'] = 'mastodon'; $serverdata['platform'] = 'mastodon';
$serverdata['network'] = Protocol::ACTIVITYPUB; $serverdata['network'] = Protocol::ACTIVITYPUB;
$valid = true; $valid = true;
} }
if (!empty($data['description'])) { if (!empty($data['description'])) {
@ -2037,20 +2039,20 @@ class GServer
if (!empty($serverdata['version']) && preg_match('/.*?\(compatible;\s(.*)\s(.*)\)/ism', $serverdata['version'], $matches)) { if (!empty($serverdata['version']) && preg_match('/.*?\(compatible;\s(.*)\s(.*)\)/ism', $serverdata['version'], $matches)) {
$serverdata['platform'] = strtolower($matches[1]); $serverdata['platform'] = strtolower($matches[1]);
$serverdata['version'] = $matches[2]; $serverdata['version'] = $matches[2];
$valid = true; $valid = true;
} }
if (!empty($serverdata['version']) && strstr(strtolower($serverdata['version']), 'pleroma')) { if (!empty($serverdata['version']) && strstr(strtolower($serverdata['version']), 'pleroma')) {
$serverdata['platform'] = 'pleroma'; $serverdata['platform'] = 'pleroma';
$serverdata['version'] = trim(str_ireplace('pleroma', '', $serverdata['version'])); $serverdata['version'] = trim(str_ireplace('pleroma', '', $serverdata['version']));
$valid = true; $valid = true;
} }
if (!empty($serverdata['platform']) && strstr($serverdata['platform'], 'pleroma')) { if (!empty($serverdata['platform']) && strstr($serverdata['platform'], 'pleroma')) {
$serverdata['version'] = trim(str_ireplace('pleroma', '', $serverdata['platform'])); $serverdata['version'] = trim(str_ireplace('pleroma', '', $serverdata['platform']));
$serverdata['platform'] = 'pleroma'; $serverdata['platform'] = 'pleroma';
$valid = true; $valid = true;
} }
if ($valid && in_array($serverdata['detection-method'], self::DETECT_UNSPECIFIC)) { if ($valid && in_array($serverdata['detection-method'], self::DETECT_UNSPECIFIC)) {
@ -2086,14 +2088,14 @@ class GServer
if (!empty($data['site']['platform'])) { if (!empty($data['site']['platform'])) {
$serverdata['platform'] = strtolower($data['site']['platform']['PLATFORM_NAME']); $serverdata['platform'] = strtolower($data['site']['platform']['PLATFORM_NAME']);
$serverdata['version'] = $data['site']['platform']['STD_VERSION']; $serverdata['version'] = $data['site']['platform']['STD_VERSION'];
$serverdata['network'] = Protocol::ZOT; $serverdata['network'] = Protocol::ZOT;
} }
if (!empty($data['site']['hubzilla'])) { if (!empty($data['site']['hubzilla'])) {
$serverdata['platform'] = strtolower($data['site']['hubzilla']['PLATFORM_NAME']); $serverdata['platform'] = strtolower($data['site']['hubzilla']['PLATFORM_NAME']);
$serverdata['version'] = $data['site']['hubzilla']['RED_VERSION']; $serverdata['version'] = $data['site']['hubzilla']['RED_VERSION'];
$serverdata['network'] = Protocol::ZOT; $serverdata['network'] = Protocol::ZOT;
} }
if (!empty($data['site']['redmatrix'])) { if (!empty($data['site']['redmatrix'])) {
@ -2107,9 +2109,9 @@ class GServer
$serverdata['network'] = Protocol::ZOT; $serverdata['network'] = Protocol::ZOT;
} }
$private = false; $private = false;
$inviteonly = false; $inviteonly = false;
$closed = false; $closed = false;
if (!empty($data['site']['closed'])) { if (!empty($data['site']['closed'])) {
$closed = self::toBoolean($data['site']['closed']); $closed = self::toBoolean($data['site']['closed']);
@ -2196,11 +2198,11 @@ class GServer
if (!empty($serverdata['version']) && strtolower(substr($serverdata['version'], 0, 7)) == 'pleroma') { if (!empty($serverdata['version']) && strtolower(substr($serverdata['version'], 0, 7)) == 'pleroma') {
$serverdata['platform'] = 'pleroma'; $serverdata['platform'] = 'pleroma';
$serverdata['version'] = trim(str_ireplace('pleroma', '', $serverdata['version'])); $serverdata['version'] = trim(str_ireplace('pleroma', '', $serverdata['version']));
$serverdata['network'] = Protocol::ACTIVITYPUB; $serverdata['network'] = Protocol::ACTIVITYPUB;
} else { } else {
$serverdata['platform'] = 'statusnet'; $serverdata['platform'] = 'statusnet';
$serverdata['network'] = Protocol::OSTATUS; $serverdata['network'] = Protocol::OSTATUS;
} }
if (in_array($serverdata['detection-method'], self::DETECT_UNSPECIFIC)) { if (in_array($serverdata['detection-method'], self::DETECT_UNSPECIFIC)) {
@ -2226,11 +2228,11 @@ class GServer
$curlResult = DI::httpClient()->get($url . '/friendica/json', HttpClientAccept::DEFAULT, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]); $curlResult = DI::httpClient()->get($url . '/friendica/json', HttpClientAccept::DEFAULT, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess()) { if (!$curlResult->isSuccess()) {
$curlResult = DI::httpClient()->get($url . '/friendika/json', HttpClientAccept::DEFAULT, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]); $curlResult = DI::httpClient()->get($url . '/friendika/json', HttpClientAccept::DEFAULT, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
$friendika = true; $friendika = true;
$platform = 'Friendika'; $platform = 'Friendika';
} else { } else {
$friendika = false; $friendika = false;
$platform = 'Friendica'; $platform = 'Friendica';
} }
if (!$curlResult->isSuccess()) { if (!$curlResult->isSuccess()) {
@ -2316,14 +2318,14 @@ class GServer
$doc = new DOMDocument(); $doc = new DOMDocument();
@$doc->loadHTML($curlResult->getBodyString()); @$doc->loadHTML($curlResult->getBodyString());
$xpath = new DOMXPath($doc); $xpath = new DOMXPath($doc);
$assigned = false; $assigned = false;
// We can only detect honk via some HTML element on their page // We can only detect honk via some HTML element on their page
if ($xpath->query('//div[@id="honksonpage"]')->count() == 1) { if ($xpath->query('//div[@id="honksonpage"]')->count() == 1) {
$serverdata['platform'] = 'honk'; $serverdata['platform'] = 'honk';
$serverdata['network'] = Protocol::ACTIVITYPUB; $serverdata['network'] = Protocol::ACTIVITYPUB;
$assigned = true; $assigned = true;
} }
$title = trim(XML::getFirstNodeValue($xpath, '//head/title/text()')); $title = trim(XML::getFirstNodeValue($xpath, '//head/title/text()'));
@ -2356,11 +2358,11 @@ class GServer
if (in_array($attr['name'], ['application-name', 'al:android:app_name', 'al:ios:app_name', if (in_array($attr['name'], ['application-name', 'al:android:app_name', 'al:ios:app_name',
'twitter:app:name:googleplay', 'twitter:app:name:iphone', 'twitter:app:name:ipad', 'generator'])) { 'twitter:app:name:googleplay', 'twitter:app:name:iphone', 'twitter:app:name:ipad', 'generator'])) {
$platform = str_ireplace(array_keys($platforms), array_values($platforms), $attr['content']); $platform = str_ireplace(array_keys($platforms), array_values($platforms), $attr['content']);
$platform = str_replace('/', ' ', $platform); $platform = str_replace('/', ' ', $platform);
$platform_parts = explode(' ', $platform); $platform_parts = explode(' ', $platform);
if ((count($platform_parts) >= 2) && in_array(strtolower($platform_parts[0]), array_values($platforms))) { if ((count($platform_parts) >= 2) && in_array(strtolower($platform_parts[0]), array_values($platforms))) {
$platform = $platform_parts[0]; $platform = $platform_parts[0];
$serverdata['version'] = $platform_parts[1]; $serverdata['version'] = $platform_parts[1];
} }
if (in_array($platform, array_values($grouped_platforms['dfrn_platforms']))) { if (in_array($platform, array_values($grouped_platforms['dfrn_platforms']))) {
@ -2372,7 +2374,7 @@ class GServer
} }
if (in_array($platform, array_values($platforms))) { if (in_array($platform, array_values($platforms))) {
$serverdata['platform'] = $platform; $serverdata['platform'] = $platform;
$assigned = true; $assigned = true;
} }
} }
} }
@ -2407,7 +2409,7 @@ class GServer
if (in_array($attr['property'], ['og:platform', 'generator'])) { if (in_array($attr['property'], ['og:platform', 'generator'])) {
if (in_array($attr['content'], array_keys($platforms))) { if (in_array($attr['content'], array_keys($platforms))) {
$serverdata['platform'] = $platforms[$attr['content']]; $serverdata['platform'] = $platforms[$attr['content']];
$assigned = true; $assigned = true;
} }
if (in_array($attr['content'], array_keys($grouped_platforms['ap_platforms']))) { if (in_array($attr['content'], array_keys($grouped_platforms['ap_platforms']))) {
@ -2422,10 +2424,10 @@ class GServer
foreach ($list as $node) { foreach ($list as $node) {
foreach ($node->attributes as $attribute) { foreach ($node->attributes as $attribute) {
if (parse_url(trim($attribute->value), PHP_URL_HOST) == 'micro.blog') { if (parse_url(trim($attribute->value), PHP_URL_HOST) == 'micro.blog') {
$serverdata['version'] = trim($serverdata['platform'] . ' ' . $serverdata['version']); $serverdata['version'] = trim($serverdata['platform'] . ' ' . $serverdata['version']);
$serverdata['platform'] = 'microblog'; $serverdata['platform'] = 'microblog';
$serverdata['network'] = Protocol::ACTIVITYPUB; $serverdata['network'] = Protocol::ACTIVITYPUB;
$assigned = true; $assigned = true;
} }
} }
} }
@ -2435,10 +2437,10 @@ class GServer
foreach ($list as $node) { foreach ($list as $node) {
foreach ($node->attributes as $attribute) { foreach ($node->attributes as $attribute) {
if (trim($attribute->value) == 'https://micro.blog/micropub') { if (trim($attribute->value) == 'https://micro.blog/micropub') {
$serverdata['version'] = trim($serverdata['platform'] . ' ' . $serverdata['version']); $serverdata['version'] = trim($serverdata['platform'] . ' ' . $serverdata['version']);
$serverdata['platform'] = 'microblog'; $serverdata['platform'] = 'microblog';
$serverdata['network'] = Protocol::ACTIVITYPUB; $serverdata['network'] = Protocol::ACTIVITYPUB;
$assigned = true; $assigned = true;
} }
} }
} }
@ -2463,15 +2465,15 @@ class GServer
{ {
if ($curlResult->getHeader('server') == 'Mastodon') { if ($curlResult->getHeader('server') == 'Mastodon') {
$serverdata['platform'] = 'mastodon'; $serverdata['platform'] = 'mastodon';
$serverdata['network'] = Protocol::ACTIVITYPUB; $serverdata['network'] = Protocol::ACTIVITYPUB;
} elseif ($curlResult->inHeader('x-diaspora-version')) { } elseif ($curlResult->inHeader('x-diaspora-version')) {
$serverdata['platform'] = 'diaspora'; $serverdata['platform'] = 'diaspora';
$serverdata['network'] = Protocol::DIASPORA; $serverdata['network'] = Protocol::DIASPORA;
$serverdata['version'] = $curlResult->getHeader('x-diaspora-version')[0] ?? ''; $serverdata['version'] = $curlResult->getHeader('x-diaspora-version')[0] ?? '';
} elseif ($curlResult->inHeader('x-friendica-version')) { } elseif ($curlResult->inHeader('x-friendica-version')) {
$serverdata['platform'] = 'friendica'; $serverdata['platform'] = 'friendica';
$serverdata['network'] = Protocol::DFRN; $serverdata['network'] = Protocol::DFRN;
$serverdata['version'] = $curlResult->getHeader('x-friendica-version')[0] ?? ''; $serverdata['version'] = $curlResult->getHeader('x-friendica-version')[0] ?? '';
} else { } else {
return $serverdata; return $serverdata;
} }
@ -2501,9 +2503,12 @@ class GServer
$last_update = date('c', time() - (60 * 60 * 24 * $requery_days)); $last_update = date('c', time() - (60 * 60 * 24 * $requery_days));
$gservers = DBA::select('gserver', ['id', 'url', 'nurl', 'network', 'poco', 'directory-type'], $gservers = DBA::select(
'gserver',
['id', 'url', 'nurl', 'network', 'poco', 'directory-type'],
["NOT `blocked` AND NOT `failed` AND `directory-type` != ? AND `last_poco_query` < ?", GServer::DT_NONE, $last_update], ["NOT `blocked` AND NOT `failed` AND `directory-type` != ? AND `last_poco_query` < ?", GServer::DT_NONE, $last_update],
['order' => ['RAND()']]); ['order' => ['RAND()']]
);
while ($gserver = DBA::fetch($gservers)) { while ($gserver = DBA::fetch($gservers)) {
DI::logger()->info('Update peer list', ['server' => $gserver['url'], 'id' => $gserver['id']]); DI::logger()->info('Update peer list', ['server' => $gserver['url'], 'id' => $gserver['id']]);
@ -2541,7 +2546,7 @@ class GServer
// Discover federated servers // Discover federated servers
$protocols = ['activitypub', 'diaspora', 'dfrn', 'ostatus']; $protocols = ['activitypub', 'diaspora', 'dfrn', 'ostatus'];
foreach ($protocols as $protocol) { foreach ($protocols as $protocol) {
$query = '{nodes(protocol:"' . $protocol . '"){host}}'; $query = '{nodes(protocol:"' . $protocol . '"){host}}';
$curlResult = DI::httpClient()->fetch('https://the-federation.info/graphql?query=' . urlencode($query), HttpClientAccept::JSON, 0, '', HttpClientRequest::SERVERDISCOVER); $curlResult = DI::httpClient()->fetch('https://the-federation.info/graphql?query=' . urlencode($query), HttpClientAccept::JSON, 0, '', HttpClientRequest::SERVERDISCOVER);
if (!empty($curlResult)) { if (!empty($curlResult)) {
$data = json_decode($curlResult, true); $data = json_decode($curlResult, true);
@ -2558,7 +2563,7 @@ class GServer
$accesstoken = DI::config()->get('system', 'instances_social_key'); $accesstoken = DI::config()->get('system', 'instances_social_key');
if (!empty($accesstoken)) { if (!empty($accesstoken)) {
$api = 'https://instances.social/api/1.0/instances/list?count=0'; $api = 'https://instances.social/api/1.0/instances/list?count=0';
$curlResult = DI::httpClient()->get($api, HttpClientAccept::JSON, [HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . $accesstoken], HttpClientOptions::REQUEST => HttpClientRequest::SERVERDISCOVER]]); $curlResult = DI::httpClient()->get($api, HttpClientAccept::JSON, [HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . $accesstoken], HttpClientOptions::REQUEST => HttpClientRequest::SERVERDISCOVER]]);
if ($curlResult->isSuccess()) { if ($curlResult->isSuccess()) {
$servers = json_decode($curlResult->getBodyString(), true); $servers = json_decode($curlResult->getBodyString(), true);

View file

@ -45,38 +45,38 @@ use LanguageDetection\Language;
class Item class Item
{ {
// Posting types, inspired by https://www.w3.org/TR/activitystreams-vocabulary/#object-types // Posting types, inspired by https://www.w3.org/TR/activitystreams-vocabulary/#object-types
const PT_ARTICLE = 0; const PT_ARTICLE = 0;
const PT_NOTE = 1; const PT_NOTE = 1;
const PT_PAGE = 2; const PT_PAGE = 2;
const PT_IMAGE = 16; const PT_IMAGE = 16;
const PT_AUDIO = 17; const PT_AUDIO = 17;
const PT_VIDEO = 18; const PT_VIDEO = 18;
const PT_DOCUMENT = 19; const PT_DOCUMENT = 19;
const PT_EVENT = 32; const PT_EVENT = 32;
const PT_POLL = 33; const PT_POLL = 33;
const PT_PERSONAL_NOTE = 128; const PT_PERSONAL_NOTE = 128;
// Posting reasons (Why had a post been stored for a user?) // Posting reasons (Why had a post been stored for a user?)
const PR_NONE = 0; const PR_NONE = 0;
const PR_TAG = 64; const PR_TAG = 64;
const PR_TO = 65; const PR_TO = 65;
const PR_CC = 66; const PR_CC = 66;
const PR_BTO = 67; const PR_BTO = 67;
const PR_BCC = 68; const PR_BCC = 68;
const PR_FOLLOWER = 69; const PR_FOLLOWER = 69;
const PR_ANNOUNCEMENT = 70; const PR_ANNOUNCEMENT = 70;
const PR_COMMENT = 71; const PR_COMMENT = 71;
const PR_STORED = 72; const PR_STORED = 72;
const PR_GLOBAL = 73; const PR_GLOBAL = 73;
const PR_RELAY = 74; const PR_RELAY = 74;
const PR_FETCHED = 75; const PR_FETCHED = 75;
const PR_COMPLETION = 76; const PR_COMPLETION = 76;
const PR_DIRECT = 77; const PR_DIRECT = 77;
const PR_ACTIVITY = 78; const PR_ACTIVITY = 78;
const PR_DISTRIBUTE = 79; const PR_DISTRIBUTE = 79;
const PR_PUSHED = 80; const PR_PUSHED = 80;
const PR_LOCAL = 81; const PR_LOCAL = 81;
const PR_AUDIENCE = 82; const PR_AUDIENCE = 82;
// system.accept_only_sharer setting values // system.accept_only_sharer setting values
const COMPLETION_NONE = 1; const COMPLETION_NONE = 1;
@ -145,8 +145,8 @@ class Item
]; ];
// Privacy levels // Privacy levels
const PUBLIC = 0; const PUBLIC = 0;
const PRIVATE = 1; const PRIVATE = 1;
const UNLISTED = 2; const UNLISTED = 2;
// Item weight for query ordering // Item weight for query ordering
@ -492,7 +492,7 @@ class Item
// Is it in the format data@host.tld? - Used for mail contacts // Is it in the format data@host.tld? - Used for mail contacts
if (empty($prefix_host) && !empty($item['author-link']) && strstr($item['author-link'], '@')) { if (empty($prefix_host) && !empty($item['author-link']) && strstr($item['author-link'], '@')) {
$mailparts = explode('@', $item['author-link']); $mailparts = explode('@', $item['author-link']);
$prefix_host = array_pop($mailparts); $prefix_host = array_pop($mailparts);
} }
} }
@ -596,7 +596,7 @@ class Item
} }
$condition = [ $condition = [
'uri-id' => $item['uri-id'], 'uid' => $item['uid'], 'uri-id' => $item['uri-id'], 'uid' => $item['uid'],
'network' => [$item['network'], Protocol::DFRN] 'network' => [$item['network'], Protocol::DFRN]
]; ];
if (Post::exists($condition)) { if (Post::exists($condition)) {
@ -681,7 +681,7 @@ class Item
} }
$condition = [ $condition = [
'verb' => Activity::FOLLOW, 'uid' => $item['uid'], 'verb' => Activity::FOLLOW, 'uid' => $item['uid'],
'parent-uri' => $item['parent-uri'], 'author-id' => $item['author-id'] 'parent-uri' => $item['parent-uri'], 'author-id' => $item['author-id']
]; ];
if (Post::exists($condition)) { if (Post::exists($condition)) {
@ -716,10 +716,10 @@ class Item
// We only log the entries with a different user id than 0. Otherwise we would have too many false positives // We only log the entries with a different user id than 0. Otherwise we would have too many false positives
if ($item['uid'] != 0) { if ($item['uid'] != 0) {
DI::logger()->notice('Item already existed for user', [ DI::logger()->notice('Item already existed for user', [
'uri-id' => $item['uri-id'], 'uri-id' => $item['uri-id'],
'uid' => $item['uid'], 'uid' => $item['uid'],
'network' => $item['network'], 'network' => $item['network'],
'existing_id' => $existing['id'], 'existing_id' => $existing['id'],
'existing_network' => $existing['network'] 'existing_network' => $existing['network']
]); ]);
} }
@ -766,8 +766,8 @@ class Item
'wall', 'private', 'origin', 'author-id' 'wall', 'private', 'origin', 'author-id'
]; ];
$condition = ['uri-id' => [$item['thr-parent-id'], $item['parent-uri-id']], 'uid' => $item['uid']]; $condition = ['uri-id' => [$item['thr-parent-id'], $item['parent-uri-id']], 'uid' => $item['uid']];
$params = ['order' => ['id' => false]]; $params = ['order' => ['id' => false]];
$parent = Post::selectFirst($fields, $condition, $params); $parent = Post::selectFirst($fields, $condition, $params);
if (!DBA::isResult($parent) && Post::exists(['uri-id' => [$item['thr-parent-id'], $item['parent-uri-id']], 'uid' => 0])) { if (!DBA::isResult($parent) && Post::exists(['uri-id' => [$item['thr-parent-id'], $item['parent-uri-id']], 'uid' => 0])) {
$stored = Item::storeForUserByUriId($item['thr-parent-id'], $item['uid'], ['post-reason' => Item::PR_COMPLETION]); $stored = Item::storeForUserByUriId($item['thr-parent-id'], $item['uid'], ['post-reason' => Item::PR_COMPLETION]);
@ -795,11 +795,11 @@ class Item
} }
$condition = [ $condition = [
'uri-id' => $parent['parent-uri-id'], 'uri-id' => $parent['parent-uri-id'],
'parent-uri-id' => $parent['parent-uri-id'], 'parent-uri-id' => $parent['parent-uri-id'],
'uid' => $parent['uid'] 'uid' => $parent['uid']
]; ];
$params = ['order' => ['id' => false]]; $params = ['order' => ['id' => false]];
$toplevel_parent = Post::selectFirst($fields, $condition, $params); $toplevel_parent = Post::selectFirst($fields, $condition, $params);
if (!DBA::isResult($toplevel_parent) && $item['origin']) { if (!DBA::isResult($toplevel_parent) && $item['origin']) {
@ -884,7 +884,7 @@ class Item
$uid = intval($item['uid']); $uid = intval($item['uid']);
$item['guid'] = self::guid($item, $notify); $item['guid'] = self::guid($item, $notify);
$item['uri'] = substr(trim($item['uri'] ?? '') ?: self::newURI($item['guid']), 0, 255); $item['uri'] = substr(trim($item['uri'] ?? '') ?: self::newURI($item['guid']), 0, 255);
// Store URI data // Store URI data
$item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]); $item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
@ -990,13 +990,13 @@ class Item
$item['gravity'] = self::getGravity($item); $item['gravity'] = self::getGravity($item);
$default = [ $default = [
'url' => $item['author-link'], 'name' => $item['author-name'], 'url' => $item['author-link'], 'name' => $item['author-name'],
'photo' => $item['author-avatar'], 'network' => $item['network'] 'photo' => $item['author-avatar'], 'network' => $item['network']
]; ];
$item['author-id'] = ($item['author-id'] ?? 0) ?: Contact::getIdForURL($item['author-link'], 0, null, $default); $item['author-id'] = ($item['author-id'] ?? 0) ?: Contact::getIdForURL($item['author-link'], 0, null, $default);
$default = [ $default = [
'url' => $item['owner-link'], 'name' => $item['owner-name'], 'url' => $item['owner-link'], 'name' => $item['owner-name'],
'photo' => $item['owner-avatar'], 'network' => $item['network'] 'photo' => $item['owner-avatar'], 'network' => $item['network']
]; ];
$item['owner-id'] = ($item['owner-id'] ?? 0) ?: Contact::getIdForURL($item['owner-link'], 0, null, $default); $item['owner-id'] = ($item['owner-id'] ?? 0) ?: Contact::getIdForURL($item['owner-link'], 0, null, $default);
@ -1072,7 +1072,7 @@ class Item
// Update the contact relations // Update the contact relations
Contact\Relation::store($toplevel_parent['author-id'], $item['author-id'], $item['created']); Contact\Relation::store($toplevel_parent['author-id'], $item['author-id'], $item['created']);
} else { } else {
$parent_id = 0; $parent_id = 0;
$parent_origin = $item['origin']; $parent_origin = $item['origin'];
if ($item['wall'] && empty($item['context'])) { if ($item['wall'] && empty($item['context'])) {
@ -1120,7 +1120,7 @@ class Item
} }
if ($notify && $post_local) { if ($notify && $post_local) {
$item['edit'] = false; $item['edit'] = false;
$item['parent'] = $parent_id; $item['parent'] = $parent_id;
// Trigger automatic reactions for addons // Trigger automatic reactions for addons
@ -1131,8 +1131,8 @@ class Item
// We have to tell the hooks who we are - this really should be improved // We have to tell the hooks who we are - this really should be improved
if (!DI::userSession()->getLocalUserId()) { if (!DI::userSession()->getLocalUserId()) {
$_SESSION['authenticated'] = true; $_SESSION['authenticated'] = true;
$_SESSION['uid'] = $uid; $_SESSION['uid'] = $uid;
$dummy_session = true; $dummy_session = true;
} else { } else {
$dummy_session = false; $dummy_session = false;
} }
@ -1250,9 +1250,9 @@ class Item
$ev['guid'] = $item['guid']; $ev['guid'] = $item['guid'];
$ev['plink'] = $item['plink']; $ev['plink'] = $item['plink'];
$ev['network'] = $item['network']; $ev['network'] = $item['network'];
$ev['protocol'] = $item['protocol'] ?? Conversation::PARCEL_UNKNOWN; $ev['protocol'] = $item['protocol'] ?? Conversation::PARCEL_UNKNOWN;
$ev['direction'] = $item['direction'] ?? Conversation::UNKNOWN; $ev['direction'] = $item['direction'] ?? Conversation::UNKNOWN;
$ev['source'] = $item['source'] ?? ''; $ev['source'] = $item['source'] ?? '';
$event = DBA::selectFirst('event', ['id'], ['uri' => $item['uri'], 'uid' => $item['uid']]); $event = DBA::selectFirst('event', ['id'], ['uri' => $item['uri'], 'uid' => $item['uid']]);
if (DBA::isResult($event)) { if (DBA::isResult($event)) {
@ -1260,7 +1260,7 @@ class Item
} }
$event_id = Event::store($ev); $event_id = Event::store($ev);
$item = Event::getItemArrayForImportedId($event_id, $item); $item = Event::getItemArrayForImportedId($event_id, $item);
DI::logger()->info('Event was stored', ['id' => $event_id]); DI::logger()->info('Event was stored', ['id' => $event_id]);
} }
@ -1542,7 +1542,7 @@ class Item
$count = 0; $count = 0;
foreach (DI::userDefinedChannel()->getMatchingChannelUsers($engagement['searchtext'], $language, $tags, $engagement['media-type'], $item['owner-id'], $reshare_id) as $uid) { foreach (DI::userDefinedChannel()->getMatchingChannelUsers($engagement['searchtext'], $language, $tags, $engagement['media-type'], $item['owner-id'], $reshare_id) as $uid) {
$condition = [ $condition = [
'verb' => Activity::ANNOUNCE, 'deleted' => false, 'gravity' => self::GRAVITY_ACTIVITY, 'verb' => Activity::ANNOUNCE, 'deleted' => false, 'gravity' => self::GRAVITY_ACTIVITY,
'author-id' => Contact::getPublicIdByUserId($uid), 'uid' => $uid, 'thr-parent-id' => $uri_id 'author-id' => Contact::getPublicIdByUserId($uid), 'uid' => $uid, 'thr-parent-id' => $uri_id
]; ];
if (!Post::exists($condition)) { if (!Post::exists($condition)) {
@ -1632,7 +1632,7 @@ class Item
} }
$self_contact = Contact::selectFirst(['id'], ['uid' => $item['uid'], 'self' => true]); $self_contact = Contact::selectFirst(['id'], ['uid' => $item['uid'], 'self' => true]);
$self = !empty($self_contact) ? $self_contact['id'] : 0; $self = !empty($self_contact) ? $self_contact['id'] : 0;
$cid = Contact::getIdForURL($author['url'], $item['uid']); $cid = Contact::getIdForURL($author['url'], $item['uid']);
if (empty($cid) || (!Contact::isSharing($cid, $item['uid']) && ($cid != $self))) { if (empty($cid) || (!Contact::isSharing($cid, $item['uid']) && ($cid != $self))) {
@ -1671,7 +1671,7 @@ class Item
foreach (Tag::getUIDListByURIId($item['uri-id']) as $uid => $tags) { foreach (Tag::getUIDListByURIId($item['uri-id']) as $uid => $tags) {
if (!empty($languages)) { if (!empty($languages)) {
$keep = false; $keep = false;
$user_languages = User::getWantedLanguages($uid); $user_languages = User::getWantedLanguages($uid);
foreach ($user_languages as $language) { foreach ($user_languages as $language) {
if (in_array($language, $languages)) { if (in_array($language, $languages)) {
@ -1705,7 +1705,7 @@ class Item
public static function distribute(int $itemid, string $signed_text = '') public static function distribute(int $itemid, string $signed_text = '')
{ {
$condition = ["`id` IN (SELECT `parent` FROM `post-user-view` WHERE `id` = ?)", $itemid]; $condition = ["`id` IN (SELECT `parent` FROM `post-user-view` WHERE `id` = ?)", $itemid];
$parent = Post::selectFirst(['owner-id'], $condition); $parent = Post::selectFirst(['owner-id'], $condition);
if (!DBA::isResult($parent)) { if (!DBA::isResult($parent)) {
DI::logger()->warning('Item not found', ['condition' => $condition]); DI::logger()->warning('Item not found', ['condition' => $condition]);
return; return;
@ -1713,7 +1713,7 @@ class Item
// Only distribute public items from native networks // Only distribute public items from native networks
$condition = [ $condition = [
'id' => $itemid, 'uid' => 0, 'id' => $itemid, 'uid' => 0,
'network' => array_merge(Protocol::FEDERATED, ['']), 'network' => array_merge(Protocol::FEDERATED, ['']),
'visible' => true, 'deleted' => false, 'private' => [self::PUBLIC, self::UNLISTED] 'visible' => true, 'deleted' => false, 'private' => [self::PUBLIC, self::UNLISTED]
]; ];
@ -1734,7 +1734,7 @@ class Item
} }
$condition = ['nurl' => $owner['nurl'], 'rel' => [Contact::SHARING, Contact::FRIEND]]; $condition = ['nurl' => $owner['nurl'], 'rel' => [Contact::SHARING, Contact::FRIEND]];
$contacts = DBA::select('contact', ['uid'], $condition); $contacts = DBA::select('contact', ['uid'], $condition);
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
if ($contact['uid'] == 0) { if ($contact['uid'] == 0) {
continue; continue;
@ -1745,7 +1745,7 @@ class Item
DBA::close($contacts); DBA::close($contacts);
$condition = ['alias' => $owner['url'], 'rel' => [Contact::SHARING, Contact::FRIEND]]; $condition = ['alias' => $owner['url'], 'rel' => [Contact::SHARING, Contact::FRIEND]];
$contacts = DBA::select('contact', ['uid'], $condition); $contacts = DBA::select('contact', ['uid'], $condition);
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
if ($contact['uid'] == 0) { if ($contact['uid'] == 0) {
continue; continue;
@ -1757,7 +1757,7 @@ class Item
if (!empty($owner['alias'])) { if (!empty($owner['alias'])) {
$condition = ['nurl' => Strings::normaliseLink($owner['alias']), 'rel' => [Contact::SHARING, Contact::FRIEND]]; $condition = ['nurl' => Strings::normaliseLink($owner['alias']), 'rel' => [Contact::SHARING, Contact::FRIEND]];
$contacts = DBA::select('contact', ['uid'], $condition); $contacts = DBA::select('contact', ['uid'], $condition);
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
if ($contact['uid'] == 0) { if ($contact['uid'] == 0) {
continue; continue;
@ -1979,21 +1979,21 @@ class Item
unset($item['postopts']); unset($item['postopts']);
unset($item['inform']); unset($item['inform']);
$item['uid'] = $uid; $item['uid'] = $uid;
$item['origin'] = 0; $item['origin'] = 0;
$item['wall'] = 0; $item['wall'] = 0;
$notify = false; $notify = false;
if ($item['gravity'] == self::GRAVITY_PARENT) { if ($item['gravity'] == self::GRAVITY_PARENT) {
$contact = DBA::selectFirst('contact', [], ['id' => $item['contact-id'], 'self' => false]); $contact = DBA::selectFirst('contact', [], ['id' => $item['contact-id'], 'self' => false]);
if (DBA::isResult($contact)) { if (DBA::isResult($contact)) {
$notify = self::isRemoteSelf($contact, $item); $notify = self::isRemoteSelf($contact, $item);
$item['wall'] = (bool)$notify; $item['wall'] = (bool)$notify;
} }
} }
$item['contact-id'] = self::contactId($item); $item['contact-id'] = self::contactId($item);
$distributed = self::insert($item, $notify); $distributed = self::insert($item, $notify);
if (!$distributed) { if (!$distributed) {
DI::logger()->info("Distributed item wasn't stored", ['uri-id' => $item['uri-id'], 'user' => $uid]); DI::logger()->info("Distributed item wasn't stored", ['uri-id' => $item['uri-id'], 'user' => $uid]);
@ -2015,9 +2015,9 @@ class Item
*/ */
private static function addShadow(int $itemid) private static function addShadow(int $itemid)
{ {
$fields = ['uid', 'private', 'visible', 'deleted', 'network', 'uri-id']; $fields = ['uid', 'private', 'visible', 'deleted', 'network', 'uri-id'];
$condition = ['id' => $itemid, 'gravity' => self::GRAVITY_PARENT]; $condition = ['id' => $itemid, 'gravity' => self::GRAVITY_PARENT];
$item = Post::selectFirst($fields, $condition); $item = Post::selectFirst($fields, $condition);
if (!DBA::isResult($item)) { if (!DBA::isResult($item)) {
return; return;
@ -2029,7 +2029,7 @@ class Item
} }
// Is it a visible public post? // Is it a visible public post?
if (!$item["visible"] || $item["deleted"] || ($item["private"] == self::PRIVATE)) { if (!$item["visible"] || $item["deleted"] || ($item["private"] == self::PRIVATE)) {
return; return;
} }
@ -2146,7 +2146,7 @@ class Item
} }
$transmitted = []; $transmitted = [];
foreach ($item['transmitted-languages'] ?? [] as $language) { foreach ($item['transmitted-languages'] ?? [] as $language) {
$transmitted[$language] = 0; $transmitted[$language] = 0;
} }
@ -2279,10 +2279,10 @@ class Item
$previous_block = self::getBlockCode($previous); $previous_block = self::getBlockCode($previous);
} }
$block = (($next != '') && \IntlChar::isalpha($next)) ? self::getBlockCode($next) : $previous_block; $block = (($next != '') && \IntlChar::isalpha($next)) ? self::getBlockCode($next) : $previous_block;
$blocks[$block] = ($blocks[$block] ?? '') . $character; $blocks[$block] = ($blocks[$block] ?? '') . $character;
} else { } else {
$block = self::getBlockCode($character); $block = self::getBlockCode($character);
$blocks[$block] = ($blocks[$block] ?? '') . $character; $blocks[$block] = ($blocks[$block] ?? '') . $character;
} }
} }
@ -2329,7 +2329,7 @@ class Item
public static function getLanguageMessage(array $item): string public static function getLanguageMessage(array $item): string
{ {
$iso639 = new \Matriphe\ISO639\ISO639; $iso639 = new \Matriphe\ISO639\ISO639();
$used_languages = ''; $used_languages = '';
foreach (json_decode($item['language'], true) as $language => $reliability) { foreach (json_decode($item['language'], true) as $language => $reliability) {
@ -2532,7 +2532,7 @@ class Item
} }
$basetag = str_replace('_', ' ', substr($tag, 1)); $basetag = str_replace('_', ' ', substr($tag, 1));
$newtag = '#[url=' . DI::baseUrl() . '/search?tag=' . urlencode($basetag) . ']' . $basetag . '[/url]'; $newtag = '#[url=' . DI::baseUrl() . '/search?tag=' . urlencode($basetag) . ']' . $basetag . '[/url]';
$body = str_replace($tag, $newtag, $body); $body = str_replace($tag, $newtag, $body);
} }
@ -2694,8 +2694,8 @@ class Item
$datarray['contact-id'] = $self['id']; $datarray['contact-id'] = $self['id'];
$datarray['author-name'] = $datarray['owner-name'] = $self['name']; $datarray['author-name'] = $datarray['owner-name'] = $self['name'];
$datarray['author-link'] = $datarray['owner-link'] = $self['url']; $datarray['author-link'] = $datarray['owner-link'] = $self['url'];
$datarray['author-avatar'] = $datarray['owner-avatar'] = $self['thumb']; $datarray['author-avatar'] = $datarray['owner-avatar'] = $self['thumb'];
unset($datarray['edited']); unset($datarray['edited']);
@ -2705,14 +2705,14 @@ class Item
unset($datarray['author-id']); unset($datarray['author-id']);
if ($contact['network'] != Protocol::FEED) { if ($contact['network'] != Protocol::FEED) {
$old_uri_id = $datarray['uri-id'] ?? 0; $old_uri_id = $datarray['uri-id'] ?? 0;
$datarray['guid'] = System::createUUID(); $datarray['guid'] = System::createUUID();
unset($datarray['plink']); unset($datarray['plink']);
$datarray['uri'] = self::newURI($datarray['guid']); $datarray['uri'] = self::newURI($datarray['guid']);
$datarray['uri-id'] = ItemURI::getIdByURI($datarray['uri']); $datarray['uri-id'] = ItemURI::getIdByURI($datarray['uri']);
$datarray['extid'] = Protocol::DFRN; $datarray['extid'] = Protocol::DFRN;
$urlpart = parse_url($datarray2['author-link']); $urlpart = parse_url($datarray2['author-link']);
$datarray['app'] = $urlpart['host']; $datarray['app'] = $urlpart['host'];
if (!empty($old_uri_id)) { if (!empty($old_uri_id)) {
Post\Media::copy($old_uri_id, $datarray['uri-id']); Post\Media::copy($old_uri_id, $datarray['uri-id']);
} }
@ -2725,7 +2725,7 @@ class Item
DI::logger()->info('remote-self post original item', ['contact' => $contact['url'], 'result' => $result, 'item' => $datarray2]); DI::logger()->info('remote-self post original item', ['contact' => $contact['url'], 'result' => $result, 'item' => $datarray2]);
} else { } else {
$datarray['app'] = 'Feed'; $datarray['app'] = 'Feed';
$result = true; $result = true;
} }
if ($result) { if ($result) {
@ -2755,11 +2755,11 @@ class Item
$site = substr(DI::baseUrl(), strpos(DI::baseUrl(), '://')); $site = substr(DI::baseUrl(), strpos(DI::baseUrl(), '://'));
$orig_body = $s; $orig_body = $s;
$new_body = ''; $new_body = '';
$img_start = strpos($orig_body, '[img'); $img_start = strpos($orig_body, '[img');
$img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
$img_len = ($img_start !== false ? strpos(substr($orig_body, $img_start + $img_st_close + 1), '[/img]') : false); $img_len = ($img_start !== false ? strpos(substr($orig_body, $img_start + $img_st_close + 1), '[/img]') : false);
while (($img_st_close !== false) && ($img_len !== false)) { while (($img_st_close !== false) && ($img_len !== false)) {
$img_st_close++; // make it point to AFTER the closing bracket $img_st_close++; // make it point to AFTER the closing bracket
@ -2770,13 +2770,13 @@ class Item
if (stristr($image, $site . '/photo/')) { if (stristr($image, $site . '/photo/')) {
// Only embed locally hosted photos // Only embed locally hosted photos
$replace = false; $replace = false;
$i = basename($image); $i = basename($image);
$i = str_replace(['.jpg', '.png', '.gif'], ['', '', ''], $i); $i = str_replace(['.jpg', '.png', '.gif'], ['', '', ''], $i);
$x = strpos($i, '-'); $x = strpos($i, '-');
if ($x) { if ($x) {
$res = substr($i, $x + 1); $res = substr($i, $x + 1);
$i = substr($i, 0, $x); $i = substr($i, 0, $x);
$photo = Photo::getPhotoForUser($uid, $i, $res); $photo = Photo::getPhotoForUser($uid, $i, $res);
if (DBA::isResult($photo)) { if (DBA::isResult($photo)) {
/* /*
@ -2806,7 +2806,7 @@ class Item
if (preg_match("/\[img\=([0-9]*)x([0-9]*)\]/is", substr($orig_body, $img_start, $img_st_close), $match)) { if (preg_match("/\[img\=([0-9]*)x([0-9]*)\]/is", substr($orig_body, $img_start, $img_st_close), $match)) {
DI::logger()->info('scaling photo'); DI::logger()->info('scaling photo');
$width = intval($match[1]); $width = intval($match[1]);
$height = intval($match[2]); $height = intval($match[2]);
$photo_img->scaleDown(max($width, $height)); $photo_img->scaleDown(max($width, $height));
@ -2823,15 +2823,15 @@ class Item
} }
} }
$new_body = $new_body . substr($orig_body, 0, $img_start + $img_st_close) . $image . '[/img]'; $new_body = $new_body . substr($orig_body, 0, $img_start + $img_st_close) . $image . '[/img]';
$orig_body = substr($orig_body, $img_start + $img_st_close + $img_len + strlen('[/img]')); $orig_body = substr($orig_body, $img_start + $img_st_close + $img_len + strlen('[/img]'));
if ($orig_body === false) { if ($orig_body === false) {
$orig_body = ''; $orig_body = '';
} }
$img_start = strpos($orig_body, '[img'); $img_start = strpos($orig_body, '[img');
$img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
$img_len = ($img_start !== false ? strpos(substr($orig_body, $img_start + $img_st_close + 1), '[/img]') : false); $img_len = ($img_start !== false ? strpos(substr($orig_body, $img_start + $img_st_close + 1), '[/img]') : false);
} }
$new_body = $new_body . $orig_body; $new_body = $new_body . $orig_body;
@ -2936,9 +2936,9 @@ class Item
$expire_items = true; $expire_items = true;
} }
$expire_notes = (bool)DI::pConfig()->get($uid, 'expire', 'notes', true); $expire_notes = (bool)DI::pConfig()->get($uid, 'expire', 'notes', true);
$expire_starred = (bool)DI::pConfig()->get($uid, 'expire', 'starred', true); $expire_starred = (bool)DI::pConfig()->get($uid, 'expire', 'starred', true);
$expire_photos = (bool)DI::pConfig()->get($uid, 'expire', 'photos', false); $expire_photos = (bool)DI::pConfig()->get($uid, 'expire', 'photos', false);
$expired = 0; $expired = 0;
@ -3114,7 +3114,7 @@ class Item
} }
$condition = [ $condition = [
'vid' => $vids, 'deleted' => false, 'gravity' => self::GRAVITY_ACTIVITY, 'vid' => $vids, 'deleted' => false, 'gravity' => self::GRAVITY_ACTIVITY,
'author-id' => $author_id, 'uid' => $uid, 'thr-parent-id' => $uri_id 'author-id' => $author_id, 'uid' => $uid, 'thr-parent-id' => $uri_id
]; ];
$like_item = Post::selectFirst(['id', 'guid', 'verb'], $condition); $like_item = Post::selectFirst(['id', 'guid', 'verb'], $condition);
@ -3160,29 +3160,29 @@ class Item
$objtype = $item['resource-id'] ? Activity\ObjectType::IMAGE : Activity\ObjectType::NOTE; $objtype = $item['resource-id'] ? Activity\ObjectType::IMAGE : Activity\ObjectType::NOTE;
$new_item = [ $new_item = [
'guid' => System::createUUID(), 'guid' => System::createUUID(),
'uri' => self::newURI(), 'uri' => self::newURI(),
'uid' => $uid, 'uid' => $uid,
'contact-id' => $owner['id'], 'contact-id' => $owner['id'],
'wall' => $item['wall'], 'wall' => $item['wall'],
'origin' => 1, 'origin' => 1,
'network' => Protocol::DFRN, 'network' => Protocol::DFRN,
'protocol' => Conversation::PARCEL_DIRECT, 'protocol' => Conversation::PARCEL_DIRECT,
'direction' => Conversation::PUSH, 'direction' => Conversation::PUSH,
'gravity' => self::GRAVITY_ACTIVITY, 'gravity' => self::GRAVITY_ACTIVITY,
'parent' => $item['id'], 'parent' => $item['id'],
'thr-parent' => $item['uri'], 'thr-parent' => $item['uri'],
'owner-id' => $author_id, 'owner-id' => $author_id,
'author-id' => $author_id, 'author-id' => $author_id,
'body' => $activity, 'body' => $activity,
'verb' => $activity, 'verb' => $activity,
'object-type' => $objtype, 'object-type' => $objtype,
'allow_cid' => $allow_cid ?? $item['allow_cid'], 'allow_cid' => $allow_cid ?? $item['allow_cid'],
'allow_gid' => $allow_gid ?? $item['allow_gid'], 'allow_gid' => $allow_gid ?? $item['allow_gid'],
'deny_cid' => $deny_cid ?? $item['deny_cid'], 'deny_cid' => $deny_cid ?? $item['deny_cid'],
'deny_gid' => $deny_gid ?? $item['deny_gid'], 'deny_gid' => $deny_gid ?? $item['deny_gid'],
'visible' => 1, 'visible' => 1,
'unseen' => 1, 'unseen' => 1,
]; ];
if (in_array($activity, [Activity::LIKE, Activity::DISLIKE])) { if (in_array($activity, [Activity::LIKE, Activity::DISLIKE])) {
@ -3210,7 +3210,7 @@ class Item
*/ */
public static function getPermissionsConditionArrayByUserId(int $owner_id): array public static function getPermissionsConditionArrayByUserId(int $owner_id): array
{ {
$local_user = DI::userSession()->getLocalUserId(); $local_user = DI::userSession()->getLocalUserId();
$remote_user = DI::userSession()->getRemoteContactID($owner_id); $remote_user = DI::userSession()->getRemoteContactID($owner_id);
// default permissions - anonymous user // default permissions - anonymous user
@ -3244,7 +3244,7 @@ class Item
*/ */
public static function getPermissionsSQLByUserId(int $owner_id, string $table = ''): string public static function getPermissionsSQLByUserId(int $owner_id, string $table = ''): string
{ {
$local_user = DI::userSession()->getLocalUserId(); $local_user = DI::userSession()->getLocalUserId();
$remote_user = DI::userSession()->getRemoteContactID($owner_id); $remote_user = DI::userSession()->getRemoteContactID($owner_id);
if (!empty($table)) { if (!empty($table)) {
@ -3372,7 +3372,7 @@ class Item
public static function prepareBody(array &$item, bool $attach = false, bool $is_preview = false, bool $only_cache = false): string public static function prepareBody(array &$item, bool $attach = false, bool $is_preview = false, bool $only_cache = false): string
{ {
$appHelper = DI::appHelper(); $appHelper = DI::appHelper();
$uid = DI::userSession()->getLocalUserId(); $uid = DI::userSession()->getLocalUserId();
Hook::callAll('prepare_body_init', $item); Hook::callAll('prepare_body_init', $item);
// In order to provide theme developers more possibilities, event items // In order to provide theme developers more possibilities, event items
@ -3384,7 +3384,7 @@ class Item
$tags = Tag::populateFromItem($item); $tags = Tag::populateFromItem($item);
$item['tags'] = $tags['tags']; $item['tags'] = $tags['tags'];
$item['hashtags'] = $tags['hashtags']; $item['hashtags'] = $tags['hashtags'];
$item['mentions'] = $tags['mentions']; $item['mentions'] = $tags['mentions'];
@ -3408,12 +3408,12 @@ class Item
$shared = DI::contentItem()->getSharedPost($item, $fields); $shared = DI::contentItem()->getSharedPost($item, $fields);
if (!empty($shared['post'])) { if (!empty($shared['post'])) {
$shared_item = $shared['post']; $shared_item = $shared['post'];
$shared_item['body'] = Post\Media::removeFromEndOfBody($shared_item['body']); $shared_item['body'] = Post\Media::removeFromEndOfBody($shared_item['body']);
$shared_item['body'] = Post\Media::replaceImage($shared_item['body']); $shared_item['body'] = Post\Media::replaceImage($shared_item['body']);
$quote_uri_id = $shared['post']['uri-id']; $quote_uri_id = $shared['post']['uri-id'];
$shared_links[] = strtolower($shared['post']['uri']); $shared_links[] = strtolower($shared['post']['uri']);
$item['body'] = BBCode::removeSharedData($item['body']); $item['body'] = BBCode::removeSharedData($item['body']);
} elseif (empty($shared_item['uri-id']) && empty($item['quote-uri-id']) && ($item['network'] != Protocol::DIASPORA)) { } elseif (empty($shared_item['uri-id']) && empty($item['quote-uri-id']) && ($item['network'] != Protocol::DIASPORA)) {
$media = Post\Media::getByURIId($item['uri-id'], [Post\Media::ACTIVITY]); $media = Post\Media::getByURIId($item['uri-id'], [Post\Media::ACTIVITY]);
if (!empty($media) && ($media[0]['media-uri-id'] != $item['uri-id'])) { if (!empty($media) && ($media[0]['media-uri-id'] != $item['uri-id'])) {
@ -3425,7 +3425,7 @@ class Item
} }
if (empty($shared_item['uri-id'])) { if (empty($shared_item['uri-id'])) {
$shared_item = Post::selectFirst($fields, ['uri' => $media[0]['url'], 'uid' => [$item['uid'], 0]]); $shared_item = Post::selectFirst($fields, ['uri' => $media[0]['url'], 'uid' => [$item['uid'], 0]]);
$shared_links[] = strtolower($media[0]['url']); $shared_links[] = strtolower($media[0]['url']);
} }
@ -3451,21 +3451,21 @@ class Item
$sharedSplitAttachments = []; $sharedSplitAttachments = [];
if (!empty($shared_item['uri-id'])) { if (!empty($shared_item['uri-id'])) {
$shared_uri_id = $shared_item['uri-id']; $shared_uri_id = $shared_item['uri-id'];
$shared_links[] = strtolower($shared_item['plink']); $shared_links[] = strtolower($shared_item['plink']);
$sharedSplitAttachments = DI::postMediaRepository()->splitAttachments($shared_uri_id, [], $shared_item['has-media']); $sharedSplitAttachments = DI::postMediaRepository()->splitAttachments($shared_uri_id, [], $shared_item['has-media']);
$shared_links = array_merge($shared_links, $sharedSplitAttachments['visual']->column('url')); $shared_links = array_merge($shared_links, $sharedSplitAttachments['visual']->column('url'));
$shared_links = array_merge($shared_links, $sharedSplitAttachments['link']->column('url')); $shared_links = array_merge($shared_links, $sharedSplitAttachments['link']->column('url'));
$shared_links = array_merge($shared_links, $sharedSplitAttachments['additional']->column('url')); $shared_links = array_merge($shared_links, $sharedSplitAttachments['additional']->column('url'));
$item['body'] = self::replaceVisualAttachments($sharedSplitAttachments['visual'], $item['body']); $item['body'] = self::replaceVisualAttachments($sharedSplitAttachments['visual'], $item['body']);
} }
$itemSplitAttachments = DI::postMediaRepository()->splitAttachments($item['uri-id'], $shared_links, $item['has-media'] ?? false); $itemSplitAttachments = DI::postMediaRepository()->splitAttachments($item['uri-id'], $shared_links, $item['has-media'] ?? false);
$item['body'] = self::replaceVisualAttachments($itemSplitAttachments['visual'], $item['body'] ?? ''); $item['body'] = self::replaceVisualAttachments($itemSplitAttachments['visual'], $item['body'] ?? '');
self::putInCache($item); self::putInCache($item);
$item['body'] = $body; $item['body'] = $body;
$s = $item["rendered-html"]; $s = $item["rendered-html"];
if ($only_cache) { if ($only_cache) {
return ''; return '';
@ -3489,7 +3489,7 @@ class Item
$item['attachments'] = $itemSplitAttachments; $item['attachments'] = $itemSplitAttachments;
$hook_data = [ $hook_data = [
'item' => $item, 'item' => $item,
'filter_reasons' => $filter_reasons 'filter_reasons' => $filter_reasons
]; ];
Hook::callAll('prepare_body_content_filter', $hook_data); Hook::callAll('prepare_body_content_filter', $hook_data);
@ -3502,9 +3502,9 @@ class Item
} }
$hook_data = [ $hook_data = [
'item' => $item, 'item' => $item,
'html' => $s, 'html' => $s,
'preview' => $is_preview, 'preview' => $is_preview,
'filter_reasons' => $filter_reasons 'filter_reasons' => $filter_reasons
]; ];
Hook::callAll('prepare_body', $hook_data); Hook::callAll('prepare_body', $hook_data);
@ -3515,22 +3515,22 @@ class Item
if (!$attach) { if (!$attach) {
// Replace the blockquotes with quotes that are used in mails. // Replace the blockquotes with quotes that are used in mails.
$mailquote = '<blockquote type="cite" class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">'; $mailquote = '<blockquote type="cite" class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">';
$s = str_replace(['<blockquote>', '<blockquote class="spoiler">', '<blockquote class="author">'], [$mailquote, $mailquote, $mailquote], $s); $s = str_replace(['<blockquote>', '<blockquote class="spoiler">', '<blockquote class="author">'], [$mailquote, $mailquote, $mailquote], $s);
return $s; return $s;
} }
if (!empty($sharedSplitAttachments)) { if (!empty($sharedSplitAttachments)) {
$s = self::addGallery($s, $sharedSplitAttachments['visual']); $s = self::addGallery($s, $sharedSplitAttachments['visual']);
$s = self::addVisualAttachments($sharedSplitAttachments['visual'], $shared_item, $s, true); $s = self::addVisualAttachments($sharedSplitAttachments['visual'], $shared_item, $s, true);
$s = self::addLinkAttachment($shared_uri_id ?: $item['uri-id'], $sharedSplitAttachments, $body, $s, true, $quote_shared_links); $s = self::addLinkAttachment($shared_uri_id ?: $item['uri-id'], $sharedSplitAttachments, $body, $s, true, $quote_shared_links);
$s = self::addNonVisualAttachments($sharedSplitAttachments['additional'], $item, $s); $s = self::addNonVisualAttachments($sharedSplitAttachments['additional'], $item, $s);
$body = BBCode::removeSharedData($body); $body = BBCode::removeSharedData($body);
} }
$pos = strpos($s, BBCode::SHARED_ANCHOR); $pos = strpos($s, BBCode::SHARED_ANCHOR);
if ($pos) { if ($pos) {
$shared_html = substr($s, $pos + strlen(BBCode::SHARED_ANCHOR)); $shared_html = substr($s, $pos + strlen(BBCode::SHARED_ANCHOR));
$s = substr($s, 0, $pos); $s = substr($s, 0, $pos);
} }
$s = self::addGallery($s, $itemSplitAttachments['visual']); $s = self::addGallery($s, $itemSplitAttachments['visual']);
@ -3550,7 +3550,7 @@ class Item
// Replace friendica image url size with theme preference. // Replace friendica image url size with theme preference.
if (!empty($appHelper->getThemeInfoValue('item_image_size'))) { if (!empty($appHelper->getThemeInfoValue('item_image_size'))) {
$ps = $appHelper->getThemeInfoValue('item_image_size'); $ps = $appHelper->getThemeInfoValue('item_image_size');
$s = preg_replace('|(<img[^>]+src="[^"]+/photo/[0-9a-f]+)-[0-9]|', "$1-" . $ps, $s); $s = preg_replace('|(<img[^>]+src="[^"]+/photo/[0-9a-f]+)-[0-9]|', "$1-" . $ps, $s);
} }
if (!empty($shared_html)) { if (!empty($shared_html)) {
@ -3592,7 +3592,7 @@ class Item
continue; continue;
} }
$element_html = $element->ownerDocument->saveHTML($element); $element_html = $element->ownerDocument->saveHTML($element);
$html = str_replace($element_html, str_replace($src, $svg, $element_html), $html); $html = str_replace($element_html, str_replace($src, $svg, $element_html), $html);
} }
return $html; return $html;
} }
@ -3616,8 +3616,8 @@ class Item
$s = preg_replace_callback($pattern, function () use ($PostMedia) { $s = preg_replace_callback($pattern, function () use ($PostMedia) {
return Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image/single_with_height_allocation.tpl'), [ return Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image/single_with_height_allocation.tpl'), [
'$image' => $PostMedia, '$image' => $PostMedia,
'$allocated_height' => $PostMedia->getAllocatedHeight(), '$allocated_height' => $PostMedia->getAllocatedHeight(),
'$allocated_max_width' => ($PostMedia->previewWidth ?? $PostMedia->width) . 'px', '$allocated_max_width' => ($PostMedia->previewWidth ?? $PostMedia->width) . 'px',
]); ]);
}, $s); }, $s);
@ -3741,10 +3741,10 @@ class Item
if ($PostMedia->mimetype->type == 'image' || $PostMedia->preview) { if ($PostMedia->mimetype->type == 'image' || $PostMedia->preview) {
$preview_size = Proxy::SIZE_MEDIUM; $preview_size = Proxy::SIZE_MEDIUM;
$preview_url = DI::baseUrl() . $PostMedia->getPreviewPath($preview_size); $preview_url = DI::baseUrl() . $PostMedia->getPreviewPath($preview_size);
} else { } else {
$preview_size = 0; $preview_size = 0;
$preview_url = ''; $preview_url = '';
} }
if ($preview_url && self::containsLink($item['body'], $preview_url)) { if ($preview_url && self::containsLink($item['body'], $preview_url)) {
@ -3780,10 +3780,10 @@ class Item
} elseif ($PostMedia->mimetype->type == 'audio') { } elseif ($PostMedia->mimetype->type == 'audio') {
$media = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/audio.tpl'), [ $media = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/audio.tpl'), [
'$audio' => [ '$audio' => [
'id' => $PostMedia->id, 'id' => $PostMedia->id,
'src' => (string)$PostMedia->url, 'src' => (string)$PostMedia->url,
'name' => $PostMedia->name ?: $PostMedia->url, 'name' => $PostMedia->name ?: $PostMedia->url,
'mime' => (string)$PostMedia->mimetype, 'mime' => (string)$PostMedia->mimetype,
], ],
]); ]);
if (($item['post-type'] ?? null) == Item::PT_AUDIO) { if (($item['post-type'] ?? null) == Item::PT_AUDIO) {
@ -3847,7 +3847,7 @@ class Item
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
// Don't show a preview when there is a visual attachment (audio or video) // Don't show a preview when there is a visual attachment (audio or video)
$types = $attachments['visual']->column('type'); $types = $attachments['visual']->column('type');
$preview = !in_array(PostMedia::TYPE_IMAGE, $types) && !in_array(PostMedia::TYPE_VIDEO, $types); $preview = !in_array(PostMedia::TYPE_IMAGE, $types) && !in_array(PostMedia::TYPE_VIDEO, $types);
/** @var ?PostMedia $attachment */ /** @var ?PostMedia $attachment */
@ -3870,18 +3870,18 @@ class Item
if (!empty($attachment)) { if (!empty($attachment)) {
$data = [ $data = [
'after' => '', 'after' => '',
'author_name' => $attachment->authorName ?? '', 'author_name' => $attachment->authorName ?? '',
'author_url' => (string)($attachment->authorUrl ?? ''), 'author_url' => (string)($attachment->authorUrl ?? ''),
'description' => $attachment->description ?? '', 'description' => $attachment->description ?? '',
'image' => '', 'image' => '',
'preview' => '', 'preview' => '',
'provider_name' => $attachment->publisherName ?? '', 'provider_name' => $attachment->publisherName ?? '',
'provider_url' => (string)($attachment->publisherUrl ?? ''), 'provider_url' => (string)($attachment->publisherUrl ?? ''),
'text' => '', 'text' => '',
'title' => $attachment->name ?? '', 'title' => $attachment->name ?? '',
'type' => 'link', 'type' => 'link',
'url' => (string)$attachment->url, 'url' => (string)$attachment->url,
]; ];
if ($preview && $attachment->preview) { if ($preview && $attachment->preview) {
@ -4022,7 +4022,7 @@ class Item
$options = Post\QuestionOption::getByURIId($item['uri-id']); $options = Post\QuestionOption::getByURIId($item['uri-id']);
foreach ($options as $key => $option) { foreach ($options as $key => $option) {
if ($question['voters'] > 0) { if ($question['voters'] > 0) {
$percent = $option['replies'] / $question['voters'] * 100; $percent = $option['replies'] / $question['voters'] * 100;
$options[$key]['vote'] = DI::l10n()->tt('%2$s (%3$d%%, %1$d vote)', '%2$s (%3$d%%, %1$d votes)', $option['replies'] ?? 0, $option['name'], round($percent, 1)); $options[$key]['vote'] = DI::l10n()->tt('%2$s (%3$d%%, %1$d vote)', '%2$s (%3$d%%, %1$d votes)', $option['replies'] ?? 0, $option['name'], round($percent, 1));
} else { } else {
$options[$key]['vote'] = DI::l10n()->tt('%2$s (%1$d vote)', '%2$s (%1$d votes)', $option['replies'] ?? 0, $option['name']); $options[$key]['vote'] = DI::l10n()->tt('%2$s (%1$d vote)', '%2$s (%1$d votes)', $option['replies'] ?? 0, $option['name']);
@ -4073,9 +4073,9 @@ class Item
if (DI::userSession()->getLocalUserId()) { if (DI::userSession()->getLocalUserId()) {
$ret = [ $ret = [
'href' => "display/" . $item['guid'], 'href' => "display/" . $item['guid'],
'orig' => "display/" . $item['guid'], 'orig' => "display/" . $item['guid'],
'title' => DI::l10n()->t('View on separate page'), 'title' => DI::l10n()->t('View on separate page'),
'orig_title' => DI::l10n()->t('View on separate page'), 'orig_title' => DI::l10n()->t('View on separate page'),
]; ];
@ -4091,14 +4091,14 @@ class Item
} }
if (!empty($plink)) { if (!empty($plink)) {
$ret['href'] = DI::baseUrl()->remove($plink); $ret['href'] = DI::baseUrl()->remove($plink);
$ret['title'] = DI::l10n()->t('Link to source'); $ret['title'] = DI::l10n()->t('Link to source');
} }
} elseif (!empty($plink) && ($item['private'] != self::PRIVATE)) { } elseif (!empty($plink) && ($item['private'] != self::PRIVATE)) {
$ret = [ $ret = [
'href' => $plink, 'href' => $plink,
'orig' => $plink, 'orig' => $plink,
'title' => DI::l10n()->t('Link to source'), 'title' => DI::l10n()->t('Link to source'),
'orig_title' => DI::l10n()->t('Link to source'), 'orig_title' => DI::l10n()->t('Link to source'),
]; ];
} else { } else {
@ -4143,7 +4143,7 @@ class Item
public static function searchByLink(string $uri, int $uid = 0): int public static function searchByLink(string $uri, int $uid = 0): int
{ {
$ssl_uri = str_replace('http://', 'https://', $uri); $ssl_uri = str_replace('http://', 'https://', $uri);
$uris = [$uri, $ssl_uri, Strings::normaliseLink($uri)]; $uris = [$uri, $ssl_uri, Strings::normaliseLink($uri)];
$item = Post::selectFirst(['id'], ['uri' => $uris, 'uid' => $uid]); $item = Post::selectFirst(['id'], ['uri' => $uris, 'uid' => $uid]);
if (DBA::isResult($item)) { if (DBA::isResult($item)) {
@ -4168,7 +4168,7 @@ class Item
public static function getURIByLink(string $uri): string public static function getURIByLink(string $uri): string
{ {
$ssl_uri = str_replace('http://', 'https://', $uri); $ssl_uri = str_replace('http://', 'https://', $uri);
$uris = [$uri, $ssl_uri, Strings::normaliseLink($uri)]; $uris = [$uri, $ssl_uri, Strings::normaliseLink($uri)];
$item = Post::selectFirst(['uri'], ['uri' => $uris]); $item = Post::selectFirst(['uri'], ['uri' => $uris]);
if (DBA::isResult($item)) { if (DBA::isResult($item)) {
@ -4222,7 +4222,7 @@ class Item
if (!$mimetype) { if (!$mimetype) {
try { try {
$curlResult = DI::httpClient()->head($uri, [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::JSON_AS, HttpClientOptions::REQUEST => HttpClientRequest::ACTIVITYPUB]); $curlResult = DI::httpClient()->head($uri, [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::JSON_AS, HttpClientOptions::REQUEST => HttpClientRequest::ACTIVITYPUB]);
$mimetype = $curlResult->getContentType(); $mimetype = $curlResult->getContentType();
} catch (\Throwable $th) { } catch (\Throwable $th) {
DI::logger()->info('Error while fetching HTTP link via HEAD', ['uid' => $uid, 'uri' => $uri, 'code' => $th->getCode(), 'message' => $th->getMessage()]); DI::logger()->info('Error while fetching HTTP link via HEAD', ['uid' => $uid, 'uri' => $uri, 'code' => $th->getCode(), 'message' => $th->getMessage()]);
return 0; return 0;
@ -4233,7 +4233,7 @@ class Item
try { try {
// Issue 14126: Workaround for Mastodon servers that return "application/json" on a "head" request. // Issue 14126: Workaround for Mastodon servers that return "application/json" on a "head" request.
$curlResult = HTTPSignature::fetchRaw($uri, $uid); $curlResult = HTTPSignature::fetchRaw($uri, $uid);
$mimetype = $curlResult->getContentType(); $mimetype = $curlResult->getContentType();
} catch (\Throwable $th) { } catch (\Throwable $th) {
DI::logger()->info('Error while fetching HTTP link via signed GET', ['uid' => $uid, 'uri' => $uri, 'code' => $th->getCode(), 'message' => $th->getMessage()]); DI::logger()->info('Error while fetching HTTP link via signed GET', ['uid' => $uid, 'uri' => $uri, 'code' => $th->getCode(), 'message' => $th->getMessage()]);
return 0; return 0;
@ -4305,7 +4305,7 @@ class Item
} }
$url = $shared['message_id'] ?: $shared['link']; $url = $shared['message_id'] ?: $shared['link'];
$id = self::fetchByLink($url, 0, ActivityPub\Receiver::COMPLETION_ASYNC); $id = self::fetchByLink($url, 0, ActivityPub\Receiver::COMPLETION_ASYNC);
if (!$id) { if (!$id) {
DI::logger()->notice('Post could not be fetched.', ['url' => $url, 'uid' => $uid]); DI::logger()->notice('Post could not be fetched.', ['url' => $url, 'uid' => $uid]);
return 0; return 0;

View file

@ -137,15 +137,15 @@ class Mail
Photo::setPermissionFromBody($body, $sender_uid, $me['id'], '<' . $contact['id'] . '>', '', '', ''); Photo::setPermissionFromBody($body, $sender_uid, $me['id'], '<' . $contact['id'] . '>', '', '', '');
$guid = System::createUUID(); $guid = System::createUUID();
$uri = Item::newURI($guid); $uri = Item::newURI($guid);
$convid = 0; $convid = 0;
$reply = false; $reply = false;
// look for any existing conversation structure // look for any existing conversation structure
if (strlen($replyto)) { if (strlen($replyto)) {
$reply = true; $reply = true;
$condition = ["`uid` = ? AND (`uri` = ? OR `parent-uri` = ?)", $condition = ["`uid` = ? AND (`uri` = ? OR `parent-uri` = ?)",
$sender_uid, $replyto, $replyto]; $sender_uid, $replyto, $replyto];
$mail = DBA::selectFirst('mail', ['convid'], $condition); $mail = DBA::selectFirst('mail', ['convid'], $condition);
@ -158,11 +158,11 @@ class Mail
if (!$convid) { if (!$convid) {
// create a new conversation // create a new conversation
$conv_guid = System::createUUID(); $conv_guid = System::createUUID();
$convuri = $contact['addr'] . ':' . $conv_guid; $convuri = $contact['addr'] . ':' . $conv_guid;
$fields = ['uid' => $sender_uid, 'guid' => $conv_guid, 'creator' => $me['addr'], $fields = ['uid' => $sender_uid, 'guid' => $conv_guid, 'creator' => $me['addr'],
'created' => DateTimeFormat::utcNow(), 'updated' => DateTimeFormat::utcNow(), 'created' => DateTimeFormat::utcNow(), 'updated' => DateTimeFormat::utcNow(),
'subject' => $subject, 'recips' => $contact['addr'] . ';' . $me['addr']]; 'subject' => $subject, 'recips' => $contact['addr'] . ';' . $me['addr']];
if (DBA::insert('conv', $fields)) { if (DBA::insert('conv', $fields)) {
$convid = DBA::lastInsertId(); $convid = DBA::lastInsertId();
} }
@ -179,21 +179,21 @@ class Mail
$post_id = self::insert( $post_id = self::insert(
[ [
'uid' => $sender_uid, 'uid' => $sender_uid,
'guid' => $guid, 'guid' => $guid,
'convid' => $convid, 'convid' => $convid,
'from-name' => $me['name'], 'from-name' => $me['name'],
'from-photo' => $me['thumb'], 'from-photo' => $me['thumb'],
'from-url' => $me['url'], 'from-url' => $me['url'],
'contact-id' => $recipient, 'contact-id' => $recipient,
'title' => $subject, 'title' => $subject,
'body' => $body, 'body' => $body,
'seen' => 1, 'seen' => 1,
'reply' => $reply, 'reply' => $reply,
'replied' => 0, 'replied' => 0,
'uri' => $uri, 'uri' => $uri,
'parent-uri' => $replyto, 'parent-uri' => $replyto,
'created' => DateTimeFormat::utcNow() 'created' => DateTimeFormat::utcNow()
], ],
false false
); );

View file

@ -212,7 +212,7 @@ class Post
public static function selectOriginal(array $fields = [], array $condition = [], array $params = []) public static function selectOriginal(array $fields = [], array $condition = [], array $params = [])
{ {
$original_fields = $fields; $original_fields = $fields;
$remove = []; $remove = [];
if (!empty($fields)) { if (!empty($fields)) {
foreach (['gravity', 'verb', 'thr-parent-id', 'uid'] as $field) { foreach (['gravity', 'verb', 'thr-parent-id', 'uid'] as $field) {
if (!in_array($field, $fields)) { if (!in_array($field, $fields)) {
@ -447,7 +447,8 @@ class Post
$selected = Item::DISPLAY_FIELDLIST; $selected = Item::DISPLAY_FIELDLIST;
} }
$condition = DBA::mergeConditions($condition, $condition = DBA::mergeConditions(
$condition,
["`visible` AND NOT `deleted` ["`visible` AND NOT `deleted`
AND NOT `author-blocked` AND NOT `owner-blocked` AND NOT `author-blocked` AND NOT `owner-blocked`
AND (NOT `causer-blocked` OR `causer-id` = ? OR `causer-id` IS NULL) AND NOT `contact-blocked` AND (NOT `causer-blocked` OR `causer-id` = ? OR `causer-id` IS NULL) AND NOT `contact-blocked`
@ -456,7 +457,8 @@ class Post
AND NOT EXISTS(SELECT `uri-id` FROM `post-user` WHERE `uid` = ? AND `uri-id` = " . DBA::quoteIdentifier($view) . ".`uri-id` AND `hidden`) AND NOT EXISTS(SELECT `uri-id` FROM `post-user` WHERE `uid` = ? AND `uri-id` = " . DBA::quoteIdentifier($view) . ".`uri-id` AND `hidden`)
AND NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` IN (`author-id`, `owner-id`) AND (`blocked` OR `ignored` OR `is-blocked`)) AND NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` IN (`author-id`, `owner-id`) AND (`blocked` OR `ignored` OR `is-blocked`))
AND NOT EXISTS(SELECT `gsid` FROM `user-gserver` WHERE `uid` = ? AND `gsid` IN (`author-gsid`, `owner-gsid`, `causer-gsid`) AND `ignored`)", AND NOT EXISTS(SELECT `gsid` FROM `user-gserver` WHERE `uid` = ? AND `gsid` IN (`author-gsid`, `owner-gsid`, `causer-gsid`) AND `ignored`)",
0, Contact::SHARING, Contact::FRIEND, 0, $uid, $uid, $uid]); 0, Contact::SHARING, Contact::FRIEND, 0, $uid, $uid, $uid]
);
$select_string = implode(', ', array_map([DBA::class, 'quoteIdentifier'], $selected)); $select_string = implode(', ', array_map([DBA::class, 'quoteIdentifier'], $selected));
@ -581,7 +583,7 @@ class Post
public static function selectOriginalForUser(int $uid, array $selected = [], array $condition = [], array $params = []) public static function selectOriginalForUser(int $uid, array $selected = [], array $condition = [], array $params = [])
{ {
$original_selected = $selected; $original_selected = $selected;
$remove = []; $remove = [];
if (!empty($selected)) { if (!empty($selected)) {
foreach (['gravity', 'verb', 'thr-parent-id'] as $field) { foreach (['gravity', 'verb', 'thr-parent-id'] as $field) {
if (!in_array($field, $selected)) { if (!in_array($field, $selected)) {

View file

@ -53,7 +53,7 @@ class Delayed
$last_publish = DI::pConfig()->get($item['uid'], 'system', 'last_publish', 0, true); $last_publish = DI::pConfig()->get($item['uid'], 'system', 'last_publish', 0, true);
$next_publish = max($last_publish + (60 * $min_posting), time()); $next_publish = max($last_publish + (60 * $min_posting), time());
$delayed = date(DateTimeFormat::MYSQL, $next_publish); $delayed = date(DateTimeFormat::MYSQL, $next_publish);
DI::pConfig()->set($item['uid'], 'system', 'last_publish', $next_publish); DI::pConfig()->set($item['uid'], 'system', 'last_publish', $next_publish);
} }
@ -151,13 +151,13 @@ class Delayed
} }
return [ return [
'parameters' => $delayed, 'parameters' => $delayed,
'item' => $parameters[0], 'item' => $parameters[0],
'notify' => $parameters[1], 'notify' => $parameters[1],
'taglist' => $parameters[2], 'taglist' => $parameters[2],
'attachments' => $parameters[3], 'attachments' => $parameters[3],
'unprepared' => $parameters[4], 'unprepared' => $parameters[4],
'uri' => $parameters[5], 'uri' => $parameters[5],
]; ];
} }

View file

@ -27,16 +27,16 @@ class Engagement
const KEYWORDS = ['source', 'server', 'from', 'to', 'group', 'application', 'tag', 'network', 'platform', 'visibility', 'language', 'media']; const KEYWORDS = ['source', 'server', 'from', 'to', 'group', 'application', 'tag', 'network', 'platform', 'visibility', 'language', 'media'];
const SHORTCUTS = ['lang' => 'language', 'net' => 'network', 'relay' => 'application']; const SHORTCUTS = ['lang' => 'language', 'net' => 'network', 'relay' => 'application'];
const ALTERNATIVES = ['source:news' => 'source:service', 'source:relay' => 'source:application', const ALTERNATIVES = ['source:news' => 'source:service', 'source:relay' => 'source:application',
'media:picture' => 'media:image', 'media:photo' => 'media:image', 'media:picture' => 'media:image', 'media:photo' => 'media:image',
'network:activitypub' => 'network:apub', 'network:friendica' => 'network:dfrn', 'network:activitypub' => 'network:apub', 'network:friendica' => 'network:dfrn',
'network:diaspora' => 'network:dspr', 'network:discourse' => 'network:dscs', 'network:diaspora' => 'network:dspr', 'network:discourse' => 'network:dscs',
'network:tumblr' => 'network:tmbl', 'network:bluesky' => 'network:bsky']; 'network:tumblr' => 'network:tmbl', 'network:bluesky' => 'network:bsky'];
const MEDIA_NONE = 0; const MEDIA_NONE = 0;
const MEDIA_IMAGE = 1; const MEDIA_IMAGE = 1;
const MEDIA_VIDEO = 2; const MEDIA_VIDEO = 2;
const MEDIA_AUDIO = 4; const MEDIA_AUDIO = 4;
const MEDIA_CARD = 8; const MEDIA_CARD = 8;
const MEDIA_POST = 16; const MEDIA_POST = 16;
/** /**
* Store engagement data from an item array * Store engagement data from an item array
@ -51,11 +51,13 @@ class Engagement
return 0; return 0;
} }
$parent = Post::selectFirst(['uri-id', 'created', 'uid', 'private', 'quote-uri-id', $parent = Post::selectFirst(
'contact-contact-type', 'network', 'title', 'content-warning', 'body', 'language', ['uri-id', 'created', 'uid', 'private', 'quote-uri-id',
'author-id', 'author-contact-type', 'author-nick', 'author-addr', 'author-gsid', 'contact-contact-type', 'network', 'title', 'content-warning', 'body', 'language',
'owner-id', 'owner-contact-type', 'owner-nick', 'owner-addr', 'owner-gsid'], 'author-id', 'author-contact-type', 'author-nick', 'author-addr', 'author-gsid',
['uri-id' => $item['parent-uri-id']]); 'owner-id', 'owner-contact-type', 'owner-nick', 'owner-addr', 'owner-gsid'],
['uri-id' => $item['parent-uri-id']]
);
if ($parent['created'] < self::getCreationDateLimit(false)) { if ($parent['created'] < self::getCreationDateLimit(false)) {
DI::logger()->debug('Post is too old', ['uri-id' => $item['uri-id'], 'parent-uri-id' => $item['parent-uri-id'], 'created' => $parent['created']]); DI::logger()->debug('Post is too old', ['uri-id' => $item['uri-id'], 'parent-uri-id' => $item['parent-uri-id'], 'created' => $parent['created']]);
@ -134,7 +136,7 @@ class Engagement
$body = BBCode::removeSharedData($body); $body = BBCode::removeSharedData($body);
$body = preg_replace('/[^@!#]\[url\=.*?\].*?\[\/url\]/ism', '', $body); $body = preg_replace('/[^@!#]\[url\=.*?\].*?\[\/url\]/ism', '', $body);
$body = BBCode::removeLinks($body); $body = BBCode::removeLinks($body);
$msg = BBCode::toPlaintext($body, false); $msg = BBCode::toPlaintext($body, false);
return mb_strlen($msg); return mb_strlen($msg);
} }
@ -203,7 +205,7 @@ class Engagement
$body = '[nosmile]network_' . $item['network']; $body = '[nosmile]network_' . $item['network'];
if (!empty($item['author-gsid'])) { if (!empty($item['author-gsid'])) {
$gserver = DBA::selectFirst('gserver', ['platform', 'nurl'], ['id' => $item['author-gsid']]); $gserver = DBA::selectFirst('gserver', ['platform', 'nurl'], ['id' => $item['author-gsid']]);
$platform = preg_replace('/[\W]/', '', $gserver['platform'] ?? ''); $platform = preg_replace('/[\W]/', '', $gserver['platform'] ?? '');
if (!empty($platform)) { if (!empty($platform)) {
$body .= ' platform_' . $platform; $body .= ' platform_' . $platform;
@ -212,7 +214,7 @@ class Engagement
} }
if (($item['owner-contact-type'] == Contact::TYPE_COMMUNITY) && !empty($item['owner-gsid']) && ($item['owner-gsid'] != ($item['author-gsid'] ?? 0))) { if (($item['owner-contact-type'] == Contact::TYPE_COMMUNITY) && !empty($item['owner-gsid']) && ($item['owner-gsid'] != ($item['author-gsid'] ?? 0))) {
$gserver = DBA::selectFirst('gserver', ['platform', 'nurl'], ['id' => $item['owner-gsid']]); $gserver = DBA::selectFirst('gserver', ['platform', 'nurl'], ['id' => $item['owner-gsid']]);
$platform = preg_replace('/[\W]/', '', $gserver['platform'] ?? ''); $platform = preg_replace('/[\W]/', '', $gserver['platform'] ?? '');
if (!empty($platform) && !strpos($body, 'platform_' . $platform)) { if (!empty($platform) && !strpos($body, 'platform_' . $platform)) {
$body .= ' platform_' . $platform; $body .= ' platform_' . $platform;
@ -252,7 +254,7 @@ class Engagement
$body .= ' from_' . $item['author-nick'] . ' from_' . $item['author-addr']; $body .= ' from_' . $item['author-nick'] . ' from_' . $item['author-addr'];
} }
if ($item['author-id'] != $item['owner-id']) { if ($item['author-id'] != $item['owner-id']) {
if ($item['owner-contact-type'] == Contact::TYPE_COMMUNITY) { if ($item['owner-contact-type'] == Contact::TYPE_COMMUNITY) {
$body .= ' group_' . $item['owner-nick'] . ' group_' . $item['owner-addr']; $body .= ' group_' . $item['owner-nick'] . ' group_' . $item['owner-addr'];
} elseif (in_array($item['owner-contact-type'], [Contact::TYPE_PERSON, Contact::TYPE_NEWS, Contact::TYPE_ORGANISATION])) { } elseif (in_array($item['owner-contact-type'], [Contact::TYPE_PERSON, Contact::TYPE_NEWS, Contact::TYPE_ORGANISATION])) {
@ -314,8 +316,10 @@ class Engagement
private static function addResharers(string $text, int $uri_id): string private static function addResharers(string $text, int $uri_id): string
{ {
$result = Post::selectPosts(['author-addr', 'author-nick', 'author-contact-type'], $result = Post::selectPosts(
['thr-parent-id' => $uri_id, 'gravity' => Item::GRAVITY_ACTIVITY, 'verb' => Activity::ANNOUNCE, 'author-contact-type' => [Contact::TYPE_RELAY, Contact::TYPE_COMMUNITY]]); ['author-addr', 'author-nick', 'author-contact-type'],
['thr-parent-id' => $uri_id, 'gravity' => Item::GRAVITY_ACTIVITY, 'verb' => Activity::ANNOUNCE, 'author-contact-type' => [Contact::TYPE_RELAY, Contact::TYPE_COMMUNITY]]
);
while ($reshare = Post::fetch($result)) { while ($reshare = Post::fetch($result)) {
$prefix = ''; $prefix = '';
@ -392,11 +396,11 @@ class Engagement
public static function escapeKeywords(string $fullTextSearch): string public static function escapeKeywords(string $fullTextSearch): string
{ {
foreach (SELF::SHORTCUTS as $search => $replace) { foreach (self::SHORTCUTS as $search => $replace) {
$fullTextSearch = preg_replace('~' . $search . ':(.[\w\*@\.-]+)~', $replace . ':$1', $fullTextSearch); $fullTextSearch = preg_replace('~' . $search . ':(.[\w\*@\.-]+)~', $replace . ':$1', $fullTextSearch);
} }
foreach (SELF::ALTERNATIVES as $search => $replace) { foreach (self::ALTERNATIVES as $search => $replace) {
$fullTextSearch = str_replace($search, $replace, $fullTextSearch); $fullTextSearch = str_replace($search, $replace, $fullTextSearch);
} }

View file

@ -61,9 +61,9 @@ class Link
$id = $link['id']; $id = $link['id'];
DI::logger()->info('Found', ['id' => $id, 'uri-id' => $uriId, 'url' => $url]); DI::logger()->info('Found', ['id' => $id, 'uri-id' => $uriId, 'url' => $url]);
} else { } else {
$fields = self::fetchMimeType($url); $fields = self::fetchMimeType($url);
$fields['uri-id'] = $uriId; $fields['uri-id'] = $uriId;
$fields['url'] = Network::sanitizeUrl($url); $fields['url'] = Network::sanitizeUrl($url);
DBA::insert('post-link', $fields, Database::INSERT_IGNORE); DBA::insert('post-link', $fields, Database::INSERT_IGNORE);
$id = DBA::lastInsertId(); $id = DBA::lastInsertId();
@ -126,7 +126,7 @@ class Link
if (Images::isSupportedMimeType($fields['mimetype'])) { if (Images::isSupportedMimeType($fields['mimetype'])) {
$img_str = $curlResult->getBodyString(); $img_str = $curlResult->getBodyString();
$image = new Image($img_str, $fields['mimetype'], $url, false); $image = new Image($img_str, $fields['mimetype'], $url, false);
if ($image->isValid()) { if ($image->isValid()) {
$fields['mimetype'] = $image->getType(); $fields['mimetype'] = $image->getType();
$fields['width'] = $image->getWidth(); $fields['width'] = $image->getWidth();

View file

@ -118,7 +118,7 @@ class UserNotification
$fields = ['id', 'uri-id', 'parent-uri-id', 'uid', 'body', 'parent', 'gravity', 'vid', 'gravity', $fields = ['id', 'uri-id', 'parent-uri-id', 'uid', 'body', 'parent', 'gravity', 'vid', 'gravity',
'contact-id', 'author-id', 'author-gsid', 'owner-id', 'owner-gsid', 'causer-id', 'causer-gsid', 'contact-id', 'author-id', 'author-gsid', 'owner-id', 'owner-gsid', 'causer-id', 'causer-gsid',
'private', 'thr-parent', 'thr-parent-id', 'parent-uri-id', 'parent-uri', 'verb']; 'private', 'thr-parent', 'thr-parent-id', 'parent-uri-id', 'parent-uri', 'verb'];
$item = Post::selectFirst($fields, ['uri-id' => $uri_id, 'uid' => $uid, 'origin' => false]); $item = Post::selectFirst($fields, ['uri-id' => $uri_id, 'uid' => $uid, 'origin' => false]);
if (!DBA::isResult($item)) { if (!DBA::isResult($item)) {
return; return;
} }

View file

@ -266,7 +266,7 @@ class Profile
*/ */
public static function getVCardHtml(array $profile, bool $block, bool $show_contacts): string public static function getVCardHtml(array $profile, bool $block, bool $show_contacts): string
{ {
$o = ''; $o = '';
$location = false; $location = false;
$profile_contact = []; $profile_contact = [];
@ -293,8 +293,8 @@ class Profile
$cid = $contact['id']; $cid = $contact['id'];
$follow_link = null; $follow_link = null;
$unfollow_link = null; $unfollow_link = null;
$wallmessage_link = null; $wallmessage_link = null;
// Who is the logged-in user to this profile? // Who is the logged-in user to this profile?
@ -303,13 +303,11 @@ class Profile
$visitor_contact = Contact::selectFirst(['rel'], ['uid' => $profile['uid'], 'nurl' => Strings::normaliseLink(DI::userSession()->getMyUrl())]); $visitor_contact = Contact::selectFirst(['rel'], ['uid' => $profile['uid'], 'nurl' => Strings::normaliseLink(DI::userSession()->getMyUrl())]);
} }
$local_user_is_self = DI::userSession()->getMyUrl() && ($profile['url'] == DI::userSession()->getMyUrl()); $local_user_is_self = DI::userSession()->getMyUrl() && ($profile['url'] == DI::userSession()->getMyUrl());
$visitor_is_authenticated = (bool)DI::userSession()->getMyUrl(); $visitor_is_authenticated = (bool)DI::userSession()->getMyUrl();
$visitor_is_following = $visitor_is_following = in_array($visitor_contact['rel'] ?? 0, [Contact::FOLLOWER, Contact::FRIEND])
in_array($visitor_contact['rel'] ?? 0, [Contact::FOLLOWER, Contact::FRIEND])
|| in_array($profile_contact['rel'] ?? 0, [Contact::SHARING, Contact::FRIEND]); || in_array($profile_contact['rel'] ?? 0, [Contact::SHARING, Contact::FRIEND]);
$visitor_is_followed = $visitor_is_followed = in_array($visitor_contact['rel'] ?? 0, [Contact::SHARING, Contact::FRIEND])
in_array($visitor_contact['rel'] ?? 0, [Contact::SHARING, Contact::FRIEND])
|| in_array($profile_contact['rel'] ?? 0, [Contact::FOLLOWER, Contact::FRIEND]); || in_array($profile_contact['rel'] ?? 0, [Contact::FOLLOWER, Contact::FRIEND]);
$visitor_base_path = DI::userSession()->getMyUrl() ? preg_replace('=/profile/(.*)=ism', '', DI::userSession()->getMyUrl()) : ''; $visitor_base_path = DI::userSession()->getMyUrl() ? preg_replace('=/profile/(.*)=ism', '', DI::userSession()->getMyUrl()) : '';
@ -341,15 +339,15 @@ class Profile
$profile['edit'] = [DI::baseUrl() . '/settings/profile', DI::l10n()->t('Edit profile'), '', DI::l10n()->t('Edit profile')]; $profile['edit'] = [DI::baseUrl() . '/settings/profile', DI::l10n()->t('Edit profile'), '', DI::l10n()->t('Edit profile')];
$profile['menu'] = [ $profile['menu'] = [
'chg_photo' => DI::l10n()->t('Change profile photo'), 'chg_photo' => DI::l10n()->t('Change profile photo'),
'cr_new' => null, 'cr_new' => null,
'entries' => [], 'entries' => [],
]; ];
} }
// Fetch the account type // Fetch the account type
$account_type = Contact::getAccountType($profile['account-type']); $account_type = Contact::getAccountType($profile['account-type']);
if (!empty($profile['address']) || !empty($profile['location'])) { if (!empty($profile['address']) || !empty($profile['location'])) {
$location = DI::l10n()->t('Location:'); $location = DI::l10n()->t('Location:');
} }
@ -363,8 +361,8 @@ class Profile
} }
$split_name = Diaspora::splitName($profile['name']); $split_name = Diaspora::splitName($profile['name']);
$firstname = $split_name['first']; $firstname = $split_name['first'];
$lastname = $split_name['last']; $lastname = $split_name['last'];
if (!empty($profile['guid'])) { if (!empty($profile['guid'])) {
$diaspora = [ $diaspora = [
@ -384,7 +382,7 @@ class Profile
} }
$contact_block = ''; $contact_block = '';
$updated = ''; $updated = '';
$contact_count = 0; $contact_count = 0;
if (!empty($profile['last-item'])) { if (!empty($profile['last-item'])) {
@ -415,7 +413,7 @@ class Profile
'upubkey' => null, 'upubkey' => null,
]; ];
foreach ($profile as $k => $v) { foreach ($profile as $k => $v) {
$k = str_replace('-', '_', $k); $k = str_replace('-', '_', $k);
$p[$k] = $v; $p[$k] = $v;
} }
@ -444,35 +442,35 @@ class Profile
$mention_url = 'compose/0?body=@' . $profile['addr']; $mention_url = 'compose/0?body=@' . $profile['addr'];
$network_label = DI::l10n()->t('Network Posts'); $network_label = DI::l10n()->t('Network Posts');
} }
$network_url = 'contact/' . $cid . '/conversations'; $network_url = 'contact/' . $cid . '/conversations';
$tpl = Renderer::getMarkupTemplate('profile/vcard.tpl'); $tpl = Renderer::getMarkupTemplate('profile/vcard.tpl');
$o .= Renderer::replaceMacros($tpl, [ $o .= Renderer::replaceMacros($tpl, [
'$profile' => $p, '$profile' => $p,
'$xmpp' => $xmpp, '$xmpp' => $xmpp,
'$matrix' => $matrix, '$matrix' => $matrix,
'$follow' => DI::l10n()->t('Follow'), '$follow' => DI::l10n()->t('Follow'),
'$follow_link' => $follow_link, '$follow_link' => $follow_link,
'$unfollow' => DI::l10n()->t('Unfollow'), '$unfollow' => DI::l10n()->t('Unfollow'),
'$unfollow_link' => $unfollow_link, '$unfollow_link' => $unfollow_link,
'$subscribe_feed' => DI::l10n()->t('Atom feed'), '$subscribe_feed' => DI::l10n()->t('Atom feed'),
'$subscribe_feed_link' => $profile['hidewall'] ?? 0 ? '' : $profile['poll'], '$subscribe_feed_link' => $profile['hidewall'] ?? 0 ? '' : $profile['poll'],
'$wallmessage' => DI::l10n()->t('Message'), '$wallmessage' => DI::l10n()->t('Message'),
'$wallmessage_link' => $wallmessage_link, '$wallmessage_link' => $wallmessage_link,
'$account_type' => $account_type, '$account_type' => $account_type,
'$location' => $location, '$location' => $location,
'$homepage' => $homepage, '$homepage' => $homepage,
'$homepage_verified' => DI::l10n()->t('This website has been verified to belong to the same person.'), '$homepage_verified' => DI::l10n()->t('This website has been verified to belong to the same person.'),
'$about' => $about, '$about' => $about,
'$network' => DI::l10n()->t('Network:'), '$network' => DI::l10n()->t('Network:'),
'$contacts' => $contact_count, '$contacts' => $contact_count,
'$updated' => $updated, '$updated' => $updated,
'$diaspora' => $diaspora, '$diaspora' => $diaspora,
'$contact_block' => $contact_block, '$contact_block' => $contact_block,
'$mention_label' => $mention_label, '$mention_label' => $mention_label,
'$mention_url' => $mention_url, '$mention_url' => $mention_url,
'$network_label' => $network_label, '$network_label' => $network_label,
'$network_url' => $network_url, '$network_url' => $network_url,
]); ]);
$arr = ['profile' => &$profile, 'entry' => &$o]; $arr = ['profile' => &$profile, 'entry' => &$o];
@ -603,7 +601,7 @@ class Profile
*/ */
public static function getEventsReminderHTML(int $uid, int $pcid): string public static function getEventsReminderHTML(int $uid, int $pcid): string
{ {
$bd_format = DI::l10n()->t('g A l F d'); // 8 AM Friday January 18 $bd_format = DI::l10n()->t('g A l F d'); // 8 AM Friday January 18
$classtoday = ''; $classtoday = '';
$condition = [ $condition = [
@ -616,13 +614,13 @@ class Profile
if (DBA::isResult($s)) { if (DBA::isResult($s)) {
$istoday = false; $istoday = false;
$total = 0; $total = 0;
while ($rr = DBA::fetch($s)) { while ($rr = DBA::fetch($s)) {
$condition = [ $condition = [
'parent-uri' => $rr['uri'], 'uid' => $rr['uid'], 'author-id' => $pcid, 'parent-uri' => $rr['uri'], 'uid' => $rr['uid'], 'author-id' => $pcid,
'vid' => [Verb::getID(Activity::ATTEND), Verb::getID(Activity::ATTENDMAYBE)], 'vid' => [Verb::getID(Activity::ATTEND), Verb::getID(Activity::ATTENDMAYBE)],
'visible' => true, 'deleted' => false 'visible' => true, 'deleted' => false
]; ];
if (!Post::exists($condition)) { if (!Post::exists($condition)) {
continue; continue;
@ -656,11 +654,11 @@ class Profile
$today = substr($strt, 0, 10) === DateTimeFormat::localNow('Y-m-d'); $today = substr($strt, 0, 10) === DateTimeFormat::localNow('Y-m-d');
$rr['title'] = $title; $rr['title'] = $title;
$rr['description'] = $description; $rr['description'] = $description;
$rr['date'] = DI::l10n()->getDay(DateTimeFormat::local($rr['start'], $bd_format)) . (($today) ? ' ' . DI::l10n()->t('[today]') : ''); $rr['date'] = DI::l10n()->getDay(DateTimeFormat::local($rr['start'], $bd_format)) . (($today) ? ' ' . DI::l10n()->t('[today]') : '');
$rr['startime'] = $strt; $rr['startime'] = $strt;
$rr['today'] = $today; $rr['today'] = $today;
$r[] = $rr; $r[] = $rr;
} }
@ -669,11 +667,11 @@ class Profile
} }
$tpl = Renderer::getMarkupTemplate('events_reminder.tpl'); $tpl = Renderer::getMarkupTemplate('events_reminder.tpl');
return Renderer::replaceMacros($tpl, [ return Renderer::replaceMacros($tpl, [
'$classtoday' => $classtoday, '$classtoday' => $classtoday,
'$count' => count($r), '$count' => count($r),
'$event_reminders' => DI::l10n()->t('Event Reminders'), '$event_reminders' => DI::l10n()->t('Event Reminders'),
'$event_title' => DI::l10n()->t('Upcoming events the next 7 days:'), '$event_title' => DI::l10n()->t('Upcoming events the next 7 days:'),
'$events' => $r, '$events' => $r,
]); ]);
} }
@ -708,9 +706,9 @@ class Profile
public static function searchProfiles(int $start = 0, int $count = 100, string $search = null): array public static function searchProfiles(int $start = 0, int $count = 100, string $search = null): array
{ {
if (!empty($search)) { if (!empty($search)) {
$publish = (DI::config()->get('system', 'publish_all') ? '' : "AND `publish` "); $publish = (DI::config()->get('system', 'publish_all') ? '' : "AND `publish` ");
$searchTerm = '%' . $search . '%'; $searchTerm = '%' . $search . '%';
$condition = [ $condition = [
"`verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired` "`verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`
$publish $publish
AND ((`name` LIKE ?) OR AND ((`name` LIKE ?) OR
@ -822,7 +820,7 @@ class Profile
$profile['profile-name'] = null; $profile['profile-name'] = null;
$profile['is-default'] = null; $profile['is-default'] = null;
DBA::update('profile', $profile, ['id' => $profile['id']]); DBA::update('profile', $profile, ['id' => $profile['id']]);
} else if (!empty($profile['id'])) { } elseif (!empty($profile['id'])) {
DBA::delete('profile', ['id' => $profile['id']]); DBA::delete('profile', ['id' => $profile['id']]);
} }
} }

View file

@ -26,13 +26,13 @@ use Friendica\Util\Strings;
*/ */
class Tag class Tag
{ {
const UNKNOWN = 0; const UNKNOWN = 0;
const HASHTAG = 1; const HASHTAG = 1;
const MENTION = 2; const MENTION = 2;
/** /**
* An implicit mention is a mention in a comment body that is redundant with the threading information. * An implicit mention is a mention in a comment body that is redundant with the threading information.
*/ */
const IMPLICIT_MENTION = 8; const IMPLICIT_MENTION = 8;
/** /**
* An exclusive mention transmits the post only to the target account without transmitting it to the followers, usually a group. * An exclusive mention transmits the post only to the target account without transmitting it to the followers, usually a group.
*/ */
@ -90,7 +90,7 @@ class Tag
return; return;
} }
$cid = 0; $cid = 0;
$tagid = 0; $tagid = 0;
if (in_array($type, [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION, self::TO, self::CC, self::BTO, self::BCC, self::AUDIENCE, self::ATTRIBUTED])) { if (in_array($type, [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION, self::TO, self::CC, self::BTO, self::BCC, self::AUDIENCE, self::ATTRIBUTED])) {
@ -137,7 +137,7 @@ class Tag
$fields = ['uri-id' => $uriId, 'type' => $type, 'tid' => $tagid, 'cid' => $cid]; $fields = ['uri-id' => $uriId, 'type' => $type, 'tid' => $tagid, 'cid' => $cid];
if (in_array($type, [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION])) { if (in_array($type, [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION])) {
$condition = $fields; $condition = $fields;
$condition['type'] = [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION]; $condition['type'] = [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION];
if (DBA::exists('post-tag', $condition)) { if (DBA::exists('post-tag', $condition)) {
DI::logger()->info('Tag already exists', $fields); DI::logger()->info('Tag already exists', $fields);
@ -528,9 +528,9 @@ class Tag
public static function populateFromItem(array &$item): array public static function populateFromItem(array &$item): array
{ {
$return = [ $return = [
'tags' => [], 'tags' => [],
'hashtags' => [], 'hashtags' => [],
'mentions' => [], 'mentions' => [],
'implicit_mentions' => [], 'implicit_mentions' => [],
]; ];
@ -556,7 +556,7 @@ class Tag
} }
$return['hashtags'][] = '<bdi>' . $prefix . '<a href="' . $tag['url'] . '" target="_blank" rel="noopener noreferrer">' . htmlspecialchars($tag['name']) . '</a></bdi>'; $return['hashtags'][] = '<bdi>' . $prefix . '<a href="' . $tag['url'] . '" target="_blank" rel="noopener noreferrer">' . htmlspecialchars($tag['name']) . '</a></bdi>';
$return['tags'][] = '<bdi>' . $prefix . '<a href="' . $tag['url'] . '" target="_blank" rel="noopener noreferrer">' . htmlspecialchars($tag['name']) . '</a></bdi>'; $return['tags'][] = '<bdi>' . $prefix . '<a href="' . $tag['url'] . '" target="_blank" rel="noopener noreferrer">' . htmlspecialchars($tag['name']) . '</a></bdi>';
break; break;
case self::MENTION: case self::MENTION:
@ -567,7 +567,7 @@ class Tag
$tag['url'] = Contact::magicLink($tag['url']); $tag['url'] = Contact::magicLink($tag['url']);
} }
$return['mentions'][] = '<bdi>' . $prefix . '<a href="' . $tag['url'] . '" target="_blank" rel="noopener noreferrer">' . htmlspecialchars($tag['name']) . '</a></bdi>'; $return['mentions'][] = '<bdi>' . $prefix . '<a href="' . $tag['url'] . '" target="_blank" rel="noopener noreferrer">' . htmlspecialchars($tag['name']) . '</a></bdi>';
$return['tags'][] = '<bdi>' . $prefix . '<a href="' . $tag['url'] . '" target="_blank" rel="noopener noreferrer">' . htmlspecialchars($tag['name']) . '</a></bdi>'; $return['tags'][] = '<bdi>' . $prefix . '<a href="' . $tag['url'] . '" target="_blank" rel="noopener noreferrer">' . htmlspecialchars($tag['name']) . '</a></bdi>';
break; break;
case self::IMPLICIT_MENTION: case self::IMPLICIT_MENTION:
@ -808,7 +808,7 @@ class Tag
*/ */
private static function getUIDListByTag(string $tag): array private static function getUIDListByTag(string $tag): array
{ {
$uids = []; $uids = [];
$searches = DBA::select('search', ['uid'], ['term' => $tag]); $searches = DBA::select('search', ['uid'], ['term' => $tag]);
while ($search = DBA::fetch($searches)) { while ($search = DBA::fetch($searches)) {
$uids[] = $search['uid']; $uids[] = $search['uid'];

View file

@ -84,12 +84,12 @@ class User
* This will only be assigned to contacts, not to user accounts * This will only be assigned to contacts, not to user accounts
* @{ * @{
*/ */
const ACCOUNT_TYPE_PERSON = 0; const ACCOUNT_TYPE_PERSON = 0;
const ACCOUNT_TYPE_ORGANISATION = 1; const ACCOUNT_TYPE_ORGANISATION = 1;
const ACCOUNT_TYPE_NEWS = 2; const ACCOUNT_TYPE_NEWS = 2;
const ACCOUNT_TYPE_COMMUNITY = 3; const ACCOUNT_TYPE_COMMUNITY = 3;
const ACCOUNT_TYPE_RELAY = 4; const ACCOUNT_TYPE_RELAY = 4;
const ACCOUNT_TYPE_DELETED = 127; const ACCOUNT_TYPE_DELETED = 127;
/** /**
* @} * @}
*/ */
@ -150,42 +150,42 @@ class User
} }
} }
$system['name'] = App::PLATFORM . " '" . App::CODENAME . "' " . App::VERSION . '-' . DB_UPDATE_VERSION; $system['name'] = App::PLATFORM . " '" . App::CODENAME . "' " . App::VERSION . '-' . DB_UPDATE_VERSION;
$system['uprvkey'] = $system['prvkey']; $system['uprvkey'] = $system['prvkey'];
$system['upubkey'] = $system['pubkey']; $system['upubkey'] = $system['pubkey'];
$system['nickname'] = $system['nick']; $system['nickname'] = $system['nick'];
$system['page-flags'] = self::PAGE_FLAGS_SOAPBOX; $system['page-flags'] = self::PAGE_FLAGS_SOAPBOX;
$system['account-type'] = $system['contact-type']; $system['account-type'] = $system['contact-type'];
$system['guid'] = ''; $system['guid'] = '';
$system['picdate'] = ''; $system['picdate'] = '';
$system['theme'] = ''; $system['theme'] = '';
$system['publish'] = false; $system['publish'] = false;
$system['net-publish'] = false; $system['net-publish'] = false;
$system['hide-friends'] = true; $system['hide-friends'] = true;
$system['hidewall'] = true; $system['hidewall'] = true;
$system['prv_keywords'] = ''; $system['prv_keywords'] = '';
$system['pub_keywords'] = ''; $system['pub_keywords'] = '';
$system['address'] = ''; $system['address'] = '';
$system['locality'] = ''; $system['locality'] = '';
$system['region'] = ''; $system['region'] = '';
$system['postal-code'] = ''; $system['postal-code'] = '';
$system['country-name'] = ''; $system['country-name'] = '';
$system['homepage'] = (string)DI::baseUrl(); $system['homepage'] = (string)DI::baseUrl();
$system['dob'] = '0000-00-00'; $system['dob'] = '0000-00-00';
// Ensure that the user contains data // Ensure that the user contains data
$user = DBA::selectFirst('user', ['prvkey', 'guid', 'language'], ['uid' => 0]); $user = DBA::selectFirst('user', ['prvkey', 'guid', 'language'], ['uid' => 0]);
if (empty($user['prvkey']) || empty($user['guid'])) { if (empty($user['prvkey']) || empty($user['guid'])) {
$fields = [ $fields = [
'username' => $system['name'], 'username' => $system['name'],
'nickname' => $system['nick'], 'nickname' => $system['nick'],
'register_date' => $system['created'], 'register_date' => $system['created'],
'pubkey' => $system['pubkey'], 'pubkey' => $system['pubkey'],
'prvkey' => $system['prvkey'], 'prvkey' => $system['prvkey'],
'guid' => System::createUUID(), 'guid' => System::createUUID(),
'verified' => true, 'verified' => true,
'page-flags' => self::PAGE_FLAGS_SOAPBOX, 'page-flags' => self::PAGE_FLAGS_SOAPBOX,
'account-type' => self::ACCOUNT_TYPE_RELAY, 'account-type' => self::ACCOUNT_TYPE_RELAY,
]; ];
DBA::update('user', $fields, ['uid' => 0]); DBA::update('user', $fields, ['uid' => 0]);
@ -349,12 +349,12 @@ class User
DI::pConfig()->set($uid, 'system', 'unlisted', true); DI::pConfig()->set($uid, 'system', 'unlisted', true);
$fields = [ $fields = [
'allow_cid' => '', 'allow_cid' => '',
'allow_gid' => $user['page-flags'] == self::PAGE_FLAGS_PRVGROUP ? '<' . Circle::FOLLOWERS . '>' : '', 'allow_gid' => $user['page-flags'] == self::PAGE_FLAGS_PRVGROUP ? '<' . Circle::FOLLOWERS . '>' : '',
'deny_cid' => '', 'deny_cid' => '',
'deny_gid' => '', 'deny_gid' => '',
'blockwall' => true, 'blockwall' => true,
'blocktags' => true, 'blocktags' => true,
]; ];
self::update($fields, $uid); self::update($fields, $uid);
@ -490,12 +490,12 @@ class User
// Check if the returned data is valid, otherwise fix it. See issue #6122 // Check if the returned data is valid, otherwise fix it. See issue #6122
// Check for correct url and normalised nurl // Check for correct url and normalised nurl
$url = DI::baseUrl() . '/profile/' . $owner['nickname']; $url = DI::baseUrl() . '/profile/' . $owner['nickname'];
$repair = empty($owner['baseurl']) || empty($owner['network']) || ($owner['url'] != $url) || ($owner['nurl'] != Strings::normaliseLink($owner['url'])); $repair = empty($owner['baseurl']) || empty($owner['network']) || ($owner['url'] != $url) || ($owner['nurl'] != Strings::normaliseLink($owner['url']));
if (!$repair) { if (!$repair) {
// Check if "addr" is present and correct // Check if "addr" is present and correct
$addr = $owner['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3); $addr = $owner['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3);
$repair = ($addr != $owner['addr']) || empty($owner['prvkey']) || empty($owner['pubkey']); $repair = ($addr != $owner['addr']) || empty($owner['prvkey']) || empty($owner['pubkey']);
} }
@ -628,7 +628,7 @@ class User
$users = DBA::select('user', ['uid', 'language'], $condition); $users = DBA::select('user', ['uid', 'language'], $condition);
while ($user = DBA::fetch($users)) { while ($user = DBA::fetch($users)) {
$uids[] = $user['uid']; $uids[] = $user['uid'];
$code = DI::l10n()->toISO6391($user['language']); $code = DI::l10n()->toISO6391($user['language']);
if (!in_array($code, $supported)) { if (!in_array($code, $supported)) {
continue; continue;
} }
@ -644,7 +644,7 @@ class User
$values = unserialize($channel['v']); $values = unserialize($channel['v']);
if (!empty($values) && is_array($values)) { if (!empty($values) && is_array($values)) {
foreach ($values as $language) { foreach ($values as $language) {
$language = DI::l10n()->toISO6391($language); $language = DI::l10n()->toISO6391($language);
$languages[$language] = $language; $languages[$language] = $language;
} }
} }
@ -807,11 +807,11 @@ class User
} elseif (is_int($user_info) || is_string($user_info)) { } elseif (is_int($user_info) || is_string($user_info)) {
$fields = ['uid', 'nickname', 'password', 'legacy_password']; $fields = ['uid', 'nickname', 'password', 'legacy_password'];
if (is_int($user_info)) { if (is_int($user_info)) {
$condition = [ $condition = [
'uid' => $user_info, 'uid' => $user_info,
'account_expired' => false, 'account_expired' => false,
'account_removed' => false, 'account_removed' => false,
'verified' => true 'verified' => true
]; ];
if (!$with_blocked) { if (!$with_blocked) {
$condition = DBA::mergeConditions($condition, ['blocked' => false]); $condition = DBA::mergeConditions($condition, ['blocked' => false]);
@ -898,9 +898,9 @@ class User
return $passwordExposedChecker->passwordExposed($password) === PasswordExposed\Enums\PasswordStatus::EXPOSED; return $passwordExposedChecker->passwordExposed($password) === PasswordExposed\Enums\PasswordStatus::EXPOSED;
} catch (Exception $e) { } catch (Exception $e) {
DI::logger()->error('Password Exposed Exception: ' . $e->getMessage(), [ DI::logger()->error('Password Exposed Exception: ' . $e->getMessage(), [
'code' => $e->getCode(), 'code' => $e->getCode(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),
'trace' => $e->getTraceAsString() 'trace' => $e->getTraceAsString()
]); ]);
@ -1001,9 +1001,9 @@ class User
private static function updatePasswordHashed(int $uid, string $password_hashed): bool private static function updatePasswordHashed(int $uid, string $password_hashed): bool
{ {
$fields = [ $fields = [
'password' => $password_hashed, 'password' => $password_hashed,
'pwdreset' => null, 'pwdreset' => null,
'pwdreset_time' => null, 'pwdreset_time' => null,
'legacy_password' => false 'legacy_password' => false
]; ];
return DBA::update('user', $fields, ['uid' => $uid]); return DBA::update('user', $fields, ['uid' => $uid]);
@ -1109,7 +1109,7 @@ class User
break; break;
} }
$updated = ''; $updated = '';
$mimetype = ''; $mimetype = '';
$photo = Photo::selectFirst(['type', 'created', 'edited', 'updated'], ["scale" => $scale, 'uid' => $user['uid'], 'profile' => true]); $photo = Photo::selectFirst(['type', 'created', 'edited', 'updated'], ["scale" => $scale, 'uid' => $user['uid'], 'profile' => true]);
@ -1214,14 +1214,14 @@ class User
throw new Exception(DI::l10n()->t('Invalid OpenID url')); throw new Exception(DI::l10n()->t('Invalid OpenID url'));
} }
$_SESSION['register'] = 1; $_SESSION['register'] = 1;
$_SESSION['openid'] = $openid_url; $_SESSION['openid'] = $openid_url;
$openid = new LightOpenID(DI::baseUrl()->getHost()); $openid = new LightOpenID(DI::baseUrl()->getHost());
/** @phpstan-ignore-next-line $openid->identity is private, but will be set via magic setter */ /** @phpstan-ignore-next-line $openid->identity is private, but will be set via magic setter */
$openid->identity = $openid_url; $openid->identity = $openid_url;
$openid->returnUrl = DI::baseUrl() . '/openid'; $openid->returnUrl = DI::baseUrl() . '/openid';
$openid->required = ['namePerson/friendly', 'contact/email', 'namePerson']; $openid->required = ['namePerson/friendly', 'contact/email', 'namePerson'];
$openid->optional = ['namePerson/first', 'media/image/aspect11', 'media/image/default']; $openid->optional = ['namePerson/first', 'media/image/aspect11', 'media/image/default'];
try { try {
$authurl = $openid->authUrl(); $authurl = $openid->authUrl();
} catch (Exception $e) { } catch (Exception $e) {
@ -1246,7 +1246,7 @@ class User
if ($username_min_length > $username_max_length) { if ($username_min_length > $username_max_length) {
DI::logger()->error(DI::l10n()->t('system.username_min_length (%s) and system.username_max_length (%s) are excluding each other, swapping values.', $username_min_length, $username_max_length)); DI::logger()->error(DI::l10n()->t('system.username_min_length (%s) and system.username_max_length (%s) are excluding each other, swapping values.', $username_min_length, $username_max_length));
$tmp = $username_min_length; $tmp = $username_min_length;
$username_min_length = $username_max_length; $username_min_length = $username_max_length;
$username_max_length = $tmp; $username_max_length = $tmp;
} }
@ -1303,7 +1303,7 @@ class User
throw new Exception(DI::l10n()->t('Nickname is already registered. Please choose another.')); throw new Exception(DI::l10n()->t('Nickname is already registered. Please choose another.'));
} }
$new_password = strlen($password) ? $password : self::generateNewPassword(); $new_password = strlen($password) ? $password : self::generateNewPassword();
$new_password_encoded = self::hashPassword($new_password); $new_password_encoded = self::hashPassword($new_password);
$return['password'] = $new_password; $return['password'] = $new_password;
@ -1317,24 +1317,24 @@ class User
$pubkey = $keys['pubkey']; $pubkey = $keys['pubkey'];
$insert_result = DBA::insert('user', [ $insert_result = DBA::insert('user', [
'guid' => System::createUUID(), 'guid' => System::createUUID(),
'username' => $username, 'username' => $username,
'password' => $new_password_encoded, 'password' => $new_password_encoded,
'email' => $email, 'email' => $email,
'openid' => $openid_url, 'openid' => $openid_url,
'nickname' => $nickname, 'nickname' => $nickname,
'pubkey' => $pubkey, 'pubkey' => $pubkey,
'prvkey' => $prvkey, 'prvkey' => $prvkey,
'verified' => $verified, 'verified' => $verified,
'blocked' => $blocked, 'blocked' => $blocked,
'language' => $language, 'language' => $language,
'timezone' => 'UTC', 'timezone' => 'UTC',
'register_date' => DateTimeFormat::utcNow(), 'register_date' => DateTimeFormat::utcNow(),
'default-location' => '' 'default-location' => ''
]); ]);
if ($insert_result) { if ($insert_result) {
$uid = DBA::lastInsertId(); $uid = DBA::lastInsertId();
$user = DBA::selectFirst('user', [], ['uid' => $uid]); $user = DBA::selectFirst('user', [], ['uid' => $uid]);
} else { } else {
throw new Exception(DI::l10n()->t('An error occurred during registration. Please try again.')); throw new Exception(DI::l10n()->t('An error occurred during registration. Please try again.'));
@ -1354,11 +1354,11 @@ class User
} }
$insert_result = DBA::insert('profile', [ $insert_result = DBA::insert('profile', [
'uid' => $uid, 'uid' => $uid,
'name' => $username, 'name' => $username,
'photo' => self::getAvatarUrl($user), 'photo' => self::getAvatarUrl($user),
'thumb' => self::getAvatarUrl($user, Proxy::SIZE_THUMB), 'thumb' => self::getAvatarUrl($user, Proxy::SIZE_THUMB),
'publish' => $publish, 'publish' => $publish,
'net-publish' => $netpublish, 'net-publish' => $netpublish,
]); ]);
if (!$insert_result) { if (!$insert_result) {
@ -1410,15 +1410,15 @@ class User
if ($curlResult->isSuccess()) { if ($curlResult->isSuccess()) {
DI::logger()->debug('Got picture', ['Content-Type' => $curlResult->getHeader('Content-Type'), 'url' => $photo]); DI::logger()->debug('Got picture', ['Content-Type' => $curlResult->getHeader('Content-Type'), 'url' => $photo]);
$img_str = $curlResult->getBodyString(); $img_str = $curlResult->getBodyString();
$type = $curlResult->getContentType(); $type = $curlResult->getContentType();
} else { } else {
$img_str = ''; $img_str = '';
$type = ''; $type = '';
} }
} catch (\Throwable $th) { } catch (\Throwable $th) {
DI::logger()->notice('Got exception', ['code' => $th->getCode(), 'message' => $th->getMessage()]); DI::logger()->notice('Got exception', ['code' => $th->getCode(), 'message' => $th->getMessage()]);
$img_str = ''; $img_str = '';
$type = ''; $type = '';
} }
$image = new Image($img_str, $type, $photo); $image = new Image($img_str, $type, $photo);
@ -1496,7 +1496,7 @@ class User
* @param bool $block Block state (default is true) * @param bool $block Block state (default is true)
* *
* @return bool True, if successfully blocked * @return bool True, if successfully blocked
*
* @throws Exception * @throws Exception
*/ */
public static function block(int $uid, bool $block = true): bool public static function block(int $uid, bool $block = true): bool
@ -1601,21 +1601,21 @@ class User
public static function createMinimal(string $name, string $email, string $nick, string $lang = L10n::DEFAULT, string $avatar = ''): bool public static function createMinimal(string $name, string $email, string $nick, string $lang = L10n::DEFAULT, string $avatar = ''): bool
{ {
if (empty($name) || if (empty($name) ||
empty($email) || empty($email) ||
empty($nick)) { empty($nick)) {
throw new HTTPException\InternalServerErrorException('Invalid arguments.'); throw new HTTPException\InternalServerErrorException('Invalid arguments.');
} }
$result = self::create([ $result = self::create([
'username' => $name, 'username' => $name,
'email' => $email, 'email' => $email,
'nickname' => $nick, 'nickname' => $nick,
'verified' => 1, 'verified' => 1,
'language' => $lang, 'language' => $lang,
'photo' => $avatar 'photo' => $avatar
]); ]);
$user = $result['user']; $user = $result['user'];
$preamble = Strings::deindent(DI::l10n()->t(' $preamble = Strings::deindent(DI::l10n()->t('
Dear %1$s, Dear %1$s,
the administrator of %2$s has set up an account for you.')); the administrator of %2$s has set up an account for you.'));
@ -1647,7 +1647,7 @@ class User
Thank you and welcome to %4$s.')); Thank you and welcome to %4$s.'));
$preamble = sprintf($preamble, $user['username'], DI::config()->get('config', 'sitename')); $preamble = sprintf($preamble, $user['username'], DI::config()->get('config', 'sitename'));
$body = sprintf($body, DI::baseUrl(), $user['nickname'], $result['password'], DI::config()->get('config', 'sitename')); $body = sprintf($body, DI::baseUrl(), $user['nickname'], $result['password'], DI::config()->get('config', 'sitename'));
$email = DI::emailer() $email = DI::emailer()
->newSystemMail() ->newSystemMail()
@ -1848,7 +1848,7 @@ class User
if (!$user['parent-uid']) { if (!$user['parent-uid']) {
// First add our own entry // First add our own entry
$identities = [[ $identities = [[
'uid' => $user['uid'], 'uid' => $user['uid'],
'username' => $user['username'], 'username' => $user['username'],
'nickname' => $user['nickname'] 'nickname' => $user['nickname']
]]; ]];
@ -1946,17 +1946,20 @@ class User
'active_users_weekly' => 0, 'active_users_weekly' => 0,
]; ];
$userStmt = DBA::select('owner-view', ['uid', 'last-activity', 'last-item'], $userStmt = DBA::select(
'owner-view',
['uid', 'last-activity', 'last-item'],
["`verified` AND `last-activity` > ? AND NOT `blocked` ["`verified` AND `last-activity` > ? AND NOT `blocked`
AND NOT `account_removed` AND NOT `account_expired`", AND NOT `account_removed` AND NOT `account_expired`",
DBA::NULL_DATETIME]); DBA::NULL_DATETIME]
);
if (!DBA::isResult($userStmt)) { if (!DBA::isResult($userStmt)) {
return $statistics; return $statistics;
} }
$halfyear = time() - (180 * 24 * 60 * 60); $halfyear = time() - (180 * 24 * 60 * 60);
$month = time() - (30 * 24 * 60 * 60); $month = time() - (30 * 24 * 60 * 60);
$week = time() - (7 * 24 * 60 * 60); $week = time() - (7 * 24 * 60 * 60);
while ($user = DBA::fetch($userStmt)) { while ($user = DBA::fetch($userStmt)) {
$statistics['total_users']++; $statistics['total_users']++;
@ -1994,18 +1997,18 @@ class User
*/ */
public static function getList(int $start = 0, int $count = Pager::ITEMS_PER_PAGE, string $type = 'all', string $order = 'name', bool $descending = false) public static function getList(int $start = 0, int $count = Pager::ITEMS_PER_PAGE, string $type = 'all', string $order = 'name', bool $descending = false)
{ {
$param = ['limit' => [$start, $count], 'order' => [$order => $descending]]; $param = ['limit' => [$start, $count], 'order' => [$order => $descending]];
$condition = []; $condition = [];
switch ($type) { switch ($type) {
case 'active': case 'active':
$condition['account_removed'] = false; $condition['account_removed'] = false;
$condition['blocked'] = false; $condition['blocked'] = false;
break; break;
case 'blocked': case 'blocked':
$condition['account_removed'] = false; $condition['account_removed'] = false;
$condition['blocked'] = true; $condition['blocked'] = true;
$condition['verified'] = true; $condition['verified'] = true;
break; break;
case 'removed': case 'removed':

View file

@ -40,8 +40,8 @@ class Objects extends BaseModule
$this->logger->info('Provided GUID found.', ['guid' => $this->parameters['guid'], 'uri-id' => $itemuri['id']]); $this->logger->info('Provided GUID found.', ['guid' => $this->parameters['guid'], 'uri-id' => $itemuri['id']]);
} else { } else {
// The item URI does not always contain the GUID. This means that we have to search the URL instead // The item URI does not always contain the GUID. This means that we have to search the URL instead
$url = DI::baseUrl() . '/' . DI::args()->getQueryString(); $url = DI::baseUrl() . '/' . DI::args()->getQueryString();
$nurl = Strings::normaliseLink($url); $nurl = Strings::normaliseLink($url);
$ssl_url = str_replace('http://', 'https://', $nurl); $ssl_url = str_replace('http://', 'https://', $nurl);
$itemuri = DBA::selectFirst('item-uri', ['guid', 'id'], ['uri' => [$url, $nurl, $ssl_url]]); $itemuri = DBA::selectFirst('item-uri', ['guid', 'id'], ['uri' => [$url, $nurl, $ssl_url]]);
@ -63,7 +63,7 @@ class Objects extends BaseModule
if (!$validated) { if (!$validated) {
$requester = HTTPSignature::getSigner('', $_SERVER); $requester = HTTPSignature::getSigner('', $_SERVER);
if (!empty($requester)) { if (!empty($requester)) {
$receivers = Item::enumeratePermissions($item, false); $receivers = Item::enumeratePermissions($item, false);
$receivers[] = $item['contact-id']; $receivers[] = $item['contact-id'];
$validated = in_array(Contact::getIdForURL($requester, $item['uid']), $receivers); $validated = in_array(Contact::getIdForURL($requester, $item['uid']), $receivers);
@ -96,16 +96,18 @@ class Objects extends BaseModule
$data = ['@context' => ActivityPub::CONTEXT]; $data = ['@context' => ActivityPub::CONTEXT];
$data = array_merge($data, $activity['object']); $data = array_merge($data, $activity['object']);
} elseif (empty($this->parameters['activity']) || in_array($this->parameters['activity'], } elseif (empty($this->parameters['activity']) || in_array(
$this->parameters['activity'],
['Create', 'Announce', 'Update', 'Like', 'Dislike', 'Accept', 'Reject', ['Create', 'Announce', 'Update', 'Like', 'Dislike', 'Accept', 'Reject',
'TentativeAccept', 'Follow', 'Add'])) { 'TentativeAccept', 'Follow', 'Add']
)) {
$data = ActivityPub\Transmitter::createCachedActivityFromItem($item['id']); $data = ActivityPub\Transmitter::createCachedActivityFromItem($item['id']);
if (empty($data)) { if (empty($data)) {
throw new HTTPException\NotFoundException(); throw new HTTPException\NotFoundException();
} }
if (!empty($this->parameters['activity']) && ($this->parameters['activity'] != 'Create')) { if (!empty($this->parameters['activity']) && ($this->parameters['activity'] != 'Create')) {
$data['type'] = $this->parameters['activity']; $data['type'] = $this->parameters['activity'];
$data['id'] = str_replace('/Create', '/' . $this->parameters['activity'], $data['id']); $data['id'] = str_replace('/Create', '/' . $this->parameters['activity'], $data['id']);
} }
} else { } else {
throw new HTTPException\NotFoundException(); throw new HTTPException\NotFoundException();

View file

@ -60,14 +60,14 @@ class Statuses extends BaseApi
} elseif (!$uid) { } elseif (!$uid) {
$condition = [ $condition = [
'author-id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED], 'author-id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED],
'uid' => 0, 'network' => Protocol::FEDERATED 'uid' => 0, 'network' => Protocol::FEDERATED
]; ];
} else { } else {
$condition = ["`author-id` = ? AND (`uid` = 0 OR (`uid` = ? AND NOT `global`))", $id, $uid]; $condition = ["`author-id` = ? AND (`uid` = 0 OR (`uid` = ? AND NOT `global`))", $id, $uid];
} }
$condition = $this->addPagingConditions($request, $condition); $condition = $this->addPagingConditions($request, $condition);
$params = $this->buildOrderAndLimitParams($request); $params = $this->buildOrderAndLimitParams($request);
if (!$request['pinned'] && !$request['only_media']) { if (!$request['pinned'] && !$request['only_media']) {
if ($request['exclude_replies']) { if ($request['exclude_replies']) {
@ -98,7 +98,7 @@ class Statuses extends BaseApi
$statuses = []; $statuses = [];
while ($item = Post::fetch($items)) { while ($item = Post::fetch($items)) {
try { try {
$status = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes); $status = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes);
$this->updateBoundaries($status, $item, $request['friendica_order']); $this->updateBoundaries($status, $item, $request['friendica_order']);
$statuses[] = $status; $statuses[] = $status;
} catch (\Throwable $th) { } catch (\Throwable $th) {

View file

@ -35,15 +35,15 @@ class Directory extends BaseApi
$this->logger->info('directory', ['offset' => $request['offset'], 'limit' => $request['limit'], 'order' => $request['order'], 'local' => $request['local']]); $this->logger->info('directory', ['offset' => $request['offset'], 'limit' => $request['limit'], 'order' => $request['order'], 'local' => $request['local']]);
if ($request['local']) { if ($request['local']) {
$table = 'owner-view'; $table = 'owner-view';
$condition = ['net-publish' => true]; $condition = ['net-publish' => true];
} else { } else {
$table = 'contact'; $table = 'contact';
$condition = ['uid' => 0, 'hidden' => false, 'network' => Protocol::FEDERATED]; $condition = ['uid' => 0, 'hidden' => false, 'network' => Protocol::FEDERATED];
} }
$params = ['limit' => [$request['offset'], $request['limit']], $params = ['limit' => [$request['offset'], $request['limit']],
'order' => [($request['order'] == 'active') ? 'last-item' : 'created' => true]]; 'order' => [($request['order'] == 'active') ? 'last-item' : 'created' => true]];
$accounts = []; $accounts = [];
$contacts = DBA::select($table, ['id', 'uid'], $condition, $params); $contacts = DBA::select($table, ['id', 'uid'], $condition, $params);

View file

@ -57,7 +57,7 @@ class Search extends BaseApi
if (!is_array($result['accounts'])) { if (!is_array($result['accounts'])) {
// Curbing the search if we got an exact result // Curbing the search if we got an exact result
$request['type'] = 'accounts'; $request['type'] = 'accounts';
$result['accounts'] = [$result['accounts']]; $result['accounts'] = [$result['accounts']];
} }
} }
@ -67,7 +67,7 @@ class Search extends BaseApi
if (!is_array($result['statuses'])) { if (!is_array($result['statuses'])) {
// Curbing the search if we got an exact result // Curbing the search if we got an exact result
$request['type'] = 'statuses'; $request['type'] = 'statuses';
$result['statuses'] = [$result['statuses']]; $result['statuses'] = [$result['statuses']];
} }
} }
@ -143,9 +143,9 @@ class Search extends BaseApi
substr($q, 1), 0, $uid, Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, $uid, 0]; substr($q, 1), 0, $uid, Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, $uid, 0];
$table = 'tag-search-view'; $table = 'tag-search-view';
} else { } else {
$q = Post\Engagement::escapeKeywords($q); $q = Post\Engagement::escapeKeywords($q);
$condition = ["MATCH (`searchtext`) AGAINST (? IN BOOLEAN MODE) AND (NOT `restricted` OR `uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `uid` = ?))", $q, $uid]; $condition = ["MATCH (`searchtext`) AGAINST (? IN BOOLEAN MODE) AND (NOT `restricted` OR `uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `uid` = ?))", $q, $uid];
$table = SearchIndex::getSearchTable(); $table = SearchIndex::getSearchTable();
} }
if (!empty($account_id)) { if (!empty($account_id)) {

View file

@ -44,7 +44,7 @@ class Home extends BaseApi
$condition = ['gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT], 'uid' => $uid]; $condition = ['gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT], 'uid' => $uid];
$condition = $this->addPagingConditions($request, $condition); $condition = $this->addPagingConditions($request, $condition);
$params = $this->buildOrderAndLimitParams($request); $params = $this->buildOrderAndLimitParams($request);
if ($request['local']) { if ($request['local']) {
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin`)"]); $condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin`)"]);
@ -74,7 +74,7 @@ class Home extends BaseApi
$statuses = []; $statuses = [];
while ($item = Post::fetch($items)) { while ($item = Post::fetch($items)) {
try { try {
$status = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes); $status = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes);
$this->updateBoundaries($status, $item, $request['friendica_order']); $this->updateBoundaries($status, $item, $request['friendica_order']);
$statuses[] = $status; $statuses[] = $status;
} catch (\Throwable $th) { } catch (\Throwable $th) {

View file

@ -72,14 +72,14 @@ class ListTimeline extends BaseApi
$items = $this->getStatusesForGroup($uid, $request); $items = $this->getStatusesForGroup($uid, $request);
} elseif (substr($this->parameters['id'], 0, 8) == 'channel:') { } elseif (substr($this->parameters['id'], 0, 8) == 'channel:') {
$items = $this->getStatusesForChannel($uid, $request); $items = $this->getStatusesForChannel($uid, $request);
} else{ } else {
$items = $this->getStatusesForCircle($uid, $request); $items = $this->getStatusesForCircle($uid, $request);
} }
$statuses = []; $statuses = [];
foreach ($items as $item) { foreach ($items as $item) {
try { try {
$status = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes); $status = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes);
$this->updateBoundaries($status, $item, $request['friendica_order']); $this->updateBoundaries($status, $item, $request['friendica_order']);
$statuses[] = $status; $statuses[] = $status;
} catch (\Throwable $th) { } catch (\Throwable $th) {
@ -135,7 +135,7 @@ class ListTimeline extends BaseApi
]; ];
$condition = $this->addPagingConditions($request, $condition); $condition = $this->addPagingConditions($request, $condition);
$params = $this->buildOrderAndLimitParams($request); $params = $this->buildOrderAndLimitParams($request);
if ($request['only_media']) { if ($request['only_media']) {
$condition = DBA::mergeConditions($condition, [ $condition = DBA::mergeConditions($condition, [

View file

@ -74,7 +74,7 @@ class PublicTimeline extends BaseApi
]; ];
$condition = $this->addPagingConditions($request, $condition); $condition = $this->addPagingConditions($request, $condition);
$params = $this->buildOrderAndLimitParams($request); $params = $this->buildOrderAndLimitParams($request);
if ($request['local']) { if ($request['local']) {
$condition = DBA::mergeConditions($condition, ['origin' => true]); $condition = DBA::mergeConditions($condition, ['origin' => true]);
@ -108,7 +108,7 @@ class PublicTimeline extends BaseApi
$statuses = []; $statuses = [];
while ($item = Post::fetch($items)) { while ($item = Post::fetch($items)) {
try { try {
$status = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes); $status = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes);
$this->updateBoundaries($status, $item, $request['friendica_order']); $this->updateBoundaries($status, $item, $request['friendica_order']);
$statuses[] = $status; $statuses[] = $status;
} catch (\Throwable $th) { } catch (\Throwable $th) {

View file

@ -51,7 +51,7 @@ class Statuses extends BaseApi
$uid = self::getCurrentUserID(); $uid = self::getCurrentUserID();
$request = $this->getRequest([ $request = $this->getRequest([
'limit' => 10, // Maximum number of results to return. Defaults to 10. 'limit' => 10, // Maximum number of results to return. Defaults to 10.
'offset' => 0, // Offset in set, Defaults to 0. 'offset' => 0, // Offset in set, Defaults to 0.
], $request); ], $request);

View file

@ -287,8 +287,8 @@ class BaseApi extends BaseModule
$prev_request = $next_request = $request; $prev_request = $next_request = $request;
if ($asDate) { if ($asDate) {
$max_date = self::$boundaries['max']; $max_date = self::$boundaries['max'];
$min_date = self::$boundaries['min']; $min_date = self::$boundaries['min'];
$prev_request['min_id'] = $max_date->format(DateTimeFormat::JSON); $prev_request['min_id'] = $max_date->format(DateTimeFormat::JSON);
$next_request['max_id'] = $min_date->format(DateTimeFormat::JSON); $next_request['max_id'] = $min_date->format(DateTimeFormat::JSON);
} else { } else {
@ -432,48 +432,48 @@ class BaseApi extends BaseModule
// Check for throttling (maximum posts per day, week and month) // Check for throttling (maximum posts per day, week and month)
$throttle_day = DI::config()->get('system', 'throttle_limit_day'); $throttle_day = DI::config()->get('system', 'throttle_limit_day');
if ($throttle_day > 0) { if ($throttle_day > 0) {
$datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60); $datefrom = date(DateTimeFormat::MYSQL, time() - 24 * 60 * 60);
$condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", Item::GRAVITY_PARENT, $uid, $datefrom]; $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", Item::GRAVITY_PARENT, $uid, $datefrom];
$posts_day = Post::countThread($condition); $posts_day = Post::countThread($condition);
if ($posts_day > $throttle_day) { if ($posts_day > $throttle_day) {
$this->logger->notice('Daily posting limit reached', ['uid' => $uid, 'posts' => $posts_day, 'limit' => $throttle_day]); $this->logger->notice('Daily posting limit reached', ['uid' => $uid, 'posts' => $posts_day, 'limit' => $throttle_day]);
$error = $this->t('Too Many Requests'); $error = $this->t('Too Many Requests');
$error_description = $this->tt("Daily posting limit of %d post reached. The post was rejected.", "Daily posting limit of %d posts reached. The post was rejected.", $throttle_day); $error_description = $this->tt("Daily posting limit of %d post reached. The post was rejected.", "Daily posting limit of %d posts reached. The post was rejected.", $throttle_day);
$errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description); $errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
$this->jsonError(429, $errorobj->toArray()); $this->jsonError(429, $errorobj->toArray());
} }
} }
$throttle_week = DI::config()->get('system', 'throttle_limit_week'); $throttle_week = DI::config()->get('system', 'throttle_limit_week');
if ($throttle_week > 0) { if ($throttle_week > 0) {
$datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*7); $datefrom = date(DateTimeFormat::MYSQL, time() - 24 * 60 * 60 * 7);
$condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", Item::GRAVITY_PARENT, $uid, $datefrom]; $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", Item::GRAVITY_PARENT, $uid, $datefrom];
$posts_week = Post::countThread($condition); $posts_week = Post::countThread($condition);
if ($posts_week > $throttle_week) { if ($posts_week > $throttle_week) {
$this->logger->notice('Weekly posting limit reached', ['uid' => $uid, 'posts' => $posts_week, 'limit' => $throttle_week]); $this->logger->notice('Weekly posting limit reached', ['uid' => $uid, 'posts' => $posts_week, 'limit' => $throttle_week]);
$error = $this->t('Too Many Requests'); $error = $this->t('Too Many Requests');
$error_description = $this->tt("Weekly posting limit of %d post reached. The post was rejected.", "Weekly posting limit of %d posts reached. The post was rejected.", $throttle_week); $error_description = $this->tt("Weekly posting limit of %d post reached. The post was rejected.", "Weekly posting limit of %d posts reached. The post was rejected.", $throttle_week);
$errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description); $errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
$this->jsonError(429, $errorobj->toArray()); $this->jsonError(429, $errorobj->toArray());
} }
} }
$throttle_month = DI::config()->get('system', 'throttle_limit_month'); $throttle_month = DI::config()->get('system', 'throttle_limit_month');
if ($throttle_month > 0) { if ($throttle_month > 0) {
$datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*30); $datefrom = date(DateTimeFormat::MYSQL, time() - 24 * 60 * 60 * 30);
$condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", Item::GRAVITY_PARENT, $uid, $datefrom]; $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", Item::GRAVITY_PARENT, $uid, $datefrom];
$posts_month = Post::countThread($condition); $posts_month = Post::countThread($condition);
if ($posts_month > $throttle_month) { if ($posts_month > $throttle_month) {
$this->logger->notice('Monthly posting limit reached', ['uid' => $uid, 'posts' => $posts_month, 'limit' => $throttle_month]); $this->logger->notice('Monthly posting limit reached', ['uid' => $uid, 'posts' => $posts_month, 'limit' => $throttle_month]);
$error = $this->t('Too Many Requests'); $error = $this->t('Too Many Requests');
$error_description = $this->tt('Monthly posting limit of %d post reached. The post was rejected.', 'Monthly posting limit of %d posts reached. The post was rejected.', $throttle_month); $error_description = $this->tt('Monthly posting limit of %d post reached. The post was rejected.', 'Monthly posting limit of %d posts reached. The post was rejected.', $throttle_month);
$errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description); $errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
$this->jsonError(429, $errorobj->toArray()); $this->jsonError(429, $errorobj->toArray());
} }
} }

View file

@ -47,13 +47,13 @@ class BaseSearch extends BaseModule
return ''; return '';
} }
$header = ''; $header = '';
$results = new ResultList(); $results = new ResultList();
if (strpos($search, '@') === 0) { if (strpos($search, '@') === 0) {
$search = trim(substr($search, 1)); $search = trim(substr($search, 1));
$type = Search::TYPE_PEOPLE; $type = Search::TYPE_PEOPLE;
$header = DI::l10n()->t('People Search - %s', $search); $header = DI::l10n()->t('People Search - %s', $search);
} elseif (strpos($search, '!') === 0) { } elseif (strpos($search, '!') === 0) {
$search = trim(substr($search, 1)); $search = trim(substr($search, 1));
$type = Search::TYPE_GROUP; $type = Search::TYPE_GROUP;
@ -63,11 +63,19 @@ class BaseSearch extends BaseModule
$search = Network::convertToIdn($search); $search = Network::convertToIdn($search);
if (DI::mode()->isMobile()) { if (DI::mode()->isMobile()) {
$itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_mobile_network', $itemsPerPage = DI::pConfig()->get(
DI::config()->get('system', 'itemspage_network_mobile')); DI::userSession()->getLocalUserId(),
'system',
'itemspage_mobile_network',
DI::config()->get('system', 'itemspage_network_mobile')
);
} else { } else {
$itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_network', $itemsPerPage = DI::pConfig()->get(
DI::config()->get('system', 'itemspage_network')); DI::userSession()->getLocalUserId(),
'system',
'itemspage_network',
DI::config()->get('system', 'itemspage_network')
);
} }
$pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage); $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage);
@ -131,7 +139,8 @@ class BaseSearch extends BaseModule
'$filtered' => $filtered ? DI::l10n()->tt( '$filtered' => $filtered ? DI::l10n()->tt(
'%d result was filtered out because your node blocks the domain it is registered on. You can review the list of domains your node is currently blocking in the <a href="/friendica">About page</a>.', '%d result was filtered out because your node blocks the domain it is registered on. You can review the list of domains your node is currently blocking in the <a href="/friendica">About page</a>.',
'%d results were filtered out because your node blocks the domain they are registered on. You can review the list of domains your node is currently blocking in the <a href="/friendica">About page</a>.', '%d results were filtered out because your node blocks the domain they are registered on. You can review the list of domains your node is currently blocking in the <a href="/friendica">About page</a>.',
$filtered) : '', $filtered
) : '',
'$contacts' => $entries, '$contacts' => $entries,
'$paginate' => $pager->renderFull($results->getTotal()), '$paginate' => $pager->renderFull($results->getTotal()),
]); ]);

View file

@ -31,11 +31,11 @@ use Friendica\Worker\UpdateContact;
class Contact extends BaseModule class Contact extends BaseModule
{ {
const TAB_CONVERSATIONS = 1; const TAB_CONVERSATIONS = 1;
const TAB_POSTS = 2; const TAB_POSTS = 2;
const TAB_PROFILE = 3; const TAB_PROFILE = 3;
const TAB_CONTACTS = 4; const TAB_CONTACTS = 4;
const TAB_ADVANCED = 5; const TAB_ADVANCED = 5;
const TAB_MEDIA = 6; const TAB_MEDIA = 6;
private static function batchActions() private static function batchActions()
{ {
@ -168,11 +168,11 @@ class Contact extends BaseModule
} }
$search = trim($_GET['search'] ?? ''); $search = trim($_GET['search'] ?? '');
$nets = trim($_GET['nets'] ?? ''); $nets = trim($_GET['nets'] ?? '');
$rel = trim($_GET['rel'] ?? ''); $rel = trim($_GET['rel'] ?? '');
$circle = trim($_GET['circle'] ?? ''); $circle = trim($_GET['circle'] ?? '');
$accounttype = $_GET['accounttype'] ?? ''; $accounttype = $_GET['accounttype'] ?? '';
$accounttypeid = User::getAccountTypeByString($accounttype); $accounttypeid = User::getAccountTypeByString($accounttype);
$page = DI::page(); $page = DI::page();
@ -182,7 +182,7 @@ class Contact extends BaseModule
$page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css')); $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
$page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css')); $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
$vcard_widget = ''; $vcard_widget = '';
$findpeople_widget = Widget::findPeople(); $findpeople_widget = Widget::findPeople();
if (isset($_GET['add'])) { if (isset($_GET['add'])) {
$follow_widget = Widget::follow($_GET['add']); $follow_widget = Widget::follow($_GET['add']);
@ -248,10 +248,10 @@ class Contact extends BaseModule
$sql_values[] = $accounttypeid; $sql_values[] = $accounttypeid;
} }
$searching = false; $searching = false;
$search_hdr = null; $search_hdr = null;
if ($search) { if ($search) {
$searching = true; $searching = true;
$search_hdr = $search; $search_hdr = $search;
$search_txt = preg_quote(trim($search, ' @!')); $search_txt = preg_quote(trim($search, ' @!'));
$sql_extra .= " AND (`name` REGEXP ? OR `url` REGEXP ? OR `nick` REGEXP ? OR `addr` REGEXP ? OR `alias` REGEXP ?)"; $sql_extra .= " AND (`name` REGEXP ? OR `url` REGEXP ? OR `nick` REGEXP ? OR `addr` REGEXP ? OR `alias` REGEXP ?)";
@ -313,80 +313,80 @@ class Contact extends BaseModule
$stmt = DBA::select('contact', [], $condition, ['order' => ['name'], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]]); $stmt = DBA::select('contact', [], $condition, ['order' => ['name'], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]]);
while ($contact = DBA::fetch($stmt)) { while ($contact = DBA::fetch($stmt)) {
$contact['blocked'] = Model\Contact\User::isBlocked($contact['id'], DI::userSession()->getLocalUserId()); $contact['blocked'] = Model\Contact\User::isBlocked($contact['id'], DI::userSession()->getLocalUserId());
$contact['readonly'] = Model\Contact\User::isIgnored($contact['id'], DI::userSession()->getLocalUserId()); $contact['readonly'] = Model\Contact\User::isIgnored($contact['id'], DI::userSession()->getLocalUserId());
$contacts[] = self::getContactTemplateVars($contact); $contacts[] = self::getContactTemplateVars($contact);
} }
DBA::close($stmt); DBA::close($stmt);
$tabs = [ $tabs = [
[ [
'label' => DI::l10n()->t('All Contacts'), 'label' => DI::l10n()->t('All Contacts'),
'url' => 'contact', 'url' => 'contact',
'sel' => !$type ? 'active' : '', 'sel' => !$type ? 'active' : '',
'title' => DI::l10n()->t('Show all contacts'), 'title' => DI::l10n()->t('Show all contacts'),
'id' => 'showall-tab', 'id' => 'showall-tab',
'accesskey' => 'l', 'accesskey' => 'l',
], ],
[ [
'label' => DI::l10n()->t('Pending'), 'label' => DI::l10n()->t('Pending'),
'url' => 'contact/pending', 'url' => 'contact/pending',
'sel' => $type == 'pending' ? 'active' : '', 'sel' => $type == 'pending' ? 'active' : '',
'title' => DI::l10n()->t('Only show pending contacts'), 'title' => DI::l10n()->t('Only show pending contacts'),
'id' => 'showpending-tab', 'id' => 'showpending-tab',
'accesskey' => 'p', 'accesskey' => 'p',
], ],
[ [
'label' => DI::l10n()->t('Blocked'), 'label' => DI::l10n()->t('Blocked'),
'url' => 'contact/blocked', 'url' => 'contact/blocked',
'sel' => $type == 'blocked' ? 'active' : '', 'sel' => $type == 'blocked' ? 'active' : '',
'title' => DI::l10n()->t('Only show blocked contacts'), 'title' => DI::l10n()->t('Only show blocked contacts'),
'id' => 'showblocked-tab', 'id' => 'showblocked-tab',
'accesskey' => 'b', 'accesskey' => 'b',
], ],
[ [
'label' => DI::l10n()->t('Ignored'), 'label' => DI::l10n()->t('Ignored'),
'url' => 'contact/ignored', 'url' => 'contact/ignored',
'sel' => $type == 'ignored' ? 'active' : '', 'sel' => $type == 'ignored' ? 'active' : '',
'title' => DI::l10n()->t('Only show ignored contacts'), 'title' => DI::l10n()->t('Only show ignored contacts'),
'id' => 'showignored-tab', 'id' => 'showignored-tab',
'accesskey' => 'i', 'accesskey' => 'i',
], ],
[ [
'label' => DI::l10n()->t('Collapsed'), 'label' => DI::l10n()->t('Collapsed'),
'url' => 'contact/collapsed', 'url' => 'contact/collapsed',
'sel' => $type == 'collapsed' ? 'active' : '', 'sel' => $type == 'collapsed' ? 'active' : '',
'title' => DI::l10n()->t('Only show collapsed contacts'), 'title' => DI::l10n()->t('Only show collapsed contacts'),
'id' => 'showcollapsed-tab', 'id' => 'showcollapsed-tab',
'accesskey' => 'c', 'accesskey' => 'c',
], ],
[ [
'label' => DI::l10n()->t('Archived'), 'label' => DI::l10n()->t('Archived'),
'url' => 'contact/archived', 'url' => 'contact/archived',
'sel' => $type == 'archived' ? 'active' : '', 'sel' => $type == 'archived' ? 'active' : '',
'title' => DI::l10n()->t('Only show archived contacts'), 'title' => DI::l10n()->t('Only show archived contacts'),
'id' => 'showarchived-tab', 'id' => 'showarchived-tab',
'accesskey' => 'y', 'accesskey' => 'y',
], ],
[ [
'label' => DI::l10n()->t('Hidden'), 'label' => DI::l10n()->t('Hidden'),
'url' => 'contact/hidden', 'url' => 'contact/hidden',
'sel' => $type == 'hidden' ? 'active' : '', 'sel' => $type == 'hidden' ? 'active' : '',
'title' => DI::l10n()->t('Only show hidden contacts'), 'title' => DI::l10n()->t('Only show hidden contacts'),
'id' => 'showhidden-tab', 'id' => 'showhidden-tab',
'accesskey' => 'h', 'accesskey' => 'h',
], ],
[ [
'label' => DI::l10n()->t('Circles'), 'label' => DI::l10n()->t('Circles'),
'url' => 'circle', 'url' => 'circle',
'sel' => '', 'sel' => '',
'title' => DI::l10n()->t('Organize your contact circles'), 'title' => DI::l10n()->t('Organize your contact circles'),
'id' => 'contactcircles-tab', 'id' => 'contactcircles-tab',
'accesskey' => 'e', 'accesskey' => 'e',
], ],
]; ];
$tabs_tpl = Renderer::getMarkupTemplate('common_tabs.tpl'); $tabs_tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
$tabs_html = Renderer::replaceMacros($tabs_tpl, ['$tabs' => $tabs]); $tabs_html = Renderer::replaceMacros($tabs_tpl, ['$tabs' => $tabs]);
switch ($rel) { switch ($rel) {
@ -431,26 +431,26 @@ class Contact extends BaseModule
$tpl = Renderer::getMarkupTemplate('contacts-template.tpl'); $tpl = Renderer::getMarkupTemplate('contacts-template.tpl');
$o .= Renderer::replaceMacros($tpl, [ $o .= Renderer::replaceMacros($tpl, [
'$header' => $header, '$header' => $header,
'$tabs' => $tabs_html, '$tabs' => $tabs_html,
'$total' => $total, '$total' => $total,
'$search' => $search_hdr, '$search' => $search_hdr,
'$desc' => DI::l10n()->t('Search your contacts'), '$desc' => DI::l10n()->t('Search your contacts'),
'$finding' => $searching ? DI::l10n()->t('Results for: %s', $search) : '', '$finding' => $searching ? DI::l10n()->t('Results for: %s', $search) : '',
'$submit' => DI::l10n()->t('Find'), '$submit' => DI::l10n()->t('Find'),
'$cmd' => DI::args()->getCommand(), '$cmd' => DI::args()->getCommand(),
'$parameter' => http_build_query($request), '$parameter' => http_build_query($request),
'$contacts' => $contacts, '$contacts' => $contacts,
'$form_security_token' => BaseModule::getFormSecurityToken('contact_batch_actions'), '$form_security_token' => BaseModule::getFormSecurityToken('contact_batch_actions'),
'multiselect' => 1, 'multiselect' => 1,
'$batch_actions' => [ '$batch_actions' => [
'contacts_batch_update' => DI::l10n()->t('Update'), 'contacts_batch_update' => DI::l10n()->t('Update'),
'contacts_batch_block' => DI::l10n()->t('Block') . '/' . DI::l10n()->t('Unblock'), 'contacts_batch_block' => DI::l10n()->t('Block') . '/' . DI::l10n()->t('Unblock'),
'contacts_batch_ignore' => DI::l10n()->t('Ignore') . '/' . DI::l10n()->t('Unignore'), 'contacts_batch_ignore' => DI::l10n()->t('Ignore') . '/' . DI::l10n()->t('Unignore'),
'contacts_batch_collapse' => DI::l10n()->t('Collapse') . '/' . DI::l10n()->t('Uncollapse'), 'contacts_batch_collapse' => DI::l10n()->t('Collapse') . '/' . DI::l10n()->t('Uncollapse'),
], ],
'$h_batch_actions' => DI::l10n()->t('Batch Actions'), '$h_batch_actions' => DI::l10n()->t('Batch Actions'),
'$paginate' => $pager->renderFull($total), '$paginate' => $pager->renderFull($total),
]); ]);
return $o; return $o;
@ -470,7 +470,7 @@ class Contact extends BaseModule
*/ */
public static function getTabsHTML(array $contact, int $active_tab) public static function getTabsHTML(array $contact, int $active_tab)
{ {
$cid = $pcid = $contact['id']; $cid = $pcid = $contact['id'];
$data = Model\Contact::getPublicAndUserContactID($contact['id'], DI::userSession()->getLocalUserId()); $data = Model\Contact::getPublicAndUserContactID($contact['id'], DI::userSession()->getLocalUserId());
if (!empty($data['user']) && ($contact['id'] == $data['public'])) { if (!empty($data['user']) && ($contact['id'] == $data['public'])) {
$cid = $data['user']; $cid = $data['user'];
@ -481,54 +481,54 @@ class Contact extends BaseModule
// tabs // tabs
$tabs = [ $tabs = [
[ [
'label' => DI::l10n()->t('Profile'), 'label' => DI::l10n()->t('Profile'),
'url' => 'contact/' . $cid, 'url' => 'contact/' . $cid,
'sel' => (($active_tab == self::TAB_PROFILE) ? 'active' : ''), 'sel' => (($active_tab == self::TAB_PROFILE) ? 'active' : ''),
'title' => DI::l10n()->t('Profile Details'), 'title' => DI::l10n()->t('Profile Details'),
'id' => 'profile-tab', 'id' => 'profile-tab',
'accesskey' => 'o', 'accesskey' => 'o',
], ],
[ [
'label' => DI::l10n()->t('Conversations'), 'label' => DI::l10n()->t('Conversations'),
'url' => 'contact/' . $pcid . '/conversations', 'url' => 'contact/' . $pcid . '/conversations',
'sel' => (($active_tab == self::TAB_CONVERSATIONS) ? 'active' : ''), 'sel' => (($active_tab == self::TAB_CONVERSATIONS) ? 'active' : ''),
'title' => DI::l10n()->t('Conversations started by this contact'), 'title' => DI::l10n()->t('Conversations started by this contact'),
'id' => 'status-tab', 'id' => 'status-tab',
'accesskey' => 'm', 'accesskey' => 'm',
], ],
[ [
'label' => DI::l10n()->t('Posts and Comments'), 'label' => DI::l10n()->t('Posts and Comments'),
'url' => 'contact/' . $pcid . '/posts', 'url' => 'contact/' . $pcid . '/posts',
'sel' => (($active_tab == self::TAB_POSTS) ? 'active' : ''), 'sel' => (($active_tab == self::TAB_POSTS) ? 'active' : ''),
'title' => DI::l10n()->t('Individual Posts and Replies'), 'title' => DI::l10n()->t('Individual Posts and Replies'),
'id' => 'posts-tab', 'id' => 'posts-tab',
'accesskey' => 'p', 'accesskey' => 'p',
], ],
[ [
'label' => DI::l10n()->t('Media'), 'label' => DI::l10n()->t('Media'),
'url' => 'contact/' . $pcid . '/media', 'url' => 'contact/' . $pcid . '/media',
'sel' => (($active_tab == self::TAB_MEDIA) ? 'active' : ''), 'sel' => (($active_tab == self::TAB_MEDIA) ? 'active' : ''),
'title' => DI::l10n()->t('Posts containing media objects'), 'title' => DI::l10n()->t('Posts containing media objects'),
'id' => 'media-tab', 'id' => 'media-tab',
'accesskey' => 'd', 'accesskey' => 'd',
], ],
[ [
'label' => DI::l10n()->t('Contacts'), 'label' => DI::l10n()->t('Contacts'),
'url' => 'contact/' . $pcid . '/contacts', 'url' => 'contact/' . $pcid . '/contacts',
'sel' => (($active_tab == self::TAB_CONTACTS) ? 'active' : ''), 'sel' => (($active_tab == self::TAB_CONTACTS) ? 'active' : ''),
'title' => DI::l10n()->t('View all known contacts'), 'title' => DI::l10n()->t('View all known contacts'),
'id' => 'contacts-tab', 'id' => 'contacts-tab',
'accesskey' => 't' 'accesskey' => 't'
], ],
]; ];
if (!empty($contact['network']) && in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) && ($cid != $pcid)) { if (!empty($contact['network']) && in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) && ($cid != $pcid)) {
$tabs[] = [ $tabs[] = [
'label' => DI::l10n()->t('Advanced'), 'label' => DI::l10n()->t('Advanced'),
'url' => 'contact/' . $cid . '/advanced/', 'url' => 'contact/' . $cid . '/advanced/',
'sel' => (($active_tab == self::TAB_ADVANCED) ? 'active' : ''), 'sel' => (($active_tab == self::TAB_ADVANCED) ? 'active' : ''),
'title' => DI::l10n()->t('Advanced Contact Settings'), 'title' => DI::l10n()->t('Advanced Contact Settings'),
'id' => 'advanced-tab', 'id' => 'advanced-tab',
'accesskey' => 'r' 'accesskey' => 'r'
]; ];
} }
@ -566,11 +566,11 @@ class Contact extends BaseModule
$alt_text = DI::l10n()->t('Mutual Friendship'); $alt_text = DI::l10n()->t('Mutual Friendship');
break; break;
case Model\Contact::FOLLOWER; case Model\Contact::FOLLOWER:
$alt_text = DI::l10n()->t('is a fan of yours'); $alt_text = DI::l10n()->t('is a fan of yours');
break; break;
case Model\Contact::SHARING; case Model\Contact::SHARING:
$alt_text = DI::l10n()->t('you are a fan of'); $alt_text = DI::l10n()->t('you are a fan of');
break; break;

View file

@ -57,7 +57,8 @@ class Token extends BaseApi
if (empty($request['client_id']) || empty($request['client_secret'])) { if (empty($request['client_id']) || empty($request['client_secret'])) {
$this->logger->warning('Incomplete request data', ['request' => $request]); $this->logger->warning('Incomplete request data', ['request' => $request]);
$this->logAndJsonError(401, $this->errorFactory->Unauthorized('invalid_client', $this->t('Incomplete request data')));; $this->logAndJsonError(401, $this->errorFactory->Unauthorized('invalid_client', $this->t('Incomplete request data')));
;
} }
$application = OAuth::getApplication($request['client_id'], $request['client_secret'], $request['redirect_uri']); $application = OAuth::getApplication($request['client_id'], $request['client_secret'], $request['redirect_uri']);
@ -90,8 +91,8 @@ class Token extends BaseApi
// now check for $grant_type === 'authorization_code' // now check for $grant_type === 'authorization_code'
// For security reasons only allow freshly created tokens // For security reasons only allow freshly created tokens
$redirect_uri = strtok($request['redirect_uri'],'?'); $redirect_uri = strtok($request['redirect_uri'], '?');
$condition = [ $condition = [
"`redirect_uri` LIKE ? AND `id` = ? AND `code` = ? AND `created_at` > ?", "`redirect_uri` LIKE ? AND `id` = ? AND `code` = ? AND `created_at` > ?",
$redirect_uri, $application['id'], $request['code'], DateTimeFormat::utc('now - 5 minutes') $redirect_uri, $application['id'], $request['code'], DateTimeFormat::utc('now - 5 minutes')
]; ];

View file

@ -62,7 +62,7 @@ class Owa extends BaseModule
$this->logger->debug('OWA header', ['addr' => $contact['addr'], 'data' => $verified]); $this->logger->debug('OWA header', ['addr' => $contact['addr'], 'data' => $verified]);
$ret['success'] = true; $ret['success'] = true;
$token = Strings::getRandomHex(32); $token = Strings::getRandomHex(32);
// Store the generated token in the database. // Store the generated token in the database.
OpenWebAuthToken::create('owt', 0, $token, $contact['addr']); OpenWebAuthToken::create('owt', 0, $token, $contact['addr']);

View file

@ -63,19 +63,19 @@ class Photo extends BaseApi
OpenWebAuth::addVisitorCookieForHTTPSigner($this->server); OpenWebAuth::addVisitorCookieForHTTPSigner($this->server);
$customsize = 0; $customsize = 0;
$square_resize = true; $square_resize = true;
$scale = null; $scale = null;
$stamp = microtime(true); $stamp = microtime(true);
// User avatar // User avatar
if (!empty($this->parameters['type'])) { if (!empty($this->parameters['type'])) {
if (!empty($this->parameters['customsize'])) { if (!empty($this->parameters['customsize'])) {
$customsize = intval($this->parameters['customsize']); $customsize = intval($this->parameters['customsize']);
$square_resize = !in_array($this->parameters['type'], ['media', 'preview']); $square_resize = !in_array($this->parameters['type'], ['media', 'preview']);
} }
if (!empty($this->parameters['guid'])) { if (!empty($this->parameters['guid'])) {
$guid = $this->parameters['guid']; $guid = $this->parameters['guid'];
$account = DBA::selectFirst('account-user-view', ['id'], ['guid' => $guid], ['order' => ['uid' => true]]); $account = DBA::selectFirst('account-user-view', ['id'], ['guid' => $guid], ['order' => ['uid' => true]]);
if (empty($account)) { if (empty($account)) {
throw new HTTPException\NotFoundException(); throw new HTTPException\NotFoundException();
@ -90,7 +90,7 @@ class Photo extends BaseApi
if (!empty($this->parameters['nickname_ext'])) { if (!empty($this->parameters['nickname_ext'])) {
$nickname = pathinfo($this->parameters['nickname_ext'], PATHINFO_FILENAME); $nickname = pathinfo($this->parameters['nickname_ext'], PATHINFO_FILENAME);
$user = User::getByNickname($nickname, ['uid']); $user = User::getByNickname($nickname, ['uid']);
if (empty($user)) { if (empty($user)) {
throw new HTTPException\NotFoundException(); throw new HTTPException\NotFoundException();
} }
@ -110,9 +110,9 @@ class Photo extends BaseApi
$photo = $this->getPhotoById($id, $this->parameters['type'], $customsize ?: Proxy::PIXEL_SMALL); $photo = $this->getPhotoById($id, $this->parameters['type'], $customsize ?: Proxy::PIXEL_SMALL);
} else { } else {
$photoid = pathinfo($this->parameters['name'], PATHINFO_FILENAME); $photoid = pathinfo($this->parameters['name'], PATHINFO_FILENAME);
$scale = 0; $scale = 0;
if (substr($photoid, -2, 1) == '-') { if (substr($photoid, -2, 1) == '-') {
$scale = intval(substr($photoid, -1, 1)); $scale = intval(substr($photoid, -1, 1));
$photoid = substr($photoid, 0, -2); $photoid = substr($photoid, 0, -2);
} }
@ -174,7 +174,7 @@ class Photo extends BaseApi
$this->logger->warning('Invalid photo', ['id' => $photo['id']]); $this->logger->warning('Invalid photo', ['id' => $photo['id']]);
if (in_array($photo['backend-class'], [ExternalResource::NAME])) { if (in_array($photo['backend-class'], [ExternalResource::NAME])) {
$reference = json_decode($photo['backend-ref'], true); $reference = json_decode($photo['backend-ref'], true);
$error = DI::l10n()->t('Invalid external resource with url %s.', $reference['url']); $error = DI::l10n()->t('Invalid external resource with url %s.', $reference['url']);
} else { } else {
$error = DI::l10n()->t('Invalid photo with id %s.', $photo['id']); $error = DI::l10n()->t('Invalid photo with id %s.', $photo['id']);
} }
@ -228,13 +228,13 @@ class Photo extends BaseApi
$output = microtime(true) - $stamp; $output = microtime(true) - $stamp;
$total = microtime(true) - $totalstamp; $total = microtime(true) - $totalstamp;
$rest = $total - ($fetch + $data + $checksum + $output); $rest = $total - ($fetch + $data + $checksum + $output);
if (!is_null($scale) && ($scale < 4)) { if (!is_null($scale) && ($scale < 4)) {
$this->logger->debug('Performance:', [ $this->logger->debug('Performance:', [
'scale' => $scale, 'resource' => $photo['resource-id'], 'scale' => $scale, 'resource' => $photo['resource-id'],
'total' => number_format($total, 3), 'fetch' => number_format($fetch, 3), 'total' => number_format($total, 3), 'fetch' => number_format($fetch, 3),
'data' => number_format($data, 3), 'checksum' => number_format($checksum, 3), 'data' => number_format($data, 3), 'checksum' => number_format($checksum, 3),
'output' => number_format($output, 3), 'rest' => number_format($rest, 3) 'output' => number_format($output, 3), 'rest' => number_format($rest, 3)
]); ]);
} }
@ -296,7 +296,7 @@ class Photo extends BaseApi
return MPhoto::createPhotoForExternalResource($link['url'], (int)DI::userSession()->getLocalUserId(), $link['mimetype'] ?? '', $link['blurhash'] ?? '', $link['width'] ?? 0, $link['height'] ?? 0); return MPhoto::createPhotoForExternalResource($link['url'], (int)DI::userSession()->getLocalUserId(), $link['mimetype'] ?? '', $link['blurhash'] ?? '', $link['width'] ?? 0, $link['height'] ?? 0);
case 'contact': case 'contact':
$fields = ['uid', 'uri-id', 'url', 'nurl', 'avatar', 'photo', 'blurhash', 'xmpp', 'addr', 'network', 'failed', 'updated']; $fields = ['uid', 'uri-id', 'url', 'nurl', 'avatar', 'photo', 'blurhash', 'xmpp', 'addr', 'network', 'failed', 'updated'];
$contact = Contact::getById($id, $fields); $contact = Contact::getById($id, $fields);
if (empty($contact)) { if (empty($contact)) {
return false; return false;
@ -357,7 +357,7 @@ class Photo extends BaseApi
&& ((time() - strtotime($contact['updated']) > 86400)); && ((time() - strtotime($contact['updated']) > 86400));
if ($update) { if ($update) {
$curlResult = DI::httpClient()->head($url, [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::IMAGE, HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]); $curlResult = DI::httpClient()->head($url, [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::IMAGE, HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]);
$update = !$curlResult->isSuccess() && ($curlResult->getReturnCode() == 404); $update = !$curlResult->isSuccess() && ($curlResult->getReturnCode() == 404);
$this->logger->debug('Got return code for avatar', ['return code' => $curlResult->getReturnCode(), 'cid' => $id, 'url' => $contact['url'], 'avatar' => $url]); $this->logger->debug('Got return code for avatar', ['return code' => $curlResult->getReturnCode(), 'cid' => $id, 'url' => $contact['url'], 'avatar' => $url]);
} }
if ($update) { if ($update) {
@ -400,7 +400,7 @@ class Photo extends BaseApi
} }
return MPhoto::createPhotoForExternalResource($url, 0, $mimetext, $contact['blurhash'] ?? null, $customsize, $customsize); return MPhoto::createPhotoForExternalResource($url, 0, $mimetext, $contact['blurhash'] ?? null, $customsize, $customsize);
case 'header': case 'header':
$fields = ['uid', 'url', 'header', 'network', 'gsid']; $fields = ['uid', 'url', 'header', 'network', 'gsid'];
$contact = Contact::getById($id, $fields); $contact = Contact::getById($id, $fields);
if (empty($contact)) { if (empty($contact)) {
return false; return false;

View file

@ -114,7 +114,7 @@ class Register extends BaseModule
if (DI::config()->get('system', 'publish_all')) { if (DI::config()->get('system', 'publish_all')) {
$profile_publish = '<input type="hidden" name="profile_publish_reg" value="1" />'; $profile_publish = '<input type="hidden" name="profile_publish_reg" value="1" />';
} else { } else {
$publish_tpl = Renderer::getMarkupTemplate('profile/publish.tpl'); $publish_tpl = Renderer::getMarkupTemplate('profile/publish.tpl');
$profile_publish = Renderer::replaceMacros($publish_tpl, [ $profile_publish = Renderer::replaceMacros($publish_tpl, [
'$instance' => 'reg', '$instance' => 'reg',
'$pubdesc' => DI::l10n()->t('Include your profile in member directory?'), '$pubdesc' => DI::l10n()->t('Include your profile in member directory?'),
@ -136,44 +136,44 @@ class Register extends BaseModule
$tpl = $arr['template']; $tpl = $arr['template'];
$o = Renderer::replaceMacros($tpl, [ $o = Renderer::replaceMacros($tpl, [
'$invitations' => DI::config()->get('system', 'invitation_only'), '$invitations' => DI::config()->get('system', 'invitation_only'),
'$permonly' => self::getPolicy() === self::APPROVE, '$permonly' => self::getPolicy() === self::APPROVE,
'$permonlybox' => ['permonlybox', DI::l10n()->t('Note for the admin'), '', DI::l10n()->t('Leave a message for the admin, why you want to join this node'), DI::l10n()->t('Required')], '$permonlybox' => ['permonlybox', DI::l10n()->t('Note for the admin'), '', DI::l10n()->t('Leave a message for the admin, why you want to join this node'), DI::l10n()->t('Required')],
'$invite_desc' => DI::l10n()->t('Membership on this site is by invitation only.'), '$invite_desc' => DI::l10n()->t('Membership on this site is by invitation only.'),
'$invite_label' => DI::l10n()->t('Your invitation code: '), '$invite_label' => DI::l10n()->t('Your invitation code: '),
'$invite_id' => $invite_id, '$invite_id' => $invite_id,
'$regtitle' => DI::l10n()->t('Registration'), '$regtitle' => DI::l10n()->t('Registration'),
'$registertext' => BBCode::convertForUriId(User::getSystemUriId(), DI::config()->get('config', 'register_text', '')), '$registertext' => BBCode::convertForUriId(User::getSystemUriId(), DI::config()->get('config', 'register_text', '')),
'$fillwith' => $fillwith, '$fillwith' => $fillwith,
'$fillext' => $fillext, '$fillext' => $fillext,
'$oidlabel' => $oidlabel, '$oidlabel' => $oidlabel,
'$openid' => $openid_url, '$openid' => $openid_url,
'$namelabel' => DI::l10n()->t('Your Display Name (as you would like it to be displayed on this system'), '$namelabel' => DI::l10n()->t('Your Display Name (as you would like it to be displayed on this system'),
'$addrlabel' => DI::l10n()->t('Your Email Address: (Initial information will be send there, so this has to be an existing address.)'), '$addrlabel' => DI::l10n()->t('Your Email Address: (Initial information will be send there, so this has to be an existing address.)'),
'$addrlabel2' => DI::l10n()->t('Please repeat your e-mail address:'), '$addrlabel2' => DI::l10n()->t('Please repeat your e-mail address:'),
'$ask_password' => $ask_password, '$ask_password' => $ask_password,
'$password1' => ['password1', DI::l10n()->t('New Password:'), '', DI::l10n()->t('Leave empty for an auto generated password.')], '$password1' => ['password1', DI::l10n()->t('New Password:'), '', DI::l10n()->t('Leave empty for an auto generated password.')],
'$password2' => ['confirm', DI::l10n()->t('Confirm:'), '', ''], '$password2' => ['confirm', DI::l10n()->t('Confirm:'), '', ''],
'$nickdesc' => DI::l10n()->t('Choose a profile nickname. This must begin with a text character. Your profile address on this site will then be "<strong>nickname@%s</strong>".', DI::baseUrl()->getHost()), '$nickdesc' => DI::l10n()->t('Choose a profile nickname. This must begin with a text character. Your profile address on this site will then be "<strong>nickname@%s</strong>".', DI::baseUrl()->getHost()),
'$nicklabel' => DI::l10n()->t('Choose a nickname: '), '$nicklabel' => DI::l10n()->t('Choose a nickname: '),
'$photo' => $photo, '$photo' => $photo,
'$publish' => $profile_publish, '$publish' => $profile_publish,
'$regbutt' => DI::l10n()->t('Register'), '$regbutt' => DI::l10n()->t('Register'),
'$username' => $username, '$username' => $username,
'$email' => $email, '$email' => $email,
'$nickname' => $nickname, '$nickname' => $nickname,
'$sitename' => DI::baseUrl()->getHost(), '$sitename' => DI::baseUrl()->getHost(),
'$importh' => DI::l10n()->t('Import'), '$importh' => DI::l10n()->t('Import'),
'$importt' => DI::l10n()->t('Import your profile to this friendica instance'), '$importt' => DI::l10n()->t('Import your profile to this friendica instance'),
'$showtoslink' => DI::config()->get('system', 'tosdisplay'), '$showtoslink' => DI::config()->get('system', 'tosdisplay'),
'$tostext' => DI::l10n()->t('Terms of Service'), '$tostext' => DI::l10n()->t('Terms of Service'),
'$showprivstatement' => DI::config()->get('system', 'tosprivstatement'), '$showprivstatement' => DI::config()->get('system', 'tosprivstatement'),
'$privstatement'=> $this->tos->privacy_complete, '$privstatement' => $this->tos->privacy_complete,
'$form_security_token' => BaseModule::getFormSecurityToken('register'), '$form_security_token' => BaseModule::getFormSecurityToken('register'),
'$explicit_content' => DI::config()->get('system', 'explicit_content', false), '$explicit_content' => DI::config()->get('system', 'explicit_content', false),
'$explicit_content_note' => DI::l10n()->t('Note: This node explicitly contains adult content'), '$explicit_content_note' => DI::l10n()->t('Note: This node explicitly contains adult content'),
'$additional' => !empty(DI::userSession()->getLocalUserId()), '$additional' => !empty(DI::userSession()->getLocalUserId()),
'$parent_password' => ['parent_password', DI::l10n()->t('Parent Password:'), '', DI::l10n()->t('Please enter the password of the parent account to legitimize your request.')] '$parent_password' => ['parent_password', DI::l10n()->t('Parent Password:'), '', DI::l10n()->t('Please enter the password of the parent account to legitimize your request.')]
]); ]);
@ -223,12 +223,12 @@ class Register extends BaseModule
switch (self::getPolicy()) { switch (self::getPolicy()) {
case self::OPEN: case self::OPEN:
$blocked = 0; $blocked = 0;
$verified = 1; $verified = 1;
break; break;
case self::APPROVE: case self::APPROVE:
$blocked = 1; $blocked = 1;
$verified = 0; $verified = 0;
break; break;
@ -238,7 +238,7 @@ class Register extends BaseModule
DI::sysmsg()->addNotice(DI::l10n()->t('Permission denied.')); DI::sysmsg()->addNotice(DI::l10n()->t('Permission denied.'));
return; return;
} }
$blocked = 1; $blocked = 1;
$verified = 0; $verified = 0;
break; break;
} }
@ -261,11 +261,11 @@ class Register extends BaseModule
DI::baseUrl()->redirect('register'); DI::baseUrl()->redirect('register');
} }
$blocked = 0; $blocked = 0;
$verified = 1; $verified = 1;
$arr['password1'] = $arr['confirm'] = $arr['parent_password']; $arr['password1'] = $arr['confirm'] = $arr['parent_password'];
$arr['repeat'] = $arr['email'] = $user['email']; $arr['repeat'] = $arr['email'] = $user['email'];
} else { } else {
// Overwriting the "tar pit" field with the real one // Overwriting the "tar pit" field with the real one
$arr['email'] = $arr['field1']; $arr['email'] = $arr['field1'];
@ -280,9 +280,9 @@ class Register extends BaseModule
//Check if nickname contains only US-ASCII and do not start with a digit //Check if nickname contains only US-ASCII and do not start with a digit
if (!preg_match('/^[a-zA-Z][a-zA-Z0-9]*$/', $arr['nickname'])) { if (!preg_match('/^[a-zA-Z][a-zA-Z0-9]*$/', $arr['nickname'])) {
if (is_numeric(substr($arr['nickname'], 0, 1))) { if (is_numeric(substr($arr['nickname'], 0, 1))) {
DI::sysmsg()->addNotice(DI::l10n()->t("Nickname cannot start with a digit.")); DI::sysmsg()->addNotice(DI::l10n()->t("Nickname cannot start with a digit."));
} else { } else {
DI::sysmsg()->addNotice(DI::l10n()->t("Nickname can only contain US-ASCII characters.")); DI::sysmsg()->addNotice(DI::l10n()->t("Nickname can only contain US-ASCII characters."));
} }
$regdata = ['email' => $arr['email'], 'nickname' => $arr['nickname'], 'username' => $arr['username']]; $regdata = ['email' => $arr['email'], 'nickname' => $arr['nickname'], 'username' => $arr['username']];
@ -290,7 +290,7 @@ class Register extends BaseModule
return; return;
} }
$arr['blocked'] = $blocked; $arr['blocked'] = $blocked;
$arr['verified'] = $verified; $arr['verified'] = $verified;
$arr['language'] = L10n::detectLanguage($_SERVER, $_GET, DI::config()->get('system', 'language')); $arr['language'] = L10n::detectLanguage($_SERVER, $_GET, DI::config()->get('system', 'language'));
@ -318,7 +318,7 @@ class Register extends BaseModule
$using_invites = DI::config()->get('system', 'invitation_only'); $using_invites = DI::config()->get('system', 'invitation_only');
$num_invites = DI::config()->get('system', 'number_invites'); $num_invites = DI::config()->get('system', 'number_invites');
$invite_id = (!empty($_POST['invite_id']) ? trim($_POST['invite_id']) : ''); $invite_id = (!empty($_POST['invite_id']) ? trim($_POST['invite_id']) : '');
if (self::getPolicy() === self::OPEN) { if (self::getPolicy() === self::OPEN) {
if ($using_invites && $invite_id) { if ($using_invites && $invite_id) {
@ -344,9 +344,11 @@ class Register extends BaseModule
DI::baseUrl()->redirect(); DI::baseUrl()->redirect();
} else { } else {
DI::sysmsg()->addNotice( DI::sysmsg()->addNotice(
DI::l10n()->t('Failed to send email message. Here your accout details:<br> login: %s<br> password: %s<br><br>You can change your password after login.', DI::l10n()->t(
'Failed to send email message. Here your accout details:<br> login: %s<br> password: %s<br><br>You can change your password after login.',
$user['email'], $user['email'],
$result['password']) $result['password']
)
); );
} }
} else { } else {
@ -422,7 +424,7 @@ class Register extends BaseModule
public static function getPolicy(): int public static function getPolicy(): int
{ {
$admins = User::getAdminList(['login_date']); $admins = User::getAdminList(['login_date']);
$days = DI::config()->get('system', 'admin_inactivity_limit'); $days = DI::config()->get('system', 'admin_inactivity_limit');
if ($days == 0 || empty($admins)) { if ($days == 0 || empty($admins)) {
return intval(DI::config()->get('config', 'register_policy')); return intval(DI::config()->get('config', 'register_policy'));
} }

View file

@ -59,12 +59,14 @@ class Index extends BaseSearch
// 10 requests are "free", after the 11th only a call per minute is allowed // 10 requests are "free", after the 11th only a call per minute is allowed
$free_crawls = intval(DI::config()->get('system', 'free_crawls')); $free_crawls = intval(DI::config()->get('system', 'free_crawls'));
if ($free_crawls == 0) if ($free_crawls == 0) {
$free_crawls = 10; $free_crawls = 10;
}
$crawl_permit_period = intval(DI::config()->get('system', 'crawl_permit_period')); $crawl_permit_period = intval(DI::config()->get('system', 'crawl_permit_period'));
if ($crawl_permit_period == 0) if ($crawl_permit_period == 0) {
$crawl_permit_period = 10; $crawl_permit_period = 10;
}
$remote = $this->remoteAddress; $remote = $this->remoteAddress;
$result = DI::cache()->get('remote_search:' . $remote); $result = DI::cache()->get('remote_search:' . $remote);
@ -87,16 +89,16 @@ class Index extends BaseSearch
$tag = false; $tag = false;
if (!empty($_GET['tag'])) { if (!empty($_GET['tag'])) {
$tag = true; $tag = true;
$search = '#' . trim(rawurldecode($_GET['tag'])); $search = '#' . trim(rawurldecode($_GET['tag']));
} }
// construct a wrapper for the search header // construct a wrapper for the search header
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('content_wrapper.tpl'), [ $o = Renderer::replaceMacros(Renderer::getMarkupTemplate('content_wrapper.tpl'), [
'name' => 'search-header', 'name' => 'search-header',
'$title' => DI::l10n()->t('Search'), '$title' => DI::l10n()->t('Search'),
'$title_size' => 3, '$title_size' => 3,
'$content' => HTML::search($search, 'search-box', false) '$content' => HTML::search($search, 'search-box', false)
]); ]);
if (!$search) { if (!$search) {
@ -104,7 +106,7 @@ class Index extends BaseSearch
} }
if (strpos($search, '#') === 0) { if (strpos($search, '#') === 0) {
$tag = true; $tag = true;
$search = substr($search, 1); $search = substr($search, 1);
} else { } else {
if (strpos($search, '@') === 0 || strpos($search, '!') === 0) { if (strpos($search, '@') === 0 || strpos($search, '!') === 0) {
@ -146,11 +148,19 @@ class Index extends BaseSearch
// No items will be shown if the member has a blocked profile wall. // No items will be shown if the member has a blocked profile wall.
if (DI::mode()->isMobile()) { if (DI::mode()->isMobile()) {
$itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_mobile_network', $itemsPerPage = DI::pConfig()->get(
DI::config()->get('system', 'itemspage_network_mobile')); DI::userSession()->getLocalUserId(),
'system',
'itemspage_mobile_network',
DI::config()->get('system', 'itemspage_network_mobile')
);
} else { } else {
$itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_network', $itemsPerPage = DI::pConfig()->get(
DI::config()->get('system', 'itemspage_network')); DI::userSession()->getLocalUserId(),
'system',
'itemspage_network',
DI::config()->get('system', 'itemspage_network')
);
} }
$last_uriid = isset($_GET['last_uriid']) ? intval($_GET['last_uriid']) : 0; $last_uriid = isset($_GET['last_uriid']) ? intval($_GET['last_uriid']) : 0;
@ -160,18 +170,18 @@ class Index extends BaseSearch
if ($tag) { if ($tag) {
$this->logger->info('Start tag search.', ['q' => $search, 'start' => $pager->getStart(), 'items' => $pager->getItemsPerPage(), 'last' => $last_uriid]); $this->logger->info('Start tag search.', ['q' => $search, 'start' => $pager->getStart(), 'items' => $pager->getItemsPerPage(), 'last' => $last_uriid]);
$uriids = Tag::getURIIdListByTag($search, DI::userSession()->getLocalUserId(), $pager->getStart(), $pager->getItemsPerPage(), $last_uriid); $uriids = Tag::getURIIdListByTag($search, DI::userSession()->getLocalUserId(), $pager->getStart(), $pager->getItemsPerPage(), $last_uriid);
$count = Tag::countByTag($search, DI::userSession()->getLocalUserId()); $count = Tag::countByTag($search, DI::userSession()->getLocalUserId());
} else { } else {
$this->logger->info('Start fulltext search.', ['q' => $search]); $this->logger->info('Start fulltext search.', ['q' => $search]);
$uriids = Post\Content::getURIIdListBySearch($search, DI::userSession()->getLocalUserId(), $pager->getStart(), $pager->getItemsPerPage(), $last_uriid); $uriids = Post\Content::getURIIdListBySearch($search, DI::userSession()->getLocalUserId(), $pager->getStart(), $pager->getItemsPerPage(), $last_uriid);
$count = Post\Content::countBySearch($search, DI::userSession()->getLocalUserId()); $count = Post\Content::countBySearch($search, DI::userSession()->getLocalUserId());
} }
if (!empty($uriids)) { if (!empty($uriids)) {
$condition = ["(`uid` = ? OR (`uid` = ? AND NOT `global`))", 0, DI::userSession()->getLocalUserId()]; $condition = ["(`uid` = ? OR (`uid` = ? AND NOT `global`))", 0, DI::userSession()->getLocalUserId()];
$condition = DBA::mergeConditions($condition, ['uri-id' => $uriids]); $condition = DBA::mergeConditions($condition, ['uri-id' => $uriids]);
$params = ['order' => ['uri-id' => true]]; $params = ['order' => ['uri-id' => true]];
$items = Post::toArray(Post::selectForUser(DI::userSession()->getLocalUserId(), Item::DISPLAY_FIELDLIST, $condition, $params)); $items = Post::toArray(Post::selectForUser(DI::userSession()->getLocalUserId(), Item::DISPLAY_FIELDLIST, $condition, $params));
} }
if (empty($items)) { if (empty($items)) {

View file

@ -32,7 +32,7 @@ class Smilies extends BaseModule
protected function content(array $request = []): string protected function content(array $request = []): string
{ {
$smilies = Content\Smilies::getList(); $smilies = Content\Smilies::getList();
$count = count($smilies['texts'] ?? []); $count = count($smilies['texts'] ?? []);
$tpl = Renderer::getMarkupTemplate('smilies.tpl'); $tpl = Renderer::getMarkupTemplate('smilies.tpl');
return Renderer::replaceMacros($tpl, [ return Renderer::replaceMacros($tpl, [

View file

@ -204,10 +204,10 @@ class Probe
// Handles the case when the hostname contains the scheme // Handles the case when the hostname contains the scheme
if (!parse_url($host, PHP_URL_SCHEME)) { if (!parse_url($host, PHP_URL_SCHEME)) {
$ssl_url = 'https://' . $host . self::HOST_META; $ssl_url = 'https://' . $host . self::HOST_META;
$url = 'http://' . $host . self::HOST_META; $url = 'http://' . $host . self::HOST_META;
} else { } else {
$ssl_url = $host . self::HOST_META; $ssl_url = $host . self::HOST_META;
$url = ''; $url = '';
} }
$xrd_timeout = DI::config()->get('system', 'xrd_timeout', 20); $xrd_timeout = DI::config()->get('system', 'xrd_timeout', 20);
@ -255,8 +255,8 @@ class Probe
return []; return [];
} }
$xml = $curlResult->getBodyString(); $xml = $curlResult->getBodyString();
$xrd = XML::parseString($xml, true); $xrd = XML::parseString($xml, true);
$host_url = 'http://' . $host; $host_url = 'http://' . $host;
} }
if (!is_object($xrd)) { if (!is_object($xrd)) {
@ -330,7 +330,7 @@ class Probe
foreach ($webfinger['aliases'] as $alias) { foreach ($webfinger['aliases'] as $alias) {
$data[] = [ $data[] = [
'@attributes' => [ '@attributes' => [
'rel' => 'alias', 'rel' => 'alias',
'href' => $alias, 'href' => $alias,
] ]
]; ];
@ -383,18 +383,18 @@ class Probe
unset($data['networks']); unset($data['networks']);
if (!empty($data['network'])) { if (!empty($data['network'])) {
$networks[$data['network']] = $data; $networks[$data['network']] = $data;
$ap_profile['guid'] = $ap_profile['guid'] ?? $data['guid'] ?? null; $ap_profile['guid'] = $ap_profile['guid'] ?? $data['guid'] ?? null;
$ap_profile['about'] = $ap_profile['about'] ?? $data['about'] ?? null; $ap_profile['about'] = $ap_profile['about'] ?? $data['about'] ?? null;
$ap_profile['keywords'] = $data['keywords'] ?? null; $ap_profile['keywords'] = $data['keywords'] ?? null;
$ap_profile['location'] = $data['location'] ?? null; $ap_profile['location'] = $data['location'] ?? null;
$ap_profile['poco'] = $data['poco'] ?? null; $ap_profile['poco'] = $data['poco'] ?? null;
$ap_profile['openwebauth'] = $data['openwebauth'] ?? null; $ap_profile['openwebauth'] = $data['openwebauth'] ?? null;
} }
$data = $ap_profile; $data = $ap_profile;
$data['networks'] = $networks; $data['networks'] = $networks;
} elseif (!empty($ap_profile)) { } elseif (!empty($ap_profile)) {
$ap_profile['batch'] = ''; $ap_profile['batch'] = '';
$data = array_merge($ap_profile, $data); $data = array_merge($ap_profile, $data);
} }
} else { } else {
$data = $ap_profile; $data = $ap_profile;
@ -582,7 +582,7 @@ class Probe
if (empty($webfinger) && empty($lrdd)) { if (empty($webfinger) && empty($lrdd)) {
while (empty($lrdd) && empty($webfinger) && (count($path_parts) > 1)) { while (empty($lrdd) && empty($webfinger) && (count($path_parts) > 1)) {
$host .= '/' . array_shift($path_parts); $host .= '/' . array_shift($path_parts);
$baseurl = $parts['scheme'] . '://' . $host; $baseurl = $parts['scheme'] . '://' . $host;
if (!empty($nick)) { if (!empty($nick)) {
@ -679,8 +679,8 @@ class Probe
// First try the address because this is the primary purpose of webfinger // First try the address because this is the primary purpose of webfinger
if ($addr !== '') { if ($addr !== '') {
$detected = $addr; $detected = $addr;
$path = str_replace('{uri}', urlencode('acct:' . $addr), $template); $path = str_replace('{uri}', urlencode('acct:' . $addr), $template);
$webfinger = self::webfinger($path, $type); $webfinger = self::webfinger($path, $type);
if (is_null($webfinger)) { if (is_null($webfinger)) {
return null; return null;
@ -689,8 +689,8 @@ class Probe
// Then try the URI // Then try the URI
if (empty($webfinger) && $uri != $addr) { if (empty($webfinger) && $uri != $addr) {
$detected = $uri; $detected = $uri;
$path = str_replace('{uri}', urlencode($uri), $template); $path = str_replace('{uri}', urlencode($uri), $template);
$webfinger = self::webfinger($path, $type); $webfinger = self::webfinger($path, $type);
if (is_null($webfinger)) { if (is_null($webfinger)) {
return null; return null;
@ -772,9 +772,9 @@ class Probe
} }
$webfinger = $data['webfinger']; $webfinger = $data['webfinger'];
$nick = $data['nick'] ?? ''; $nick = $data['nick'] ?? '';
$addr = $data['addr'] ?? ''; $addr = $data['addr'] ?? '';
$baseurl = $data['baseurl'] ?? ''; $baseurl = $data['baseurl'] ?? '';
$result = []; $result = [];
@ -962,7 +962,7 @@ class Probe
$data['hide'] = !$json['searchable']; $data['hide'] = !$json['searchable'];
} }
if (!empty($json['public_forum'])) { if (!empty($json['public_forum'])) {
$data['community'] = $json['public_forum']; $data['community'] = $json['public_forum'];
$data['account-type'] = User::ACCOUNT_TYPE_COMMUNITY; $data['account-type'] = User::ACCOUNT_TYPE_COMMUNITY;
} elseif (($json['channel_type'] ?? '') == 'normal') { } elseif (($json['channel_type'] ?? '') == 'normal') {
$data['account-type'] = User::ACCOUNT_TYPE_PERSON; $data['account-type'] = User::ACCOUNT_TYPE_PERSON;
@ -1189,7 +1189,7 @@ class Probe
private static function dfrn(array $webfinger): array private static function dfrn(array $webfinger): array
{ {
$hcard_url = ''; $hcard_url = '';
$data = []; $data = [];
// The array is reversed to take into account the order of preference for same-rel links // The array is reversed to take into account the order of preference for same-rel links
// See: https://tools.ietf.org/html/rfc7033#section-4.4.4 // See: https://tools.ietf.org/html/rfc7033#section-4.4.4
foreach (array_reverse($webfinger['links']) as $link) { foreach (array_reverse($webfinger['links']) as $link) {
@ -1380,7 +1380,7 @@ class Probe
private static function diaspora(array $webfinger): array private static function diaspora(array $webfinger): array
{ {
$hcard_url = ''; $hcard_url = '';
$data = []; $data = [];
// The array is reversed to take into account the order of preference for same-rel links // The array is reversed to take into account the order of preference for same-rel links
// See: https://tools.ietf.org/html/rfc7033#section-4.4.4 // See: https://tools.ietf.org/html/rfc7033#section-4.4.4
@ -1446,7 +1446,7 @@ class Probe
&& !empty($data['pubkey']) && !empty($data['pubkey'])
&& $hcard_url !== '' && $hcard_url !== ''
) { ) {
$data['network'] = Protocol::DIASPORA; $data['network'] = Protocol::DIASPORA;
$data['manually-approve'] = false; $data['manually-approve'] = false;
// The Diaspora handle must always be lowercase // The Diaspora handle must always be lowercase
@ -1537,7 +1537,7 @@ class Probe
&& isset($data['poll']) && isset($data['poll'])
&& isset($data['url']) && isset($data['url'])
) { ) {
$data['network'] = Protocol::OSTATUS; $data['network'] = Protocol::OSTATUS;
$data['manually-approve'] = false; $data['manually-approve'] = false;
} else { } else {
return $short ? false : []; return $short ? false : [];
@ -1558,7 +1558,7 @@ class Probe
self::$isTimeout = true; self::$isTimeout = true;
return []; return [];
} }
$feed = $curlResult->getBodyString(); $feed = $curlResult->getBodyString();
$feed_data = Feed::import($feed); $feed_data = Feed::import($feed);
if (!$feed_data) { if (!$feed_data) {
return []; return [];
@ -1668,10 +1668,12 @@ class Probe
// Resolve arbitrary relative path // Resolve arbitrary relative path
// Lifted from https://www.php.net/manual/en/function.realpath.php#84012 // Lifted from https://www.php.net/manual/en/function.realpath.php#84012
$parts = array_filter(explode('/', $path), 'strlen'); $parts = array_filter(explode('/', $path), 'strlen');
$absolutes = []; $absolutes = [];
foreach ($parts as $part) { foreach ($parts as $part) {
if ('.' == $part) continue; if ('.' == $part) {
continue;
}
if ('..' == $part) { if ('..' == $part) {
array_pop($absolutes); array_pop($absolutes);
} else { } else {
@ -1721,18 +1723,18 @@ class Probe
return []; return [];
} }
$nick = $profile->handle ?? $profile->did; $nick = $profile->handle ?? $profile->did;
$name = $profile->displayName ?? $nick; $name = $profile->displayName ?? $nick;
$data = [ $data = [
'network' => Protocol::BLUESKY, 'network' => Protocol::BLUESKY,
'url' => $profile->did, 'url' => $profile->did,
'alias' => ATProtocol::WEB . '/profile/' . $nick, 'alias' => ATProtocol::WEB . '/profile/' . $nick,
'name' => $name ?: $nick, 'name' => $name ?: $nick,
'nick' => $nick, 'nick' => $nick,
'addr' => $nick, 'addr' => $nick,
'poll' => ATProtocol::WEB . '/profile/' . $profile->did . '/rss', 'poll' => ATProtocol::WEB . '/profile/' . $profile->did . '/rss',
'photo' => $profile->avatar ?? '', 'photo' => $profile->avatar ?? '',
]; ];
if (!empty($profile->description)) { if (!empty($profile->description)) {
@ -1819,7 +1821,7 @@ class Probe
$data['alias'] = $feed_data['header']['author-id']; $data['alias'] = $feed_data['header']['author-id'];
} }
$data['url'] = $url; $data['url'] = $url;
$data['poll'] = $url; $data['poll'] = $url;
$data['network'] = Protocol::FEED; $data['network'] = Protocol::FEED;
@ -1849,14 +1851,14 @@ class Probe
$user = DBA::selectFirst('user', ['prvkey'], ['uid' => $uid]); $user = DBA::selectFirst('user', ['prvkey'], ['uid' => $uid]);
$condition = ["`uid` = ? AND `server` != ''", $uid]; $condition = ["`uid` = ? AND `server` != ''", $uid];
$fields = ['pass', 'user', 'server', 'port', 'ssltype', 'mailbox']; $fields = ['pass', 'user', 'server', 'port', 'ssltype', 'mailbox'];
$mailacct = DBA::selectFirst('mailacct', $fields, $condition); $mailacct = DBA::selectFirst('mailacct', $fields, $condition);
if (!DBA::isResult($user) || !DBA::isResult($mailacct)) { if (!DBA::isResult($user) || !DBA::isResult($mailacct)) {
return []; return [];
} }
$mailbox = Email::constructMailboxName($mailacct); $mailbox = Email::constructMailboxName($mailacct);
$password = ''; $password = '';
openssl_private_decrypt(hex2bin($mailacct['pass']), $password, $user['prvkey']); openssl_private_decrypt(hex2bin($mailacct['pass']), $password, $user['prvkey']);
$mbox = Email::connect($mailbox, $mailacct['user'], $password); $mbox = Email::connect($mailbox, $mailacct['user'], $password);
@ -1883,7 +1885,7 @@ class Probe
'poll' => 'email ' . Strings::getRandomHex(), 'poll' => 'email ' . Strings::getRandomHex(),
]; ];
$data['nick'] = $data['name']; $data['nick'] = $data['name'];
$x = Email::messageMeta($mbox, $msgs[0]); $x = Email::messageMeta($mbox, $msgs[0]);
@ -1899,7 +1901,7 @@ class Probe
&& (strcasecmp($feadr->host, $phost) == 0) && (strcasecmp($feadr->host, $phost) == 0)
&& !empty($feadr->personal) && !empty($feadr->personal)
) { ) {
$personal = imap_mime_header_decode($feadr->personal); $personal = imap_mime_header_decode($feadr->personal);
$data['name'] = ''; $data['name'] = '';
foreach ($personal as $perspart) { foreach ($personal as $perspart) {
if ($perspart->charset != 'default') { if ($perspart->charset != 'default') {
@ -2064,9 +2066,9 @@ class Probe
$last_updated = ''; $last_updated = '';
foreach ($items as $activity) { foreach ($items as $activity) {
if (!empty($activity['published'])) { if (!empty($activity['published'])) {
$published = DateTimeFormat::utc($activity['published']); $published = DateTimeFormat::utc($activity['published']);
} elseif (!empty($activity['object']['published'])) { } elseif (!empty($activity['object']['published'])) {
$published = DateTimeFormat::utc($activity['object']['published']); $published = DateTimeFormat::utc($activity['object']['published']);
} else { } else {
continue; continue;
} }
@ -2182,7 +2184,7 @@ class Probe
'sharedinbox' => $approfile['endpoints']['sharedInbox'], 'network' => Protocol::DFRN, 'sharedinbox' => $approfile['endpoints']['sharedInbox'], 'network' => Protocol::DFRN,
'pubkey' => $owner['upubkey'], 'baseurl' => $approfile['generator']['url'], 'gsid' => $owner['gsid'], 'pubkey' => $owner['upubkey'], 'baseurl' => $approfile['generator']['url'], 'gsid' => $owner['gsid'],
'manually-approve' => in_array($owner['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP, User::PAGE_FLAGS_COMM_MAN]), 'manually-approve' => in_array($owner['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP, User::PAGE_FLAGS_COMM_MAN]),
'networks' => [ 'networks' => [
Protocol::DIASPORA => [ Protocol::DIASPORA => [
'name' => $owner['name'], 'name' => $owner['name'],
'given_name' => $split_name['first'], 'given_name' => $split_name['first'],
@ -2207,7 +2209,7 @@ class Probe
} catch (Exception $e) { } catch (Exception $e) {
// Default values for nonexistent targets // Default values for nonexistent targets
$data = [ $data = [
'name' => $url, 'nick' => $url, 'url' => $url, 'network' => Protocol::PHANTOM, 'name' => $url, 'nick' => $url, 'url' => $url, 'network' => Protocol::PHANTOM,
'photo' => DI::baseUrl() . Contact::DEFAULT_AVATAR_PHOTO 'photo' => DI::baseUrl() . Contact::DEFAULT_AVATAR_PHOTO
]; ];
} }

View file

@ -47,26 +47,26 @@ class Attachment extends BaseDataTransferObject
*/ */
public function __construct(array $attachment, string $type, string $url, string $preview, string $remote) public function __construct(array $attachment, string $type, string $url, string $preview, string $remote)
{ {
$this->id = (string)$attachment['id']; $this->id = (string)$attachment['id'];
$this->type = $type; $this->type = $type;
$this->url = $url; $this->url = $url;
$this->preview_url = $preview; $this->preview_url = $preview;
$this->remote_url = $remote; $this->remote_url = $remote;
$this->text_url = $this->remote_url ?? $this->url; $this->text_url = $this->remote_url ?? $this->url;
$this->description = $attachment['description']; $this->description = $attachment['description'];
$this->blurhash = $attachment['blurhash']; $this->blurhash = $attachment['blurhash'];
if ($type === 'image') { if ($type === 'image') {
if ((int) $attachment['width'] > 0 && (int) $attachment['height'] > 0) { if ((int) $attachment['width'] > 0 && (int) $attachment['height'] > 0) {
$this->meta['original']['width'] = (int) $attachment['width']; $this->meta['original']['width'] = (int) $attachment['width'];
$this->meta['original']['height'] = (int) $attachment['height']; $this->meta['original']['height'] = (int) $attachment['height'];
$this->meta['original']['size'] = (int) $attachment['width'] . 'x' . (int) $attachment['height']; $this->meta['original']['size'] = (int) $attachment['width'] . 'x' . (int) $attachment['height'];
$this->meta['original']['aspect'] = (float) ((int) $attachment['width'] / (int) $attachment['height']); $this->meta['original']['aspect'] = (float) ((int) $attachment['width'] / (int) $attachment['height']);
} }
if (isset($attachment['preview-width']) && (int) $attachment['preview-width'] > 0 && (int) $attachment['preview-height'] > 0) { if (isset($attachment['preview-width']) && (int) $attachment['preview-width'] > 0 && (int) $attachment['preview-height'] > 0) {
$this->meta['small']['width'] = (int) $attachment['preview-width']; $this->meta['small']['width'] = (int) $attachment['preview-width'];
$this->meta['small']['height'] = (int) $attachment['preview-height']; $this->meta['small']['height'] = (int) $attachment['preview-height'];
$this->meta['small']['size'] = (int) $attachment['preview-width'] . 'x' . (int) $attachment['preview-height']; $this->meta['small']['size'] = (int) $attachment['preview-width'] . 'x' . (int) $attachment['preview-height'];
$this->meta['small']['aspect'] = (float) ((int) $attachment['preview-width'] / (int) $attachment['preview-height']); $this->meta['small']['aspect'] = (float) ((int) $attachment['preview-width'] / (int) $attachment['preview-height']);
} }
} }

View file

@ -33,31 +33,31 @@ use InvalidArgumentException;
*/ */
class Post class Post
{ {
private $data = []; private $data = [];
private $template = null; private $template = null;
private $available_templates = [ private $available_templates = [
'wall' => 'wall_thread.tpl', 'wall' => 'wall_thread.tpl',
'wall2wall' => 'wallwall_thread.tpl' 'wall2wall' => 'wallwall_thread.tpl'
]; ];
private $comment_box_template = 'comment_item.tpl'; private $comment_box_template = 'comment_item.tpl';
private $toplevel = false; private $toplevel = false;
private $writable = false; private $writable = false;
/** /**
* @var Post[] * @var Post[]
*/ */
private $children = []; private $children = [];
private $parent = null; private $parent = null;
/** /**
* @var Thread * @var Thread
*/ */
private $thread = null; private $thread = null;
private $redirect_url = null; private $redirect_url = null;
private $owner_url = ''; private $owner_url = '';
private $owner_name = ''; private $owner_name = '';
private $wall_to_wall = false; private $wall_to_wall = false;
private $threaded = false; private $threaded = false;
private $visiting = false; private $visiting = false;
/** /**
* Constructor * Constructor
@ -76,7 +76,7 @@ class Post
} }
$this->writable = $this->getDataValue('writable') || $this->getDataValue('self'); $this->writable = $this->getDataValue('writable') || $this->getDataValue('self');
$author = [ $author = [
'uid' => 0, 'uid' => 0,
'id' => $this->getDataValue('author-id'), 'id' => $this->getDataValue('author-id'),
'network' => $this->getDataValue('author-network'), 'network' => $this->getDataValue('author-network'),
@ -104,7 +104,7 @@ class Post
} }
$item['pagedrop'] = $data['pagedrop']; $item['pagedrop'] = $data['pagedrop'];
$child = new Post($item); $child = new Post($item);
$this->addChild($child); $this->addChild($child);
} }
} }
@ -153,7 +153,7 @@ class Post
*/ */
public function getTemplateData(array $conv_responses, string $formSecurityToken, int $thread_level = 1, array $thread_parent = []) public function getTemplateData(array $conv_responses, string $formSecurityToken, int $thread_level = 1, array $thread_parent = [])
{ {
$item = $this->getData(); $item = $this->getData();
$edited = false; $edited = false;
/* /*
@ -177,16 +177,16 @@ class Post
'share' => null, 'share' => null,
'announce' => null, 'announce' => null,
]; ];
$dropping = false; $dropping = false;
$pinned = ''; $pinned = '';
$pin = false; $pin = false;
$star = false; $star = false;
$ignore_thread = false; $ignore_thread = false;
$ispinned = 'unpinned'; $ispinned = 'unpinned';
$isstarred = 'unstarred'; $isstarred = 'unstarred';
$indent = ''; $indent = '';
$shiny = ''; $shiny = '';
$osparkle = ''; $osparkle = '';
$total_children = $item['counts'] ?? $this->countDescendants(); $total_children = $item['counts'] ?? $this->countDescendants();
$conv = $this->getThread(); $conv = $this->getThread();
@ -257,8 +257,8 @@ class Post
$drop = [ $drop = [
'dropping' => $dropping, 'dropping' => $dropping,
'pagedrop' => $item['pagedrop'], 'pagedrop' => $item['pagedrop'],
'select' => DI::l10n()->t('Select'), 'select' => DI::l10n()->t('Select'),
'label' => $origin ? DI::l10n()->t('Delete globally') : DI::l10n()->t('Remove locally'), 'label' => $origin ? DI::l10n()->t('Delete globally') : DI::l10n()->t('Remove locally'),
]; ];
} }
@ -322,7 +322,7 @@ class Post
$response_verbs = ['like', 'dislike', 'announce', 'comment']; $response_verbs = ['like', 'dislike', 'announce', 'comment'];
$isevent = false; $isevent = false;
$attend = []; $attend = [];
if ($item['object-type'] === Activity\ObjectType::EVENT) { if ($item['object-type'] === Activity\ObjectType::EVENT) {
$response_verbs[] = 'attendyes'; $response_verbs[] = 'attendyes';
$response_verbs[] = 'attendno'; $response_verbs[] = 'attendno';
@ -330,7 +330,7 @@ class Post
if ($conv->isWritable()) { if ($conv->isWritable()) {
$isevent = true; $isevent = true;
$attend = [DI::l10n()->t('I will attend'), DI::l10n()->t('I will not attend'), DI::l10n()->t('I might attend')]; $attend = [DI::l10n()->t('I will attend'), DI::l10n()->t('I will not attend'), DI::l10n()->t('I might attend')];
} }
} }
@ -407,7 +407,7 @@ class Post
'toggle' => DI::l10n()->t('Toggle pin status'), 'toggle' => DI::l10n()->t('Toggle pin status'),
'classdo' => $item['featured'] ? 'hidden' : '', 'classdo' => $item['featured'] ? 'hidden' : '',
'classundo' => $item['featured'] ? '' : 'hidden', 'classundo' => $item['featured'] ? '' : 'hidden',
'pinned' => DI::l10n()->t('Pinned'), 'pinned' => DI::l10n()->t('Pinned'),
]; ];
} }
@ -430,7 +430,7 @@ class Post
$buttons['share'] = [DI::l10n()->t('Quote share this'), DI::l10n()->t('Quote Share')]; $buttons['share'] = [DI::l10n()->t('Quote share this'), DI::l10n()->t('Quote Share')];
} }
if ($announceable) { if ($announceable) {
$buttons['announce'] = [DI::l10n()->t('Reshare this'), DI::l10n()->t('Reshare')]; $buttons['announce'] = [DI::l10n()->t('Reshare this'), DI::l10n()->t('Reshare')];
$buttons['unannounce'] = [DI::l10n()->t('Cancel your Reshare'), DI::l10n()->t('Unshare')]; $buttons['unannounce'] = [DI::l10n()->t('Cancel your Reshare'), DI::l10n()->t('Unshare')];
} }
} }
@ -463,7 +463,7 @@ class Post
} }
$isevent = false; $isevent = false;
$tagger = ''; $tagger = '';
} }
if ($buttons['like'] && in_array($item['network'], [Protocol::FEED, Protocol::MAIL])) { if ($buttons['like'] && in_array($item['network'], [Protocol::FEED, Protocol::MAIL])) {
@ -472,7 +472,7 @@ class Post
$tags = Tag::populateFromItem($item); $tags = Tag::populateFromItem($item);
$ago = Temporal::getRelativeDate($item['created']); $ago = Temporal::getRelativeDate($item['created']);
$ago_received = Temporal::getRelativeDate($item['received']); $ago_received = Temporal::getRelativeDate($item['received']);
if (DI::config()->get('system', 'show_received') && (abs(strtotime($item['created']) - strtotime($item['received'])) > DI::config()->get('system', 'show_received_seconds')) && ($ago != $ago_received)) { if (DI::config()->get('system', 'show_received') && (abs(strtotime($item['created']) - strtotime($item['received'])) > DI::config()->get('system', 'show_received_seconds')) && ($ago != $ago_received)) {
$ago = DI::l10n()->t('%s (Received %s)', $ago, $ago_received); $ago = DI::l10n()->t('%s (Received %s)', $ago, $ago_received);
@ -486,7 +486,7 @@ class Post
]; ];
// Ensure to either display the remote comment or the local activities // Ensure to either display the remote comment or the local activities
$buttons = []; $buttons = [];
$comment_html = ''; $comment_html = '';
} else { } else {
$remote_comment = ''; $remote_comment = '';
@ -513,109 +513,109 @@ class Post
$parent_unknown = $parent_username ? '' : DI::l10n()->t('Unknown parent'); $parent_unknown = $parent_username ? '' : DI::l10n()->t('Unknown parent');
$tmp_item = [ $tmp_item = [
'parentguid' => $parent_guid, 'parentguid' => $parent_guid,
'inreplyto' => DI::l10n()->t('in reply to %s', $parent_username), 'inreplyto' => DI::l10n()->t('in reply to %s', $parent_username),
'isunknown' => $parent_unknown, 'isunknown' => $parent_unknown,
'isunknown_label' => DI::l10n()->t('Parent is probably private or not federated.'), 'isunknown_label' => DI::l10n()->t('Parent is probably private or not federated.'),
'template' => $this->getTemplate(), 'template' => $this->getTemplate(),
'type' => implode('', array_slice(explode('/', $item['verb']), -1)), 'type' => implode('', array_slice(explode('/', $item['verb']), -1)),
'comment_firstcollapsed' => false, 'comment_firstcollapsed' => false,
'comment_lastcollapsed' => false, 'comment_lastcollapsed' => false,
'suppress_tags' => DI::config()->get('system', 'suppress_tags'), 'suppress_tags' => DI::config()->get('system', 'suppress_tags'),
'tags' => $tags['tags'], 'tags' => $tags['tags'],
'hashtags' => $tags['hashtags'], 'hashtags' => $tags['hashtags'],
'mentions' => $tags['mentions'], 'mentions' => $tags['mentions'],
'implicit_mentions' => $tags['implicit_mentions'], 'implicit_mentions' => $tags['implicit_mentions'],
'txt_cats' => DI::l10n()->t('Categories:'), 'txt_cats' => DI::l10n()->t('Categories:'),
'txt_folders' => DI::l10n()->t('Filed under:'), 'txt_folders' => DI::l10n()->t('Filed under:'),
'has_cats' => ((count($categories)) ? 'true' : ''), 'has_cats' => ((count($categories)) ? 'true' : ''),
'has_folders' => ((count($folders)) ? 'true' : ''), 'has_folders' => ((count($folders)) ? 'true' : ''),
'categories' => $categories, 'categories' => $categories,
'folders' => $folders, 'folders' => $folders,
'body_html' => $body_html, 'body_html' => $body_html,
'text' => strip_tags($body_html), 'text' => strip_tags($body_html),
'id' => $this->getId(), 'id' => $this->getId(),
'guid' => urlencode($item['guid']), 'guid' => urlencode($item['guid']),
'isevent' => $isevent, 'isevent' => $isevent,
'attend' => $attend, 'attend' => $attend,
'linktitle' => DI::l10n()->t('View %s\'s profile @ %s', $profile_name, $item['author-link']), 'linktitle' => DI::l10n()->t('View %s\'s profile @ %s', $profile_name, $item['author-link']),
'olinktitle' => DI::l10n()->t('View %s\'s profile @ %s', $this->getOwnerName(), $item['owner-link']), 'olinktitle' => DI::l10n()->t('View %s\'s profile @ %s', $this->getOwnerName(), $item['owner-link']),
'to' => DI::l10n()->t('to'), 'to' => DI::l10n()->t('to'),
'via' => DI::l10n()->t('via'), 'via' => DI::l10n()->t('via'),
'wall' => DI::l10n()->t('Wall-to-Wall'), 'wall' => DI::l10n()->t('Wall-to-Wall'),
'vwall' => DI::l10n()->t('via Wall-To-Wall:'), 'vwall' => DI::l10n()->t('via Wall-To-Wall:'),
'profile_url' => $profile_link, 'profile_url' => $profile_link,
'name' => $profile_name, 'name' => $profile_name,
'item_photo_menu_html' => DI::contentItem()->photoMenu($item, $formSecurityToken), 'item_photo_menu_html' => DI::contentItem()->photoMenu($item, $formSecurityToken),
'thumb' => DI::baseUrl()->remove(DI::contentItem()->getAuthorAvatar($item)), 'thumb' => DI::baseUrl()->remove(DI::contentItem()->getAuthorAvatar($item)),
'osparkle' => $osparkle, 'osparkle' => $osparkle,
'sparkle' => $sparkle, 'sparkle' => $sparkle,
'title' => $item['title'], 'title' => $item['title'],
'summary' => $item['content-warning'], 'summary' => $item['content-warning'],
'localtime' => DateTimeFormat::local($item['created'], 'r'), 'localtime' => DateTimeFormat::local($item['created'], 'r'),
'ago' => $item['app'] ? DI::l10n()->t('%s from %s', $ago, $item['app']) : $ago, 'ago' => $item['app'] ? DI::l10n()->t('%s from %s', $ago, $item['app']) : $ago,
'app' => $item['app'], 'app' => $item['app'],
'created' => $ago, 'created' => $ago,
'lock' => $lock, 'lock' => $lock,
'private' => $item['private'], 'private' => $item['private'],
'privacy' => $privacy, 'privacy' => $privacy,
'connector' => $connector, 'connector' => $connector,
'location_html' => $location_html, 'location_html' => $location_html,
'indent' => $indent, 'indent' => $indent,
'shiny' => $shiny, 'shiny' => $shiny,
'owner_self' => $item['author-link'] == DI::session()->get('my_url'), 'owner_self' => $item['author-link'] == DI::session()->get('my_url'),
'owner_url' => $this->getOwnerUrl(), 'owner_url' => $this->getOwnerUrl(),
'owner_photo' => DI::baseUrl()->remove(DI::contentItem()->getOwnerAvatar($item)), 'owner_photo' => DI::baseUrl()->remove(DI::contentItem()->getOwnerAvatar($item)),
'owner_name' => $this->getOwnerName(), 'owner_name' => $this->getOwnerName(),
'plink' => Item::getPlink($item), 'plink' => Item::getPlink($item),
'browsershare' => $browsershare, 'browsershare' => $browsershare,
'edpost' => $edpost, 'edpost' => $edpost,
'ispinned' => $ispinned, 'ispinned' => $ispinned,
'pin' => $pin, 'pin' => $pin,
'pinned' => $pinned, 'pinned' => $pinned,
'isstarred' => $isstarred, 'isstarred' => $isstarred,
'star' => $star, 'star' => $star,
'ignore' => $ignore_thread, 'ignore' => $ignore_thread,
'tagger' => $tagger, 'tagger' => $tagger,
'filer' => $filer, 'filer' => $filer,
'language' => $languages, 'language' => $languages,
'searchtext' => DI::l10n()->t('Search Text'), 'searchtext' => DI::l10n()->t('Search Text'),
'drop' => $drop, 'drop' => $drop,
'block' => $block, 'block' => $block,
'ignore_author' => $ignore, 'ignore_author' => $ignore,
'collapse' => $collapse, 'collapse' => $collapse,
'report' => $report, 'report' => $report,
'ignore_server' => $ignoreServer, 'ignore_server' => $ignoreServer,
'vote' => $buttons, 'vote' => $buttons,
'like_html' => $responses['like']['output'], 'like_html' => $responses['like']['output'],
'dislike_html' => $responses['dislike']['output'], 'dislike_html' => $responses['dislike']['output'],
'hide_dislike' => $hide_dislike, 'hide_dislike' => $hide_dislike,
'emojis' => $emojis, 'emojis' => $emojis,
'quoteshares' => $this->getQuoteShares($item['quoteshares']), 'quoteshares' => $this->getQuoteShares($item['quoteshares']),
'reactions' => $reactions, 'reactions' => $reactions,
'responses' => $responses, 'responses' => $responses,
'legacy_activities' => DI::config()->get('system', 'legacy_activities'), 'legacy_activities' => DI::config()->get('system', 'legacy_activities'),
'switchcomment' => DI::l10n()->t('Comment'), 'switchcomment' => DI::l10n()->t('Comment'),
'reply_label' => DI::l10n()->t('Reply to %s', $profile_name), 'reply_label' => DI::l10n()->t('Reply to %s', $profile_name),
'comment_html' => $comment_html, 'comment_html' => $comment_html,
'remote_comment' => $remote_comment, 'remote_comment' => $remote_comment,
'menu' => DI::l10n()->t('More'), 'menu' => DI::l10n()->t('More'),
'previewing' => $conv->isPreview() ? ' preview ' : '', 'previewing' => $conv->isPreview() ? ' preview ' : '',
'wait' => DI::l10n()->t('Please wait'), 'wait' => DI::l10n()->t('Please wait'),
'thread_level' => $thread_level, 'thread_level' => $thread_level,
'edited' => $edited, 'edited' => $edited,
'author_gsid' => $item['author-gsid'], 'author_gsid' => $item['author-gsid'],
'network' => $item['network'], 'network' => $item['network'],
'network_name' => ContactSelector::networkToName($item['author-network'], $item['network'], $item['author-gsid']), 'network_name' => ContactSelector::networkToName($item['author-network'], $item['network'], $item['author-gsid']),
'network_svg' => ContactSelector::networkToSVG($item['network'], $item['author-gsid'], '', DI::userSession()->getLocalUserId()), 'network_svg' => ContactSelector::networkToSVG($item['network'], $item['author-gsid'], '', DI::userSession()->getLocalUserId()),
'received' => $item['received'], 'received' => $item['received'],
'commented' => $item['commented'], 'commented' => $item['commented'],
'created_date' => $item['created'], 'created_date' => $item['created'],
'uriid' => $item['uri-id'], 'uriid' => $item['uri-id'],
'return' => (DI::args()->getCommand()) ? bin2hex(DI::args()->getCommand()) : '', 'return' => (DI::args()->getCommand()) ? bin2hex(DI::args()->getCommand()) : '',
'direction' => $direction, 'direction' => $direction,
'reshared' => $item['reshared'] ?? '', 'reshared' => $item['reshared'] ?? '',
'delivery' => [ 'delivery' => [
'queue_count' => $item['delivery_queue_count'], 'queue_count' => $item['delivery_queue_count'],
'queue_done' => $item['delivery_queue_done'] + $item['delivery_queue_failed'], /// @todo Possibly display it separately in the future 'queue_done' => $item['delivery_queue_done'] + $item['delivery_queue_failed'], /// @todo Possibly display it separately in the future
'notifier_pending' => DI::l10n()->t('Notifier task is pending'), 'notifier_pending' => DI::l10n()->t('Notifier task is pending'),
@ -632,21 +632,21 @@ class Post
$result = $arr['output']; $result = $arr['output'];
$result['children'] = []; $result['children'] = [];
$children = $this->getChildren(); $children = $this->getChildren();
$nb_children = count($children); $nb_children = count($children);
if ($nb_children > 0) { if ($nb_children > 0) {
$thread_parent[$item['uri-id']] = ['guid' => $item['guid'], 'name' => $item['author-name']]; $thread_parent[$item['uri-id']] = ['guid' => $item['guid'], 'name' => $item['author-name']];
foreach ($children as $child) { foreach ($children as $child) {
$thread_parent[$child->getDataValue('uri-id')] = ['guid' => $child->getDataValue('guid'), 'name' => $child->getDataValue('author-name')]; $thread_parent[$child->getDataValue('uri-id')] = ['guid' => $child->getDataValue('guid'), 'name' => $child->getDataValue('author-name')];
$result['children'][] = $child->getTemplateData($conv_responses, $formSecurityToken, $thread_level + 1, $thread_parent); $result['children'][] = $child->getTemplateData($conv_responses, $formSecurityToken, $thread_level + 1, $thread_parent);
} }
// Collapse // Collapse
if (($nb_children > 2) || ($thread_level > 1)) { if (($nb_children > 2) || ($thread_level > 1)) {
$result['children'][0]['comment_firstcollapsed'] = true; $result['children'][0]['comment_firstcollapsed'] = true;
$result['children'][0]['num_comments'] = DI::l10n()->tt('%d comment', '%d comments', $total_children); $result['children'][0]['num_comments'] = DI::l10n()->tt('%d comment', '%d comments', $total_children);
$result['children'][0]['show_text'] = DI::l10n()->t('Show more'); $result['children'][0]['show_text'] = DI::l10n()->t('Show more');
$result['children'][0]['hide_text'] = DI::l10n()->t('Show fewer'); $result['children'][0]['hide_text'] = DI::l10n()->t('Show fewer');
if ($thread_level > 1) { if ($thread_level > 1) {
$result['children'][$nb_children - 1]['comment_lastcollapsed'] = true; $result['children'][$nb_children - 1]['comment_lastcollapsed'] = true;
} else { } else {
@ -1022,7 +1022,7 @@ class Post
private function countDescendants(): int private function countDescendants(): int
{ {
$children = $this->getChildren(); $children = $this->getChildren();
$total = count($children); $total = count($children);
if ($total > 0) { if ($total > 0) {
foreach ($children as $child) { foreach ($children as $child) {
$total += $child->countDescendants(); $total += $child->countDescendants();
@ -1055,7 +1055,7 @@ class Post
} }
$owner = User::getOwnerDataById(DI::userSession()->getLocalUserId()); $owner = User::getOwnerDataById(DI::userSession()->getLocalUserId());
$item = $this->getData(); $item = $this->getData();
if (!empty($item['content-warning']) && Feature::isEnabled(DI::userSession()->getLocalUserId(), Feature::ADD_ABSTRACT)) { if (!empty($item['content-warning']) && Feature::isEnabled(DI::userSession()->getLocalUserId(), Feature::ADD_ABSTRACT)) {
$text = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $item['content-warning'] . "[/abstract]\n"; $text = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $item['content-warning'] . "[/abstract]\n";
@ -1102,7 +1102,7 @@ class Post
private function getCommentBox(string $indent) private function getCommentBox(string $indent)
{ {
$comment_box = ''; $comment_box = '';
$conv = $this->getThread(); $conv = $this->getThread();
if ($conv->isWritable() && $this->isWritable()) { if ($conv->isWritable() && $this->isWritable()) {
/* /*
@ -1111,12 +1111,12 @@ class Post
*/ */
$qcomment = null; $qcomment = null;
if (Addon::isEnabled('qcomment')) { if (Addon::isEnabled('qcomment')) {
$words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words'); $words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words');
$qcomment = $words ? explode("\n", $words) : []; $qcomment = $words ? explode("\n", $words) : [];
} }
// Fetch the user id from the parent when the owner user is empty // Fetch the user id from the parent when the owner user is empty
$uid = $conv->getProfileOwner(); $uid = $conv->getProfileOwner();
$parent_uid = $this->getDataValue('uid'); $parent_uid = $this->getDataValue('uid');
$owner = User::getOwnerDataById(DI::userSession()->getLocalUserId()); $owner = User::getOwnerDataById(DI::userSession()->getLocalUserId());
@ -1127,7 +1127,7 @@ class Post
$uid = $parent_uid; $uid = $parent_uid;
} }
$template = Renderer::getMarkupTemplate($this->getCommentBoxTemplate()); $template = Renderer::getMarkupTemplate($this->getCommentBoxTemplate());
$comment_box = Renderer::replaceMacros($template, [ $comment_box = Renderer::replaceMacros($template, [
'$return_path' => DI::args()->getQueryString(), '$return_path' => DI::args()->getQueryString(),
'$threaded' => $this->isThreaded(), '$threaded' => $this->isThreaded(),
@ -1180,7 +1180,7 @@ class Post
*/ */
protected function checkWallToWall() protected function checkWallToWall()
{ {
$conv = $this->getThread(); $conv = $this->getThread();
$this->wall_to_wall = false; $this->wall_to_wall = false;
if ($this->isToplevel()) { if ($this->isToplevel()) {
@ -1200,7 +1200,7 @@ class Post
* well that it's the same Bob Smith. * well that it's the same Bob Smith.
* But it could be somebody else with the same name. It just isn't highly likely. * But it could be somebody else with the same name. It just isn't highly likely.
*/ */
$this->owner_name = $this->getDataValue('owner-name'); $this->owner_name = $this->getDataValue('owner-name');
$this->wall_to_wall = true; $this->wall_to_wall = true;
$owner = [ $owner = [
@ -1218,7 +1218,7 @@ class Post
if (!$this->wall_to_wall) { if (!$this->wall_to_wall) {
$this->setTemplate('wall'); $this->setTemplate('wall');
$this->owner_url = ''; $this->owner_url = '';
$this->owner_name = ''; $this->owner_name = '';
} }
} }

View file

@ -21,11 +21,11 @@ use Friendica\Security\Security;
class Thread class Thread
{ {
/** @var Post[] */ /** @var Post[] */
private $parents = []; private $parents = [];
private $mode = null; private $mode = null;
private $writable = false; private $writable = false;
private $profile_owner = 0; private $profile_owner = 0;
private $preview = false; private $preview = false;
/** /**
* Constructor * Constructor
@ -62,18 +62,18 @@ class Thread
case Conversation::MODE_NETWORK: case Conversation::MODE_NETWORK:
case Conversation::MODE_NOTES: case Conversation::MODE_NOTES:
$this->profile_owner = DI::userSession()->getLocalUserId(); $this->profile_owner = DI::userSession()->getLocalUserId();
$this->writable = true; $this->writable = true;
break; break;
case Conversation::MODE_PROFILE: case Conversation::MODE_PROFILE:
case Conversation::MODE_DISPLAY: case Conversation::MODE_DISPLAY:
$this->profile_owner = $appHelper->getProfileOwner(); $this->profile_owner = $appHelper->getProfileOwner();
$this->writable = Security::canWriteToUserWall($this->profile_owner) || $writable; $this->writable = Security::canWriteToUserWall($this->profile_owner) || $writable;
break; break;
case Conversation::MODE_CHANNEL: case Conversation::MODE_CHANNEL:
case Conversation::MODE_COMMUNITY: case Conversation::MODE_COMMUNITY:
case Conversation::MODE_CONTACTS: case Conversation::MODE_CONTACTS:
$this->profile_owner = 0; $this->profile_owner = 0;
$this->writable = $writable; $this->writable = $writable;
break; break;
default: default:
DI::logger()->info('[ERROR] Conversation::setMode : Unhandled mode ('. $mode .').'); DI::logger()->info('[ERROR] Conversation::setMode : Unhandled mode ('. $mode .').');

View file

@ -47,28 +47,28 @@ use Friendica\Util\JsonLD;
class ActivityPub class ActivityPub
{ {
const PUBLIC_COLLECTION = 'https://www.w3.org/ns/activitystreams#Public'; const PUBLIC_COLLECTION = 'https://www.w3.org/ns/activitystreams#Public';
const CONTEXT = [ const CONTEXT = [
'https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1', 'https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1',
[ [
'ostatus' => 'http://ostatus.org#', 'ostatus' => 'http://ostatus.org#',
'vcard' => 'http://www.w3.org/2006/vcard/ns#', 'vcard' => 'http://www.w3.org/2006/vcard/ns#',
'dfrn' => 'http://purl.org/macgirvin/dfrn/1.0/', 'dfrn' => 'http://purl.org/macgirvin/dfrn/1.0/',
'diaspora' => 'https://diasporafoundation.org/ns/', 'diaspora' => 'https://diasporafoundation.org/ns/',
'litepub' => 'http://litepub.social/ns#', 'litepub' => 'http://litepub.social/ns#',
'toot' => 'http://joinmastodon.org/ns#', 'toot' => 'http://joinmastodon.org/ns#',
'featured' => [ 'featured' => [
"@id" => "toot:featured", "@id" => "toot:featured",
"@type" => "@id", "@type" => "@id",
], ],
'schema' => 'http://schema.org#', 'schema' => 'http://schema.org#',
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
'sensitive' => 'as:sensitive', 'Hashtag' => 'as:Hashtag', 'sensitive' => 'as:sensitive', 'Hashtag' => 'as:Hashtag',
'quoteUrl' => 'as:quoteUrl', 'quoteUrl' => 'as:quoteUrl',
'conversation' => 'ostatus:conversation', 'conversation' => 'ostatus:conversation',
'directMessage' => 'litepub:directMessage', 'directMessage' => 'litepub:directMessage',
'discoverable' => 'toot:discoverable', 'discoverable' => 'toot:discoverable',
'PropertyValue' => 'schema:PropertyValue', 'PropertyValue' => 'schema:PropertyValue',
'value' => 'schema:value', 'value' => 'schema:value',
] ]
]; ];
const ACCOUNT_TYPES = ['Person', 'Organization', 'Service', 'Group', 'Application', 'Tombstone']; const ACCOUNT_TYPES = ['Person', 'Organization', 'Service', 'Group', 'Application', 'Tombstone'];
@ -142,35 +142,35 @@ class ActivityPub
return []; return [];
} }
$profile = ['network' => Protocol::ACTIVITYPUB]; $profile = ['network' => Protocol::ACTIVITYPUB];
$profile['nick'] = $apcontact['nick']; $profile['nick'] = $apcontact['nick'];
$profile['name'] = $apcontact['name']; $profile['name'] = $apcontact['name'];
$profile['guid'] = $apcontact['uuid']; $profile['guid'] = $apcontact['uuid'];
$profile['url'] = $apcontact['url']; $profile['url'] = $apcontact['url'];
$profile['addr'] = $apcontact['addr']; $profile['addr'] = $apcontact['addr'];
$profile['alias'] = $apcontact['alias']; $profile['alias'] = $apcontact['alias'];
$profile['following'] = $apcontact['following']; $profile['following'] = $apcontact['following'];
$profile['followers'] = $apcontact['followers']; $profile['followers'] = $apcontact['followers'];
$profile['inbox'] = $apcontact['inbox']; $profile['inbox'] = $apcontact['inbox'];
$profile['outbox'] = $apcontact['outbox']; $profile['outbox'] = $apcontact['outbox'];
$profile['sharedinbox'] = $apcontact['sharedinbox']; $profile['sharedinbox'] = $apcontact['sharedinbox'];
$profile['photo'] = $apcontact['photo']; $profile['photo'] = $apcontact['photo'];
$profile['header'] = $apcontact['header']; $profile['header'] = $apcontact['header'];
$profile['account-type'] = self::getAccountType($apcontact); $profile['account-type'] = self::getAccountType($apcontact);
$profile['community'] = ($profile['account-type'] == User::ACCOUNT_TYPE_COMMUNITY); $profile['community'] = ($profile['account-type'] == User::ACCOUNT_TYPE_COMMUNITY);
// $profile['keywords'] // $profile['keywords']
// $profile['location'] // $profile['location']
$profile['about'] = $apcontact['about']; $profile['about'] = $apcontact['about'];
$profile['xmpp'] = $apcontact['xmpp']; $profile['xmpp'] = $apcontact['xmpp'];
$profile['matrix'] = $apcontact['matrix']; $profile['matrix'] = $apcontact['matrix'];
$profile['batch'] = $apcontact['sharedinbox']; $profile['batch'] = $apcontact['sharedinbox'];
$profile['notify'] = $apcontact['inbox']; $profile['notify'] = $apcontact['inbox'];
$profile['poll'] = $apcontact['outbox']; $profile['poll'] = $apcontact['outbox'];
$profile['pubkey'] = $apcontact['pubkey']; $profile['pubkey'] = $apcontact['pubkey'];
$profile['subscribe'] = $apcontact['subscribe']; $profile['subscribe'] = $apcontact['subscribe'];
$profile['manually-approve'] = $apcontact['manually-approve']; $profile['manually-approve'] = $apcontact['manually-approve'];
$profile['baseurl'] = $apcontact['baseurl']; $profile['baseurl'] = $apcontact['baseurl'];
$profile['gsid'] = $apcontact['gsid']; $profile['gsid'] = $apcontact['gsid'];
if (!is_null($apcontact['discoverable'])) { if (!is_null($apcontact['discoverable'])) {
$profile['hide'] = !$apcontact['discoverable']; $profile['hide'] = !$apcontact['discoverable'];
@ -307,7 +307,7 @@ class ActivityPub
$limited = DI::config()->get('system', 'limited_servers'); $limited = DI::config()->get('system', 'limited_servers');
if (!empty($limited)) { if (!empty($limited)) {
$servers = explode(',', str_replace(' ', '', $limited)); $servers = explode(',', str_replace(' ', '', $limited));
$host = parse_url($apcontact['baseurl'], PHP_URL_HOST); $host = parse_url($apcontact['baseurl'], PHP_URL_HOST);
if (!empty($host) && in_array($host, $servers)) { if (!empty($host) && in_array($host, $servers)) {
return false; return false;
} }

View file

@ -250,7 +250,7 @@ class ClientToServer
$item['uid'] = $uid; $item['uid'] = $uid;
$item['verb'] = Activity::POST; $item['verb'] = Activity::POST;
$item['contact-id'] = $owner['id']; $item['contact-id'] = $owner['id'];
$item['author-id'] = $item['owner-id'] = Contact::getPublicIdByUserId($uid); $item['author-id'] = $item['owner-id'] = Contact::getPublicIdByUserId($uid);
$item['title'] = $object_data['name']; $item['title'] = $object_data['name'];
$item['body'] = Markdown::toBBCode($object_data['content'] ?? ''); $item['body'] = Markdown::toBBCode($object_data['content'] ?? '');
$item['app'] = $application['name'] ?? 'API'; $item['app'] = $application['name'] ?? 'API';
@ -322,8 +322,10 @@ class ClientToServer
if (!empty($requester_id)) { if (!empty($requester_id)) {
$permissionSets = DI::permissionSet()->selectByContactId($requester_id, $owner['uid']); $permissionSets = DI::permissionSet()->selectByContactId($requester_id, $owner['uid']);
if (count($permissionSets) > 0) { if (count($permissionSets) > 0) {
$condition = ['psid' => array_merge($permissionSets->column('id'), $condition = ['psid' => array_merge(
[DI::permissionSet()->selectPublicForUser($owner['uid'])])]; $permissionSets->column('id'),
[DI::permissionSet()->selectPublicForUser($owner['uid'])]
)];
} }
} }
} }

View file

@ -115,7 +115,7 @@ class Delivery
} else { } else {
$data = ActivityPub\Transmitter::createCachedActivityFromItem($item_id); $data = ActivityPub\Transmitter::createCachedActivityFromItem($item_id);
if (!empty($data)) { if (!empty($data)) {
$timestamp = microtime(true); $timestamp = microtime(true);
try { try {
$response = HTTPSignature::post($data, $inbox, $owner); $response = HTTPSignature::post($data, $inbox, $owner);
$success = $response->isSuccess(); $success = $response->isSuccess();
@ -150,7 +150,7 @@ class Delivery
// Resubscribe to relay server upon client error // Resubscribe to relay server upon client error
if (!$serverfail && ($response->getReturnCode() >= 400) && ($response->getReturnCode() <= 499)) { if (!$serverfail && ($response->getReturnCode() >= 400) && ($response->getReturnCode() <= 499)) {
$actor = self:: fetchActorForRelayInbox($inbox); $actor = self::fetchActorForRelayInbox($inbox);
if (!empty($actor)) { if (!empty($actor)) {
$drop = !ActivityPub\Transmitter::sendRelayFollow($actor); $drop = !ActivityPub\Transmitter::sendRelayFollow($actor);
DI::logger()->notice('Resubscribed to relay', ['url' => $actor, 'success' => !$drop]); DI::logger()->notice('Resubscribed to relay', ['url' => $actor, 'success' => !$drop]);

View file

@ -93,7 +93,7 @@ class Processor
private static function processLanguages(array $languages): string private static function processLanguages(array $languages): string
{ {
$codes = array_keys($languages); $codes = array_keys($languages);
$lang = []; $lang = [];
foreach ($codes as $code) { foreach ($codes as $code) {
$lang[$code] = 1; $lang[$code] = 1;
} }
@ -145,15 +145,15 @@ class Processor
return; return;
} }
$data = ['uri-id' => $uriid]; $data = ['uri-id' => $uriid];
$data['type'] = Post\Media::UNKNOWN; $data['type'] = Post\Media::UNKNOWN;
$data['url'] = $attachment['url']; $data['url'] = $attachment['url'];
$data['mimetype'] = $attachment['mediaType'] ?? null; $data['mimetype'] = $attachment['mediaType'] ?? null;
$data['height'] = $attachment['height'] ?? null; $data['height'] = $attachment['height'] ?? null;
$data['width'] = $attachment['width'] ?? null; $data['width'] = $attachment['width'] ?? null;
$data['size'] = $attachment['size'] ?? null; $data['size'] = $attachment['size'] ?? null;
$data['preview'] = $attachment['image'] ?? null; $data['preview'] = $attachment['image'] ?? null;
$data['description'] = $attachment['name'] ?? null; $data['description'] = $attachment['name'] ?? null;
Post\Media::insert($data); Post\Media::insert($data);
} }
@ -229,7 +229,7 @@ class Processor
} }
$item['changed'] = DateTimeFormat::utcNow(); $item['changed'] = DateTimeFormat::utcNow();
$item['edited'] = DateTimeFormat::utc($activity['updated']); $item['edited'] = DateTimeFormat::utc($activity['updated']);
Post\Media::deleteByURIId($item['uri-id'], [Post\Media::AUDIO, Post\Media::VIDEO, Post\Media::IMAGE, Post\Media::HTML]); Post\Media::deleteByURIId($item['uri-id'], [Post\Media::AUDIO, Post\Media::VIDEO, Post\Media::IMAGE, Post\Media::HTML]);
$item = self::processContent($activity, $item); $item = self::processContent($activity, $item);
@ -265,11 +265,11 @@ class Processor
{ {
$event = DBA::selectFirst('event', [], ['id' => $event_id]); $event = DBA::selectFirst('event', [], ['id' => $event_id]);
$event['edited'] = DateTimeFormat::utc($activity['updated']); $event['edited'] = DateTimeFormat::utc($activity['updated']);
$event['summary'] = HTML::toBBCode($activity['name']); $event['summary'] = HTML::toBBCode($activity['name']);
$event['desc'] = HTML::toBBCode($activity['content']); $event['desc'] = HTML::toBBCode($activity['content']);
if (!empty($activity['start-time'])) { if (!empty($activity['start-time'])) {
$event['start'] = DateTimeFormat::utc($activity['start-time']); $event['start'] = DateTimeFormat::utc($activity['start-time']);
} }
if (!empty($activity['end-time'])) { if (!empty($activity['end-time'])) {
$event['finish'] = DateTimeFormat::utc($activity['end-time']); $event['finish'] = DateTimeFormat::utc($activity['end-time']);
@ -294,15 +294,15 @@ class Processor
*/ */
public static function createItem(array $activity, bool $fetch_parents): array public static function createItem(array $activity, bool $fetch_parents): array
{ {
$item = []; $item = [];
$item['verb'] = Activity::POST; $item['verb'] = Activity::POST;
$item['thr-parent'] = $activity['reply-to-id']; $item['thr-parent'] = $activity['reply-to-id'];
if ($activity['reply-to-id'] == $activity['id']) { if ($activity['reply-to-id'] == $activity['id']) {
$item['gravity'] = Item::GRAVITY_PARENT; $item['gravity'] = Item::GRAVITY_PARENT;
$item['object-type'] = Activity\ObjectType::NOTE; $item['object-type'] = Activity\ObjectType::NOTE;
} else { } else {
$item['gravity'] = Item::GRAVITY_COMMENT; $item['gravity'] = Item::GRAVITY_COMMENT;
$item['object-type'] = Activity\ObjectType::COMMENT; $item['object-type'] = Activity\ObjectType::COMMENT;
} }
@ -318,14 +318,14 @@ class Processor
$conversation = Post::selectFirstThread(['uri'], ['context' => $item['context']]); $conversation = Post::selectFirstThread(['uri'], ['context' => $item['context']]);
if (!empty($conversation)) { if (!empty($conversation)) {
DI::logger()->debug('Got context', ['context' => $item['context'], 'parent' => $conversation]); DI::logger()->debug('Got context', ['context' => $item['context'], 'parent' => $conversation]);
$item['parent-uri'] = $conversation['uri']; $item['parent-uri'] = $conversation['uri'];
$item['parent-uri-id'] = ItemURI::getIdByURI($item['parent-uri']); $item['parent-uri-id'] = ItemURI::getIdByURI($item['parent-uri']);
} }
} elseif (!empty($item['conversation'])) { } elseif (!empty($item['conversation'])) {
$conversation = Post::selectFirstThread(['uri'], ['conversation' => $item['conversation']]); $conversation = Post::selectFirstThread(['uri'], ['conversation' => $item['conversation']]);
if (!empty($conversation)) { if (!empty($conversation)) {
DI::logger()->debug('Got conversation', ['conversation' => $item['conversation'], 'parent' => $conversation]); DI::logger()->debug('Got conversation', ['conversation' => $item['conversation'], 'parent' => $conversation]);
$item['parent-uri'] = $conversation['uri']; $item['parent-uri'] = $conversation['uri'];
$item['parent-uri-id'] = ItemURI::getIdByURI($item['parent-uri']); $item['parent-uri-id'] = ItemURI::getIdByURI($item['parent-uri']);
} }
} else { } else {
@ -360,11 +360,11 @@ class Processor
return []; return [];
} }
$item['network'] = Protocol::ACTIVITYPUB; $item['network'] = Protocol::ACTIVITYPUB;
$item['author-link'] = $activity['author']; $item['author-link'] = $activity['author'];
$item['author-id'] = Contact::getIdForURL($activity['author']); $item['author-id'] = Contact::getIdForURL($activity['author']);
$item['owner-link'] = $activity['actor']; $item['owner-link'] = $activity['actor'];
$item['owner-id'] = Contact::getIdForURL($activity['actor']); $item['owner-id'] = Contact::getIdForURL($activity['actor']);
if (in_array(0, $activity['receiver']) && !empty($activity['unlisted'])) { if (in_array(0, $activity['receiver']) && !empty($activity['unlisted'])) {
$item['private'] = Item::UNLISTED; $item['private'] = Item::UNLISTED;
@ -412,7 +412,7 @@ class Processor
if (!empty($activity['thread-completion'])) { if (!empty($activity['thread-completion'])) {
if ($activity['thread-completion'] != $item['owner-id']) { if ($activity['thread-completion'] != $item['owner-id']) {
$actor = Contact::getById($activity['thread-completion'], ['url']); $actor = Contact::getById($activity['thread-completion'], ['url']);
$item['causer-link'] = $actor['url']; $item['causer-link'] = $actor['url'];
$item['causer-id'] = $activity['thread-completion']; $item['causer-id'] = $activity['thread-completion'];
DI::logger()->info('Use inherited actor as causer.', ['id' => $item['owner-id'], 'activity' => $activity['thread-completion'], 'owner' => $item['owner-link'], 'actor' => $actor['url']]); DI::logger()->info('Use inherited actor as causer.', ['id' => $item['owner-id'], 'activity' => $activity['thread-completion'], 'owner' => $item['owner-link'], 'actor' => $actor['url']]);
@ -461,7 +461,7 @@ class Processor
$item['causer-id'] = Contact::getIdForURL($item['causer-link']); $item['causer-id'] = Contact::getIdForURL($item['causer-link']);
} }
$item['uri'] = $activity['id']; $item['uri'] = $activity['id'];
$item['sensitive'] = $activity['sensitive']; $item['sensitive'] = $activity['sensitive'];
if (empty($activity['published']) || empty($activity['updated'])) { if (empty($activity['published']) || empty($activity['updated'])) {
@ -469,9 +469,9 @@ class Processor
} }
$item['created'] = DateTimeFormat::utc($activity['published'] ?? 'now'); $item['created'] = DateTimeFormat::utc($activity['published'] ?? 'now');
$item['edited'] = DateTimeFormat::utc($activity['updated'] ?? 'now'); $item['edited'] = DateTimeFormat::utc($activity['updated'] ?? 'now');
$guid = $activity['sc:identifier'] ?: self::getGUIDByURL($item['uri']); $guid = $activity['sc:identifier'] ?: self::getGUIDByURL($item['uri']);
$item['guid'] = $activity['diaspora:guid'] ?: $guid; $item['guid'] = $activity['diaspora:guid'] ?: $guid;
$item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]); $item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
if (empty($item['uri-id'])) { if (empty($item['uri-id'])) {
@ -525,7 +525,7 @@ class Processor
$replies[] = $item['parent-uri']; $replies[] = $item['parent-uri'];
} }
$condition = DBA::mergeConditions(['uri' => $replies], ["`replies-id` IS NOT NULL"]); $condition = DBA::mergeConditions(['uri' => $replies], ["`replies-id` IS NOT NULL"]);
$posts = Post::select(['replies', 'replies-id'], $condition); $posts = Post::select(['replies', 'replies-id'], $condition);
while ($post = Post::fetch($posts)) { while ($post = Post::fetch($posts)) {
$cachekey = 'Processor-CreateItem-Replies-' . $post['replies-id']; $cachekey = 'Processor-CreateItem-Replies-' . $post['replies-id'];
if (!DI::cache()->get($cachekey)) { if (!DI::cache()->get($cachekey)) {
@ -614,7 +614,7 @@ class Processor
DI::logger()->notice('Fetching is done by worker.', ['parent' => $activity['reply-to-id'], 'recursion-depth' => $recursion_depth]); DI::logger()->notice('Fetching is done by worker.', ['parent' => $activity['reply-to-id'], 'recursion-depth' => $recursion_depth]);
Fetch::add($activity['reply-to-id']); Fetch::add($activity['reply-to-id']);
$activity['recursion-depth'] = 0; $activity['recursion-depth'] = 0;
$wid = Worker::add(Worker::PRIORITY_HIGH, 'FetchMissingActivity', $activity['reply-to-id'], $activity, '', Receiver::COMPLETION_ASYNC); $wid = Worker::add(Worker::PRIORITY_HIGH, 'FetchMissingActivity', $activity['reply-to-id'], $activity, '', Receiver::COMPLETION_ASYNC);
Fetch::setWorkerId($activity['reply-to-id'], $wid); Fetch::setWorkerId($activity['reply-to-id'], $wid);
} else { } else {
DI::logger()->debug('Activity will already be fetched via a worker.', ['url' => $activity['reply-to-id']]); DI::logger()->debug('Activity will already be fetched via a worker.', ['url' => $activity['reply-to-id']]);
@ -676,7 +676,7 @@ class Processor
{ {
$owner = Contact::getIdForURL($activity['actor']); $owner = Contact::getIdForURL($activity['actor']);
DI::logger()->info('Deleting item', ['object' => $activity['object_id'], 'owner' => $owner]); DI::logger()->info('Deleting item', ['object' => $activity['object_id'], 'owner' => $owner]);
Item::markForDeletion(['uri' => $activity['object_id'], 'owner-id' => $owner]); Item::markForDeletion(['uri' => $activity['object_id'], 'owner-id' => $owner]);
Queue::remove($activity); Queue::remove($activity);
} }
@ -722,15 +722,15 @@ class Processor
public static function createActivity(array $activity, string $verb) public static function createActivity(array $activity, string $verb)
{ {
$activity['reply-to-id'] = $activity['object_id']; $activity['reply-to-id'] = $activity['object_id'];
$item = self::createItem($activity, false); $item = self::createItem($activity, false);
if (empty($item)) { if (empty($item)) {
DI::logger()->debug('Activity was not prepared', ['id' => $activity['object_id']]); DI::logger()->debug('Activity was not prepared', ['id' => $activity['object_id']]);
return; return;
} }
$item['verb'] = $verb; $item['verb'] = $verb;
$item['thr-parent'] = $activity['object_id']; $item['thr-parent'] = $activity['object_id'];
$item['gravity'] = Item::GRAVITY_ACTIVITY; $item['gravity'] = Item::GRAVITY_ACTIVITY;
unset($item['post-type']); unset($item['post-type']);
$item['object-type'] = Activity\ObjectType::NOTE; $item['object-type'] = Activity\ObjectType::NOTE;
@ -830,10 +830,10 @@ class Processor
*/ */
public static function createEvent(array $activity, array $item): int public static function createEvent(array $activity, array $item): int
{ {
$event['summary'] = HTML::toBBCode($activity['name'] ?: $activity['summary']); $event['summary'] = HTML::toBBCode($activity['name'] ?: $activity['summary']);
$event['desc'] = HTML::toBBCode($activity['content'] ?? ''); $event['desc'] = HTML::toBBCode($activity['content'] ?? '');
if (!empty($activity['start-time'])) { if (!empty($activity['start-time'])) {
$event['start'] = DateTimeFormat::utc($activity['start-time']); $event['start'] = DateTimeFormat::utc($activity['start-time']);
} }
if (!empty($activity['end-time'])) { if (!empty($activity['end-time'])) {
$event['finish'] = DateTimeFormat::utc($activity['end-time']); $event['finish'] = DateTimeFormat::utc($activity['end-time']);
@ -876,17 +876,17 @@ class Processor
{ {
if (!empty($activity['mediatype']) && ($activity['mediatype'] == 'text/markdown')) { if (!empty($activity['mediatype']) && ($activity['mediatype'] == 'text/markdown')) {
$item['title'] = strip_tags($activity['name'] ?? ''); $item['title'] = strip_tags($activity['name'] ?? '');
$content = Markdown::toBBCode($activity['content'] ?? ''); $content = Markdown::toBBCode($activity['content'] ?? '');
} elseif (!empty($activity['mediatype']) && ($activity['mediatype'] == 'text/bbcode')) { } elseif (!empty($activity['mediatype']) && ($activity['mediatype'] == 'text/bbcode')) {
$item['title'] = $activity['name'] ?? ''; $item['title'] = $activity['name'] ?? '';
$content = $activity['content'] ?? ''; $content = $activity['content'] ?? '';
} else { } else {
// By default assume "text/html" // By default assume "text/html"
$item['title'] = HTML::toBBCode($activity['name'] ?? ''); $item['title'] = HTML::toBBCode($activity['name'] ?? '');
$content = HTML::toBBCode($activity['content'] ?? ''); $content = HTML::toBBCode($activity['content'] ?? '');
} }
$item['title'] = trim(BBCode::toPlaintext($item['title'])); $item['title'] = trim(BBCode::toPlaintext($item['title']));
$item['content-warning'] = HTML::toBBCode($activity['summary'] ?? ''); $item['content-warning'] = HTML::toBBCode($activity['summary'] ?? '');
if (!empty($activity['languages'])) { if (!empty($activity['languages'])) {
@ -904,7 +904,7 @@ class Processor
if (!empty($activity['quote-url'])) { if (!empty($activity['quote-url'])) {
$id = Item::fetchByLink($activity['quote-url'], 0, ActivityPub\Receiver::COMPLETION_ASYNC); $id = Item::fetchByLink($activity['quote-url'], 0, ActivityPub\Receiver::COMPLETION_ASYNC);
if ($id) { if ($id) {
$shared_item = Post::selectFirst(['uri-id'], ['id' => $id]); $shared_item = Post::selectFirst(['uri-id'], ['id' => $id]);
$item['quote-uri-id'] = $shared_item['uri-id']; $item['quote-uri-id'] = $shared_item['uri-id'];
DI::logger()->debug('Quote is found', ['guid' => $item['guid'], 'uri-id' => $item['uri-id'], 'quote' => $activity['quote-url'], 'quote-uri-id' => $item['quote-uri-id']]); DI::logger()->debug('Quote is found', ['guid' => $item['guid'], 'uri-id' => $item['uri-id'], 'quote' => $activity['quote-url'], 'quote-uri-id' => $item['quote-uri-id']]);
} elseif ($uri_id = ItemURI::getIdByURI($activity['quote-url'], false)) { } elseif ($uri_id = ItemURI::getIdByURI($activity['quote-url'], false)) {
@ -919,7 +919,7 @@ class Processor
} }
if (!empty($activity['source'])) { if (!empty($activity['source'])) {
$item['body'] = $activity['source']; $item['body'] = $activity['source'];
$item['raw-body'] = $content; $item['raw-body'] = $content;
$quote_uri_id = Item::getQuoteUriId($item['body']); $quote_uri_id = Item::getQuoteUriId($item['body']);
@ -1093,7 +1093,7 @@ class Processor
return; return;
} }
$stored = false; $stored = false;
$success = false; $success = false;
ksort($activity['receiver']); ksort($activity['receiver']);
@ -1194,14 +1194,16 @@ class Processor
if ((DI::pConfig()->get($receiver, 'system', 'accept_only_sharer') != Item::COMPLETION_LIKE) if ((DI::pConfig()->get($receiver, 'system', 'accept_only_sharer') != Item::COMPLETION_LIKE)
&& in_array($activity['thread-children-type'] ?? '', Receiver::ACTIVITY_TYPES)) { && in_array($activity['thread-children-type'] ?? '', Receiver::ACTIVITY_TYPES)) {
DI::logger()->info('Top level post from thread completion from a non sharer had been initiated via an activity, ignoring', DI::logger()->info(
['type' => $activity['thread-children-type'], 'user' => $item['uid'], 'causer' => $item['causer-link'], 'author' => $activity['author'], 'url' => $item['uri']]); 'Top level post from thread completion from a non sharer had been initiated via an activity, ignoring',
['type' => $activity['thread-children-type'], 'user' => $item['uid'], 'causer' => $item['causer-link'], 'author' => $activity['author'], 'url' => $item['uri']]
);
continue; continue;
} }
} }
$isGroup = false; $isGroup = false;
$user = User::getById($receiver, ['account-type']); $user = User::getById($receiver, ['account-type']);
if (!empty($user['account-type'])) { if (!empty($user['account-type'])) {
$isGroup = ($user['account-type'] == User::ACCOUNT_TYPE_COMMUNITY); $isGroup = ($user['account-type'] == User::ACCOUNT_TYPE_COMMUNITY);
} }
@ -1310,7 +1312,7 @@ class Processor
if (Post::exists(['uri-id' => $item['parent-uri-id'], 'uid' => $receiver])) { if (Post::exists(['uri-id' => $item['parent-uri-id'], 'uid' => $receiver])) {
$has_parents = true; $has_parents = true;
} elseif ($add_parent && Post::exists(['uri-id' => $item['parent-uri-id'], 'uid' => 0])) { } elseif ($add_parent && Post::exists(['uri-id' => $item['parent-uri-id'], 'uid' => 0])) {
$stored = Item::storeForUserByUriId($item['parent-uri-id'], $receiver, $fields); $stored = Item::storeForUserByUriId($item['parent-uri-id'], $receiver, $fields);
$has_parents = (bool)$stored; $has_parents = (bool)$stored;
if ($stored) { if ($stored) {
DI::logger()->notice('Inserted missing parent post', ['stored' => $stored, 'uid' => $receiver, 'parent' => $item['parent-uri']]); DI::logger()->notice('Inserted missing parent post', ['stored' => $stored, 'uid' => $receiver, 'parent' => $item['parent-uri']]);
@ -1329,7 +1331,7 @@ class Processor
if (Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => $receiver])) { if (Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => $receiver])) {
$has_parents = true; $has_parents = true;
} elseif (($has_parents || $add_parent) && Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => 0])) { } elseif (($has_parents || $add_parent) && Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => 0])) {
$stored = Item::storeForUserByUriId($item['thr-parent-id'], $receiver, $fields); $stored = Item::storeForUserByUriId($item['thr-parent-id'], $receiver, $fields);
$has_parents = $has_parents || (bool)$stored; $has_parents = $has_parents || (bool)$stored;
if ($stored) { if ($stored) {
DI::logger()->notice('Inserted missing thread parent post', ['stored' => $stored, 'uid' => $receiver, 'thread-parent' => $item['thr-parent']]); DI::logger()->notice('Inserted missing thread parent post', ['stored' => $stored, 'uid' => $receiver, 'thread-parent' => $item['thr-parent']]);
@ -1462,23 +1464,23 @@ class Processor
DI::logger()->info('Direct Message', $item); DI::logger()->info('Direct Message', $item);
$msg = []; $msg = [];
$msg['uid'] = $item['uid']; $msg['uid'] = $item['uid'];
$msg['contact-id'] = $item['contact-id']; $msg['contact-id'] = $item['contact-id'];
$contact = Contact::getById($item['contact-id'], ['name', 'url', 'photo']); $contact = Contact::getById($item['contact-id'], ['name', 'url', 'photo']);
$msg['from-name'] = $contact['name']; $msg['from-name'] = $contact['name'];
$msg['from-url'] = $contact['url']; $msg['from-url'] = $contact['url'];
$msg['from-photo'] = $contact['photo']; $msg['from-photo'] = $contact['photo'];
$msg['uri'] = $item['uri']; $msg['uri'] = $item['uri'];
$msg['created'] = $item['created']; $msg['created'] = $item['created'];
$parent = DBA::selectFirst('mail', ['parent-uri', 'title'], ['uri' => $item['thr-parent']]); $parent = DBA::selectFirst('mail', ['parent-uri', 'title'], ['uri' => $item['thr-parent']]);
if (DBA::isResult($parent)) { if (DBA::isResult($parent)) {
$msg['parent-uri'] = $parent['parent-uri']; $msg['parent-uri'] = $parent['parent-uri'];
$msg['title'] = $parent['title']; $msg['title'] = $parent['title'];
} else { } else {
$msg['parent-uri'] = $item['thr-parent']; $msg['parent-uri'] = $item['thr-parent'];
@ -1587,7 +1589,7 @@ class Processor
public static function fetchCachedActivity(string $url, int $uid): array public static function fetchCachedActivity(string $url, int $uid): array
{ {
$cachekey = self::CACHEKEY_FETCH_ACTIVITY . $uid . ':' . hash('sha256', $url); $cachekey = self::CACHEKEY_FETCH_ACTIVITY . $uid . ':' . hash('sha256', $url);
$object = DI::cache()->get($cachekey); $object = DI::cache()->get($cachekey);
if (!is_null($object)) { if (!is_null($object)) {
if (!empty($object)) { if (!empty($object)) {
@ -1709,7 +1711,7 @@ class Processor
$signer[] = $object_actor; $signer[] = $object_actor;
if (!empty($child['author'])) { if (!empty($child['author'])) {
$actor = $child['author']; $actor = $child['author'];
$signer[] = $actor; $signer[] = $actor;
} else { } else {
$actor = $object_actor; $actor = $object_actor;
@ -1740,7 +1742,7 @@ class Processor
} }
$ldactivity['recursion-depth'] = !empty($child['recursion-depth']) ? $child['recursion-depth'] + 1 : 0; $ldactivity['recursion-depth'] = !empty($child['recursion-depth']) ? $child['recursion-depth'] + 1 : 0;
$ldactivity['children'] = $child['children'] ?? []; $ldactivity['children'] = $child['children'] ?? [];
$ldactivity['callstack'] = $child['callstack'] ?? []; $ldactivity['callstack'] = $child['callstack'] ?? [];
// This check is mostly superfluous, since there are similar checks before. This covers the case, when the fetched id doesn't match the url // This check is mostly superfluous, since there are similar checks before. This covers the case, when the fetched id doesn't match the url
if (in_array($activity['id'], $ldactivity['children'])) { if (in_array($activity['id'], $ldactivity['children'])) {
@ -1801,7 +1803,7 @@ class Processor
} }
} }
$callstack = array_slice(array_column(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 'function'), 1); $callstack = array_slice(array_column(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 'function'), 1);
$system_count = 0; $system_count = 0;
foreach ($callstack as $function) { foreach ($callstack as $function) {
if ($function == __FUNCTION__) { if ($function == __FUNCTION__) {
@ -1829,7 +1831,7 @@ class Processor
if (is_array($reply)) { if (is_array($reply)) {
$ldobject = JsonLD::compact($reply); $ldobject = JsonLD::compact($reply);
$id = JsonLD::fetchElement($ldobject, '@id'); $id = JsonLD::fetchElement($ldobject, '@id');
if (Processor::alreadyKnown($id, $child['id'] ?? '')) { if (Processor::alreadyKnown($id, $child['id'] ?? '')) {
continue; continue;
} }
@ -1915,7 +1917,7 @@ class Processor
$actor = JsonLD::fetchElement($ldobject, 'as:actor', '@id'); $actor = JsonLD::fetchElement($ldobject, 'as:actor', '@id');
$attributed_to = JsonLD::fetchElement($ldobject, 'as:attributedTo', '@id'); $attributed_to = JsonLD::fetchElement($ldobject, 'as:attributedTo', '@id');
$id_host = parse_url($id, PHP_URL_HOST); $id_host = parse_url($id, PHP_URL_HOST);
if (!empty($actor) && !in_array($type, Receiver::CONTENT_TYPES) && !empty($object_id)) { if (!empty($actor) && !in_array($type, Receiver::CONTENT_TYPES) && !empty($object_id)) {
$actor_host = parse_url($actor, PHP_URL_HOST); $actor_host = parse_url($actor, PHP_URL_HOST);
@ -1951,17 +1953,17 @@ class Processor
$published = DateTimeFormat::utcNow(); $published = DateTimeFormat::utcNow();
} }
$activity = []; $activity = [];
$activity['@context'] = $object['@context'] ?? ActivityPub::CONTEXT; $activity['@context'] = $object['@context'] ?? ActivityPub::CONTEXT;
unset($object['@context']); unset($object['@context']);
$activity['id'] = $object['id']; $activity['id'] = $object['id'];
$activity['to'] = $object['to'] ?? []; $activity['to'] = $object['to'] ?? [];
$activity['cc'] = $object['cc'] ?? []; $activity['cc'] = $object['cc'] ?? [];
$activity['audience'] = $object['audience'] ?? []; $activity['audience'] = $object['audience'] ?? [];
$activity['actor'] = $actor; $activity['actor'] = $actor;
$activity['object'] = $object; $activity['object'] = $object;
$activity['published'] = $published; $activity['published'] = $published;
$activity['type'] = 'Create'; $activity['type'] = 'Create';
return $activity; return $activity;
} }
@ -1983,16 +1985,16 @@ class Processor
$id = JsonLD::fetchElement($activity, 'as:object', '@id'); $id = JsonLD::fetchElement($activity, 'as:object', '@id');
$replyto = JsonLD::fetchElement($activity['as:object'], 'as:inReplyTo', '@id'); $replyto = JsonLD::fetchElement($activity['as:object'], 'as:inReplyTo', '@id');
$uriid = ItemURI::getIdByURI($replyto ?? ''); $uriid = ItemURI::getIdByURI($replyto ?? '');
if (Post::exists(['uri-id' => $uriid])) { if (Post::exists(['uri-id' => $uriid])) {
DI::logger()->info('Post is a reply to an existing post - accepted', ['id' => $id, 'uri-id' => $uriid, 'replyto' => $replyto]); DI::logger()->info('Post is a reply to an existing post - accepted', ['id' => $id, 'uri-id' => $uriid, 'replyto' => $replyto]);
return true; return true;
} }
$attributed_to = JsonLD::fetchElement($activity['as:object'], 'as:attributedTo', '@id'); $attributed_to = JsonLD::fetchElement($activity['as:object'], 'as:attributedTo', '@id');
$authorid = Contact::getIdForURL($attributed_to); $authorid = Contact::getIdForURL($attributed_to);
$content = JsonLD::fetchElement($activity['as:object'], 'as:name', '@value') ?? ''; $content = JsonLD::fetchElement($activity['as:object'], 'as:name', '@value') ?? '';
$content .= ' ' . JsonLD::fetchElement($activity['as:object'], 'as:summary', '@value') ?? ''; $content .= ' ' . JsonLD::fetchElement($activity['as:object'], 'as:summary', '@value') ?? '';
$content .= ' ' . HTML::toBBCode(JsonLD::fetchElement($activity['as:object'], 'as:content', '@value') ?? ''); $content .= ' ' . HTML::toBBCode(JsonLD::fetchElement($activity['as:object'], 'as:content', '@value') ?? '');
@ -2007,7 +2009,7 @@ class Processor
} }
$messageTags = []; $messageTags = [];
$tags = Receiver::processTags(JsonLD::fetchElementArray($activity['as:object'], 'as:tag') ?? []); $tags = Receiver::processTags(JsonLD::fetchElementArray($activity['as:object'], 'as:tag') ?? []);
if (!empty($tags)) { if (!empty($tags)) {
foreach ($tags as $tag) { foreach ($tags as $tag) {
if (($tag['type'] != 'Hashtag') && !strpos($tag['type'], ':Hashtag') || empty($tag['name'])) { if (($tag['type'] != 'Hashtag') && !strpos($tag['type'], ':Hashtag') || empty($tag['name'])) {
@ -2047,13 +2049,13 @@ class Processor
*/ */
public static function getPostLanguages(array $activity): array public static function getPostLanguages(array $activity): array
{ {
$content = JsonLD::fetchElement($activity, 'as:content') ?? ''; $content = JsonLD::fetchElement($activity, 'as:content') ?? '';
$languages = JsonLD::fetchElementArray($activity, 'as:content', '@language') ?? []; $languages = JsonLD::fetchElementArray($activity, 'as:content', '@language') ?? [];
if (empty($languages)) { if (empty($languages)) {
return []; return [];
} }
$iso639 = new \Matriphe\ISO639\ISO639; $iso639 = new \Matriphe\ISO639\ISO639();
$result = []; $result = [];
foreach ($languages as $language) { foreach ($languages as $language) {
@ -2097,7 +2099,7 @@ class Processor
} }
$item = [ $item = [
'author-id' => Contact::getIdForURL($activity['actor']), 'author-id' => Contact::getIdForURL($activity['actor']),
'author-link' => $activity['actor'], 'author-link' => $activity['actor'],
]; ];
@ -2134,7 +2136,7 @@ class Processor
private static function transmitPendingEvents(int $cid, int $uid) private static function transmitPendingEvents(int $cid, int $uid)
{ {
$account = DBA::selectFirst('account-user-view', ['ap-inbox', 'ap-sharedinbox'], ['id' => $cid]); $account = DBA::selectFirst('account-user-view', ['ap-inbox', 'ap-sharedinbox'], ['id' => $cid]);
$inbox = $account['ap-sharedinbox'] ?: $account['ap-inbox']; $inbox = $account['ap-sharedinbox'] ?: $account['ap-inbox'];
$events = DBA::select('event', ['id'], ["`uid` = ? AND `start` > ? AND `type` != ?", $uid, DateTimeFormat::utcNow(), 'birthday']); $events = DBA::select('event', ['id'], ["`uid` = ? AND `start` > ? AND `type` != ?", $uid, DateTimeFormat::utcNow(), 'birthday']);
while ($event = DBA::fetch($events)) { while ($event = DBA::fetch($events)) {
@ -2334,7 +2336,7 @@ class Processor
$check_id = false; $check_id = false;
if (!empty($activity['object_actor'])) { if (!empty($activity['object_actor'])) {
$uid = User::getIdForURL($activity['object_actor']); $uid = User::getIdForURL($activity['object_actor']);
} elseif (!empty($activity['receiver']) && (count($activity['receiver']) == 1)) { } elseif (!empty($activity['receiver']) && (count($activity['receiver']) == 1)) {
$uid = array_shift($activity['receiver']); $uid = array_shift($activity['receiver']);
$check_id = true; $check_id = true;
@ -2610,9 +2612,9 @@ class Processor
*/ */
public static function addToCallstack(array $callstack): array public static function addToCallstack(array $callstack): array
{ {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$functions = array_slice(array_column($trace, 'function'), 1); $functions = array_slice(array_column($trace, 'function'), 1);
$function = array_shift($functions); $function = array_shift($functions);
if (in_array($function, $callstack)) { if (in_array($function, $callstack)) {
DI::logger()->notice('Callstack already contains "' . $function . '"', ['callstack' => $callstack]); DI::logger()->notice('Callstack already contains "' . $function . '"', ['callstack' => $callstack]);

View file

@ -192,7 +192,7 @@ class Queue
if (!empty($entry['wid'])) { if (!empty($entry['wid'])) {
$worker = DI::appHelper()->getQueue(); $worker = DI::appHelper()->getQueue();
$wid = $worker['id'] ?? 0; $wid = $worker['id'] ?? 0;
if ($entry['wid'] != $wid) { if ($entry['wid'] != $wid) {
$workerqueue = DBA::selectFirst('workerqueue', ['pid'], ['id' => $entry['wid'], 'done' => false]); $workerqueue = DBA::selectFirst('workerqueue', ['pid'], ['id' => $entry['wid'], 'done' => false]);
if (!empty($workerqueue['pid']) && posix_kill($workerqueue['pid'], 0)) { if (!empty($workerqueue['pid']) && posix_kill($workerqueue['pid'], 0)) {
@ -327,8 +327,8 @@ class Queue
return false; return false;
} }
$activity['recursion-depth'] = 0; $activity['recursion-depth'] = 0;
$activity['callstack'] = Processor::addToCallstack($activity['callstack'] ?? []); $activity['callstack'] = Processor::addToCallstack($activity['callstack'] ?? []);
$wid = Worker::add(Worker::PRIORITY_HIGH, 'FetchMissingActivity', $entry['in-reply-to-id'], $activity, '', Receiver::COMPLETION_ASYNC); $wid = Worker::add(Worker::PRIORITY_HIGH, 'FetchMissingActivity', $entry['in-reply-to-id'], $activity, '', Receiver::COMPLETION_ASYNC);
Fetch::setWorkerId($entry['in-reply-to-id'], $wid); Fetch::setWorkerId($entry['in-reply-to-id'], $wid);
DI::logger()->debug('Fetch missing activity', ['wid' => $wid, 'id' => $entry['activity-id'], 'reply-to-id' => $entry['in-reply-to-id']]); DI::logger()->debug('Fetch missing activity', ['wid' => $wid, 'id' => $entry['activity-id'], 'reply-to-id' => $entry['in-reply-to-id']]);
self::retrial($id); self::retrial($id);
@ -348,7 +348,7 @@ class Queue
*/ */
public static function processReplyByUri(string $uri, array $parent = []): int public static function processReplyByUri(string $uri, array $parent = []): int
{ {
$count = 0; $count = 0;
$entries = DBA::select('inbox-entry', ['id'], ["`in-reply-to-id` = ? AND `object-id` != ?", $uri, $uri]); $entries = DBA::select('inbox-entry', ['id'], ["`in-reply-to-id` = ? AND `object-id` != ?", $uri, $uri]);
while ($entry = DBA::fetch($entries)) { while ($entry = DBA::fetch($entries)) {
$count += 1; $count += 1;

View file

@ -80,7 +80,7 @@ class Transmitter
} }
foreach ($relays as $relay) { foreach ($relays as $relay) {
$contact = Contact::getByURLForUser($relay['url'], $item['uid'], false, ['id']); $contact = Contact::getByURLForUser($relay['url'], $item['uid'], false, ['id']);
$inboxes[$relay['batch']][] = $contact['id'] ?? 0; $inboxes[$relay['batch']][] = $contact['id'] ?? 0;
} }
return $inboxes; return $inboxes;
@ -100,7 +100,7 @@ class Transmitter
} }
$activity_id = self::activityIDFromContact($contact['id']); $activity_id = self::activityIDFromContact($contact['id']);
$success = self::sendActivity('Follow', $url, 0, $activity_id); $success = self::sendActivity('Follow', $url, 0, $activity_id);
if ($success) { if ($success) {
Contact::update(['rel' => Contact::FRIEND], ['id' => $contact['id']]); Contact::update(['rel' => Contact::FRIEND], ['id' => $contact['id']]);
} }
@ -147,18 +147,18 @@ class Transmitter
{ {
if (empty($page)) { if (empty($page)) {
$cachekey = self::CACHEKEY_CONTACTS . $module . ':' . $owner['uid']; $cachekey = self::CACHEKEY_CONTACTS . $module . ':' . $owner['uid'];
$result = DI::cache()->get($cachekey); $result = DI::cache()->get($cachekey);
if (!$nocache && !is_null($result)) { if (!$nocache && !is_null($result)) {
return $result; return $result;
} }
} }
$parameters = [ $parameters = [
'rel' => $rel, 'rel' => $rel,
'uid' => $owner['uid'], 'uid' => $owner['uid'],
'self' => false, 'self' => false,
'deleted' => false, 'deleted' => false,
'hidden' => false, 'hidden' => false,
'archive' => false, 'archive' => false,
'pending' => false, 'pending' => false,
'blocked' => false, 'blocked' => false,
@ -170,9 +170,9 @@ class Transmitter
$modulePath = '/' . $module . '/'; $modulePath = '/' . $module . '/';
$data = ['@context' => ActivityPub::CONTEXT]; $data = ['@context' => ActivityPub::CONTEXT];
$data['id'] = DI::baseUrl() . $modulePath . $owner['nickname']; $data['id'] = DI::baseUrl() . $modulePath . $owner['nickname'];
$data['type'] = 'OrderedCollection'; $data['type'] = 'OrderedCollection';
$data['totalItems'] = $total; $data['totalItems'] = $total;
if (!empty($page)) { if (!empty($page)) {
@ -199,7 +199,7 @@ class Transmitter
$data['first'] = DI::baseUrl() . $modulePath . $owner['nickname'] . '?page=1'; $data['first'] = DI::baseUrl() . $modulePath . $owner['nickname'] . '?page=1';
} else { } else {
$data['type'] = 'OrderedCollectionPage'; $data['type'] = 'OrderedCollectionPage';
$list = []; $list = [];
$contacts = DBA::select('contact', ['url'], $condition, ['limit' => [($page - 1) * 100, 100]]); $contacts = DBA::select('contact', ['url'], $condition, ['limit' => [($page - 1) * 100, 100]]);
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
@ -238,7 +238,7 @@ class Transmitter
{ {
if (empty($page)) { if (empty($page)) {
$cachekey = self::CACHEKEY_FEATURED . $owner['uid']; $cachekey = self::CACHEKEY_FEATURED . $owner['uid'];
$result = DI::cache()->get($cachekey); $result = DI::cache()->get($cachekey);
if (!$nocache && !is_null($result)) { if (!$nocache && !is_null($result)) {
return $result; return $result;
} }
@ -252,7 +252,7 @@ class Transmitter
]; ];
$condition = DBA::mergeConditions($condition, [ $condition = DBA::mergeConditions($condition, [
'uid' => $owner['uid'], 'uid' => $owner['uid'],
'author-id' => $owner_cid, 'author-id' => $owner_cid,
'private' => [Item::PUBLIC, Item::UNLISTED], 'private' => [Item::PUBLIC, Item::UNLISTED],
'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT], 'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT],
@ -265,9 +265,9 @@ class Transmitter
$count = Post::count($condition); $count = Post::count($condition);
$data = ['@context' => ActivityPub::CONTEXT]; $data = ['@context' => ActivityPub::CONTEXT];
$data['id'] = DI::baseUrl() . '/featured/' . $owner['nickname']; $data['id'] = DI::baseUrl() . '/featured/' . $owner['nickname'];
$data['type'] = 'OrderedCollection'; $data['type'] = 'OrderedCollection';
$data['totalItems'] = $count; $data['totalItems'] = $count;
if (!empty($page)) { if (!empty($page)) {
@ -278,7 +278,7 @@ class Transmitter
$items = Post::select(['id'], $condition, ['limit' => 20, 'order' => ['created' => true]]); $items = Post::select(['id'], $condition, ['limit' => 20, 'order' => ['created' => true]]);
} else { } else {
$data['type'] = 'OrderedCollectionPage'; $data['type'] = 'OrderedCollectionPage';
$items = Post::select(['id'], $condition, ['limit' => [($page - 1) * 20, 20], 'order' => ['created' => true]]); $items = Post::select(['id'], $condition, ['limit' => [($page - 1) * 20, 20], 'order' => ['created' => true]]);
} }
$list = []; $list = [];
@ -317,7 +317,7 @@ class Transmitter
return [ return [
'id' => (string)DI::baseUrl() . '/friendica', 'id' => (string)DI::baseUrl() . '/friendica',
'type' => 'Application', 'type' => 'Application',
'name' => App::PLATFORM . " '" . App::CODENAME . "' " . App::VERSION . '-' . DB_UPDATE_VERSION, 'name' => App::PLATFORM . " '" . App::CODENAME . "' " . App::VERSION . '-' . DB_UPDATE_VERSION,
'url' => (string)DI::baseUrl(), 'url' => (string)DI::baseUrl(),
]; ];
} }
@ -339,7 +339,7 @@ class Transmitter
throw new HTTPException\NotFoundException('User not found.'); throw new HTTPException\NotFoundException('User not found.');
} }
$data = ['@context' => ActivityPub::CONTEXT]; $data = ['@context' => ActivityPub::CONTEXT];
$data['id'] = $owner['url']; $data['id'] = $owner['url'];
if (!empty($owner['guid'])) { if (!empty($owner['guid'])) {
@ -360,11 +360,11 @@ class Transmitter
} }
$data['preferredUsername'] = $owner['nick']; $data['preferredUsername'] = $owner['nick'];
$data['name'] = $full ? $owner['name'] : $owner['nick']; $data['name'] = $full ? $owner['name'] : $owner['nick'];
if ($full && !empty($owner['country-name'] . $owner['region'] . $owner['locality'])) { if ($full && !empty($owner['country-name'] . $owner['region'] . $owner['locality'])) {
$data['vcard:hasAddress'] = [ $data['vcard:hasAddress'] = [
'@type' => 'vcard:Home', 'vcard:country-name' => $owner['country-name'], '@type' => 'vcard:Home', 'vcard:country-name' => $owner['country-name'],
'vcard:region' => $owner['region'], 'vcard:locality' => $owner['locality'] 'vcard:region' => $owner['region'], 'vcard:locality' => $owner['locality']
]; ];
} }
@ -384,12 +384,12 @@ class Transmitter
} }
} }
$data['url'] = $owner['url']; $data['url'] = $owner['url'];
$data['manuallyApprovesFollowers'] = in_array($owner['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP]); $data['manuallyApprovesFollowers'] = in_array($owner['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP]);
$data['discoverable'] = (bool)$owner['net-publish'] && $full; $data['discoverable'] = (bool)$owner['net-publish'] && $full;
$data['publicKey'] = [ $data['publicKey'] = [
'id' => $owner['url'] . '#main-key', 'id' => $owner['url'] . '#main-key',
'owner' => $owner['url'], 'owner' => $owner['url'],
'publicKeyPem' => $owner['pubkey'] 'publicKeyPem' => $owner['pubkey']
]; ];
$data['endpoints'] = ['sharedInbox' => DI::baseUrl() . '/inbox']; $data['endpoints'] = ['sharedInbox' => DI::baseUrl() . '/inbox'];
@ -420,8 +420,8 @@ class Transmitter
foreach (DI::profileField()->selectByContactId(0, $uid) as $profile_field) { foreach (DI::profileField()->selectByContactId(0, $uid) as $profile_field) {
$custom_fields[] = [ $custom_fields[] = [
'type' => 'PropertyValue', 'type' => 'PropertyValue',
'name' => $profile_field->label, 'name' => $profile_field->label,
'value' => BBCode::convertForUriId($owner['uri-id'], $profile_field->value) 'value' => BBCode::convertForUriId($owner['uri-id'], $profile_field->value)
]; ];
}; };
@ -446,7 +446,7 @@ class Transmitter
private static function getActorArrayByCid(int $cid): array private static function getActorArrayByCid(int $cid): array
{ {
$contact = Contact::getById($cid); $contact = Contact::getById($cid);
$data = [ $data = [
'id' => $contact['url'], 'id' => $contact['url'],
'type' => $data['type'] = ActivityPub::ACCOUNT_TYPES[$contact['contact-type']], 'type' => $data['type'] = ActivityPub::ACCOUNT_TYPES[$contact['contact-type']],
'url' => $contact['alias'], 'url' => $contact['alias'],
@ -473,12 +473,12 @@ class Transmitter
public static function getDeletedUser(string $username): array public static function getDeletedUser(string $username): array
{ {
return [ return [
'@context' => ActivityPub::CONTEXT, '@context' => ActivityPub::CONTEXT,
'id' => DI::baseUrl() . '/profile/' . $username, 'id' => DI::baseUrl() . '/profile/' . $username,
'type' => 'Tombstone', 'type' => 'Tombstone',
'published' => DateTimeFormat::utcNow(DateTimeFormat::ATOM), 'published' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
'updated' => DateTimeFormat::utcNow(DateTimeFormat::ATOM), 'updated' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
'deleted' => DateTimeFormat::utcNow(DateTimeFormat::ATOM), 'deleted' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
]; ];
} }
@ -513,7 +513,7 @@ class Transmitter
$parent_profile = APContact::getByURL($parent['author-link']); $parent_profile = APContact::getByURL($parent['author-link']);
$item_profile = APContact::getByURL($item['author-link']); $item_profile = APContact::getByURL($item['author-link']);
$exclude[] = $item['author-link']; $exclude[] = $item['author-link'];
if ($item['gravity'] == Item::GRAVITY_PARENT) { if ($item['gravity'] == Item::GRAVITY_PARENT) {
$exclude[] = $item['owner-link']; $exclude[] = $item['owner-link'];
@ -591,7 +591,7 @@ class Transmitter
$parent = Post::selectFirst(['causer-link', 'post-reason'], ['id' => $item['parent']]); $parent = Post::selectFirst(['causer-link', 'post-reason'], ['id' => $item['parent']]);
if (!empty($parent) && ($parent['post-reason'] == Item::PR_ANNOUNCEMENT) && !empty($parent['causer-link'])) { if (!empty($parent) && ($parent['post-reason'] == Item::PR_ANNOUNCEMENT) && !empty($parent['causer-link'])) {
$profile = APContact::getByURL($parent['causer-link'], false); $profile = APContact::getByURL($parent['causer-link'], false);
$is_group_thread = isset($profile['type']) && $profile['type'] == 'Group'; $is_group_thread = isset($profile['type']) && $profile['type'] == 'Group';
} else { } else {
$is_group_thread = false; $is_group_thread = false;
@ -607,7 +607,7 @@ class Transmitter
} }
$profile = APContact::getByURL($tag['url'], false); $profile = APContact::getByURL($tag['url'], false);
if (!empty($profile) && ($profile['type'] == 'Group')) { if (!empty($profile) && ($profile['type'] == 'Group')) {
$audience[] = $tag['url']; $audience[] = $tag['url'];
$is_group_thread = true; $is_group_thread = true;
} }
} }
@ -798,7 +798,7 @@ class Transmitter
} }
if (!empty($item['quote-uri-id']) && in_array($item['private'], [Item::PUBLIC, Item::UNLISTED])) { if (!empty($item['quote-uri-id']) && in_array($item['private'], [Item::PUBLIC, Item::UNLISTED])) {
$quoted = Post::selectFirst(['author-link'], ['uri-id' => $item['quote-uri-id']]); $quoted = Post::selectFirst(['author-link'], ['uri-id' => $item['quote-uri-id']]);
$profile = APContact::getByURL($quoted['author-link'], false); $profile = APContact::getByURL($quoted['author-link'], false);
if (!empty($profile)) { if (!empty($profile)) {
$data['cc'][] = $profile['url']; $data['cc'][] = $profile['url'];
@ -982,12 +982,12 @@ class Transmitter
public static function fetchTargetInboxesforUser(int $uid): array public static function fetchTargetInboxesforUser(int $uid): array
{ {
$condition = [ $condition = [
'uid' => $uid, 'uid' => $uid,
'self' => false, 'self' => false,
'archive' => false, 'archive' => false,
'pending' => false, 'pending' => false,
'blocked' => false, 'blocked' => false,
'network' => Protocol::FEDERATED, 'network' => Protocol::FEDERATED,
]; ];
if (!empty($uid)) { if (!empty($uid)) {
@ -1149,7 +1149,7 @@ class Transmitter
$mail['content-warning'] = $mail['title']; $mail['content-warning'] = $mail['title'];
$mail['title'] = ''; $mail['title'] = '';
} else { } else {
$mail['content-warning'] = ''; $mail['content-warning'] = '';
} }
$mail['sensitive'] = false; $mail['sensitive'] = false;
$mail['author-link'] = $mail['owner-link'] = $mail['from-url']; $mail['author-link'] = $mail['owner-link'] = $mail['from-url'];
@ -1196,12 +1196,12 @@ class Transmitter
$data = []; $data = [];
} }
$data['id'] = $mail['uri'] . '/Create'; $data['id'] = $mail['uri'] . '/Create';
$data['type'] = 'Create'; $data['type'] = 'Create';
$data['actor'] = $mail['author-link']; $data['actor'] = $mail['author-link'];
$data['published'] = DateTimeFormat::utc($mail['created'] . '+00:00', DateTimeFormat::ATOM); $data['published'] = DateTimeFormat::utc($mail['created'] . '+00:00', DateTimeFormat::ATOM);
$data['instrument'] = self::getService(); $data['instrument'] = self::getService();
$data = array_merge($data, self::createPermissionBlockForItem($mail, true)); $data = array_merge($data, self::createPermissionBlockForItem($mail, true));
if (empty($data['to']) && !empty($data['cc'])) { if (empty($data['to']) && !empty($data['cc'])) {
$data['to'] = $data['cc']; $data['to'] = $data['cc'];
@ -1215,7 +1215,7 @@ class Transmitter
unset($data['bcc']); unset($data['bcc']);
unset($data['audience']); unset($data['audience']);
$object['to'] = $data['to']; $object['to'] = $data['to'];
$object['tag'] = [['type' => 'Mention', 'href' => $object['to'][0], 'name' => '']]; $object['tag'] = [['type' => 'Mention', 'href' => $object['to'][0], 'name' => '']];
unset($object['cc']); unset($object['cc']);
@ -1403,7 +1403,8 @@ class Transmitter
} }
if ($type == 'Delete') { if ($type == 'Delete') {
$data['id'] = Item::newURI($item['guid']) . '/' . $type;; $data['id'] = Item::newURI($item['guid']) . '/' . $type;
;
} elseif (($item['gravity'] == Item::GRAVITY_ACTIVITY) && ($type != 'Undo')) { } elseif (($item['gravity'] == Item::GRAVITY_ACTIVITY) && ($type != 'Undo')) {
$data['id'] = $item['uri']; $data['id'] = $item['uri'];
} else { } else {
@ -1439,7 +1440,7 @@ class Transmitter
} elseif ($data['type'] == 'Announce') { } elseif ($data['type'] == 'Announce') {
if ($item['verb'] == ACTIVITY::ANNOUNCE) { if ($item['verb'] == ACTIVITY::ANNOUNCE) {
if ($announce_activity) { if ($announce_activity) {
$anounced_item = Post::selectFirst(['uid'], ['uri-id' => $item['thr-parent-id'], 'origin' => true]); $anounced_item = Post::selectFirst(['uid'], ['uri-id' => $item['thr-parent-id'], 'origin' => true]);
$data['object'] = self::createActivityFromUriId($item['thr-parent-id'], $anounced_item['uid'] ?? 0); $data['object'] = self::createActivityFromUriId($item['thr-parent-id'], $anounced_item['uid'] ?? 0);
unset($data['object']['@context']); unset($data['object']['@context']);
} else { } else {
@ -1505,7 +1506,7 @@ class Transmitter
} }
if (!empty($coord['lat']) && !empty($coord['lon'])) { if (!empty($coord['lat']) && !empty($coord['lon'])) {
$location['latitude'] = $coord['lat']; $location['latitude'] = $coord['lat'];
$location['longitude'] = $coord['lon']; $location['longitude'] = $coord['lon'];
} }
@ -1528,7 +1529,7 @@ class Transmitter
'name' => $name, 'name' => $name,
'icon' => [ 'icon' => [
'type' => 'Image', 'type' => 'Image',
'url' => $url, 'url' => $url,
], ],
]; ];
} }
@ -1550,7 +1551,7 @@ class Transmitter
$terms = Tag::getByURIId($item['uri-id'], [Tag::HASHTAG, Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION]); $terms = Tag::getByURIId($item['uri-id'], [Tag::HASHTAG, Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION]);
foreach ($terms as $term) { foreach ($terms as $term) {
if ($term['type'] == Tag::HASHTAG) { if ($term['type'] == Tag::HASHTAG) {
$url = DI::baseUrl() . '/search?tag=' . urlencode($term['name']); $url = DI::baseUrl() . '/search?tag=' . urlencode($term['name']);
$tags[] = ['type' => 'Hashtag', 'href' => $url, 'name' => '#' . $term['name']]; $tags[] = ['type' => 'Hashtag', 'href' => $url, 'name' => '#' . $term['name']];
} else { } else {
$contact = Contact::getByURL($term['url'], false, ['addr']); $contact = Contact::getByURL($term['url'], false, ['addr']);
@ -1606,10 +1607,10 @@ class Transmitter
$urls[] = $attachment['url']; $urls[] = $attachment['url'];
$attach = [ $attach = [
'type' => 'Document', 'type' => 'Document',
'mediaType' => $attachment['mimetype'], 'mediaType' => $attachment['mimetype'],
'url' => $attachment['url'], 'url' => $attachment['url'],
'name' => $attachment['description'] 'name' => $attachment['description']
]; ];
if (!empty($attachment['height'])) { if (!empty($attachment['height'])) {
@ -1692,9 +1693,9 @@ class Transmitter
*/ */
private static function createEvent(array $item): array private static function createEvent(array $item): array
{ {
$event = []; $event = [];
$event['name'] = $item['event-summary']; $event['name'] = $item['event-summary'];
$event['content'] = BBCode::convertForUriId($item['uri-id'], $item['event-desc'], BBCode::ACTIVITYPUB); $event['content'] = BBCode::convertForUriId($item['uri-id'], $item['event-desc'], BBCode::ACTIVITYPUB);
$event['startTime'] = DateTimeFormat::utc($item['event-start'], 'c'); $event['startTime'] = DateTimeFormat::utc($item['event-start'], 'c');
if (!$item['event-nofinish']) { if (!$item['event-nofinish']) {
@ -1702,7 +1703,7 @@ class Transmitter
} }
if (!empty($item['event-location'])) { if (!empty($item['event-location'])) {
$item['location'] = $item['event-location']; $item['location'] = $item['event-location'];
$event['location'] = self::createLocation($item); $event['location'] = self::createLocation($item);
} }
@ -1732,7 +1733,7 @@ class Transmitter
// But to not risk compatibility issues we currently perform the changes only for communities. // But to not risk compatibility issues we currently perform the changes only for communities.
if ($item['gravity'] == Item::GRAVITY_PARENT) { if ($item['gravity'] == Item::GRAVITY_PARENT) {
$isCommunityPost = !empty(Tag::getByURIId($item['uri-id'], [Tag::EXCLUSIVE_MENTION])); $isCommunityPost = !empty(Tag::getByURIId($item['uri-id'], [Tag::EXCLUSIVE_MENTION]));
$links = Post\Media::getByURIId($item['uri-id'], [Post\Media::HTML]); $links = Post\Media::getByURIId($item['uri-id'], [Post\Media::HTML]);
if ($isCommunityPost && (count($links) == 1)) { if ($isCommunityPost && (count($links) == 1)) {
$link = $links[0]['url']; $link = $links[0]['url'];
} }
@ -1763,10 +1764,10 @@ class Transmitter
$title = ''; $title = '';
break; break;
case ActivityPub::ARTICLE_EMBED_TITLE: case ActivityPub::ARTICLE_EMBED_TITLE:
$type = 'Note'; $type = 'Note';
$item['raw-body'] = '[b]' . $title . "[/b]\n\n" . $item['raw-body']; $item['raw-body'] = '[b]' . $title . "[/b]\n\n" . $item['raw-body'];
$item['body'] = '[b]' . $title . "[/b]\n\n" . $item['body']; $item['body'] = '[b]' . $title . "[/b]\n\n" . $item['body'];
$title = ''; $title = '';
break; break;
} }
} else { } else {
@ -1781,8 +1782,8 @@ class Transmitter
$type = 'Tombstone'; $type = 'Tombstone';
} }
$data = []; $data = [];
$data['id'] = $item['uri']; $data['id'] = $item['uri'];
$data['type'] = $type; $data['type'] = $type;
if ($item['deleted']) { if ($item['deleted']) {
@ -1796,7 +1797,7 @@ class Transmitter
} }
$data['diaspora:guid'] = $item['guid']; $data['diaspora:guid'] = $item['guid'];
$data['published'] = DateTimeFormat::utc($item['created'] . '+00:00', DateTimeFormat::ATOM); $data['published'] = DateTimeFormat::utc($item['created'] . '+00:00', DateTimeFormat::ATOM);
if ($item['created'] != $item['edited']) { if ($item['created'] != $item['edited']) {
$data['updated'] = DateTimeFormat::utc($item['edited'] . '+00:00', DateTimeFormat::ATOM); $data['updated'] = DateTimeFormat::utc($item['edited'] . '+00:00', DateTimeFormat::ATOM);
@ -1832,7 +1833,7 @@ class Transmitter
$item = Post\Media::addHTMLAttachmentToItem($item); $item = Post\Media::addHTMLAttachmentToItem($item);
$body = $item['body']; $body = $item['body'];
$emojis = []; $emojis = [];
if ($type == 'Note') { if ($type == 'Note') {
$body = $item['raw-body'] ?? self::removePictures($body); $body = $item['raw-body'] ?? self::removePictures($body);
@ -1876,10 +1877,10 @@ class Transmitter
if (!empty($item['quote-uri-id']) && ($item['quote-uri-id'] != $item['uri-id'])) { if (!empty($item['quote-uri-id']) && ($item['quote-uri-id'] != $item['uri-id'])) {
if (Post::exists(['uri-id' => $item['quote-uri-id'], 'network' => [Protocol::ACTIVITYPUB, Protocol::DFRN]])) { if (Post::exists(['uri-id' => $item['quote-uri-id'], 'network' => [Protocol::ACTIVITYPUB, Protocol::DFRN]])) {
$real_quote = true; $real_quote = true;
$data['_misskey_content'] = BBCode::removeSharedData($body); $data['_misskey_content'] = BBCode::removeSharedData($body);
$data['quoteUrl'] = $item['quote-uri']; $data['quoteUrl'] = $item['quote-uri'];
$body = DI::contentItem()->addShareLink($body, $item['quote-uri-id']); $body = DI::contentItem()->addShareLink($body, $item['quote-uri-id']);
} else { } else {
$body = DI::contentItem()->addSharedPost($item, $body); $body = DI::contentItem()->addSharedPost($item, $body);
} }
@ -1920,7 +1921,7 @@ class Transmitter
} }
$data['attachment'] = self::createAttachmentList($item); $data['attachment'] = self::createAttachmentList($item);
$data['tag'] = array_merge(self::createTagList($item, $data['quoteUrl'] ?? ''), $emojis); $data['tag'] = array_merge(self::createTagList($item, $data['quoteUrl'] ?? ''), $emojis);
if (empty($data['location']) && (!empty($item['coord']) || !empty($item['location']))) { if (empty($data['location']) && (!empty($item['coord']) || !empty($item['location']))) {
$data['location'] = self::createLocation($item); $data['location'] = self::createLocation($item);
@ -1978,10 +1979,10 @@ class Transmitter
$target = XML::parseString($item['target']); $target = XML::parseString($item['target']);
$activity['diaspora:guid'] = $item['guid']; $activity['diaspora:guid'] = $item['guid'];
$activity['actor'] = $item['author-link']; $activity['actor'] = $item['author-link'];
$activity['target'] = (string)$target->id; $activity['target'] = (string)$target->id;
$activity['summary'] = BBCode::toPlaintext($item['body']); $activity['summary'] = BBCode::toPlaintext($item['body']);
$activity['object'] = ['id' => (string)$object->id, 'type' => 'tag', 'name' => (string)$object->title, 'content' => (string)$object->content]; $activity['object'] = ['id' => (string)$object->id, 'type' => 'tag', 'name' => (string)$object->title, 'content' => (string)$object->content];
return $activity; return $activity;
} }
@ -1999,23 +2000,23 @@ class Transmitter
private static function createAnnounce(array $item, array $activity, bool $api_mode = false): array private static function createAnnounce(array $item, array $activity, bool $api_mode = false): array
{ {
$orig_body = $item['body']; $orig_body = $item['body'];
$announce = self::getAnnounceArray($item); $announce = self::getAnnounceArray($item);
if (empty($announce)) { if (empty($announce)) {
$activity['type'] = 'Create'; $activity['type'] = 'Create';
$activity['object'] = self::createNote($item, $api_mode); $activity['object'] = self::createNote($item, $api_mode);
return $activity; return $activity;
} }
if (empty($announce['comment'])) { if (empty($announce['comment'])) {
// Pure announce, without a quote // Pure announce, without a quote
$activity['type'] = 'Announce'; $activity['type'] = 'Announce';
$activity['object'] = $announce['object']['uri']; $activity['object'] = $announce['object']['uri'];
return $activity; return $activity;
} }
// Quote // Quote
$activity['type'] = 'Create'; $activity['type'] = 'Create';
$item['body'] = $announce['comment'] . "\n" . $announce['object']['plink']; $item['body'] = $announce['comment'] . "\n" . $announce['object']['plink'];
$activity['object'] = self::createNote($item, $api_mode); $activity['object'] = self::createNote($item, $api_mode);
/// @todo Finally decide how to implement this in AP. This is a possible way: /// @todo Finally decide how to implement this in AP. This is a possible way:
@ -2104,15 +2105,15 @@ class Transmitter
$suggestion = DI::fsuggest()->selectOneById($suggestion_id); $suggestion = DI::fsuggest()->selectOneById($suggestion_id);
$data = [ $data = [
'@context' => ActivityPub::CONTEXT, '@context' => ActivityPub::CONTEXT,
'id' => DI::baseUrl() . '/activity/' . System::createGUID(), 'id' => DI::baseUrl() . '/activity/' . System::createGUID(),
'type' => 'Announce', 'type' => 'Announce',
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => $suggestion->url, 'object' => $suggestion->url,
'content' => $suggestion->note, 'content' => $suggestion->note,
'instrument' => self::getService(), 'instrument' => self::getService(),
'to' => [ActivityPub::PUBLIC_COLLECTION], 'to' => [ActivityPub::PUBLIC_COLLECTION],
'cc' => [] 'cc' => []
]; ];
$signed = LDSignature::sign($data, $owner); $signed = LDSignature::sign($data, $owner);
@ -2132,15 +2133,15 @@ class Transmitter
public static function sendProfileRelocation(array $owner, string $inbox): bool public static function sendProfileRelocation(array $owner, string $inbox): bool
{ {
$data = [ $data = [
'@context' => ActivityPub::CONTEXT, '@context' => ActivityPub::CONTEXT,
'id' => DI::baseUrl() . '/activity/' . System::createGUID(), 'id' => DI::baseUrl() . '/activity/' . System::createGUID(),
'type' => 'dfrn:relocate', 'type' => 'dfrn:relocate',
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => $owner['url'], 'object' => $owner['url'],
'published' => DateTimeFormat::utcNow(DateTimeFormat::ATOM), 'published' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
'instrument' => self::getService(), 'instrument' => self::getService(),
'to' => [ActivityPub::PUBLIC_COLLECTION], 'to' => [ActivityPub::PUBLIC_COLLECTION],
'cc' => [] 'cc' => []
]; ];
$signed = LDSignature::sign($data, $owner); $signed = LDSignature::sign($data, $owner);
@ -2165,15 +2166,15 @@ class Transmitter
} }
$data = [ $data = [
'@context' => ActivityPub::CONTEXT, '@context' => ActivityPub::CONTEXT,
'id' => DI::baseUrl() . '/activity/' . System::createGUID(), 'id' => DI::baseUrl() . '/activity/' . System::createGUID(),
'type' => 'Delete', 'type' => 'Delete',
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => $owner['url'], 'object' => $owner['url'],
'published' => DateTimeFormat::utcNow(DateTimeFormat::ATOM), 'published' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
'instrument' => self::getService(), 'instrument' => self::getService(),
'to' => [ActivityPub::PUBLIC_COLLECTION], 'to' => [ActivityPub::PUBLIC_COLLECTION],
'cc' => [] 'cc' => []
]; ];
$signed = LDSignature::sign($data, $owner); $signed = LDSignature::sign($data, $owner);
@ -2197,15 +2198,15 @@ class Transmitter
$profile = APContact::getByURL($owner['url']); $profile = APContact::getByURL($owner['url']);
$data = [ $data = [
'@context' => ActivityPub::CONTEXT, '@context' => ActivityPub::CONTEXT,
'id' => DI::baseUrl() . '/activity/' . System::createGUID(), 'id' => DI::baseUrl() . '/activity/' . System::createGUID(),
'type' => 'Update', 'type' => 'Update',
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => self::getProfile($owner['uid']), 'object' => self::getProfile($owner['uid']),
'published' => DateTimeFormat::utcNow(DateTimeFormat::ATOM), 'published' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
'instrument' => self::getService(), 'instrument' => self::getService(),
'to' => [$profile['followers']], 'to' => [$profile['followers']],
'cc' => [] 'cc' => []
]; ];
$signed = LDSignature::sign($data, $owner); $signed = LDSignature::sign($data, $owner);
@ -2245,13 +2246,13 @@ class Transmitter
} }
$data = [ $data = [
'@context' => ActivityPub::CONTEXT, '@context' => ActivityPub::CONTEXT,
'id' => $id, 'id' => $id,
'type' => $activity, 'type' => $activity,
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => $profile['url'], 'object' => $profile['url'],
'instrument' => self::getService(), 'instrument' => self::getService(),
'to' => [$profile['url']], 'to' => [$profile['url']],
]; ];
DI::logger()->info('Sending activity ' . $activity . ' to ' . $target . ' for user ' . $uid); DI::logger()->info('Sending activity ' . $activity . ' to ' . $target . ' for user ' . $uid);
@ -2292,7 +2293,7 @@ class Transmitter
} }
$condition = [ $condition = [
'verb' => Activity::FOLLOW, 'uid' => 0, 'parent-uri' => $object, 'verb' => Activity::FOLLOW, 'uid' => 0, 'parent-uri' => $object,
'author-id' => Contact::getPublicIdByUserId($uid) 'author-id' => Contact::getPublicIdByUserId($uid)
]; ];
if (Post::exists($condition)) { if (Post::exists($condition)) {
@ -2303,13 +2304,13 @@ class Transmitter
$owner = User::getOwnerDataById($uid); $owner = User::getOwnerDataById($uid);
$data = [ $data = [
'@context' => ActivityPub::CONTEXT, '@context' => ActivityPub::CONTEXT,
'id' => DI::baseUrl() . '/activity/' . System::createGUID(), 'id' => DI::baseUrl() . '/activity/' . System::createGUID(),
'type' => 'Follow', 'type' => 'Follow',
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => $object, 'object' => $object,
'instrument' => self::getService(), 'instrument' => self::getService(),
'to' => [$profile['url']], 'to' => [$profile['url']],
]; ];
DI::logger()->info('Sending follow ' . $object . ' to ' . $target . ' for user ' . $uid); DI::logger()->info('Sending follow ' . $object . ' to ' . $target . ' for user ' . $uid);
@ -2344,17 +2345,17 @@ class Transmitter
$data = [ $data = [
'@context' => ActivityPub::CONTEXT, '@context' => ActivityPub::CONTEXT,
'id' => DI::baseUrl() . '/activity/' . System::createGUID(), 'id' => DI::baseUrl() . '/activity/' . System::createGUID(),
'type' => 'Accept', 'type' => 'Accept',
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => [ 'object' => [
'id' => $id, 'id' => $id,
'type' => 'Follow', 'type' => 'Follow',
'actor' => $profile['url'], 'actor' => $profile['url'],
'object' => $owner['url'] 'object' => $owner['url']
], ],
'instrument' => self::getService(), 'instrument' => self::getService(),
'to' => [$profile['url']], 'to' => [$profile['url']],
]; ];
DI::logger()->debug('Sending accept to ' . $target . ' for user ' . $uid . ' with id ' . $id); DI::logger()->debug('Sending accept to ' . $target . ' for user ' . $uid . ' with id ' . $id);
@ -2383,17 +2384,17 @@ class Transmitter
$data = [ $data = [
'@context' => ActivityPub::CONTEXT, '@context' => ActivityPub::CONTEXT,
'id' => DI::baseUrl() . '/activity/' . System::createGUID(), 'id' => DI::baseUrl() . '/activity/' . System::createGUID(),
'type' => 'Reject', 'type' => 'Reject',
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => [ 'object' => [
'id' => $objectId, 'id' => $objectId,
'type' => 'Follow', 'type' => 'Follow',
'actor' => $profile['url'], 'actor' => $profile['url'],
'object' => $owner['url'] 'object' => $owner['url']
], ],
'instrument' => self::getService(), 'instrument' => self::getService(),
'to' => [$profile['url']], 'to' => [$profile['url']],
]; ];
DI::logger()->debug('Sending reject to ' . $target . ' for user ' . $owner['uid'] . ' with id ' . $objectId); DI::logger()->debug('Sending reject to ' . $target . ' for user ' . $owner['uid'] . ' with id ' . $objectId);
@ -2430,17 +2431,17 @@ class Transmitter
$data = [ $data = [
'@context' => ActivityPub::CONTEXT, '@context' => ActivityPub::CONTEXT,
'id' => $objectId, 'id' => $objectId,
'type' => 'Undo', 'type' => 'Undo',
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => [ 'object' => [
'id' => $object_id, 'id' => $object_id,
'type' => 'Follow', 'type' => 'Follow',
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => $profile['url'] 'object' => $profile['url']
], ],
'instrument' => self::getService(), 'instrument' => self::getService(),
'to' => [$profile['url']], 'to' => [$profile['url']],
]; ];
DI::logger()->info('Sending undo to ' . $target . ' for user ' . $owner['uid'] . ' with id ' . $objectId); DI::logger()->info('Sending undo to ' . $target . ' for user ' . $owner['uid'] . ' with id ' . $objectId);
@ -2477,17 +2478,17 @@ class Transmitter
$data = [ $data = [
'@context' => ActivityPub::CONTEXT, '@context' => ActivityPub::CONTEXT,
'id' => $objectId, 'id' => $objectId,
'type' => 'Undo', 'type' => 'Undo',
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => [ 'object' => [
'id' => $object_id, 'id' => $object_id,
'type' => 'Block', 'type' => 'Block',
'actor' => $owner['url'], 'actor' => $owner['url'],
'object' => $profile['url'] 'object' => $profile['url']
], ],
'instrument' => self::getService(), 'instrument' => self::getService(),
'to' => [$profile['url']], 'to' => [$profile['url']],
]; ];
DI::logger()->info('Sending undo to ' . $target . ' for user ' . $owner['uid'] . ' with id ' . $objectId); DI::logger()->info('Sending undo to ' . $target . ' for user ' . $owner['uid'] . ' with id ' . $objectId);

View file

@ -45,7 +45,6 @@ use GuzzleHttp\Psr7\Uri;
*/ */
class DFRN class DFRN
{ {
const TOP_LEVEL = 0; // Top level posting const TOP_LEVEL = 0; // Top level posting
const REPLY = 1; // Regular reply that is stored locally const REPLY = 1; // Regular reply that is stored locally
const REPLY_RC = 2; // Reply that will be relayed const REPLY_RC = 2; // Reply that will be relayed
@ -64,27 +63,27 @@ class DFRN
public static function getImporter(int $cid, int $uid = 0): array public static function getImporter(int $cid, int $uid = 0): array
{ {
$condition = ['id' => $cid, 'blocked' => false, 'pending' => false]; $condition = ['id' => $cid, 'blocked' => false, 'pending' => false];
$contact = DBA::selectFirst('contact', [], $condition); $contact = DBA::selectFirst('contact', [], $condition);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
return []; return [];
} }
$contact['cpubkey'] = $contact['pubkey']; $contact['cpubkey'] = $contact['pubkey'];
$contact['cprvkey'] = $contact['prvkey']; $contact['cprvkey'] = $contact['prvkey'];
$contact['senderName'] = $contact['name']; $contact['senderName'] = $contact['name'];
if ($uid != 0) { if ($uid != 0) {
$condition = ['uid' => $uid, 'verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false]; $condition = ['uid' => $uid, 'verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false];
$user = DBA::selectFirst('user', [], $condition); $user = DBA::selectFirst('user', [], $condition);
if (!DBA::isResult($user)) { if (!DBA::isResult($user)) {
return []; return [];
} }
$user['importer_uid'] = $user['uid']; $user['importer_uid'] = $user['uid'];
$user['uprvkey'] = $user['prvkey']; $user['uprvkey'] = $user['prvkey'];
} else { } else {
$user = ['importer_uid' => 0, 'uprvkey' => '', 'timezone' => 'UTC', $user = ['importer_uid' => 0, 'uprvkey' => '', 'timezone' => 'UTC',
'nickname' => '', 'page-flags' => 0, 'account-type' => 0, 'prvnets' => 0]; 'nickname' => '', 'page-flags' => 0, 'account-type' => 0, 'prvnets' => 0];
} }
return array_merge($contact, $user); return array_merge($contact, $user);
@ -105,7 +104,7 @@ class DFRN
*/ */
public static function entries(array $items, array $owner): string public static function entries(array $items, array $owner): string
{ {
$doc = new DOMDocument('1.0', 'utf-8'); $doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true; $doc->formatOutput = true;
$root = self::addHeader($doc, $owner, 'dfrn:owner', '', false); $root = self::addHeader($doc, $owner, 'dfrn:owner', '', false);
@ -118,7 +117,7 @@ class DFRN
// These values aren't sent when sending from the queue. // These values aren't sent when sending from the queue.
/// @todo Check if we can set these values from the queue or if they are needed at all. /// @todo Check if we can set these values from the queue or if they are needed at all.
$item['entry:comment-allow'] = ($item['entry:comment-allow'] ?? '') ?: true; $item['entry:comment-allow'] = ($item['entry:comment-allow'] ?? '') ?: true;
$item['entry:cid'] = $item['entry:cid'] ?? 0; $item['entry:cid'] = $item['entry:cid'] ?? 0;
$entry = self::entry($doc, 'text', $item, $owner, $item['entry:comment-allow'], $item['entry:cid']); $entry = self::entry($doc, 'text', $item, $owner, $item['entry:comment-allow'], $item['entry:cid']);
if (isset($entry)) { if (isset($entry)) {
@ -166,9 +165,9 @@ class DFRN
$owner = ['uid' => 0, 'nick' => 'feed-item']; $owner = ['uid' => 0, 'nick' => 'feed-item'];
} }
$doc = new DOMDocument('1.0', 'utf-8'); $doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true; $doc->formatOutput = true;
$type = 'html'; $type = 'html';
if ($conversation) { if ($conversation) {
$root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed'); $root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed');
@ -210,12 +209,12 @@ class DFRN
*/ */
public static function mail(array $mail, array $owner): string public static function mail(array $mail, array $owner): string
{ {
$doc = new DOMDocument('1.0', 'utf-8'); $doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true; $doc->formatOutput = true;
$root = self::addHeader($doc, $owner, 'dfrn:owner', '', false); $root = self::addHeader($doc, $owner, 'dfrn:owner', '', false);
$mailElement = $doc->createElement('dfrn:mail'); $mailElement = $doc->createElement('dfrn:mail');
$senderElement = $doc->createElement('dfrn:sender'); $senderElement = $doc->createElement('dfrn:sender');
XML::addElement($doc, $senderElement, 'dfrn:name', $owner['name']); XML::addElement($doc, $senderElement, 'dfrn:name', $owner['name']);
@ -247,7 +246,7 @@ class DFRN
*/ */
public static function fsuggest(array $item, array $owner): string public static function fsuggest(array $item, array $owner): string
{ {
$doc = new DOMDocument('1.0', 'utf-8'); $doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true; $doc->formatOutput = true;
$root = self::addHeader($doc, $owner, 'dfrn:owner', '', false); $root = self::addHeader($doc, $owner, 'dfrn:owner', '', false);
@ -293,7 +292,7 @@ class DFRN
$photos[$p['scale']] = DI::baseUrl() . '/photo/' . $p['resource-id'] . '-' . $p['scale'] . Images::getExtensionByMimeType($p['type']); $photos[$p['scale']] = DI::baseUrl() . '/photo/' . $p['resource-id'] . '-' . $p['scale'] . Images::getExtensionByMimeType($p['type']);
} }
$doc = new DOMDocument('1.0', 'utf-8'); $doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true; $doc->formatOutput = true;
$root = self::addHeader($doc, $owner, 'dfrn:owner', '', false); $root = self::addHeader($doc, $owner, 'dfrn:owner', '', false);
@ -411,10 +410,10 @@ class DFRN
if (DBA::isResult($profile)) { if (DBA::isResult($profile)) {
$tmp_dob = substr($profile['dob'], 5); $tmp_dob = substr($profile['dob'], 5);
if (intval($tmp_dob)) { if (intval($tmp_dob)) {
$y = DateTimeFormat::timezoneNow($tz, 'Y'); $y = DateTimeFormat::timezoneNow($tz, 'Y');
$bd = $y . '-' . $tmp_dob . ' 00:00'; $bd = $y . '-' . $tmp_dob . ' 00:00';
$t_dob = strtotime($bd); $t_dob = strtotime($bd);
$now = strtotime(DateTimeFormat::timezoneNow($tz)); $now = strtotime(DateTimeFormat::timezoneNow($tz));
if ($t_dob < $now) { if ($t_dob < $now) {
$bd = (int) $y + 1 . '-' . $tmp_dob . ' 00:00'; $bd = (int) $y + 1 . '-' . $tmp_dob . ' 00:00';
} }
@ -457,11 +456,11 @@ class DFRN
XML::addElement($doc, $author, 'dfrn:handle', $owner['addr'], $attributes); XML::addElement($doc, $author, 'dfrn:handle', $owner['addr'], $attributes);
$attributes = [ $attributes = [
'rel' => 'photo', 'rel' => 'photo',
'type' => 'image/jpeg', 'type' => 'image/jpeg',
'media:width' => Proxy::PIXEL_SMALL, 'media:width' => Proxy::PIXEL_SMALL,
'media:height' => Proxy::PIXEL_SMALL, 'media:height' => Proxy::PIXEL_SMALL,
'href' => User::getAvatarUrl($owner, Proxy::SIZE_SMALL), 'href' => User::getAvatarUrl($owner, Proxy::SIZE_SMALL),
]; ];
if (!$public || !$hide) { if (!$public || !$hide) {
@ -489,9 +488,11 @@ class DFRN
} }
// Only show contact details when we are allowed to // Only show contact details when we are allowed to
$profile = DBA::selectFirst('owner-view', $profile = DBA::selectFirst(
'owner-view',
['about', 'name', 'homepage', 'nickname', 'timezone', 'locality', 'region', 'country-name', 'pub_keywords', 'xmpp', 'dob'], ['about', 'name', 'homepage', 'nickname', 'timezone', 'locality', 'region', 'country-name', 'pub_keywords', 'xmpp', 'dob'],
['uid' => $owner['uid'], 'hidewall' => false]); ['uid' => $owner['uid'], 'hidewall' => false]
);
if (DBA::isResult($profile)) { if (DBA::isResult($profile)) {
XML::addElement($doc, $author, 'poco:displayName', $profile['name']); XML::addElement($doc, $author, 'poco:displayName', $profile['name']);
XML::addElement($doc, $author, 'poco:updated', $namdate); XML::addElement($doc, $author, 'poco:updated', $namdate);
@ -578,20 +579,20 @@ class DFRN
/// - Check real image type and image size /// - Check real image type and image size
/// - Check which of these elements we should use /// - Check which of these elements we should use
$attributes = [ $attributes = [
'rel' => 'photo', 'rel' => 'photo',
'type' => 'image/jpeg', 'type' => 'image/jpeg',
'media:width' => 80, 'media:width' => 80,
'media:height' => 80, 'media:height' => 80,
'href' => $contact['photo'], 'href' => $contact['photo'],
]; ];
XML::addElement($doc, $author, 'link', '', $attributes); XML::addElement($doc, $author, 'link', '', $attributes);
$attributes = [ $attributes = [
'rel' => 'avatar', 'rel' => 'avatar',
'type' => 'image/jpeg', 'type' => 'image/jpeg',
'media:width' => 80, 'media:width' => 80,
'media:height' => 80, 'media:height' => 80,
'href' => $contact['photo'], 'href' => $contact['photo'],
]; ];
XML::addElement($doc, $author, 'link', '', $attributes); XML::addElement($doc, $author, 'link', '', $attributes);
} }
@ -680,8 +681,8 @@ class DFRN
{ {
foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::DOCUMENT, Post\Media::TORRENT]) as $attachment) { foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::DOCUMENT, Post\Media::TORRENT]) as $attachment) {
$attributes = ['rel' => 'enclosure', $attributes = ['rel' => 'enclosure',
'href' => $attachment['url'], 'href' => $attachment['url'],
'type' => $attachment['mimetype']]; 'type' => $attachment['mimetype']];
if (!empty($attachment['size'])) { if (!empty($attachment['size'])) {
$attributes['length'] = intval($attachment['size']); $attributes['length'] = intval($attachment['size']);
@ -771,8 +772,8 @@ class DFRN
if ($item['gravity'] != Item::GRAVITY_PARENT) { if ($item['gravity'] != Item::GRAVITY_PARENT) {
$parent = Post::selectFirst(['guid', 'plink'], ['uri' => $item['thr-parent'], 'uid' => $item['uid']]); $parent = Post::selectFirst(['guid', 'plink'], ['uri' => $item['thr-parent'], 'uid' => $item['uid']]);
if (DBA::isResult($parent)) { if (DBA::isResult($parent)) {
$attributes = ["ref" => $item['thr-parent'], "type" => "text/html", $attributes = ["ref" => $item['thr-parent'], "type" => "text/html",
"href" => $parent['plink'], "href" => $parent['plink'],
"dfrn:diaspora_guid" => $parent['guid']]; "dfrn:diaspora_guid" => $parent['guid']];
XML::addElement($doc, $entry, "thr:in-reply-to", "", $attributes); XML::addElement($doc, $entry, "thr:in-reply-to", "", $attributes);
} }
@ -781,7 +782,7 @@ class DFRN
// Add conversation data. This is used for OStatus // Add conversation data. This is used for OStatus
$attributes = [ $attributes = [
'href' => $item['conversation'], 'href' => $item['conversation'],
'ref' => $item['conversation'], 'ref' => $item['conversation'],
]; ];
XML::addElement($doc, $entry, 'ostatus:conversation', $item['conversation'], $attributes); XML::addElement($doc, $entry, 'ostatus:conversation', $item['conversation'], $attributes);
@ -806,7 +807,7 @@ class DFRN
'link', 'link',
'', '',
[ [
'rel' => 'alternate', 'rel' => 'alternate',
'type' => 'text/html', 'type' => 'text/html',
'href' => DI::baseUrl() . '/display/' . $item['guid'] 'href' => DI::baseUrl() . '/display/' . $item['guid']
], ],
@ -888,7 +889,7 @@ class DFRN
foreach ($mentioned as $mention) { foreach ($mentioned as $mention) {
$condition = ['uid' => $owner['uid'], 'nurl' => Strings::normaliseLink($mention)]; $condition = ['uid' => $owner['uid'], 'nurl' => Strings::normaliseLink($mention)];
$contact = DBA::selectFirst('contact', ['contact-type'], $condition); $contact = DBA::selectFirst('contact', ['contact-type'], $condition);
if (DBA::isResult($contact) && ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) { if (DBA::isResult($contact) && ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) {
XML::addElement( XML::addElement(
@ -897,9 +898,9 @@ class DFRN
'link', 'link',
'', '',
[ [
'rel' => 'mentioned', 'rel' => 'mentioned',
'ostatus:object-type' => Activity\ObjectType::GROUP, 'ostatus:object-type' => Activity\ObjectType::GROUP,
'href' => $mention, 'href' => $mention,
], ],
); );
} else { } else {
@ -909,9 +910,9 @@ class DFRN
'link', 'link',
'', '',
[ [
'rel' => 'mentioned', 'rel' => 'mentioned',
'ostatus:object-type' => Activity\ObjectType::PERSON, 'ostatus:object-type' => Activity\ObjectType::PERSON,
'href' => $mention, 'href' => $mention,
], ],
); );
} }
@ -939,7 +940,7 @@ class DFRN
if (empty($contact['addr'])) { if (empty($contact['addr'])) {
DI::logger()->notice('Empty contact handle for ' . $contact['id'] . ' - ' . $contact['url'] . ' - trying to update it.'); DI::logger()->notice('Empty contact handle for ' . $contact['id'] . ' - ' . $contact['url'] . ' - trying to update it.');
if (Contact::updateFromProbe($contact['id'])) { if (Contact::updateFromProbe($contact['id'])) {
$new_contact = DBA::selectFirst('contact', ['addr'], ['id' => $contact['id']]); $new_contact = DBA::selectFirst('contact', ['addr'], ['id' => $contact['id']]);
$contact['addr'] = $new_contact['addr']; $contact['addr'] = $new_contact['addr'];
} }
@ -963,10 +964,10 @@ class DFRN
// Create the endpoint for public posts. This is some WIP and should later be added to the probing // Create the endpoint for public posts. This is some WIP and should later be added to the probing
if ($public_batch && empty($contact['batch'])) { if ($public_batch && empty($contact['batch'])) {
$parts = parse_url($contact['notify']); $parts = parse_url($contact['notify']);
$path_parts = explode('/', $parts['path']); $path_parts = explode('/', $parts['path']);
array_pop($path_parts); array_pop($path_parts);
$parts['path'] = implode('/', $path_parts); $parts['path'] = implode('/', $path_parts);
$contact['batch'] = (string)Uri::fromParts($parts); $contact['batch'] = (string)Uri::fromParts($parts);
} }
@ -1043,7 +1044,7 @@ class DFRN
*/ */
private static function fetchauthor(\DOMXPath $xpath, \DOMNode $context, array $importer, string $element, bool $onlyfetch, string $xml = ''): array private static function fetchauthor(\DOMXPath $xpath, \DOMNode $context, array $importer, string $element, bool $onlyfetch, string $xml = ''): array
{ {
$author = []; $author = [];
$author["name"] = XML::getFirstNodeValue($xpath, $element."/atom:name/text()", $context); $author["name"] = XML::getFirstNodeValue($xpath, $element."/atom:name/text()", $context);
$author["link"] = XML::getFirstNodeValue($xpath, $element."/atom:uri/text()", $context); $author["link"] = XML::getFirstNodeValue($xpath, $element."/atom:uri/text()", $context);
@ -1060,15 +1061,15 @@ class DFRN
if (DBA::isResult($contact_old)) { if (DBA::isResult($contact_old)) {
$author["contact-id"] = $contact_old["id"]; $author["contact-id"] = $contact_old["id"];
$author["network"] = $contact_old["network"]; $author["network"] = $contact_old["network"];
} else { } else {
DI::logger()->info('Contact not found', ['condition' => $condition]); DI::logger()->info('Contact not found', ['condition' => $condition]);
$author["contact-unknown"] = true; $author["contact-unknown"] = true;
$contact = Contact::getByURL($author["link"], null, ["id", "network"]); $contact = Contact::getByURL($author["link"], null, ["id", "network"]);
$author["contact-id"] = $contact["id"] ?? $importer["id"]; $author["contact-id"] = $contact["id"] ?? $importer["id"];
$author["network"] = $contact["network"] ?? $importer["network"]; $author["network"] = $contact["network"] ?? $importer["network"];
$onlyfetch = true; $onlyfetch = true;
} }
// Until now we aren't serving different sizes - but maybe later // Until now we aren't serving different sizes - but maybe later
@ -1076,7 +1077,7 @@ class DFRN
/// @todo check if "avatar" or "photo" would be the best field in the specification /// @todo check if "avatar" or "photo" would be the best field in the specification
$avatars = $xpath->query($element . "/atom:link[@rel='avatar']", $context); $avatars = $xpath->query($element . "/atom:link[@rel='avatar']", $context);
foreach ($avatars as $avatar) { foreach ($avatars as $avatar) {
$href = ""; $href = "";
$width = 0; $width = 0;
foreach ($avatar->attributes as $attributes) { foreach ($avatar->attributes as $attributes) {
/// @TODO Rewrite these similar if() to one switch /// @TODO Rewrite these similar if() to one switch
@ -1186,7 +1187,7 @@ class DFRN
} }
// Save the keywords into the contact table // Save the keywords into the contact table
$tags = []; $tags = [];
$tagelements = $xpath->evaluate($element . "/poco:tags/text()", $context); $tagelements = $xpath->evaluate($element . "/poco:tags/text()", $context);
foreach ($tagelements as $tag) { foreach ($tagelements as $tag) {
$tags[$tag->nodeValue] = $tag->nodeValue; $tags[$tag->nodeValue] = $tag->nodeValue;
@ -1212,7 +1213,7 @@ class DFRN
if (!in_array($value, ["", "0000-00-00", DBA::NULL_DATE])) { if (!in_array($value, ["", "0000-00-00", DBA::NULL_DATE])) {
$bdyear = date("Y"); $bdyear = date("Y");
$value = str_replace(["0000", "0001"], $bdyear, $value); $value = str_replace(["0000", "0001"], $bdyear, $value);
if (strtotime($value) < time()) { if (strtotime($value) < time()) {
$value = str_replace($bdyear, $bdyear + 1, $value); $value = str_replace($bdyear, $bdyear + 1, $value);
@ -1228,10 +1229,10 @@ class DFRN
} }
$fields = ['name' => $contact['name'], 'nick' => $contact['nick'], 'about' => $contact['about'], $fields = ['name' => $contact['name'], 'nick' => $contact['nick'], 'about' => $contact['about'],
'location' => $contact['location'], 'addr' => $contact['addr'], 'keywords' => $contact['keywords'], 'location' => $contact['location'], 'addr' => $contact['addr'], 'keywords' => $contact['keywords'],
'bdyear' => $contact['bdyear'], 'bd' => $contact['bd'], 'hidden' => $contact['hidden'], 'bdyear' => $contact['bdyear'], 'bd' => $contact['bd'], 'hidden' => $contact['hidden'],
'xmpp' => $contact['xmpp'], 'name-date' => DateTimeFormat::utc($contact['name-date']), 'xmpp' => $contact['xmpp'], 'name-date' => DateTimeFormat::utc($contact['name-date']),
'unsearchable' => $contact['hidden'], 'uri-date' => DateTimeFormat::utc($contact['uri-date'])]; 'unsearchable' => $contact['hidden'], 'uri-date' => DateTimeFormat::utc($contact['uri-date'])];
Contact::update($fields, ['id' => $contact['id'], 'network' => $contact['network']], $contact_old); Contact::update($fields, ['id' => $contact['id'], 'network' => $contact['network']], $contact_old);
@ -1267,7 +1268,7 @@ class DFRN
return ""; return "";
} }
$obj_doc = new DOMDocument("1.0", "utf-8"); $obj_doc = new DOMDocument("1.0", "utf-8");
$obj_doc->formatOutput = true; $obj_doc->formatOutput = true;
$obj_element = $obj_doc->createElementNS( ActivityNamespace::ATOM1, $element); $obj_element = $obj_doc->createElementNS( ActivityNamespace::ATOM1, $element);
@ -1319,17 +1320,17 @@ class DFRN
{ {
DI::logger()->info("Processing mails"); DI::logger()->info("Processing mails");
$msg = []; $msg = [];
$msg['uid'] = $importer['importer_uid']; $msg['uid'] = $importer['importer_uid'];
$msg['from-name'] = XML::getFirstValue($xpath, 'dfrn:sender/dfrn:name/text()', $mail); $msg['from-name'] = XML::getFirstValue($xpath, 'dfrn:sender/dfrn:name/text()', $mail);
$msg['from-url'] = XML::getFirstValue($xpath, 'dfrn:sender/dfrn:uri/text()', $mail); $msg['from-url'] = XML::getFirstValue($xpath, 'dfrn:sender/dfrn:uri/text()', $mail);
$msg['from-photo'] = XML::getFirstValue($xpath, 'dfrn:sender/dfrn:avatar/text()', $mail); $msg['from-photo'] = XML::getFirstValue($xpath, 'dfrn:sender/dfrn:avatar/text()', $mail);
$msg['contact-id'] = $importer['id']; $msg['contact-id'] = $importer['id'];
$msg['uri'] = XML::getFirstValue($xpath, 'dfrn:id/text()', $mail); $msg['uri'] = XML::getFirstValue($xpath, 'dfrn:id/text()', $mail);
$msg['parent-uri'] = XML::getFirstValue($xpath, 'dfrn:in-reply-to/text()', $mail); $msg['parent-uri'] = XML::getFirstValue($xpath, 'dfrn:in-reply-to/text()', $mail);
$msg['created'] = DateTimeFormat::utc(XML::getFirstValue($xpath, 'dfrn:sentdate/text()', $mail)); $msg['created'] = DateTimeFormat::utc(XML::getFirstValue($xpath, 'dfrn:sentdate/text()', $mail));
$msg['title'] = XML::getFirstValue($xpath, 'dfrn:subject/text()', $mail); $msg['title'] = XML::getFirstValue($xpath, 'dfrn:subject/text()', $mail);
$msg['body'] = XML::getFirstValue($xpath, 'dfrn:content/text()', $mail); $msg['body'] = XML::getFirstValue($xpath, 'dfrn:content/text()', $mail);
Mail::insert($msg); Mail::insert($msg);
} }
@ -1347,8 +1348,8 @@ class DFRN
{ {
DI::logger()->info('Processing suggestions'); DI::logger()->info('Processing suggestions');
$url = $xpath->evaluate('string(dfrn:url[1]/text())', $suggestion); $url = $xpath->evaluate('string(dfrn:url[1]/text())', $suggestion);
$cid = Contact::getIdForURL($url); $cid = Contact::getIdForURL($url);
$note = $xpath->evaluate('string(dfrn:note[1]/text())', $suggestion); $note = $xpath->evaluate('string(dfrn:note[1]/text())', $suggestion);
return self::addSuggestion($importer['importer_uid'], $cid, $importer['id'], $note); return self::addSuggestion($importer['importer_uid'], $cid, $importer['id'], $note);
@ -1364,8 +1365,8 @@ class DFRN
*/ */
private static function addSuggestion(int $uid, int $cid, int $from_cid, string $note = ''): bool private static function addSuggestion(int $uid, int $cid, int $from_cid, string $note = ''): bool
{ {
$owner = User::getOwnerDataById($uid); $owner = User::getOwnerDataById($uid);
$contact = Contact::getById($cid); $contact = Contact::getById($cid);
$from_contact = Contact::getById($from_cid); $from_contact = Contact::getById($from_cid);
if (DBA::exists('contact', ['nurl' => Strings::normaliseLink($contact['url']), 'uid' => $uid])) { if (DBA::exists('contact', ['nurl' => Strings::normaliseLink($contact['url']), 'uid' => $uid])) {
@ -1377,15 +1378,15 @@ class DFRN
return false; return false;
} }
$suggest = []; $suggest = [];
$suggest['uid'] = $uid; $suggest['uid'] = $uid;
$suggest['cid'] = $from_cid; $suggest['cid'] = $from_cid;
$suggest['url'] = $contact['url']; $suggest['url'] = $contact['url'];
$suggest['name'] = $contact['name']; $suggest['name'] = $contact['name'];
$suggest['photo'] = $contact['photo']; $suggest['photo'] = $contact['photo'];
$suggest['request'] = $contact['request']; $suggest['request'] = $contact['request'];
$suggest['title'] = ''; $suggest['title'] = '';
$suggest['body'] = $note; $suggest['body'] = $note;
DI::intro()->save(DI::introFactory()->createNew( DI::intro()->save(DI::introFactory()->createNew(
$suggest['uid'], $suggest['uid'],
@ -1424,20 +1425,20 @@ class DFRN
DI::logger()->info("Processing relocations"); DI::logger()->info("Processing relocations");
/// @TODO Rewrite this to one statement /// @TODO Rewrite this to one statement
$relocate = []; $relocate = [];
$relocate['uid'] = $importer['importer_uid']; $relocate['uid'] = $importer['importer_uid'];
$relocate['cid'] = $importer['id']; $relocate['cid'] = $importer['id'];
$relocate['url'] = $xpath->query('dfrn:url/text()', $relocation)->item(0)->nodeValue; $relocate['url'] = $xpath->query('dfrn:url/text()', $relocation)->item(0)->nodeValue;
$relocate['addr'] = $xpath->query('dfrn:addr/text()', $relocation)->item(0)->nodeValue; $relocate['addr'] = $xpath->query('dfrn:addr/text()', $relocation)->item(0)->nodeValue;
$relocate['name'] = $xpath->query('dfrn:name/text()', $relocation)->item(0)->nodeValue; $relocate['name'] = $xpath->query('dfrn:name/text()', $relocation)->item(0)->nodeValue;
$relocate['avatar'] = $xpath->query('dfrn:avatar/text()', $relocation)->item(0)->nodeValue; $relocate['avatar'] = $xpath->query('dfrn:avatar/text()', $relocation)->item(0)->nodeValue;
$relocate['photo'] = $xpath->query('dfrn:photo/text()', $relocation)->item(0)->nodeValue; $relocate['photo'] = $xpath->query('dfrn:photo/text()', $relocation)->item(0)->nodeValue;
$relocate['thumb'] = $xpath->query('dfrn:thumb/text()', $relocation)->item(0)->nodeValue; $relocate['thumb'] = $xpath->query('dfrn:thumb/text()', $relocation)->item(0)->nodeValue;
$relocate['micro'] = $xpath->query('dfrn:micro/text()', $relocation)->item(0)->nodeValue; $relocate['micro'] = $xpath->query('dfrn:micro/text()', $relocation)->item(0)->nodeValue;
$relocate['request'] = $xpath->query('dfrn:request/text()', $relocation)->item(0)->nodeValue; $relocate['request'] = $xpath->query('dfrn:request/text()', $relocation)->item(0)->nodeValue;
$relocate['confirm'] = $xpath->query('dfrn:confirm/text()', $relocation)->item(0)->nodeValue; $relocate['confirm'] = $xpath->query('dfrn:confirm/text()', $relocation)->item(0)->nodeValue;
$relocate['notify'] = $xpath->query('dfrn:notify/text()', $relocation)->item(0)->nodeValue; $relocate['notify'] = $xpath->query('dfrn:notify/text()', $relocation)->item(0)->nodeValue;
$relocate['poll'] = $xpath->query('dfrn:poll/text()', $relocation)->item(0)->nodeValue; $relocate['poll'] = $xpath->query('dfrn:poll/text()', $relocation)->item(0)->nodeValue;
$relocate['sitepubkey'] = $xpath->query('dfrn:sitepubkey/text()', $relocation)->item(0)->nodeValue; $relocate['sitepubkey'] = $xpath->query('dfrn:sitepubkey/text()', $relocation)->item(0)->nodeValue;
if (($relocate['avatar'] == '') && ($relocate['photo'] != '')) { if (($relocate['avatar'] == '') && ($relocate['photo'] != '')) {
@ -1457,15 +1458,15 @@ class DFRN
// Update the contact table. We try to find every entry. // Update the contact table. We try to find every entry.
$fields = [ $fields = [
'name' => $relocate['name'], 'name' => $relocate['name'],
'avatar' => $relocate['avatar'], 'avatar' => $relocate['avatar'],
'url' => $relocate['url'], 'url' => $relocate['url'],
'nurl' => Strings::normaliseLink($relocate['url']), 'nurl' => Strings::normaliseLink($relocate['url']),
'addr' => $relocate['addr'], 'addr' => $relocate['addr'],
'request' => $relocate['request'], 'request' => $relocate['request'],
'confirm' => $relocate['confirm'], 'confirm' => $relocate['confirm'],
'notify' => $relocate['notify'], 'notify' => $relocate['notify'],
'poll' => $relocate['poll'], 'poll' => $relocate['poll'],
'site-pubkey' => $relocate['sitepubkey'], 'site-pubkey' => $relocate['sitepubkey'],
]; ];
$condition = ["(`id` = ?) OR (`nurl` = ?)", $importer['id'], Strings::normaliseLink($old['url'])]; $condition = ["(`id` = ?) OR (`nurl` = ?)", $importer['id'], Strings::normaliseLink($old['url'])];
@ -1506,10 +1507,10 @@ class DFRN
} }
$fields = [ $fields = [
'title' => $item['title'] ?? '', 'title' => $item['title'] ?? '',
'body' => $item['body'] ?? '', 'body' => $item['body'] ?? '',
'changed' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow(),
'edited' => DateTimeFormat::utc($item['edited']), 'edited' => DateTimeFormat::utc($item['edited']),
]; ];
$condition = ["`uri` = ? AND `uid` IN (0, ?)", $item['uri'], $importer['importer_uid']]; $condition = ["`uri` = ? AND `uid` IN (0, ?)", $item['uri'], $importer['importer_uid']];
@ -1611,16 +1612,16 @@ class DFRN
} }
$condition = ['uid' => $item['uid'], 'author-id' => $item['author-id'], 'gravity' => Item::GRAVITY_ACTIVITY, $condition = ['uid' => $item['uid'], 'author-id' => $item['author-id'], 'gravity' => Item::GRAVITY_ACTIVITY,
'verb' => $item['verb'], 'thr-parent' => $item['thr-parent']]; 'verb' => $item['verb'], 'thr-parent' => $item['thr-parent']];
if (Post::exists($condition)) { if (Post::exists($condition)) {
return false; return false;
} }
// The owner of an activity must be the author // The owner of an activity must be the author
$item['owner-name'] = $item['author-name']; $item['owner-name'] = $item['author-name'];
$item['owner-link'] = $item['author-link']; $item['owner-link'] = $item['author-link'];
$item['owner-avatar'] = $item['author-avatar']; $item['owner-avatar'] = $item['author-avatar'];
$item['owner-id'] = $item['author-id']; $item['owner-id'] = $item['author-id'];
} }
if (($item['verb'] == Activity::TAG) && ($item['object-type'] == Activity\ObjectType::TAGTERM)) { if (($item['verb'] == Activity::TAG) && ($item['object-type'] == Activity\ObjectType::TAGTERM)) {
@ -1654,19 +1655,24 @@ class DFRN
*/ */
private static function parseLinks($links, array &$item) private static function parseLinks($links, array &$item)
{ {
$rel = ''; $rel = '';
$href = ''; $href = '';
$type = null; $type = null;
$length = null; $length = null;
$title = null; $title = null;
foreach ($links as $link) { foreach ($links as $link) {
foreach ($link->attributes as $attributes) { foreach ($link->attributes as $attributes) {
switch ($attributes->name) { switch ($attributes->name) {
case 'href' : $href = $attributes->textContent; break; case 'href': $href = $attributes->textContent;
case 'rel' : $rel = $attributes->textContent; break; break;
case 'type' : $type = $attributes->textContent; break; case 'rel': $rel = $attributes->textContent;
case 'length': $length = $attributes->textContent; break; break;
case 'title' : $title = $attributes->textContent; break; case 'type': $type = $attributes->textContent;
break;
case 'length': $length = $attributes->textContent;
break;
case 'title': $title = $attributes->textContent;
break;
} }
} }
if (($rel != '') && ($href != '')) { if (($rel != '') && ($href != '')) {
@ -1677,7 +1683,7 @@ class DFRN
case 'enclosure': case 'enclosure':
Post\Media::insert(['uri-id' => $item['uri-id'], 'type' => Post\Media::DOCUMENT, Post\Media::insert(['uri-id' => $item['uri-id'], 'type' => Post\Media::DOCUMENT,
'url' => $href, 'mimetype' => $type, 'size' => $length, 'description' => $title]); 'url' => $href, 'mimetype' => $type, 'size' => $length, 'description' => $title]);
break; break;
} }
} }
@ -1745,7 +1751,8 @@ class DFRN
$item['edited'] = XML::getFirstNodeValue($xpath, 'atom:updated/text()', $entry); $item['edited'] = XML::getFirstNodeValue($xpath, 'atom:updated/text()', $entry);
$current = Post::selectFirst(['id', 'uid', 'edited', 'body'], $current = Post::selectFirst(
['id', 'uid', 'edited', 'body'],
['uri' => $item['uri'], 'uid' => $importer['importer_uid']] ['uri' => $item['uri'], 'uid' => $importer['importer_uid']]
); );
// Is there an existing item? // Is there an existing item?
@ -1759,18 +1766,18 @@ class DFRN
$owner_unknown = (isset($owner['contact-unknown']) && $owner['contact-unknown']); $owner_unknown = (isset($owner['contact-unknown']) && $owner['contact-unknown']);
$item['owner-name'] = $owner['name']; $item['owner-name'] = $owner['name'];
$item['owner-link'] = $owner['link']; $item['owner-link'] = $owner['link'];
$item['owner-avatar'] = $owner['avatar']; $item['owner-avatar'] = $owner['avatar'];
$item['owner-id'] = Contact::getIdForURL($owner['link'], 0); $item['owner-id'] = Contact::getIdForURL($owner['link'], 0);
// fetch the author // fetch the author
$author = self::fetchauthor($xpath, $entry, $importer, 'atom:author', true, $xml); $author = self::fetchauthor($xpath, $entry, $importer, 'atom:author', true, $xml);
$item['author-name'] = $author['name']; $item['author-name'] = $author['name'];
$item['author-link'] = $author['link']; $item['author-link'] = $author['link'];
$item['author-avatar'] = $author['avatar']; $item['author-avatar'] = $author['avatar'];
$item['author-id'] = Contact::getIdForURL($author['link'], 0); $item['author-id'] = Contact::getIdForURL($author['link'], 0);
$item['title'] = XML::getFirstNodeValue($xpath, 'atom:title/text()', $entry); $item['title'] = XML::getFirstNodeValue($xpath, 'atom:title/text()', $entry);
@ -1794,8 +1801,8 @@ class DFRN
// We don't need the content element since "dfrn:env" is always present // We don't need the content element since "dfrn:env" is always present
//$item['body'] = $xpath->query('atom:content/text()', $entry)->item(0)->nodeValue; //$item['body'] = $xpath->query('atom:content/text()', $entry)->item(0)->nodeValue;
$item['location'] = XML::getFirstNodeValue($xpath, 'dfrn:location/text()', $entry); $item['location'] = XML::getFirstNodeValue($xpath, 'dfrn:location/text()', $entry);
$item['coord'] = XML::getFirstNodeValue($xpath, 'georss:point', $entry); $item['coord'] = XML::getFirstNodeValue($xpath, 'georss:point', $entry);
$item['private'] = XML::getFirstNodeValue($xpath, 'dfrn:private/text()', $entry); $item['private'] = XML::getFirstNodeValue($xpath, 'dfrn:private/text()', $entry);
$unlisted = XML::getFirstNodeValue($xpath, 'dfrn:unlisted/text()', $entry); $unlisted = XML::getFirstNodeValue($xpath, 'dfrn:unlisted/text()', $entry);
if (!empty($unlisted) && ($item['private'] != Item::PRIVATE)) { if (!empty($unlisted) && ($item['private'] != Item::PRIVATE)) {
@ -1845,7 +1852,7 @@ class DFRN
$item['object-type'] = XML::getFirstNodeValue($xpath, 'activity:object-type/text()', $entry); $item['object-type'] = XML::getFirstNodeValue($xpath, 'activity:object-type/text()', $entry);
} }
$object = $xpath->query('activity:object', $entry)->item(0); $object = $xpath->query('activity:object', $entry)->item(0);
$item['object'] = self::transformActivity($xpath, $object, 'object'); $item['object'] = self::transformActivity($xpath, $object, 'object');
if (trim($item['object']) != '') { if (trim($item['object']) != '') {
@ -1855,13 +1862,13 @@ class DFRN
} }
} }
$target = $xpath->query('activity:target', $entry)->item(0); $target = $xpath->query('activity:target', $entry)->item(0);
$item['target'] = self::transformActivity($xpath, $target, 'target'); $item['target'] = self::transformActivity($xpath, $target, 'target');
$categories = $xpath->query('atom:category', $entry); $categories = $xpath->query('atom:category', $entry);
if ($categories) { if ($categories) {
foreach ($categories as $category) { foreach ($categories as $category) {
$term = ''; $term = '';
$scheme = ''; $scheme = '';
foreach ($category->attributes as $attributes) { foreach ($category->attributes as $attributes) {
if ($attributes->name == 'term') { if ($attributes->name == 'term') {
@ -1977,7 +1984,7 @@ class DFRN
$ev['source'] = $item['source']; $ev['source'] = $item['source'];
$condition = ['uri' => $item['uri'], 'uid' => $importer['importer_uid']]; $condition = ['uri' => $item['uri'], 'uid' => $importer['importer_uid']];
$event = DBA::selectFirst('event', ['id'], $condition); $event = DBA::selectFirst('event', ['id'], $condition);
if (DBA::isResult($event)) { if (DBA::isResult($event)) {
$ev['id'] = $event['id']; $ev['id'] = $event['id'];
} }
@ -2054,7 +2061,7 @@ class DFRN
// This is my contact on another system, but it's really me. // This is my contact on another system, but it's really me.
// Turn this into a wall post. // Turn this into a wall post.
$notify = Item::isRemoteSelf($importer, $item); $notify = Item::isRemoteSelf($importer, $item);
$item['wall'] = (bool)$notify; $item['wall'] = (bool)$notify;
$posted_id = Item::insert($item, $notify); $posted_id = Item::insert($item, $notify);
@ -2096,7 +2103,7 @@ class DFRN
} }
$condition = ['uri' => $uri, 'uid' => $importer['importer_uid']]; $condition = ['uri' => $uri, 'uid' => $importer['importer_uid']];
$item = Post::selectFirst(['id', 'parent', 'contact-id', 'uri-id', 'deleted', 'gravity'], $condition); $item = Post::selectFirst(['id', 'parent', 'contact-id', 'uri-id', 'deleted', 'gravity'], $condition);
if (!DBA::isResult($item)) { if (!DBA::isResult($item)) {
DI::logger()->info('Item with URI ' . $uri . ' for user ' . $importer['importer_uid'] . ' was not found.'); DI::logger()->info('Item with URI ' . $uri . ' for user ' . $importer['importer_uid'] . ' was not found.');
return; return;
@ -2163,12 +2170,12 @@ class DFRN
$xpath->registerNamespace('ostatus', ActivityNamespace::OSTATUS); $xpath->registerNamespace('ostatus', ActivityNamespace::OSTATUS);
$xpath->registerNamespace('statusnet', ActivityNamespace::STATUSNET); $xpath->registerNamespace('statusnet', ActivityNamespace::STATUSNET);
$header = []; $header = [];
$header['uid'] = $importer['importer_uid']; $header['uid'] = $importer['importer_uid'];
$header['network'] = Protocol::DFRN; $header['network'] = Protocol::DFRN;
$header['protocol'] = $protocol; $header['protocol'] = $protocol;
$header['wall'] = 0; $header['wall'] = 0;
$header['origin'] = 0; $header['origin'] = 0;
$header['contact-id'] = $importer['id']; $header['contact-id'] = $importer['id'];
$header = Diaspora::setDirection($header, $direction); $header = Diaspora::setDirection($header, $direction);
@ -2314,7 +2321,7 @@ class DFRN
} }
$existing_edited = DateTimeFormat::utc($existing['edited']); $existing_edited = DateTimeFormat::utc($existing['edited']);
$update_edited = DateTimeFormat::utc($update['edited']); $update_edited = DateTimeFormat::utc($update['edited']);
return (strcmp($existing_edited, $update_edited) < 0); return (strcmp($existing_edited, $update_edited) < 0);
} }

View file

@ -185,7 +185,9 @@ class Delivery
} }
// We don't deliver our items to blocked, archived or pending contacts, and not to ourselves either // We don't deliver our items to blocked, archived or pending contacts, and not to ourselves either
$contact = DBA::selectFirst('contact', [], $contact = DBA::selectFirst(
'contact',
[],
['id' => $contact_id, 'archive' => false, 'blocked' => false, 'pending' => false, 'self' => false] ['id' => $contact_id, 'archive' => false, 'blocked' => false, 'pending' => false, 'self' => false]
); );
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
@ -533,10 +535,10 @@ class Delivery
if (($contact['rel'] == Contact::FRIEND) && !$contact['blocked']) { if (($contact['rel'] == Contact::FRIEND) && !$contact['blocked']) {
if ($reply_to) { if ($reply_to) {
$headers = 'From: ' . Email::encodeHeader($local_user['username'],'UTF-8') . ' <' . $reply_to . '>' . "\n"; $headers = 'From: ' . Email::encodeHeader($local_user['username'], 'UTF-8') . ' <' . $reply_to . '>' . "\n";
$headers .= 'Sender: ' . $local_user['email'] . "\n"; $headers .= 'Sender: ' . $local_user['email'] . "\n";
} else { } else {
$headers = 'From: ' . Email::encodeHeader($local_user['username'],'UTF-8') . ' <' . $local_user['email'] . '>' . "\n"; $headers = 'From: ' . Email::encodeHeader($local_user['username'], 'UTF-8') . ' <' . $local_user['email'] . '>' . "\n";
} }
} else { } else {
$sender = DI::config()->get('config', 'sender_email', 'noreply@' . DI::baseUrl()->getHost()); $sender = DI::config()->get('config', 'sender_email', 'noreply@' . DI::baseUrl()->getHost());

View file

@ -31,7 +31,6 @@ use Friendica\Network\HTTPException;
use Friendica\Network\HTTPException\InternalServerErrorException; use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Network\HTTPException\NotFoundException; use Friendica\Network\HTTPException\NotFoundException;
use Friendica\Network\Probe; use Friendica\Network\Probe;
use Friendica\Protocol\Delivery;
use Friendica\Util\Crypto; use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Map; use Friendica\Util\Map;
@ -141,14 +140,14 @@ class Diaspora
$alg = $children->alg; $alg = $children->alg;
$sig = Strings::base64UrlDecode($children->sig); $sig = Strings::base64UrlDecode($children->sig);
$key_id = $children->sig->attributes()->key_id[0]; $key_id = $children->sig->attributes()->key_id[0];
if ($key_id != '') { if ($key_id != '') {
$handle = Strings::base64UrlDecode($key_id); $handle = Strings::base64UrlDecode($key_id);
} }
$b64url_data = Strings::base64UrlEncode($data); $b64url_data = Strings::base64UrlEncode($data);
$msg = str_replace(["\n", "\r", " ", "\t"], ['', '', '', ''], $b64url_data); $msg = str_replace(["\n", "\r", " ", "\t"], ['', '', '', ''], $b64url_data);
$signable_data = $msg . '.' . Strings::base64UrlEncode($type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($alg); $signable_data = $msg . '.' . Strings::base64UrlEncode($type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($alg);
@ -231,7 +230,7 @@ class Diaspora
} }
$encrypted_aes_key_bundle = base64_decode($data->aes_key); $encrypted_aes_key_bundle = base64_decode($data->aes_key);
$ciphertext = base64_decode($data->encrypted_magic_envelope); $ciphertext = base64_decode($data->encrypted_magic_envelope);
$outer_key_bundle = ''; $outer_key_bundle = '';
@openssl_private_decrypt($encrypted_aes_key_bundle, $outer_key_bundle, $privKey); @openssl_private_decrypt($encrypted_aes_key_bundle, $outer_key_bundle, $privKey);
@ -247,7 +246,7 @@ class Diaspora
throw new \RuntimeException('Missing keys "iv" and/or "key" from outer Salmon'); throw new \RuntimeException('Missing keys "iv" and/or "key" from outer Salmon');
} }
$outer_iv = base64_decode($j_outer_key_bundle->iv); $outer_iv = base64_decode($j_outer_key_bundle->iv);
$outer_key = base64_decode($j_outer_key_bundle->key); $outer_key = base64_decode($j_outer_key_bundle->key);
$xml = self::aesDecrypt($outer_key, $outer_iv, $ciphertext); $xml = self::aesDecrypt($outer_key, $outer_iv, $ciphertext);
@ -280,16 +279,16 @@ class Diaspora
$data = str_replace([" ", "\t", "\r", "\n"], ['', '', '', ''], $base->data); $data = str_replace([" ", "\t", "\r", "\n"], ['', '', '', ''], $base->data);
// Build the signed data // Build the signed data
$type = $base->data[0]->attributes()->type[0]; $type = $base->data[0]->attributes()->type[0];
$encoding = $base->encoding; $encoding = $base->encoding;
$alg = $base->alg; $alg = $base->alg;
$signed_data = $data . '.' . Strings::base64UrlEncode($type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($alg); $signed_data = $data . '.' . Strings::base64UrlEncode($type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($alg);
// This is the signature // This is the signature
$signature = Strings::base64UrlDecode($base->sig); $signature = Strings::base64UrlDecode($base->sig);
// Get the senders' public key // Get the senders' public key
$key_id = $base->sig[0]->attributes()->key_id[0]; $key_id = $base->sig[0]->attributes()->key_id[0];
$author_addr = base64_decode($key_id); $author_addr = base64_decode($key_id);
if ($author_addr == '') { if ($author_addr == '') {
DI::logger()->notice('No author could be decoded. Discarding. Message: ' . $xml); DI::logger()->notice('No author could be decoded. Discarding. Message: ' . $xml);
@ -302,7 +301,7 @@ class Diaspora
try { try {
$author = WebFingerUri::fromString($author_addr); $author = WebFingerUri::fromString($author_addr);
$key = self::key($author); $key = self::key($author);
if ($key == '') { if ($key == '') {
throw new \InvalidArgumentException(); throw new \InvalidArgumentException();
} }
@ -347,7 +346,7 @@ class Diaspora
*/ */
public static function decode(string $xml, string $privKey = '') public static function decode(string $xml, string $privKey = '')
{ {
$public = false; $public = false;
$basedom = XML::parseString($xml); $basedom = XML::parseString($xml);
if (!is_object($basedom)) { if (!is_object($basedom)) {
@ -357,11 +356,11 @@ class Diaspora
$children = $basedom->children('https://joindiaspora.com/protocol'); $children = $basedom->children('https://joindiaspora.com/protocol');
$inner_aes_key = null; $inner_aes_key = null;
$inner_iv = null; $inner_iv = null;
if ($children->header) { if ($children->header) {
$public = true; $public = true;
$idom = $children->header; $idom = $children->header;
} else { } else {
// This happens with posts from a relais // This happens with posts from a relais
if (empty($privKey)) { if (empty($privKey)) {
@ -372,14 +371,14 @@ class Diaspora
$encrypted_header = json_decode(base64_decode($children->encrypted_header)); $encrypted_header = json_decode(base64_decode($children->encrypted_header));
$encrypted_aes_key_bundle = base64_decode($encrypted_header->aes_key); $encrypted_aes_key_bundle = base64_decode($encrypted_header->aes_key);
$ciphertext = base64_decode($encrypted_header->ciphertext); $ciphertext = base64_decode($encrypted_header->ciphertext);
$outer_key_bundle = ''; $outer_key_bundle = '';
openssl_private_decrypt($encrypted_aes_key_bundle, $outer_key_bundle, $privKey); openssl_private_decrypt($encrypted_aes_key_bundle, $outer_key_bundle, $privKey);
$j_outer_key_bundle = json_decode($outer_key_bundle); $j_outer_key_bundle = json_decode($outer_key_bundle);
$outer_iv = base64_decode($j_outer_key_bundle->iv); $outer_iv = base64_decode($j_outer_key_bundle->iv);
$outer_key = base64_decode($j_outer_key_bundle->key); $outer_key = base64_decode($j_outer_key_bundle->key);
$decrypted = self::aesDecrypt($outer_key, $outer_iv, $ciphertext); $decrypted = self::aesDecrypt($outer_key, $outer_iv, $ciphertext);
@ -387,7 +386,7 @@ class Diaspora
DI::logger()->info('decrypted', ['data' => $decrypted]); DI::logger()->info('decrypted', ['data' => $decrypted]);
$idom = XML::parseString($decrypted); $idom = XML::parseString($decrypted);
$inner_iv = base64_decode($idom->iv); $inner_iv = base64_decode($idom->iv);
$inner_aes_key = base64_decode($idom->aes_key); $inner_aes_key = base64_decode($idom->aes_key);
} }
@ -428,10 +427,10 @@ class Diaspora
// stash away some other stuff for later // stash away some other stuff for later
$type = $base->data[0]->attributes()->type[0]; $type = $base->data[0]->attributes()->type[0];
$keyhash = $base->sig[0]->attributes()->keyhash[0]; $keyhash = $base->sig[0]->attributes()->keyhash[0];
$encoding = $base->encoding; $encoding = $base->encoding;
$alg = $base->alg; $alg = $base->alg;
$signed_data = $data . '.' . Strings::base64UrlEncode($type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($alg); $signed_data = $data . '.' . Strings::base64UrlEncode($type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($alg);
@ -496,7 +495,7 @@ class Diaspora
} }
$importer = [ $importer = [
'uid' => 0, 'uid' => 0,
'page-flags' => User::PAGE_FLAGS_FREELOVE 'page-flags' => User::PAGE_FLAGS_FREELOVE
]; ];
$success = self::dispatch($importer, $msg, $fields, $direction); $success = self::dispatch($importer, $msg, $fields, $direction);
@ -641,15 +640,16 @@ class Diaspora
} }
} }
$type = $element->getName(); $type = $element->getName();
$orig_type = $type; $orig_type = $type;
DI::logger()->debug('Got message', ['type' => $type, 'message' => $msg['message']]); DI::logger()->debug('Got message', ['type' => $type, 'message' => $msg['message']]);
// All retractions are handled identically from now on. // All retractions are handled identically from now on.
// In the new version there will only be "retraction". // In the new version there will only be "retraction".
if (in_array($type, ['signed_retraction', 'relayable_retraction'])) if (in_array($type, ['signed_retraction', 'relayable_retraction'])) {
$type = 'retraction'; $type = 'retraction';
}
if ($type == 'request') { if ($type == 'request') {
$type = 'contact'; $type = 'contact';
@ -657,8 +657,8 @@ class Diaspora
$fields = new SimpleXMLElement('<' . $type . '/>'); $fields = new SimpleXMLElement('<' . $type . '/>');
$signed_data = ''; $signed_data = '';
$author_signature = null; $author_signature = null;
$parent_author_signature = null; $parent_author_signature = null;
foreach ($element->children() as $fieldname => $entry) { foreach ($element->children() as $fieldname => $entry) {
@ -750,7 +750,7 @@ class Diaspora
} }
if (!Crypto::rsaVerify($signed_data, $parent_author_signature, $key, 'sha256')) { if (!Crypto::rsaVerify($signed_data, $parent_author_signature, $key, 'sha256')) {
DI::logger()->info('No valid parent author signature', ['author' => $msg['author'], 'type' => $type, 'signed data' => $signed_data, 'message' => $msg['message'], 'signature' => $parent_author_signature]); DI::logger()->info('No valid parent author signature', ['author' => $msg['author'], 'type' => $type, 'signed data' => $signed_data, 'message' => $msg['message'], 'signature' => $parent_author_signature]);
return false; return false;
} }
} }
@ -766,7 +766,7 @@ class Diaspora
} }
if (!Crypto::rsaVerify($signed_data, $author_signature, $key, 'sha256')) { if (!Crypto::rsaVerify($signed_data, $author_signature, $key, 'sha256')) {
DI::logger()->info('No valid author signature for author', ['author' => $fields->author, 'type' => $type, 'signed data' => $signed_data, 'message' => $msg['message'], 'signature' => $author_signature]); DI::logger()->info('No valid author signature for author', ['author' => $fields->author, 'type' => $type, 'signed data' => $signed_data, 'message' => $msg['message'], 'signature' => $author_signature]);
return false; return false;
} else { } else {
return $fields; return $fields;
@ -982,7 +982,7 @@ class Diaspora
} else { } else {
// No local match, restoring absolute remote URL from author scheme and host // No local match, restoring absolute remote URL from author scheme and host
$author_url = parse_url($author_link); $author_url = parse_url($author_link);
$return = '[url=' . $author_url['scheme'] . '://' . $author_url['host'] . '/people/' . $match[1] . ']' . $match[2] . '[/url]'; $return = '[url=' . $author_url['scheme'] . '://' . $author_url['host'] . '/people/' . $match[1] . ']' . $match[2] . '[/url]';
} }
return $return; return $return;
@ -1186,7 +1186,7 @@ class Diaspora
]; ];
$condition = ['uid' => $uid, 'guid' => $guid]; $condition = ['uid' => $uid, 'guid' => $guid];
$item = Post::selectFirst($fields, $condition); $item = Post::selectFirst($fields, $condition);
if (!DBA::isResult($item)) { if (!DBA::isResult($item)) {
try { try {
@ -1231,17 +1231,17 @@ class Diaspora
private static function authorContactByUrl(array $def_contact, string $contact_url, int $uid): array private static function authorContactByUrl(array $def_contact, string $contact_url, int $uid): array
{ {
$condition = ['nurl' => Strings::normaliseLink($contact_url), 'uid' => $uid]; $condition = ['nurl' => Strings::normaliseLink($contact_url), 'uid' => $uid];
$contact = DBA::selectFirst('contact', ['id', 'network'], $condition); $contact = DBA::selectFirst('contact', ['id', 'network'], $condition);
if (DBA::isResult($contact)) { if (DBA::isResult($contact)) {
$cid = $contact['id']; $cid = $contact['id'];
$network = $contact['network']; $network = $contact['network'];
} else { } else {
$cid = $def_contact['id']; $cid = $def_contact['id'];
$network = Protocol::DIASPORA; $network = Protocol::DIASPORA;
} }
return [ return [
'cid' => $cid, 'cid' => $cid,
'network' => $network 'network' => $network
]; ];
} }
@ -1283,7 +1283,7 @@ class Diaspora
} }
$platform = ''; $platform = '';
$gserver = DBA::selectFirst('gserver', ['platform'], ['nurl' => Strings::normaliseLink($contact['baseurl'])]); $gserver = DBA::selectFirst('gserver', ['platform'], ['nurl' => Strings::normaliseLink($contact['baseurl'])]);
if (!empty($gserver['platform'])) { if (!empty($gserver['platform'])) {
$platform = strtolower($gserver['platform']); $platform = strtolower($gserver['platform']);
DI::logger()->info('Detected platform', ['platform' => $platform, 'url' => $contact['url']]); DI::logger()->info('Detected platform', ['platform' => $platform, 'url' => $contact['url']]);
@ -1355,7 +1355,7 @@ class Diaspora
// Check signature // Check signature
$signed_text = 'AccountMigration:' . $old_author . ':' . $new_author; $signed_text = 'AccountMigration:' . $old_author . ':' . $new_author;
$key = self::key($old_author); $key = self::key($old_author);
if (!Crypto::rsaVerify($signed_text, $signature, $key, 'sha256')) { if (!Crypto::rsaVerify($signed_text, $signature, $key, 'sha256')) {
DI::logger()->notice('No valid signature for migration.'); DI::logger()->notice('No valid signature for migration.');
return false; return false;
@ -1487,10 +1487,10 @@ class Diaspora
*/ */
private static function receiveComment(array $importer, WebFingerUri $sender, SimpleXMLElement $data, string $xml, int $direction): bool private static function receiveComment(array $importer, WebFingerUri $sender, SimpleXMLElement $data, string $xml, int $direction): bool
{ {
$author = WebFingerUri::fromString(XML::unescape($data->author)); $author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid); $guid = XML::unescape($data->guid);
$parent_guid = XML::unescape($data->parent_guid); $parent_guid = XML::unescape($data->parent_guid);
$text = XML::unescape($data->text); $text = XML::unescape($data->text);
if (isset($data->created_at)) { if (isset($data->created_at)) {
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at)); $created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
@ -1500,7 +1500,7 @@ class Diaspora
if (isset($data->thread_parent_guid)) { if (isset($data->thread_parent_guid)) {
$thread_parent_guid = XML::unescape($data->thread_parent_guid); $thread_parent_guid = XML::unescape($data->thread_parent_guid);
$thr_parent = self::getUriFromGuid($thread_parent_guid); $thr_parent = self::getUriFromGuid($thread_parent_guid);
} else { } else {
$thr_parent = ''; $thr_parent = '';
} }
@ -1536,24 +1536,24 @@ class Diaspora
$datarray = []; $datarray = [];
$datarray['uid'] = $importer['uid']; $datarray['uid'] = $importer['uid'];
$datarray['contact-id'] = $author_contact['cid']; $datarray['contact-id'] = $author_contact['cid'];
$datarray['network'] = $author_contact['network']; $datarray['network'] = $author_contact['network'];
$datarray['author-link'] = $author_url; $datarray['author-link'] = $author_url;
$datarray['author-id'] = Contact::getIdForURL($author_url); $datarray['author-id'] = Contact::getIdForURL($author_url);
$datarray['owner-link'] = $contact['url']; $datarray['owner-link'] = $contact['url'];
$datarray['owner-id'] = Contact::getIdForURL($contact['url']); $datarray['owner-id'] = Contact::getIdForURL($contact['url']);
// Will be overwritten for sharing accounts in Item::insert // Will be overwritten for sharing accounts in Item::insert
$datarray = self::setDirection($datarray, $direction); $datarray = self::setDirection($datarray, $direction);
$datarray['guid'] = $guid; $datarray['guid'] = $guid;
$datarray['uri'] = self::getUriFromGuid($guid, $author); $datarray['uri'] = self::getUriFromGuid($guid, $author);
$datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]); $datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]);
$datarray['verb'] = Activity::POST; $datarray['verb'] = Activity::POST;
$datarray['gravity'] = Item::GRAVITY_COMMENT; $datarray['gravity'] = Item::GRAVITY_COMMENT;
$datarray['private'] = $toplevel_parent_item['private']; $datarray['private'] = $toplevel_parent_item['private'];
@ -1565,17 +1565,17 @@ class Diaspora
$datarray['thr-parent'] = $thr_parent ?: $toplevel_parent_item['uri']; $datarray['thr-parent'] = $thr_parent ?: $toplevel_parent_item['uri'];
$datarray['object-type'] = Activity\ObjectType::COMMENT; $datarray['object-type'] = Activity\ObjectType::COMMENT;
$datarray['post-type'] = Item::PT_NOTE; $datarray['post-type'] = Item::PT_NOTE;
$datarray['protocol'] = Conversation::PARCEL_DIASPORA; $datarray['protocol'] = Conversation::PARCEL_DIASPORA;
$datarray['source'] = $xml; $datarray['source'] = $xml;
$datarray = self::setDirection($datarray, $direction); $datarray = self::setDirection($datarray, $direction);
$datarray['changed'] = $datarray['created'] = $datarray['edited'] = $created_at; $datarray['changed'] = $datarray['created'] = $datarray['edited'] = $created_at;
$datarray['plink'] = self::plink($author, $guid, $toplevel_parent_item['guid']); $datarray['plink'] = self::plink($author, $guid, $toplevel_parent_item['guid']);
$body = Markdown::toBBCode($text); $body = Markdown::toBBCode($text);
$datarray['body'] = self::replacePeopleGuid($body, $author_url); $datarray['body'] = self::replacePeopleGuid($body, $author_url);
@ -1628,8 +1628,8 @@ class Diaspora
private static function receiveConversationMessage(array $importer, array $contact, SimpleXMLElement $data, array $msg, $mesg, array $conversation): bool private static function receiveConversationMessage(array $importer, array $contact, SimpleXMLElement $data, array $msg, $mesg, array $conversation): bool
{ {
$author_handle = XML::unescape($data->author); $author_handle = XML::unescape($data->author);
$guid = XML::unescape($data->guid); $guid = XML::unescape($data->guid);
$subject = XML::unescape($data->subject); $subject = XML::unescape($data->subject);
// "diaspora_handle" is the element name from the old version // "diaspora_handle" is the element name from the old version
// "author" is the element name from the new version // "author" is the element name from the new version
@ -1647,10 +1647,10 @@ class Diaspora
return false; return false;
} }
$msg_guid = XML::unescape($mesg->guid); $msg_guid = XML::unescape($mesg->guid);
$msg_conversation_guid = XML::unescape($mesg->conversation_guid); $msg_conversation_guid = XML::unescape($mesg->conversation_guid);
$msg_text = XML::unescape($mesg->text); $msg_text = XML::unescape($mesg->text);
$msg_created_at = DateTimeFormat::utc(XML::unescape($mesg->created_at)); $msg_created_at = DateTimeFormat::utc(XML::unescape($mesg->created_at));
if ($msg_conversation_guid != $guid) { if ($msg_conversation_guid != $guid) {
DI::logger()->notice('Message conversation guid does not belong to the current conversation.', ['guid' => $guid]); DI::logger()->notice('Message conversation guid does not belong to the current conversation.', ['guid' => $guid]);
@ -1688,10 +1688,10 @@ class Diaspora
private static function receiveConversation(array $importer, array $msg, SimpleXMLElement $data) private static function receiveConversation(array $importer, array $msg, SimpleXMLElement $data)
{ {
$author_handle = XML::unescape($data->author); $author_handle = XML::unescape($data->author);
$guid = XML::unescape($data->guid); $guid = XML::unescape($data->guid);
$subject = XML::unescape($data->subject); $subject = XML::unescape($data->subject);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at)); $created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
$participants = XML::unescape($data->participants); $participants = XML::unescape($data->participants);
$messages = $data->message; $messages = $data->message;
@ -1751,11 +1751,11 @@ class Diaspora
*/ */
private static function receiveLike(array $importer, WebFingerUri $sender, SimpleXMLElement $data, int $direction): bool private static function receiveLike(array $importer, WebFingerUri $sender, SimpleXMLElement $data, int $direction): bool
{ {
$author = WebFingerUri::fromString(XML::unescape($data->author)); $author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid); $guid = XML::unescape($data->guid);
$parent_guid = XML::unescape($data->parent_guid); $parent_guid = XML::unescape($data->parent_guid);
$parent_type = XML::unescape($data->parent_type); $parent_type = XML::unescape($data->parent_type);
$positive = XML::unescape($data->positive); $positive = XML::unescape($data->positive);
// likes on comments aren't supported by Diaspora - only on posts // likes on comments aren't supported by Diaspora - only on posts
// But maybe this will be supported in the future, so we will accept it. // But maybe this will be supported in the future, so we will accept it.
@ -1804,19 +1804,19 @@ class Diaspora
$datarray['protocol'] = Conversation::PARCEL_DIASPORA; $datarray['protocol'] = Conversation::PARCEL_DIASPORA;
$datarray['uid'] = $importer['uid']; $datarray['uid'] = $importer['uid'];
$datarray['contact-id'] = $author_contact['cid']; $datarray['contact-id'] = $author_contact['cid'];
$datarray['network'] = $author_contact['network']; $datarray['network'] = $author_contact['network'];
$datarray = self::setDirection($datarray, $direction); $datarray = self::setDirection($datarray, $direction);
$datarray['owner-link'] = $datarray['author-link'] = $author_url; $datarray['owner-link'] = $datarray['author-link'] = $author_url;
$datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($author_url); $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($author_url);
$datarray['guid'] = $guid; $datarray['guid'] = $guid;
$datarray['uri'] = self::getUriFromGuid($guid, $author); $datarray['uri'] = self::getUriFromGuid($guid, $author);
$datarray['verb'] = $verb; $datarray['verb'] = $verb;
$datarray['gravity'] = Item::GRAVITY_ACTIVITY; $datarray['gravity'] = Item::GRAVITY_ACTIVITY;
$datarray['private'] = $toplevel_parent_item['private']; $datarray['private'] = $toplevel_parent_item['private'];
@ -1837,7 +1837,7 @@ class Diaspora
// like on comments have the comment as parent. So we need to fetch the toplevel parent // like on comments have the comment as parent. So we need to fetch the toplevel parent
if ($toplevel_parent_item['gravity'] != Item::GRAVITY_PARENT) { if ($toplevel_parent_item['gravity'] != Item::GRAVITY_PARENT) {
$toplevel = Post::selectFirst(['origin'], ['id' => $toplevel_parent_item['parent']]); $toplevel = Post::selectFirst(['origin'], ['id' => $toplevel_parent_item['parent']]);
$origin = $toplevel['origin']; $origin = $toplevel['origin'];
} else { } else {
$origin = $toplevel_parent_item['origin']; $origin = $toplevel_parent_item['origin'];
} }
@ -1880,11 +1880,11 @@ class Diaspora
*/ */
private static function receiveMessage(array $importer, SimpleXMLElement $data): bool private static function receiveMessage(array $importer, SimpleXMLElement $data): bool
{ {
$author_uri = WebFingerUri::fromString(XML::unescape($data->author)); $author_uri = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid); $guid = XML::unescape($data->guid);
$conversation_guid = XML::unescape($data->conversation_guid); $conversation_guid = XML::unescape($data->conversation_guid);
$text = XML::unescape($data->text); $text = XML::unescape($data->text);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at)); $created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
$contact = self::allowedContactByHandle($importer, $author_uri, true); $contact = self::allowedContactByHandle($importer, $author_uri, true);
if (!$contact) { if (!$contact) {
@ -1895,7 +1895,7 @@ class Diaspora
GServer::setProtocol($contact['gsid'], Post\DeliveryData::DIASPORA); GServer::setProtocol($contact['gsid'], Post\DeliveryData::DIASPORA);
} }
$condition = ['uid' => $importer['uid'], 'guid' => $conversation_guid]; $condition = ['uid' => $importer['uid'], 'guid' => $conversation_guid];
$conversation = DBA::selectFirst('conv', [], $condition); $conversation = DBA::selectFirst('conv', [], $condition);
if (!DBA::isResult($conversation)) { if (!DBA::isResult($conversation)) {
DI::logger()->notice('Conversation not available.'); DI::logger()->notice('Conversation not available.');
@ -1943,8 +1943,8 @@ class Diaspora
*/ */
private static function receiveParticipation(array $importer, SimpleXMLElement $data, int $direction): bool private static function receiveParticipation(array $importer, SimpleXMLElement $data, int $direction): bool
{ {
$author = WebFingerUri::fromString(strtolower(XML::unescape($data->author))); $author = WebFingerUri::fromString(strtolower(XML::unescape($data->author)));
$guid = XML::unescape($data->guid); $guid = XML::unescape($data->guid);
$parent_guid = XML::unescape($data->parent_guid); $parent_guid = XML::unescape($data->parent_guid);
$contact = self::allowedContactByHandle($importer, $author, true); $contact = self::allowedContactByHandle($importer, $author, true);
@ -1988,20 +1988,20 @@ class Diaspora
$datarray['protocol'] = Conversation::PARCEL_DIASPORA; $datarray['protocol'] = Conversation::PARCEL_DIASPORA;
$datarray['uid'] = $importer['uid']; $datarray['uid'] = $importer['uid'];
$datarray['contact-id'] = $author_contact['cid']; $datarray['contact-id'] = $author_contact['cid'];
$datarray['network'] = $author_contact['network']; $datarray['network'] = $author_contact['network'];
$datarray = self::setDirection($datarray, $direction); $datarray = self::setDirection($datarray, $direction);
$datarray['owner-link'] = $datarray['author-link'] = $author_url; $datarray['owner-link'] = $datarray['author-link'] = $author_url;
$datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($author_url); $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($author_url);
$datarray['guid'] = $guid; $datarray['guid'] = $guid;
$datarray['uri'] = self::getUriFromGuid($guid, $author); $datarray['uri'] = self::getUriFromGuid($guid, $author);
$datarray['verb'] = Activity::FOLLOW; $datarray['verb'] = Activity::FOLLOW;
$datarray['gravity'] = Item::GRAVITY_ACTIVITY; $datarray['gravity'] = Item::GRAVITY_ACTIVITY;
$datarray['thr-parent'] = $toplevel_parent_item['uri']; $datarray['thr-parent'] = $toplevel_parent_item['uri'];
$datarray['object-type'] = Activity\ObjectType::NOTE; $datarray['object-type'] = Activity\ObjectType::NOTE;
@ -2099,14 +2099,14 @@ class Diaspora
return false; return false;
} }
$name = XML::unescape($data->first_name) . ((strlen($data->last_name)) ? ' ' . XML::unescape($data->last_name) : ''); $name = XML::unescape($data->first_name) . ((strlen($data->last_name)) ? ' ' . XML::unescape($data->last_name) : '');
$image_url = XML::unescape($data->image_url); $image_url = XML::unescape($data->image_url);
$birthday = XML::unescape($data->birthday); $birthday = XML::unescape($data->birthday);
$about = Markdown::toBBCode(XML::unescape($data->bio)); $about = Markdown::toBBCode(XML::unescape($data->bio));
$location = Markdown::toBBCode(XML::unescape($data->location)); $location = Markdown::toBBCode(XML::unescape($data->location));
$searchable = (XML::unescape($data->searchable) == 'true'); $searchable = (XML::unescape($data->searchable) == 'true');
$nsfw = (XML::unescape($data->nsfw) == 'true'); $nsfw = (XML::unescape($data->nsfw) == 'true');
$tags = XML::unescape($data->tag_string); $tags = XML::unescape($data->tag_string);
$tags = explode('#', $tags); $tags = explode('#', $tags);
@ -2147,9 +2147,9 @@ class Diaspora
} }
$fields = [ $fields = [
'name' => $name, 'location' => $location, 'name' => $name, 'location' => $location,
'name-date' => DateTimeFormat::utcNow(), 'about' => $about, 'name-date' => DateTimeFormat::utcNow(), 'about' => $about,
'addr' => $author->getAddr(), 'nick' => $author->getUser(), 'keywords' => $keywords, 'addr' => $author->getAddr(), 'nick' => $author->getUser(), 'keywords' => $keywords,
'unsearchable' => !$searchable, 'sensitive' => $nsfw 'unsearchable' => !$searchable, 'sensitive' => $nsfw
]; ];
@ -2194,7 +2194,7 @@ class Diaspora
private static function receiveContactRequest(array $importer, SimpleXMLElement $data): bool private static function receiveContactRequest(array $importer, SimpleXMLElement $data): bool
{ {
$author_handle = XML::unescape($data->author); $author_handle = XML::unescape($data->author);
$recipient = XML::unescape($data->recipient); $recipient = XML::unescape($data->recipient);
if (!$author_handle || !$recipient) { if (!$author_handle || !$recipient) {
return false; return false;
@ -2312,8 +2312,8 @@ class Diaspora
*/ */
private static function receiveReshare(array $importer, SimpleXMLElement $data, string $xml, int $direction): bool private static function receiveReshare(array $importer, SimpleXMLElement $data, string $xml, int $direction): bool
{ {
$author = WebFingerUri::fromString(XML::unescape($data->author)); $author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid); $guid = XML::unescape($data->guid);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at)); $created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
try { try {
$root_author = WebFingerUri::fromString(XML::unescape($data->root_author)); $root_author = WebFingerUri::fromString(XML::unescape($data->root_author));
@ -2347,25 +2347,25 @@ class Diaspora
$datarray = []; $datarray = [];
$datarray['uid'] = $importer['uid']; $datarray['uid'] = $importer['uid'];
$datarray['contact-id'] = $contact['id']; $datarray['contact-id'] = $contact['id'];
$datarray['network'] = Protocol::DIASPORA; $datarray['network'] = Protocol::DIASPORA;
$datarray['author-link'] = $contact['url']; $datarray['author-link'] = $contact['url'];
$datarray['author-id'] = Contact::getIdForURL($contact['url'], 0); $datarray['author-id'] = Contact::getIdForURL($contact['url'], 0);
$datarray['owner-link'] = $datarray['author-link']; $datarray['owner-link'] = $datarray['author-link'];
$datarray['owner-id'] = $datarray['author-id']; $datarray['owner-id'] = $datarray['author-id'];
$datarray['guid'] = $guid; $datarray['guid'] = $guid;
$datarray['uri'] = $datarray['thr-parent'] = self::getUriFromGuid($guid, $author); $datarray['uri'] = $datarray['thr-parent'] = self::getUriFromGuid($guid, $author);
$datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]); $datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]);
$datarray['verb'] = Activity::POST; $datarray['verb'] = Activity::POST;
$datarray['gravity'] = Item::GRAVITY_PARENT; $datarray['gravity'] = Item::GRAVITY_PARENT;
$datarray['protocol'] = Conversation::PARCEL_DIASPORA; $datarray['protocol'] = Conversation::PARCEL_DIASPORA;
$datarray['source'] = $xml; $datarray['source'] = $xml;
$datarray = self::setDirection($datarray, $direction); $datarray = self::setDirection($datarray, $direction);
@ -2624,11 +2624,11 @@ class Diaspora
*/ */
private static function receiveStatusMessage(array $importer, SimpleXMLElement $data, string $xml, int $direction) private static function receiveStatusMessage(array $importer, SimpleXMLElement $data, string $xml, int $direction)
{ {
$author = WebFingerUri::fromString(XML::unescape($data->author)); $author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid); $guid = XML::unescape($data->guid);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at)); $created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
$public = XML::unescape($data->public); $public = XML::unescape($data->public);
$text = XML::unescape($data->text); $text = XML::unescape($data->text);
$provider_display_name = XML::unescape($data->provider_display_name); $provider_display_name = XML::unescape($data->provider_display_name);
$contact = self::allowedContactByHandle($importer, $author); $contact = self::allowedContactByHandle($importer, $author);
@ -2688,7 +2688,7 @@ class Diaspora
} }
$datarray['object-type'] = Activity\ObjectType::IMAGE; $datarray['object-type'] = Activity\ObjectType::IMAGE;
$datarray['post-type'] = Item::PT_IMAGE; $datarray['post-type'] = Item::PT_IMAGE;
} elseif ($data->poll) { } elseif ($data->poll) {
$datarray['post-type'] = Item::PT_POLL; $datarray['post-type'] = Item::PT_POLL;
} }
@ -2798,10 +2798,10 @@ class Diaspora
return false; return false;
} }
$aes_key = random_bytes(32); $aes_key = random_bytes(32);
$b_aes_key = base64_encode($aes_key); $b_aes_key = base64_encode($aes_key);
$iv = random_bytes(16); $iv = random_bytes(16);
$b_iv = base64_encode($iv); $b_iv = base64_encode($iv);
$ciphertext = self::aesEncrypt($aes_key, $iv, $msg); $ciphertext = self::aesEncrypt($aes_key, $iv, $msg);
@ -2814,7 +2814,7 @@ class Diaspora
$json_object = json_encode( $json_object = json_encode(
[ [
'aes_key' => base64_encode($encrypted_key_bundle), 'aes_key' => base64_encode($encrypted_key_bundle),
'encrypted_magic_envelope' => base64_encode($ciphertext) 'encrypted_magic_envelope' => base64_encode($ciphertext)
] ]
); );
@ -2834,12 +2834,12 @@ class Diaspora
public static function buildMagicEnvelope(string $msg, array $user): string public static function buildMagicEnvelope(string $msg, array $user): string
{ {
$b64url_data = Strings::base64UrlEncode($msg); $b64url_data = Strings::base64UrlEncode($msg);
$data = str_replace(["\n", "\r", ' ', "\t"], ['', '', '', ''], $b64url_data); $data = str_replace(["\n", "\r", ' ', "\t"], ['', '', '', ''], $b64url_data);
$key_id = Strings::base64UrlEncode(self::myHandle($user)); $key_id = Strings::base64UrlEncode(self::myHandle($user));
$type = 'application/xml'; $type = 'application/xml';
$encoding = 'base64url'; $encoding = 'base64url';
$alg = 'RSA-SHA256'; $alg = 'RSA-SHA256';
$signable_data = $data . '.' . Strings::base64UrlEncode($type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($alg); $signable_data = $data . '.' . Strings::base64UrlEncode($type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($alg);
// Fallback if the private key wasn't transmitted in the expected field // Fallback if the private key wasn't transmitted in the expected field
@ -2848,7 +2848,7 @@ class Diaspora
} }
$signature = Crypto::rsaSign($signable_data, $user['uprvkey']); $signature = Crypto::rsaSign($signable_data, $user['uprvkey']);
$sig = Strings::base64UrlEncode($signature); $sig = Strings::base64UrlEncode($signature);
$xmldata = [ $xmldata = [
'me:env' => [ 'me:env' => [
@ -2935,7 +2935,7 @@ class Diaspora
// We always try to use the data from the diaspora-contact table. // We always try to use the data from the diaspora-contact table.
// This is important for transmitting data to Friendica servers. // This is important for transmitting data to Friendica servers.
try { try {
$target = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr'])); $target = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr']));
$dest_url = $public_batch ? $target->batch : $target->notify; $dest_url = $public_batch ? $target->batch : $target->notify;
} catch (HTTPException\NotFoundException | \InvalidArgumentException $e) { } catch (HTTPException\NotFoundException | \InvalidArgumentException $e) {
} }
@ -3069,9 +3069,9 @@ class Diaspora
// If the item belongs to a user, we take this user id. // If the item belongs to a user, we take this user id.
if ($item['uid'] == 0) { if ($item['uid'] == 0) {
// @todo Possibly use an administrator account? // @todo Possibly use an administrator account?
$condition = ['verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false, 'account-type' => User::ACCOUNT_TYPE_PERSON]; $condition = ['verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false, 'account-type' => User::ACCOUNT_TYPE_PERSON];
$first_user = DBA::selectFirst('user', ['uid'], $condition, ['order' => ['uid']]); $first_user = DBA::selectFirst('user', ['uid'], $condition, ['order' => ['uid']]);
$owner = User::getOwnerDataById($first_user['uid']); $owner = User::getOwnerDataById($first_user['uid']);
} else { } else {
$owner = User::getOwnerDataById($item['uid']); $owner = User::getOwnerDataById($item['uid']);
} }
@ -3079,8 +3079,8 @@ class Diaspora
$author_handle = self::myHandle($owner); $author_handle = self::myHandle($owner);
$message = [ $message = [
'author' => $author_handle, 'author' => $author_handle,
'guid' => System::createUUID(), 'guid' => System::createUUID(),
'parent_type' => 'Post', 'parent_type' => 'Post',
'parent_guid' => $item['guid'] 'parent_guid' => $item['guid']
]; ];
@ -3107,14 +3107,14 @@ class Diaspora
public static function sendAccountMigration(array $owner, array $contact, int $uid): int public static function sendAccountMigration(array $owner, array $contact, int $uid): int
{ {
$old_handle = DI::pConfig()->get($uid, 'system', 'previous_addr'); $old_handle = DI::pConfig()->get($uid, 'system', 'previous_addr');
$profile = self::createProfileData($uid); $profile = self::createProfileData($uid);
$signed_text = 'AccountMigration:' . $old_handle . ':' . $profile['author']; $signed_text = 'AccountMigration:' . $old_handle . ':' . $profile['author'];
$signature = base64_encode(Crypto::rsaSign($signed_text, $owner['uprvkey'], 'sha256')); $signature = base64_encode(Crypto::rsaSign($signed_text, $owner['uprvkey'], 'sha256'));
$message = [ $message = [
'author' => $old_handle, 'author' => $old_handle,
'profile' => $profile, 'profile' => $profile,
'signature' => $signature 'signature' => $signature
]; ];
@ -3158,10 +3158,10 @@ class Diaspora
*/ */
$message = [ $message = [
'author' => self::myHandle($owner), 'author' => self::myHandle($owner),
'recipient' => $contact['addr'], 'recipient' => $contact['addr'],
'following' => 'true', 'following' => 'true',
'sharing' => 'true' 'sharing' => 'true'
]; ];
DI::logger()->info('Send share', ['msg' => $message]); DI::logger()->info('Send share', ['msg' => $message]);
@ -3270,9 +3270,9 @@ class Diaspora
} }
if ($event['location']) { if ($event['location']) {
$event['location'] = preg_replace("/\[map\](.*?)\[\/map\]/ism", '$1', $event['location']); $event['location'] = preg_replace("/\[map\](.*?)\[\/map\]/ism", '$1', $event['location']);
$coord = Map::getCoordinates($event['location']); $coord = Map::getCoordinates($event['location']);
$location = []; $location = [];
$location['address'] = html_entity_decode(BBCode::toMarkdown($event['location'])); $location['address'] = html_entity_decode(BBCode::toMarkdown($event['location']));
if (!empty($coord['lat']) && !empty($coord['lon'])) { if (!empty($coord['lat']) && !empty($coord['lon'])) {
$location['lat'] = $coord['lat']; $location['lat'] = $coord['lat'];
@ -3310,9 +3310,9 @@ class Diaspora
$myaddr = self::myHandle($owner); $myaddr = self::myHandle($owner);
$public = ($item['private'] == Item::PRIVATE ? 'false' : 'true'); $public = ($item['private'] == Item::PRIVATE ? 'false' : 'true');
$created = DateTimeFormat::utc($item['received'], DateTimeFormat::ATOM); $created = DateTimeFormat::utc($item['received'], DateTimeFormat::ATOM);
$edited = DateTimeFormat::utc($item['edited'] ?? $item['created'], DateTimeFormat::ATOM); $edited = DateTimeFormat::utc($item['edited'] ?? $item['created'], DateTimeFormat::ATOM);
// Detect a share element and do a reshare // Detect a share element and do a reshare
if (($item['private'] != Item::PRIVATE) && ($ret = self::getReshareDetails($item))) { if (($item['private'] != Item::PRIVATE) && ($ret = self::getReshareDetails($item))) {
@ -3366,24 +3366,25 @@ class Diaspora
$location = []; $location = [];
if ($item['location'] != '') if ($item['location'] != '') {
$location['address'] = $item['location']; $location['address'] = $item['location'];
}
if ($item['coord'] != '') { if ($item['coord'] != '') {
$coord = explode(' ', $item['coord']); $coord = explode(' ', $item['coord']);
$location['lat'] = $coord[0]; $location['lat'] = $coord[0];
$location['lng'] = $coord[1]; $location['lng'] = $coord[1];
} }
$message = [ $message = [
'author' => $myaddr, 'author' => $myaddr,
'guid' => $item['guid'], 'guid' => $item['guid'],
'created_at' => $created, 'created_at' => $created,
'edited_at' => $edited, 'edited_at' => $edited,
'public' => $public, 'public' => $public,
'text' => $body, 'text' => $body,
'provider_display_name' => $item['app'], 'provider_display_name' => $item['app'],
'location' => $location 'location' => $location
]; ];
if ($native_photos) { if ($native_photos) {
@ -3514,7 +3515,7 @@ class Diaspora
} }
$target_type = ($parent['uri'] === $parent['thr-parent'] ? 'Post' : 'Comment'); $target_type = ($parent['uri'] === $parent['thr-parent'] ? 'Post' : 'Comment');
$positive = null; $positive = null;
if ($item['verb'] === Activity::LIKE) { if ($item['verb'] === Activity::LIKE) {
$positive = 'true'; $positive = 'true';
} elseif ($item['verb'] === Activity::DISLIKE) { } elseif ($item['verb'] === Activity::DISLIKE) {
@ -3563,10 +3564,10 @@ class Diaspora
} }
return [ return [
'author' => self::myHandle($owner), 'author' => self::myHandle($owner),
'guid' => $item['guid'], 'guid' => $item['guid'],
'parent_guid' => $parent['guid'], 'parent_guid' => $parent['guid'],
'status' => $attend_answer, 'status' => $attend_answer,
'author_signature' => '' 'author_signature' => ''
]; ];
} }
@ -3616,17 +3617,17 @@ class Diaspora
$body = self::prependParentAuthorMention($body, $thread_parent_item['author-link']); $body = self::prependParentAuthorMention($body, $thread_parent_item['author-link']);
} }
$text = html_entity_decode(BBCode::toMarkdown($body)); $text = html_entity_decode(BBCode::toMarkdown($body));
$created = DateTimeFormat::utc($item['created'], DateTimeFormat::ATOM); $created = DateTimeFormat::utc($item['created'], DateTimeFormat::ATOM);
$edited = DateTimeFormat::utc($item['edited'], DateTimeFormat::ATOM); $edited = DateTimeFormat::utc($item['edited'], DateTimeFormat::ATOM);
$comment = [ $comment = [
'author' => self::myHandle($owner), 'author' => self::myHandle($owner),
'guid' => $item['guid'], 'guid' => $item['guid'],
'created_at' => $created, 'created_at' => $created,
'edited_at' => $edited, 'edited_at' => $edited,
'parent_guid' => $toplevel_item['guid'], 'parent_guid' => $toplevel_item['guid'],
'text' => $text, 'text' => $text,
'author_signature' => '', 'author_signature' => '',
]; ];
@ -3658,13 +3659,13 @@ class Diaspora
if (in_array($item['verb'], [Activity::ATTEND, Activity::ATTENDNO, Activity::ATTENDMAYBE])) { if (in_array($item['verb'], [Activity::ATTEND, Activity::ATTENDNO, Activity::ATTENDMAYBE])) {
$message = self::constructAttend($item, $owner); $message = self::constructAttend($item, $owner);
$type = 'event_participation'; $type = 'event_participation';
} elseif (in_array($item['verb'], [Activity::LIKE, Activity::DISLIKE])) { } elseif (in_array($item['verb'], [Activity::LIKE, Activity::DISLIKE])) {
$message = self::constructLike($item, $owner); $message = self::constructLike($item, $owner);
$type = 'like'; $type = 'like';
} elseif (!in_array($item['verb'], [Activity::FOLLOW, Activity::TAG])) { } elseif (!in_array($item['verb'], [Activity::FOLLOW, Activity::TAG])) {
$message = self::constructComment($item, $owner); $message = self::constructComment($item, $owner);
$type = 'comment'; $type = 'comment';
} }
if (empty($message)) { if (empty($message)) {
@ -3753,7 +3754,7 @@ class Diaspora
} }
$message = [ $message = [
'author' => $itemaddr, 'author' => $itemaddr,
'target_guid' => $item['guid'], 'target_guid' => $item['guid'],
'target_type' => $target_type 'target_type' => $target_type
]; ];
@ -3784,28 +3785,28 @@ class Diaspora
return -1; return -1;
} }
$body = BBCode::toMarkdown($item['body']); $body = BBCode::toMarkdown($item['body']);
$created = DateTimeFormat::utc($item['created'], DateTimeFormat::ATOM); $created = DateTimeFormat::utc($item['created'], DateTimeFormat::ATOM);
$msg = [ $msg = [
'author' => $myaddr, 'author' => $myaddr,
'guid' => $item['guid'], 'guid' => $item['guid'],
'conversation_guid' => $cnv['guid'], 'conversation_guid' => $cnv['guid'],
'text' => $body, 'text' => $body,
'created_at' => $created, 'created_at' => $created,
]; ];
if ($item['reply']) { if ($item['reply']) {
$message = $msg; $message = $msg;
$type = 'message'; $type = 'message';
} else { } else {
$message = [ $message = [
'author' => $cnv['creator'], 'author' => $cnv['creator'],
'guid' => $cnv['guid'], 'guid' => $cnv['guid'],
'subject' => $cnv['subject'], 'subject' => $cnv['subject'],
'created_at' => DateTimeFormat::utc($cnv['created'], DateTimeFormat::ATOM), 'created_at' => DateTimeFormat::utc($cnv['created'], DateTimeFormat::ATOM),
'participants' => $cnv['recips'], 'participants' => $cnv['recips'],
'message' => $msg 'message' => $msg
]; ];
$type = 'conversation'; $type = 'conversation';
@ -3836,14 +3837,14 @@ class Diaspora
// Take the first word as first name // Take the first word as first name
$first = ((strpos($name, ' ') ? trim(substr($name, 0, strpos($name, ' '))) : $name)); $first = ((strpos($name, ' ') ? trim(substr($name, 0, strpos($name, ' '))) : $name));
$last = (($first === $name) ? '' : trim(substr($name, strlen($first)))); $last = (($first === $name) ? '' : trim(substr($name, strlen($first))));
if ((strlen($first) < 32) && (strlen($last) < 32)) { if ((strlen($first) < 32) && (strlen($last) < 32)) {
return ['first' => $first, 'last' => $last]; return ['first' => $first, 'last' => $last];
} }
// Take the last word as last name // Take the last word as last name
$first = ((strrpos($name, ' ') ? trim(substr($name, 0, strrpos($name, ' '))) : $name)); $first = ((strrpos($name, ' ') ? trim(substr($name, 0, strrpos($name, ' '))) : $name));
$last = (($first === $name) ? '' : trim(substr($name, strlen($first)))); $last = (($first === $name) ? '' : trim(substr($name, strlen($first))));
if ((strlen($first) < 32) && (strlen($last) < 32)) { if ((strlen($first) < 32) && (strlen($last) < 32)) {
return ['first' => $first, 'last' => $last]; return ['first' => $first, 'last' => $last];
@ -3852,12 +3853,12 @@ class Diaspora
// Take the first 32 characters if there is no space in the first 32 characters // Take the first 32 characters if there is no space in the first 32 characters
if ((strpos($name, ' ') > 32) || (strpos($name, ' ') === false)) { if ((strpos($name, ' ') > 32) || (strpos($name, ' ') === false)) {
$first = substr($name, 0, 32); $first = substr($name, 0, 32);
$last = substr($name, 32); $last = substr($name, 32);
return ['first' => $first, 'last' => $last]; return ['first' => $first, 'last' => $last];
} }
$first = trim(substr($name, 0, strrpos(substr($name, 0, 33), ' '))); $first = trim(substr($name, 0, strrpos(substr($name, 0, 33), ' ')));
$last = (($first === $name) ? '' : trim(substr($name, strlen($first)))); $last = (($first === $name) ? '' : trim(substr($name, strlen($first))));
// Check if the last name is longer than 32 characters // Check if the last name is longer than 32 characters
if (strlen($last) > 32) { if (strlen($last) > 32) {
@ -3912,7 +3913,7 @@ class Diaspora
$data['birthday'] = ''; $data['birthday'] = '';
if ($profile['dob'] && ($profile['dob'] > '0000-00-00')) { if ($profile['dob'] && ($profile['dob'] > '0000-00-00')) {
[$year, $month, $day] = sscanf($profile['dob'], '%4d-%2d-%2d'); list($year, $month, $day) = sscanf($profile['dob'], '%4d-%2d-%2d');
if ($year < 1004) { if ($year < 1004) {
$year = 1004; $year = 1004;
} }
@ -3921,12 +3922,12 @@ class Diaspora
$data['bio'] = BBCode::toMarkdown($profile['about'] ?? ''); $data['bio'] = BBCode::toMarkdown($profile['about'] ?? '');
$data['location'] = $profile['location']; $data['location'] = $profile['location'];
$data['tag_string'] = ''; $data['tag_string'] = '';
if ($profile['pub_keywords']) { if ($profile['pub_keywords']) {
$kw = str_replace(',', ' ', $profile['pub_keywords']); $kw = str_replace(',', ' ', $profile['pub_keywords']);
$kw = str_replace(' ', ' ', $kw); $kw = str_replace(' ', ' ', $kw);
$arr = explode(' ', $kw); $arr = explode(' ', $kw);
if (count($arr)) { if (count($arr)) {
for ($x = 0; $x < 5; $x++) { for ($x = 0; $x < 5; $x++) {
@ -4003,7 +4004,8 @@ class Diaspora
} }
if (!in_array($item['verb'], [Activity::LIKE, Activity::DISLIKE])) { if (!in_array($item['verb'], [Activity::LIKE, Activity::DISLIKE])) {
DI::logger()->warning('Item is neither a like nor a dislike', ['uid' => $uid, 'item[verb]' => $item['verb']]);; DI::logger()->warning('Item is neither a like nor a dislike', ['uid' => $uid, 'item[verb]' => $item['verb']]);
;
return false; return false;
} }
@ -4120,8 +4122,8 @@ class Diaspora
'quote-uri-id' => $UriId, 'quote-uri-id' => $UriId,
'allow_cid' => $owner['allow_cid'] ?? '', 'allow_cid' => $owner['allow_cid'] ?? '',
'allow_gid' => $owner['allow_gid'] ?? '', 'allow_gid' => $owner['allow_gid'] ?? '',
'deny_cid' => $owner['deny_cid'] ?? '', 'deny_cid' => $owner['deny_cid'] ?? '',
'deny_gid' => $owner['deny_gid'] ?? '', 'deny_gid' => $owner['deny_gid'] ?? '',
]; ];
if (!empty($item['allow_cid'] . $item['allow_gid'] . $item['deny_cid'] . $item['deny_gid'])) { if (!empty($item['allow_cid'] . $item['allow_gid'] . $item['deny_cid'] . $item['deny_gid'])) {

View file

@ -127,7 +127,7 @@ class Email
*/ */
public static function getMessage($mbox, int $uid, string $reply, array $item): array public static function getMessage($mbox, int $uid, string $reply, array $item): array
{ {
$ret = $item; $ret = $item;
$struc = (($mbox && $uid) ? @imap_fetchstructure($mbox, $uid, FT_UID) : null); $struc = (($mbox && $uid) ? @imap_fetchstructure($mbox, $uid, FT_UID) : null);
if (!$struc) { if (!$struc) {
@ -154,7 +154,7 @@ class Email
$message = ['text' => $text, 'html' => '', 'item' => $ret]; $message = ['text' => $text, 'html' => '', 'item' => $ret];
Hook::callAll('email_getmessage', $message); Hook::callAll('email_getmessage', $message);
$ret = $message['item']; $ret = $message['item'];
$ret['body'] = $message['text']; $ret['body'] = $message['text'];
} }
} else { } else {
@ -186,7 +186,7 @@ class Email
} }
$ret['body'] = self::removeGPG($ret['body']); $ret['body'] = self::removeGPG($ret['body']);
$msg = self::removeSig($ret['body']); $msg = self::removeSig($ret['body']);
$ret['body'] = $msg['body']; $ret['body'] = $msg['body'];
$ret['body'] = self::convertQuote($ret['body'], $reply); $ret['body'] = self::convertQuote($ret['body'], $reply);
@ -221,8 +221,8 @@ class Email
// DECODE DATA // DECODE DATA
$data = ($partno) $data = ($partno)
? @imap_fetchbody($mbox, $uid, $partno, FT_UID|FT_PEEK) ? @imap_fetchbody($mbox, $uid, $partno, FT_UID | FT_PEEK)
: @imap_body($mbox, $uid, FT_UID|FT_PEEK); : @imap_body($mbox, $uid, FT_UID | FT_PEEK);
// Any part may be encoded, even plain text messages, so check everything. // Any part may be encoded, even plain text messages, so check everything.
if ($p->encoding == 4) { if ($p->encoding == 4) {
@ -261,7 +261,7 @@ class Email
if ($p->type == 0 && $data) { if ($p->type == 0 && $data) {
// Messages may be split in different parts because of inline attachments, // Messages may be split in different parts because of inline attachments,
// so append parts together with blank row. // so append parts together with blank row.
if (strtolower($p->subtype)==$subtype) { if (strtolower($p->subtype) == $subtype) {
$data = iconv($params['charset'], 'UTF-8//IGNORE', $data); $data = iconv($params['charset'], 'UTF-8//IGNORE', $data);
return (trim($data) ."\n\n"); return (trim($data) ."\n\n");
} else { } else {
@ -285,7 +285,7 @@ class Email
if (isset($p->parts) && $p->parts) { if (isset($p->parts) && $p->parts) {
$x = ""; $x = "";
foreach ($p->parts as $partno0 => $p2) { foreach ($p->parts as $partno0 => $p2) {
$x .= self::messageGetPart($mbox, $uid, $p2, $partno . '.' . ($partno0+1), $subtype); // 1.2, 1.2.1, etc. $x .= self::messageGetPart($mbox, $uid, $p2, $partno . '.' . ($partno0 + 1), $subtype); // 1.2, 1.2.1, etc.
} }
return $x; return $x;
} }
@ -301,10 +301,10 @@ class Email
*/ */
public static function encodeHeader(string $in_str, string $charset): string public static function encodeHeader(string $in_str, string $charset): string
{ {
$out_str = $in_str; $out_str = $in_str;
$need_to_convert = false; $need_to_convert = false;
for ($x = 0; $x < strlen($in_str); $x ++) { for ($x = 0; $x < strlen($in_str); $x++) {
if ((ord($in_str[$x]) == 0) || ((ord($in_str[$x]) > 128))) { if ((ord($in_str[$x]) == 0) || ((ord($in_str[$x]) > 128))) {
$need_to_convert = true; $need_to_convert = true;
} }
@ -316,8 +316,8 @@ class Email
if ($out_str && $charset) { if ($out_str && $charset) {
// define start delimiter, end delimiter and spacer // define start delimiter, end delimiter and spacer
$end = "?="; $end = "?=";
$start = "=?" . $charset . "?B?"; $start = "=?" . $charset . "?B?";
$spacer = $end . "\r\n " . $start; $spacer = $end . "\r\n " . $start;
// determine length of encoded text within chunks // determine length of encoded text within chunks
@ -344,7 +344,7 @@ class Email
// remove trailing spacer and // remove trailing spacer and
// add start and end delimiters // add start and end delimiters
$spacer = preg_quote($spacer, '/'); $spacer = preg_quote($spacer, '/');
$out_str = preg_replace("/" . $spacer . "$/", "", $out_str); $out_str = preg_replace("/" . $spacer . "$/", "", $out_str);
$out_str = $start . $out_str . $end; $out_str = $start . $out_str . $end;
} }
@ -374,7 +374,7 @@ class Email
$part = uniqid('', true); $part = uniqid('', true);
$html = Item::prepareBody($item); $html = Item::prepareBody($item);
$headers .= "Mime-Version: 1.0\n"; $headers .= "Mime-Version: 1.0\n";
$headers .= 'Content-Type: multipart/alternative; boundary="=_'.$part.'"'."\n\n"; $headers .= 'Content-Type: multipart/alternative; boundary="=_'.$part.'"'."\n\n";
@ -571,13 +571,13 @@ class Email
*/ */
private static function removeSig(string $message): array private static function removeSig(string $message): array
{ {
$sigpos = strrpos($message, "\n-- \n"); $sigpos = strrpos($message, "\n-- \n");
$quotepos = strrpos($message, "[/quote]"); $quotepos = strrpos($message, "[/quote]");
if ($sigpos == 0) { if ($sigpos == 0) {
// Especially for web.de who are using that as a separator // Especially for web.de who are using that as a separator
$message = str_replace("\n___________________________________________________________\n", "\n-- \n", $message); $message = str_replace("\n___________________________________________________________\n", "\n-- \n", $message);
$sigpos = strrpos($message, "\n-- \n"); $sigpos = strrpos($message, "\n-- \n");
$quotepos = strrpos($message, "[/quote]"); $quotepos = strrpos($message, "[/quote]");
} }
@ -592,10 +592,10 @@ class Email
if (!empty($result[1]) && !empty($result[2])) { if (!empty($result[1]) && !empty($result[2])) {
$cleaned = trim($result[1])."\n"; $cleaned = trim($result[1])."\n";
$sig = trim($result[2]); $sig = trim($result[2]);
} else { } else {
$cleaned = $message; $cleaned = $message;
$sig = ''; $sig = '';
} }
return ['body' => $cleaned, 'sig' => $sig]; return ['body' => $cleaned, 'sig' => $sig];
@ -611,13 +611,13 @@ class Email
{ {
$arrbody = explode("\n", trim($message)); $arrbody = explode("\n", trim($message));
$lines = []; $lines = [];
$lineno = 0; $lineno = 0;
foreach ($arrbody as $i => $line) { foreach ($arrbody as $i => $line) {
$currquotelevel = 0; $currquotelevel = 0;
$currline = $line; $currline = $line;
while ((strlen($currline)>0) && ((substr($currline, 0, 1) == '>') while ((strlen($currline) > 0) && ((substr($currline, 0, 1) == '>')
|| (substr($currline, 0, 1) == ' '))) { || (substr($currline, 0, 1) == ' '))) {
if (substr($currline, 0, 1) == '>') { if (substr($currline, 0, 1) == '>') {
$currquotelevel++; $currquotelevel++;
@ -627,8 +627,8 @@ class Email
} }
$quotelevel = 0; $quotelevel = 0;
$nextline = trim($arrbody[$i + 1] ?? ''); $nextline = trim($arrbody[$i + 1] ?? '');
while ((strlen($nextline)>0) && ((substr($nextline, 0, 1) == '>') while ((strlen($nextline) > 0) && ((substr($nextline, 0, 1) == '>')
|| (substr($nextline, 0, 1) == ' '))) { || (substr($nextline, 0, 1) == ' '))) {
if (substr($nextline, 0, 1) == '>') { if (substr($nextline, 0, 1) == '>') {
$quotelevel++; $quotelevel++;
@ -642,7 +642,7 @@ class Email
$lines[$lineno] .= ' '; $lines[$lineno] .= ' ';
} }
while ((strlen($line)>0) && ((substr($line, 0, 1) == '>') while ((strlen($line) > 0) && ((substr($line, 0, 1) == '>')
|| (substr($line, 0, 1) == ' '))) { || (substr($line, 0, 1) == ' '))) {
$line = ltrim(substr($line, 1)); $line = ltrim(substr($line, 1));
@ -663,34 +663,35 @@ class Email
private static function convertQuote(string $body, string $reply): string private static function convertQuote(string $body, string $reply): string
{ {
// Convert Quotes // Convert Quotes
$arrbody = explode("\n", trim($body)); $arrbody = explode("\n", trim($body));
$arrlevel = []; $arrlevel = [];
for ($i = 0; $i < count($arrbody); $i++) { for ($i = 0; $i < count($arrbody); $i++) {
$quotelevel = 0; $quotelevel = 0;
$quoteline = $arrbody[$i]; $quoteline = $arrbody[$i];
while ((strlen($quoteline)>0) and ((substr($quoteline, 0, 1) == '>') while ((strlen($quoteline) > 0) and ((substr($quoteline, 0, 1) == '>')
|| (substr($quoteline, 0, 1) == ' '))) { || (substr($quoteline, 0, 1) == ' '))) {
if (substr($quoteline, 0, 1) == '>') if (substr($quoteline, 0, 1) == '>') {
$quotelevel++; $quotelevel++;
}
$quoteline = ltrim(substr($quoteline, 1)); $quoteline = ltrim(substr($quoteline, 1));
} }
$arrlevel[$i] = $quotelevel; $arrlevel[$i] = $quotelevel;
$arrbody[$i] = $quoteline; $arrbody[$i] = $quoteline;
} }
$quotelevel = 0; $quotelevel = 0;
$arrbodyquoted = []; $arrbodyquoted = [];
for ($i = 0; $i < count($arrbody); $i++) { for ($i = 0; $i < count($arrbody); $i++) {
$previousquote = $quotelevel; $previousquote = $quotelevel;
$quotelevel = $arrlevel[$i]; $quotelevel = $arrlevel[$i];
while ($previousquote < $quotelevel) { while ($previousquote < $quotelevel) {
$quote = "[quote]"; $quote = "[quote]";
$arrbody[$i] = $quote.$arrbody[$i]; $arrbody[$i] = $quote.$arrbody[$i];
$previousquote++; $previousquote++;
} }
@ -726,8 +727,8 @@ class Email
do { do {
$oldmessage = $message; $oldmessage = $message;
$message = preg_replace('=\[/quote\][\s](.*?)\[quote\]=i', '$1', $message); $message = preg_replace('=\[/quote\][\s](.*?)\[quote\]=i', '$1', $message);
$message = str_replace('[/quote][quote]', '', $message); $message = str_replace('[/quote][quote]', '', $message);
} while ($message != $oldmessage); } while ($message != $oldmessage);
$quotes = []; $quotes = [];
@ -738,12 +739,12 @@ class Email
while (($pos = strpos($message, '[quote', $start)) > 0) { while (($pos = strpos($message, '[quote', $start)) > 0) {
$quotes[$pos] = -1; $quotes[$pos] = -1;
$start = $pos + 7; $start = $pos + 7;
$startquotes++; $startquotes++;
} }
$endquotes = 0; $endquotes = 0;
$start = 0; $start = 0;
while (($pos = strpos($message, '[/quote]', $start)) > 0) { while (($pos = strpos($message, '[/quote]', $start)) > 0) {
$start = $pos + 7; $start = $pos + 7;
@ -759,7 +760,7 @@ class Email
while (($pos = strpos($message, '[/quote]', $start)) > 0) { while (($pos = strpos($message, '[/quote]', $start)) > 0) {
$quotes[$pos] = 1; $quotes[$pos] = 1;
$start = $pos + 7; $start = $pos + 7;
} }
if (strtolower(substr($message, -8)) != '[/quote]') { if (strtolower(substr($message, -8)) != '[/quote]') {
@ -773,12 +774,13 @@ class Email
foreach ($quotes as $index => $quote) { foreach ($quotes as $index => $quote) {
$quotelevel += $quote; $quotelevel += $quote;
if (($quotelevel == 0) and ($quotestart == 0)) if (($quotelevel == 0) and ($quotestart == 0)) {
$quotestart = $index; $quotestart = $index;
}
} }
if ($quotestart != 0) { if ($quotestart != 0) {
$message = trim(substr($message, 0, $quotestart))."\n[spoiler]".substr($message, $quotestart+7, -8) . '[/spoiler]'; $message = trim(substr($message, 0, $quotestart))."\n[spoiler]".substr($message, $quotestart + 7, -8) . '[/spoiler]';
} }
return $message; return $message;

View file

@ -88,14 +88,14 @@ class Feed
$xpath->registerNamespace('media', 'http://search.yahoo.com/mrss/'); $xpath->registerNamespace('media', 'http://search.yahoo.com/mrss/');
$xpath->registerNamespace('poco', ActivityNamespace::POCO); $xpath->registerNamespace('poco', ActivityNamespace::POCO);
$author = []; $author = [];
$atomns = 'atom'; $atomns = 'atom';
$entries = null; $entries = null;
$protocol = Conversation::PARCEL_UNKNOWN; $protocol = Conversation::PARCEL_UNKNOWN;
// Is it RDF? // Is it RDF?
if ($xpath->query('/rdf:RDF/rss:channel')->length > 0) { if ($xpath->query('/rdf:RDF/rss:channel')->length > 0) {
$protocol = Conversation::PARCEL_RDF; $protocol = Conversation::PARCEL_RDF;
$author['author-link'] = XML::getFirstNodeValue($xpath, '/rdf:RDF/rss:channel/rss:link/text()'); $author['author-link'] = XML::getFirstNodeValue($xpath, '/rdf:RDF/rss:channel/rss:link/text()');
$author['author-name'] = XML::getFirstNodeValue($xpath, '/rdf:RDF/rss:channel/rss:title/text()'); $author['author-name'] = XML::getFirstNodeValue($xpath, '/rdf:RDF/rss:channel/rss:title/text()');
@ -106,9 +106,9 @@ class Feed
} }
if ($xpath->query('/opml')->length > 0) { if ($xpath->query('/opml')->length > 0) {
$protocol = Conversation::PARCEL_OPML; $protocol = Conversation::PARCEL_OPML;
$author['author-name'] = XML::getFirstNodeValue($xpath, '/opml/head/title/text()'); $author['author-name'] = XML::getFirstNodeValue($xpath, '/opml/head/title/text()');
$entries = $xpath->query('/opml/body/outline'); $entries = $xpath->query('/opml/body/outline');
} }
// Is it Atom? // Is it Atom?
@ -116,7 +116,7 @@ class Feed
$protocol = Conversation::PARCEL_ATOM; $protocol = Conversation::PARCEL_ATOM;
} elseif ($xpath->query('/atom03:feed')->length > 0) { } elseif ($xpath->query('/atom03:feed')->length > 0) {
$protocol = Conversation::PARCEL_ATOM03; $protocol = Conversation::PARCEL_ATOM03;
$atomns = 'atom03'; $atomns = 'atom03';
} }
if (in_array($protocol, [Conversation::PARCEL_ATOM, Conversation::PARCEL_ATOM03])) { if (in_array($protocol, [Conversation::PARCEL_ATOM, Conversation::PARCEL_ATOM03])) {
@ -203,7 +203,7 @@ class Feed
// Is it RSS? // Is it RSS?
if ($xpath->query('/rss/channel')->length > 0) { if ($xpath->query('/rss/channel')->length > 0) {
$protocol = Conversation::PARCEL_RSS; $protocol = Conversation::PARCEL_RSS;
$author['author-link'] = XML::getFirstNodeValue($xpath, '/rss/channel/link/text()'); $author['author-link'] = XML::getFirstNodeValue($xpath, '/rss/channel/link/text()');
$author['author-name'] = XML::getFirstNodeValue($xpath, '/rss/channel/title/text()'); $author['author-name'] = XML::getFirstNodeValue($xpath, '/rss/channel/title/text()');
@ -251,8 +251,8 @@ class Feed
$author['author-avatar'] = $contact['thumb']; $author['author-avatar'] = $contact['thumb'];
$author['owner-link'] = $contact['url']; $author['owner-link'] = $contact['url'];
$author['owner-name'] = $contact['name']; $author['owner-name'] = $contact['name'];
$author['owner-avatar'] = $contact['thumb']; $author['owner-avatar'] = $contact['thumb'];
} }
@ -269,7 +269,7 @@ class Feed
'contact-id' => $contact['id'] ?? 0, 'contact-id' => $contact['id'] ?? 0,
]; ];
$datarray['protocol'] = $protocol; $datarray['protocol'] = $protocol;
$datarray['direction'] = Conversation::PULL; $datarray['direction'] = Conversation::PULL;
if (!is_object($entries)) { if (!is_object($entries)) {
@ -277,12 +277,12 @@ class Feed
return []; return [];
} }
$items = []; $items = [];
$creation_dates = []; $creation_dates = [];
// Limit the number of items that are about to be fetched // Limit the number of items that are about to be fetched
$total_items = ($entries->length - 1); $total_items = ($entries->length - 1);
$max_items = DI::config()->get('system', 'max_feed_items'); $max_items = DI::config()->get('system', 'max_feed_items');
if (($max_items > 0) && ($total_items > $max_items)) { if (($max_items > 0) && ($total_items > $max_items)) {
$total_items = $max_items; $total_items = $max_items;
} }
@ -311,7 +311,7 @@ class Feed
if ($entry->nodeName == 'outline') { if ($entry->nodeName == 'outline') {
$isrss = false; $isrss = false;
$plink = ''; $plink = '';
$uri = ''; $uri = '';
foreach ($entry->attributes as $attribute) { foreach ($entry->attributes as $attribute) {
switch ($attribute->nodeName) { switch ($attribute->nodeName) {
case 'title': case 'title':
@ -336,7 +336,7 @@ class Feed
} }
} }
$item['plink'] = $plink ?: $uri; $item['plink'] = $plink ?: $uri;
$item['uri'] = $uri ?: $plink; $item['uri'] = $uri ?: $plink;
if (!$isrss || empty($item['uri'])) { if (!$isrss || empty($item['uri'])) {
continue; continue;
} }
@ -476,9 +476,9 @@ class Feed
$enclosures = $xpath->query("enclosure|$atomns:link[@rel='enclosure']", $entry); $enclosures = $xpath->query("enclosure|$atomns:link[@rel='enclosure']", $entry);
if (!empty($enclosures)) { if (!empty($enclosures)) {
foreach ($enclosures as $enclosure) { foreach ($enclosures as $enclosure) {
$href = ''; $href = '';
$length = null; $length = null;
$type = null; $type = null;
foreach ($enclosure->attributes as $attribute) { foreach ($enclosure->attributes as $attribute) {
if (in_array($attribute->name, ['url', 'href'])) { if (in_array($attribute->name, ['url', 'href'])) {
@ -505,7 +505,7 @@ class Feed
} }
} }
$taglist = []; $taglist = [];
$categories = $xpath->query('category', $entry); $categories = $xpath->query('category', $entry);
foreach ($categories as $category) { foreach ($categories as $category) {
$taglist[] = $category->nodeValue; $taglist[] = $category->nodeValue;
@ -526,7 +526,7 @@ class Feed
} }
if (empty($body)) { if (empty($body)) {
$body = $summary; $body = $summary;
$summary = ''; $summary = '';
} }
@ -537,16 +537,16 @@ class Feed
} }
$item['body'] = self::formatBody($body, $basepath); $item['body'] = self::formatBody($body, $basepath);
$summary = self::formatBody($summary, $basepath); $summary = self::formatBody($summary, $basepath);
if (($item['body'] == '') && ($item['title'] != '')) { if (($item['body'] == '') && ($item['title'] != '')) {
$item['body'] = $item['title']; $item['body'] = $item['title'];
$item['title'] = ''; $item['title'] = '';
} }
if ($dryRun) { if ($dryRun) {
$item['attachments'] = $attachments; $item['attachments'] = $attachments;
$items[] = $item; $items[] = $item;
break; break;
} elseif (!Item::isValid($item)) { } elseif (!Item::isValid($item)) {
DI::logger()->info('Feed item is invalid', ['created' => $item['created'], 'uid' => $item['uid'], 'uri' => $item['uri']]); DI::logger()->info('Feed item is invalid', ['created' => $item['created'], 'uid' => $item['uid'], 'uri' => $item['uri']]);
@ -582,7 +582,7 @@ class Feed
$summary = ''; $summary = '';
} }
$saved_body = $item['body']; $saved_body = $item['body'];
$saved_title = $item['title']; $saved_title = $item['title'];
if (self::replaceBodyWithTitle($item['body'], $item['title'])) { if (self::replaceBodyWithTitle($item['body'], $item['title'])) {
@ -612,7 +612,7 @@ class Feed
// Take the data that was provided by the feed if the query is empty // Take the data that was provided by the feed if the query is empty
if (($data['type'] == 'link') && empty($data['title']) && empty($data['text'])) { if (($data['type'] == 'link') && empty($data['title']) && empty($data['text'])) {
$data['title'] = $saved_title; $data['title'] = $saved_title;
$item['body'] = $saved_body; $item['body'] = $saved_body;
} }
$data_text = strip_tags(trim($data['text'] ?? '')); $data_text = strip_tags(trim($data['text'] ?? ''));
@ -623,11 +623,11 @@ class Feed
} }
// We always strip the title since it will be added in the page information // We always strip the title since it will be added in the page information
$item['title'] = ''; $item['title'] = '';
$item['body'] = $item['body'] . "\n" . PageInfo::getFooterFromData($data, false); $item['body'] = $item['body'] . "\n" . PageInfo::getFooterFromData($data, false);
$taglist = $fetch_further_information == LocalRelationship::FFI_BOTH ? PageInfo::getTagsFromUrl($item['plink'], $preview, $contact['ffi_keyword_denylist'] ?? '') : []; $taglist = $fetch_further_information == LocalRelationship::FFI_BOTH ? PageInfo::getTagsFromUrl($item['plink'], $preview, $contact['ffi_keyword_denylist'] ?? '') : [];
$item['object-type'] = Activity\ObjectType::BOOKMARK; $item['object-type'] = Activity\ObjectType::BOOKMARK;
$attachments = []; $attachments = [];
foreach (['audio', 'video'] as $elementname) { foreach (['audio', 'video'] as $elementname) {
if (!empty($data[$elementname])) { if (!empty($data[$elementname])) {
@ -674,7 +674,7 @@ class Feed
DI::logger()->info('Stored feed', ['item' => $item]); DI::logger()->info('Stored feed', ['item' => $item]);
$notify = Item::isRemoteSelf($contact, $item); $notify = Item::isRemoteSelf($contact, $item);
$item['wall'] = (bool)$notify; $item['wall'] = (bool)$notify;
// Distributed items should have a well-formatted URI. // Distributed items should have a well-formatted URI.
@ -696,7 +696,7 @@ class Feed
Post\Delayed::publish($item, $notify, $taglist, $attachments); Post\Delayed::publish($item, $notify, $taglist, $attachments);
} else { } else {
$postings[] = [ $postings[] = [
'item' => $item, 'notify' => $notify, 'item' => $item, 'notify' => $notify,
'taglist' => $taglist, 'attachments' => $attachments 'taglist' => $taglist, 'attachments' => $attachments
]; ];
} }
@ -707,11 +707,11 @@ class Feed
if (!empty($postings)) { if (!empty($postings)) {
$min_posting = DI::config()->get('system', 'minimum_posting_interval', 0); $min_posting = DI::config()->get('system', 'minimum_posting_interval', 0);
$total = count($postings); $total = count($postings);
if ($total > 1) { if ($total > 1) {
// Posts shouldn't be delayed more than a day // Posts shouldn't be delayed more than a day
$interval = min(1440, self::getPollInterval($contact)); $interval = min(1440, self::getPollInterval($contact));
$delay = max(round(($interval * 60) / $total), 60 * $min_posting); $delay = max(round(($interval * 60) / $total), 60 * $min_posting);
DI::logger()->info('Got posting delay', ['delay' => $delay, 'interval' => $interval, 'items' => $total, 'cid' => $contact['id'], 'url' => $contact['url']]); DI::logger()->info('Got posting delay', ['delay' => $delay, 'interval' => $interval, 'items' => $total, 'cid' => $contact['id'], 'url' => $contact['url']]);
} else { } else {
$delay = 0; $delay = 0;
@ -795,15 +795,15 @@ class Feed
if (!empty($creation_dates)) { if (!empty($creation_dates)) {
// Count the post frequency and the earliest and latest post date // Count the post frequency and the earliest and latest post date
$frequency = []; $frequency = [];
$oldest = time(); $oldest = time();
$newest = 0; $newest = 0;
$newest_date = ''; $newest_date = '';
foreach ($creation_dates as $date) { foreach ($creation_dates as $date) {
$timestamp = strtotime($date); $timestamp = strtotime($date);
$day = intdiv($timestamp, 86400); $day = intdiv($timestamp, 86400);
$hour = $timestamp % 86400; $hour = $timestamp % 86400;
// Only have a look at values from the last seven days // Only have a look at values from the last seven days
if (((time() / 86400) - $day) < 7) { if (((time() / 86400) - $day) < 7) {
@ -824,7 +824,7 @@ class Feed
} }
if ($newest < $day) { if ($newest < $day) {
$newest = $day; $newest = $day;
$newest_date = $date; $newest_date = $date;
} }
} }
@ -862,7 +862,7 @@ class Feed
// Assume at least four hours between oldest and newest post per day - should be okay for news outlets // Assume at least four hours between oldest and newest post per day - should be okay for news outlets
$duration = max($entry['high'] - $entry['low'], 14400); $duration = max($entry['high'] - $entry['low'], 14400);
$ppd = (86400 / $duration) * $entry['count']; $ppd = (86400 / $duration) * $entry['count'];
if ($ppd > $max) { if ($ppd > $max) {
$max = $ppd; $max = $ppd;
} }
@ -980,7 +980,7 @@ class Feed
$pos = strrpos($title, '...'); $pos = strrpos($title, '...');
if ($pos > 0) { if ($pos > 0) {
$title = substr($title, 0, $pos); $title = substr($title, 0, $pos);
$body = substr($body, 0, $pos); $body = substr($body, 0, $pos);
} }
} }
return ($title == $body); return ($title == $body);
@ -1019,7 +1019,7 @@ class Feed
$previous_created = $last_update; $previous_created = $last_update;
$check_date = empty($last_update) ? '' : DateTimeFormat::utc($last_update); $check_date = empty($last_update) ? '' : DateTimeFormat::utc($last_update);
$authorid = Contact::getIdForURL($owner['url']); $authorid = Contact::getIdForURL($owner['url']);
$condition = [ $condition = [
"`uid` = ? AND `received` > ? AND NOT `deleted` AND `gravity` IN (?, ?) "`uid` = ? AND `received` > ? AND NOT `deleted` AND `gravity` IN (?, ?)
@ -1049,7 +1049,7 @@ class Feed
$items = Post::toArray($ret); $items = Post::toArray($ret);
$doc = new DOMDocument('1.0', 'utf-8'); $doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true; $doc->formatOutput = true;
$root = self::addHeader($doc, $owner, $filter); $root = self::addHeader($doc, $owner, $filter);
@ -1085,7 +1085,7 @@ class Feed
$root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed'); $root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed');
$doc->appendChild($root); $doc->appendChild($root);
$title = ''; $title = '';
$selfUri = '/feed/' . $owner['nick'] . '/'; $selfUri = '/feed/' . $owner['nick'] . '/';
switch ($filter) { switch ($filter) {
case 'activity': case 'activity':
@ -1198,7 +1198,7 @@ class Feed
'link', 'link',
'', '',
[ [
'rel' => 'alternate', 'type' => 'text/html', 'rel' => 'alternate', 'type' => 'text/html',
'href' => DI::baseUrl() . '/display/' . $item['guid'] 'href' => DI::baseUrl() . '/display/' . $item['guid']
] ]
); );
@ -1290,11 +1290,11 @@ class Feed
// Remove the share element before fetching the first line // Remove the share element before fetching the first line
$title = trim(preg_replace("/\[share.*?\](.*?)\[\/share\]/ism", "\n$1\n", $item['body'])); $title = trim(preg_replace("/\[share.*?\](.*?)\[\/share\]/ism", "\n$1\n", $item['body']));
$title = BBCode::toPlaintext($title) . "\n"; $title = BBCode::toPlaintext($title) . "\n";
$pos = strpos($title, "\n"); $pos = strpos($title, "\n");
$trailer = ''; $trailer = '';
if (($pos == 0) || ($pos > 100)) { if (($pos == 0) || ($pos > 100)) {
$pos = 100; $pos = 100;
$trailer = '...'; $trailer = '...';
} }
@ -1347,8 +1347,8 @@ class Feed
{ {
foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO, Post\Media::DOCUMENT, Post\Media::TORRENT]) as $attachment) { foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO, Post\Media::DOCUMENT, Post\Media::TORRENT]) as $attachment) {
$attributes = ['rel' => 'enclosure', $attributes = ['rel' => 'enclosure',
'href' => $attachment['url'], 'href' => $attachment['url'],
'type' => $attachment['mimetype']]; 'type' => $attachment['mimetype']];
if (!empty($attachment['size'])) { if (!empty($attachment['size'])) {
$attributes['length'] = intval($attachment['size']); $attributes['length'] = intval($attachment['size']);
@ -1401,9 +1401,9 @@ class Feed
if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) { if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) {
$entry->setAttribute('xmlns:activity', ActivityNamespace::ACTIVITY); $entry->setAttribute('xmlns:activity', ActivityNamespace::ACTIVITY);
$contact = Contact::getByURL($item['author-link']) ?: $owner; $contact = Contact::getByURL($item['author-link']) ?: $owner;
$contact['nickname'] = $contact['nickname'] ?? $contact['nick']; $contact['nickname'] = $contact['nickname'] ?? $contact['nick'];
$author = self::addAuthor($doc, $contact); $author = self::addAuthor($doc, $contact);
$entry->appendChild($author); $entry->appendChild($author);
} }
} else { } else {

View file

@ -33,7 +33,7 @@ use Friendica\Util\Strings;
class Relay class Relay
{ {
const SCOPE_NONE = ''; const SCOPE_NONE = '';
const SCOPE_ALL = 'all'; const SCOPE_ALL = 'all';
const SCOPE_TAGS = 'tags'; const SCOPE_TAGS = 'tags';
/** /**
@ -76,7 +76,7 @@ class Relay
if (!empty($causerid)) { if (!empty($causerid)) {
$contact = Contact::getById($causerid, ['url']); $contact = Contact::getById($causerid, ['url']);
$causer = $contact['url'] ?? ''; $causer = $contact['url'] ?? '';
} else { } else {
$causer = ''; $causer = '';
} }
@ -86,7 +86,7 @@ class Relay
if ($scope == self::SCOPE_TAGS) { if ($scope == self::SCOPE_TAGS) {
$tagList = self::getSubscribedTags(); $tagList = self::getSubscribedTags();
} else { } else {
$tagList = []; $tagList = [];
} }
$denyTags = Strings::getTagArrayByString($config->get('system', 'relay_deny_tags')); $denyTags = Strings::getTagArrayByString($config->get('system', 'relay_deny_tags'));
@ -96,7 +96,7 @@ class Relay
$max_tags = $config->get('system', 'relay_max_tags'); $max_tags = $config->get('system', 'relay_max_tags');
if ($max_tags && (count($tags) > $max_tags) && preg_match('/[^@!#]\[url\=.*?\].*?\[\/url\]/ism', $body)) { if ($max_tags && (count($tags) > $max_tags) && preg_match('/[^@!#]\[url\=.*?\].*?\[\/url\]/ism', $body)) {
$cleaned = preg_replace('/[@!#]\[url\=.*?\].*?\[\/url\]/ism', '', $body); $cleaned = preg_replace('/[@!#]\[url\=.*?\].*?\[\/url\]/ism', '', $body);
$content_cleaned = mb_strtolower(BBCode::toPlaintext($cleaned, false)); $content_cleaned = mb_strtolower(BBCode::toPlaintext($cleaned, false));
if (strlen($content_cleaned) < strlen($content) / 2) { if (strlen($content_cleaned) < strlen($content) / 2) {
@ -223,12 +223,12 @@ class Relay
} }
$condition = ['uid' => 0, 'gsid' => $gserver['id'], 'contact-type' => Contact::TYPE_RELAY]; $condition = ['uid' => 0, 'gsid' => $gserver['id'], 'contact-type' => Contact::TYPE_RELAY];
$old = DBA::selectFirst('contact', [], $condition); $old = DBA::selectFirst('contact', [], $condition);
if (!DBA::isResult($old)) { if (!DBA::isResult($old)) {
$condition = ['uid' => 0, 'nurl' => Strings::normaliseLink($gserver['url'])]; $condition = ['uid' => 0, 'nurl' => Strings::normaliseLink($gserver['url'])];
$old = DBA::selectFirst('contact', [], $condition); $old = DBA::selectFirst('contact', [], $condition);
if (DBA::isResult($old)) { if (DBA::isResult($old)) {
$fields['gsid'] = $gserver['id']; $fields['gsid'] = $gserver['id'];
$fields['contact-type'] = Contact::TYPE_RELAY; $fields['contact-type'] = Contact::TYPE_RELAY;
DI::logger()->info('Assigning missing data for relay contact', ['server' => $gserver['url'], 'id' => $old['id']]); DI::logger()->info('Assigning missing data for relay contact', ['server' => $gserver['url'], 'id' => $old['id']]);
} }
@ -245,15 +245,15 @@ class Relay
Contact::update($fields, ['id' => $old['id']], $old); Contact::update($fields, ['id' => $old['id']], $old);
} else { } else {
$default = ['created' => DateTimeFormat::utcNow(), $default = ['created' => DateTimeFormat::utcNow(),
'name' => 'relay', 'nick' => 'relay', 'url' => $gserver['url'], 'name' => 'relay', 'nick' => 'relay', 'url' => $gserver['url'],
'nurl' => Strings::normaliseLink($gserver['url']), 'nurl' => Strings::normaliseLink($gserver['url']),
'network' => Protocol::DIASPORA, 'uid' => 0, 'network' => Protocol::DIASPORA, 'uid' => 0,
'batch' => $gserver['url'] . '/receive/public', 'batch' => $gserver['url'] . '/receive/public',
'rel' => Contact::FOLLOWER, 'blocked' => false, 'rel' => Contact::FOLLOWER, 'blocked' => false,
'pending' => false, 'writable' => true, 'pending' => false, 'writable' => true,
'gsid' => $gserver['id'], 'gsid' => $gserver['id'],
'unsearchable' => true, 'unsearchable' => true,
'baseurl' => $gserver['url'], 'contact-type' => Contact::TYPE_RELAY]; 'baseurl' => $gserver['url'], 'contact-type' => Contact::TYPE_RELAY];
$fields = array_merge($default, $fields); $fields = array_merge($default, $fields);
@ -278,14 +278,14 @@ class Relay
$relay_contact = $contact; $relay_contact = $contact;
} elseif (empty($contact['baseurl'])) { } elseif (empty($contact['baseurl'])) {
if (!empty($contact['batch'])) { if (!empty($contact['batch'])) {
$condition = ['uid' => 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => Contact::TYPE_RELAY]; $condition = ['uid' => 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => Contact::TYPE_RELAY];
$relay_contact = DBA::selectFirst('contact', [], $condition); $relay_contact = DBA::selectFirst('contact', [], $condition);
} else { } else {
return; return;
} }
} else { } else {
$gserver = ['id' => $contact['gsid'] ?: GServer::getID($contact['baseurl'], true), $gserver = ['id' => $contact['gsid'] ?: GServer::getID($contact['baseurl'], true),
'url' => $contact['baseurl'], 'network' => $contact['network']]; 'url' => $contact['baseurl'], 'network' => $contact['network']];
$relay_contact = self::getContact($gserver, []); $relay_contact = self::getContact($gserver, []);
} }
@ -324,7 +324,7 @@ class Relay
DBA::close($servers); DBA::close($servers);
// All tags of the current post // All tags of the current post
$tags = DBA::select('tag-view', ['name'], ['uri-id' => $parent['uri-id'], 'type' => Tag::HASHTAG]); $tags = DBA::select('tag-view', ['name'], ['uri-id' => $parent['uri-id'], 'type' => Tag::HASHTAG]);
$taglist = []; $taglist = [];
while ($tag = DBA::fetch($tags)) { while ($tag = DBA::fetch($tags)) {
$taglist[] = $tag['name']; $taglist[] = $tag['name'];
@ -376,8 +376,11 @@ class Relay
*/ */
public static function getList(array $fields = []): array public static function getList(array $fields = []): array
{ {
return DBA::selectToArray('apcontact', $fields, return DBA::selectToArray(
["`type` IN (?, ?) AND `url` IN (SELECT `url` FROM `contact` WHERE `uid` = ? AND `rel` = ?)", 'Application', 'Service', 0, Contact::FRIEND]); 'apcontact',
$fields,
["`type` IN (?, ?) AND `url` IN (SELECT `url` FROM `contact` WHERE `uid` = ? AND `rel` = ?)", 'Application', 'Service', 0, Contact::FRIEND]
);
} }
/** /**
@ -392,7 +395,7 @@ class Relay
{ {
// Fetch the relay contact // Fetch the relay contact
$condition = ['uid' => 0, 'gsid' => $gserver['id'], 'contact-type' => Contact::TYPE_RELAY]; $condition = ['uid' => 0, 'gsid' => $gserver['id'], 'contact-type' => Contact::TYPE_RELAY];
$contact = DBA::selectFirst('contact', $fields, $condition); $contact = DBA::selectFirst('contact', $fields, $condition);
if (DBA::isResult($contact)) { if (DBA::isResult($contact)) {
if ($contact['archive'] || $contact['blocked']) { if ($contact['archive'] || $contact['blocked']) {
return false; return false;

View file

@ -103,7 +103,7 @@ class BasicAuth
* *
* @return integer User ID * @return integer User ID
*/ */
private static function getUserIdByAuth(bool $do_login = true):int private static function getUserIdByAuth(bool $do_login = true): int
{ {
self::$current_user_id = 0; self::$current_user_id = 0;
@ -111,14 +111,14 @@ class BasicAuth
if (!empty($_SERVER['REDIRECT_REMOTE_USER'])) { if (!empty($_SERVER['REDIRECT_REMOTE_USER'])) {
$userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6)); $userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6));
if (!empty($userpass) && strpos($userpass, ':')) { if (!empty($userpass) && strpos($userpass, ':')) {
list($name, $password) = explode(':', $userpass); list($name, $password) = explode(':', $userpass);
$_SERVER['PHP_AUTH_USER'] = $name; $_SERVER['PHP_AUTH_USER'] = $name;
$_SERVER['PHP_AUTH_PW'] = $password; $_SERVER['PHP_AUTH_PW'] = $password;
} }
} }
$user = $_SERVER['PHP_AUTH_USER'] ?? ''; $user = $_SERVER['PHP_AUTH_USER'] ?? '';
$password = $_SERVER['PHP_AUTH_PW'] ?? ''; $password = $_SERVER['PHP_AUTH_PW'] ?? '';
// allow "user@server" login (but ignore 'server' part) // allow "user@server" login (but ignore 'server' part)
$at = strstr($user, "@", true); $at = strstr($user, "@", true);
@ -130,10 +130,10 @@ class BasicAuth
$record = null; $record = null;
$addon_auth = [ $addon_auth = [
'username' => trim($user), 'username' => trim($user),
'password' => trim($password), 'password' => trim($password),
'authenticated' => 0, 'authenticated' => 0,
'user_record' => null, 'user_record' => null,
]; ];
/* /*
@ -148,7 +148,7 @@ class BasicAuth
} else { } else {
try { try {
$user_id = User::getIdFromPasswordAuthentication(trim($user), trim($password), true); $user_id = User::getIdFromPasswordAuthentication(trim($user), trim($password), true);
$record = DBA::selectFirst('user', [], ['uid' => $user_id]); $record = DBA::selectFirst('user', [], ['uid' => $user_id]);
} catch (Exception $ex) { } catch (Exception $ex) {
$record = []; $record = [];
} }

View file

@ -120,7 +120,7 @@ class OAuth
if (!empty($redirect_uri)) { if (!empty($redirect_uri)) {
$redirect_uri = strtok($redirect_uri, '?'); $redirect_uri = strtok($redirect_uri, '?');
$condition = DBA::mergeConditions($condition, ["`redirect_uri` LIKE ?", '%' . $redirect_uri . '%']); $condition = DBA::mergeConditions($condition, ["`redirect_uri` LIKE ?", '%' . $redirect_uri . '%']);
} }
$application = DBA::selectFirst('application', [], $condition); $application = DBA::selectFirst('application', [], $condition);

View file

@ -89,7 +89,7 @@ class Crypto
openssl_pkey_export($result, $response['prvkey']); openssl_pkey_export($result, $response['prvkey']);
// Get public key // Get public key
$pkey = openssl_pkey_get_details($result); $pkey = openssl_pkey_get_details($result);
$response['pubkey'] = $pkey["key"]; $response['pubkey'] = $pkey["key"];
return $response; return $response;
@ -164,9 +164,9 @@ class Crypto
} }
$fn = 'encrypt' . strtoupper($alg); $fn = 'encrypt' . strtoupper($alg);
if (method_exists(__CLASS__, $fn)) { if (method_exists(__CLASS__, $fn)) {
$result = ['encrypted' => true]; $result = ['encrypted' => true];
$key = random_bytes(256); $key = random_bytes(256);
$iv = random_bytes(256); $iv = random_bytes(256);
$result['data'] = Strings::base64UrlEncode(self::$fn($data, $key, $iv), true); $result['data'] = Strings::base64UrlEncode(self::$fn($data, $key, $iv), true);
// log the offending call so we can track it down // log the offending call so we can track it down
@ -205,9 +205,9 @@ class Crypto
DI::logger()->notice('aes_encapsulate: no key. data: ' . $data); DI::logger()->notice('aes_encapsulate: no key. data: ' . $data);
} }
$key = random_bytes(32); $key = random_bytes(32);
$iv = random_bytes(16); $iv = random_bytes(16);
$result = ['encrypted' => true]; $result = ['encrypted' => true];
$result['data'] = Strings::base64UrlEncode(self::encryptAES256CBC($data, $key, $iv), true); $result['data'] = Strings::base64UrlEncode(self::encryptAES256CBC($data, $key, $iv), true);
// log the offending call so we can track it down // log the offending call so we can track it down

View file

@ -23,7 +23,7 @@ class DateTimeFormat
const JSON = 'Y-m-d\TH:i:s.v\Z'; const JSON = 'Y-m-d\TH:i:s.v\Z';
const API = 'D M d H:i:s +0000 Y'; const API = 'D M d H:i:s +0000 Y';
static $localTimezone = 'UTC'; public static $localTimezone = 'UTC';
public static function setLocalTimeZone(string $timezone) public static function setLocalTimeZone(string $timezone)
{ {

View file

@ -49,19 +49,19 @@ class HTTPSignature
{ {
$headers = null; $headers = null;
$spoofable = false; $spoofable = false;
$result = [ $result = [
'signer' => '', 'signer' => '',
'header_signed' => false, 'header_signed' => false,
'header_valid' => false 'header_valid' => false
]; ];
// Decide if $data arrived via controller submission or curl. // Decide if $data arrived via controller submission or curl.
$headers = []; $headers = [];
$headers['(request-target)'] = strtolower(DI::args()->getMethod()) . ' ' . $_SERVER['REQUEST_URI']; $headers['(request-target)'] = strtolower(DI::args()->getMethod()) . ' ' . $_SERVER['REQUEST_URI'];
foreach ($_SERVER as $k => $v) { foreach ($_SERVER as $k => $v) {
if (strpos($k, 'HTTP_') === 0) { if (strpos($k, 'HTTP_') === 0) {
$field = str_replace('_', '-', strtolower(substr($k, 5))); $field = str_replace('_', '-', strtolower(substr($k, 5)));
$headers[$field] = $v; $headers[$field] = $v;
} }
} }
@ -98,7 +98,7 @@ class HTTPSignature
if ($key && function_exists($key)) { if ($key && function_exists($key)) {
$result['signer'] = $sig_block['keyId']; $result['signer'] = $sig_block['keyId'];
$key = $key($sig_block['keyId']); $key = $key($sig_block['keyId']);
} }
DI::logger()->info('Got keyID ' . $sig_block['keyId']); DI::logger()->info('Got keyID ' . $sig_block['keyId']);
@ -136,7 +136,7 @@ class HTTPSignature
$return_headers = $head; $return_headers = $head;
} }
$alg = 'sha512'; $alg = 'sha512';
$algorithm = 'rsa-sha512'; $algorithm = 'rsa-sha512';
$x = self::sign($head, $prvkey, $alg); $x = self::sign($head, $prvkey, $alg);
@ -158,7 +158,7 @@ class HTTPSignature
*/ */
private static function sign(array $head, string $prvkey, string $alg = 'sha256'): array private static function sign(array $head, string $prvkey, string $alg = 'sha256'): array
{ {
$ret = []; $ret = [];
$headers = ''; $headers = '';
$fields = ''; $fields = '';
@ -220,10 +220,10 @@ class HTTPSignature
} }
$return = [ $return = [
'keyId' => $headers['keyId'] ?? '', 'keyId' => $headers['keyId'] ?? '',
'algorithm' => $headers['algorithm'] ?? 'rsa-sha256', 'algorithm' => $headers['algorithm'] ?? 'rsa-sha256',
'created' => $headers['created'] ?? null, 'created' => $headers['created'] ?? null,
'expires' => $headers['expires'] ?? null, 'expires' => $headers['expires'] ?? null,
'headers' => explode(' ', $headers['headers'] ?? ''), 'headers' => explode(' ', $headers['headers'] ?? ''),
'signature' => base64_decode(preg_replace('/\s+/', '', $headers['signature'] ?? '')), 'signature' => base64_decode(preg_replace('/\s+/', '', $headers['signature'] ?? '')),
]; ];
@ -268,17 +268,17 @@ class HTTPSignature
$content = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $content = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
// Header data that is about to be signed. // Header data that is about to be signed.
$host = strtolower(parse_url($target, PHP_URL_HOST)); $host = strtolower(parse_url($target, PHP_URL_HOST));
$path = parse_url($target, PHP_URL_PATH); $path = parse_url($target, PHP_URL_PATH);
$digest = 'SHA-256=' . base64_encode(hash('sha256', $content, true)); $digest = 'SHA-256=' . base64_encode(hash('sha256', $content, true));
$content_length = strlen($content); $content_length = strlen($content);
$date = DateTimeFormat::utcNow(DateTimeFormat::HTTP); $date = DateTimeFormat::utcNow(DateTimeFormat::HTTP);
$headers = [ $headers = [
'Date' => $date, 'Date' => $date,
'Content-Length' => $content_length, 'Content-Length' => $content_length,
'Digest' => $digest, 'Digest' => $digest,
'Host' => $host 'Host' => $host
]; ];
$signed_data = "(request-target): post " . $path . "\ndate: " . $date . "\ncontent-length: " . $content_length . "\ndigest: " . $digest . "\nhost: " . $host; $signed_data = "(request-target): post " . $path . "\ndate: " . $date . "\ncontent-length: " . $content_length . "\ndigest: " . $digest . "\nhost: " . $host;
@ -289,7 +289,7 @@ class HTTPSignature
$headers['Content-Type'] = 'application/activity+json'; $headers['Content-Type'] = 'application/activity+json';
$postResult = DI::httpClient()->post($target, $content, $headers, DI::config()->get('system', 'curl_timeout'), HttpClientRequest::ACTIVITYPUB); $postResult = DI::httpClient()->post($target, $content, $headers, DI::config()->get('system', 'curl_timeout'), HttpClientRequest::ACTIVITYPUB);
$return_code = $postResult->getReturnCode(); $return_code = $postResult->getReturnCode();
DI::logger()->info('Transmit to ' . $target . ' returned ' . $return_code); DI::logger()->info('Transmit to ' . $target . ' returned ' . $return_code);
@ -318,10 +318,10 @@ class HTTPSignature
return false; return false;
} }
$activity = JsonLD::compact($data); $activity = JsonLD::compact($data);
$type = JsonLD::fetchElement($activity, '@type'); $type = JsonLD::fetchElement($activity, '@type');
$trust_source = true; $trust_source = true;
$object_data = Receiver::prepareObjectData($activity, $uid, true, $trust_source, $owner['url']); $object_data = Receiver::prepareObjectData($activity, $uid, true, $trust_source, $owner['url']);
if (empty($object_data)) { if (empty($object_data)) {
return false; return false;
} }
@ -387,7 +387,7 @@ class HTTPSignature
* @param int $gsid Server ID * @param int $gsid Server ID
* @throws \Exception * @throws \Exception
*/ */
static public function setInboxStatus(string $url, bool $success, bool $shared = false, int $gsid = null) public static function setInboxStatus(string $url, bool $success, bool $shared = false, int $gsid = null)
{ {
$now = DateTimeFormat::utcNow(); $now = DateTimeFormat::utcNow();
@ -434,7 +434,7 @@ class HTTPSignature
$stamp1 = strtotime($status['success']); $stamp1 = strtotime($status['success']);
} }
$stamp2 = strtotime($now); $stamp2 = strtotime($now);
$previous_stamp = strtotime($status['previous']); $previous_stamp = strtotime($status['previous']);
// Archive the inbox when there had been failures for five days. // Archive the inbox when there had been failures for five days.
@ -629,19 +629,19 @@ class HTTPSignature
$actor = ''; $actor = '';
} }
$headers = []; $headers = [];
$headers['(request-target)'] = strtolower(DI::args()->getMethod()) . ' ' . parse_url($http_headers['REQUEST_URI'], PHP_URL_PATH); $headers['(request-target)'] = strtolower(DI::args()->getMethod()) . ' ' . parse_url($http_headers['REQUEST_URI'], PHP_URL_PATH);
// First take every header // First take every header
foreach ($http_headers as $k => $v) { foreach ($http_headers as $k => $v) {
$field = str_replace('_', '-', strtolower($k)); $field = str_replace('_', '-', strtolower($k));
$headers[$field] = $v; $headers[$field] = $v;
} }
// Now add every http header // Now add every http header
foreach ($http_headers as $k => $v) { foreach ($http_headers as $k => $v) {
if (strpos($k, 'HTTP_') === 0) { if (strpos($k, 'HTTP_') === 0) {
$field = str_replace('_', '-', strtolower(substr($k, 5))); $field = str_replace('_', '-', strtolower(substr($k, 5)));
$headers[$field] = $v; $headers[$field] = $v;
} }
} }

View file

@ -403,19 +403,19 @@ class Images
// constrain the width - let the height float. // constrain the width - let the height float.
if ((($height * 9) / 16) > $width) { if ((($height * 9) / 16) > $width) {
$dest_width = $max; $dest_width = $max;
$dest_height = intval(ceil(($height * $max) / $width)); $dest_height = intval(ceil(($height * $max) / $width));
} elseif ($width > $height) { } elseif ($width > $height) {
// else constrain both dimensions // else constrain both dimensions
$dest_width = $max; $dest_width = $max;
$dest_height = intval(ceil(($height * $max) / $width)); $dest_height = intval(ceil(($height * $max) / $width));
} else { } else {
$dest_width = intval(ceil(($width * $max) / $height)); $dest_width = intval(ceil(($width * $max) / $height));
$dest_height = $max; $dest_height = $max;
} }
} else { } else {
if ($width > $max) { if ($width > $max) {
$dest_width = $max; $dest_width = $max;
$dest_height = intval(ceil(($height * $max) / $width)); $dest_height = intval(ceil(($height * $max) / $width));
} else { } else {
if ($height > $max) { if ($height > $max) {
@ -423,14 +423,14 @@ class Images
// but width is OK - don't do anything // but width is OK - don't do anything
if ((($height * 9) / 16) > $width) { if ((($height * 9) / 16) > $width) {
$dest_width = $width; $dest_width = $width;
$dest_height = $height; $dest_height = $height;
} else { } else {
$dest_width = intval(ceil(($width * $max) / $height)); $dest_width = intval(ceil(($width * $max) / $height));
$dest_height = $max; $dest_height = $max;
} }
} else { } else {
$dest_width = $width; $dest_width = $width;
$dest_height = $height; $dest_height = $height;
} }
} }

View file

@ -62,7 +62,7 @@ class JsonLD
break; break;
default: default:
switch (parse_url($url, PHP_URL_PATH)) { switch (parse_url($url, PHP_URL_PATH)) {
case '/schemas/litepub-0.1.jsonld'; case '/schemas/litepub-0.1.jsonld':
$url = DI::basePath() . '/static/litepub-0.1.jsonld'; $url = DI::basePath() . '/static/litepub-0.1.jsonld';
break; break;
case '/apschema/v1.2': case '/apschema/v1.2':
@ -117,10 +117,10 @@ class JsonLD
$jsonobj = json_decode(json_encode($json, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); $jsonobj = json_decode(json_encode($json, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
try { try {
$normalized = jsonld_normalize($jsonobj, array('algorithm' => 'URDNA2015', 'format' => 'application/nquads')); $normalized = jsonld_normalize($jsonobj, ['algorithm' => 'URDNA2015', 'format' => 'application/nquads']);
} catch (Exception $e) { } catch (Exception $e) {
$normalized = false; $normalized = false;
$messages = []; $messages = [];
$currentException = $e; $currentException = $e;
do { do {
$messages[] = $currentException->getMessage(); $messages[] = $currentException->getMessage();
@ -148,23 +148,23 @@ class JsonLD
jsonld_set_document_loader('Friendica\Util\JsonLD::documentLoader'); jsonld_set_document_loader('Friendica\Util\JsonLD::documentLoader');
$context = (object)[ $context = (object)[
'as' => 'https://www.w3.org/ns/activitystreams#', 'as' => 'https://www.w3.org/ns/activitystreams#',
'w3id' => 'https://w3id.org/security#', 'w3id' => 'https://w3id.org/security#',
'ldp' => (object)['@id' => 'http://www.w3.org/ns/ldp#', '@type' => '@id'], 'ldp' => (object)['@id' => 'http://www.w3.org/ns/ldp#', '@type' => '@id'],
'vcard' => (object)['@id' => 'http://www.w3.org/2006/vcard/ns#', '@type' => '@id'], 'vcard' => (object)['@id' => 'http://www.w3.org/2006/vcard/ns#', '@type' => '@id'],
'dfrn' => (object)['@id' => 'http://purl.org/macgirvin/dfrn/1.0/', '@type' => '@id'], 'dfrn' => (object)['@id' => 'http://purl.org/macgirvin/dfrn/1.0/', '@type' => '@id'],
'diaspora' => (object)['@id' => 'https://diasporafoundation.org/ns/', '@type' => '@id'], 'diaspora' => (object)['@id' => 'https://diasporafoundation.org/ns/', '@type' => '@id'],
'ostatus' => (object)['@id' => 'http://ostatus.org#', '@type' => '@id'], 'ostatus' => (object)['@id' => 'http://ostatus.org#', '@type' => '@id'],
'dc' => (object)['@id' => 'http://purl.org/dc/terms/', '@type' => '@id'], 'dc' => (object)['@id' => 'http://purl.org/dc/terms/', '@type' => '@id'],
'toot' => (object)['@id' => 'http://joinmastodon.org/ns#', '@type' => '@id'], 'toot' => (object)['@id' => 'http://joinmastodon.org/ns#', '@type' => '@id'],
'litepub' => (object)['@id' => 'http://litepub.social/ns#', '@type' => '@id'], 'litepub' => (object)['@id' => 'http://litepub.social/ns#', '@type' => '@id'],
'sc' => (object)['@id' => 'http://schema.org#', '@type' => '@id'], 'sc' => (object)['@id' => 'http://schema.org#', '@type' => '@id'],
'pt' => (object)['@id' => 'https://joinpeertube.org/ns#', '@type' => '@id'], 'pt' => (object)['@id' => 'https://joinpeertube.org/ns#', '@type' => '@id'],
'mobilizon' => (object)['@id' => 'https://joinmobilizon.org/ns#', '@type' => '@id'], 'mobilizon' => (object)['@id' => 'https://joinmobilizon.org/ns#', '@type' => '@id'],
'fedibird' => (object)['@id' => 'http://fedibird.com/ns#', '@type' => '@id'], 'fedibird' => (object)['@id' => 'http://fedibird.com/ns#', '@type' => '@id'],
'misskey' => (object)['@id' => 'https://misskey-hub.net/ns#', '@type' => '@id'], 'misskey' => (object)['@id' => 'https://misskey-hub.net/ns#', '@type' => '@id'],
'pixelfed' => (object)['@id' => 'http://pixelfed.org/ns#', '@type' => '@id'], 'pixelfed' => (object)['@id' => 'http://pixelfed.org/ns#', '@type' => '@id'],
'lemmy' => (object)['@id' => 'https://join-lemmy.org/ns#', '@type' => '@id'], 'lemmy' => (object)['@id' => 'https://join-lemmy.org/ns#', '@type' => '@id'],
]; ];
$orig_json = $json; $orig_json = $json;

View file

@ -74,14 +74,14 @@ class LDSignature
public static function sign(array $data, array $owner): array public static function sign(array $data, array $owner): array
{ {
$options = [ $options = [
'type' => 'RsaSignature2017', 'type' => 'RsaSignature2017',
'nonce' => Strings::getRandomHex(64), 'nonce' => Strings::getRandomHex(64),
'creator' => $owner['url'] . '#main-key', 'creator' => $owner['url'] . '#main-key',
'created' => DateTimeFormat::utcNow(DateTimeFormat::ATOM), 'created' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
]; ];
$ohash = self::hash(self::signableOptions($options)); $ohash = self::hash(self::signableOptions($options));
$dhash = self::hash(self::signableData($data)); $dhash = self::hash(self::signableData($data));
$options['signatureValue'] = base64_encode(Crypto::rsaSign($ohash . $dhash, $owner['uprvkey'])); $options['signatureValue'] = base64_encode(Crypto::rsaSign($ohash . $dhash, $owner['uprvkey']));
return array_merge($data, ['signature' => $options]); return array_merge($data, ['signature' => $options]);

View file

@ -20,7 +20,6 @@ use Psr\Http\Message\UriInterface;
class Network class Network
{ {
/** /**
* Return raw post data from a post request * Return raw post data from a post request
* *
@ -58,7 +57,7 @@ class Network
} }
$xrd_timeout = DI::config()->get('system', 'xrd_timeout'); $xrd_timeout = DI::config()->get('system', 'xrd_timeout');
$host = parse_url($url, PHP_URL_HOST); $host = parse_url($url, PHP_URL_HOST);
if (empty($host) || !(filter_var($host, FILTER_VALIDATE_IP) || @dns_get_record($host . '.', DNS_A + DNS_AAAA))) { if (empty($host) || !(filter_var($host, FILTER_VALIDATE_IP) || @dns_get_record($host . '.', DNS_A + DNS_AAAA))) {
return false; return false;
@ -66,7 +65,7 @@ class Network
if (in_array(parse_url($url, PHP_URL_SCHEME), ['https', 'http'])) { if (in_array(parse_url($url, PHP_URL_SCHEME), ['https', 'http'])) {
$options = [HttpClientOptions::VERIFY => true, HttpClientOptions::TIMEOUT => $xrd_timeout, $options = [HttpClientOptions::VERIFY => true, HttpClientOptions::TIMEOUT => $xrd_timeout,
HttpClientOptions::REQUEST => HttpClientRequest::URLVERIFIER]; HttpClientOptions::REQUEST => HttpClientRequest::URLVERIFIER];
try { try {
$curlResult = DI::httpClient()->head($url, $options); $curlResult = DI::httpClient()->head($url, $options);
} catch (\Exception $e) { } catch (\Exception $e) {
@ -298,9 +297,9 @@ class Network
public static function lookupAvatarByEmail(string $email): string public static function lookupAvatarByEmail(string $email): string
{ {
$avatar['size'] = 300; $avatar['size'] = 300;
$avatar['email'] = $email; $avatar['email'] = $email;
$avatar['url'] = ''; $avatar['url'] = '';
$avatar['success'] = false; $avatar['success'] = false;
Hook::callAll('avatar_lookup', $avatar); Hook::callAll('avatar_lookup', $avatar);
@ -340,18 +339,18 @@ class Network
'fb_action_ids', 'fb_action_types', 'fb_ref', 'fb_action_ids', 'fb_action_types', 'fb_ref',
'awesm', 'wtrid', 'awesm', 'wtrid',
'woo_campaign', 'woo_source', 'woo_medium', 'woo_content', 'woo_term'] 'woo_campaign', 'woo_source', 'woo_medium', 'woo_content', 'woo_term']
) )
) { ) {
$pair = $param . '=' . urlencode($value); $pair = $param . '=' . urlencode($value);
$url = str_replace($pair, '', $url); $url = str_replace($pair, '', $url);
// Second try: if the url isn't encoded completely // Second try: if the url isn't encoded completely
$pair = $param . '=' . str_replace(' ', '+', $value); $pair = $param . '=' . str_replace(' ', '+', $value);
$url = str_replace($pair, '', $url); $url = str_replace($pair, '', $url);
// Third try: Maybe the url isn't encoded at all // Third try: Maybe the url isn't encoded at all
$pair = $param . '=' . $value; $pair = $param . '=' . $value;
$url = str_replace($pair, '', $url); $url = str_replace($pair, '', $url);
$url = str_replace(['?&', '&&'], ['?', ''], $url); $url = str_replace(['?&', '&&'], ['?', ''], $url);
} }
@ -383,7 +382,7 @@ class Network
$base = [ $base = [
'scheme' => parse_url($basepath, PHP_URL_SCHEME), 'scheme' => parse_url($basepath, PHP_URL_SCHEME),
'host' => parse_url($basepath, PHP_URL_HOST), 'host' => parse_url($basepath, PHP_URL_HOST),
]; ];
$parts = array_merge($base, parse_url('/' . ltrim($url, '/'))); $parts = array_merge($base, parse_url('/' . ltrim($url, '/')));
@ -463,7 +462,7 @@ class Network
$pathparts1 = explode('/', $parts1['path']); $pathparts1 = explode('/', $parts1['path']);
$pathparts2 = explode('/', $parts2['path']); $pathparts2 = explode('/', $parts2['path']);
$i = 0; $i = 0;
$path = ''; $path = '';
do { do {
$path1 = $pathparts1[$i] ?? ''; $path1 = $pathparts1[$i] ?? '';
@ -491,7 +490,7 @@ class Network
$parts = parse_url($uri); $parts = parse_url($uri);
if (!empty($parts['scheme']) && !empty($parts['host'])) { if (!empty($parts['scheme']) && !empty($parts['host'])) {
$parts['host'] = self::idnToAscii($parts['host']); $parts['host'] = self::idnToAscii($parts['host']);
$uri = (string)Uri::fromParts($parts); $uri = (string)Uri::fromParts($parts);
} else { } else {
$parts = explode('@', $uri); $parts = explode('@', $uri);
if (count($parts) == 2) { if (count($parts) == 2) {

View file

@ -77,7 +77,7 @@ class ParseUrl
return []; return [];
} }
$contenttype = $curlResult->getContentType(); $contenttype = $curlResult->getContentType();
if (empty($contenttype)) { if (empty($contenttype)) {
return ['application', 'octet-stream']; return ['application', 'octet-stream'];
} }
@ -108,14 +108,16 @@ class ParseUrl
{ {
if (empty($url)) { if (empty($url)) {
return [ return [
'url' => '', 'url' => '',
'type' => 'error', 'type' => 'error',
]; ];
} }
$urlHash = hash('sha256', $url); $urlHash = hash('sha256', $url);
$parsed_url = DBA::selectFirst('parsed_url', ['content'], $parsed_url = DBA::selectFirst(
'parsed_url',
['content'],
['url_hash' => $urlHash, 'oembed' => false] ['url_hash' => $urlHash, 'oembed' => false]
); );
if (!empty($parsed_url['content'])) { if (!empty($parsed_url['content'])) {
@ -186,7 +188,7 @@ class ParseUrl
{ {
if (empty($url)) { if (empty($url)) {
return [ return [
'url' => '', 'url' => '',
'type' => 'error', 'type' => 'error',
]; ];
} }
@ -203,8 +205,8 @@ class ParseUrl
$url = Network::stripTrackingQueryParams($url); $url = Network::stripTrackingQueryParams($url);
$siteinfo = [ $siteinfo = [
'url' => $url, 'url' => $url,
'type' => 'link', 'type' => 'link',
'expires' => DateTimeFormat::utc(self::DEFAULT_EXPIRATION_FAILURE), 'expires' => DateTimeFormat::utc(self::DEFAULT_EXPIRATION_FAILURE),
]; ];
@ -244,12 +246,12 @@ class ParseUrl
if ($cacheControlHeader = $curlResult->getHeader('Cache-Control')[0] ?? '') { if ($cacheControlHeader = $curlResult->getHeader('Cache-Control')[0] ?? '') {
if (preg_match('/max-age=([0-9]+)/i', $cacheControlHeader, $matches)) { if (preg_match('/max-age=([0-9]+)/i', $cacheControlHeader, $matches)) {
$maxAge = max(86400, (int)array_pop($matches)); $maxAge = max(86400, (int)array_pop($matches));
$siteinfo['expires'] = DateTimeFormat::utc("now + $maxAge seconds"); $siteinfo['expires'] = DateTimeFormat::utc("now + $maxAge seconds");
} }
} }
$body = $curlResult->getBodyString(); $body = $curlResult->getBodyString();
$siteinfo['size'] = mb_strlen($body); $siteinfo['size'] = mb_strlen($body);
$charset = ''; $charset = '';
@ -259,7 +261,8 @@ class ParseUrl
if (isset($mediaType->parameters['charset'])) { if (isset($mediaType->parameters['charset'])) {
$charset = $mediaType->parameters['charset']; $charset = $mediaType->parameters['charset'];
} }
} catch(\InvalidArgumentException $e) {} } catch(\InvalidArgumentException $e) {
}
$siteinfo['charset'] = $charset; $siteinfo['charset'] = $charset;
@ -305,9 +308,9 @@ class ParseUrl
} }
if (@$meta_tag['http-equiv'] == 'refresh') { if (@$meta_tag['http-equiv'] == 'refresh') {
$path = $meta_tag['content']; $path = $meta_tag['content'];
$pathinfo = explode(';', $path); $pathinfo = explode(';', $path);
$content = ''; $content = '';
foreach ($pathinfo as $value) { foreach ($pathinfo as $value) {
if (substr(strtolower($value), 0, 4) == 'url=') { if (substr(strtolower($value), 0, 4) == 'url=') {
$content = substr($value, 4); $content = substr($value, 4);
@ -487,7 +490,7 @@ class ParseUrl
if (!empty($siteinfo['text']) && mb_strlen($siteinfo['text']) > self::MAX_DESC_COUNT) { if (!empty($siteinfo['text']) && mb_strlen($siteinfo['text']) > self::MAX_DESC_COUNT) {
$siteinfo['text'] = mb_substr($siteinfo['text'], 0, self::MAX_DESC_COUNT) . '…'; $siteinfo['text'] = mb_substr($siteinfo['text'], 0, self::MAX_DESC_COUNT) . '…';
$pos = mb_strrpos($siteinfo['text'], '.'); $pos = mb_strrpos($siteinfo['text'], '.');
if ($pos > self::MIN_DESC_COUNT) { if ($pos > self::MIN_DESC_COUNT) {
$siteinfo['text'] = mb_substr($siteinfo['text'], 0, $pos + 1); $siteinfo['text'] = mb_substr($siteinfo['text'], 0, $pos + 1);
} }
@ -510,7 +513,7 @@ class ParseUrl
* @param array $siteinfo * @param array $siteinfo
* @return array * @return array
*/ */
private static function checkMedia(string $page_url, array $siteinfo) : array private static function checkMedia(string $page_url, array $siteinfo): array
{ {
if (!empty($siteinfo['images'])) { if (!empty($siteinfo['images'])) {
array_walk($siteinfo['images'], function (&$image) use ($page_url) { array_walk($siteinfo['images'], function (&$image) use ($page_url) {
@ -521,13 +524,13 @@ class ParseUrl
*/ */
if (!empty($image['url'])) { if (!empty($image['url'])) {
$image['url'] = self::completeUrl($image['url'], $page_url); $image['url'] = self::completeUrl($image['url'], $page_url);
$photodata = Images::getInfoFromURLCached($image['url']); $photodata = Images::getInfoFromURLCached($image['url']);
if (($photodata) && ($photodata[0] > 50) && ($photodata[1] > 50)) { if (($photodata) && ($photodata[0] > 50) && ($photodata[1] > 50)) {
$image['src'] = $image['url']; $image['src'] = $image['url'];
$image['width'] = $photodata[0]; $image['width'] = $photodata[0];
$image['height'] = $photodata[1]; $image['height'] = $photodata[1];
$image['contenttype'] = $photodata['mime']; $image['contenttype'] = $photodata['mime'];
$image['blurhash'] = $photodata['blurhash'] ?? null; $image['blurhash'] = $photodata['blurhash'] ?? null;
unset($image['url']); unset($image['url']);
ksort($image); ksort($image);
} else { } else {
@ -544,14 +547,14 @@ class ParseUrl
foreach (['audio', 'video'] as $element) { foreach (['audio', 'video'] as $element) {
if (!empty($siteinfo[$element])) { if (!empty($siteinfo[$element])) {
array_walk($siteinfo[$element], function (&$media) use ($page_url, &$siteinfo) { array_walk($siteinfo[$element], function (&$media) use ($page_url, &$siteinfo) {
$url = ''; $url = '';
$embed = ''; $embed = '';
$content = ''; $content = '';
$contenttype = ''; $contenttype = '';
foreach (['embed', 'content', 'url'] as $field) { foreach (['embed', 'content', 'url'] as $field) {
if (!empty($media[$field])) { if (!empty($media[$field])) {
$media[$field] = self::completeUrl($media[$field], $page_url); $media[$field] = self::completeUrl($media[$field], $page_url);
$type = self::getContentType($media[$field]); $type = self::getContentType($media[$field]);
if (($type[0] ?? '') == 'text') { if (($type[0] ?? '') == 'text') {
if ($field == 'embed') { if ($field == 'embed') {
$embed = $media[$field]; $embed = $media[$field];
@ -559,7 +562,7 @@ class ParseUrl
$url = $media[$field]; $url = $media[$field];
} }
} elseif (!empty($type[0])) { } elseif (!empty($type[0])) {
$content = $media[$field]; $content = $media[$field];
$contenttype = implode('/', $type); $contenttype = implode('/', $type);
} }
} }
@ -706,7 +709,7 @@ class ParseUrl
} elseif (!empty($jsonld['@type'])) { } elseif (!empty($jsonld['@type'])) {
$siteinfo = self::parseJsonLd($siteinfo, $jsonld); $siteinfo = self::parseJsonLd($siteinfo, $jsonld);
} elseif (!empty($jsonld)) { } elseif (!empty($jsonld)) {
$keys = array_keys($jsonld); $keys = array_keys($jsonld);
$numeric_keys = true; $numeric_keys = true;
foreach ($keys as $key) { foreach ($keys as $key) {
if (!is_int($key)) { if (!is_int($key)) {
@ -810,7 +813,7 @@ class ParseUrl
case 'Person': case 'Person':
case 'Patient': case 'Patient':
case 'PerformingGroup': case 'PerformingGroup':
case 'DanceGroup'; case 'DanceGroup':
case 'MusicGroup': case 'MusicGroup':
case 'TheaterGroup': case 'TheaterGroup':
return self::parseJsonLdWebPerson($siteinfo, $jsonld); return self::parseJsonLdWebPerson($siteinfo, $jsonld);
@ -953,7 +956,7 @@ class ParseUrl
$content = JsonLD::fetchElement($jsonld, 'keywords'); $content = JsonLD::fetchElement($jsonld, 'keywords');
if (!empty($content)) { if (!empty($content)) {
$siteinfo['keywords'] = []; $siteinfo['keywords'] = [];
$keywords = explode(',', $content); $keywords = explode(',', $content);
foreach ($keywords as $keyword) { foreach ($keywords as $keyword) {
$siteinfo['keywords'][] = trim($keyword); $siteinfo['keywords'][] = trim($keyword);
} }

View file

@ -37,7 +37,8 @@ class Proxy
/** /**
* Private constructor * Private constructor
*/ */
private function __construct () { private function __construct()
{
// No instances from utilities classes // No instances from utilities classes
} }

View file

@ -155,7 +155,7 @@ class Strings
{ {
if ($network != '') { if ($network != '') {
if ($url != '') { if ($url != '') {
$gsid = ContactSelector::getServerIdForProfile($url); $gsid = ContactSelector::getServerIdForProfile($url);
$network_name = '<a href="' . $url . '">' . ContactSelector::networkToName($network, '', $gsid) . '</a>'; $network_name = '<a href="' . $url . '">' . ContactSelector::networkToName($network, '', $gsid) . '</a>';
} else { } else {
$network_name = ContactSelector::networkToName($network); $network_name = ContactSelector::networkToName($network);
@ -214,8 +214,8 @@ class Strings
$units = ['B', 'KiB', 'MiB', 'GiB', 'TiB']; $units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
$bytes = max($bytes, 0); $bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1); $pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow); $bytes /= pow(1024, $pow);
return round($bytes, $precision) . ' ' . $units[$pow]; return round($bytes, $precision) . ' ' . $units[$pow];
@ -448,13 +448,13 @@ class Strings
if ($start < 0) { if ($start < 0) {
$start = max(0, $string_length + $start); $start = max(0, $string_length + $start);
} else if ($start > $string_length) { } elseif ($start > $string_length) {
$start = $string_length; $start = $string_length;
} }
if ($length < 0) { if ($length < 0) {
$length = max(0, $string_length - $start + $length); $length = max(0, $string_length - $start + $length);
} else if ($length > $string_length) { } elseif ($length > $string_length) {
$length = $string_length; $length = $string_length;
} }
@ -544,8 +544,10 @@ class Strings
switch ($last) { switch ($last) {
case 'g': case 'g':
$shorthand *= 1024; $shorthand *= 1024;
// no break
case 'm': case 'm':
$shorthand *= 1024; $shorthand *= 1024;
// no break
case 'k': case 'k':
$shorthand *= 1024; $shorthand *= 1024;
} }
@ -567,7 +569,7 @@ class Strings
return $url; return $url;
} }
$scheme = [$parts['scheme'] . '://www.', $parts['scheme'] . '://']; $scheme = [$parts['scheme'] . '://www.', $parts['scheme'] . '://'];
$styled_url = str_replace($scheme, '', $url); $styled_url = str_replace($scheme, '', $url);
if (!empty($max_length) && strlen($styled_url) > $max_length) { if (!empty($max_length) && strlen($styled_url) > $max_length) {

View file

@ -45,9 +45,9 @@ class XML
$root = new SimpleXMLElement('<' . $key . '>' . self::escape($value ?? '') . '</' . $key . '>'); $root = new SimpleXMLElement('<' . $key . '>' . self::escape($value ?? '') . '</' . $key . '>');
} }
$dom = dom_import_simplexml($root)->ownerDocument; $dom = dom_import_simplexml($root)->ownerDocument;
$dom->formatOutput = true; $dom->formatOutput = true;
$xml = $dom; $xml = $dom;
$xml_text = $dom->saveXML(); $xml_text = $dom->saveXML();
@ -154,7 +154,7 @@ class XML
$element = $doc->createElement($element, self::escape($value)); $element = $doc->createElement($element, self::escape($value));
foreach ($attributes as $key => $value) { foreach ($attributes as $key => $value) {
$attribute = $doc->createAttribute($key); $attribute = $doc->createAttribute($key);
$attribute->value = self::escape($value ?? ''); $attribute->value = self::escape($value ?? '');
$element->appendChild($attribute); $element->appendChild($attribute);
} }
@ -199,7 +199,7 @@ class XML
&& (get_class($xml_element) == 'SimpleXMLElement') && (get_class($xml_element) == 'SimpleXMLElement')
) { ) {
$xml_element_copy = $xml_element; $xml_element_copy = $xml_element;
$xml_element = get_object_vars($xml_element); $xml_element = get_object_vars($xml_element);
} }
if (is_array($xml_element)) { if (is_array($xml_element)) {
@ -210,12 +210,12 @@ class XML
foreach ($xml_element as $key => $value) { foreach ($xml_element as $key => $value) {
$recursion_depth++; $recursion_depth++;
$result_array[strtolower($key)] = self::elementToArray($value, $recursion_depth); $result_array[strtolower($key)] = self::elementToArray($value, $recursion_depth);
$recursion_depth--; $recursion_depth--;
} }
if ($recursion_depth == 0) { if ($recursion_depth == 0) {
$temp_array = $result_array; $temp_array = $result_array;
$result_array = [ $result_array = [
strtolower($xml_element_copy->getName()) => $temp_array, strtolower($xml_element_copy->getName()) => $temp_array,
]; ];
@ -306,7 +306,7 @@ class XML
$attributes = isset($data['attributes']) ? $data['attributes'] : null; $attributes = isset($data['attributes']) ? $data['attributes'] : null;
$value = isset($data['value']) ? $data['value'] : null; $value = isset($data['value']) ? $data['value'] : null;
$result = []; $result = [];
$attributes_data = []; $attributes_data = [];
if (isset($value)) { if (isset($value)) {
@ -330,14 +330,14 @@ class XML
// See tag status and do the needed. // See tag status and do the needed.
if ($namespaces && strpos($tag, ':')) { if ($namespaces && strpos($tag, ':')) {
$namespc = substr($tag, 0, strrpos($tag, ':')); $namespc = substr($tag, 0, strrpos($tag, ':'));
$tag = strtolower(substr($tag, strlen($namespc)+1)); $tag = strtolower(substr($tag, strlen($namespc) + 1));
$result['@namespace'] = $namespc; $result['@namespace'] = $namespc;
} }
$tag = strtolower($tag); $tag = strtolower($tag);
if ($type == 'open') { // The starting of the tag '<tag>' if ($type == 'open') { // The starting of the tag '<tag>'
$parent[$level-1] = &$current; $parent[$level - 1] = &$current;
if (!is_array($current) || (!in_array($tag, array_keys($current)))) { // Insert New tag if (!is_array($current) || (!in_array($tag, array_keys($current)))) { // Insert New tag
$current[$tag] = $result; $current[$tag] = $result;
if ($attributes_data) { if ($attributes_data) {
@ -352,7 +352,7 @@ class XML
$current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result; $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
$repeated_tag_index[$tag . '_' . $level]++; $repeated_tag_index[$tag . '_' . $level]++;
} else { // This section will make the value an array if multiple tags with the same name appear together } else { // This section will make the value an array if multiple tags with the same name appear together
$current[$tag] = [$current[$tag], $result]; // This will combine the existing item and the new item together to make an array $current[$tag] = [$current[$tag], $result]; // This will combine the existing item and the new item together to make an array
$repeated_tag_index[$tag . '_' . $level] = 2; $repeated_tag_index[$tag . '_' . $level] = 2;
if (isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well if (isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
@ -360,13 +360,13 @@ class XML
unset($current[$tag.'_attr']); unset($current[$tag.'_attr']);
} }
} }
$last_item_index = $repeated_tag_index[$tag . '_' . $level]-1; $last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1;
$current = &$current[$tag][$last_item_index]; $current = &$current[$tag][$last_item_index];
} }
} elseif ($type == 'complete') { // Tags that ends in 1 line '<tag />' } elseif ($type == 'complete') { // Tags that ends in 1 line '<tag />'
//See if the key is already taken. //See if the key is already taken.
if (!isset($current[$tag])) { //New Key if (!isset($current[$tag])) { //New Key
$current[$tag] = $result; $current[$tag] = $result;
$repeated_tag_index[$tag . '_' . $level] = 1; $repeated_tag_index[$tag . '_' . $level] = 1;
if ($priority == 'tag' and $attributes_data) { if ($priority == 'tag' and $attributes_data) {
$current[$tag. '_attr'] = $attributes_data; $current[$tag. '_attr'] = $attributes_data;
@ -382,7 +382,7 @@ class XML
} }
$repeated_tag_index[$tag . '_' . $level]++; $repeated_tag_index[$tag . '_' . $level]++;
} else { // If it is not an array... } else { // If it is not an array...
$current[$tag] = [$current[$tag], $result]; //...Make it an array using the existing value and the new value $current[$tag] = [$current[$tag], $result]; //...Make it an array using the existing value and the new value
$repeated_tag_index[$tag . '_' . $level] = 1; $repeated_tag_index[$tag . '_' . $level] = 1;
if ($priority == 'tag' and $get_attributes) { if ($priority == 'tag' and $get_attributes) {
if (isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well if (isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
@ -399,7 +399,7 @@ class XML
} }
} }
} elseif ($type == 'close') { // End of tag '</tag>' } elseif ($type == 'close') { // End of tag '</tag>'
$current = &$parent[$level-1]; $current = &$parent[$level - 1];
} }
} }
@ -416,7 +416,7 @@ class XML
public static function deleteNode(DOMDocument $doc, string $node) public static function deleteNode(DOMDocument $doc, string $node)
{ {
$xpath = new DOMXPath($doc); $xpath = new DOMXPath($doc);
$list = $xpath->query('//' . $node); $list = $xpath->query('//' . $node);
foreach ($list as $child) { foreach ($list as $child) {
$child->parentNode->removeChild($child); $child->parentNode->removeChild($child);
} }

View file

@ -12,6 +12,7 @@ use Friendica\DI;
use Friendica\Model\Post; use Friendica\Model\Post;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Protocol\ActivityPub; use Friendica\Protocol\ActivityPub;
class APDelivery class APDelivery
{ {
/** /**
@ -31,7 +32,7 @@ class APDelivery
if (ActivityPub\Transmitter::archivedInbox($inbox)) { if (ActivityPub\Transmitter::archivedInbox($inbox)) {
DI::logger()->info('Inbox is archived', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $item_id, 'uri-id' => $uri_id, 'uid' => $uid]); DI::logger()->info('Inbox is archived', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $item_id, 'uri-id' => $uri_id, 'uid' => $uid]);
if (empty($uri_id) && !empty($item_id)) { if (empty($uri_id) && !empty($item_id)) {
$item = Post::selectFirst(['uri-id'], ['id' => $item_id]); $item = Post::selectFirst(['uri-id'], ['id' => $item_id]);
$uri_id = $item['uri-id'] ?? 0; $uri_id = $item['uri-id'] ?? 0;
} }
if (empty($uri_id)) { if (empty($uri_id)) {

View file

@ -12,17 +12,17 @@ use Friendica\Model\Post;
class DelayedPublish class DelayedPublish
{ {
/** /**
* Publish a post, used for delayed postings * Publish a post, used for delayed postings
* *
* @param array $item * @param array $item
* @param int $notify * @param int $notify
* @param array $taglist * @param array $taglist
* @param array $attachments * @param array $attachments
* @param int $preparation_mode * @param int $preparation_mode
* @param string $uri * @param string $uri
* @return void * @return void
*/ */
public static function execute(array $item, int $notify = 0, array $taglist = [], array $attachments = [], int $preparation_mode = Post\Delayed::PREPARED, string $uri = '') public static function execute(array $item, int $notify = 0, array $taglist = [], array $attachments = [], int $preparation_mode = Post\Delayed::PREPARED, string $uri = '')
{ {
$id = Post\Delayed::publish($item, $notify, $taglist, $attachments, $preparation_mode, $uri); $id = Post\Delayed::publish($item, $notify, $taglist, $attachments, $preparation_mode, $uri);

View file

@ -47,7 +47,8 @@ class Directory
return; return;
} }
private static function updateAll() { private static function updateAll()
{
$users = DBA::select('owner-view', ['url'], ['net-publish' => true, 'verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false]); $users = DBA::select('owner-view', ['url'], ['net-publish' => true, 'verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false]);
while ($user = DBA::fetch($users)) { while ($user = DBA::fetch($users)) {
Worker::add(Worker::PRIORITY_LOW, 'Directory', $user['url']); Worker::add(Worker::PRIORITY_LOW, 'Directory', $user['url']);

View file

@ -47,16 +47,23 @@ class Expire
$r = DBA::select('user', ['uid', 'username'], ["`expire` != ?", 0]); $r = DBA::select('user', ['uid', 'username'], ["`expire` != ?", 0]);
while ($row = DBA::fetch($r)) { while ($row = DBA::fetch($r)) {
DI::logger()->info('Calling expiry', ['user' => $row['uid'], 'username' => $row['username']]); DI::logger()->info('Calling expiry', ['user' => $row['uid'], 'username' => $row['username']]);
Worker::add(['priority' => $appHelper->getQueueValue('priority'), 'created' => $appHelper->getQueueValue('created'), 'dont_fork' => true], Worker::add(
'Expire', (int)$row['uid']); ['priority' => $appHelper->getQueueValue('priority'), 'created' => $appHelper->getQueueValue('created'), 'dont_fork' => true],
'Expire',
(int)$row['uid']
);
} }
DBA::close($r); DBA::close($r);
DI::logger()->notice('calling hooks'); DI::logger()->notice('calling hooks');
foreach (Hook::getByName('expire') as $hook) { foreach (Hook::getByName('expire') as $hook) {
DI::logger()->info('Calling expire', ['hook' => $hook[1]]); DI::logger()->info('Calling expire', ['hook' => $hook[1]]);
Worker::add(['priority' => $appHelper->getQueueValue('priority'), 'created' => $appHelper->getQueueValue('created'), 'dont_fork' => true], Worker::add(
'Expire', 'hook', $hook[1]); ['priority' => $appHelper->getQueueValue('priority'), 'created' => $appHelper->getQueueValue('created'), 'dont_fork' => true],
'Expire',
'hook',
$hook[1]
);
} }
DI::logger()->notice('calling hooks done'); DI::logger()->notice('calling hooks done');

View file

@ -29,7 +29,7 @@ class ExpireAndRemoveUsers
// Ensure to never remove the user with uid=0 // Ensure to never remove the user with uid=0
DBA::update('user', ['verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false, DBA::update('user', ['verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false,
'account_expires_on' => DBA::NULL_DATETIME], ['uid' => 0]); 'account_expires_on' => DBA::NULL_DATETIME], ['uid' => 0]);
// Remove any freshly expired account // Remove any freshly expired account
$users = DBA::select('user', ['uid'], ['account_expired' => true, 'account_removed' => false]); $users = DBA::select('user', ['uid'], ['account_expired' => true, 'account_removed' => false]);

View file

@ -73,10 +73,10 @@ class ExpirePosts
DI::logger()->notice('Delete expired posts'); DI::logger()->notice('Delete expired posts');
// physically remove anything that has been deleted for more than two months // physically remove anything that has been deleted for more than two months
$condition = ["`gravity` = ? AND `deleted` AND `edited` < ?", Item::GRAVITY_PARENT, DateTimeFormat::utc('now - 60 days')]; $condition = ["`gravity` = ? AND `deleted` AND `edited` < ?", Item::GRAVITY_PARENT, DateTimeFormat::utc('now - 60 days')];
$pass = 0; $pass = 0;
do { do {
++$pass; ++$pass;
$rows = DBA::select('post-user', ['uri-id', 'uid'], $condition, ['limit' => $limit]); $rows = DBA::select('post-user', ['uri-id', 'uid'], $condition, ['limit' => $limit]);
$affected_count = 0; $affected_count = 0;
while ($row = Post::fetch($rows)) { while ($row = Post::fetch($rows)) {
DI::logger()->info('Delete expired item', ['pass' => $pass, 'uri-id' => $row['uri-id']]); DI::logger()->info('Delete expired item', ['pass' => $pass, 'uri-id' => $row['uri-id']]);
@ -108,7 +108,7 @@ class ExpirePosts
} }
DI::logger()->notice('Start collecting orphaned entries', ['table' => $table]); DI::logger()->notice('Start collecting orphaned entries', ['table' => $table]);
$uris = DBA::select($table, ['uri-id'], ["NOT `uri-id` IN (SELECT `uri-id` FROM `post-user`)"]); $uris = DBA::select($table, ['uri-id'], ["NOT `uri-id` IN (SELECT `uri-id` FROM `post-user`)"]);
$affected_count = 0; $affected_count = 0;
DI::logger()->notice('Deleting orphaned entries - start', ['table' => $table]); DI::logger()->notice('Deleting orphaned entries - start', ['table' => $table]);
while ($rows = DBA::toArray($uris, false, 100)) { while ($rows = DBA::toArray($uris, false, 100)) {
@ -132,7 +132,7 @@ class ExpirePosts
{ {
DI::logger()->notice('Adding missing entries'); DI::logger()->notice('Adding missing entries');
$rows = 0; $rows = 0;
$userposts = DBA::select('post-user', [], ["`uri-id` not in (select `uri-id` from `post`)"]); $userposts = DBA::select('post-user', [], ["`uri-id` not in (select `uri-id` from `post`)"]);
while ($fields = DBA::fetch($userposts)) { while ($fields = DBA::fetch($userposts)) {
$post_fields = DI::dbaDefinition()->truncateFieldsForTable('post', $fields); $post_fields = DI::dbaDefinition()->truncateFieldsForTable('post', $fields);
@ -146,10 +146,10 @@ class ExpirePosts
DI::logger()->notice('No post entries added'); DI::logger()->notice('No post entries added');
} }
$rows = 0; $rows = 0;
$userposts = DBA::select('post-user', [], ["`gravity` = ? AND `uri-id` not in (select `uri-id` from `post-thread`)", Item::GRAVITY_PARENT]); $userposts = DBA::select('post-user', [], ["`gravity` = ? AND `uri-id` not in (select `uri-id` from `post-thread`)", Item::GRAVITY_PARENT]);
while ($fields = DBA::fetch($userposts)) { while ($fields = DBA::fetch($userposts)) {
$post_fields = DI::dbaDefinition()->truncateFieldsForTable('post-thread', $fields); $post_fields = DI::dbaDefinition()->truncateFieldsForTable('post-thread', $fields);
$post_fields['commented'] = $post_fields['changed'] = $post_fields['created']; $post_fields['commented'] = $post_fields['changed'] = $post_fields['created'];
DBA::insert('post-thread', $post_fields, Database::INSERT_IGNORE); DBA::insert('post-thread', $post_fields, Database::INSERT_IGNORE);
$rows++; $rows++;
@ -161,10 +161,10 @@ class ExpirePosts
DI::logger()->notice('No post-thread entries added'); DI::logger()->notice('No post-thread entries added');
} }
$rows = 0; $rows = 0;
$userposts = DBA::select('post-user', [], ["`gravity` = ? AND `id` not in (select `post-user-id` from `post-thread-user`)", Item::GRAVITY_PARENT]); $userposts = DBA::select('post-user', [], ["`gravity` = ? AND `id` not in (select `post-user-id` from `post-thread-user`)", Item::GRAVITY_PARENT]);
while ($fields = DBA::fetch($userposts)) { while ($fields = DBA::fetch($userposts)) {
$post_fields = DI::dbaDefinition()->truncateFieldsForTable('post-thread-user', $fields); $post_fields = DI::dbaDefinition()->truncateFieldsForTable('post-thread-user', $fields);
$post_fields['commented'] = $post_fields['changed'] = $post_fields['created']; $post_fields['commented'] = $post_fields['changed'] = $post_fields['created'];
DBA::insert('post-thread-user', $post_fields, Database::INSERT_IGNORE); DBA::insert('post-thread-user', $post_fields, Database::INSERT_IGNORE);
$rows++; $rows++;
@ -223,7 +223,7 @@ class ExpirePosts
$pass = 0; $pass = 0;
do { do {
++$pass; ++$pass;
$uris = DBA::select('item-uri', ['id'], $condition, ['limit' => $limit]); $uris = DBA::select('item-uri', ['id'], $condition, ['limit' => $limit]);
$total = DBA::numRows($uris); $total = DBA::numRows($uris);
DI::logger()->notice('Start deleting orphaned URI-ID', ['pass' => $pass, 'last-id' => $item['uri-id']]); DI::logger()->notice('Start deleting orphaned URI-ID', ['pass' => $pass, 'last-id' => $item['uri-id']]);
$affected_count = 0; $affected_count = 0;
@ -249,7 +249,7 @@ class ExpirePosts
return; return;
} }
$expire_days = DI::config()->get('system', 'dbclean-expire-days'); $expire_days = DI::config()->get('system', 'dbclean-expire-days');
$expire_days_unclaimed = DI::config()->get('system', 'dbclean-expire-unclaimed'); $expire_days_unclaimed = DI::config()->get('system', 'dbclean-expire-unclaimed');
if (empty($expire_days_unclaimed)) { if (empty($expire_days_unclaimed)) {
$expire_days_unclaimed = $expire_days; $expire_days_unclaimed = $expire_days;
@ -306,7 +306,7 @@ class ExpirePosts
$pass = 0; $pass = 0;
do { do {
++$pass; ++$pass;
$uris = DBA::select('post-user', ['uri-id'], $condition, ['limit' => $limit]); $uris = DBA::select('post-user', ['uri-id'], $condition, ['limit' => $limit]);
$total = DBA::numRows($uris); $total = DBA::numRows($uris);
DI::logger()->notice('Start deleting unclaimed public items', ['pass' => $pass]); DI::logger()->notice('Start deleting unclaimed public items', ['pass' => $pass]);
$affected_count = 0; $affected_count = 0;

View file

@ -47,42 +47,50 @@ class Notifier
DI::logger()->info('Invoked', ['cmd' => $cmd, 'target' => $post_uriid, 'sender_uid' => $sender_uid]); DI::logger()->info('Invoked', ['cmd' => $cmd, 'target' => $post_uriid, 'sender_uid' => $sender_uid]);
$target_id = $post_uriid; $target_id = $post_uriid;
$recipients = []; $recipients = [];
$delivery_contacts_stmt = null; $delivery_contacts_stmt = null;
$target_item = []; $target_item = [];
$parent = []; $parent = [];
$thr_parent = []; $thr_parent = [];
$items = []; $items = [];
$delivery_queue_count = 0; $delivery_queue_count = 0;
$ap_contacts = []; $ap_contacts = [];
if ($cmd == Delivery::MAIL) { if ($cmd == Delivery::MAIL) {
$message = DBA::selectFirst('mail', ['uid', 'contact-id'], ['id' => $target_id]); $message = DBA::selectFirst('mail', ['uid', 'contact-id'], ['id' => $target_id]);
if (!DBA::isResult($message)) { if (!DBA::isResult($message)) {
return; return;
} }
$uid = $message['uid']; $uid = $message['uid'];
$recipients[] = $message['contact-id']; $recipients[] = $message['contact-id'];
$inboxes = ActivityPub\Transmitter::fetchTargetInboxesFromMail($target_id); $inboxes = ActivityPub\Transmitter::fetchTargetInboxesFromMail($target_id);
foreach ($inboxes as $inbox => $receivers) { foreach ($inboxes as $inbox => $receivers) {
$ap_contacts = array_merge($ap_contacts, $receivers); $ap_contacts = array_merge($ap_contacts, $receivers);
DI::logger()->info('Delivery via ActivityPub', ['cmd' => $cmd, 'target' => $target_id, 'inbox' => $inbox]); DI::logger()->info('Delivery via ActivityPub', ['cmd' => $cmd, 'target' => $target_id, 'inbox' => $inbox]);
Worker::add(['priority' => Worker::PRIORITY_HIGH, 'created' => $appHelper->getQueueValue('created'), 'dont_fork' => true], Worker::add(
'APDelivery', $cmd, $target_id, $inbox, $uid, $receivers, $post_uriid); ['priority' => Worker::PRIORITY_HIGH, 'created' => $appHelper->getQueueValue('created'), 'dont_fork' => true],
'APDelivery',
$cmd,
$target_id,
$inbox,
$uid,
$receivers,
$post_uriid
);
} }
} elseif ($cmd == Delivery::SUGGESTION) { } elseif ($cmd == Delivery::SUGGESTION) {
$suggest = DI::fsuggest()->selectOneById($target_id); $suggest = DI::fsuggest()->selectOneById($target_id);
$uid = $suggest->uid; $uid = $suggest->uid;
$recipients[] = $suggest->cid; $recipients[] = $suggest->cid;
} elseif ($cmd == Delivery::REMOVAL) { } elseif ($cmd == Delivery::REMOVAL) {
return self::notifySelfRemoval($target_id, $appHelper->getQueueValue('priority'), $appHelper->getQueueValue('created')); return self::notifySelfRemoval($target_id, $appHelper->getQueueValue('priority'), $appHelper->getQueueValue('created'));
} elseif ($cmd == Delivery::RELOCATION) { } elseif ($cmd == Delivery::RELOCATION) {
$uid = $target_id; $uid = $target_id;
$condition = ['uid' => $target_id, 'self' => false, 'network' => [Protocol::DFRN, Protocol::DIASPORA]]; $condition = ['uid' => $target_id, 'self' => false, 'network' => [Protocol::DFRN, Protocol::DIASPORA]];
$delivery_contacts_stmt = DBA::select('contact', ['id', 'uri-id', 'url', 'addr', 'network', 'protocol', 'baseurl', 'gsid', 'batch'], $condition); $delivery_contacts_stmt = DBA::select('contact', ['id', 'uri-id', 'url', 'addr', 'network', 'protocol', 'baseurl', 'gsid', 'batch'], $condition);
} else { } else {
$post = Post::selectFirst(['id'], ['uri-id' => $post_uriid, 'uid' => $sender_uid]); $post = Post::selectFirst(['id'], ['uri-id' => $post_uriid, 'uid' => $sender_uid]);
@ -93,7 +101,7 @@ class Notifier
$target_id = $post['id']; $target_id = $post['id'];
// find ancestors // find ancestors
$condition = ['id' => $target_id, 'visible' => true]; $condition = ['id' => $target_id, 'visible' => true];
$target_item = Post::selectFirst(Item::DELIVER_FIELDLIST, $condition); $target_item = Post::selectFirst(Item::DELIVER_FIELDLIST, $condition);
$target_item = Post\Media::addHTMLAttachmentToItem($target_item); $target_item = Post\Media::addHTMLAttachmentToItem($target_item);
@ -111,8 +119,8 @@ class Notifier
return; return;
} }
$condition = ['parent' => $target_item['parent'], 'visible' => true]; $condition = ['parent' => $target_item['parent'], 'visible' => true];
$params = ['order' => ['id']]; $params = ['order' => ['id']];
$items_stmt = Post::select(Item::DELIVER_FIELDLIST, $condition, $params); $items_stmt = Post::select(Item::DELIVER_FIELDLIST, $condition, $params);
if (!DBA::isResult($items_stmt)) { if (!DBA::isResult($items_stmt)) {
DI::logger()->info('No item found', ['cmd' => $cmd, 'target' => $target_id]); DI::logger()->info('No item found', ['cmd' => $cmd, 'target' => $target_id]);
@ -145,14 +153,14 @@ class Notifier
$only_ap_delivery = false; $only_ap_delivery = false;
$followup = false; $followup = false;
$recipients_followup = []; $recipients_followup = [];
if (!empty($target_item) && !empty($items)) { if (!empty($target_item) && !empty($items)) {
$parent = $items[0]; $parent = $items[0];
$fields = ['network', 'private', 'author-id', 'author-link', 'author-network', 'owner-id']; $fields = ['network', 'private', 'author-id', 'author-link', 'author-network', 'owner-id'];
$condition = ['uri' => $target_item['thr-parent'], 'uid' => $target_item['uid']]; $condition = ['uri' => $target_item['thr-parent'], 'uid' => $target_item['uid']];
$thr_parent = Post::selectFirst($fields, $condition); $thr_parent = Post::selectFirst($fields, $condition);
if (empty($thr_parent)) { if (empty($thr_parent)) {
$thr_parent = $parent; $thr_parent = $parent;
@ -162,9 +170,9 @@ class Notifier
// Restrict distribution to AP, when there are no permissions. // Restrict distribution to AP, when there are no permissions.
if (!self::isRemovalActivity($cmd, $owner, Protocol::ACTIVITYPUB) && ($target_item['private'] == Item::PRIVATE) && empty($target_item['allow_cid']) && empty($target_item['allow_gid']) && empty($target_item['deny_cid']) && empty($target_item['deny_gid'])) { if (!self::isRemovalActivity($cmd, $owner, Protocol::ACTIVITYPUB) && ($target_item['private'] == Item::PRIVATE) && empty($target_item['allow_cid']) && empty($target_item['allow_gid']) && empty($target_item['deny_cid']) && empty($target_item['deny_gid'])) {
$only_ap_delivery = true; $only_ap_delivery = true;
$public_message = false; $public_message = false;
$diaspora_delivery = false; $diaspora_delivery = false;
} }
if (!$target_item['origin'] && $target_item['network'] == Protocol::ACTIVITYPUB) { if (!$target_item['origin'] && $target_item['network'] == Protocol::ACTIVITYPUB) {
@ -225,10 +233,10 @@ class Notifier
if ($relay_to_owner) { if ($relay_to_owner) {
// local followup to remote post // local followup to remote post
$followup = true; $followup = true;
$public_message = false; // not public $public_message = false; // not public
$recipients = [$parent['contact-id']]; $recipients = [$parent['contact-id']];
$recipients_followup = [$parent['contact-id']]; $recipients_followup = [$parent['contact-id']];
DI::logger()->info('Followup', ['target' => $target_id, 'guid' => $target_item['guid'], 'to' => $parent['contact-id']]); DI::logger()->info('Followup', ['target' => $target_id, 'guid' => $target_item['guid'], 'to' => $parent['contact-id']]);
} elseif ($exclusive_delivery) { } elseif ($exclusive_delivery) {
@ -264,9 +272,9 @@ class Notifier
$aclFormatter = DI::aclFormatter(); $aclFormatter = DI::aclFormatter();
$allow_people = $aclFormatter->expand($parent['allow_cid']); $allow_people = $aclFormatter->expand($parent['allow_cid']);
$allow_circles = Circle::expand($uid, $aclFormatter->expand($parent['allow_gid']), true); $allow_circles = Circle::expand($uid, $aclFormatter->expand($parent['allow_gid']), true);
$deny_people = $aclFormatter->expand($parent['deny_cid']); $deny_people = $aclFormatter->expand($parent['deny_cid']);
$deny_circles = Circle::expand($uid, $aclFormatter->expand($parent['deny_gid'])); $deny_circles = Circle::expand($uid, $aclFormatter->expand($parent['deny_gid']));
foreach ($items as $item) { foreach ($items as $item) {
@ -283,7 +291,7 @@ class Notifier
} }
$recipients = array_unique(array_merge($recipients, $allow_people, $allow_circles)); $recipients = array_unique(array_merge($recipients, $allow_people, $allow_circles));
$deny = array_unique(array_merge($deny_people, $deny_circles)); $deny = array_unique(array_merge($deny_people, $deny_circles));
$recipients = array_diff($recipients, $deny); $recipients = array_diff($recipients, $deny);
// If this is a public message and pubmail is set on the parent, include all your email contacts // If this is a public message and pubmail is set on the parent, include all your email contacts
@ -320,21 +328,21 @@ class Notifier
$recipients = $recipients_followup; $recipients = $recipients_followup;
} }
$apdelivery = self::activityPubDelivery($cmd, $target_item, $parent, $thr_parent, $appHelper->getQueueValue('priority'), $appHelper->getQueueValue('created'), $recipients); $apdelivery = self::activityPubDelivery($cmd, $target_item, $parent, $thr_parent, $appHelper->getQueueValue('priority'), $appHelper->getQueueValue('created'), $recipients);
$ap_contacts = $apdelivery['contacts']; $ap_contacts = $apdelivery['contacts'];
$delivery_queue_count += $apdelivery['count']; $delivery_queue_count += $apdelivery['count'];
if (!$only_ap_delivery) { if (!$only_ap_delivery) {
if (empty($delivery_contacts_stmt)) { if (empty($delivery_contacts_stmt)) {
$condition = ['id' => $recipients, 'self' => false, 'uid' => [0, $uid], $condition = ['id' => $recipients, 'self' => false, 'uid' => [0, $uid],
'blocked' => false, 'pending' => false, 'archive' => false]; 'blocked' => false, 'pending' => false, 'archive' => false];
if (!empty($networks)) { if (!empty($networks)) {
$condition['network'] = $networks; $condition['network'] = $networks;
} }
$delivery_contacts_stmt = DBA::select('contact', ['id', 'uri-id', 'addr', 'url', 'network', 'protocol', 'baseurl', 'gsid', 'batch'], $condition); $delivery_contacts_stmt = DBA::select('contact', ['id', 'uri-id', 'addr', 'url', 'network', 'protocol', 'baseurl', 'gsid', 'batch'], $condition);
} }
$conversants = []; $conversants = [];
$batch_delivery = false; $batch_delivery = false;
if ($public_message && !in_array($cmd, [Delivery::MAIL, Delivery::SUGGESTION]) && !$followup) { if ($public_message && !in_array($cmd, [Delivery::MAIL, Delivery::SUGGESTION]) && !$followup) {
@ -343,9 +351,12 @@ class Notifier
if ($diaspora_delivery && !$unlisted) { if ($diaspora_delivery && !$unlisted) {
$batch_delivery = true; $batch_delivery = true;
$participants = DBA::selectToArray('contact', ['batch', 'network', 'protocol', 'baseurl', 'gsid', 'id', 'url', 'name'], $participants = DBA::selectToArray(
'contact',
['batch', 'network', 'protocol', 'baseurl', 'gsid', 'id', 'url', 'name'],
["`network` = ? AND `batch` != '' AND `uid` = ? AND `rel` != ? AND NOT `blocked` AND NOT `pending` AND NOT `archive`", Protocol::DIASPORA, $owner['uid'], Contact::SHARING], ["`network` = ? AND `batch` != '' AND `uid` = ? AND `rel` != ? AND NOT `blocked` AND NOT `pending` AND NOT `archive`", Protocol::DIASPORA, $owner['uid'], Contact::SHARING],
['group_by' => ['batch', 'network', 'protocol']]); ['group_by' => ['batch', 'network', 'protocol']]
);
// Fetch the participation list // Fetch the participation list
// The function will ensure that there are no duplicates // The function will ensure that there are no duplicates
@ -415,7 +426,7 @@ class Notifier
*/ */
private static function delivery(string $cmd, int $post_uriid, int $sender_uid, array $target_item, array $parent, array $thr_parent, array $owner, bool $batch_delivery, bool $in_batch, array $contacts, array $ap_contacts, array $conversants = []): int private static function delivery(string $cmd, int $post_uriid, int $sender_uid, array $target_item, array $parent, array $thr_parent, array $owner, bool $batch_delivery, bool $in_batch, array $contacts, array $ap_contacts, array $conversants = []): int
{ {
$appHelper = DI::appHelper(); $appHelper = DI::appHelper();
$delivery_queue_count = 0; $delivery_queue_count = 0;
if (!empty($target_item['verb']) && ($target_item['verb'] == Activity::ANNOUNCE)) { if (!empty($target_item['verb']) && ($target_item['verb'] == Activity::ANNOUNCE)) {
@ -617,8 +628,15 @@ class Notifier
$inboxes = ActivityPub\Transmitter::fetchTargetInboxesforUser($self_user_id); $inboxes = ActivityPub\Transmitter::fetchTargetInboxesforUser($self_user_id);
foreach ($inboxes as $inbox => $receivers) { foreach ($inboxes as $inbox => $receivers) {
DI::logger()->info('Account removal via ActivityPub', ['uid' => $self_user_id, 'inbox' => $inbox]); DI::logger()->info('Account removal via ActivityPub', ['uid' => $self_user_id, 'inbox' => $inbox]);
Worker::add(['priority' => Worker::PRIORITY_NEGLIGIBLE, 'created' => $created, 'dont_fork' => true], Worker::add(
'APDelivery', Delivery::REMOVAL, 0, $inbox, $self_user_id, $receivers); ['priority' => Worker::PRIORITY_NEGLIGIBLE, 'created' => $created, 'dont_fork' => true],
'APDelivery',
Delivery::REMOVAL,
0,
$inbox,
$self_user_id,
$receivers
);
Worker::coolDown(); Worker::coolDown();
} }
@ -665,7 +683,7 @@ class Notifier
return ['count' => 0, 'contacts' => []]; return ['count' => 0, 'contacts' => []];
} }
$inboxes = []; $inboxes = [];
$relay_inboxes = []; $relay_inboxes = [];
$uid = $target_item['contact-uid'] ?: $target_item['uid']; $uid = $target_item['contact-uid'] ?: $target_item['uid'];
@ -682,7 +700,7 @@ class Notifier
$inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid); $inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid);
if (in_array($target_item['private'], [Item::PUBLIC])) { if (in_array($target_item['private'], [Item::PUBLIC])) {
$inboxes = ActivityPub\Transmitter::addRelayServerInboxesForItem($target_item['id'], $inboxes); $inboxes = ActivityPub\Transmitter::addRelayServerInboxesForItem($target_item['id'], $inboxes);
$relay_inboxes = ActivityPub\Transmitter::addRelayServerInboxes(); $relay_inboxes = ActivityPub\Transmitter::addRelayServerInboxes();
} }
@ -727,7 +745,7 @@ class Notifier
} }
$delivery_queue_count = 0; $delivery_queue_count = 0;
$contacts = []; $contacts = [];
foreach ($inboxes as $inbox => $receivers) { foreach ($inboxes as $inbox => $receivers) {
$contacts = array_merge($contacts, $receivers); $contacts = array_merge($contacts, $receivers);
@ -763,8 +781,16 @@ class Notifier
Post\Delivery::add($target_item['uri-id'], $uid, $inbox, $target_item['created'], $cmd, $receivers); Post\Delivery::add($target_item['uri-id'], $uid, $inbox, $target_item['created'], $cmd, $receivers);
Worker::add([Worker::PRIORITY_HIGH, 'dont_fork' => true], 'APDelivery', '', 0, $inbox, 0); Worker::add([Worker::PRIORITY_HIGH, 'dont_fork' => true], 'APDelivery', '', 0, $inbox, 0);
} else { } else {
if (Worker::add(['priority' => $priority, 'created' => $created, 'dont_fork' => true], if (Worker::add(
'APDelivery', $cmd, $target_item['id'], $inbox, $uid, $receivers, $target_item['uri-id'])) { ['priority' => $priority, 'created' => $created, 'dont_fork' => true],
'APDelivery',
$cmd,
$target_item['id'],
$inbox,
$uid,
$receivers,
$target_item['uri-id']
)) {
$delivery_queue_count++; $delivery_queue_count++;
} }
} }

View file

@ -167,7 +167,7 @@ class OnePoll
if ($curlResult->redirectIsPermanent()) { if ($curlResult->redirectIsPermanent()) {
DI::logger()->notice('Poll address permanently changed', [ DI::logger()->notice('Poll address permanently changed', [
'id' => $contact['id'], 'id' => $contact['id'],
'uid' => $contact['uid'], 'uid' => $contact['uid'],
'old' => $contact['poll'], 'old' => $contact['poll'],
'new' => $curlResult->getRedirectUrl(), 'new' => $curlResult->getRedirectUrl(),
@ -222,9 +222,9 @@ class OnePoll
$user = DBA::selectFirst('user', ['prvkey'], ['uid' => $importer_uid]); $user = DBA::selectFirst('user', ['prvkey'], ['uid' => $importer_uid]);
$condition = ["`server` != ? AND `user` != ? AND `port` != ? AND `uid` = ?", '', '', 0, $importer_uid]; $condition = ["`server` != ? AND `user` != ? AND `port` != ? AND `uid` = ?", '', '', 0, $importer_uid];
$mailconf = DBA::selectFirst('mailacct', [], $condition); $mailconf = DBA::selectFirst('mailacct', [], $condition);
if (DBA::isResult($user) && DBA::isResult($mailconf)) { if (DBA::isResult($user) && DBA::isResult($mailconf)) {
$mailbox = Email::constructMailboxName($mailconf); $mailbox = Email::constructMailboxName($mailconf);
$password = ''; $password = '';
openssl_private_decrypt(hex2bin($mailconf['pass']), $password, $user['prvkey']); openssl_private_decrypt(hex2bin($mailconf['pass']), $password, $user['prvkey']);
$mbox = Email::connect($mailbox, $mailconf['user'], $password); $mbox = Email::connect($mailbox, $mailconf['user'], $password);
@ -278,18 +278,19 @@ class OnePoll
// $meta = Email::messageMeta($mbox, $msg_uid); // $meta = Email::messageMeta($mbox, $msg_uid);
// Have we seen it before? // Have we seen it before?
$fields = ['deleted', 'id']; $fields = ['deleted', 'id'];
$condition = ['uid' => $importer_uid, 'uri' => $datarray['uri']]; $condition = ['uid' => $importer_uid, 'uri' => $datarray['uri']];
$item = Post::selectFirst($fields, $condition); $item = Post::selectFirst($fields, $condition);
if (DBA::isResult($item)) { if (DBA::isResult($item)) {
DI::logger()->info('Mail: Seen before ' . $msg_uid . ' for ' . $mailconf['user'] . ' UID: ' . $importer_uid . ' URI: ' . $datarray['uri']); DI::logger()->info('Mail: Seen before ' . $msg_uid . ' for ' . $mailconf['user'] . ' UID: ' . $importer_uid . ' URI: ' . $datarray['uri']);
// Only delete when mails aren't automatically moved or deleted // Only delete when mails aren't automatically moved or deleted
if (($mailconf['action'] != 1) && ($mailconf['action'] != 3)) if (($mailconf['action'] != 1) && ($mailconf['action'] != 3)) {
if ($meta->deleted && ! $item['deleted']) { if ($meta->deleted && ! $item['deleted']) {
$fields = ['deleted' => true, 'changed' => $updated]; $fields = ['deleted' => true, 'changed' => $updated];
Item::update($fields, ['id' => $item['id']]); Item::update($fields, ['id' => $item['id']]);
} }
}
switch ($mailconf['action']) { switch ($mailconf['action']) {
case 0: case 0:
@ -327,19 +328,19 @@ class OnePoll
if ($raw_refs) { if ($raw_refs) {
$refs_arr = explode(' ', $raw_refs); $refs_arr = explode(' ', $raw_refs);
if (count($refs_arr)) { if (count($refs_arr)) {
for ($x = 0; $x < count($refs_arr); $x ++) { for ($x = 0; $x < count($refs_arr); $x++) {
$refs_arr[$x] = Email::msgid2iri(str_replace(['<', '>', ' '],['', '', ''], $refs_arr[$x])); $refs_arr[$x] = Email::msgid2iri(str_replace(['<', '>', ' '], ['', '', ''], $refs_arr[$x]));
} }
} }
$condition = ['uri' => $refs_arr, 'uid' => $importer_uid]; $condition = ['uri' => $refs_arr, 'uid' => $importer_uid];
$parent = Post::selectFirst(['uri'], $condition); $parent = Post::selectFirst(['uri'], $condition);
if (DBA::isResult($parent)) { if (DBA::isResult($parent)) {
$datarray['thr-parent'] = $parent['uri']; $datarray['thr-parent'] = $parent['uri'];
} }
} }
// Decoding the header // Decoding the header
$subject = imap_mime_header_decode($meta->subject ?? ''); $subject = imap_mime_header_decode($meta->subject ?? '');
$datarray['title'] = ""; $datarray['title'] = "";
foreach ($subject as $subpart) { foreach ($subject as $subpart) {
if ($subpart->charset != "default") { if ($subpart->charset != "default") {
@ -364,8 +365,8 @@ class OnePoll
// If it seems to be a reply but a header couldn't be found take the last message with matching subject // If it seems to be a reply but a header couldn't be found take the last message with matching subject
if (empty($datarray['thr-parent']) && $reply) { if (empty($datarray['thr-parent']) && $reply) {
$condition = ['title' => $datarray['title'], 'uid' => $importer_uid, 'network' => Protocol::MAIL]; $condition = ['title' => $datarray['title'], 'uid' => $importer_uid, 'network' => Protocol::MAIL];
$params = ['order' => ['created' => true]]; $params = ['order' => ['created' => true]];
$parent = Post::selectFirst(['uri'], $condition, $params); $parent = Post::selectFirst(['uri'], $condition, $params);
if (DBA::isResult($parent)) { if (DBA::isResult($parent)) {
$datarray['thr-parent'] = $parent['uri']; $datarray['thr-parent'] = $parent['uri'];
} }
@ -400,12 +401,12 @@ class OnePoll
$fromname = $headers->from[0]->personal; $fromname = $headers->from[0]->personal;
} }
$datarray['author-name'] = $fromname; $datarray['author-name'] = $fromname;
$datarray['author-link'] = 'mailto:' . $frommail; $datarray['author-link'] = 'mailto:' . $frommail;
$datarray['author-avatar'] = $contact['photo']; $datarray['author-avatar'] = $contact['photo'];
$datarray['owner-name'] = $contact['name']; $datarray['owner-name'] = $contact['name'];
$datarray['owner-link'] = 'mailto:' . $contact['addr']; $datarray['owner-link'] = 'mailto:' . $contact['addr'];
$datarray['owner-avatar'] = $contact['photo']; $datarray['owner-avatar'] = $contact['photo'];
if (empty($datarray['thr-parent']) || ($datarray['thr-parent'] === $datarray['uri'])) { if (empty($datarray['thr-parent']) || ($datarray['thr-parent'] === $datarray['uri'])) {
@ -413,7 +414,7 @@ class OnePoll
} }
if (!DI::pConfig()->get($importer_uid, 'system', 'allow_public_email_replies')) { if (!DI::pConfig()->get($importer_uid, 'system', 'allow_public_email_replies')) {
$datarray['private'] = Item::PRIVATE; $datarray['private'] = Item::PRIVATE;
$datarray['allow_cid'] = '<' . $contact['id'] . '>'; $datarray['allow_cid'] = '<' . $contact['id'] . '>';
} }

View file

@ -29,11 +29,15 @@ class PollContacts
$condition = ['network' => [Protocol::FEED, Protocol::MAIL], 'self' => false, 'blocked' => false, 'archive' => false]; $condition = ['network' => [Protocol::FEED, Protocol::MAIL], 'self' => false, 'blocked' => false, 'archive' => false];
if (!empty($abandon_days)) { if (!empty($abandon_days)) {
$condition = DBA::mergeConditions($condition, $condition = DBA::mergeConditions(
["`uid` != ? AND `uid` IN (SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired` AND `last-activity` > ?)", 0, DateTimeFormat::utc('now - ' . $abandon_days . ' days')]); $condition,
} else { ["`uid` != ? AND `uid` IN (SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired` AND `last-activity` > ?)", 0, DateTimeFormat::utc('now - ' . $abandon_days . ' days')]
$condition = DBA::mergeConditions($condition, );
["`uid` != ? AND `uid` IN (SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`)", 0]); } else {
$condition = DBA::mergeConditions(
$condition,
["`uid` != ? AND `uid` IN (SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`)", 0]
);
} }
$contacts = DBA::select('contact', ['id', 'nick', 'name', 'network', 'archive', 'last-update', 'priority', 'rating'], $condition); $contacts = DBA::select('contact', ['id', 'nick', 'name', 'network', 'archive', 'last-update', 'priority', 'rating'], $condition);
@ -47,10 +51,10 @@ class PollContacts
continue; continue;
} }
$now = DateTimeFormat::utcNow(); $now = DateTimeFormat::utcNow();
$next_update = DateTimeFormat::utc($contact['last-update'] . ' + ' . $interval . ' minute'); $next_update = DateTimeFormat::utc($contact['last-update'] . ' + ' . $interval . ' minute');
if ($now < $next_update) { if ($now < $next_update) {
DI::logger()->debug('No update', ['cid' => $contact['id'], 'interval' => $interval, 'next' => $next_update, 'now' => $now]); DI::logger()->debug('No update', ['cid' => $contact['id'], 'interval' => $interval, 'next' => $next_update, 'now' => $now]);
continue; continue;
} }

View file

@ -16,7 +16,8 @@ use Friendica\Protocol\ActivityPub;
/** /**
* Send updated profile data to Diaspora and ActivityPub * Send updated profile data to Diaspora and ActivityPub
*/ */
class ProfileUpdate { class ProfileUpdate
{
/** /**
* Sends updated profile data to Diaspora and ActivityPub * Sends updated profile data to Diaspora and ActivityPub
* *
@ -35,7 +36,8 @@ class ProfileUpdate {
foreach ($inboxes as $inbox => $receivers) { foreach ($inboxes as $inbox => $receivers) {
DI::logger()->info('Profile update for user ' . $uid . ' to ' . $inbox .' via ActivityPub'); DI::logger()->info('Profile update for user ' . $uid . ' to ' . $inbox .' via ActivityPub');
Worker::add(['priority' => $appHelper->getQueueValue('priority'), 'created' => $appHelper->getQueueValue('created'), 'dont_fork' => true], Worker::add(
['priority' => $appHelper->getQueueValue('priority'), 'created' => $appHelper->getQueueValue('created'), 'dont_fork' => true],
'APDelivery', 'APDelivery',
Delivery::PROFILEUPDATE, Delivery::PROFILEUPDATE,
0, 0,

View file

@ -75,7 +75,7 @@ class PushSubscription
} }
$message = DI::notificationFactory()->getMessageFromNotification($notification); $message = DI::notificationFactory()->getMessageFromNotification($notification);
$title = $message['plain'] ?? ''; $title = $message['plain'] ?? '';
$push = Subscription::create([ $push = Subscription::create([
'contentEncoding' => 'aesgcm', 'contentEncoding' => 'aesgcm',

View file

@ -33,7 +33,7 @@ class RemoveUnusedAvatars
$total = DBA::count('contact', $condition); $total = DBA::count('contact', $condition);
DI::logger()->notice('Starting removal', ['total' => $total]); DI::logger()->notice('Starting removal', ['total' => $total]);
$count = 0; $count = 0;
$contacts = DBA::select('contact', ['id', 'uri-id', 'uid', 'photo', 'thumb', 'micro'], $condition); $contacts = DBA::select('contact', ['id', 'uri-id', 'uid', 'photo', 'thumb', 'micro'], $condition);
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
if (Avatar::deleteCache($contact) || Photo::delete(['uid' => 0, 'contact-id' => $contact['id'], 'photo-type' => [Photo::CONTACT_AVATAR, Photo::CONTACT_BANNER]])) { if (Avatar::deleteCache($contact) || Photo::delete(['uid' => 0, 'contact-id' => $contact['id'], 'photo-type' => [Photo::CONTACT_AVATAR, Photo::CONTACT_BANNER]])) {
@ -52,8 +52,8 @@ class RemoveUnusedAvatars
private static function fixPhotoContacts() private static function fixPhotoContacts()
{ {
$total = 0; $total = 0;
$deleted = 0; $deleted = 0;
$updated1 = 0; $updated1 = 0;
$updated2 = 0; $updated2 = 0;
DI::logger()->notice('Starting contact fix'); DI::logger()->notice('Starting contact fix');
@ -61,7 +61,7 @@ class RemoveUnusedAvatars
while ($photo = DBA::fetch($photos)) { while ($photo = DBA::fetch($photos)) {
$total++; $total++;
$photo_contact = Contact::getById($photo['contact-id']); $photo_contact = Contact::getById($photo['contact-id']);
$resource = Photo::ridFromURI($photo_contact['photo']); $resource = Photo::ridFromURI($photo_contact['photo']);
if ($photo['resource-id'] == $resource) { if ($photo['resource-id'] == $resource) {
$contact = DBA::selectFirst('contact', [], ['nurl' => $photo_contact['nurl'], 'uid' => 0]); $contact = DBA::selectFirst('contact', [], ['nurl' => $photo_contact['nurl'], 'uid' => 0]);
if (!empty($contact['photo']) && ($contact['photo'] == $photo_contact['photo'])) { if (!empty($contact['photo']) && ($contact['photo'] == $photo_contact['photo'])) {
@ -70,7 +70,7 @@ class RemoveUnusedAvatars
$updated1++; $updated1++;
} }
} else { } else {
$updated = false; $updated = false;
$contacts = DBA::select('contact', [], ['nurl' => $photo_contact['nurl']]); $contacts = DBA::select('contact', [], ['nurl' => $photo_contact['nurl']]);
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
if ($photo['resource-id'] == Photo::ridFromURI($contact['photo'])) { if ($photo['resource-id'] == Photo::ridFromURI($contact['photo'])) {
@ -96,7 +96,7 @@ class RemoveUnusedAvatars
{ {
$size = [4 => 'photo', 5 => 'thumb', 6 => 'micro']; $size = [4 => 'photo', 5 => 'thumb', 6 => 'micro'];
$total = 0; $total = 0;
$deleted = 0; $deleted = 0;
DI::logger()->notice('Starting duplicate removal'); DI::logger()->notice('Starting duplicate removal');
$photos = DBA::p("SELECT `photo`.`id`, `photo`.`uid`, `photo`.`scale`, `photo`.`album`, `photo`.`contact-id`, `photo`.`resource-id`, `contact`.`photo`, `contact`.`thumb`, `contact`.`micro` FROM `photo` INNER JOIN `contact` ON `contact`.`id` = `photo`.`contact-id` and `photo`.`contact-id` != ? AND `photo`.`scale` IN (?, ?, ?)", 0, 4, 5, 6); $photos = DBA::p("SELECT `photo`.`id`, `photo`.`uid`, `photo`.`scale`, `photo`.`album`, `photo`.`contact-id`, `photo`.`resource-id`, `contact`.`photo`, `contact`.`thumb`, `contact`.`micro` FROM `photo` INNER JOIN `contact` ON `contact`.`id` = `photo`.`contact-id` and `photo`.`contact-id` != ? AND `photo`.`scale` IN (?, ?, ?)", 0, 4, 5, 6);

View file

@ -68,7 +68,7 @@ class RemoveUnusedContacts
} }
$contacts = DBA::select('contact', ['id', 'uid', 'photo', 'thumb', 'micro'], $condition, ['limit' => 1000]); $contacts = DBA::select('contact', ['id', 'uid', 'photo', 'thumb', 'micro'], $condition, ['limit' => 1000]);
$count = 0; $count = 0;
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
++$count; ++$count;
Photo::delete(['uid' => $contact['uid'], 'contact-id' => $contact['id']]); Photo::delete(['uid' => $contact['uid'], 'contact-id' => $contact['id']]);

View file

@ -14,11 +14,13 @@ use Friendica\Model\Item;
/** /**
* Posts items that where spooled because they couldn't be posted. * Posts items that where spooled because they couldn't be posted.
*/ */
class SpoolPost { class SpoolPost
public static function execute() { {
public static function execute()
{
$path = System::getSpoolPath(); $path = System::getSpoolPath();
if (($path != '') && is_writable($path)){ if (($path != '') && is_writable($path)) {
if ($dh = opendir($path)) { if ($dh = opendir($path)) {
while (($file = readdir($dh)) !== false) { while (($file = readdir($dh)) !== false) {

View file

@ -20,8 +20,8 @@ class UpdateBlockedServers
public static function execute() public static function execute()
{ {
DI::logger()->info('Update blocked servers - start'); DI::logger()->info('Update blocked servers - start');
$gservers = DBA::select('gserver', ['id', 'url', 'blocked']); $gservers = DBA::select('gserver', ['id', 'url', 'blocked']);
$changed = 0; $changed = 0;
$unchanged = 0; $unchanged = 0;
while ($gserver = DBA::fetch($gservers)) { while ($gserver = DBA::fetch($gservers)) {
$blocked = Network::isUrlBlocked($gserver['url']); $blocked = Network::isUrlBlocked($gserver['url']);

View file

@ -27,7 +27,7 @@ class UpdateContacts
} }
$updating = Worker::countWorkersByCommand('UpdateContact'); $updating = Worker::countWorkersByCommand('UpdateContact');
$limit = $update_limit - $updating; $limit = $update_limit - $updating;
if ($limit <= 0) { if ($limit <= 0) {
DI::logger()->info('The number of currently running jobs exceed the limit'); DI::logger()->info('The number of currently running jobs exceed the limit');
return; return;
@ -42,8 +42,8 @@ class UpdateContacts
} }
$condition = DBA::mergeConditions(["`next-update` < ?", DateTimeFormat::utcNow()], $condition); $condition = DBA::mergeConditions(["`next-update` < ?", DateTimeFormat::utcNow()], $condition);
$contacts = DBA::select('contact', ['id', 'url', 'gsid', 'baseurl'], $condition, ['order' => ['next-update'], 'limit' => $limit]); $contacts = DBA::select('contact', ['id', 'url', 'gsid', 'baseurl'], $condition, ['order' => ['next-update'], 'limit' => $limit]);
$count = 0; $count = 0;
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
if (Contact::isLocal($contact['url'])) { if (Contact::isLocal($contact['url'])) {
continue; continue;
@ -51,7 +51,7 @@ class UpdateContacts
try { try {
if ((!empty($contact['gsid']) || !empty($contact['baseurl'])) && GServer::reachable($contact)) { if ((!empty($contact['gsid']) || !empty($contact['baseurl'])) && GServer::reachable($contact)) {
$stamp = (float)microtime(true); $stamp = (float)microtime(true);
$success = Contact::updateFromProbe($contact['id']); $success = Contact::updateFromProbe($contact['id']);
DI::logger()->debug('Direct update', ['id' => $contact['id'], 'count' => $count, 'duration' => round((float)microtime(true) - $stamp, 3), 'success' => $success]); DI::logger()->debug('Direct update', ['id' => $contact['id'], 'count' => $count, 'duration' => round((float)microtime(true) - $stamp, 3), 'success' => $success]);
++$count; ++$count;

View file

@ -28,15 +28,15 @@ class UpdateGServers
} }
$updating = Worker::countWorkersByCommand('UpdateGServer'); $updating = Worker::countWorkersByCommand('UpdateGServer');
$limit = $update_limit - $updating; $limit = $update_limit - $updating;
if ($limit <= 0) { if ($limit <= 0) {
DI::logger()->info('The number of currently running jobs exceed the limit'); DI::logger()->info('The number of currently running jobs exceed the limit');
return; return;
} }
$total = DBA::count('gserver'); $total = DBA::count('gserver');
$condition = ["NOT `blocked` AND `next_contact` < ? AND (`nurl` != ? OR `url` != ?)", DateTimeFormat::utcNow(), '', '']; $condition = ["NOT `blocked` AND `next_contact` < ? AND (`nurl` != ? OR `url` != ?)", DateTimeFormat::utcNow(), '', ''];
$outdated = DBA::count('gserver', $condition); $outdated = DBA::count('gserver', $condition);
DI::logger()->info('Server status', ['total' => $total, 'outdated' => $outdated, 'updating' => $limit]); DI::logger()->info('Server status', ['total' => $total, 'outdated' => $outdated, 'updating' => $limit]);
$gservers = DBA::select('gserver', ['id', 'url', 'nurl', 'failed', 'created', 'last_contact'], $condition, ['limit' => $limit]); $gservers = DBA::select('gserver', ['id', 'url', 'nurl', 'failed', 'created', 'last_contact'], $condition, ['limit' => $limit]);

View file

@ -57,13 +57,14 @@ class AutomaticInstallationConsoleTest extends ConsoleTestCase
*/ */
private $dice; private $dice;
public function setUp() : void public function setUp(): void
{ {
static::markTestSkipped('Needs class \'Installer\' as constructing argument for console tests'); static::markTestSkipped('Needs class \'Installer\' as constructing argument for console tests');
parent::setUp(); parent::setUp();
$this->setUpVfsDir();; $this->setUpVfsDir();
;
if ($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php')) { if ($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php')) {
$this->root->getChild('config') $this->root->getChild('config')
@ -75,8 +76,8 @@ class AutomaticInstallationConsoleTest extends ConsoleTestCase
$l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; }); $l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
$this->dice->shouldReceive('create') $this->dice->shouldReceive('create')
->with(L10n::class) ->with(L10n::class)
->andReturn($l10nMock); ->andReturn($l10nMock);
DI::init($this->dice); DI::init($this->dice);
@ -115,11 +116,11 @@ class AutomaticInstallationConsoleTest extends ConsoleTestCase
'empty' => [ 'empty' => [
'data' => [ 'data' => [
'database' => [ 'database' => [
'hostname' => '', 'hostname' => '',
'username' => '', 'username' => '',
'password' => '', 'password' => '',
'database' => '', 'database' => '',
'port' => '', 'port' => '',
], ],
'config' => [ 'config' => [
'php_path' => '', 'php_path' => '',
@ -127,23 +128,23 @@ class AutomaticInstallationConsoleTest extends ConsoleTestCase
'admin_email' => '', 'admin_email' => '',
], ],
'system' => [ 'system' => [
'basepath' => '', 'basepath' => '',
'urlpath' => '', 'urlpath' => '',
'url' => 'http://friendica.local', 'url' => 'http://friendica.local',
'ssl_policy' => 0, 'ssl_policy' => 0,
'default_timezone' => '', 'default_timezone' => '',
'language' => '', 'language' => '',
], ],
], ],
], ],
'normal' => [ 'normal' => [
'data' => [ 'data' => [
'database' => [ 'database' => [
'hostname' => 'testhost', 'hostname' => 'testhost',
'port' => 3306, 'port' => 3306,
'username' => 'friendica', 'username' => 'friendica',
'password' => 'a password', 'password' => 'a password',
'database' => 'database', 'database' => 'database',
], ],
'config' => [ 'config' => [
'php_path' => '', 'php_path' => '',
@ -151,23 +152,23 @@ class AutomaticInstallationConsoleTest extends ConsoleTestCase
'admin_email' => 'admin@philipp.info', 'admin_email' => 'admin@philipp.info',
], ],
'system' => [ 'system' => [
'urlpath' => 'test/it', 'urlpath' => 'test/it',
'url' => 'http://friendica.local/test/it', 'url' => 'http://friendica.local/test/it',
'basepath' => '', 'basepath' => '',
'ssl_policy' => '2', 'ssl_policy' => '2',
'default_timezone' => 'en', 'default_timezone' => 'en',
'language' => 'Europe/Berlin', 'language' => 'Europe/Berlin',
], ],
], ],
], ],
'special' => [ 'special' => [
'data' => [ 'data' => [
'database' => [ 'database' => [
'hostname' => 'testhost.new.domain', 'hostname' => 'testhost.new.domain',
'port' => 3341, 'port' => 3341,
'username' => 'fr"§%ica', 'username' => 'fr"§%ica',
'password' => '$%\"gse', 'password' => '$%\"gse',
'database' => 'db', 'database' => 'db',
], ],
'config' => [ 'config' => [
'php_path' => '', 'php_path' => '',
@ -175,12 +176,12 @@ class AutomaticInstallationConsoleTest extends ConsoleTestCase
'admin_email' => 'admin@philipp.info', 'admin_email' => 'admin@philipp.info',
], ],
'system' => [ 'system' => [
'urlpath' => 'test/it', 'urlpath' => 'test/it',
'url' => 'https://friendica.local/test/it', 'url' => 'https://friendica.local/test/it',
'basepath' => '', 'basepath' => '',
'ssl_policy' => '1', 'ssl_policy' => '1',
'default_timezone' => 'en', 'default_timezone' => 'en',
'language' => 'Europe/Berlin', 'language' => 'Europe/Berlin',
], ],
], ],
], ],
@ -562,22 +563,22 @@ CONF;
$console = new AutomaticInstallation($this->consoleArgv); $console = new AutomaticInstallation($this->consoleArgv);
$option = function($var, $cat, $key) use ($data, $console) { $option = function ($var, $cat, $key) use ($data, $console) {
if (!empty($data[$cat][$key])) { if (!empty($data[$cat][$key])) {
$console->setOption($var, $data[$cat][$key]); $console->setOption($var, $data[$cat][$key]);
} }
}; };
$option('dbhost' , 'database', 'hostname'); $option('dbhost', 'database', 'hostname');
$option('dbport' , 'database', 'port'); $option('dbport', 'database', 'port');
$option('dbuser' , 'database', 'username'); $option('dbuser', 'database', 'username');
$option('dbpass' , 'database', 'password'); $option('dbpass', 'database', 'password');
$option('dbdata' , 'database', 'database'); $option('dbdata', 'database', 'database');
$option('url' , 'system' , 'url'); $option('url', 'system', 'url');
$option('phppath' , 'config' , 'php_path'); $option('phppath', 'config', 'php_path');
$option('admin' , 'config' , 'admin_email'); $option('admin', 'config', 'admin_email');
$option('tz' , 'system' , 'default_timezone'); $option('tz', 'system', 'default_timezone');
$option('lang' , 'system' , 'language'); $option('lang', 'system', 'language');
$option('basepath' , 'system' , 'basepath'); $option('basepath', 'system', 'basepath');
$txt = $this->dumpExecute($console); $txt = $this->dumpExecute($console);

View file

@ -59,15 +59,15 @@ function update_1298()
{ {
$keys = ['gender', 'marital', 'sexual']; $keys = ['gender', 'marital', 'sexual'];
foreach ($keys as $translateKey) { foreach ($keys as $translateKey) {
$allData = DBA::select('profile', ['id', $translateKey]); $allData = DBA::select('profile', ['id', $translateKey]);
$allLangs = DI::l10n()->getAvailableLanguages(); $allLangs = DI::l10n()->getAvailableLanguages();
$success = 0; $success = 0;
$fail = 0; $fail = 0;
foreach ($allData as $key => $data) { foreach ($allData as $key => $data) {
$toTranslate = $data[$translateKey]; $toTranslate = $data[$translateKey];
if ($toTranslate != '') { if ($toTranslate != '') {
foreach ($allLangs as $key => $lang) { foreach ($allLangs as $key => $lang) {
$a = new \stdClass(); $a = new \stdClass();
$a->strings = []; $a->strings = [];
// First we get the localizations // First we get the localizations
@ -95,7 +95,7 @@ function update_1298()
} else { } else {
DBA::update('profile', [$translateKey => $key], ['id' => $data['id']]); DBA::update('profile', [$translateKey => $key], ['id' => $data['id']]);
DI::logger()->notice('Updated contact', ['action' => 'update', 'contact' => $data['id'], "$translateKey" => $key, DI::logger()->notice('Updated contact', ['action' => 'update', 'contact' => $data['id'], "$translateKey" => $key,
'was' => $data[$translateKey]]); 'was' => $data[$translateKey]]);
Contact::updateSelfFromUserID($data['id']); Contact::updateSelfFromUserID($data['id']);
Profile::publishUpdate($data['id']); Profile::publishUpdate($data['id']);
@ -190,7 +190,7 @@ function update_1330()
// Update attachments and photos // Update attachments and photos
if (!DBA::e("UPDATE `photo` SET `photo`.`backend-class` = SUBSTR(`photo`.`backend-class`, 25) WHERE `photo`.`backend-class` LIKE 'Friendica\\\Model\\\Storage\\\%' ESCAPE '|'") || if (!DBA::e("UPDATE `photo` SET `photo`.`backend-class` = SUBSTR(`photo`.`backend-class`, 25) WHERE `photo`.`backend-class` LIKE 'Friendica\\\Model\\\Storage\\\%' ESCAPE '|'") ||
!DBA::e("UPDATE `attach` SET `attach`.`backend-class` = SUBSTR(`attach`.`backend-class`, 25) WHERE `attach`.`backend-class` LIKE 'Friendica\\\Model\\\Storage\\\%' ESCAPE '|'")) { !DBA::e("UPDATE `attach` SET `attach`.`backend-class` = SUBSTR(`attach`.`backend-class`, 25) WHERE `attach`.`backend-class` LIKE 'Friendica\\\Model\\\Storage\\\%' ESCAPE '|'")) {
return Update::FAILED; return Update::FAILED;
}; };
@ -200,7 +200,7 @@ function update_1330()
function update_1332() function update_1332()
{ {
$condition = ["`is-default` IS NOT NULL"]; $condition = ["`is-default` IS NOT NULL"];
$profiles = DBA::select('profile', [], $condition); $profiles = DBA::select('profile', [], $condition);
while ($profile = DBA::fetch($profiles)) { while ($profile = DBA::fetch($profiles)) {
Profile::migrate($profile); Profile::migrate($profile);
@ -650,13 +650,19 @@ function pre_update_1377()
function update_1380() function update_1380()
{ {
if (!DBA::e("UPDATE `notify` INNER JOIN `item` ON `item`.`id` = `notify`.`iid` SET `notify`.`uri-id` = `item`.`uri-id` WHERE `notify`.`uri-id` IS NULL AND `notify`.`otype` IN (?, ?)", if (!DBA::e(
Notification\ObjectType::ITEM, Notification\ObjectType::PERSON)) { "UPDATE `notify` INNER JOIN `item` ON `item`.`id` = `notify`.`iid` SET `notify`.`uri-id` = `item`.`uri-id` WHERE `notify`.`uri-id` IS NULL AND `notify`.`otype` IN (?, ?)",
Notification\ObjectType::ITEM,
Notification\ObjectType::PERSON
)) {
return Update::FAILED; return Update::FAILED;
} }
if (!DBA::e("UPDATE `notify` INNER JOIN `item` ON `item`.`id` = `notify`.`parent` SET `notify`.`parent-uri-id` = `item`.`uri-id` WHERE `notify`.`parent-uri-id` IS NULL AND `notify`.`otype` IN (?, ?)", if (!DBA::e(
Notification\ObjectType::ITEM, Notification\ObjectType::PERSON)) { "UPDATE `notify` INNER JOIN `item` ON `item`.`id` = `notify`.`parent` SET `notify`.`parent-uri-id` = `item`.`uri-id` WHERE `notify`.`parent-uri-id` IS NULL AND `notify`.`otype` IN (?, ?)",
Notification\ObjectType::ITEM,
Notification\ObjectType::PERSON
)) {
return Update::FAILED; return Update::FAILED;
} }
@ -763,7 +769,7 @@ function update_1398()
if (!DBA::e("INSERT IGNORE INTO `post-thread` (`uri-id`, `owner-id`, `author-id`, `network`, `created`, `received`, `changed`, `commented`) if (!DBA::e("INSERT IGNORE INTO `post-thread` (`uri-id`, `owner-id`, `author-id`, `network`, `created`, `received`, `changed`, `commented`)
SELECT `uri-id`, `owner-id`, `author-id`, `network`, `created`, `received`, `changed`, `commented` FROM `thread`")) { SELECT `uri-id`, `owner-id`, `author-id`, `network`, `created`, `received`, `changed`, `commented` FROM `thread`")) {
return Update::FAILED; return Update::FAILED;
} }
if (!DBStructure::existsTable('thread')) { if (!DBStructure::existsTable('thread')) {
@ -772,7 +778,7 @@ function update_1398()
if (!DBA::e("UPDATE `post-thread-user` INNER JOIN `thread` ON `thread`.`uid` = `post-thread-user`.`uid` AND `thread`.`uri-id` = `post-thread-user`.`uri-id` if (!DBA::e("UPDATE `post-thread-user` INNER JOIN `thread` ON `thread`.`uid` = `post-thread-user`.`uid` AND `thread`.`uri-id` = `post-thread-user`.`uri-id`
SET `post-thread-user`.`mention` = `thread`.`mention`")) { SET `post-thread-user`.`mention` = `thread`.`mention`")) {
return Update::FAILED; return Update::FAILED;
} }
return Update::SUCCESS; return Update::SUCCESS;
@ -784,7 +790,7 @@ function update_1399()
SET `post-thread-user`.`contact-id` = `post-user`.`contact-id`, `post-thread-user`.`unseen` = `post-user`.`unseen`, SET `post-thread-user`.`contact-id` = `post-user`.`contact-id`, `post-thread-user`.`unseen` = `post-user`.`unseen`,
`post-thread-user`.`hidden` = `post-user`.`hidden`, `post-thread-user`.`origin` = `post-user`.`origin`, `post-thread-user`.`hidden` = `post-user`.`hidden`, `post-thread-user`.`origin` = `post-user`.`origin`,
`post-thread-user`.`psid` = `post-user`.`psid`, `post-thread-user`.`post-user-id` = `post-user`.`id`")) { `post-thread-user`.`psid` = `post-user`.`psid`, `post-thread-user`.`post-user-id` = `post-user`.`id`")) {
return Update::FAILED; return Update::FAILED;
} }
return Update::SUCCESS; return Update::SUCCESS;
@ -796,7 +802,7 @@ function update_1400()
`created`, `received`, `edited`, `gravity`, `causer-id`, `post-type`, `vid`, `private`, `visible`, `deleted`, `global`) `created`, `received`, `edited`, `gravity`, `causer-id`, `post-type`, `vid`, `private`, `visible`, `deleted`, `global`)
SELECT `uri-id`, `parent-uri-id`, `thr-parent-id`, `owner-id`, `author-id`, `network`, `created`, `received`, `edited`, SELECT `uri-id`, `parent-uri-id`, `thr-parent-id`, `owner-id`, `author-id`, `network`, `created`, `received`, `edited`,
`gravity`, `causer-id`, `post-type`, `vid`, `private`, `visible`, `deleted`, `global` FROM `item`")) { `gravity`, `causer-id`, `post-type`, `vid`, `private`, `visible`, `deleted`, `global` FROM `item`")) {
return Update::FAILED; return Update::FAILED;
} }
if (!DBA::e("UPDATE `post-user` INNER JOIN `item` ON `item`.`uri-id` = `post-user`.`uri-id` AND `item`.`uid` = `post-user`.`uid` if (!DBA::e("UPDATE `post-user` INNER JOIN `item` ON `item`.`uri-id` = `post-user`.`uri-id` AND `item`.`uid` = `post-user`.`uid`
@ -936,7 +942,7 @@ function update_1419()
{ {
$mails = DBA::select('mail', ['id', 'from-url', 'uri', 'parent-uri', 'guid'], [], ['order' => ['id']]); $mails = DBA::select('mail', ['id', 'from-url', 'uri', 'parent-uri', 'guid'], [], ['order' => ['id']]);
while ($mail = DBA::fetch($mails)) { while ($mail = DBA::fetch($mails)) {
$fields = []; $fields = [];
$fields['author-id'] = Contact::getIdForURL($mail['from-url'], 0, false); $fields['author-id'] = Contact::getIdForURL($mail['from-url'], 0, false);
if (empty($fields['author-id'])) { if (empty($fields['author-id'])) {
continue; continue;
@ -1015,7 +1021,7 @@ function update_1439()
if (!empty($fcontact['url'])) { if (!empty($fcontact['url'])) {
$id = Contact::getIdForURL($fcontact['url']); $id = Contact::getIdForURL($fcontact['url']);
if (!empty($id)) { if (!empty($id)) {
DBA::update('intro',['suggest-cid' => $id], ['id' => $intro['id']]); DBA::update('intro', ['suggest-cid' => $id], ['id' => $intro['id']]);
} }
} }
} }
@ -1076,7 +1082,7 @@ function update_1444()
function update_1446() function update_1446()
{ {
$distributed_cache_driver_source = DI::config()->getCache()->getSource('system', 'distributed_cache_driver'); $distributed_cache_driver_source = DI::config()->getCache()->getSource('system', 'distributed_cache_driver');
$cache_driver_source = DI::config()->getCache()->getSource('system', 'cache_driver'); $cache_driver_source = DI::config()->getCache()->getSource('system', 'cache_driver');
// In case the distributed cache driver is the default value, but the current cache driver isn't default, // In case the distributed cache driver is the default value, but the current cache driver isn't default,
// we assume that the distributed cache driver should be the same as the current cache driver // we assume that the distributed cache driver should be the same as the current cache driver
@ -1196,7 +1202,7 @@ function update_1509()
foreach ($addons as $addon) { foreach ($addons as $addon) {
$newConfig->set('addons', $addon['name'], [ $newConfig->set('addons', $addon['name'], [
'last_update' => $addon['timestamp'], 'last_update' => $addon['timestamp'],
'admin' => (bool)$addon['plugin_admin'], 'admin' => (bool)$addon['plugin_admin'],
]); ]);
} }
@ -1251,7 +1257,7 @@ function update_1514()
if (file_exists(dirname(__FILE__) . '/config/node.config.php')) { if (file_exists(dirname(__FILE__) . '/config/node.config.php')) {
$transactionalConfig = DI::config()->beginTransaction(); $transactionalConfig = DI::config()->beginTransaction();
$oldConfig = include dirname(__FILE__) . '/config/node.config.php'; $oldConfig = include dirname(__FILE__) . '/config/node.config.php';
if (is_array($oldConfig)) { if (is_array($oldConfig)) {
$categories = array_keys($oldConfig); $categories = array_keys($oldConfig);
@ -1525,4 +1531,4 @@ function update_1573()
} }
DBA::close($media); DBA::close($media);
return Update::SUCCESS; return Update::SUCCESS;
} }

Some files were not shown because too many files have changed in this diff Show more