aboutsummaryrefslogtreecommitdiffstats
path: root/Zotlabs/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Zotlabs/Lib')
-rw-r--r--Zotlabs/Lib/AccessList.php239
-rw-r--r--Zotlabs/Lib/Activity.php30
-rw-r--r--Zotlabs/Lib/Connect.php5
-rw-r--r--Zotlabs/Lib/Enotify.php2
-rw-r--r--Zotlabs/Lib/Group.php405
-rw-r--r--Zotlabs/Lib/Libzot.php43
-rw-r--r--Zotlabs/Lib/Permcat.php130
-rw-r--r--Zotlabs/Lib/ThreadItem.php31
8 files changed, 308 insertions, 577 deletions
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 @@
-<?php
+<?php
namespace Zotlabs\Lib;
-use Zotlabs\Lib\Libsync;
-
-
class AccessList {
-
- static function add($uid,$name,$public = 0) {
- $ret = false;
+ static function add($uid, $name, $public = 0) {
+
+ $ret = false;
+ $hash = '';
if ($uid && $name) {
- $r = self::byname($uid,$name); // check for dups
+ $r = self::by_name($uid, $name); // check for dups
if ($r !== false) {
- // This could be a problem.
+ // This could be a problem.
// Let's assume we've just created a list which we once deleted
// all the old members are gone, but the list remains so we don't break any security
// access lists. What we're doing here is reviving the dead list, but old content which
- // was restricted to this list may now be seen by the new list members.
+ // was restricted to this list may now be seen by the new list members.
$z = q("SELECT * FROM pgrp WHERE id = %d LIMIT 1",
intval($r)
);
- if(($z) && $z[0]['deleted']) {
+ if (($z) && $z[0]['deleted']) {
q('UPDATE pgrp SET deleted = 0 WHERE id = %d', intval($z[0]['id']));
- notice( t('A deleted list with this name was revived. Existing item permissions <strong>may</strong> 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 <strong>may</strong> 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 @@
-<?php
-
-namespace Zotlabs\Lib;
-
-use Zotlabs\Lib\Libsync;
-
-
-class Group {
-
- static function add($uid,$name,$public = 0) {
-
- $ret = false;
- if(x($uid) && x($name)) {
- $r = self::byname($uid,$name); // check for dups
- if($r !== false) {
-
- // This could be a problem.
- // Let's assume we've just created a group which we once deleted
- // all the old members are gone, but the group remains so we don't break any security
- // access lists. What we're doing here is reviving the dead group, but old content which
- // was restricted to this group may now be seen by the new group members.
-
- $z = q("SELECT * FROM pgrp WHERE id = %d LIMIT 1",
- intval($r)
- );
- if(($z) && $z[0]['deleted']) {
- q('UPDATE pgrp SET deleted = 0 WHERE id = %d', intval($z[0]['id']));
- notice( t('A deleted group with this name was revived. Existing item permissions <strong>may</strong> 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');