namespace Friendica\Model;
-use Friendica\BaseObject;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
use Friendica\Core\Config;
use Friendica\Core\System;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
+use Friendica\DI;
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;
use Friendica\Worker\Delivery;
use Text_LanguageDetect;
-class Item extends BaseObject
+class Item
{
// Posting types, inspired by https://www.w3.org/TR/activitystreams-vocabulary/#object-types
const PT_ARTICLE = 0;
'iaid' => 'internal-iaid'];
if ($usermode) {
- $fields['user-item'] = ['pinned', 'ignored' => 'internal-user-ignored'];
+ $fields['user-item'] = ['pinned', 'notification-type', 'ignored' => 'internal-user-ignored'];
}
$fields['item-activity'] = ['activity', 'activity' => 'internal-activity'];
// "Deleting" global items just means hiding them
if ($item['uid'] == 0) {
DBA::update('user-item', ['hidden' => true], ['iid' => $item['id'], 'uid' => $uid], true);
+
+ // Delete notifications
+ DBA::delete('notify', ['iid' => $item['id'], 'uid' => $uid]);
} elseif ($item['uid'] == $uid) {
self::deleteById($item['id'], PRIORITY_HIGH);
} else {
// Delete tags that had been attached to other items
self::deleteTagsFromItem($item);
+ // Delete notifications
+ DBA::delete('notify', ['iid' => $item['id'], 'uid' => $item['uid']]);
+
// Set the item to "deleted"
$item_fields = ['deleted' => true, 'edited' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()];
DBA::update('item', $item_fields, ['id' => $item['id']]);
if ($notify) {
// We have to avoid duplicates. So we create the GUID in form of a hash of the plink or uri.
// We add the hash of our own host because our host is the original creator of the post.
- $prefix_host = \get_app()->getHostName();
+ $prefix_host = DI::baseUrl()->getHostname();
} else {
$prefix_host = '';
$item['parent-uri'] = $item['thr-parent'];
}
- /** @var Activity $activity */
- $activity = self::getClass(Activity::class);
+ $activity = DI::activity();
if (isset($item['gravity'])) {
$item['gravity'] = intval($item['gravity']);
$item['edited'] = DateTimeFormat::utcNow();
}
- $item['plink'] = ($item['plink'] ?? '') ?: System::baseUrl() . '/display/' . urlencode($item['guid']);
+ $item['plink'] = ($item['plink'] ?? '') ?: DI::baseUrl() . '/display/' . urlencode($item['guid']);
$default = ['url' => $item['author-link'], 'name' => $item['author-name'],
'photo' => $item['author-avatar'], 'network' => $item['network']];
self::updateContact($item);
+ UserItem::setNotification($current_post);
+
check_user_notification($current_post);
if ($notify || ($item['visible'] && ((!empty($parent) && $parent['origin']) || $item['origin']))) {
}
// To avoid timing problems, we are using locks.
- $locked = Lock::acquire('item_insert_activity');
+ $locked = DI::lock()->acquire('item_insert_activity');
if (!$locked) {
Logger::log("Couldn't acquire lock for URI " . $item['uri'] . " - proceeding anyway.");
}
} else {
// This shouldn't happen.
Logger::log('Could not insert activity for URI ' . $item['uri'] . ' - should not happen');
- Lock::release('item_insert_activity');
+ DI::lock()->release('item_insert_activity');
return false;
}
if ($locked) {
- Lock::release('item_insert_activity');
+ DI::lock()->release('item_insert_activity');
}
return true;
}
}
// To avoid timing problems, we are using locks.
- $locked = Lock::acquire('item_insert_content');
+ $locked = DI::lock()->acquire('item_insert_content');
if (!$locked) {
Logger::log("Couldn't acquire lock for URI " . $item['uri'] . " - proceeding anyway.");
}
Logger::log('Could not insert content for URI ' . $item['uri'] . ' - should not happen');
}
if ($locked) {
- Lock::release('item_insert_content');
+ DI::lock()->release('item_insert_content');
}
}
$guid = System::createUUID();
}
- return self::getApp()->getBaseURL() . '/objects/' . $guid;
+ return DI::baseUrl()->get() . '/objects/' . $guid;
}
/**
// All hashtags should point to the home server if "local_tags" is activated
if (Config::get('system', 'local_tags')) {
$item["body"] = preg_replace("/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
- "#[url=".System::baseUrl()."/search?tag=$2]$2[/url]", $item["body"]);
+ "#[url=".DI::baseUrl()."/search?tag=$2]$2[/url]", $item["body"]);
$item["tag"] = preg_replace("/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
- "#[url=".System::baseUrl()."/search?tag=$2]$2[/url]", $item["tag"]);
+ "#[url=".DI::baseUrl()."/search?tag=$2]$2[/url]", $item["tag"]);
}
// mask hashtags inside of url, bookmarks and attachments to avoid urls in urls
"#$2", $item["body"]);
foreach ($tags as $tag) {
- if ((strpos($tag, '#') !== 0) || strpos($tag, '[url=') || $tag[1] == '#') {
+ if ((strpos($tag, '#') !== 0) || strpos($tag, '[url=') || strlen($tag) < 2 || $tag[1] == '#') {
continue;
}
$basetag = str_replace('_',' ',substr($tag,1));
- $newtag = '#[url=' . System::baseUrl() . '/search?tag=' . $basetag . ']' . $basetag . '[/url]';
+ $newtag = '#[url=' . DI::baseUrl() . '/search?tag=' . $basetag . ']' . $basetag . '[/url]';
$item["body"] = str_replace($tag, $newtag, $item["body"]);
return false;
}
- $link = Strings::normaliseLink(System::baseUrl() . '/profile/' . $user['nickname']);
+ $link = Strings::normaliseLink(DI::baseUrl() . '/profile/' . $user['nickname']);
/*
* Diaspora uses their own hardwired link URL in @-tags
* instead of the one we supply with webfinger
*/
- $dlink = Strings::normaliseLink(System::baseUrl() . '/u/' . $user['nickname']);
+ $dlink = Strings::normaliseLink(DI::baseUrl() . '/u/' . $user['nickname']);
$cnt = preg_match_all('/[\@\!]\[url\=(.*?)\](.*?)\[\/url\]/ism', $item['body'], $matches, PREG_SET_ORDER);
if ($cnt) {
public static function isRemoteSelf($contact, &$datarray)
{
- $a = \get_app();
-
if (!$contact['remote_self']) {
return false;
}
}
// Prevent to forward already forwarded posts
- if ($datarray["app"] == $a->getHostName()) {
+ if ($datarray["app"] == DI::baseUrl()->getHostname()) {
Logger::log('Already forwarded (second test)', Logger::DEBUG);
return false;
}
}
Logger::log('check for photos', Logger::DEBUG);
- $site = substr(System::baseUrl(), strpos(System::baseUrl(), '://'));
+ $site = substr(DI::baseUrl(), strpos(DI::baseUrl(), '://'));
$orig_body = $s;
$new_body = '';
*/
public static function enumeratePermissions(array $obj, bool $check_dead = false)
{
- /** @var ACLFormatter $aclFormater */
- $aclFormater = self::getClass(ACLFormatter::class);
+ $aclFormater = DI::aclFormatter();
$allow_people = $aclFormater->expand($obj['allow_cid']);
$allow_groups = Group::expand($obj['uid'], $aclFormater->expand($obj['allow_gid']), $check_dead);
*/
private static function addRedirToImageTags(array &$item)
{
- $app = self::getApp();
+ $app = DI::app();
$matches = [];
$cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER);
*/
public static function prepareBody(array &$item, $attach = false, $is_preview = false)
{
- $a = self::getApp();
+ $a = DI::app();
Hook::callAll('prepare_body_init', $item);
// In order to provide theme developers more possibilities, event items
if (strpos($mime, 'video') !== false) {
if (!$vhead) {
$vhead = true;
- $a->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('videos_head.tpl'));
+ DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('videos_head.tpl'));
}
$url_parts = explode('/', $the_url);
*/
public static function getPlink($item)
{
- $a = self::getApp();
+ $a = DI::app();
if ($a->user['nickname'] != "") {
$ret = [
];
if (!empty($item['plink'])) {
- $ret["href"] = $a->removeBaseURL($item['plink']);
+ $ret["href"] = DI::baseUrl()->remove($item['plink']);
$ret["title"] = L10n::t('link to source');
}
return 0;
}
+
+ /**
+ * Return share data from an item array (if the item is shared item)
+ * We are providing the complete Item array, because at some time in the future
+ * we hopefully will define these values not in the body anymore but in some item fields.
+ * This function is meant to replace all similar functions in the system.
+ *
+ * @param array $item
+ *
+ * @return array with share information
+ */
+ public static function getShareArray($item)
+ {
+ if (!preg_match("/(.*?)\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", $item['body'], $matches)) {
+ return [];
+ }
+
+ $attribute_string = $matches[2];
+ $attributes = ['comment' => trim($matches[1]), 'shared' => trim($matches[3])];
+ foreach (['author', 'profile', 'avatar', 'guid', 'posted', 'link'] as $field) {
+ if (preg_match("/$field=(['\"])(.+?)\\1/ism", $attribute_string, $matches)) {
+ $attributes[$field] = trim(html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8'));
+ }
+ }
+ return $attributes;
+ }
+
+ /**
+ * Fetch item information for shared items from the original items and adds it.
+ *
+ * @param array $item
+ *
+ * @return array item array with data from the original item
+ */
+ public static function addShareDataFromOriginal($item)
+ {
+ $shared = self::getShareArray($item);
+ if (empty($shared)) {
+ return $item;
+ }
+
+ // Real reshares always have got a GUID.
+ if (empty($shared['guid'])) {
+ return $item;
+ }
+
+ $uid = $item['uid'] ?? 0;
+
+ // first try to fetch the item via the GUID. This will work for all reshares that had been created on this system
+ $shared_item = self::selectFirst(['title', 'body', 'attach'], ['guid' => $shared['guid'], 'uid' => [0, $uid]]);
+ if (!DBA::isResult($shared_item)) {
+ // Otherwhise try to find (and possibly fetch) the item via the link. This should work for Diaspora and ActivityPub posts
+ $id = self::fetchByLink($shared['link'], $uid);
+ if (empty($id)) {
+ Logger::info('Original item not found', ['url' => $shared['link'], 'callstack' => System::callstack()]);
+ return $item;
+ }
+
+ $shared_item = self::selectFirst(['title', 'body', 'attach'], ['id' => $id]);
+ if (!DBA::isResult($shared_item)) {
+ return $item;
+ }
+ Logger::info('Got shared data from url', ['url' => $shared['link'], 'callstack' => System::callstack()]);
+ } else {
+ Logger::info('Got shared data from guid', ['guid' => $shared['guid'], 'callstack' => System::callstack()]);
+ }
+
+ if (!empty($shared_item['title'])) {
+ $body = '[h3]' . $shared_item['title'] . "[/h3]\n" . $shared_item['body'];
+ unset($shared_item['title']);
+ } else {
+ $body = $shared_item['body'];
+ }
+
+ $item['body'] = preg_replace("/\[share ([^\[\]]*)\].*\[\/share\]/ism", '[share $1]' . $body . '[/share]', $item['body']);
+ unset($shared_item['body']);
+
+ return array_merge($item, $shared_item);
+ }
}