From 2968bf8241d2969c4d51f1651fc3f8c7688b2fca Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 15 Dec 2021 12:17:19 +0000 Subject: merge branch perms_ng into dev --- Zotlabs/Lib/AccessList.php | 239 +++++++++++++------------- Zotlabs/Lib/Activity.php | 30 ++-- Zotlabs/Lib/Connect.php | 5 +- Zotlabs/Lib/Enotify.php | 2 +- Zotlabs/Lib/Group.php | 405 --------------------------------------------- Zotlabs/Lib/Libzot.php | 43 ++--- Zotlabs/Lib/Permcat.php | 130 +++++++++++++-- Zotlabs/Lib/ThreadItem.php | 31 +++- 8 files changed, 308 insertions(+), 577 deletions(-) delete mode 100644 Zotlabs/Lib/Group.php (limited to 'Zotlabs/Lib') diff --git a/Zotlabs/Lib/AccessList.php b/Zotlabs/Lib/AccessList.php index 3c008f8c7..51c100afb 100644 --- a/Zotlabs/Lib/AccessList.php +++ b/Zotlabs/Lib/AccessList.php @@ -1,38 +1,37 @@ -may apply to this list and any future members. If this is not what you intended, please create another list with a different name.') . EOL); + notice(t('A deleted list with this name was revived. Existing item permissions may apply to this list and any future members. If this is not what you intended, please create another list with a different name.') . EOL); } - return true; + $hash = self::by_id($uid, $r); + return $hash; } $hash = new_uuid(); - $r = q("INSERT INTO pgrp ( hash, uid, visible, gname ) + $r = q("INSERT INTO pgrp ( hash, uid, visible, gname ) VALUES( '%s', %d, %d, '%s' ) ", dbesc($hash), intval($uid), @@ -42,12 +41,12 @@ class AccessList { $ret = $r; } - Libsync::build_sync_packet($uid,null,true); - return $ret; - } + Libsync::build_sync_packet($uid, null, true); + return (($ret) ? $hash : $ret); + } - static function remove($uid,$name) { + static function remove($uid, $name) { $ret = false; if ($uid && $name) { $r = q("SELECT id, hash FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1", @@ -55,36 +54,36 @@ class AccessList { dbesc($name) ); if ($r) { - $group_id = $r[0]['id']; + $group_id = $r[0]['id']; $group_hash = $r[0]['hash']; } else { return false; } - + // remove group from default posting lists $r = q("SELECT channel_default_group, channel_allow_gid, channel_deny_gid FROM channel WHERE channel_id = %d LIMIT 1", - intval($uid) + intval($uid) ); if ($r) { $user_info = array_shift($r); - $change = false; + $change = false; if ($user_info['channel_default_group'] == $group_hash) { $user_info['channel_default_group'] = ''; - $change = true; + $change = true; } if (strpos($user_info['channel_allow_gid'], '<' . $group_hash . '>') !== false) { $user_info['channel_allow_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_allow_gid']); - $change = true; + $change = true; } if (strpos($user_info['channel_deny_gid'], '<' . $group_hash . '>') !== false) { $user_info['channel_deny_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_deny_gid']); - $change = true; + $change = true; } if ($change) { - q("UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s' + q("UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s' WHERE channel_id = %d", intval($user_info['channel_default_group']), dbesc($user_info['channel_allow_gid']), @@ -110,16 +109,16 @@ class AccessList { } - Libsync::build_sync_packet($uid,null,true); + Libsync::build_sync_packet($uid, null, true); return $ret; } // returns the integer id of an access group owned by $uid and named $name // or false. - - static function byname($uid,$name) { - if (! ($uid && $name)) { + + static function by_name($uid, $name) { + if (!($uid && $name)) { return false; } $r = q("SELECT id FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1", @@ -132,11 +131,11 @@ class AccessList { return false; } - static function by_id($uid,$id) { - if (! ($uid && $id)) { + static function by_id($uid, $id) { + if (!($uid && $id)) { return false; } - + $r = q("SELECT * FROM pgrp WHERE uid = %d AND id = %d and deleted = 0", intval($uid), intval($id) @@ -147,10 +146,8 @@ class AccessList { return false; } - - - static function rec_byhash($uid,$hash) { - if (! ( $uid && $hash)) { + static function by_hash($uid, $hash) { + if (!($uid && $hash)) { return false; } $r = q("SELECT * FROM pgrp WHERE uid = %d AND hash = '%s' LIMIT 1", @@ -163,46 +160,43 @@ class AccessList { return false; } + static function member_remove($uid, $name, $member) { + $gid = self::by_name($uid, $name); - static function member_remove($uid,$name,$member) { - $gid = self::byname($uid,$name); - if (! $gid) { - return false; - } - if (! ($uid && $gid && $member)) { + if (!($uid && $gid && $member)) { return false; } + $r = q("DELETE FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' ", intval($uid), intval($gid), dbesc($member) ); - Libsync::build_sync_packet($uid,null,true); + Libsync::build_sync_packet($uid, null, true); return $r; } - - static function member_add($uid,$name,$member,$gid = 0) { - if (! $gid) { - $gid = self::byname($uid,$name); + static function member_add($uid, $name, $member, $gid = 0) { + if (!$gid) { + $gid = self::by_name($uid, $name); } - if (! ($gid && $uid && $member)) { + if (!($gid && $uid && $member)) { return false; } - $r = q("SELECT * FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1", + $r = q("SELECT * FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1", intval($uid), intval($gid), dbesc($member) ); if ($r) { - return true; // You might question this, but - // we indicate success because the group member was in fact created - // -- It was just created at another time + return true; // You might question this, but + // we indicate success because the group member was in fact created + // -- It was just created at another time } - else { + else { $r = q("INSERT INTO pgrp_member (uid, gid, xchan) VALUES( %d, %d, '%s' ) ", intval($uid), @@ -210,15 +204,14 @@ class AccessList { dbesc($member) ); } - Libsync::build_sync_packet($uid,null,true); + Libsync::build_sync_packet($uid, null, true); return $r; } - static function members($uid, $gid) { $ret = []; if (intval($gid)) { - $r = q("SELECT * FROM pgrp_member + $r = q("SELECT * FROM pgrp_member LEFT JOIN abook ON abook_xchan = pgrp_member.xchan left join xchan on xchan_hash = abook_xchan WHERE gid = %d AND abook_channel = %d and pgrp_member.uid = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC ", intval($gid), @@ -232,7 +225,7 @@ class AccessList { return $ret; } - static function members_xchan($uid,$gid) { + static function members_xchan($uid, $gid) { $ret = []; if (intval($gid)) { $r = q("SELECT xchan FROM pgrp_member WHERE gid = %d AND uid = %d", @@ -248,99 +241,123 @@ class AccessList { return $ret; } - static function members_profile_xchan($uid,$gid) { + static function profile_members_xchan($uid,$gid) { $ret = []; - if (intval($gid)) { + + if(intval($gid)) { $r = q("SELECT abook_xchan as xchan from abook left join profile on abook_profile = profile_guid where profile.id = %d and profile.uid = %d", intval($gid), intval($uid) ); - if ($r) { - foreach($r as $rv) { - $ret[] = $rv['xchan']; + if($r) { + foreach($r as $rr) { + $ret[] = $rr['xchan']; } } } return $ret; } + static function select($uid, $options) { + $selected = $options['selected'] ?? ''; + $form_id = $options['form_id'] ?? 'accesslist_select'; + $label = $options['label'] ?? t('Select a privacy group'); + $before = $options['before'] ?? []; + $after = $options['after'] ?? []; - - static function select($uid,$group = '') { - $grps = []; + $o = ''; + + $grps[] = [ + 'name' => '', + 'id' => '0', + 'selected' => false + ]; + + if ($before) { + $grps[] = $before; + } $r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", intval($uid) ); - $grps[] = [ 'name' => '', 'hash' => '0', 'selected' => '' ]; - if ($r) { - foreach ($r as $rr) { - $grps[] = [ 'name' => $rr['gname'], 'id' => $rr['hash'], 'selected' => (($group == $rr['hash']) ? 'true' : '') ]; + + if($r) { + foreach($r as $rr) { + $grps[] = [ + 'name' => $rr['gname'], + 'id' => $rr['hash'], + 'selected' => ($selected == $rr['hash']) + ]; } + } + if ($after) { + $grps[] = $after; } - - return replace_macros(get_markup_template('group_selection.tpl'), [ - '$label' => t('Add new connections to this access list'), - '$groups' => $grps - ]); - } + logger('select: ' . print_r($grps,true), LOGGER_DATA); - static function widget($every="connections",$each="lists",$edit = false, $group_id = 0, $cid = '',$mode = 1) { + $o = replace_macros(get_markup_template('group_selection.tpl'), array( + '$label' => $label, + '$form_id' => $form_id, + '$groups' => $grps + )); + + return $o; + } - $o = ''; + + static function widget($every = "connections", $each = "lists", $edit = false, $group_id = 0, $cid = '', $mode = 1) { $groups = []; - $r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", + $r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", intval($_SESSION['uid']) ); $member_of = []; if ($cid) { - $member_of = self::containing(local_channel(),$cid); - } + $member_of = self::containing(local_channel(), $cid); + } if ($r) { foreach ($r as $rr) { $selected = (($group_id == $rr['id']) ? ' group-selected' : ''); - + if ($edit) { - $groupedit = [ 'href' => "lists/".$rr['id'], 'title' => t('edit') ]; - } + $groupedit = ['href' => "lists/" . $rr['id'], 'title' => t('edit')]; + } else { $groupedit = null; } - + $groups[] = [ - 'id' => $rr['id'], - 'enc_cid' => base64url_encode($cid), - 'cid' => $cid, - 'text' => $rr['gname'], - 'selected' => $selected, - 'href' => (($mode == 0) ? $each.'?f=&gid='.$rr['id'] : $each."/".$rr['id']) . ((x($_GET,'new')) ? '&new=' . $_GET['new'] : '') . ((x($_GET,'order')) ? '&order=' . $_GET['order'] : ''), - 'edit' => $groupedit, - 'ismember' => in_array($rr['id'],$member_of), + 'id' => $rr['id'], + 'enc_cid' => base64url_encode($cid), + 'cid' => $cid, + 'text' => $rr['gname'], + 'selected' => $selected, + 'href' => (($mode == 0) ? $each . '?f=&gid=' . $rr['id'] : $each . "/" . $rr['id']) . ((x($_GET, 'new')) ? '&new=' . $_GET['new'] : '') . ((x($_GET, 'order')) ? '&order=' . $_GET['order'] : ''), + 'edit' => $groupedit, + 'ismember' => in_array($rr['id'], $member_of), ]; } } - + return replace_macros(get_markup_template('group_side.tpl'), [ - '$title' => t('Lists'), - '$edittext' => t('Edit list'), - '$createtext' => t('Create new list'), - '$ungrouped' => (($every === 'contacts') ? t('Channels not in any access list') : ''), - '$groups' => $groups, - '$add' => t('add'), + '$title' => t('Lists'), + '$edittext' => t('Edit list'), + '$createtext' => t('Create new list'), + '$ungrouped' => (($every === 'contacts') ? t('Channels not in any access list') : ''), + '$groups' => $groups, + '$add' => t('add'), ]); } - static function expand($g) { - if (! (is_array($g) && count($g))) { + if (!(is_array($g) && count($g))) { return []; } @@ -350,8 +367,8 @@ class AccessList { // private profile linked virtual groups foreach ($g as $gv) { - if (substr($gv,0,3) === 'vp.') { - $profile_hash = substr($gv,3); + if (substr($gv, 0, 3) === 'vp.') { + $profile_hash = substr($gv, 3); if ($profile_hash) { $r = q("select abook_xchan from abook where abook_profile = '%s'", dbesc($profile_hash) @@ -366,10 +383,10 @@ class AccessList { else { $x[] = $gv; } - } + } if ($x) { - stringify_array_elms($x,true); + stringify_array_elms($x, true); $groups = implode(',', $x); if ($groups) { $r = q("SELECT xchan FROM pgrp_member WHERE gid IN ( select id from pgrp where hash in ( $groups ))"); @@ -383,9 +400,8 @@ class AccessList { return $ret; } - static function member_of($c) { - $r = q("SELECT pgrp.gname, pgrp.id FROM pgrp LEFT JOIN pgrp_member ON pgrp_member.gid = pgrp.id + $r = q("SELECT pgrp.gname, pgrp.id FROM pgrp LEFT JOIN pgrp_member ON pgrp_member.gid = pgrp.id WHERE pgrp_member.xchan = '%s' AND pgrp.deleted = 0 ORDER BY pgrp.gname ASC ", dbesc($c) ); @@ -393,7 +409,7 @@ class AccessList { return $r; } - static function containing($uid,$c) { + static function containing($uid, $c) { $r = q("SELECT gid FROM pgrp_member WHERE uid = %d AND pgrp_member.xchan = '%s' ", intval($uid), @@ -405,7 +421,8 @@ class AccessList { foreach ($r as $rv) $ret[] = $rv['gid']; } - + return $ret; } -} \ No newline at end of file + +} diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 664886fc2..8e5a4e1a6 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -886,10 +886,6 @@ class Activity { else return []; - if (strpos($i['body'], '[/share]') !== false) { - $i['obj'] = null; - } - if ($i['obj']) { if (!is_array($i['obj'])) { $i['obj'] = json_decode($i['obj'], true); @@ -899,8 +895,10 @@ class Activity { } $obj = self::encode_object($i['obj']); - if ($obj) + + if ($obj) { $ret['object'] = $obj; + } else return []; } @@ -1042,7 +1040,7 @@ class Activity { $tmp = expand_acl($i['allow_cid']); $list = stringify_array($tmp, true); if ($list) { - $details = q("select hubloc_id_url from hubloc where hubloc_hash in (" . $list . ") and hubloc_id_url != ''"); + $details = q("select hubloc_id_url from hubloc where hubloc_hash in (" . $list . ") and hubloc_id_url != '' and hubloc_deleted = 0"); if ($details) { foreach ($details as $d) { $ret[] = $d['hubloc_id_url']; @@ -1089,10 +1087,11 @@ class Activity { $ret['type'] = 'Person'; if ($c) { - $role = get_pconfig($c['channel_id'], 'system', 'permissions_role'); - if (strpos($role, 'forum') !== false) { + if (get_pconfig($c['channel_id'], 'system', 'group_actor')) { $ret['type'] = 'Group'; } + + $ret['manuallyApprovesFollowers'] = ((get_pconfig($c['channel_id'], 'system', 'autoperms')) ? false : true); } if ($c) { @@ -1554,9 +1553,9 @@ class Activity { /* If there is a default group for this channel and permissions are automatic, add this member to it */ if ($channel['channel_default_group'] && $automatic) { - $g = Group::rec_byhash($channel['channel_id'], $channel['channel_default_group']); + $g = AccessList::by_hash($channel['channel_id'], $channel['channel_default_group']); if ($g) - Group::member_add($channel['channel_id'], '', $ret['xchan_hash'], $g['id']); + AccessList::member_add($channel['channel_id'], '', $ret['xchan_hash'], $g['id']); } @@ -2692,6 +2691,17 @@ class Activity { // set the owner to the owner of the parent $item['owner_xchan'] = $p[0]['owner_xchan']; + // quietly reject group comment boosts by group owner + // (usually only sent via ActivityPub so groups will work on microblog platforms) + // This catches those activities if they slipped in via a conversation fetch + + if ($p[0]['parent_mid'] !== $item['parent_mid']) { + if ($item['verb'] === 'Announce' && $item['author_xchan'] === $item['owner_xchan']) { + logger('group boost activity by group owner rejected'); + return; + } + } + // check permissions against the author, not the sender $allowed = perm_is_allowed($channel['channel_id'], $item['author_xchan'], 'post_comments'); if ((!$allowed)/* && $permit_mentions*/) { diff --git a/Zotlabs/Lib/Connect.php b/Zotlabs/Lib/Connect.php index 38fe69995..0b9ff7089 100644 --- a/Zotlabs/Lib/Connect.php +++ b/Zotlabs/Lib/Connect.php @@ -261,7 +261,8 @@ class Connect { 'abook_feed' => intval(($xchan['xchan_network'] === 'rss') ? 1 : 0), 'abook_created' => datetime_convert(), 'abook_updated' => datetime_convert(), - 'abook_instance' => (($singleton) ? z_root() : '') + 'abook_instance' => (($singleton) ? z_root() : ''), + 'abook_role' => get_pconfig($uid, 'system', 'default_permcat', 'default') ] ); } @@ -300,7 +301,7 @@ class Connect { /** If there is a default group for this channel, add this connection to it */ if ($default_group) { - $g = AccessList::rec_byhash($uid,$default_group); + $g = AccessList::by_hash($uid,$default_group); if ($g) { AccessList::member_add($uid,'',$xchan_hash,$g['id']); } diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php index fdc7d4567..2e483cb92 100644 --- a/Zotlabs/Lib/Enotify.php +++ b/Zotlabs/Lib/Enotify.php @@ -902,7 +902,7 @@ class Enotify { static public function format_intros($rr) { return [ - 'notify_link' => z_root() . '/connections/ifpending', + 'notify_link' => z_root() . '/connections#' . $rr['abook_id'], 'name' => $rr['xchan_name'], 'addr' => $rr['xchan_addr'], 'url' => $rr['xchan_url'], diff --git a/Zotlabs/Lib/Group.php b/Zotlabs/Lib/Group.php deleted file mode 100644 index a4ff4fced..000000000 --- a/Zotlabs/Lib/Group.php +++ /dev/null @@ -1,405 +0,0 @@ -may apply to this group and any future members. If this is not what you intended, please create another group with a different name.') . EOL); - } - return true; - } - - do { - $dups = false; - $hash = random_string(32) . str_replace(['<','>'],['.','.'], $name); - - $r = q("SELECT id FROM pgrp WHERE hash = '%s' LIMIT 1", dbesc($hash)); - if($r) - $dups = true; - } while($dups == true); - - - $r = q("INSERT INTO pgrp ( hash, uid, visible, gname ) - VALUES( '%s', %d, %d, '%s' ) ", - dbesc($hash), - intval($uid), - intval($public), - dbesc($name) - ); - $ret = $r; - } - - Libsync::build_sync_packet($uid,null,true); - return $ret; - } - - - static function remove($uid,$name) { - $ret = false; - if(x($uid) && x($name)) { - $r = q("SELECT id, hash FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1", - intval($uid), - dbesc($name) - ); - if($r) { - $group_id = $r[0]['id']; - $group_hash = $r[0]['hash']; - } - - if(! $group_id) - return false; - - // remove group from default posting lists - $r = q("SELECT channel_default_group, channel_allow_gid, channel_deny_gid FROM channel WHERE channel_id = %d LIMIT 1", - intval($uid) - ); - if($r) { - $user_info = $r[0]; - $change = false; - - if($user_info['channel_default_group'] == $group_hash) { - $user_info['channel_default_group'] = ''; - $change = true; - } - if(strpos($user_info['channel_allow_gid'], '<' . $group_hash . '>') !== false) { - $user_info['channel_allow_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_allow_gid']); - $change = true; - } - if(strpos($user_info['channel_deny_gid'], '<' . $group_hash . '>') !== false) { - $user_info['channel_deny_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_deny_gid']); - $change = true; - } - - if($change) { - q("UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s' - WHERE channel_id = %d", - intval($user_info['channel_default_group']), - dbesc($user_info['channel_allow_gid']), - dbesc($user_info['channel_deny_gid']), - intval($uid) - ); - } - } - - // remove all members - $r = q("DELETE FROM pgrp_member WHERE uid = %d AND gid = %d ", - intval($uid), - intval($group_id) - ); - - // remove group - $r = q("UPDATE pgrp SET deleted = 1 WHERE uid = %d AND gname = '%s'", - intval($uid), - dbesc($name) - ); - - $ret = $r; - - } - - Libsync::build_sync_packet($uid,null,true); - - return $ret; - } - - - static function byname($uid,$name) { - if((! $uid) || (! strlen($name))) - return false; - $r = q("SELECT * FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1", - intval($uid), - dbesc($name) - ); - if($r) - return $r[0]['id']; - return false; - } - - - static function rec_byhash($uid,$hash) { - if((! $uid) || (! strlen($hash))) - return false; - $r = q("SELECT * FROM pgrp WHERE uid = %d AND hash = '%s' LIMIT 1", - intval($uid), - dbesc($hash) - ); - if($r) - return $r[0]; - return false; - } - - - static function member_remove($uid,$name,$member) { - $gid = self::byname($uid,$name); - if(! $gid) - return false; - if(! ( $uid && $gid && $member)) - return false; - $r = q("DELETE FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' ", - intval($uid), - intval($gid), - dbesc($member) - ); - - Libsync::build_sync_packet($uid,null,true); - - return $r; - } - - - static function member_add($uid,$name,$member,$gid = 0) { - if(! $gid) - $gid = self::byname($uid,$name); - if((! $gid) || (! $uid) || (! $member)) - return false; - - $r = q("SELECT * FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1", - intval($uid), - intval($gid), - dbesc($member) - ); - if($r) - return true; // You might question this, but - // we indicate success because the group member was in fact created - // -- It was just created at another time - if(! $r) - $r = q("INSERT INTO pgrp_member (uid, gid, xchan) - VALUES( %d, %d, '%s' ) ", - intval($uid), - intval($gid), - dbesc($member) - ); - - Libsync::build_sync_packet($uid,null,true); - - return $r; - } - - - static function members($gid) { - $ret = array(); - if(intval($gid)) { - $r = q("SELECT * FROM pgrp_member - LEFT JOIN abook ON abook_xchan = pgrp_member.xchan left join xchan on xchan_hash = abook_xchan - WHERE gid = %d AND abook_channel = %d and pgrp_member.uid = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC ", - intval($gid), - intval(local_channel()), - intval(local_channel()) - ); - if($r) - $ret = $r; - } - return $ret; - } - - static function members_xchan($gid) { - $ret = []; - if(intval($gid)) { - $r = q("SELECT xchan FROM pgrp_member WHERE gid = %d AND uid = %d", - intval($gid), - intval(local_channel()) - ); - if($r) { - foreach($r as $rr) { - $ret[] = $rr['xchan']; - } - } - } - return $ret; - } - - static function members_profile_xchan($uid,$gid) { - $ret = []; - - if(intval($gid)) { - $r = q("SELECT abook_xchan as xchan from abook left join profile on abook_profile = profile_guid where profile.id = %d and profile.uid = %d", - intval($gid), - intval($uid) - ); - if($r) { - foreach($r as $rr) { - $ret[] = $rr['xchan']; - } - } - } - return $ret; - } - - - - - static function select($uid,$group = '') { - - $grps = []; - $o = ''; - - $r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", - intval($uid) - ); - $grps[] = array('name' => '', 'hash' => '0', 'selected' => ''); - if($r) { - foreach($r as $rr) { - $grps[] = array('name' => $rr['gname'], 'id' => $rr['hash'], 'selected' => (($group == $rr['hash']) ? 'true' : '')); - } - - } - logger('select: ' . print_r($grps,true), LOGGER_DATA); - - $o = replace_macros(get_markup_template('group_selection.tpl'), array( - '$label' => t('Add new connections to this privacy group'), - '$groups' => $grps - )); - return $o; - } - - - - - static function widget($every="connections",$each="group",$edit = false, $group_id = 0, $cid = '',$mode = 1) { - - $o = ''; - - if(! (local_channel() && feature_enabled(local_channel(),'groups'))) { - return ''; - } - - $groups = array(); - - $r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", - intval($_SESSION['uid']) - ); - $member_of = array(); - if($cid) { - $member_of = self::containing(local_channel(),$cid); - } - - if($r) { - foreach($r as $rr) { - $selected = (($group_id == $rr['id']) ? ' group-selected' : ''); - - if ($edit) { - $groupedit = [ 'href' => "group/".$rr['id'], 'title' => t('edit') ]; - } - else { - $groupedit = null; - } - - $groups[] = [ - 'id' => $rr['id'], - 'enc_cid' => base64url_encode($cid), - 'cid' => $cid, - 'text' => $rr['gname'], - 'selected' => $selected, - 'href' => (($mode == 0) ? $each.'?f=&gid='.$rr['id'] : $each."/".$rr['id']) . ((x($_GET,'new')) ? '&new=' . $_GET['new'] : '') . ((x($_GET,'order')) ? '&order=' . $_GET['order'] : ''), - 'edit' => $groupedit, - 'ismember' => in_array($rr['id'],$member_of), - ]; - } - } - - - $tpl = get_markup_template("group_side.tpl"); - $o = replace_macros($tpl, array( - '$title' => t('Privacy Groups'), - '$edittext' => t('Edit group'), - '$createtext' => t('Add privacy group'), - '$ungrouped' => (($every === 'contacts') ? t('Channels not in any privacy group') : ''), - '$groups' => $groups, - '$add' => t('add'), - )); - - - return $o; - } - - - static function expand($g) { - if(! (is_array($g) && count($g))) - return array(); - - $ret = []; - $x = []; - - // private profile linked virtual groups - - foreach($g as $gv) { - if(substr($gv,0,3) === 'vp.') { - $profile_hash = substr($gv,3); - if($profile_hash) { - $r = q("select abook_xchan from abook where abook_profile = '%s'", - dbesc($profile_hash) - ); - if($r) { - foreach($r as $rv) { - $ret[] = $rv['abook_xchan']; - } - } - } - } - else { - $x[] = $gv; - } - } - - if($x) { - stringify_array_elms($x,true); - $groups = implode(',', $x); - if($groups) { - $r = q("SELECT xchan FROM pgrp_member WHERE gid IN ( select id from pgrp where hash in ( $groups ))"); - if($r) { - foreach($r as $rr) { - $ret[] = $rr['xchan']; - } - } - } - } - return $ret; - } - - - static function member_of($c) { - $r = q("SELECT pgrp.gname, pgrp.id FROM pgrp LEFT JOIN pgrp_member ON pgrp_member.gid = pgrp.id WHERE pgrp_member.xchan = '%s' AND pgrp.deleted = 0 ORDER BY pgrp.gname ASC ", - dbesc($c) - ); - - return $r; - - } - - static function containing($uid,$c) { - - $r = q("SELECT gid FROM pgrp_member WHERE uid = %d AND pgrp_member.xchan = '%s' ", - intval($uid), - dbesc($c) - ); - - $ret = array(); - if($r) { - foreach($r as $rr) - $ret[] = $rr['gid']; - } - - return $ret; - } -} \ No newline at end of file diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index 31b8f04de..e2cbc66e6 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -386,9 +386,10 @@ class Libzot { else { $p = Permissions::connect_perms($channel['channel_id']); - $my_perms = $p['perms']; + $my_perms = $p['perms']; $automatic = $p['automatic']; + $role = (($automatic) ? $p['role'] : ''); // new connection @@ -410,7 +411,8 @@ class Libzot { 'abook_created' => datetime_convert(), 'abook_updated' => datetime_convert(), 'abook_dob' => $next_birthday, - 'abook_pending' => intval(($automatic) ? 0 : 1) + 'abook_pending' => intval(($automatic) ? 0 : 1), + 'abook_role' => $role ] ); @@ -435,7 +437,7 @@ class Libzot { 'type' => NOTIFY_INTRO, 'from_xchan' => $x['hash'], 'to_xchan' => $channel['channel_hash'], - 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'] + 'link' => z_root() . '/connections#' . $new_connection[0]['abook_id'] ] ); @@ -453,10 +455,10 @@ class Libzot { $default_group = $channel['channel_default_group']; if ($default_group) { - $g = Group::rec_byhash($channel['channel_id'], $default_group); + $g = AccessList::by_hash($channel['channel_id'], $default_group); if ($g) { - Group::member_add($channel['channel_id'], '', $x['hash'], $g['id']); + AccessList::member_add($channel['channel_id'], '', $x['hash'], $g['id']); } } } @@ -1143,6 +1145,7 @@ class Libzot { if ($env['encoding'] === 'activitystreams') { $AS = new ActivityStreams($data); + if (!$AS->is_valid()) { logger('Activity rejected: ' . print_r($data, true)); return; @@ -1158,8 +1161,6 @@ class Libzot { } - - $deliveries = null; if (array_key_exists('recipients', $env) && count($env['recipients'])) { @@ -1592,6 +1593,7 @@ class Libzot { if ((!$tag_delivery) && (!$local_public)) { $allowed = (perm_is_allowed($channel['channel_id'], $sender, $perm)); + if ((!$allowed) && $perm === 'post_comments') { $parent = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($arr['parent_mid']), @@ -2785,28 +2787,6 @@ class Libzot { if ($deleted || $censored || $sys_channel) $searchable = false; - $public_forum = false; - - $role = get_pconfig($e['channel_id'], 'system', 'permissions_role'); - if ($role === 'forum' || $role === 'repository') { - $public_forum = true; - } - else { - // check if it has characteristics of a public forum based on custom permissions. - $m = Permissions::FilledAutoperms($e['channel_id']); - if ($m) { - foreach ($m as $k => $v) { - if ($k == 'tag_deliver' && intval($v) == 1) - $ch++; - if ($k == 'send_stream' && intval($v) == 0) - $ch++; - } - if ($ch == 2) - $public_forum = true; - } - } - - // This is for birthdays and keywords, but must check access permissions $p = q("select * from profile where uid = %d and is_default = 1", intval($e['channel_id']) @@ -2875,6 +2855,7 @@ class Libzot { ]; $ret['channel_role'] = get_pconfig($e['channel_id'], 'system', 'permissions_role', 'custom'); + $ret['channel_type'] = ((get_pconfig($e['channel_id'], 'system', 'group_actor')) ? 'group' : 'normal'); $hookinfo = [ 'channel_id' => $id, @@ -2890,8 +2871,10 @@ class Libzot { $ret['protocols'] = $hookinfo['protocols']; $ret['searchable'] = $searchable; $ret['adult_content'] = $adult_channel; - $ret['public_forum'] = $public_forum; + // now all forums (public, restricted, and private) set the public_forum flag. So it really means "is a group" + // and has nothing to do with accessibility. + $ret['public_forum'] = get_pconfig($e['channel_id'], 'system', 'group_actor'); $ret['comments'] = map_scope(PermissionLimits::Get($e['channel_id'], 'post_comments')); $ret['mail'] = map_scope(PermissionLimits::Get($e['channel_id'], 'post_mail')); diff --git a/Zotlabs/Lib/Permcat.php b/Zotlabs/Lib/Permcat.php index ca4aed9ed..bda35a9cb 100644 --- a/Zotlabs/Lib/Permcat.php +++ b/Zotlabs/Lib/Permcat.php @@ -4,6 +4,8 @@ namespace Zotlabs\Lib; use Zotlabs\Access\PermissionRoles; use Zotlabs\Access\Permissions; +use Zotlabs\Lib\Libsync; +use Zotlabs\Daemon\Master; /** * @brief Permission Categories. Permission rules for various classes of connections. @@ -79,8 +81,9 @@ class Permcat { $this->permcats[] = [ 'name' => 'default', - 'localname' => t('default','permcat'), + 'localname' => t('Default','permcat'), 'perms' => Permissions::Operms($perms), + 'raw_perms' => $perms, 'system' => 1 ]; @@ -92,6 +95,7 @@ class Permcat { 'name' => $p[$x][0], 'localname' => $p[$x][1], 'perms' => Permissions::Operms(Permissions::FilledPerms($p[$x][2])), + 'raw_perms' => Permissions::FilledPerms($p[$x][2]), 'system' => intval($p[$x][3]) ]; } @@ -128,27 +132,24 @@ class Permcat { } public function load_permcats($uid) { - +/* $permcats = [ - [ 'follower', t('follower','permcat'), - [ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki', - 'post_like' ], 1 + [ 'contributor', t('Contributor','permcat'), + [ 'view_stream','view_profile','view_contacts','view_storage','view_pages', + 'write_storage','post_wall','write_pages','write_wiki','post_comments', 'post_mail', 'post_like', + 'chat' ], 1 ], - [ 'contributor', t('contributor','permcat'), + [ 'muted', t('Muted','permcat'), [ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki', - 'post_wall','post_comments','write_wiki','post_like','tag_deliver','chat' ], 1 + 'post_comments','write_wiki','post_like' ], 1 ], - [ 'publisher', t('publisher','permcat'), - [ 'view_stream','view_profile','view_contacts','view_storage','view_pages', - 'write_storage','post_wall','write_pages','write_wiki','post_comments','post_like','tag_deliver', - 'chat', 'republish' ], 1 - ] ]; - +*/ if($uid) { $x = q("select * from pconfig where uid = %d and cat = 'permcat'", intval($uid) ); + if($x) { foreach($x as $xv) { $value = ((preg_match('|^a:[0-9]+:{.*}$|s', $xv['v'])) ? unserialize($xv['v']) : $xv['v']); @@ -183,4 +184,105 @@ class Permcat { PConfig::Delete($channel_id, 'permcat', $name); } -} \ No newline at end of file + /** + * @brief assign a contact role to contacts + * + * @param int $channel_id + * @param string $role the name of the role + * @param array $contacts an array of contact hashes + */ + public static function assign($channel, $role, $contacts) { + + if(!isset($channel['channel_id'])) { + return; + } + + if(!is_array($contacts) || empty($contacts)) { + return; + } + + if(!$role) { + // lookup the default + $role = get_pconfig($channel_id, 'system', 'default_permcat', 'default'); + } + + + // Doublecheck that we do not assign a role to ourself. + // It does not make a difference but could be confusing. + if (in_array($channel['channel_hash'], $contacts)) { + $contacts = array_diff($contacts, [$channel['channel_hash']]); + } + + $all_perms = Permissions::Perms(); + $permcats = new Permcat($channel['channel_id']); + $role_perms = $permcats->fetch($role); + + if (isset($role_perms['error'])) { + return false; + } + + $perms = $role_perms['raw_perms']; + + $values_sql = ''; + stringify_array_elms($contacts, true); + + if ($all_perms && $perms) { + + foreach ($contacts as $contact) { + foreach ($all_perms as $perm => $desc) { + if (array_key_exists($perm, $perms)) { + $values_sql .= " (" . intval($channel['channel_id']) . ", " . protect_sprintf($contact) . ", 'my_perms', '" . dbesc($perm) . "', " . intval($perms[$perm]) . "),"; + } + else { + $values_sql .= " (" . intval($channel['channel_id']) . ", " . protect_sprintf($contact) . ", 'my_perms', '" . dbesc($perm) . "', 0), "; + } + } + } + } + + $values_sql = rtrim($values_sql, ','); + + dbq("DELETE FROM abconfig WHERE chan = " . intval($channel['channel_id']) . " AND cat = 'my_perms' AND xchan IN (" . protect_sprintf(implode(',', $contacts)) . ")"); + + dbq("INSERT INTO abconfig ( chan, xchan, cat, k, v ) VALUES $values_sql"); + + q("UPDATE abook SET abook_role = '%s' + WHERE abook_xchan IN (" . protect_sprintf(implode(',', $contacts)) . ") AND abook_channel = %d", + dbesc($role), + intval($channel['channel_id']) + ); + + $r = q("SELECT abook.*, xchan.* FROM abook LEFT JOIN xchan ON abook.abook_xchan = xchan.xchan_hash WHERE abook.abook_xchan IN (" . protect_sprintf(implode(',', $contacts)) . ") AND abook.abook_channel = %d AND abook_self = 0", + intval($channel['channel_id']) + ); + + foreach ($r as $rr) { + + if (intval($rr['abook_self'])) { + continue; + } + + Master::Summon([ + 'Notifier', + 'permission_update', + $rr['abook_id'] + ]); + + $clone = $rr; + + unset($clone['abook_id']); + unset($clone['abook_account']); + unset($clone['abook_channel']); + + $abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']); + if ($abconfig) + $clone['abconfig'] = $abconfig; + + Libsync::build_sync_packet(0 /* use the current local_channel */, ['abook' => [$clone]]); + + } + + return true; + } + +} diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index cd54fea17..e7cb2d5de 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -3,6 +3,7 @@ namespace Zotlabs\Lib; use Zotlabs\Lib\Apps; +use Zotlabs\Access\AccessList; require_once('include/text.php'); @@ -58,6 +59,9 @@ class ThreadItem { $child = new ThreadItem($item); $this->add_child($child); } + + // performance: we have already added the children + unset($this->data['children']); } // allow a site to configure the order and content of the reaction emoji list @@ -98,11 +102,20 @@ class ThreadItem { $conv = $this->get_conversation(); $observer = $conv->get_observer(); - $lock = (((intval($item['item_private'])) || (($item['uid'] == local_channel()) && (strlen($item['allow_cid']) || strlen($item['allow_gid']) - || strlen($item['deny_cid']) || strlen($item['deny_gid'])))) - ? t('Private Message') + $acl = new AccessList(false); + $acl->set($item); + + $lock = ((intval($item['item_private']) || ($item['uid'] == local_channel() && $acl->is_private())) + ? t('Restricted message') : false); - $locktype = $item['item_private']; + + // 1 = restricted message, 2 = direct message + $locktype = intval($item['item_private']); + // 0 = limited based on public policy + if ($item['uid'] == local_channel() && intval($item['item_private']) && !$acl->is_private() && strlen($item['public_policy'])) { + $lock = t('Public Policy'); + $locktype = 0; + } $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false); @@ -110,6 +123,16 @@ class ThreadItem { if($item['author']['xchan_network'] === 'rss') $shareable = true; + // @fixme + // Have recently added code to properly handle polls in group reshares by redirecting all of the poll responses to the group. + // Sharing a poll using a regular embedded share is harder because the poll will need to fork. This is due to comment permissions. + // The original poll author may not accept responses from strangers. Forking the poll will receive responses from the sharer's + // followers, but there's no elegant way to merge these two sets of results together. For now, we'll disable sharing polls. + + if ($item['obj_type'] === 'Question') { + $shareable = false; + } + $privacy_warning = false; if(intval($item['item_private']) && ($item['owner']['xchan_network'] === 'activitypub')) { $recips = get_iconfig($item['parent'], 'activitypub', 'recips'); -- cgit v1.2.3