X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=mod%2Fitem.php;h=6a3fd1896bc4a9f3a29e47a653e80e6aa95d283b;hb=59c42a92375b21028cc286bb991cc159c1c8ecf3;hp=2e5a08203621c94c9d90cf7e77506096ab08336d;hpb=09961a07e48de836f0629b807c4a276c2ff06e87;p=friendica.git diff --git a/mod/item.php b/mod/item.php index 2e5a082036..6a3fd1896b 100644 --- a/mod/item.php +++ b/mod/item.php @@ -29,11 +29,12 @@ */ use Friendica\App; -use Friendica\Content\Pager; +use Friendica\Content\Item as ItemHelper; use Friendica\Content\Text\BBCode; use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Core\Protocol; +use Friendica\Core\Renderer; use Friendica\Core\Session; use Friendica\Core\System; use Friendica\Core\Worker; @@ -46,7 +47,7 @@ use Friendica\Model\FileTag; use Friendica\Model\Item; use Friendica\Model\Notify\Type; use Friendica\Model\Photo; -use Friendica\Model\Term; +use Friendica\Model\Tag; use Friendica\Network\HTTPException; use Friendica\Object\EMail\ItemCCEMail; use Friendica\Protocol\Activity; @@ -67,7 +68,10 @@ function item_post(App $a) { if (!empty($_REQUEST['dropitems'])) { $arr_drop = explode(',', $_REQUEST['dropitems']); - drop_items($arr_drop); + foreach ($arr_drop as $item) { + Item::deleteForUser(['id' => $item], $uid); + } + $json = ['success' => 1]; System::jsonExit($json); } @@ -101,14 +105,9 @@ function item_post(App $a) { $toplevel_item_id = intval($_REQUEST['parent'] ?? 0); $thr_parent_uri = trim($_REQUEST['parent_uri'] ?? ''); - $thread_parent_id = 0; - $thread_parent_contact = null; - $toplevel_item = null; $parent_user = null; - $parent_contact = null; - $objecttype = null; $profile_uid = ($_REQUEST['profile_uid'] ?? 0) ?: local_user(); $posttype = ($_REQUEST['post_type'] ?? '') ?: Item::PT_ARTICLE; @@ -123,12 +122,10 @@ function item_post(App $a) { // if this isn't the top-level parent of the conversation, find it if (DBA::isResult($toplevel_item)) { // The URI and the contact is taken from the direct parent which needn't to be the top parent - $thread_parent_id = $toplevel_item['id']; $thr_parent_uri = $toplevel_item['uri']; - $thread_parent_contact = Contact::getDetailsByURL($toplevel_item["author-link"]); - if ($toplevel_item['id'] != $toplevel_item['parent']) { - $toplevel_item = Item::selectFirst(Item::ITEM_FIELDLIST, ['id' => $toplevel_item['parent']]); + if ($toplevel_item['gravity'] != GRAVITY_PARENT) { + $toplevel_item = Item::selectFirst([], ['id' => $toplevel_item['parent']]); } } @@ -253,7 +250,7 @@ function item_post(App $a) { $verb = $orig_post['verb']; $objecttype = $orig_post['object-type']; $app = $orig_post['app']; - $categories = $orig_post['file']; + $categories = $orig_post['file'] ?? ''; $title = Strings::escapeTags(trim($_REQUEST['title'])); $body = trim($body); $private = $orig_post['private']; @@ -261,32 +258,18 @@ function item_post(App $a) { $network = $orig_post['network']; $guid = $orig_post['guid']; $extid = $orig_post['extid']; - } else { + $str_contact_allow = ''; + $str_group_allow = ''; + $str_contact_deny = ''; + $str_group_deny = ''; - /* - * if coming from the API and no privacy settings are set, - * use the user default permissions - as they won't have - * been supplied via a form. - */ - if ($api_source - && !array_key_exists('contact_allow', $_REQUEST) - && !array_key_exists('group_allow', $_REQUEST) - && !array_key_exists('contact_deny', $_REQUEST) - && !array_key_exists('group_deny', $_REQUEST)) { - $str_group_allow = $user['allow_gid']; - $str_contact_allow = $user['allow_cid']; - $str_group_deny = $user['deny_gid']; - $str_contact_deny = $user['deny_cid']; - } else { - // use the posted permissions - + if (($_REQUEST['visibility'] ?? '') !== 'public') { $aclFormatter = DI::aclFormatter(); - - $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'] ?? ''); + $str_contact_allow = isset($_REQUEST['contact_allow']) ? $aclFormatter->toString($_REQUEST['contact_allow']) : $user['allow_cid'] ?? ''; + $str_group_allow = isset($_REQUEST['group_allow']) ? $aclFormatter->toString($_REQUEST['group_allow']) : $user['allow_gid'] ?? ''; + $str_contact_deny = isset($_REQUEST['contact_deny']) ? $aclFormatter->toString($_REQUEST['contact_deny']) : $user['deny_cid'] ?? ''; + $str_group_deny = isset($_REQUEST['group_deny']) ? $aclFormatter->toString($_REQUEST['group_deny']) : $user['deny_gid'] ?? ''; } $title = Strings::escapeTags(trim($_REQUEST['title'] ?? '')); @@ -300,7 +283,13 @@ function item_post(App $a) { $postopts = $_REQUEST['postopts'] ?? ''; - $private = ((strlen($str_group_allow) || strlen($str_contact_allow) || strlen($str_group_deny) || strlen($str_contact_deny)) ? 1 : 0); + if (strlen($str_group_allow) || strlen($str_contact_allow) || strlen($str_group_deny) || strlen($str_contact_deny)) { + $private = Item::PRIVATE; + } elseif (DI::pConfig()->get($profile_uid, 'system', 'unlisted')) { + $private = Item::UNLISTED; + } else { + $private = Item::PUBLIC; + } // If this is a comment, set the permissions from the parent. @@ -312,10 +301,10 @@ function item_post(App $a) { $network = $toplevel_item['network']; } - $str_contact_allow = $toplevel_item['allow_cid']; - $str_group_allow = $toplevel_item['allow_gid']; - $str_contact_deny = $toplevel_item['deny_cid']; - $str_group_deny = $toplevel_item['deny_gid']; + $str_contact_allow = $toplevel_item['allow_cid'] ?? ''; + $str_group_allow = $toplevel_item['allow_gid'] ?? ''; + $str_contact_deny = $toplevel_item['deny_cid'] ?? ''; + $str_group_deny = $toplevel_item['deny_gid'] ?? ''; $private = $toplevel_item['private']; $wall = $toplevel_item['wall']; @@ -378,74 +367,63 @@ function item_post(App $a) { // get contact info for owner if ($profile_uid == local_user() || $allow_comment) { - $contact_record = $author; + $contact_record = $author ?: []; } else { - $contact_record = DBA::selectFirst('contact', [], ['uid' => $profile_uid, 'self' => true]); + $contact_record = DBA::selectFirst('contact', [], ['uid' => $profile_uid, 'self' => true]) ?: []; } // Look for any tags and linkify them - $str_tags = ''; $inform = ''; - - $tags = BBCode::getTags($body); - - if ($thread_parent_id && !\Friendica\Content\Feature::isEnabled($uid, 'explicit_mentions')) { - $tags = item_add_implicit_mentions($tags, $thread_parent_contact, $thread_parent_id); - } - - $tagged = []; - $private_forum = false; + $private_id = null; $only_to_forum = false; $forum_contact = []; - if (count($tags)) { + $body = BBCode::performWithEscapedTags($body, ['noparse', 'pre', 'code', 'img'], function ($body) use ($profile_uid, $network, $str_contact_allow, &$inform, &$private_forum, &$private_id, &$only_to_forum, &$forum_contact) { + $tags = BBCode::getTags($body); + + $tagged = []; + foreach ($tags as $tag) { $tag_type = substr($tag, 0, 1); - if ($tag_type == Term::TAG_CHARACTER[Term::HASHTAG]) { + if ($tag_type == Tag::TAG_CHARACTER[Tag::HASHTAG]) { continue; } - /* - * If we already tagged 'Robert Johnson', don't try and tag 'Robert'. + /* If we already tagged 'Robert Johnson', don't try and tag 'Robert'. * Robert Johnson should be first in the $tags array */ - $fullnametagged = false; - /// @TODO $tagged is initialized above if () block and is not filled, maybe old-lost code? foreach ($tagged as $nextTag) { if (stristr($nextTag, $tag . ' ')) { - $fullnametagged = true; - break; + continue 2; } } - if ($fullnametagged) { - continue; - } - $success = handle_tag($body, $inform, $str_tags, local_user() ? local_user() : $profile_uid, $tag, $network); + $success = ItemHelper::replaceTag($body, $inform, local_user() ? local_user() : $profile_uid, $tag, $network); if ($success['replaced']) { $tagged[] = $tag; } // When the forum is private or the forum is addressed with a "!" make the post private - if (is_array($success['contact']) && (!empty($success['contact']['prv']) || ($tag_type == Term::TAG_CHARACTER[Term::EXCLUSIVE_MENTION]))) { + if (!empty($success['contact']['prv']) || ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION])) { $private_forum = $success['contact']['prv']; - $only_to_forum = ($tag_type == Term::TAG_CHARACTER[Term::EXCLUSIVE_MENTION]); + $only_to_forum = ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]); $private_id = $success['contact']['id']; $forum_contact = $success['contact']; - } elseif (is_array($success['contact']) && !empty($success['contact']['forum']) && - ($str_contact_allow == '<' . $success['contact']['id'] . '>')) { + } elseif (!empty($success['contact']['forum']) && ($str_contact_allow == '<' . $success['contact']['id'] . '>')) { $private_forum = false; $only_to_forum = true; $private_id = $success['contact']['id']; $forum_contact = $success['contact']; } } - } + + return $body; + }); $original_contact_id = $contact_id; - if (!$toplevel_item_id && count($forum_contact) && ($private_forum || $only_to_forum)) { + if (!$toplevel_item_id && !empty($forum_contact) && ($private_forum || $only_to_forum)) { // we tagged a forum in a top level post. Now we change the post $private = $private_forum; @@ -475,7 +453,7 @@ function item_post(App $a) { $match = null; - if (!$preview && Photo::setPermissionFromBody($body, $profile_uid, $original_contact_id, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny)) { + if (!$preview && Photo::setPermissionFromBody($body, $uid, $original_contact_id, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny)) { $objecttype = Activity\ObjectType::IMAGE; } @@ -586,9 +564,9 @@ function item_post(App $a) { $datarray['gravity'] = $gravity; $datarray['network'] = $network; $datarray['contact-id'] = $contact_id; - $datarray['owner-name'] = $contact_record['name']; - $datarray['owner-link'] = $contact_record['url']; - $datarray['owner-avatar'] = $contact_record['thumb']; + $datarray['owner-name'] = $contact_record['name'] ?? ''; + $datarray['owner-link'] = $contact_record['url'] ?? ''; + $datarray['owner-avatar'] = $contact_record['thumb'] ?? ''; $datarray['owner-id'] = Contact::getIdForURL($datarray['owner-link']); $datarray['author-name'] = $author['name']; $datarray['author-link'] = $author['url']; @@ -607,7 +585,6 @@ function item_post(App $a) { $datarray['app'] = $app; $datarray['location'] = $location; $datarray['coord'] = $coord; - $datarray['tag'] = $str_tags; $datarray['file'] = $categories; $datarray['inform'] = $inform; $datarray['verb'] = $verb; @@ -664,7 +641,7 @@ function item_post(App $a) { // Check for hashtags in the body and repair or add hashtag links if ($preview || $orig_post) { - Item::setHashtags($datarray); + $datarray['body'] = Item::setHashtags($datarray['body']); } // preview mode - prepare the body for display and send it via json @@ -672,6 +649,7 @@ function item_post(App $a) { // We set the datarray ID to -1 because in preview mode the dataray // doesn't have an ID. $datarray["id"] = -1; + $datarray["uri-id"] = -1; $datarray["item_id"] = -1; $datarray["author-network"] = Protocol::DFRN; @@ -704,7 +682,6 @@ function item_post(App $a) { $fields = [ 'title' => $datarray['title'], 'body' => $datarray['body'], - 'tag' => $datarray['tag'], 'attach' => $datarray['attach'], 'file' => $datarray['file'], 'rendered-html' => $datarray['rendered-html'], @@ -758,12 +735,18 @@ function item_post(App $a) { throw new HTTPException\InternalServerErrorException(DI::l10n()->t('Item couldn\'t be fetched.')); } + Tag::storeFromBody($datarray['uri-id'], $datarray['body']); + + if (!\Friendica\Content\Feature::isEnabled($uid, 'explicit_mentions') && ($datarray['gravity'] == GRAVITY_COMMENT)) { + Tag::createImplicitMentions($datarray['uri-id'], $datarray['thr-parent-id']); + } + // update filetags in pconfig FileTag::updatePconfig($uid, $categories_old, $categories_new, 'category'); // These notifications are sent if someone else is commenting other your wall - if ($toplevel_item_id) { - if ($contact_record != $author) { + if ($contact_record != $author) { + if ($toplevel_item_id) { notification([ 'type' => Type::COMMENT, 'notify_flags' => $user['notify-flags'], @@ -781,9 +764,7 @@ function item_post(App $a) { 'parent' => $toplevel_item_id, 'parent_uri' => $toplevel_item['uri'] ]); - } - } else { - if (($contact_record != $author) && !count($forum_contact)) { + } elseif (empty($forum_contact)) { notification([ 'type' => Type::WALL, 'notify_flags' => $user['notify-flags'], @@ -871,7 +852,9 @@ function item_content(App $a) if (($a->argc >= 3) && ($a->argv[1] === 'drop') && intval($a->argv[2])) { if (DI::mode()->isAjax()) { - $o = Item::deleteForUser(['id' => $a->argv[2]], local_user()); + Item::deleteForUser(['id' => $a->argv[2]], local_user()); + // ajax return: [, 0 (no perm) | ] + System::jsonExit([intval($a->argv[2]), local_user()]); } else { if (!empty($a->argv[3])) { $o = drop_item($a->argv[2], $a->argv[3]); @@ -880,203 +863,110 @@ function item_content(App $a) $o = drop_item($a->argv[2]); } } - - if (DI::mode()->isAjax()) { - // ajax return: [, 0 (no perm) | ] - System::jsonExit([intval($a->argv[2]), intval($o)]); - } } return $o; } /** - * This function removes the tag $tag from the text $body and replaces it with - * the appropriate link. - * - * @param App $a - * @param string $body the text to replace the tag in - * @param string $inform a comma-seperated string containing everybody to inform - * @param string $str_tags string to add the tag to - * @param integer $profile_uid - * @param string $tag the tag to replace - * @param string $network The network of the post - * - * @return array|bool ['replaced' => $replaced, 'contact' => $contact]; - * @throws ImagickException + * @param int $id + * @param string $return + * @return string * @throws HTTPException\InternalServerErrorException */ -function handle_tag(&$body, &$inform, &$str_tags, $profile_uid, $tag, $network = "") +function drop_item(int $id, string $return = '') { - $replaced = false; - $r = null; - - //is it a person tag? - if (Term::isType($tag, Term::MENTION, Term::IMPLICIT_MENTION, Term::EXCLUSIVE_MENTION)) { - $tag_type = substr($tag, 0, 1); - //is it already replaced? - if (strpos($tag, '[url=')) { - //append tag to str_tags - if (!stristr($str_tags, $tag)) { - if (strlen($str_tags)) { - $str_tags .= ','; - } - $str_tags .= $tag; - } + // locate item to be deleted + $fields = ['id', 'uid', 'guid', 'contact-id', 'deleted', 'gravity', 'parent']; + $item = Item::selectFirstForUser(local_user(), $fields, ['id' => $id]); - // Checking for the alias that is used for OStatus - $pattern = "/[@!]\[url\=(.*?)\](.*?)\[\/url\]/ism"; - if (preg_match($pattern, $tag, $matches)) { - $data = Contact::getDetailsByURL($matches[1]); - - if ($data["alias"] != "") { - $newtag = '@[url=' . $data["alias"] . ']' . $data["nick"] . '[/url]'; - - if (!stripos($str_tags, '[url=' . $data["alias"] . ']')) { - if (strlen($str_tags)) { - $str_tags .= ','; - } - - $str_tags .= $newtag; - } - } - } - - return $replaced; - } - - //get the person's name - $name = substr($tag, 1); - - // Sometimes the tag detection doesn't seem to work right - // This is some workaround - $nameparts = explode(" ", $name); - $name = $nameparts[0]; - - // Try to detect the contact in various ways - if (strpos($name, 'http://')) { - // At first we have to ensure that the contact exists - Contact::getIdForURL($name); + if (!DBA::isResult($item)) { + notice(DI::l10n()->t('Item not found.') . EOL); + DI::baseUrl()->redirect('network'); + } - // Now we should have something - $contact = Contact::getDetailsByURL($name); - } elseif (strpos($name, '@')) { - // This function automatically probes when no entry was found - $contact = Contact::getDetailsByAddr($name); - } else { - $contact = false; - $fields = ['id', 'url', 'nick', 'name', 'alias', 'network', 'forum', 'prv']; + if ($item['deleted']) { + return ''; + } - if (strrpos($name, '+')) { - // Is it in format @nick+number? - $tagcid = intval(substr($name, strrpos($name, '+') + 1)); - $contact = DBA::selectFirst('contact', $fields, ['id' => $tagcid, 'uid' => $profile_uid]); - } + $contact_id = 0; - // select someone by nick or attag in the current network - if (!DBA::isResult($contact) && ($network != "")) { - $condition = ["(`nick` = ? OR `attag` = ?) AND `network` = ? AND `uid` = ?", - $name, $name, $network, $profile_uid]; - $contact = DBA::selectFirst('contact', $fields, $condition); - } + // check if logged in user is either the author or owner of this item + if (Session::getRemoteContactID($item['uid']) == $item['contact-id']) { + $contact_id = $item['contact-id']; + } - //select someone by name in the current network - if (!DBA::isResult($contact) && ($network != "")) { - $condition = ['name' => $name, 'network' => $network, 'uid' => $profile_uid]; - $contact = DBA::selectFirst('contact', $fields, $condition); - } + if ((local_user() == $item['uid']) || $contact_id) { + // Check if we should do HTML-based delete confirmation + if (!empty($_REQUEST['confirm'])) { + //
can't take arguments in its "action" parameter + // so add any arguments as hidden inputs + $query = explode_querystring(DI::args()->getQueryString()); + $inputs = []; - // select someone by nick or attag in any network - if (!DBA::isResult($contact)) { - $condition = ["(`nick` = ? OR `attag` = ?) AND `uid` = ?", $name, $name, $profile_uid]; - $contact = DBA::selectFirst('contact', $fields, $condition); + foreach ($query['args'] as $arg) { + if (strpos($arg, 'confirm=') === false) { + $arg_parts = explode('=', $arg); + $inputs[] = ['name' => $arg_parts[0], 'value' => $arg_parts[1]]; + } } - // select someone by name in any network - if (!DBA::isResult($contact)) { - $condition = ['name' => $name, 'uid' => $profile_uid]; - $contact = DBA::selectFirst('contact', $fields, $condition); - } + return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [ + '$method' => 'get', + '$message' => DI::l10n()->t('Do you really want to delete this item?'), + '$extra_inputs' => $inputs, + '$confirm' => DI::l10n()->t('Yes'), + '$confirm_url' => $query['base'], + '$confirm_name' => 'confirmed', + '$cancel' => DI::l10n()->t('Cancel'), + ]); } - - // Check if $contact has been successfully loaded - if (DBA::isResult($contact)) { - if (strlen($inform) && (isset($contact["notify"]) || isset($contact["id"]))) { - $inform .= ','; - } - - if (isset($contact["id"])) { - $inform .= 'cid:' . $contact["id"]; - } elseif (isset($contact["notify"])) { - $inform .= $contact["notify"]; - } - - $profile = $contact["url"]; - $alias = $contact["alias"]; - $newname = ($contact["name"] ?? '') ?: $contact["nick"]; + // Now check how the user responded to the confirmation query + if (!empty($_REQUEST['canceled'])) { + DI::baseUrl()->redirect('display/' . $item['guid']); } - //if there is an url for this persons profile - if (isset($profile) && ($newname != "")) { - $replaced = true; - // create profile link - $profile = str_replace(',', '%2c', $profile); - $newtag = $tag_type.'[url=' . $profile . ']' . $newname . '[/url]'; - $body = str_replace($tag_type . $name, $newtag, $body); - // append tag to str_tags - if (!stristr($str_tags, $newtag)) { - if (strlen($str_tags)) { - $str_tags .= ','; - } - $str_tags .= $newtag; - } - - /* - * Status.Net seems to require the numeric ID URL in a mention if the person isn't - * subscribed to you. But the nickname URL is OK if they are. Grrr. We'll tag both. - */ - if (!empty($alias)) { - $newtag = '@[url=' . $alias . ']' . $newname . '[/url]'; - if (!stripos($str_tags, '[url=' . $alias . ']')) { - if (strlen($str_tags)) { - $str_tags .= ','; - } - $str_tags .= $newtag; - } - } + $is_comment = $item['gravity'] == GRAVITY_COMMENT; + $parentitem = null; + if (!empty($item['parent'])) { + $fields = ['guid']; + $parentitem = Item::selectFirstForUser(local_user(), $fields, ['id' => $item['parent']]); } - } - return ['replaced' => $replaced, 'contact' => $contact]; -} - -function item_add_implicit_mentions(array $tags, array $thread_parent_contact, $thread_parent_id) -{ - if (DI::config()->get('system', 'disable_implicit_mentions')) { - // Add a tag if the parent contact is from ActivityPub or OStatus (This will notify them) - if (in_array($thread_parent_contact['network'], [Protocol::OSTATUS, Protocol::ACTIVITYPUB])) { - $contact = Term::TAG_CHARACTER[Term::MENTION] . '[url=' . $thread_parent_contact['url'] . ']' . $thread_parent_contact['nick'] . '[/url]'; - if (!stripos(implode($tags), '[url=' . $thread_parent_contact['url'] . ']')) { - $tags[] = $contact; - } - } - } else { - $implicit_mentions = [ - $thread_parent_contact['url'] => $thread_parent_contact['nick'] - ]; + // delete the item + Item::deleteForUser(['id' => $item['id']], local_user()); - $parent_terms = Term::tagArrayFromItemId($thread_parent_id, [Term::MENTION, Term::IMPLICIT_MENTION]); + $return_url = hex2bin($return); - foreach ($parent_terms as $parent_term) { - $implicit_mentions[$parent_term['url']] = $parent_term['term']; - } + // removes update_* from return_url to ignore Ajax refresh + $return_url = str_replace("update_", "", $return_url); - foreach ($implicit_mentions as $url => $label) { - if ($url != \Friendica\Model\Profile::getMyURL() && !stripos(implode($tags), '[url=' . $url . ']')) { - $tags[] = Term::TAG_CHARACTER[Term::IMPLICIT_MENTION] . '[url=' . $url . ']' . $label . '[/url]'; + // Check if delete a comment + if ($is_comment) { + // Return to parent guid + if (!empty($parentitem)) { + DI::baseUrl()->redirect('display/' . $parentitem['guid']); + //NOTREACHED + } // In case something goes wrong + else { + DI::baseUrl()->redirect('network'); + //NOTREACHED + } + } else { + // if unknown location or deleting top level post called from display + if (empty($return_url) || strpos($return_url, 'display') !== false) { + DI::baseUrl()->redirect('network'); + //NOTREACHED + } else { + DI::baseUrl()->redirect($return_url); + //NOTREACHED } } + } else { + notice(DI::l10n()->t('Permission denied.')); + DI::baseUrl()->redirect('display/' . $item['guid']); + //NOTREACHED } - return $tags; + return ''; }