Browse Source

Merge pull request #7765 from nupplaphil/task/move_text

Move include/text.php to class structure
pull/7767/head
Hypolite Petovan 2 years ago
committed by GitHub
parent
commit
9f460c6797
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      composer.json
  2. 23
      doc/Addons.md
  3. 31
      doc/de/Addons.md
  4. 49
      include/conversation.php
  5. 275
      include/text.php
  6. 18
      mod/editpost.php
  7. 14
      mod/events.php
  8. 21
      mod/item.php
  9. 13
      mod/lockview.php
  10. 11
      mod/network.php
  11. 35
      mod/photos.php
  12. 13
      mod/settings.php
  13. 2
      src/BaseObject.php
  14. 79
      src/Content/Item.php
  15. 32
      src/Content/Text/BBCode/Video.php
  16. 6
      src/Model/Event.php
  17. 52
      src/Model/Item.php
  18. 24
      src/Model/Profile.php
  19. 12
      src/Module/Item/Compose.php
  20. 5
      src/Module/Profile.php
  21. 14
      src/Object/Post.php
  22. 23
      src/Protocol/Activity.php
  23. 12
      src/Protocol/DFRN.php
  24. 67
      src/Util/ACLFormatter.php
  25. 33
      src/Util/DateTimeFormat.php
  26. 12
      src/Worker/Notifier.php
  27. 13
      tests/src/Content/ItemTest.php
  28. 43
      tests/src/Content/Text/BBCode/VideoTest.php
  29. 3
      tests/src/Core/InstallerTest.php
  30. 57
      tests/src/Protocol/ActivityTest.php
  31. 133
      tests/src/Util/ACLFormaterTest.php
  32. 61
      tests/src/Util/DateTimeFormatTest.php

1
composer.json

@ -83,7 +83,6 @@
"include/dba.php",
"include/enotify.php",
"include/items.php",
"include/text.php",
"boot.php"
]
},

23
doc/Addons.md

@ -503,16 +503,6 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
Hook::callAll('item_photo_menu', $args);
Hook::callAll('jot_tool', $jotplugins);
### include/text.php
Hook::callAll('contact_block_end', $arr);
Hook::callAll('poke_verbs', $arr);
Hook::callAll('put_item_in_cache', $hook_data);
Hook::callAll('prepare_body_init', $item);
Hook::callAll('prepare_body_content_filter', $hook_data);
Hook::callAll('prepare_body', $hook_data);
Hook::callAll('prepare_body_final', $hook_data);
### include/items.php
Hook::callAll('page_info_data', $data);
@ -649,6 +639,11 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
Hook::callAll('post_remote_end', $posted_item);
Hook::callAll('tagged', $arr);
Hook::callAll('post_local_end', $new_item);
Hook::callAll('put_item_in_cache', $hook_data);
Hook::callAll('prepare_body_init', $item);
Hook::callAll('prepare_body_content_filter', $hook_data);
Hook::callAll('prepare_body', $hook_data);
Hook::callAll('prepare_body_final', $hook_data);
### src/Model/Contact.php
@ -673,6 +668,10 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
Hook::callAll('register_account', $uid);
Hook::callAll('remove_user', $user);
### src/Content/ContactBlock.php
Hook::callAll('contact_block_end', $arr);
### src/Content/Text/BBCode.php
Hook::callAll('bbcode', $text);
@ -746,6 +745,10 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
self::callSingle(self::getApp(), 'hook_fork', $fork_hook, $hookdata);
### src/Core/L10n/L10n.php
Hook::callAll('poke_verbs', $arr);
### src/Core/Worker.php
Hook::callAll("proc_run", $arr);

31
doc/de/Addons.md

@ -226,16 +226,6 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
Hook::callAll('item_photo_menu', $args);
Hook::callAll('jot_tool', $jotplugins);
### include/text.php
Hook::callAll('contact_block_end', $arr);
Hook::callAll('poke_verbs', $arr);
Hook::callAll('put_item_in_cache', $hook_data);
Hook::callAll('prepare_body_init', $item);
Hook::callAll('prepare_body_content_filter', $hook_data);
Hook::callAll('prepare_body', $hook_data);
Hook::callAll('prepare_body_final', $hook_data);
### include/items.php
Hook::callAll('page_info_data', $data);
@ -365,6 +355,11 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
Hook::callAll('post_remote_end', $posted_item);
Hook::callAll('tagged', $arr);
Hook::callAll('post_local_end', $new_item);
Hook::callAll('put_item_in_cache', $hook_data);
Hook::callAll('prepare_body_init', $item);
Hook::callAll('prepare_body_content_filter', $hook_data);
Hook::callAll('prepare_body', $hook_data);
Hook::callAll('prepare_body_final', $hook_data);
### src/Model/Contact.php
@ -387,6 +382,10 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
Hook::callAll('register_account', $uid);
Hook::callAll('remove_user', $user);
### src/Content/ContactBlock.php
Hook::callAll('contact_block_end', $arr);
### src/Content/Text/BBCode.php
@ -457,6 +456,18 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
Hook::callAll($a->module.'_post_'.$selname, $o);
Hook::callAll('jot_networks', $jotnets);
### src/Core/Authentication.php
Hook::callAll('logged_in', $a->user);
### src/Core/Hook.php
self::callSingle(self::getApp(), 'hook_fork', $fork_hook, $hookdata);
### src/Core/L10n/L10n.php
Hook::callAll('poke_verbs', $arr);
### src/Core/Worker.php
Hook::callAll("proc_run", $arr);

49
include/conversation.php

