diff --git a/boot.php b/boot.php index 8c3cd786f5..ba9d3bf496 100644 --- a/boot.php +++ b/boot.php @@ -228,9 +228,10 @@ define('ACCOUNT_TYPE_COMMUNITY', 3); * Type of the community page * @{ */ -define('CP_NO_COMMUNITY_PAGE', -1); +define('CP_NO_COMMUNITY_PAGE', -1); define('CP_USERS_ON_SERVER', 0); define('CP_GLOBAL_COMMUNITY', 1); +define('CP_USERS_AND_GLOBAL', 2); /** * @} */ diff --git a/include/conversation.php b/include/conversation.php index 958d507d78..97630a65fb 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -490,7 +490,6 @@ function item_condition() { * */ function conversation(App $a, $items, $mode, $update, $preview = false) { - require_once 'include/bbcode.php'; require_once 'mod/proxy.php'; @@ -575,10 +574,14 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { . " var profile_page = 1; "; } } elseif ($mode === 'community') { + if (!$community_readonly) { + $items = community_add_items($items); + } $profile_owner = 0; if (!$update) { $live_update_div = '
' . "\r\n" - . "\r\n"; + . "\r\n"; } } elseif ($mode === 'search') { $live_update_div = '' . "\r\n"; @@ -613,14 +616,25 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { $page_template = get_markup_template("conversation.tpl"); if ($items && count($items)) { + $community_readonly = ($mode === 'community'); + // Currently behind a config value. This allows the commenting and sharing of every public item. - if (Config::get('system', 'comment_public') && local_user()) { - $writable = ($items[0]['uid'] == 0) && in_array($items[0]['network'], array(NETWORK_OSTATUS, NETWORK_DIASPORA)); + if (Config::get('system', 'comment_public')) { + if ($mode === 'community') { + $community_readonly = false; + $writable = true; + } else { + $writable = ($items[0]['uid'] == 0) && in_array($items[0]['network'], array(NETWORK_OSTATUS, NETWORK_DIASPORA)); + } } else { $writable = false; } - if ($mode === 'network-new' || $mode === 'search' || $mode === 'community') { + if (!local_user()) { + $writable = false; + } + + if ($mode === 'network-new' || $mode === 'search' || $community_readonly) { /* * "New Item View" on network page or search page results @@ -891,6 +905,55 @@ function conversation(App $a, $items, $mode, $update, $preview = false) { return $o; } +/** + * @brief Add comments to top level entries that had been fetched before + * + * The system will fetch the comments for the local user whenever possible. + * This behaviour is currently needed to allow commenting on Friendica posts. + * + * @param array $parents Parent items + * + * @return array items with parents and comments + */ +function community_add_items($parents) { + $max_comments = Config::get("system", "max_comments", 100); + + $items = array(); + + foreach ($parents AS $parent) { + $thread_items = dba::p(item_query()." AND `item`.`uid` = ? + AND `item`.`parent-uri` = ? + ORDER BY `item`.`commented` DESC LIMIT ".intval($max_comments + 1), + local_user(), + $parent['uri'] + ); + $comments = dba::inArray($thread_items); + + if (count($comments) == 0) { + $thread_items = dba::p(item_query()." AND `item`.`uid` = 0 + AND `item`.`parent-uri` = ? + ORDER BY `item`.`commented` DESC LIMIT ".intval($max_comments + 1), + $parent['uri'] + ); + $comments = dba::inArray($thread_items); + } + + if (count($comments) != 0) { + $items = array_merge($items, $comments); + } + } + + foreach ($items as $index => $item) { + if ($item['uid'] == 0) { + $items[$index]['writable'] = in_array($item['network'], [NETWORK_DIASPORA, NETWORK_OSTATUS]); + } + } + + $items = conv_sort($items, "`commented`"); + + return $items; +} + function best_link_url($item, &$sparkle, $url = '') { $best_url = ''; diff --git a/include/nav.php b/include/nav.php index 58ec150e7a..1bc9a3ae1d 100644 --- a/include/nav.php +++ b/include/nav.php @@ -147,10 +147,10 @@ function nav_info(App $a) if (strlen($gdir)) { $gdirpath = zrl($gdir, true); } - } elseif (Config::get('system', 'community_page_style') == CP_USERS_ON_SERVER) { - $nav['community'] = array('community', t('Community'), '', t('Conversations on this site')); - } elseif (Config::get('system', 'community_page_style') == CP_GLOBAL_COMMUNITY) { - $nav['community'] = array('community', t('Community'), '', t('Conversations on the network')); + } + + if (local_user() || Config::get('system', 'community_page_style') != CP_NO_COMMUNITY_PAGE) { + $nav['community'] = array('community', t('Community'), '', t('Conversations on this and other servers')); } if (local_user()) { @@ -230,6 +230,7 @@ function nav_info(App $a) function nav_set_selected($item){ $a = get_app(); $a->nav_sel = array( + 'global' => null, 'community' => null, 'network' => null, 'home' => null, diff --git a/mod/admin.php b/mod/admin.php index 9408aa31c6..0b44fbd6a8 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -1176,7 +1176,8 @@ function admin_page_site(App $a) $community_page_style_choices = array( CP_NO_COMMUNITY_PAGE => t("No community page"), CP_USERS_ON_SERVER => t("Public postings from users of this site"), - CP_GLOBAL_COMMUNITY => t("Global community page") + CP_GLOBAL_COMMUNITY => t("Public postings from the federated network"), + CP_USERS_AND_GLOBAL => t("Public postings from local users and the federated network") ); /* OStatus conversation poll choices */ @@ -1311,7 +1312,7 @@ function admin_page_site(App $a) '$no_multi_reg' => array('no_multi_reg', t("Block multiple registrations"), Config::get('system','block_extended_register'), t("Disallow users to register additional accounts for use as pages.")), '$no_openid' => array('no_openid', t("OpenID support"), !Config::get('system','no_openid'), t("OpenID support for registration and logins.")), '$no_regfullname' => array('no_regfullname', t("Fullname check"), !Config::get('system','no_regfullname'), t("Force users to register with a space between firstname and lastname in Full name, as an antispam measure")), - '$community_page_style' => array('community_page_style', t("Community Page Style"), Config::get('system','community_page_style'), t("Type of community page to show. 'Global community' shows every public posting from an open distributed network that arrived on this server."), $community_page_style_choices), + '$community_page_style' => array('community_page_style', t("Community pages for visitors"), Config::get('system','community_page_style'), t("Which community pages should be available for visitors. Local users always see both pages."), $community_page_style_choices), '$max_author_posts_community_page' => array('max_author_posts_community_page', t("Posts per user on community page"), Config::get('system','max_author_posts_community_page'), t("The maximum number of posts per user on the community page. (Not valid for 'Global Community')")), '$ostatus_disabled' => array('ostatus_disabled', t("Enable OStatus support"), !Config::get('system','ostatus_disabled'), t("Provide built-in OStatus \x28StatusNet, GNU Social etc.\x29 compatibility. All communications in OStatus are public, so privacy warnings will be occasionally displayed.")), '$ostatus_full_threads' => array('ostatus_full_threads', t("Only import OStatus threads from our contacts"), Config::get('system','ostatus_full_threads'), t("Normally we import every content from our OStatus contacts. With this option we only store threads that are started by a contact that is known on our system.")), diff --git a/mod/community.php b/mod/community.php index df21263278..a75a3cf7dd 100644 --- a/mod/community.php +++ b/mod/community.php @@ -2,10 +2,11 @@ use Friendica\App; use Friendica\Core\Config; +use Friendica\Core\PConfig; use Friendica\Database\DBM; function community_init(App $a) { - if (! local_user()) { + if (!local_user()) { unset($_SESSION['theme']); unset($_SESSION['mobile-theme']); } @@ -14,44 +15,102 @@ function community_init(App $a) { function community_content(App $a, $update = 0) { $o = ''; - if ((Config::get('system','block_public')) && (! local_user()) && (! remote_user())) { - notice( t('Public access denied.') . EOL); + if (Config::get('system','block_public') && !local_user() && !remote_user()) { + notice(t('Public access denied.') . EOL); return; } - if (Config::get('system','community_page_style') == CP_NO_COMMUNITY_PAGE) { - notice( t('Not available.') . EOL); + $page_style = Config::get('system','community_page_style'); + + if ($a->argc > 1) { + $content = $a->argv[1]; + } else { + // When only the global community is allowed, we use this as default + $content = $page_style == CP_GLOBAL_COMMUNITY ? 'global' : 'local'; + } + + if (!in_array($content, ['local', 'global'])) { + notice(t('Community option not available.') . EOL); return; } - require_once("include/bbcode.php"); - require_once('include/security.php'); - require_once('include/conversation.php'); + // Check if we are allowed to display the content to visitors + if (!local_user()) { + $available = $page_style == CP_USERS_AND_GLOBAL; + + if (!$available) { + $available = ($page_style == CP_USERS_ON_SERVER) && ($content == 'local'); + } + + if (!$available) { + $available = ($page_style == CP_GLOBAL_COMMUNITY) && ($content == 'global'); + } + + if (!$available) { + notice(t('Not available.') . EOL); + return; + } + } + + require_once 'include/bbcode.php'; + require_once 'include/security.php'; + require_once 'include/conversation.php'; + + if (!$update) { + $tabs = []; + + if (local_user() || in_array($page_style, [CP_USERS_AND_GLOBAL, CP_USERS_ON_SERVER])) { + $tabs[] = array('label'=>t('Community'), + 'url' => 'community/local', + 'sel' => $content == 'local' ? 'active' : '', + 'title' => t('Posts from local users on this server'), + 'id' => 'community-local-tab', + 'accesskey' => 'l'); + } + + if (local_user() || in_array($page_style, [CP_USERS_AND_GLOBAL, CP_GLOBAL_COMMUNITY])) { + $tabs[] = array('label' => t('Global Timeline'), + 'url' => 'community/global', + 'sel' => $content == 'global' ? 'active' : '', + 'title' => t('Posts from users of the federated network'), + 'id' => 'community-global-tab', + 'accesskey' => 'g'); + } + + $tab_tpl = get_markup_template('common_tabs.tpl'); + $o .= replace_macros($tab_tpl, array('$tabs' => $tabs)); - if (! $update) { nav_set_selected('community'); } - if (x($a->data,'search')) { - $search = notags(trim($a->data['search'])); - } else { - $search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : ''); + if (Config::get('system', 'comment_public')) { + // check if we serve a mobile device and get the user settings + // accordingly + if ($a->is_mobile) { + $itemspage_network = PConfig::get(local_user(),'system','itemspage_mobile_network', 20); + } else { + $itemspage_network = PConfig::get(local_user(),'system','itemspage_network', 40); + } + + // now that we have the user settings, see if the theme forces + // a maximum item number which is lower then the user choice + if (($a->force_max_items > 0) && ($a->force_max_items < $itemspage_network)) { + $itemspage_network = $a->force_max_items; + } + + $a->set_pager_itemspage($itemspage_network); } - // Here is the way permissions work in this module... - // Only public posts can be shown - // OR your own posts if you are a logged in member + $r = community_getitems($a->pager['start'], $a->pager['itemspage'], $content); - $r = community_getitems($a->pager['start'], $a->pager['itemspage']); - - if (! DBM::is_result($r)) { - info( t('No results.') . EOL); + if (!DBM::is_result($r)) { + info(t('No results.') . EOL); return $o; } $maxpostperauthor = Config::get('system','max_author_posts_community_page'); - if ($maxpostperauthor != 0) { + if (($maxpostperauthor != 0) && ($content == 'local')) { $count = 1; $previousauthor = ""; $numposts = 0; @@ -70,53 +129,52 @@ function community_content(App $a, $update = 0) { $s[] = $item; } } - if ((sizeof($s) < $a->pager['itemspage'])) { - $r = community_getitems($a->pager['start'] + ($count * $a->pager['itemspage']), $a->pager['itemspage']); + if (sizeof($s) < $a->pager['itemspage']) { + $r = community_getitems($a->pager['start'] + ($count * $a->pager['itemspage']), $a->pager['itemspage'], $content); } } while ((sizeof($s) < $a->pager['itemspage']) && (++$count < 50) && (sizeof($r) > 0)); } else { $s = $r; } - // we behave the same in message lists as the search module $o .= conversation($a, $s, 'community', $update); - $o .= alt_pager($a, count($r)); + if (!$update) { + $o .= alt_pager($a, count($r)); + } $t = get_markup_template("community.tpl"); return replace_macros($t, array( '$content' => $o, - '$header' => t("Community"), - '$show_global_community_hint' => (Config::get('system', 'community_page_style') == CP_GLOBAL_COMMUNITY && Config::get('system', 'show_global_community_hint')), + '$header' => '', + '$show_global_community_hint' => ($content == 'global') && Config::get('system', 'show_global_community_hint'), '$global_community_hint' => t("This community stream shows all public posts received by this node. They may not reflect the opinions of this node’s users.") )); } -function community_getitems($start, $itemspage) { - if (Config::get('system','community_page_style') == CP_GLOBAL_COMMUNITY) { - return(community_getpublicitems($start, $itemspage)); +function community_getitems($start, $itemspage, $content) { + if ($content == 'local') { + $r = dba::p("SELECT ".item_fieldlists()." FROM `thread` + INNER JOIN `user` ON `user`.`uid` = `thread`.`uid` AND NOT `user`.`hidewall` + INNER JOIN `item` ON `item`.`id` = `thread`.`iid` + AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' + AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = ''". + item_joins()." AND `contact`.`self` + WHERE `thread`.`visible` AND NOT `thread`.`deleted` AND NOT `thread`.`moderated` + AND NOT `thread`.`private` AND `thread`.`wall` + ORDER BY `thread`.`received` DESC LIMIT ".intval($start).", ".intval($itemspage) + ); + return dba::inArray($r); + } elseif ($content == 'global') { + $r = dba::p("SELECT ".item_fieldlists()." FROM `thread` + INNER JOIN `item` ON `item`.`id` = `thread`.`iid` ".item_joins(). + "WHERE `thread`.`uid` = 0 AND `verb` = ? + ORDER BY `thread`.`created` DESC LIMIT ".intval($start).", ".intval($itemspage), + ACTIVITY_POST + ); + return dba::inArray($r); } - $r = dba::p("SELECT ".item_fieldlists()." FROM `thread` - INNER JOIN `user` ON `user`.`uid` = `thread`.`uid` AND NOT `user`.`hidewall` - INNER JOIN `item` ON `item`.`id` = `thread`.`iid` - AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' - AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = ''". - item_joins()." AND `contact`.`self` - WHERE `thread`.`visible` AND NOT `thread`.`deleted` AND NOT `thread`.`moderated` - AND NOT `thread`.`private` AND `thread`.`wall` - ORDER BY `thread`.`received` DESC LIMIT ".intval($start).", ".intval($itemspage) - ); - return dba::inArray($r); -} - -function community_getpublicitems($start, $itemspage) { - $r = dba::p("SELECT ".item_fieldlists()." FROM `thread` - INNER JOIN `item` ON `item`.`id` = `thread`.`iid` ".item_joins(). - "WHERE `thread`.`uid` = 0 AND `verb` = ? - ORDER BY `thread`.`created` DESC LIMIT ".intval($start).", ".intval($itemspage), - ACTIVITY_POST - ); - - return dba::inArray($r); + // Should never happen + return array(); } diff --git a/mod/update_community.php b/mod/update_community.php index c0e4c33238..037c724891 100644 --- a/mod/update_community.php +++ b/mod/update_community.php @@ -8,12 +8,16 @@ use Friendica\Core\PConfig; require_once("mod/community.php"); function update_community_content(App $a) { - header("Content-type: text/html"); echo "\r\n"; echo "