use Friendica\Content\Text\HTML;
use Friendica\Core\Config;
use Friendica\Core\Hook;
+use Friendica\Core\L10n;
use Friendica\Core\Lock;
use Friendica\Core\Logger;
-use Friendica\Core\L10n;
use Friendica\Core\PConfig;
use Friendica\Core\Protocol;
use Friendica\Core\Renderer;
use Friendica\Protocol\OStatus;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Map;
-use Friendica\Util\XML;
+use Friendica\Util\Network;
use Friendica\Util\Security;
use Friendica\Util\Strings;
+use Friendica\Util\XML;
+use Friendica\Worker\Delivery;
use Text_LanguageDetect;
class Item extends BaseObject
// Field list that is used to display the items
const DISPLAY_FIELDLIST = [
- 'uid', 'id', 'parent', 'uri', 'thr-parent', 'parent-uri', 'guid', 'network',
+ 'uid', 'id', 'parent', 'uri', 'thr-parent', 'parent-uri', 'guid', 'network', 'gravity',
'commented', 'created', 'edited', 'received', 'verb', 'object-type', 'postopts', 'plink',
'wall', 'private', 'starred', 'origin', 'title', 'body', 'file', 'attach', 'language',
'content-warning', 'location', 'coord', 'app', 'rendered-hash', 'rendered-html', 'object',
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'item_id',
'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network',
'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'owner-network',
- 'contact-id', 'contact-link', 'contact-name', 'contact-avatar',
+ 'contact-id', 'contact-uid', 'contact-link', 'contact-name', 'contact-avatar',
'writable', 'self', 'cid', 'alias',
'event-id', 'event-created', 'event-edited', 'event-start', 'event-finish',
'event-summary', 'event-desc', 'event-location', 'event-type',
// Field list that is used to deliver items via the protocols
const DELIVER_FIELDLIST = ['uid', 'id', 'parent', 'uri', 'thr-parent', 'parent-uri', 'guid',
- 'created', 'edited', 'verb', 'object-type', 'object', 'target',
+ 'parent-guid', 'created', 'edited', 'verb', 'object-type', 'object', 'target',
'private', 'title', 'body', 'location', 'coord', 'app',
'attach', 'tag', 'deleted', 'extid', 'post-type',
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
'unseen', 'deleted', 'origin', 'forum_mode', 'mention', 'global', 'network',
'title', 'content-warning', 'body', 'location', 'coord', 'app',
'rendered-hash', 'rendered-html', 'object-type', 'object', 'target-type', 'target',
- 'author-id', 'author-link', 'author-name', 'author-avatar',
+ 'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network',
'owner-id', 'owner-link', 'owner-name', 'owner-avatar'];
// Never reorder or remove entries from this list. Just add new ones at the end, if needed.
// The item-activity table only stores the index and needs this array to know the matching activity.
- const ACTIVITIES = [ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE];
+ const ACTIVITIES = [ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE, ACTIVITY_FOLLOW, ACTIVITY2_ANNOUNCE];
private static $legacy_mode = null;
// We only need to notfiy others when it is an original entry from us.
// Only call the notifier when the item has some content relevant change.
if ($item['origin'] && in_array('edited', array_keys($fields))) {
- Worker::add(PRIORITY_HIGH, "Notifier", 'edit_post', $item['id']);
+ Worker::add(PRIORITY_HIGH, "Notifier", Delivery::POST, $item['id']);
}
}
self::delete(['uri' => $item['uri'], 'deleted' => false], $priority);
// send the notification upstream/downstream
- Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", "drop", intval($item['id']));
+ Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", Delivery::DELETION, intval($item['id']));
} elseif ($item['uid'] != 0) {
// When we delete just our local user copy of an item, we have to set a marker to hide it
$expire_date = time() - ($expire_interval * 86400);
$created_date = strtotime($item['created']);
if ($created_date < $expire_date) {
- Logger::log('item-store: item created ('.date('c', $created_date).') before expiration time ('.date('c', $expire_date).'). ignored. ' . print_r($item,true), Logger::DEBUG);
+ Logger::notice('Item created before expiration interval.', [
+ 'created' => date('c', $created_date),
+ 'expired' => date('c', $expire_date),
+ '$item' => $item
+ ]);
return 0;
}
}
if (DBA::isResult($existing)) {
// We only log the entries with a different user id than 0. Otherwise we would have too many false positives
if ($uid != 0) {
- Logger::log("Item with uri ".$item['uri']." already existed for user ".$uid." with id ".$existing["id"]." target network ".$existing["network"]." - new network: ".$item['network']);
+ Logger::notice('Item already existed for user', [
+ 'uri' => $item['uri'],
+ 'uid' => $uid,
+ 'network' => $item['network'],
+ 'existing_id' => $existing["id"],
+ 'existing_network' => $existing["network"]
+ ]);
}
return $existing["id"];
// When there is no content then we don't post it
if ($item['body'].$item['title'] == '') {
- Logger::log('No body, no title.');
+ Logger::notice('No body, no title.');
return 0;
}
$default = ['url' => $item['author-link'], 'name' => $item['author-name'],
'photo' => $item['author-avatar'], 'network' => $item['network']];
- $item['author-id'] = defaults($item, 'author-id', Contact::getIdForURL($item["author-link"], 0, false, $default));
+ $item['author-id'] = defaults($item, 'author-id', Contact::getIdForURL($item['author-link'], 0, false, $default));
- if (Contact::isBlocked($item["author-id"])) {
- Logger::log('Contact '.$item["author-id"].' is blocked, item '.$item["uri"].' will not be stored');
+ if (Contact::isBlocked($item['author-id'])) {
+ Logger::notice('Author is blocked node-wide', ['author-link' => $item['author-link'], 'item-uri' => $item['uri']]);
+ return 0;
+ }
+
+ if (!empty($item['author-link']) && Network::isUrlBlocked($item['author-link'])) {
+ Logger::notice('Author server is blocked', ['author-link' => $item['author-link'], 'item-uri' => $item['uri']]);
+ return 0;
+ }
+
+ if (!empty($uid) && Contact::isBlockedByUser($item['author-link'], $uid)) {
+ Logger::notice('Author is blocked by user', ['author-link' => $item['author-link'], 'uid' => $uid, 'item-uri' => $item['uri']]);
+ return 0;
+ }
+
+ if (!empty($uid) && ($item['parent-uri'] === $item['uri']) && Contact::isIgnoredByUser($item['author-link'], $uid)) {
+ Logger::notice('Author is ignored by user', ['author-link' => $item['author-link'], 'uid' => $uid, 'item-uri' => $item['uri']]);
return 0;
}
$default = ['url' => $item['owner-link'], 'name' => $item['owner-name'],
'photo' => $item['owner-avatar'], 'network' => $item['network']];
- $item['owner-id'] = defaults($item, 'owner-id', Contact::getIdForURL($item["owner-link"], 0, false, $default));
+ $item['owner-id'] = defaults($item, 'owner-id', Contact::getIdForURL($item['owner-link'], 0, false, $default));
- if (Contact::isBlocked($item["owner-id"])) {
- Logger::log('Contact '.$item["owner-id"].' is blocked, item '.$item["uri"].' will not be stored');
+ if (Contact::isBlocked($item['owner-id'])) {
+ Logger::notice('Owner is blocked node-wide', ['owner-link' => $item['owner-link'], 'item-uri' => $item['uri']]);
return 0;
}
- if ($item['network'] == Protocol::PHANTOM) {
- Logger::log('Missing network. Called by: '.System::callstack(), Logger::DEBUG);
+ if (!empty($item['owner-link']) && Network::isUrlBlocked($item['owner-link'])) {
+ Logger::notice('Owner server is blocked', ['owner-link' => $item['owner-link'], 'item-uri' => $item['uri']]);
+ return 0;
+ }
+ if (!empty($uid) && Contact::isBlockedByUser($item['owner-link'], $uid)) {
+ Logger::notice('Owner is blocked by user', ['owner-link' => $item['owner-link'], 'uid' => $uid, 'item-uri' => $item['uri']]);
+ return 0;
+ }
+
+ if (!empty($uid) && ($item['parent-uri'] === $item['uri']) && Contact::isIgnoredByUser($item['owner-link'], $uid)) {
+ Logger::notice('Owner is ignored by user', ['owner-link' => $item['owner-link'], 'uid' => $uid, 'item-uri' => $item['uri']]);
+ return 0;
+ }
+
+ if ($item['network'] == Protocol::PHANTOM) {
$item['network'] = Protocol::DFRN;
- Logger::log("Set network to " . $item["network"] . " for " . $item["uri"], Logger::DEBUG);
+ Logger::notice('Missing network, setting to {network}.', [
+ 'uri' => $item["uri"],
+ 'network' => $item['network'],
+ 'callstack' => System::callstack()
+ ]);
}
// Checking if there is already an item with the same guid
- Logger::log('Checking for an item for user '.$item['uid'].' on network '.$item['network'].' with the guid '.$item['guid'], Logger::DEBUG);
$condition = ['guid' => $item['guid'], 'network' => $item['network'], 'uid' => $item['uid']];
if (self::exists($condition)) {
- Logger::log('found item with guid '.$item['guid'].' for user '.$item['uid'].' on network '.$item['network'], Logger::DEBUG);
+ Logger::notice('Found already existing item', [
+ 'guid' => $item['guid'],
+ 'uid' => $item['uid'],
+ 'network' => $item['network']
+ ]);
return 0;
}
$item['thr-parent'] = $item['parent-uri'];
- $notify_type = '';
+ $notify_type = Delivery::POST;
$allow_cid = '';
$allow_gid = '';
$deny_cid = '';
$allow_gid = $item['allow_gid'];
$deny_cid = $item['deny_cid'];
$deny_gid = $item['deny_gid'];
- $notify_type = 'wall-new';
} else {
// find the parent and snarf the item id and ACLs
// and anything else we need to inherit
$allow_gid = $parent['allow_gid'];
$deny_cid = $parent['deny_cid'];
$deny_gid = $parent['deny_gid'];
- $item['wall'] = $parent['wall'];
- $notify_type = 'comment-new';
+ $item['wall'] = $parent['wall'];
/*
* If the parent is private, force privacy for the entire conversation
}
}
+ if (stristr($item['verb'], ACTIVITY_POKE)) {
+ $notify_type = Delivery::POKE;
+ }
+
$item['parent-uri-id'] = ItemURI::getIdByURI($item['parent-uri']);
$item['thr-parent-id'] = ItemURI::getIdByURI($item['thr-parent']);
$item["global"] = true;
// Set the global flag on all items if this was a global item entry
- self::update(['global' => true], ['uri' => $item["uri"]]);
+ DBA::update('item', ['global' => true], ['uri' => $item["uri"]]);
} else {
$item["global"] = self::exists(['uid' => 0, 'uri' => $item["uri"]]);
}
unset($item['author-link']);
unset($item['author-name']);
unset($item['author-avatar']);
+ unset($item['author-network']);
unset($item['owner-link']);
unset($item['owner-name']);
}
// Set parent id
- self::update(['parent' => $parent_id], ['id' => $current_post]);
+ DBA::update('item', ['parent' => $parent_id], ['id' => $current_post]);
$item['id'] = $current_post;
$item['parent'] = $parent_id;
// update the commented timestamp on the parent
// Only update "commented" if it is really a comment
if (($item['gravity'] != GRAVITY_ACTIVITY) || !Config::get("system", "like_no_comment")) {
- self::update(['commented' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()], ['id' => $parent_id]);
+ DBA::update('item', ['commented' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()], ['id' => $parent_id]);
} else {
- self::update(['changed' => DateTimeFormat::utcNow()], ['id' => $parent_id]);
+ DBA::update('item', ['changed' => DateTimeFormat::utcNow()], ['id' => $parent_id]);
}
if ($dsprsig) {
check_user_notification($current_post);
- if ($notify) {
+ if ($notify || ($item['visible'] && ((!empty($parent) && $parent['origin']) || $item['origin']))) {
Worker::add(['priority' => $priority, 'dont_fork' => true], 'Notifier', $notify_type, $current_post);
- } elseif ($item['visible'] && ((!empty($parent) && $parent['origin']) || $item['origin'])) {
- if ($item['gravity'] == GRAVITY_ACTIVITY) {
- $cmd = $item['origin'] ? 'activity-new' : 'activity-import';
- } elseif ($item['gravity'] == GRAVITY_COMMENT) {
- $cmd = $item['origin'] ? 'comment-new' : 'comment-import';
- } else {
- $cmd = 'wall-new';
- }
-
- Worker::add(['priority' => $priority, 'dont_fork' => true], 'Notifier', $cmd, $current_post);
}
return $current_post;
}, $item["body"]);
}
- public static function getGuidById($id)
- {
- $item = self::selectFirst(['guid'], ['id' => $id]);
- if (DBA::isResult($item)) {
- return $item['guid'];
- } else {
- return '';
- }
- }
-
- /**
- * This function is only used for the old Friendica app on Android that doesn't like paths with guid
- *
- * @param string $guid item guid
- * @param int $uid user id
- * @return array with id and nick of the item with the given guid
- * @throws \Exception
- */
- public static function getIdAndNickByGuid($guid, $uid = 0)
- {
- $nick = "";
- $id = 0;
-
- if ($uid == 0) {
- $uid = local_user();
- }
-
- // Does the given user have this item?
- if ($uid) {
- $item = self::selectFirst(['id'], ['guid' => $guid, 'uid' => $uid]);
- if (DBA::isResult($item)) {
- $user = DBA::selectFirst('user', ['nickname'], ['uid' => $uid]);
- if (!DBA::isResult($user)) {
- return;
- }
- $id = $item['id'];
- $nick = $user['nickname'];
- }
- }
-
- // Or is it anywhere on the server?
- if ($nick == "") {
- $condition = ["`guid` = ? AND `uid` != 0", $guid];
- $item = self::selectFirst(['id', 'uid'], $condition);
- if (DBA::isResult($item)) {
- $user = DBA::selectFirst('user', ['nickname'], ['uid' => $item['uid']]);
- if (!DBA::isResult($user)) {
- return;
- }
- $id = $item['id'];
- $nick = $user['nickname'];
- }
- }
- return ["nick" => $nick, "id" => $id];
- }
-
/**
* look for mention tags and setup a second delivery chain for forum/community posts if appropriate
*
self::updateThread($item_id);
- Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], 'Notifier', 'tgroup', $item_id);
+ Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], 'Notifier', Delivery::POST, $item_id);
}
public static function isRemoteSelf($contact, &$datarray)
$datarray['author-link'] = $datarray['owner-link'];
$datarray['author-avatar'] = $datarray['owner-avatar'];
- unset($datarray['created']);
unset($datarray['edited']);
unset($datarray['network']);
if (strpos($mime, 'video') !== false) {
if (!$vhead) {
$vhead = true;
- $a->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('videos_head.tpl'), [
- '$baseurl' => System::baseUrl(),
- ]);
+ $a->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('videos_head.tpl'));
}
$url_parts = explode('/', $the_url);
return $ret;
}
+
+ /**
+ * Is the given item array a post that is sent as starting post to a forum?
+ *
+ * @param array $item
+ * @param array $owner
+ *
+ * @return boolean "true" when it is a forum post
+ */
+ public static function isForumPost(array $item, array $owner = [])
+ {
+ if (empty($owner)) {
+ $owner = User::getOwnerDataById($item['uid']);
+ if (empty($owner)) {
+ return false;
+ }
+ }
+
+ if (($item['author-id'] == $item['owner-id']) ||
+ ($owner['id'] == $item['contact-id']) ||
+ ($item['uri'] != $item['parent-uri']) ||
+ $item['origin']) {
+ return false;
+ }
+
+ return Contact::isForum($item['contact-id']);
+ }
}