@ -4,8 +4,10 @@
*/
use Friendica\App;
use Friendica\BaseObject;
use Friendica\Content\ContactSelector;
use Friendica\Content\Feature;
use Friendica\Content\Item as ContentItem;
use Friendica\Content\Pager;
use Friendica\Content\Text\BBCode;
use Friendica\Core\Config;
@ -24,6 +26,7 @@ use Friendica\Model\Profile;
use Friendica\Model\Term;
use Friendica\Object\Post;
use Friendica\Object\Thread;
use Friendica\Protocol\Activity;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Temporal;
@ -138,12 +141,15 @@ function localize_item(&$item)
During the further steps of the database restructuring I would like to address this issue.
*/
/** @var Activity $activity */
$activity = BaseObject::getClass(Activity::class);
$xmlhead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">";
if (activity_match($item['verb'], ACTIVITY_LIKE)
|| activity_match($item['verb'], ACTIVITY_DISLIKE)
|| activity_match($item['verb'], ACTIVITY_ATTEND)
|| activity_match($item['verb'], ACTIVITY_ATTENDNO)
|| activity_match($item['verb'], ACTIVITY_ATTENDMAYBE)) {
if ($activity->match($item['verb'], ACTIVITY_LIKE)
|| $activity->match($item['verb'], ACTIVITY_DISLIKE)
|| $activity->match($item['verb'], ACTIVITY_ATTEND)
|| $activity->match($item['verb'], ACTIVITY_ATTENDNO)
|| $activity->match($item['verb'], ACTIVITY_ATTENDMAYBE)) {
$fields = ['author-link', 'author-name', 'verb', 'object-type', 'resource-id', 'body', 'plink'];
$obj = Item::selectFirst($fields, ['uri' => $item['parent-uri']]);
@ -178,22 +184,22 @@ function localize_item(&$item)
$plink = '[url=' . $obj['plink'] . ']' . $post_type . '[/url]';
$bodyverb = '';
if (activity_match($item['verb'], ACTIVITY_LIKE)) {
if ($activity->match($item['verb'], ACTIVITY_LIKE)) {
$bodyverb = L10n::t('%1$s likes %2$s\'s %3$s');
} elseif (activity_match($item['verb'], ACTIVITY_DISLIKE)) {
} elseif ($activity->match($item['verb'], ACTIVITY_DISLIKE)) {
$bodyverb = L10n::t('%1$s doesn\'t like %2$s\'s %3$s');
} elseif (activity_match($item['verb'], ACTIVITY_ATTEND)) {
} elseif ($activity->match($item['verb'], ACTIVITY_ATTEND)) {
$bodyverb = L10n::t('%1$s attends %2$s\'s %3$s');
} elseif (activity_match($item['verb'], ACTIVITY_ATTENDNO)) {
} elseif ($activity->match($item['verb'], ACTIVITY_ATTENDNO)) {
$bodyverb = L10n::t('%1$s doesn\'t attend %2$s\'s %3$s');
} elseif (activity_match($item['verb'], ACTIVITY_ATTENDMAYBE)) {
} elseif ($activity->match($item['verb'], ACTIVITY_ATTENDMAYBE)) {
$bodyverb = L10n::t('%1$s attends maybe %2$s\'s %3$s');
}
$item['body'] = sprintf($bodyverb, $author, $objauthor, $plink);
}
if (activity_match($item['verb'], ACTIVITY_FRIEND)) {
if ($activity->match($item['verb'], ACTIVITY_FRIEND)) {
if ($item['object-type']=="" || $item['object-type']!== ACTIVITY_OBJ_PERSON) return;
@ -275,7 +281,7 @@ function localize_item(&$item)
}
if (activity_match($item['verb'], ACTIVITY_TAG)) {
if ($activity->match($item['verb'], ACTIVITY_TAG)) {
$fields = ['author-id', 'author-link', 'author-name', 'author-network',
'verb', 'object-type', 'resource-id', 'body', 'plink'];
$obj = Item::selectFirst($fields, ['uri' => $item['parent-uri']]);
@ -320,7 +326,7 @@ function localize_item(&$item)
$item['body'] = L10n::t('%1$s tagged %2$s\'s %3$s with %4$s', $author, $objauthor, $plink, $tag);
}
if (activity_match($item['verb'], ACTIVITY_FAVORITE)) {
if ($activity->match($item['verb'], ACTIVITY_FAVORITE)) {
if ($item['object-type'] == "") {
return;
}
@ -393,19 +399,22 @@ function count_descendants($item) {
function visible_activity($item) {
/** @var Activity $activity */
$activity = BaseObject::getClass(Activity::class);
/*
* likes (etc.) can apply to other things besides posts. Check if they are post children,
* in which case we handle them specially
*/
$hidden_activities = [ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE, ACTIVITY_FOLLOW, ACTIVITY2_ANNOUNCE];
foreach ($hidden_activities as $act) {
if (activity_match($item['verb'], $act)) {
if ($activity->match($item['verb'], $act)) {
return false;
}
}
// @TODO below if() block can be rewritten to a single line: $isVisible = allConditionsHere;
if (activity_match($item['verb'], ACTIVITY_FOLLOW) && $item['object-type'] === ACTIVITY_OBJ_NOTE && empty($item['self']) && $item['uid'] == local_user()) {
if ($activity->match($item['verb'], ACTIVITY_FOLLOW) && $item['object-type'] === ACTIVITY_OBJ_NOTE && empty($item['self']) && $item['uid'] == local_user()) {
return false;
}
@ -663,7 +672,10 @@ function conversation(App $a, array $items, Pager $pager, $mode, $update, $previ
$body = Item::prepareBody($item, true, $preview);
list($categories, $folders) = get_cats_and_terms($item);
/** @var ContentItem $contItem */
$contItem = BaseObject::getClass(ContentItem::class);
list($categories, $folders) = $contItem->determineCategoriesTerms($item);
if (!empty($item['content-warning']) && PConfig::get(local_user(), 'system', 'disable_cw', false)) {
$title = ucfirst($item['content-warning']);
@ -1017,7 +1029,10 @@ function builtin_activity_puller($item, &$conv_responses) {
return;
}
if (activity_match($item['verb'], $verb) && ($item['id'] != $item['parent'])) {
/** @var Activity $activity */
$activity = BaseObject::getClass(Activity::class);
if ($activity->match($item['verb'], $verb) && ($item['id'] != $item['parent'])) {
$author = ['uid' => 0, 'id' => $item['author-id'],
'network' => $item['author-network'], 'url' => $item['author-link']];
$url = Contact::magicLinkByContact($author);

275
include/text.php

@ -1,275 +0,0 @@
<?php
/**
* @file include/text.php
*/
use Friendica\App;
use Friendica\Content\Text\BBCode;
use Friendica\Core\Protocol;
use Friendica\Model\Contact;
use Friendica\Model\FileTag;
use Friendica\Model\Group;
use Friendica\Util\Strings;
/**
* Turn user/group ACLs stored as angle bracketed text into arrays
*
* @param string $s
* @return array
*/
function expand_acl($s) {
// turn string array of angle-bracketed elements into numeric array
// e.g. "<1><2><3>" => array(1,2,3);
preg_match_all('/<(' . Group::FOLLOWERS . '|'. Group::MUTUALS . '|[0-9]+)>/', $s, $matches, PREG_PATTERN_ORDER);
return $matches[1];
}
/**
* Wrap ACL elements in angle brackets for storage
* @param string $item
*/
function sanitise_acl(&$item) {
if (intval($item)) {
$item = '<' . intval(Strings::escapeTags(trim($item))) . '>';
} elseif (in_array($item, [Group::FOLLOWERS, Group::MUTUALS])) {
$item = '<' . $item . '>';
} else {
unset($item);
}
}
/**
* Convert an ACL array to a storable string
*
* Normally ACL permissions will be an array.
* We'll also allow a comma-separated string.
*
* @param string|array $p
* @return string
*/
function perms2str($p) {
$ret = '';
if (is_array($p)) {
$tmp = $p;
} else {
$tmp = explode(',', $p);
}
if (is_array($tmp)) {
array_walk($tmp, 'sanitise_acl');
$ret = implode('', $tmp);
}
return $ret;
}
/**
* for html,xml parsing - let's say you've got
* an attribute foobar="class1 class2 class3"
* and you want to find out if it contains 'class3'.
* you can't use a normal sub string search because you
* might match 'notclass3' and a regex to do the job is
* possible but a bit complicated.
* pass the attribute string as $attr and the attribute you
* are looking for as $s - returns true if found, otherwise false
*
* @param string $attr attribute value
* @param string $s string to search
* @return boolean True if found, False otherwise
*/
function attribute_contains($attr, $s) {
$a = explode(' ', $attr);
return (count($a) && in_array($s,$a));
}
/**
* Compare activity uri. Knows about activity namespace.
*
* @param string $haystack
* @param string $needle
* @return boolean
*/
function activity_match($haystack,$needle) {
return (($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle, NAMESPACE_ACTIVITY_SCHEMA)));
}
/**
* quick and dirty quoted_printable encoding
*
* @param string $s
* @return string
*/
function qp($s) {
return str_replace("%", "=", rawurlencode($s));
}
/**
* @brief Find any non-embedded images in private items and add redir links to them
*
* @param App $a
* @param array &$item The field array of an item row
*/
function redir_private_images($a, &$item)
{
$matches = [];
$cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER);
if ($cnt) {
foreach ($matches as $mtch) {
if (strpos($mtch[1], '/redir') !== false) {
continue;
}
if ((local_user() == $item['uid']) && ($item['private'] == 1) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == Protocol::DFRN)) {
$img_url = 'redir/' . $item['contact-id'] . '?url=' . urlencode($mtch[1]);
$item['body'] = str_replace($mtch[0], '[img]' . $img_url . '[/img]', $item['body']);
}
}
}
}
/**
* @brief Given a text string, convert from bbcode to html and add smilie icons.
*
* @param string $text String with bbcode.
* @return string Formatted HTML
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
function prepare_text($text)
{
$s = BBCode::convert($text);
return trim($s);
}
/**
* return array with details for categories and folders for an item
*
* @param array $item
* @return array
*
* [
* [ // categories array
* {
* 'name': 'category name',
* 'removeurl': 'url to remove this category',
* 'first': 'is the first in this array? true/false',
* 'last': 'is the last in this array? true/false',
* } ,
* ....
* ],
* [ //folders array
* {
* 'name': 'folder name',
* 'removeurl': 'url to remove this folder',
* 'first': 'is the first in this array? true/false',
* 'last': 'is the last in this array? true/false',
* } ,
* ....
* ]
* ]
*/
function get_cats_and_terms($item)
{
$categories = [];
$folders = [];
$first = true;
foreach (FileTag::fileToArray($item['file'] ?? '', 'category') as $savedFolderName) {
$categories[] = [
'name' => $savedFolderName,
'url' => "#",
'removeurl' => ((local_user() == $item['uid']) ? 'filerm/' . $item['id'] . '?f=&cat=' . rawurlencode($savedFolderName) : ""),
'first' => $first,
'last' => false
];
$first = false;
}
if (count($categories)) {
$categories[count($categories) - 1]['last'] = true;
}
if (local_user() == $item['uid']) {
foreach (FileTag::fileToArray($item['file'] ?? '') as $savedFolderName) {
$folders[] = [
'name' => $savedFolderName,
'url' => "#",
'removeurl' => ((local_user() == $item['uid']) ? 'filerm/' . $item['id'] . '?f=&term=' . rawurlencode($savedFolderName) : ""),
'first' => $first,
'last' => false
];
$first = false;
}
}
if (count($folders)) {
$folders[count($folders) - 1]['last'] = true;
}
return [$categories, $folders];
}
/**
* return number of bytes in size (K, M, G)
* @param string $size_str
* @return int
*/
function return_bytes($size_str) {
switch (substr ($size_str, -1)) {
case 'M': case 'm': return (int)$size_str * 1048576;
case 'K': case 'k': return (int)$size_str * 1024;
case 'G': case 'g': return (int)$size_str * 1073741824;
default: return $size_str;
}
}
function bb_translate_video($s) {
$matches = null;
$r = preg_match_all("/\[video\](.*?)\[\/video\]/ism",$s,$matches,PREG_SET_ORDER);
if ($r) {
foreach ($matches as $mtch) {
if ((stristr($mtch[1], 'youtube')) || (stristr($mtch[1], 'youtu.be'))) {
$s = str_replace($mtch[0], '[youtube]' . $mtch[1] . '[/youtube]', $s);
} elseif (stristr($mtch[1], 'vimeo')) {
$s = str_replace($mtch[0], '[vimeo]' . $mtch[1] . '[/vimeo]', $s);
}
}
}
return $s;
}
function undo_post_tagging($s) {
$matches = null;
$cnt = preg_match_all('/([!#@])\[url=(.*?)\](.*?)\[\/url\]/ism', $s, $matches, PREG_SET_ORDER);
if ($cnt) {
foreach ($matches as $mtch) {
if (in_array($mtch[1], ['!', '@'])) {
$contact = Contact::getDetailsByURL($mtch[2]);
$mtch[3] = empty($contact['addr']) ? $mtch[2] : $contact['addr'];
}
$s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s);
}
}
return $s;
}
/// @TODO Rewrite this
function is_a_date_arg($s) {
$i = intval($s);
if ($i > 1900) {
$y = date('Y');
if ($i <= $y + 1 && strpos($s, '-') == 4) {
$m = intval(substr($s, 5));
if ($m > 0 && $m <= 12) {
return true;
}
}
}
return false;
}

18
mod/editpost.php

@ -8,9 +8,10 @@ use Friendica\Content\Feature;
use Friendica\Core\Hook;
use Friendica\Core\L10n;
use Friendica\Core\Renderer;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\FileTag;
use Friendica\Model\Item;
use Friendica\Database\DBA;
use Friendica\Util\Crypto;
function editpost_content(App $a)
@ -118,3 +119,18 @@ function editpost_content(App $a)
return $o;
}
function undo_post_tagging($s) {
$matches = null;
$cnt = preg_match_all('/([!#@])\[url=(.*?)\](.*?)\[\/url\]/ism', $s, $matches, PREG_SET_ORDER);
if ($cnt) {
foreach ($matches as $mtch) {
if (in_array($mtch[1], ['!', '@'])) {
$contact = Contact::getDetailsByURL($mtch[2]);
$mtch[3] = empty($contact['addr']) ? $mtch[2] : $contact['addr'];
}
$s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s);
}
}
return $s;
}

14
mod/events.php

@ -5,6 +5,7 @@
*/
use Friendica\App;
use Friendica\BaseObject;
use Friendica\Content\Nav;
use Friendica\Content\Widget\CalendarExport;
use Friendica\Core\ACL;
@ -18,6 +19,7 @@ use Friendica\Model\Event;
use Friendica\Model\Item;
use Friendica\Model\Profile;
use Friendica\Module\Login;
use Friendica\Util\ACLFormatter;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
use Friendica\Util\Temporal;
@ -146,10 +148,14 @@ function events_post(App $a)
if ($share) {
$str_group_allow = perms2str($_POST['group_allow'] ?? '');
$str_contact_allow = perms2str($_POST['contact_allow'] ?? '');
$str_group_deny = perms2str($_POST['group_deny'] ?? '');
$str_contact_deny = perms2str($_POST['contact_deny'] ?? '');
/** @var ACLFormatter $aclFormatter */
$aclFormatter = BaseObject::getClass(ACLFormatter::class);
$str_group_allow = $aclFormatter->toString($_POST['group_allow'] ?? '');
$str_contact_allow = $aclFormatter->toString($_POST['contact_allow'] ?? '');
$str_group_deny = $aclFormatter->toString($_POST['group_deny'] ?? '');
$str_contact_deny = $aclFormatter->toString($_POST['contact_deny'] ?? '');
// Undo the pseudo-contact of self, since there are real contacts now
if (strpos($str_contact_allow, '<' . $self . '>') !== false) {

21
mod/item.php

@ -16,6 +16,7 @@
*/
use Friendica\App;
use Friendica\BaseObject;
use Friendica\Content\Pager;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
@ -24,8 +25,8 @@ use Friendica\Core\Hook;
use Friendica\Core\L10n;
use Friendica\Core\Logger;
use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Core\Session;
use Friendica\Core\System;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
use Friendica\Model\Attach;
@ -37,6 +38,7 @@ use Friendica\Model\Photo;
use Friendica\Model\Term;
use Friendica\Protocol\Diaspora;
use Friendica\Protocol\Email;
use Friendica\Util\ACLFormatter;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Emailer;
use Friendica\Util\Security;
@ -269,10 +271,14 @@ function item_post(App $a) {
$str_contact_deny = $user['deny_cid'];
} else {
// use the posted permissions
$str_group_allow = perms2str($_REQUEST['group_allow'] ?? '');
$str_contact_allow = perms2str($_REQUEST['contact_allow'] ?? '');
$str_group_deny = perms2str($_REQUEST['group_deny'] ?? '');
$str_contact_deny = perms2str($_REQUEST['contact_deny'] ?? '');
/** @var ACLFormatter $aclFormatter */
$aclFormatter = BaseObject::getClass(ACLFormatter::class);
$str_group_allow = $aclFormatter->toString($_REQUEST['group_allow'] ?? '');
$str_contact_allow = $aclFormatter->toString($_REQUEST['contact_allow'] ?? '');
$str_group_deny = $aclFormatter->toString($_REQUEST['group_deny'] ?? '');
$str_contact_deny = $aclFormatter->toString($_REQUEST['contact_deny'] ?? '');
}
$title = Strings::escapeTags(trim($_REQUEST['title'] ?? ''));
@ -499,8 +505,9 @@ function item_post(App $a) {
$objecttype = ACTIVITY_OBJ_BOOKMARK;
}
$body = bb_translate_video($body);
/** @var BBCode\Video $bbCodeVideo */
$bbCodeVideo = BaseObject::getClass(BBCode\Video::class);
$body = $bbCodeVideo->transform($body);
// Fold multi-line [code] sequences
$body = preg_replace('/\[\/code\]\s*\[code\]/ism', "\n", $body);

13
mod/lockview.php

@ -3,11 +3,13 @@
* @file mod/lockview.php
*/
use Friendica\App;
use Friendica\BaseObject;
use Friendica\Core\Hook;
use Friendica\Core\L10n;
use Friendica\Database\DBA;
use Friendica\Model\Group;
use Friendica\Model\Item;
use Friendica\Util\ACLFormatter;
function lockview_content(App $a)
{
@ -59,10 +61,13 @@ function lockview_content(App $a)
exit();
}
$allowed_users = expand_acl($item['allow_cid']);
$allowed_groups = expand_acl($item['allow_gid']);
$deny_users = expand_acl($item['deny_cid']);
$deny_groups = expand_acl($item['deny_gid']);
/** @var ACLFormatter $aclFormatter */
$aclFormatter = BaseObject::getClass(ACLFormatter::class);
$allowed_users = $aclFormatter->expand($item['allow_cid']);
$allowed_groups = $aclFormatter->expand($item['allow_gid']);
$deny_users = $aclFormatter->expand($item['deny_cid']);
$deny_groups = $aclFormatter->expand($item['deny_gid']);
$o = L10n::t('Visible to:') . '<br />';
$l = [];

11
mod/network.php

@ -5,6 +5,7 @@
*/
use Friendica\App;
use Friendica\BaseObject;
use Friendica\Content\Feature;
use Friendica\Content\ForumManager;
use Friendica\Content\Nav;
@ -51,9 +52,12 @@ function network_init(App $a)
$group_id = 0;
}
/** @var DateTimeFormat $dtFormat */
$dtFormat = BaseObject::getClass(DateTimeFormat::class);
if ($a->argc > 1) {
for ($x = 1; $x < $a->argc; $x ++) {
if (is_a_date_arg($a->argv[$x])) {
if ($dtFormat->isYearMonth($a->argv[$x])) {
$is_a_date_query = true;
break;
}
@ -461,9 +465,12 @@ function networkThreadedView(App $a, $update, $parent)
$default_permissions = [];
/** @var DateTimeFormat $dtFormat */
$dtFormat = BaseObject::getClass(DateTimeFormat::class);
if ($a->argc > 1) {
for ($x = 1; $x < $a->argc; $x ++) {
if (is_a_date_arg($a->argv[$x])) {
if ($dtFormat->isYearMonth($a->argv[$x])) {
if ($datequery) {
$datequery2 = Strings::escapeHtml($a->argv[$x]);
} else {

35
mod/photos.php

@ -4,6 +4,7 @@
*/
use Friendica\App;
use Friendica\BaseObject;
use Friendica\Content\Feature;
use Friendica\Content\Nav;
use Friendica\Content\Pager;
@ -14,18 +15,17 @@ use Friendica\Core\Hook;
use Friendica\Core\L10n;
use Friendica\Core\Logger;
use Friendica\Core\Renderer;
use Friendica\Core\System;
use Friendica\Core\Session;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\Group;
use Friendica\Model\Item;
use Friendica\Model\Photo;
use Friendica\Model\Profile;
use Friendica\Model\User;
use Friendica\Network\Probe;
use Friendica\Object\Image;
use Friendica\Protocol\DFRN;
use Friendica\Util\ACLFormatter;
use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Map;
@ -296,10 +296,13 @@ function photos_post(App $a)
$albname = !empty($_POST['albname']) ? Strings::escapeTags(trim($_POST['albname'])) : '';
$origaname = !empty($_POST['origaname']) ? Strings::escapeTags(trim($_POST['origaname'])) : '';
$str_group_allow = !empty($_POST['group_allow']) ? perms2str($_POST['group_allow']) : '';
$str_contact_allow = !empty($_POST['contact_allow']) ? perms2str($_POST['contact_allow']) : '';
$str_group_deny = !empty($_POST['group_deny']) ? perms2str($_POST['group_deny']) : '';
$str_contact_deny = !empty($_POST['contact_deny']) ? perms2str($_POST['contact_deny']) : '';
/** @var ACLFormatter $aclFormatter */
$aclFormatter = BaseObject::getClass(ACLFormatter::class);
$str_group_allow = !empty($_POST['group_allow']) ? $aclFormatter->toString($_POST['group_allow']) : '';
$str_contact_allow = !empty($_POST['contact_allow']) ? $aclFormatter->toString($_POST['contact_allow']) : '';
$str_group_deny = !empty($_POST['group_deny']) ? $aclFormatter->toString($_POST['group_deny']) : '';
$str_contact_deny = !empty($_POST['contact_deny']) ? $aclFormatter->toString($_POST['contact_deny']) : '';
$resource_id = $a->argv[3];
@ -635,10 +638,13 @@ function photos_post(App $a)
$group_deny = $_REQUEST['group_deny'] ?? [];
$contact_deny = $_REQUEST['contact_deny'] ?? [];
$str_group_allow = perms2str(is_array($group_allow) ? $group_allow : explode(',', $group_allow));
$str_contact_allow = perms2str(is_array($contact_allow) ? $contact_allow : explode(',', $contact_allow));
$str_group_deny = perms2str(is_array($group_deny) ? $group_deny : explode(',', $group_deny));
$str_contact_deny = perms2str(is_array($contact_deny) ? $contact_deny : explode(',', $contact_deny));
/** @var ACLFormatter $aclFormatter */
$aclFormatter = BaseObject::getClass(ACLFormatter::class);
$str_group_allow = $aclFormatter->toString(is_array($group_allow) ? $group_allow : explode(',', $group_allow));
$str_contact_allow = $aclFormatter->toString(is_array($contact_allow) ? $contact_allow : explode(',', $contact_allow));
$str_group_deny = $aclFormatter->toString(is_array($group_deny) ? $group_deny : explode(',', $group_deny));
$str_contact_deny = $aclFormatter->toString(is_array($contact_deny) ? $contact_deny : explode(',', $contact_deny));
$ret = ['src' => '', 'filename' => '', 'filesize' => 0, 'type' => ''];
@ -1438,7 +1444,12 @@ function photos_content(App $a)
$template = $tpl;
$sparkle = '';
if ((activity_match($item['verb'], ACTIVITY_LIKE) || activity_match($item['verb'], ACTIVITY_DISLIKE)) && ($item['id'] != $item['parent'])) {
/** @var \Friendica\Protocol\Activity $activity */
$activity = BaseObject::getClass(\Friendica\Protocol\Activity::class);
if (($activity->match($item['verb'], ACTIVITY_LIKE) ||
$activity->match($item['verb'], ACTIVITY_DISLIKE)) &&
($item['id'] != $item['parent'])) {
continue;
}

13
mod/settings.php

@ -5,6 +5,7 @@
use Friendica\App;
use Friendica\BaseModule;
use Friendica\BaseObject;
use Friendica\Content\Feature;
use Friendica\Content\Nav;
use Friendica\Core\ACL;
@ -25,6 +26,7 @@ use Friendica\Model\Group;
use Friendica\Model\User;
use Friendica\Module\Login;
use Friendica\Protocol\Email;
use Friendica\Util\ACLFormatter;
use Friendica\Util\Network;
use Friendica\Util\Strings;
use Friendica\Util\Temporal;
@ -533,10 +535,13 @@ function settings_post(App $a)
date_default_timezone_set($timezone);
}
$str_group_allow = !empty($_POST['group_allow']) ? perms2str($_POST['group_allow']) : '';
$str_contact_allow = !empty($_POST['contact_allow']) ? perms2str($_POST['contact_allow']) : '';
$str_group_deny = !empty($_POST['group_deny']) ? perms2str($_POST['group_deny']) : '';
$str_contact_deny = !empty($_POST['contact_deny']) ? perms2str($_POST['contact_deny']) : '';
/** @var ACLFormatter $aclFormatter */
$aclFormatter = BaseObject::getClass(ACLFormatter::class);
$str_group_allow = !empty($_POST['group_allow']) ? $aclFormatter->toString($_POST['group_allow']) : '';
$str_contact_allow = !empty($_POST['contact_allow']) ? $aclFormatter->toString($_POST['contact_allow']) : '';
$str_group_deny = !empty($_POST['group_deny']) ? $aclFormatter->toString($_POST['group_deny']) : '';
$str_contact_deny = !empty($_POST['contact_deny']) ? $aclFormatter->toString($_POST['contact_deny']) : '';
$openidserver = $a->user['openidserver'];
//$openid = Strings::normaliseOpenID($openid);

2
src/BaseObject.php

@ -54,7 +54,7 @@ class BaseObject
*
* @throws InternalServerErrorException
*/
protected static function getClass(string $name)
public static function getClass(string $name)
{
if (empty(self::$dice)) {
throw new InternalServerErrorException('DICE isn\'t initialized.');

79
src/Content/Item.php

@ -0,0 +1,79 @@
<?php
namespace Friendica\Content;
use Friendica\Model\FileTag;
/**
* A content helper class for displaying items
*/
final class Item
{
/**
* Return array with details for categories and folders for an item
*
* @param array $item
* @return [array, array]
*
* [
* [ // categories array
* {
* 'name': 'category name',
* 'removeurl': 'url to remove this category',
* 'first': 'is the first in this array? true/false',
* 'last': 'is the last in this array? true/false',
* } ,
* ....
* ],
* [ //folders array
* {
* 'name': 'folder name',
* 'removeurl': 'url to remove this folder',
* 'first': 'is the first in this array? true/false',
* 'last': 'is the last in this array? true/false',
* } ,
* ....
* ]
* ]
*/
public function determineCategoriesTerms(array $item)
{
$categories = [];
$folders = [];
$first = true;
foreach (FileTag::fileToArray($item['file'] ?? '', 'category') as $savedFolderName) {
$categories[] = [
'name' => $savedFolderName,
'url' => "#",
'removeurl' => ((local_user() == $item['uid']) ? 'filerm/' . $item['id'] . '?f=&cat=' . rawurlencode($savedFolderName) : ""),
'first' => $first,
'last' => false
];
$first = false;
}
if (count($categories)) {
$categories[count($categories) - 1]['last'] = true;
}
if (local_user() == $item['uid']) {
foreach (FileTag::fileToArray($item['file'] ?? '') as $savedFolderName) {
$folders[] = [
'name' => $savedFolderName,
'url' => "#",
'removeurl' => ((local_user() == $item['uid']) ? 'filerm/' . $item['id'] . '?f=&term=' . rawurlencode($savedFolderName) : ""),
'first' => $first,
'last' => false
];
$first = false;
}
}
if (count($folders)) {
$folders[count($folders) - 1]['last'] = true;
}
return [$categories, $folders];
}
}

32
src/Content/Text/BBCode/Video.php

@ -0,0 +1,32 @@
<?php
namespace Friendica\Content\Text\BBCode;
/**
* Video specific BBCode util class
*/
final class Video
{
/**
* Transforms video BBCode tagged links to youtube/vimeo tagged links
*
* @param string $bbCodeString The input BBCode styled string
*
* @return string The transformed text
*/
public function transform(string $bbCodeString)
{
$matches = null;
$found = preg_match_all("/\[video\](.*?)\[\/video\]/ism",$bbCodeString,$matches,PREG_SET_ORDER);
if ($found) {
foreach ($matches as $match) {
if ((stristr($match[1], 'youtube')) || (stristr($match[1], 'youtu.be'))) {
$bbCodeString = str_replace($match[0], '[youtube]' . $match[1] . '[/youtube]', $bbCodeString);
} elseif (stristr($match[1], 'vimeo')) {
$bbCodeString = str_replace($match[0], '[vimeo]' . $match[1] . '[/vimeo]', $bbCodeString);
}
}
}
return $bbCodeString;
}
}

6
src/Model/Event.php

@ -911,7 +911,7 @@ class Event extends BaseObject
$tpl = Renderer::getMarkupTemplate('event_stream_item.tpl');
$return = Renderer::replaceMacros($tpl, [
'$id' => $item['event-id'],
'$title' => prepare_text($item['event-summary']),
'$title' => BBCode::convert($item['event-summary']),
'$dtstart_label' => L10n::t('Starts:'),
'$dtstart_title' => $dtstart_title,
'$dtstart_dt' => $dtstart_dt,
@ -929,7 +929,7 @@ class Event extends BaseObject
'$author_name' => $item['author-name'],
'$author_link' => $profile_link,
'$author_avatar' => $item['author-avatar'],
'$description' => prepare_text($item['event-desc']),
'$description' => BBCode::convert($item['event-desc']),
'$location_label' => L10n::t('Location:'),
'$show_map_label' => L10n::t('Show map'),
'$hide_map_label' => L10n::t('Hide map'),
@ -979,7 +979,7 @@ class Event extends BaseObject
}
}
$location['name'] = prepare_text($location['name']);
$location['name'] = BBCode::convert($location['name']);
// Construct the map HTML.
if (isset($location['address'])) {

52
src/Model/Item.php

@ -17,13 +17,15 @@ use Friendica\Core\Logger;
use Friendica\Core\PConfig;
use Friendica\Core\Protocol;
use Friendica\Core\Renderer;
use Friendica\Core\System;
use Friendica\Core\Session;
use Friendica\Core\System;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
use Friendica\Protocol\Activity;
use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\Diaspora;
use Friendica\Protocol\OStatus;
use Friendica\Util\ACLFormatter;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Map;
use Friendica\Util\Network;
@ -1357,13 +1359,16 @@ class Item extends BaseObject
$item['parent-uri'] = $item['thr-parent'];
}
/** @var Activity $activity */
$activity = self::getClass(Activity::class);
if (isset($item['gravity'])) {
$item['gravity'] = intval($item['gravity']);
} elseif ($item['parent-uri'] === $item['uri']) {
$item['gravity'] = GRAVITY_PARENT;
} elseif (activity_match($item['verb'], ACTIVITY_POST)) {
} elseif ($activity->match($item['verb'], ACTIVITY_POST)) {
$item['gravity'] = GRAVITY_COMMENT;
} elseif (activity_match($item['verb'], ACTIVITY_FOLLOW)) {
} elseif ($activity->match($item['verb'], ACTIVITY_FOLLOW)) {
$item['gravity'] = GRAVITY_ACTIVITY;
} else {
$item['gravity'] = GRAVITY_UNKNOWN; // Should not happen
@ -2892,10 +2897,13 @@ class Item extends BaseObject
*/
public static function enumeratePermissions(array $obj, bool $check_dead = false)
{
$allow_people = expand_acl($obj['allow_cid']);
$allow_groups = Group::expand($obj['uid'], expand_acl($obj['allow_gid']), $check_dead);
$deny_people = expand_acl($obj['deny_cid']);
$deny_groups = Group::expand($obj['uid'], expand_acl($obj['deny_gid']), $check_dead);
/** @var ACLFormatter $aclFormater */
$aclFormater = self::getClass(ACLFormatter::class);
$allow_people = $aclFormater->expand($obj['allow_cid']);
$allow_groups = Group::expand($obj['uid'], $aclFormater->expand($obj['allow_gid']), $check_dead);
$deny_people = $aclFormater->expand($obj['deny_cid']);
$deny_groups = Group::expand($obj['uid'], $aclFormater->expand($obj['deny_gid']), $check_dead);
$recipients = array_unique(array_merge($allow_people, $allow_groups));
$deny = array_unique(array_merge($deny_people, $deny_groups));
$recipients = array_diff($recipients, $deny);
@ -3342,10 +3350,9 @@ class Item extends BaseObject
|| $rendered_hash != hash("md5", $item["body"])
|| Config::get("system", "ignore_cache")
) {
$a = self::getApp();
redir_private_images($a, $item);
self::addRedirToImageTags($item);
$item["rendered-html"] = prepare_text($item["body"]);
$item["rendered-html"] = BBCode::convert($item["body"]);
$item["rendered-hash"] = hash("md5", $item["body"]);
$hook_data = ['item' => $item, 'rendered-html' => $item['rendered-html'], 'rendered-hash' => $item['rendered-hash']];
@ -3378,6 +3385,31 @@ class Item extends BaseObject
$item["body"] = $body;
}
/**
* @brief Find any non-embedded images in private items and add redir links to them
*
* @param array &$item The field array of an item row
*/
private static function addRedirToImageTags(array &$item)
{
$app = self::getApp();
$matches = [];
$cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER);
if ($cnt) {
foreach ($matches as $mtch) {
if (strpos($mtch[1], '/redir') !== false) {
continue;
}
if ((local_user() == $item['uid']) && ($item['private'] == 1) && ($item['contact-id'] != $app->contact['id']) && ($item['network'] == Protocol::DFRN)) {
$img_url = 'redir/' . $item['contact-id'] . '?url=' . urlencode($mtch[1]);
$item['body'] = str_replace($mtch[0], '[img]' . $img_url . '[/img]', $item['body']);
}
}
}
}
/**
* @brief Given an item array, convert the body element from bbcode to html and add smilie icons.
* If attach is true, also add icons for item attachments.

24
src/Model/Profile.php

@ -823,51 +823,51 @@ class Profile
$profile['religion'] = [L10n::t('Religion:'), $a->profile['religion']];
}
if ($txt = prepare_text($a->profile['about'])) {
if ($txt = BBCode::convert($a->profile['about'])) {
$profile['about'] = [L10n::t('About:'), $txt];
}
if ($txt = prepare_text($a->profile['interest'])) {
if ($txt = BBCode::convert($a->profile['interest'])) {
$profile['interest'] = [L10n::t('Hobbies/Interests:'), $txt];
}
if ($txt = prepare_text($a->profile['likes'])) {
if ($txt = BBCode::convert($a->profile['likes'])) {
$profile['likes'] = [L10n::t('Likes:'), $txt];
}
if ($txt = prepare_text($a->profile['dislikes'])) {
if ($txt = BBCode::convert($a->profile['dislikes'])) {
$profile['dislikes'] = [L10n::t('Dislikes:'), $txt];
}
if ($txt = prepare_text($a->profile['contact'])) {
if ($txt = BBCode::convert($a->profile['contact'])) {
$profile['contact'] = [L10n::t('Contact information and Social Networks:'), $txt];
}
if ($txt = prepare_text($a->profile['music'])) {
if ($txt = BBCode::convert($a->profile['music'])) {
$profile['music'] = [L10n::t('Musical interests:'), $txt];
}
if ($txt = prepare_text($a->profile['book'])) {
if ($txt = BBCode::convert($a->profile['book'])) {
$profile['book'] = [L10n::t('Books, literature:'), $txt];
}
if ($txt = prepare_text($a->profile['tv'])) {
if ($txt = BBCode::convert($a->profile['tv'])) {
$profile['tv'] = [L10n::t('Television:'), $txt];
}
if ($txt = prepare_text($a->profile['film'])) {
if ($txt = BBCode::convert($a->profile['film'])) {
$profile['film'] = [L10n::t('Film/dance/culture/entertainment:'), $txt];
}
if ($txt = prepare_text($a->profile['romance'])) {
if ($txt = BBCode::convert($a->profile['romance'])) {
$profile['romance'] = [L10n::t('Love/Romance:'), $txt];
}
if ($txt = prepare_text($a->profile['work'])) {
if ($txt = BBCode::convert($a->profile['work'])) {
$profile['work'] = [L10n::t('Work/employment:'), $txt];
}
if ($txt = prepare_text($a->profile['education'])) {
if ($txt = BBCode::convert($a->profile['education'])) {
$profile['education'] = [L10n::t('School/education:'), $txt];
}

12
src/Module/Item/Compose.php

@ -16,6 +16,7 @@ use Friendica\Model\Item;
use Friendica\Model\User;
use Friendica\Module\Login;
use Friendica\Network\HTTPException\NotImplementedException;
use Friendica\Util\ACLFormatter;
use Friendica\Util\Crypto;
class Compose extends BaseModule
@ -58,6 +59,9 @@ class Compose extends BaseModule
$user = User::getById(local_user(), ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'hidewall', 'default-location']);
/** @var ACLFormatter $aclFormatter */
$aclFormatter = self::getClass(ACLFormatter::class);
switch ($posttype) {
case Item::PT_PERSONAL_NOTE:
$compose_title = L10n::t('Compose new personal note');
@ -70,8 +74,8 @@ class Compose extends BaseModule
$compose_title = L10n::t('Compose new post');
$type = 'post';
$doesFederate = true;
$contact_allow = implode(',', expand_acl($user['allow_cid']));
$group_allow = implode(',', expand_acl($user['allow_gid'])) ?: Group::FOLLOWERS;
$contact_allow = implode(',', $aclFormatter->expand($user['allow_cid']));
$group_allow = implode(',', $aclFormatter->expand($user['allow_gid'])) ?: Group::FOLLOWERS;
break;
}
@ -82,8 +86,8 @@ class Compose extends BaseModule
$wall = $_REQUEST['wall'] ?? $type == 'post';
$contact_allow = $_REQUEST['contact_allow'] ?? $contact_allow;
$group_allow = $_REQUEST['group_allow'] ?? $group_allow;
$contact_deny = $_REQUEST['contact_deny'] ?? implode(',', expand_acl($user['deny_cid']));
$group_deny = $_REQUEST['group_deny'] ?? implode(',', expand_acl($user['deny_gid']));
$contact_deny = $_REQUEST['contact_deny'] ?? implode(',', $aclFormatter->expand($user['deny_cid']));
$group_deny = $_REQUEST['group_deny'] ?? implode(',', $aclFormatter->expand($user['deny_gid']));
$visibility = ($contact_allow . $user['allow_gid'] . $user['deny_cid'] . $user['deny_gid']) ? 'custom' : 'public';
$acl_contacts = Contact::selectToArray(['id', 'name', 'addr', 'micro'], ['uid' => local_user(), 'pending' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]]);

5
src/Module/Profile.php

@ -131,9 +131,12 @@ class Profile extends BaseModule
$category = $datequery = $datequery2 = '';
/** @var DateTimeFormat $dtFormat */
$dtFormat = self::getClass(DateTimeFormat::class);
if ($a->argc > 2) {
for ($x = 2; $x < $a->argc; $x ++) {
if (is_a_date_arg($a->argv[$x])) {
if ($dtFormat->isYearMonth($a->argv[$x])) {
if ($datequery) {
$datequery2 = Strings::escapeHtml($a->argv[$x]);
} else {

14
src/Object/Post.php

@ -7,6 +7,7 @@ namespace Friendica\Object;
use Friendica\BaseObject;
use Friendica\Content\ContactSelector;
use Friendica\Content\Feature;
use Friendica\Content\Item as ContentItem;
use Friendica\Core\Addon;
use Friendica\Core\Config;
use Friendica\Core\Hook;
@ -21,6 +22,7 @@ use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Model\Term;
use Friendica\Model\User;
use Friendica\Protocol\Activity;
use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Proxy as ProxyUtils;
@ -323,7 +325,10 @@ class Post extends BaseObject
$body = Item::prepareBody($item, true);
list($categories, $folders) = get_cats_and_terms($item);
/** @var ContentItem $contItem */
$contItem = self::getClass(ContentItem::class);
list($categories, $folders) = $contItem->determineCategoriesTerms($item);
$body_e = $body;
$text_e = strip_tags($body);
@ -517,12 +522,17 @@ class Post extends BaseObject
Logger::log('[WARN] Post::addChild : Item already exists (' . $item->getId() . ').', Logger::DEBUG);
return false;
}
/** @var Activity $activity */
$activity = self::getClass(Activity::class);
/*
* Only add what will be displayed
*/
if ($item->getDataValue('network') === Protocol::MAIL && local_user() != $item->getDataValue('uid')) {
return false;
} elseif (activity_match($item->getDataValue('verb'), ACTIVITY_LIKE) || activity_match($item->getDataValue('verb'), ACTIVITY_DISLIKE)) {
} elseif ($activity->match($item->getDataValue('verb'), ACTIVITY_LIKE) ||
$activity->match($item->getDataValue('verb'), ACTIVITY_DISLIKE)) {
return false;
}

23
src/Protocol/Activity.php

@ -0,0 +1,23 @@
<?php
namespace Friendica\Protocol;
/**
* Base class for the Activity namespace
*/
final class Activity
{
/**
* Compare activity uri. Knows about activity namespace.
*
* @param string $haystack
* @param string $needle
*
* @return boolean
*/
public function match(string $haystack, string $needle) {
return (($haystack === $needle) ||
((basename($needle) === $haystack) &&
strstr($needle, NAMESPACE_ACTIVITY_SCHEMA)));
}
}

12
src/Protocol/DFRN.php

@ -12,6 +12,7 @@ use DOMDocument;
use DOMXPath;
use Friendica\App;
use Friendica\App\BaseURL;
use Friendica\BaseObject;
use Friendica\Content\OEmbed;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
@ -2180,24 +2181,27 @@ class DFRN
// The functions below are partly used by ostatus.php as well - where we have this variable
$contact = Contact::selectFirst([], ['id' => $importer['id']]);
/** @var Activity $activity */
$activity = BaseObject::getClass(Activity::class);
// Big question: Do we need these functions? They were part of the "consume_feed" function.
// This function once was responsible for DFRN and OStatus.
if (activity_match($item["verb"], ACTIVITY_FOLLOW)) {
if ($activity->match($item["verb"], ACTIVITY_FOLLOW)) {
Logger::log("New follower");
Contact::addRelationship($importer, $contact, $item);
return false;
}
if (activity_match($item["verb"], ACTIVITY_UNFOLLOW)) {
if ($activity->match($item["verb"], ACTIVITY_UNFOLLOW)) {
Logger::log("Lost follower");
Contact::removeFollower($importer, $contact, $item);
return false;
}
if (activity_match($item["verb"], ACTIVITY_REQ_FRIEND)) {
if ($activity->match($item["verb"], ACTIVITY_REQ_FRIEND)) {
Logger::log("New friend request");
Contact::addRelationship($importer, $contact, $item, true);
return false;
}
if (activity_match($item["verb"], ACTIVITY_UNFRIEND)) {
if ($activity->match($item["verb"], ACTIVITY_UNFRIEND)) {
Logger::log("Lost sharer");
Contact::removeSharer($importer, $contact, $item);
return false;

67
src/Util/ACLFormatter.php

@ -0,0 +1,67 @@
<?php
namespace Friendica\Util;
use Friendica\Model\Group;
/**
* Util class for ACL formatting
*/
final class ACLFormatter
{
/**
* Turn user/group ACLs stored as angle bracketed text into arrays
*
* @param string $ids A angle-bracketed list of IDs
*
* @return array The array based on the IDs
*/
public function expand(string $ids)
{
// turn string array of angle-bracketed elements into numeric array
// e.g. "<1><2><3>" => array(1,2,3);
preg_match_all('/<(' . Group::FOLLOWERS . '|'. Group::MUTUALS . '|[0-9]+)>/', $ids, $matches, PREG_PATTERN_ORDER);
return $matches[1];
}
/**
* Wrap ACL elements in angle brackets for storage
*
* @param string $item The item to sanitise
*/
private function sanitize(string &$item) {
if (intval($item)) {
$item = '<' . intval(Strings::escapeTags(trim($item))) . '>';
} elseif (in_array($item, [Group::FOLLOWERS, Group::MUTUALS])) {
$item = '<' . $item . '>';
} else {
$item = '';
}
}
/**
* Convert an ACL array to a storable string
*
* Normally ACL permissions will be an array.
* We'll also allow a comma-separated string.
*
* @param string|array $permissions
*
* @return string
*/
function toString($permissions) {
$return = '';
if (is_array($permissions)) {
$item = $permissions;
} else {
$item = explode(',', $permissions);
}
if (is_array($item)) {
array_walk($item, [$this, 'sanitize']);
$return = implode('', $item);
}
return $return;
}
}

33
src/Util/DateTimeFormat.php

@ -148,4 +148,37 @@ class DateTimeFormat
return $d->format($format);
}
/**
* Checks, if the given string is a date with the pattern YYYY-MM
*
* @param string $dateString The given date
*
* @return boolean True, if the date is a valid pattern
*/
public function isYearMonth(string $dateString)
{
// Check format (2019-01, 2019-1, 2019-10)
if (!preg_match('/^([12]\d{3}-(1[0-2]|0[1-9]|\d))$/', $dateString)) {
return false;
}
$date = DateTime::createFromFormat('Y-m', $dateString);
if (!$date) {
return false;
}
try {
$now = new DateTime();
} catch (\Throwable $t) {
return false;
}
if ($date > $now) {
return false;
}
return true;
}
}

12
src/Worker/Notifier.php

@ -24,6 +24,7 @@ use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\Diaspora;
use Friendica\Protocol\OStatus;
use Friendica\Protocol\Salmon;
use Friendica\Util\ACLFormatter;
require_once 'include/items.php';
@ -272,10 +273,13 @@ class Notifier
$public_message = false; // private recipients, not public
}
$allow_people = expand_acl($parent['allow_cid']);
$allow_groups = Group::expand($uid, expand_acl($parent['allow_gid']),true);
$deny_people = expand_acl($parent['deny_cid']);
$deny_groups = Group::expand($uid, expand_acl($parent['deny_gid']));
/** @var ACLFormatter $aclFormatter */
$aclFormatter = BaseObject::getClass(ACLFormatter::class);
$allow_people = $aclFormatter->expand($parent['allow_cid']);
$allow_groups = Group::expand($uid, $aclFormatter->expand($parent['allow_gid']),true);
$deny_people = $aclFormatter->expand($parent['deny_cid']);
$deny_groups = Group::expand($uid, $aclFormatter->expand($parent['deny_gid']));
// if our parent is a public forum (forum_mode == 1), uplink to the origional author causing
// a delivery fork. private groups (forum_mode == 2) do not uplink

13
tests/src/Content/ItemTest.php

@ -0,0 +1,13 @@
<?php
namespace Friendica\Test\src\Content;
use Friendica\Test\MockedTest;
class ItemTest extends MockedTest
{
public function testDetermineCategoriesTerms()
{
$this->markTestIncomplete('Test data needed.');
}
}

43
tests/src/Content/Text/BBCode/VideoTest.php

@ -0,0 +1,43 @@
<?php
namespace Friendica\Test\Content\Text\BBCode;
use Friendica\Content\Text\BBCode\Video;
use Friendica\Test\MockedTest;
class VideoTest extends MockedTest
{
public function dataVideo()
{
return [
'youtube' => [
'input' => '[video]https://youtube.link/4523[/video]',
'assert' => '[youtube]https://youtube.link/4523[/youtube]',
],
'youtu.be' => [
'input' => '[video]https://youtu.be.link/4523[/video]',
'assert' => '[youtube]https://youtu.be.link/4523[/youtube]',
],
'vimeo' => [
'input' => '[video]https://vimeo.link/2343[/video]',
'assert' => '[vimeo]https://vimeo.link/2343[/vimeo]',
],
'mixed' => [
'input' => '[video]https://vimeo.link/2343[/video] With other [b]string[/b] [video]https://youtu.be/blaa[/video]',
'assert' => '[vimeo]https://vimeo.link/2343[/vimeo] With other [b]string[/b] [youtube]https://youtu.be/blaa[/youtube]',
]
];
}
/**
* Test if the BBCode is successfully transformed for video links
*
* @dataProvider dataVideo
*/
public function testTransform(string $input, string $assert)
{
$bbCodeVideo = new Video();
$this->assertEquals($assert, $bbCodeVideo->transform($input));
}
}

3
tests/src/Core/InstallerTest.php

@ -339,9 +339,6 @@ class InstallerTest extends MockedTest
// Mocking that we can use CURL
$this->setFunctions(['curl_init' => true]);
// needed because of "normalise_link"
require_once __DIR__ . '/../../../include/text.php';
$install = new Installer();
$this->assertTrue($install->checkHtAccess('https://test'));

57
tests/src/Protocol/ActivityTest.php

@ -0,0 +1,57 @@
<?php
namespace Friendica\Test\Protocol;
use Friendica\Protocol\Activity;
use Friendica\Test\MockedTest;
class ActivityTest extends MockedTest
{
public function dataMatch()
{
return [
'empty' => [
'haystack' => '',
'needle' => '',
'assert' => true,
],
'simple' => [
'haystack' => ACTIVITY_OBJ_TAGTERM,
'needle' => ACTIVITY_OBJ_TAGTERM,
'assert' => true,
],
'withNamespace' => [
'haystack' => 'tagterm',
'needle' => NAMESPACE_ACTIVITY_SCHEMA . ACTIVITY_OBJ_TAGTERM,
'assert' => true,
],
'invalidSimple' => [
'haystack' => 'tagterm',
'needle' => '',
'assert' => false,
],
'invalidWithOutNamespace' => [
'haystack' => 'tagterm',
'needle' => ACTIVITY_OBJ_TAGTERM,
'assert' => false,
],
'withSubPath' => [
'haystack' => 'tagterm',
'needle' => NAMESPACE_ACTIVITY_SCHEMA . '/bla/' . ACTIVITY_OBJ_TAGTERM,
'assert' => true,
],
];
}
/**
* Test the different, possible matchings
*
* @dataProvider dataMatch
*/
public function testMatch(string $haystack, string $needle, bool $assert)
{
$activity = new Activity();
$this->assertEquals($assert, $activity->match($haystack, $needle));
}
}

133
tests/include/TextTest.php → tests/src/Util/ACLFormaterTest.php

@ -1,63 +1,25 @@
<?php
/**
* TextTest class.
*/
namespace Friendica\Test;
namespace Friendica\Test\src\Util;
use Friendica\Model\Group;
use Friendica\Util\ACLFormatter;
use PHPUnit\Framework\TestCase;
/**
* Tests for text functions.