X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=inline;f=include%2Fconversation.php;h=3eae184a827c4b562b160d02b29e45fd9ebda64e;hb=f43aaf5227af34b870bae3715f2cb1de293aa272;hp=27f9578ed8009b9f7bfc3fe8ff0df801a2c64cd3;hpb=7a9456d5ac2e73229b2237074c700731c63661a6;p=friendica.git diff --git a/include/conversation.php b/include/conversation.php index 27f9578ed8..3eae184a82 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1,6 +1,14 @@ array('tid'))); + while ($tag = dba::fetch($taglist)) { if ($tag["url"] == "") { $tag["url"] = $searchpath . strtolower($tag["term"]); } + $tag["url"] = best_link_url($item, $sp, $tag["url"]); + if ($tag["type"] == TERM_HASHTAG) { $hashtags[] = "#" . $tag["term"] . ""; $prefix = "#"; @@ -699,9 +718,10 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { } $tags[] = $prefix."" . $tag["term"] . ""; } + dba::close($taglist); $sp = false; - $profile_link = best_link_url($item,$sp); + $profile_link = best_link_url($item, $sp); if ($profile_link === 'mailbox') { $profile_link = ''; } @@ -712,7 +732,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { $profile_link = zrl($profile_link); } - if (!x($item, 'author-thumb') OR ($item['author-thumb'] == "")) { + if (!x($item, 'author-thumb') || ($item['author-thumb'] == "")) { $author_contact = get_contact_details_by_url($item['author-link'], $profile_owner); if ($author_contact["thumb"]) { $item['author-thumb'] = $author_contact["thumb"]; @@ -721,7 +741,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { } } - if (!isset($item['owner-thumb']) OR ($item['owner-thumb'] == "")) { + if (!isset($item['owner-thumb']) || ($item['owner-thumb'] == "")) { $owner_contact = get_contact_details_by_url($item['owner-link'], $profile_owner); if ($owner_contact["thumb"]) { $item['owner-thumb'] = $owner_contact["thumb"]; @@ -787,6 +807,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { $tmp_item = array( 'template' => $tpl, 'id' => (($preview) ? 'P0' : $item['item_id']), + 'guid' => (($preview) ? 'Q0' : $item['guid']), 'network' => $item['item_network'], 'network_name' => network_to_name($item['item_network'], $profile_link), 'linktitle' => sprintf( t('View %s\'s profile @ %s'), $profile_name, ((strlen($item['author-link'])) ? $item['author-link'] : $item['url'])), @@ -795,7 +816,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { 'name' => $profile_name_e, 'sparkle' => $sparkle, 'lock' => $lock, - 'thumb' => App::remove_baseurl(proxy_url($item['author-thumb'], false, PROXY_SIZE_THUMB)), + 'thumb' => System::removedBaseUrl(proxy_url($item['author-thumb'], false, PROXY_SIZE_THUMB)), 'title' => $item['title_e'], 'body' => $body_e, 'tags' => $tags_e, @@ -814,7 +835,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { 'indent' => '', 'owner_name' => $owner_name_e, 'owner_url' => $owner_url, - 'owner_photo' => App::remove_baseurl(proxy_url($item['owner-thumb'], false, PROXY_SIZE_THUMB)), + 'owner_photo' => System::removedBaseUrl(proxy_url($item['owner-thumb'], false, PROXY_SIZE_THUMB)), 'plink' => get_plink($item), 'edpost' => false, 'isstarred' => $isstarred, @@ -843,9 +864,6 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { // Normal View $page_template = get_markup_template("threaded_conversation.tpl"); - require_once 'object/Conversation.php'; - require_once 'object/Item.php'; - $conv = new Conversation($mode, $preview); /* @@ -887,11 +905,11 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { if ($item['id'] == $item['parent']) { $item_object = new Item($item); - $conv->add_thread($item_object); + $conv->addThread($item_object); } } - $threads = $conv->get_template_data($conv_responses); + $threads = $conv->getTemplateData($conv_responses); if (!$threads) { logger('[ERROR] conversation : Failed to get template data.', LOGGER_DEBUG); @@ -901,7 +919,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { } $o = replace_macros($page_template, array( - '$baseurl' => App::get_baseurl($ssl_state), + '$baseurl' => System::baseUrl($ssl_state), '$return_path' => $a->query_string, '$live_update' => $live_update_div, '$remove' => t('remove'), @@ -914,7 +932,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { return $o; }} -function best_link_url($item, &$sparkle, $ssl_state = false) { +function best_link_url($item, &$sparkle, $url = '') { $best_url = ''; $sparkle = false; @@ -922,15 +940,26 @@ function best_link_url($item, &$sparkle, $ssl_state = false) { $clean_url = normalise_link($item['author-link']); if (local_user()) { - $r = q("SELECT `id` FROM `contact` WHERE `network` = '%s' AND `uid` = %d AND `nurl` = '%s' AND NOT `pending` LIMIT 1", - dbesc(NETWORK_DFRN), intval(local_user()), dbesc(normalise_link($clean_url))); - if (dbm::is_result($r)) { - $best_url = 'redir/' . $r[0]['id']; + $r = dba::select('contact', array('id'), + array('network' => NETWORK_DFRN, 'uid' => local_user(), 'nurl' => normalise_link($clean_url), 'pending' => false), + array('limit' => 1)); + if (DBM::is_result($r)) { + $best_url = 'redir/' . $r['id']; $sparkle = true; + if ($url != '') { + $hostname = get_app()->get_hostname(); + if (!strstr($url, $hostname)) { + $best_url .= "?url=".$url; + } else { + $best_url = $url; + } + } } } if (! $best_url) { - if (strlen($item['author-link'])) { + if ($url != '') { + $best_url = $url; + } elseif (strlen($item['author-link'])) { $best_url = $item['author-link']; } else { $best_url = $item['url']; @@ -941,14 +970,7 @@ function best_link_url($item, &$sparkle, $ssl_state = false) { } -if (! function_exists('item_photo_menu')) { function item_photo_menu($item) { - $ssl_state = false; - - if (local_user()) { - $ssl_state = true; - } - $sub_link = ''; $poke_link = ''; $contact_url = ''; @@ -963,7 +985,7 @@ function item_photo_menu($item) { } $sparkle = false; - $profile_link = best_link_url($item, $sparkle, $ssl_state); + $profile_link = best_link_url($item, $sparkle); if ($profile_link === 'mailbox') { $profile_link = ''; } @@ -971,12 +993,11 @@ function item_photo_menu($item) { $cid = 0; $network = ''; $rel = 0; - $r = q("SELECT `id`, `network`, `rel` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' LIMIT 1", - intval(local_user()), dbesc(normalise_link($item['author-link']))); - if (dbm::is_result($r)) { - $cid = $r[0]['id']; - $network = $r[0]['network']; - $rel = $r[0]['rel']; + $r = dba::select('contact', array('id', 'network', 'rel'), array('uid' => local_user(), 'nurl' => normalise_link($item['author-link'])), array('limit' => 1)); + if (DBM::is_result($r)) { + $cid = $r['id']; + $network = $r['network']; + $rel = $r['rel']; } if ($sparkle) { @@ -1013,7 +1034,7 @@ function item_photo_menu($item) { $menu[t("Poke")] = $poke_link; } - if ((($cid == 0) OR ($rel == CONTACT_IS_FOLLOWER)) AND + if ((($cid == 0) || ($rel == CONTACT_IS_FOLLOWER)) && in_array($item['network'], array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA))) { $menu[t('Connect/Follow')] = 'follow?url=' . urlencode($item['author-link']); } @@ -1037,7 +1058,7 @@ function item_photo_menu($item) { } } return $o; -}} +} if (! function_exists('builtin_activity_puller')) { /** @@ -1218,7 +1239,7 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false) { $tpl = get_markup_template('jot-header.tpl'); $a->page['htmlhead'] .= replace_macros($tpl, array( '$newpost' => 'true', - '$baseurl' => App::get_baseurl(true), + '$baseurl' => System::baseUrl(true), '$geotag' => $geotag, '$nickname' => $x['nickname'], '$ispublic' => t('Visible to everybody'), @@ -1234,7 +1255,7 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false) { $tpl = get_markup_template('jot-end.tpl'); $a->page['end'] .= replace_macros($tpl, array( '$newpost' => 'true', - '$baseurl' => App::get_baseurl(true), + '$baseurl' => System::baseUrl(true), '$geotag' => $geotag, '$nickname' => $x['nickname'], '$ispublic' => t('Visible to everybody'), @@ -1302,7 +1323,7 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false) { '$ptyp' => (($notes_cid) ? 'note' : 'wall'), '$content' => $x['content'], '$post_id' => $x['post_id'], - '$baseurl' => App::get_baseurl(true), + '$baseurl' => System::baseUrl(true), '$defloc' => $x['default_location'], '$visitor' => $x['visitor'], '$pvisit' => (($notes_cid) ? 'none' : $x['visitor']), @@ -1340,13 +1361,22 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false) { return $o; } - -function get_item_children($arr, $parent) { - $children = array(); - $a = get_app(); - foreach ($arr as $item) { +/** + * Plucks the children of the given parent from a given item list. + * + * @brief Plucks all the children in the given item list of the given parent + * + * @param array $item_list + * @param array $parent + * @param bool $recursive + * @return type + */ +function get_item_children(array &$item_list, array $parent, $recursive = true) +{ + $children = []; + foreach ($item_list as $i => $item) { if ($item['id'] != $item['parent']) { - if (get_config('system', 'thread_allow') && $a->theme_thread_allow) { + if ($recursive) { // Fallback to parent-uri if thr-parent is not set $thr_parent = $item['thr-parent']; if ($thr_parent == '') { @@ -1354,65 +1384,141 @@ function get_item_children($arr, $parent) { } if ($thr_parent == $parent['uri']) { - $item['children'] = get_item_children($arr, $item); + $item['children'] = get_item_children($item_list, $item); $children[] = $item; + unset($item_list[$i]); } } elseif ($item['parent'] == $parent['id']) { $children[] = $item; + unset($item_list[$i]); } } } return $children; } -/// @TODO Add type-hint -function sort_item_children($items) { +/** + * @brief Recursively sorts a tree-like item array + * + * @param array $items + * @return array + */ +function sort_item_children(array $items) +{ $result = $items; usort($result, 'sort_thr_created_rev'); foreach ($result as $k => $i) { - if (count($result[$k]['children'])) { + if (isset($result[$k]['children'])) { $result[$k]['children'] = sort_item_children($result[$k]['children']); } } return $result; } -/// @TODO Add type-hint -function add_children_to_list($children, &$arr) { - foreach ($children as $y) { - $arr[] = $y; - if (count($y['children'])) { - add_children_to_list($y['children'], $arr); +/** + * @brief Recursively add all children items at the top level of a list + * + * @param array $children List of items to append + * @param array $item_list + */ +function add_children_to_list(array $children, array &$item_list) +{ + foreach ($children as $child) { + $item_list[] = $child; + if (isset($child['children'])) { + add_children_to_list($child['children'], $item_list); } } } -/// @TODO Add type-hint -function conv_sort($arr, $order) { - - if ((!(is_array($arr) && count($arr)))) { - return array(); +/** + * This recursive function takes the item tree structure created by conv_sort() and + * flatten the extraneous depth levels when people reply sequentially, removing the + * stairs effect in threaded conversations limiting the available content width. + * + * The basic principle is the following: if a post item has only one reply and is + * the last reply of its parent, then the reply is moved to the parent. + * + * This process is rendered somewhat more complicated because items can be either + * replies or likes, and these don't factor at all in the reply count/last reply. + * + * @brief Selectively flattens a tree-like item structure to prevent threading stairs + * + * @param array $parent A tree-like array of items + * @return array + */ +function smart_flatten_conversation(array $parent) +{ + if (! isset($parent['children']) || count($parent['children']) == 0) { + return $parent; } - $parents = array(); - $children = array(); - $newarr = array(); + // We use a for loop to ensure we process the newly-moved items + for ($i = 0; $i < count($parent['children']); $i++) { + $child = $parent['children'][$i]; - /* - * This is a preparation for having two different items with the same uri in one thread - * This will otherwise lead to an endless loop. - */ - foreach ($arr as $x) { - if (!isset($newarr[$x['uri']])) { - $newarr[$x['uri']] = $x; + if (isset($child['children']) && count($child['children'])) { + // This helps counting only the regular posts + $count_post_closure = function($var) { + return $var['verb'] === ACTIVITY_POST; + }; + + $child_post_count = count(array_filter($child['children'], $count_post_closure)); + + $remaining_post_count = count(array_filter(array_slice($parent['children'], $i), $count_post_closure)); + + // If there's only one child's children post and this is the last child post + if ($child_post_count == 1 && $remaining_post_count == 1) { + + // Searches the post item in the children + $j = 0; + while($child['children'][$j]['verb'] !== ACTIVITY_POST && $j < count($child['children'])) { + $j ++; + } + + $moved_item = $child['children'][$j]; + unset($parent['children'][$i]['children'][$j]); + $parent['children'][] = $moved_item; + } else { + $parent['children'][$i] = smart_flatten_conversation($child); + } } } - $arr = $newarr; + return $parent; +} + + +/** + * Expands a flat list of items into corresponding tree-like conversation structures, + * sort the top-level posts either on "created" or "commented", and finally + * append all the items at the top level (???) + * + * @brief Expands a flat item list into a conversation array for display + * + * @param array $item_list A list of items belonging to one or more conversations + * @param string $order Either on "created" or "commented" + * @return array + */ +function conv_sort(array $item_list, $order) +{ + $parents = []; + + if (!(is_array($item_list) && count($item_list))) { + return $parents; + } - foreach ($arr as $x) { - if ($x['id'] == $x['parent']) { - $parents[] = $x; + $item_array = []; + + // Dedupes the item list on the uri to prevent infinite loops + foreach ($item_list as $item) { + $item_array[$item['uri']] = $item; + } + + // Extract the top level items + foreach ($item_array as $item) { + if ($item['id'] == $item['parent']) { + $parents[] = $item; } } @@ -1422,73 +1528,75 @@ function conv_sort($arr, $order) { usort($parents, 'sort_thr_commented'); } - if (count($parents)) { - foreach ($parents as $i => $_x) { - $parents[$i]['children'] = get_item_children($arr, $_x); - } + $thread_allowed = Config::get('system', 'thread_allow') && get_app()->theme_thread_allow; + + /* + * Plucks children from the item_array, second pass collects eventual orphan + * items and add them as children of their top-level post. + */ + foreach ($parents as $i => $parent) { + $parents[$i]['children'] = + get_item_children($item_array, $parent, $thread_allowed) + + get_item_children($item_array, $parent, false); } - /// @TODO Old-lost code? - /*foreach ($arr as $x) { - if ($x['id'] != $x['parent']) { - $p = find_thread_parent_index($parents,$x); - if ($p !== false) - $parents[$p]['children'][] = $x; - } - }*/ - if (count($parents)) { - foreach ($parents as $k => $v) { - if (count($parents[$k]['children'])) { - $parents[$k]['children'] = sort_item_children($parents[$k]['children']); - /// @TODO Old-lost code? - /*$y = $parents[$k]['children']; - usort($y,'sort_thr_created_rev'); - $parents[$k]['children'] = $y;*/ - } + foreach ($parents as $i => $parent) { + $parents[$i]['children'] = sort_item_children($parents[$i]['children']); + } + + if ($thread_allowed && PConfig::get(local_user(), 'system', 'smart_threading', 0)) { + foreach ($parents as $i => $parent) { + $parents[$i] = smart_flatten_conversation($parent); } } - $ret = array(); - if (count($parents)) { - foreach ($parents as $x) { - $ret[] = $x; - if (count($x['children'])) { - add_children_to_list($x['children'], $ret); - /// @TODO Old-lost code? - /*foreach ($x['children'] as $y) - $ret[] = $y;*/ - } + /// @TODO: Stop recusrsively adding all children back to the top level (!!!) + /// However, this apparently ensures responses (likes, attendance) display (?!) + foreach ($parents as $parent) { + if (count($parent['children'])) { + add_children_to_list($parent['children'], $parents); } } - return $ret; + return $parents; } -/// @TODO Add type-hint -function sort_thr_created($a, $b) { +/** + * @brief usort() callback to sort item arrays by the created key + * + * @param array $a + * @param array $b + * @return int + */ +function sort_thr_created(array $a, array $b) +{ return strcmp($b['created'], $a['created']); } -/// @TODO Add type-hint -function sort_thr_created_rev($a, $b) { +/** + * @brief usort() callback to reverse sort item arrays by the created key + * + * @param array $a + * @param array $b + * @return int + */ +function sort_thr_created_rev(array $a, array $b) +{ return strcmp($a['created'], $b['created']); } -/// @TODO Add type-hint -function sort_thr_commented($a, $b) { +/** + * @brief usort() callback to sort item arrays by the commented key + * + * @param array $a + * @param array $b + * @return type + */ +function sort_thr_commented(array $a, array $b) +{ return strcmp($b['commented'], $a['commented']); } -/// @TODO Add type-hint -function find_thread_parent_index($arr, $x) { - foreach ($arr as $k => $v) { - if ($v['id'] == $x['parent']) { - return $k; - } - } - return false; -} - /// @TODO Add type-hint function render_location_dummy($item) { if ($item['location'] != "") { @@ -1511,7 +1619,7 @@ function get_responses($conv_responses, $response_verbs, $ob, $item) { if (count($ret[$v]['list']) > MAX_LIKERS) { $ret[$v]['list_part'] = array_slice($ret[$v]['list'], 0, MAX_LIKERS); array_push($ret[$v]['list_part'], '' . t('View all') . ''); + . (($ob) ? $ob->getId() : $item['id']) . '">' . t('View all') . ''); } else { $ret[$v]['list_part'] = ''; }