diff options
94 files changed, 3138 insertions, 2259 deletions
diff --git a/Zotlabs/Access/PermissionLimits.php b/Zotlabs/Access/PermissionLimits.php index fb5fe6133..7a574ba6a 100644 --- a/Zotlabs/Access/PermissionLimits.php +++ b/Zotlabs/Access/PermissionLimits.php @@ -89,4 +89,4 @@ class PermissionLimits { return false; } -}
\ No newline at end of file +} diff --git a/Zotlabs/Access/PermissionRoles.php b/Zotlabs/Access/PermissionRoles.php index 998b6d8d2..2078b52a8 100644 --- a/Zotlabs/Access/PermissionRoles.php +++ b/Zotlabs/Access/PermissionRoles.php @@ -17,7 +17,7 @@ class PermissionRoles { * @return number */ static public function version() { - return 2; + return 3; } static function role_perms($role) { @@ -27,6 +27,54 @@ class PermissionRoles { $ret['role'] = $role; switch($role) { + + case 'public': + $ret['default_collection'] = false; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki', + 'send_stream', 'post_comments', 'post_mail', 'post_wall', 'chat', 'post_like', 'republish' + ]; + $ret['limits'] = PermissionLimits::Std_Limits(); + $ret['limits']['post_comments'] = PERMS_AUTHED; + $ret['limits']['post_mail'] = PERMS_AUTHED; + $ret['limits']['post_like'] = PERMS_AUTHED; + $ret['limits']['chat'] = PERMS_AUTHED; + break; + + // Hubzilla default role + case 'personal': + $ret['default_collection'] = true; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki', + 'send_stream', 'post_comments', 'post_mail', 'chat', 'post_like' + ]; + $ret['limits'] = PermissionLimits::Std_Limits(); + $ret['limits']['view_contacts'] = PERMS_SPECIFIC; + break; + + case 'group': + $ret['default_collection'] = false; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'view_wiki', 'post_wall', 'post_comments', + 'post_mail', 'post_like', 'chat' + ]; + $ret['limits'] = PermissionLimits::Std_Limits(); + $ret['channel_type'] = 'group'; + break; + + // Provide some defaults for the custom role so that we do not start + // with no permissions at all if we create a new channel with this role + case 'custom': + $ret['default_collection'] = true; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki', + 'send_stream', 'post_comments', 'post_mail', 'chat', 'post_like' + ]; + $ret['limits'] = PermissionLimits::Std_Limits(); + break; + +/* case 'social': $ret['perms_auto'] = false; $ret['default_collection'] = false; @@ -193,13 +241,14 @@ class PermissionRoles { $ret['channel_type'] = 'group'; break; +*/ - case 'custom': default: break; } $x = get_config('system','role_perms'); + // let system settings over-ride any or all if($x && is_array($x) && array_key_exists($role,$x)) $ret = array_merge($ret,$x[$role]); @@ -284,6 +333,7 @@ class PermissionRoles { */ static public function roles() { $roles = [ + t('Social Networking') => [ 'social_federation' => t('Social - Federation'), 'social' => t('Social - Mostly Public'), @@ -317,4 +367,29 @@ class PermissionRoles { return $roles; } + /** + * @brief Array with translated role names and grouping. + * + * Return an associative array with role names that can be used + * to create select groups like in \e field_select_grouped.tpl. + * + * @return array + */ + static public function channel_roles() { + $channel_roles = [ + //'public' => [t('Public'), t('A very permissive role suited for participation in the fediverse')], + //'personal' => [t('Personal'), t('The $Projectname default role suited for a personal channel')], + //'forum' => [t('Community forum'), t('This role configures your channel to act as an community forum')], + //'custom' => [t('Custom'), t('This role comes with the presets of the personal role but allows you to configure it to your needs')] + 'public' => t('Public'), + 'personal' => t('Personal'), + 'group' => t('Community forum'), + 'custom' => t('Custom') + ]; + + call_hooks('list_channel_roles', $channel_roles); + + return $channel_roles; + } + } diff --git a/Zotlabs/Access/Permissions.php b/Zotlabs/Access/Permissions.php index 45dd30d69..09f4c9678 100644 --- a/Zotlabs/Access/Permissions.php +++ b/Zotlabs/Access/Permissions.php @@ -41,7 +41,7 @@ class Permissions { * @return number */ static public function version() { - return 2; + return 3; } /** @@ -67,9 +67,9 @@ class Permissions { 'post_comments' => t('Can comment on or like my posts'), 'post_mail' => t('Can send me direct messages'), 'post_like' => t('Can like/dislike profiles and profile things'), - 'tag_deliver' => t('Can forward direct messages to all my channel connections (forum)'), 'chat' => t('Can chat with me'), - 'republish' => t('Can source my public posts in derived channels'), + 'republish' => t('Can source/mirror my public posts in derived channels'), + //'tag_deliver' => t('Can forward to my contacts via direct messages (forum)'), 'delegate' => t('Can administer my channel') ]; @@ -217,25 +217,23 @@ class Permissions { $my_perms = []; $permcat = null; - $automatic = 0; + $automatic = get_pconfig($channel_id, 'system', 'autoperms'); // If a default permcat exists, use that - $pc = ((feature_enabled($channel_id, 'permcats')) ? get_pconfig($channel_id, 'system', 'default_permcat') : 'default'); - if (!in_array($pc, ['', 'default'])) { - $pcp = new Zlib\Permcat($channel_id); - $permcat = $pcp->fetch($pc); - if ($permcat && $permcat['perms']) { - foreach ($permcat['perms'] as $p) { - $my_perms[$p['name']] = $p['value']; - } + $pc = get_pconfig($channel_id, 'system', 'default_permcat', 'default'); + $pcp = new Zlib\Permcat($channel_id); + $permcat = $pcp->fetch($pc); + if ($permcat && $permcat['perms']) { + foreach ($permcat['perms'] as $p) { + $my_perms[$p['name']] = $p['value']; } } // look up the permission role to see if it specified auto-connect // and if there was no permcat or a default permcat, set the perms // from the role - +/* $role = get_pconfig($channel_id, 'system', 'permissions_role'); if ($role) { $xx = PermissionRoles::role_perms($role); @@ -247,11 +245,12 @@ class Permissions { $my_perms = Permissions::FilledPerms($default_perms); } } +*/ // If we reached this point without having any permission information, // it is likely a custom permissions role. First see if there are any // automatic permissions. - +/* if (!$my_perms) { $m = Permissions::FilledAutoperms($channel_id); if ($m) { @@ -259,11 +258,12 @@ class Permissions { $my_perms = $m; } } - +*/ // If we reached this point with no permissions, the channel is using // custom perms but they are not automatic. They will be stored in abconfig with // the channel's channel_hash (the 'self' connection). +/* if (!$my_perms) { $r = q("select channel_hash from channel where channel_id = %d", intval($channel_id) @@ -280,10 +280,10 @@ class Permissions { } } } - - return (['perms' => $my_perms, 'automatic' => $automatic]); +*/ + return (['perms' => $my_perms, 'automatic' => $automatic, 'role' => $pc]); } - +/* static public function serialise($p) { $n = []; if ($p) { @@ -295,4 +295,5 @@ class Permissions { } return implode(',', $n); } +*/ } diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index 4ca109495..23bddae8f 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -630,6 +630,18 @@ class Notifier { // default: zot protocol + // Prevent zot6 delivery of group comment boosts, which are not required for conversational platforms. + // ActivityPub conversational platforms may wish to filter these if they don't want or require them. + // We will assume here that if $target_item exists and has a verb that it is an actual item structure + // so we won't need to check the existence of the other item fields prior to evaluation. + + // This shouldn't produce false positives on comment boosts that were generated on other platforms + // because we won't be delivering them. + + if (isset($target_item) && isset($target_item['verb']) && $target_item['verb'] === 'Announce' && $target_item['author_xchan'] === $target_item['owner_xchan'] && ! intval($target_item['item_thread_top'])) { + continue; + } + $hash = new_uuid(); $env = (($hub_env && $hub_env[$hub['hubloc_site_id']]) ? $hub_env[$hub['hubloc_site_id']] : ''); 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'); diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php index aeb02eeaa..5b37f2707 100644 --- a/Zotlabs/Module/Acl.php +++ b/Zotlabs/Module/Acl.php @@ -3,9 +3,9 @@ namespace Zotlabs\Module; use Zotlabs\Lib\Libzotdir; +use Zotlabs\Lib\AccessList; require_once 'include/acl_selectors.php'; -require_once 'include/group.php'; /** * @brief ACL selector json backend. @@ -123,7 +123,7 @@ class Acl extends \Zotlabs\Web\Controller { "name" => t('Profile','acl') . ' ' . $rv['profile_name'], "id" => 'vp' . $rv['id'], "xid" => 'vp.' . $rv['profile_guid'], - "uids" => group_get_profile_members_xchan(local_channel(), $rv['id']), + "uids" => AccessList::profile_members_xchan(local_channel(), $rv['id']), "link" => '' ); } @@ -146,14 +146,14 @@ class Acl extends \Zotlabs\Web\Controller { if($r) { foreach($r as $g){ - // logger('acl: group: ' . $g['gname'] . ' members: ' . group_get_members_xchan($g['id'])); + // logger('acl: group: ' . $g['gname'] . ' members: ' . AccessList::members_xchan(local_channel(), $g['id'])); $groups[] = array( "type" => "g", "photo" => "images/twopeople.png", "name" => $g['gname'], "id" => $g['id'], "xid" => $g['hash'], - "uids" => group_get_members_xchan($g['id']), + "uids" => AccessList::members_xchan(local_channel(), $g['id']), "link" => '' ); } diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php index 76e117a84..00095187d 100644 --- a/Zotlabs/Module/Admin/Site.php +++ b/Zotlabs/Module/Admin/Site.php @@ -339,8 +339,8 @@ class Site { // now invert the logic for the setting. $discover_tab = (1 - $discover_tab); - $perm_roles = \Zotlabs\Access\PermissionRoles::roles(); - $default_role = get_config('system','default_permissions_role','social'); + $perm_roles = \Zotlabs\Access\PermissionRoles::channel_roles(); + $default_role = get_config('system','default_permissions_role','personal'); $role = array('permissions_role' , t('Default permission role for new accounts'), $default_role, t('This role will be used for the first channel created after registration.'),$perm_roles); diff --git a/Zotlabs/Module/Apschema.php b/Zotlabs/Module/Apschema.php index eab82eb29..e8d45c522 100644 --- a/Zotlabs/Module/Apschema.php +++ b/Zotlabs/Module/Apschema.php @@ -14,7 +14,7 @@ class Apschema extends \Zotlabs\Web\Controller { 'zot' => z_root() . '/apschema#', 'id' => '@id', 'type' => '@type', - 'commentPolicy' => 'as:commentPolicy', + 'commentPolicy' => 'zot:commentPolicy', 'meData' => 'zot:meData', 'meDataType' => 'zot:meDataType', 'meEncoding' => 'zot:meEncoding', @@ -33,6 +33,9 @@ class Apschema extends \Zotlabs\Web\Controller { 'PropertyValue' => 'schema:PropertyValue', 'value' => 'schema:value', + 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', + + 'magicEnv' => [ '@id' => 'zot:magicEnv', '@type' => '@id' diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php index 4ebd2ee29..aebc70c15 100644 --- a/Zotlabs/Module/Channel.php +++ b/Zotlabs/Module/Channel.php @@ -95,6 +95,10 @@ class Channel extends Controller { http_status_exit(410, 'Gone'); } + if (get_pconfig($channel['channel_id'], 'system', 'index_opt_out')) { + App::$meta->set('robots', 'noindex, noarchive'); + } + if (ActivityStreams::is_as_request($channel)) { // Somebody may attempt an ActivityStreams fetch on one of our message permalinks diff --git a/Zotlabs/Module/Connections.php b/Zotlabs/Module/Connections.php index 5025f4e22..b77e5d06d 100644 --- a/Zotlabs/Module/Connections.php +++ b/Zotlabs/Module/Connections.php @@ -2,32 +2,32 @@ namespace Zotlabs\Module; use App; +use Zotlabs\Lib\Permcat; require_once('include/socgraph.php'); require_once('include/selectors.php'); -require_once('include/group.php'); class Connections extends \Zotlabs\Web\Controller { function init() { - + if(! local_channel()) return; App::$profile_uid = local_channel(); - + $channel = App::get_channel(); if($channel) head_set_icon($channel['xchan_photo_s']); - + } - + function get() { - + $sort_type = 0; $o = ''; - - + + if(! local_channel()) { notice( t('Permission denied.') . EOL); return login(); @@ -44,13 +44,13 @@ class Connections extends \Zotlabs\Web\Controller { $pending = false; $unconnected = false; $all = false; - + if(! $_REQUEST['aj']) $_SESSION['return_url'] = App::$query_string; - + $search_flags = ""; $head = ''; - + if(argc() == 2) { switch(argv(1)) { case 'active': @@ -106,7 +106,7 @@ class Connections extends \Zotlabs\Web\Controller { // $head = t('Unconnected'); // $unconnected = true; // break; - + case 'all': $head = t('All'); break; @@ -115,19 +115,19 @@ class Connections extends \Zotlabs\Web\Controller { $active = true; $head = t('Active'); break; - + } - + $sql_extra = $search_flags; if(argv(1) === 'pending') $sql_extra .= " and abook_ignored = 0 "; - + } else { $sql_extra = " and abook_blocked = 0 "; $unblocked = true; } - + switch($_REQUEST['order']) { case 'name_desc': $sql_order = 'xchan_name DESC'; @@ -143,32 +143,32 @@ class Connections extends \Zotlabs\Web\Controller { } $search = ((x($_REQUEST,'search')) ? notags(trim($_REQUEST['search'])) : ''); - + $tabs = array( /* array( 'label' => t('Suggestions'), - 'url' => z_root() . '/suggest', + 'url' => z_root() . '/suggest', 'sel' => '', 'title' => t('Suggest new connections'), ), */ - + 'active' => array( 'label' => t('Active Connections'), - 'url' => z_root() . '/connections/active', + 'url' => z_root() . '/connections/active', 'sel' => ($active) ? 'active' : '', 'title' => t('Show active connections'), ), 'pending' => array( 'label' => t('New Connections'), - 'url' => z_root() . '/connections/pending', + 'url' => z_root() . '/connections/pending', 'sel' => ($pending) ? 'active' : '', 'title' => t('Show pending (new) connections'), ), - - + + /* array( 'label' => t('Unblocked'), @@ -177,55 +177,55 @@ class Connections extends \Zotlabs\Web\Controller { 'title' => t('Only show unblocked connections'), ), */ - + 'blocked' => array( 'label' => t('Blocked'), 'url' => z_root() . '/connections/blocked', 'sel' => ($blocked) ? 'active' : '', 'title' => t('Only show blocked connections'), ), - + 'ignored' => array( 'label' => t('Ignored'), 'url' => z_root() . '/connections/ignored', 'sel' => ($ignored) ? 'active' : '', 'title' => t('Only show ignored connections'), ), - + 'archived' => array( 'label' => t('Archived/Unreachable'), 'url' => z_root() . '/connections/archived', 'sel' => ($archived) ? 'active' : '', 'title' => t('Only show archived/unreachable connections'), ), - + 'hidden' => array( 'label' => t('Hidden'), 'url' => z_root() . '/connections/hidden', 'sel' => ($hidden) ? 'active' : '', 'title' => t('Only show hidden connections'), ), - + // array( // 'label' => t('Unconnected'), // 'url' => z_root() . '/connections/unconnected', // 'sel' => ($unconnected) ? 'active' : '', // 'title' => t('Only show one-way connections'), // ), - + 'all' => array( 'label' => t('All Connections'), - 'url' => z_root() . '/connections', + 'url' => z_root() . '/connections', 'sel' => ($all) ? 'active' : '', 'title' => t('Show all connections'), ), - + ); - + //$tab_tpl = get_markup_template('common_tabs.tpl'); //$t = replace_macros($tab_tpl, array('$tabs'=>$tabs)); - + $searching = false; if($search) { $search_hdr = $search; @@ -233,12 +233,12 @@ class Connections extends \Zotlabs\Web\Controller { $searching = true; } $sql_extra .= (($searching) ? protect_sprintf(" AND xchan_name like '%$search_txt%' ") : ""); - + if($_REQUEST['gid']) { $sql_extra .= " and xchan_hash in ( select xchan from pgrp_member where gid = " . intval($_REQUEST['gid']) . " and uid = " . intval(local_channel()) . " ) "; } - - $r = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash + + $r = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 and xchan_orphan = 0 $sql_extra ", intval(local_channel()) ); @@ -246,19 +246,27 @@ class Connections extends \Zotlabs\Web\Controller { App::set_pager_total($r[0]['total']); $total = $r[0]['total']; } - + $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash WHERE abook_channel = %d and abook_self = 0 and xchan_deleted = 0 and xchan_orphan = 0 $sql_extra ORDER BY $sql_order LIMIT %d OFFSET %d ", intval(local_channel()), intval(App::$pager['itemspage']), intval(App::$pager['start']) ); - + + $roles = new Permcat(local_channel()); + $roles_list = $roles->listing(); + $roles_dict = []; + + foreach ($roles_list as $role) { + $roles_dict[$role['name']] = $role['localname']; + } + $contacts = array(); - + if($r) { - vcard_query($r); + //vcard_query($r); foreach($r as $rr) { @@ -268,7 +276,7 @@ class Connections extends \Zotlabs\Web\Controller { $phone = $rr['vcard']['tels'][0]['nr']; else $phone = ''; - + $status_str = ''; $status = array( ((intval($rr['abook_active'])) ? t('Active') : ''), @@ -306,7 +314,7 @@ class Connections extends \Zotlabs\Web\Controller { $perminfo['connperms'] .= t('Nothing'); } - + foreach($status as $str) { if(!$str) continue; @@ -314,14 +322,14 @@ class Connections extends \Zotlabs\Web\Controller { $status_str .= ', '; } $status_str = rtrim($status_str, ', '); - + $contacts[] = array( 'img_hover' => sprintf( t('%1$s [%2$s]'),$rr['xchan_name'],$rr['xchan_url']), 'edit_hover' => t('Edit connection'), 'edit' => t('Edit'), 'delete_hover' => t('Delete connection'), 'id' => $rr['abook_id'], - 'thumb' => $rr['xchan_photo_m'], + 'thumb' => $rr['xchan_photo_m'], 'name' => $rr['xchan_name'], 'classes' => ((intval($rr['abook_archived']) || intval($rr['abook_not_here'])) ? 'archived' : ''), 'link' => z_root() . '/connedit/' . $rr['abook_id'], @@ -349,13 +357,21 @@ class Connections extends \Zotlabs\Web\Controller { 'perminfo' => $perminfo, 'connect' => (intval($rr['abook_not_here']) ? t('Connect') : ''), 'follow' => z_root() . '/follow/?f=&url=' . urlencode($rr['xchan_hash']) . '&interactive=0', - 'connect_hover' => t('Connect at this location') + 'connect_hover' => t('Connect at this location'), + 'role' => $roles_dict[$rr['abook_role']] ); } } } - - + + $limit = service_class_fetch(local_channel(),'total_channels'); + if($limit !== false) { + $abook_usage_message = sprintf( t("You have %1$.0f of %2$.0f allowed connections."), $$total, $limit); + } + else { + $abook_usage_message = ''; + } + if($_REQUEST['aj']) { if($contacts) { $o = replace_macros(get_markup_template('contactsajax.tpl'),array( @@ -371,27 +387,29 @@ class Connections extends \Zotlabs\Web\Controller { } else { $o .= "<script> var page_query = '" . escape_tags(urlencode($_GET['q'])) . "'; var extra_args = '" . extra_query_args() . "' ; </script>"; - $o .= replace_macros(get_markup_template('connections.tpl'),array( + $o .= replace_macros(get_markup_template('connections.tpl'), [ '$header' => t('Connections') . (($head) ? ': ' . $head : ''), '$tabs' => $tabs, '$total' => $total, '$search' => $search_hdr, '$label' => t('Search'), + '$role_label' => t('Contact role'), '$desc' => t('Search your connections'), - '$finding' => (($searching) ? t('Connections search') . ": '" . $search . "'" : ""), + '$finding' => (($searching) ? t('Contact search') . ": '" . $search . "'" : ""), '$submit' => t('Find'), '$edit' => t('Edit'), '$cmd' => App::$cmd, '$contacts' => $contacts, '$paginate' => paginate($a), - - )); + '$abook_usage_message' => $abook_usage_message, + '$group_label' => t('This is a group/forum channel') + ]); } - + if(! $contacts) $o .= '<div id="content-complete"></div>'; - + return $o; } - + } diff --git a/Zotlabs/Module/Connedit.php b/Zotlabs/Module/Connedit.php index 7fabf1224..6bebef026 100644 --- a/Zotlabs/Module/Connedit.php +++ b/Zotlabs/Module/Connedit.php @@ -1,4 +1,5 @@ <?php + namespace Zotlabs\Module; /* @file connedit.php @@ -8,8 +9,8 @@ namespace Zotlabs\Module; */ use App; +use Sabre\VObject\Reader; use Zotlabs\Lib\Apps; -use Zotlabs\Lib\Crypto; use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Libsync; use Zotlabs\Daemon\Master; @@ -18,13 +19,12 @@ use Zotlabs\Access\Permissions; use Zotlabs\Access\PermissionLimits; use Zotlabs\Web\HTTPHeaders; use Zotlabs\Lib\Permcat; +use Zotlabs\Lib\AccessList; require_once('include/socgraph.php'); require_once('include/selectors.php'); -require_once('include/group.php'); require_once('include/photos.php'); - class Connedit extends Controller { /* @brief Initialize the connection-editor @@ -34,26 +34,25 @@ class Connedit extends Controller { function init() { - if(! local_channel()) + if (!local_channel()) return; - if((argc() >= 2) && intval(argv(1))) { + if ((argc() >= 2) && intval(argv(1))) { $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash - WHERE abook_channel = %d and abook_id = %d and xchan_deleted = 0 LIMIT 1", + WHERE abook_channel = %d and abook_id = %d and abook_self = 0 and xchan_deleted = 0 LIMIT 1", intval(local_channel()), intval(argv(1)) ); - if($r) { + if ($r) { App::$poi = $r[0]; } } - $channel = App::get_channel(); - if($channel) + if ($channel) { head_set_icon($channel['xchan_photo_s']); - + } } @@ -63,188 +62,98 @@ class Connedit extends Controller { function post() { - if(! local_channel()) + if (!local_channel()) return; $contact_id = intval(argv(1)); - if(! $contact_id) + if (!$contact_id) return; $channel = App::get_channel(); - // TODO if configured for hassle-free permissions, we'll post the form with ajax as soon as the - // connection enable is toggled to a special autopost url and set permissions immediately, leaving - // the other form elements alone pending a manual submit of the form. The downside is that there - // will be a window of opportunity when the permissions have been set but before you've had a chance - // to review and possibly restrict them. The upside is we won't have to warn you that your connection - // can't do anything until you save the bloody form. - - $autopost = (((argc() > 2) && (argv(2) === 'auto')) ? true : false); - - $orig_record = q("SELECT * FROM abook WHERE abook_id = %d AND abook_channel = %d LIMIT 1", + $orig_record = q("SELECT * FROM abook WHERE abook_id = %d AND abook_channel = %d AND abook_self = 0 LIMIT 1", intval($contact_id), intval(local_channel()) ); - if(! $orig_record) { - notice( t('Could not access contact record.') . EOL); + if (!$orig_record) { + notice(t('Could not access contact record.') . EOL); goaway(z_root() . '/connections'); return; // NOTREACHED } call_hooks('contact_edit_post', $_POST); - $vc = get_abconfig(local_channel(),$orig_record['abook_xchan'],'system','vcard'); - $vcard = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); - $serialised_vcard = update_vcard($_REQUEST,$vcard); - if($serialised_vcard) - set_abconfig(local_channel(),$orig_record[0]['abook_xchan'],'system','vcard',$serialised_vcard); - - if(intval($orig_record[0]['abook_self'])) { - $autoperms = intval($_POST['autoperms']); - $is_self = true; - } - else { - $autoperms = null; - $is_self = false; - } + $vc = get_abconfig(local_channel(), $orig_record['abook_xchan'], 'system', 'vcard'); + $vcard = (($vc) ? Reader::read($vc) : null); + $serialised_vcard = update_vcard($_REQUEST, $vcard); + if ($serialised_vcard) + set_abconfig(local_channel(), $orig_record[0]['abook_xchan'], 'system', 'vcard', $serialised_vcard); + $profile_id = ((array_key_exists('profile_assign', $_POST)) ? $_POST['profile_assign'] : $orig_record[0]['abook_profile']); - $profile_id = ((array_key_exists('profile_assign',$_POST)) ? $_POST['profile_assign'] : $orig_record[0]['abook_profile']); - - if($profile_id) { + if ($profile_id) { $r = q("SELECT profile_guid FROM profile WHERE profile_guid = '%s' AND uid = %d LIMIT 1", dbesc($profile_id), intval(local_channel()) ); - if(! count($r)) { - notice( t('Could not locate selected profile.') . EOL); + if (!count($r)) { + notice(t('Could not locate selected profile.') . EOL); return; } } - $abook_incl = ((array_key_exists('abook_incl',$_POST)) ? escape_tags($_POST['abook_incl']) : $orig_record[0]['abook_incl']); - $abook_excl = ((array_key_exists('abook_excl',$_POST)) ? escape_tags($_POST['abook_excl']) : $orig_record[0]['abook_excl']); - - - $hidden = intval($_POST['hidden']); + $abook_incl = ((array_key_exists('abook_incl', $_POST)) ? escape_tags($_POST['abook_incl']) : $orig_record[0]['abook_incl']); + $abook_excl = ((array_key_exists('abook_excl', $_POST)) ? escape_tags($_POST['abook_excl']) : $orig_record[0]['abook_excl']); + $abook_role = ((array_key_exists('permcat', $_POST)) ? escape_tags($_POST['permcat']) : $orig_record[0]['abook_role']); - $priority = intval($_POST['poll']); - if($priority > 5 || $priority < 0) - $priority = 0; - - if(! array_key_exists('closeness',$_POST)) { + if (!array_key_exists('closeness', $_POST)) { $_POST['closeness'] = 80; } $closeness = intval($_POST['closeness']); - if($closeness < 0 || $closeness > 99) { + if ($closeness < 0 || $closeness > 99) { $closeness = 80; } - $rating = intval($_POST['rating']); - if($rating < (-10)) - $rating = (-10); - if($rating > 10) - $rating = 10; - - $rating_text = trim(escape_tags($_REQUEST['rating_text'])); + $new_friend = ((intval($orig_record[0]['abook_pending'])) ? true : false); - $all_perms = Permissions::Perms(); +/* + $perms = []; + $permcats = new Permcat(local_channel()); + $role_perms = $permcats->fetch($abook_role); + $all_perms = Permissions::Perms(); - if($all_perms) { - foreach($all_perms as $perm => $desc) { - if(array_key_exists('perms_' . $perm, $_POST)) { - set_abconfig($channel['channel_id'],$orig_record[0]['abook_xchan'],'my_perms',$perm, - intval($_POST['perms_' . $perm])); - if($autoperms) { - set_pconfig($channel['channel_id'],'autoperms',$perm,intval($_POST['perms_' . $perm])); - } - } - else { - set_abconfig($channel['channel_id'],$orig_record[0]['abook_xchan'],'my_perms',$perm,0); - if($autoperms) { - set_pconfig($channel['channel_id'],'autoperms',$perm,0); - } - } - } + // if we got a valid role use the role (default behaviour because a role is mandatory since version 7.0) + if (!isset($role_perms['error'])) { + $perms = $role_perms['raw_perms']; + if (intval($orig_record[0]['abook_pending'])) + $new_friend = true; } - if(! is_null($autoperms)) - set_pconfig($channel['channel_id'],'system','autoperms',$autoperms); - - $new_friend = false; - - // only store a record and notify the directory if the rating changed - - if(! $is_self) { - - $signed = $orig_record[0]['abook_xchan'] . '.' . $rating . '.' . $rating_text; - $sig = base64url_encode(Crypto::sign($signed,$channel['channel_prvkey'])); - - $rated = ((intval($rating) || strlen($rating_text)) ? true : false); - - $record = 0; - - $z = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1 limit 1", - dbesc($channel['channel_hash']), - dbesc($orig_record[0]['abook_xchan']) - ); - - if($z) { - if(($z[0]['xlink_rating'] != $rating) || ($z[0]['xlink_rating_text'] != $rating_text)) { - $record = $z[0]['xlink_id']; - $w = q("update xlink set xlink_rating = '%d', xlink_rating_text = '%s', xlink_sig = '%s', xlink_updated = '%s' - where xlink_id = %d", - intval($rating), - dbesc($rating_text), - dbesc($sig), - dbesc(datetime_convert()), - intval($record) - ); - } - } - elseif($rated) { - // only create a record if there's something to save - $w = q("insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_rating_text, xlink_sig, xlink_updated, xlink_static ) values ( '%s', '%s', %d, '%s', '%s', '%s', 1 ) ", - dbesc($channel['channel_hash']), - dbesc($orig_record[0]['abook_xchan']), - intval($rating), - dbesc($rating_text), - dbesc($sig), - dbesc(datetime_convert()) - ); - $z = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1 limit 1", - dbesc($channel['channel_hash']), - dbesc($orig_record[0]['abook_xchan']) - ); - if($z) - $record = $z[0]['xlink_id']; - } - } - - if(($_REQUEST['pending']) && intval($orig_record[0]['abook_pending'])) { - + // approve shortcut (no role provided) + if (!$perms && intval($orig_record[0]['abook_pending'])) { + $connect_perms = Permissions::connect_perms(local_channel()); + $perms = $connect_perms['perms']; + // set the role from $connect_perms + $abook_role = $connect_perms['role']; $new_friend = true; + } - // @fixme it won't be common, but when you accept a new connection request - // the permissions will now be that of your permissions role and ignore - // any you may have set manually on the form. We'll probably see a bug if somebody - // tries to set the permissions *and* approve the connection in the same - // request. The workaround is to approve the connection, then go back and - // adjust permissions as desired. - - $p = Permissions::connect_perms(local_channel()); - $my_perms = $p['perms']; - if($my_perms) { - foreach($my_perms as $k => $v) { - set_abconfig($channel['channel_id'],$orig_record[0]['abook_xchan'],'my_perms',$k,$v); + if ($all_perms && $perms) { + foreach ($all_perms as $perm => $desc) { + if (array_key_exists($perm, $perms)) { + set_abconfig($channel['channel_id'], $orig_record[0]['abook_xchan'], 'my_perms', $perm, intval($perms[$perm])); + } + else { + set_abconfig($channel['channel_id'], $orig_record[0]['abook_xchan'], 'my_perms', $perm, 0); } } } +*/ - $abook_pending = (($new_friend) ? 0 : $orig_record[0]['abook_pending']); - + \Zotlabs\Lib\Permcat::assign($channel, $abook_role, [$orig_record[0]['abook_xchan']]); + $abook_pending = (($new_friend) ? 0 : $orig_record[0]['abook_pending']); $r = q("UPDATE abook SET abook_profile = '%s', abook_closeness = %d, abook_pending = %d, abook_incl = '%s', abook_excl = '%s' @@ -258,30 +167,29 @@ class Connedit extends Controller { intval(local_channel()) ); - if($r) - info( t('Connection updated.') . EOL); + if ($r) + info(t('Connection updated.') . EOL); else - notice( t('Failed to update connection record.') . EOL); + notice(t('Failed to update connection record.') . EOL); - if(! intval(App::$poi['abook_self'])) { - if($new_friend) { - Master::Summon( [ 'Notifier', 'permission_accept', $contact_id ] ); + if (!intval(App::$poi['abook_self'])) { + if ($new_friend) { + Master::Summon(['Notifier', 'permission_accept', $contact_id]); } - Master::Summon( [ + Master::Summon([ 'Notifier', (($new_friend) ? 'permission_create' : 'permission_update'), $contact_id ]); } - if($new_friend) { + if ($new_friend) { $default_group = $channel['channel_default_group']; - if($default_group) { - require_once('include/group.php'); - $g = group_rec_byhash(local_channel(),$default_group); - if($g) - group_add_member(local_channel(),'',App::$poi['abook_xchan'],$g['id']); + if ($default_group) { + $g = AccessList::by_hash(local_channel(), $default_group); + if ($g) + AccessList::member_add(local_channel(), '', App::$poi['abook_xchan'], $g['id']); } // Check if settings permit ("post new friend activity" is allowed, and @@ -291,18 +199,18 @@ class Connedit extends Controller { $pr = q("select * from profile where uid = %d and is_default = 1 and hide_friends = 0", intval($channel['channel_id']) ); - if(($pr) && (! intval($orig_record[0]['abook_hidden'])) && (intval(get_pconfig($channel['channel_id'],'system','post_newfriend')))) { + if (($pr) && (!intval($orig_record[0]['abook_hidden'])) && (intval(get_pconfig($channel['channel_id'], 'system', 'post_newfriend')))) { $xarr = []; - $xarr['item_wall'] = 1; - $xarr['item_origin'] = 1; + $xarr['item_wall'] = 1; + $xarr['item_origin'] = 1; $xarr['item_thread_top'] = 1; - $xarr['owner_xchan'] = $xarr['author_xchan'] = $channel['channel_hash']; - $xarr['allow_cid'] = $channel['channel_allow_cid']; - $xarr['allow_gid'] = $channel['channel_allow_gid']; - $xarr['deny_cid'] = $channel['channel_deny_cid']; - $xarr['deny_gid'] = $channel['channel_deny_gid']; - $xarr['item_private'] = (($xarr['allow_cid']||$xarr['allow_gid']||$xarr['deny_cid']||$xarr['deny_gid']) ? 1 : 0); + $xarr['owner_xchan'] = $xarr['author_xchan'] = $channel['channel_hash']; + $xarr['allow_cid'] = $channel['channel_allow_cid']; + $xarr['allow_gid'] = $channel['channel_allow_gid']; + $xarr['deny_cid'] = $channel['channel_deny_cid']; + $xarr['deny_gid'] = $channel['channel_deny_gid']; + $xarr['item_private'] = (($xarr['allow_cid'] || $xarr['allow_gid'] || $xarr['deny_cid'] || $xarr['deny_gid']) ? 1 : 0); $xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . App::$poi['xchan_url'] . ']' . App::$poi['xchan_name'] . '[/zrl]'; @@ -312,9 +220,8 @@ class Connedit extends Controller { } - // pull in a bit of content if there is any to pull in - Master::Summon(array('Onepoll',$contact_id)); + Master::Summon(['Onepoll', $contact_id]); } @@ -326,18 +233,18 @@ class Connedit extends Controller { intval(local_channel()), intval($contact_id) ); - if($r) { + if ($r) { App::$poi = $r[0]; } - if($new_friend) { - $arr = array('channel_id' => local_channel(), 'abook' => App::$poi); + if ($new_friend) { + $arr = ['channel_id' => local_channel(), 'abook' => App::$poi]; call_hooks('accept_follow', $arr); } - $this->connedit_clone($a); + $this->connedit_clone(); - if(($_REQUEST['pending']) && (!$_REQUEST['done'])) + if (($_REQUEST['pending']) && (!$_REQUEST['done'])) goaway(z_root() . '/connections/ifpending'); return; @@ -349,35 +256,34 @@ class Connedit extends Controller { * */ - function connedit_clone(&$a) { - - if(! App::$poi) - return; + function connedit_clone() { + if (!App::$poi) + return; - $channel = App::get_channel(); + $channel = App::get_channel(); - $r = q("SELECT abook.*, xchan.* + $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d LIMIT 1", - intval(local_channel()), - intval(App::$poi['abook_id']) - ); - if($r) { - App::$poi = array_shift($r); - } + intval(local_channel()), + intval(App::$poi['abook_id']) + ); + if ($r) { + App::$poi = $r[0]; + } - $clone = App::$poi; + $clone = App::$poi; - unset($clone['abook_id']); - unset($clone['abook_account']); - unset($clone['abook_channel']); + 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; + $abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']); + if ($abconfig) + $clone['abconfig'] = $abconfig; - Libsync::build_sync_packet(0 /* use the current local_channel */, array('abook' => array($clone))); + Libsync::build_sync_packet(0 /* use the current local_channel */, ['abook' => [$clone]]); } /* @brief Generate content of connection edit page @@ -387,37 +293,19 @@ class Connedit extends Controller { function get() { - $sort_type = 0; $o = ''; - if(! local_channel()) { - notice( t('Permission denied.') . EOL); + if (!local_channel()) { + notice(t('Permission denied.') . EOL); return login(); } - $section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : ''); - $channel = App::get_channel(); - - $yes_no = array(t('No'),t('Yes')); - - $connect_perms = Permissions::connect_perms(local_channel()); - - $o .= "<script>function connectDefaultShare() { - \$('.abook-edit-me').each(function() { - if(! $(this).is(':disabled')) - $(this).prop('checked', false); - });\n\n"; - foreach($connect_perms['perms'] as $p => $v) { - if($v) { - $o .= "\$('#me_id_perms_" . $p . "').prop('checked', true); \n"; - } - } - $o .= " }\n</script>\n"; + $section = ((array_key_exists('section', $_REQUEST)) ? $_REQUEST['section'] : ''); - if(argc() == 3) { + if (argc() == 3) { $contact_id = intval(argv(1)); - if(! $contact_id) + if (!$contact_id) return; $cmd = argv(2); @@ -428,35 +316,35 @@ class Connedit extends Controller { intval(local_channel()) ); - if(! count($orig_record)) { - notice( t('Could not access address book record.') . EOL); + if (!count($orig_record)) { + notice(t('Could not access address book record.') . EOL); goaway(z_root() . '/connections'); } - if($cmd === 'update') { + if ($cmd === 'update') { // pull feed and consume it, which should subscribe to the hub. - Master::Summon(array('Poller',$contact_id)); + Master::Summon(['Poller', $contact_id]); goaway(z_root() . '/connedit/' . $contact_id); } - if($cmd === 'fetchvc') { - $url = str_replace('/channel/','/profile/',$orig_record[0]['xchan_url']) . '/vcard'; + if ($cmd === 'fetchvc') { + $url = str_replace('/channel/', '/profile/', $orig_record[0]['xchan_url']) . '/vcard'; $recurse = 0; - $x = z_fetch_url(zid($url),false,$recurse,['session' => true]); - if($x['success']) { - $h = new HTTPHeaders($x['header']); + $x = z_fetch_url(zid($url), false, $recurse, ['session' => true]); + if ($x['success']) { + $h = new HTTPHeaders($x['header']); $fields = $h->fetch(); - if($fields) { - foreach($fields as $y) { - if(array_key_exists('content-type',$y)) { - $type = explode(';',trim($y['content-type'])); - if($type && $type[0] === 'text/vcard' && $x['body']) { - $vc = \Sabre\VObject\Reader::read($x['body']); + if ($fields) { + foreach ($fields as $y) { + if (array_key_exists('content-type', $y)) { + $type = explode(';', trim($y['content-type'])); + if ($type && $type[0] === 'text/vcard' && $x['body']) { + $vc = Reader::read($x['body']); $vcard = $vc->serialize(); - if($vcard) { - set_abconfig(local_channel(),$orig_record[0]['abook_xchan'],'system','vcard',$vcard); - $this->connedit_clone($a); + if ($vcard) { + set_abconfig(local_channel(), $orig_record[0]['abook_xchan'], 'system', 'vcard', $vcard); + $this->connedit_clone(); } } } @@ -467,55 +355,55 @@ class Connedit extends Controller { } - if($cmd === 'resetphoto') { + if ($cmd === 'resetphoto') { q("update xchan set xchan_photo_date = '2001-01-01 00:00:00' where xchan_hash = '%s'", dbesc($orig_record[0]['xchan_hash']) ); $cmd = 'refresh'; } - if($cmd === 'refresh') { - if($orig_record[0]['xchan_network'] === 'zot6') { - if(! Libzot::refresh($orig_record[0],App::get_channel())) - notice( t('Refresh failed - channel is currently unavailable.') ); + if ($cmd === 'refresh') { + if ($orig_record[0]['xchan_network'] === 'zot6') { + if (!Libzot::refresh($orig_record[0], App::get_channel())) + notice(t('Refresh failed - channel is currently unavailable.')); } else { // if you are on a different network we'll force a refresh of the connection basic info - Master::Summon(array('Notifier','permission_update',$contact_id)); + Master::Summon(['Notifier', 'permission_update', $contact_id]); } goaway(z_root() . '/connedit/' . $contact_id); } - if($cmd === 'block') { - if(abook_toggle_flag($orig_record[0],ABOOK_FLAG_BLOCKED)) { - $this->connedit_clone($a); + if ($cmd === 'block') { + if (abook_toggle_flag($orig_record[0], ABOOK_FLAG_BLOCKED)) { + $this->connedit_clone(); } else notice(t('Unable to set address book parameters.') . EOL); goaway(z_root() . '/connedit/' . $contact_id); } - if($cmd === 'ignore') { - if(abook_toggle_flag($orig_record[0],ABOOK_FLAG_IGNORED)) { - $this->connedit_clone($a); + if ($cmd === 'ignore') { + if (abook_toggle_flag($orig_record[0], ABOOK_FLAG_IGNORED)) { + $this->connedit_clone(); } else notice(t('Unable to set address book parameters.') . EOL); goaway(z_root() . '/connedit/' . $contact_id); } - if($cmd === 'archive') { - if(abook_toggle_flag($orig_record[0],ABOOK_FLAG_ARCHIVED)) { - $this->connedit_clone($a); + if ($cmd === 'archive') { + if (abook_toggle_flag($orig_record[0], ABOOK_FLAG_ARCHIVED)) { + $this->connedit_clone(); } else notice(t('Unable to set address book parameters.') . EOL); goaway(z_root() . '/connedit/' . $contact_id); } - if($cmd === 'hide') { - if(abook_toggle_flag($orig_record[0],ABOOK_FLAG_HIDDEN)) { - $this->connedit_clone($a); + if ($cmd === 'hide') { + if (abook_toggle_flag($orig_record[0], ABOOK_FLAG_HIDDEN)) { + $this->connedit_clone(); } else notice(t('Unable to set address book parameters.') . EOL); @@ -525,10 +413,10 @@ class Connedit extends Controller { // We'll prevent somebody from unapproving an already approved contact. // Though maybe somebody will want this eventually (??) - if($cmd === 'approve') { - if(intval($orig_record[0]['abook_pending'])) { - if(abook_toggle_flag($orig_record[0],ABOOK_FLAG_PENDING)) { - $this->connedit_clone($a); + if ($cmd === 'approve') { + if (intval($orig_record[0]['abook_pending'])) { + if (abook_toggle_flag($orig_record[0], ABOOK_FLAG_PENDING)) { + $this->connedit_clone(); } else notice(t('Unable to set address book parameters.') . EOL); @@ -537,132 +425,130 @@ class Connedit extends Controller { } - if($cmd === 'drop') { + if ($cmd === 'drop') { contact_remove(local_channel(), $orig_record[0]['abook_id']); - Master::Summon( [ 'Notifier', 'purge', local_channel(), $orig_record[0]['xchan_hash'] ] ); + Master::Summon(['Notifier', 'purge', local_channel(), $orig_record[0]['xchan_hash']]); Libsync::build_sync_packet(0 /* use the current local_channel */, - array('abook' => array(array( - 'abook_xchan' => $orig_record[0]['abook_xchan'], - 'entry_deleted' => true)) - ) + ['abook' => [[ + 'abook_xchan' => $orig_record[0]['abook_xchan'], + 'entry_deleted' => true]] + ] ); - info( t('Connection has been removed.') . EOL ); - if(x($_SESSION,'return_url')) + info(t('Connection has been removed.') . EOL); + if (x($_SESSION, 'return_url')) goaway(z_root() . '/' . $_SESSION['return_url']); goaway(z_root() . '/contacts'); } } - if(App::$poi) { + if (App::$poi) { $abook_prev = 0; $abook_next = 0; - $contact_id = App::$poi['abook_id']; - $contact = App::$poi; + $contact = App::$poi; $cn = q("SELECT abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 order by xchan_name", intval(local_channel()) ); - if($cn) { + if ($cn) { $pntotal = count($cn); - for($x = 0; $x < $pntotal; $x ++) { - if($cn[$x]['abook_id'] == $contact_id) { - if($x === 0) + for ($x = 0; $x < $pntotal; $x++) { + if ($cn[$x]['abook_id'] == $contact_id) { + if ($x === 0) $abook_prev = 0; else $abook_prev = $cn[$x - 1]['abook_id']; - if($x === $pntotal) + if ($x === $pntotal) $abook_next = 0; else - $abook_next = $cn[$x +1]['abook_id']; + $abook_next = $cn[$x + 1]['abook_id']; } } - } + } - $tools = array( + $tools = [ - 'view' => array( + 'view' => [ 'label' => t('View Profile'), 'url' => chanlink_cid($contact['abook_id']), 'sel' => '', - 'title' => sprintf( t('View %s\'s profile'), $contact['xchan_name']), - ), + 'title' => sprintf(t('View %s\'s profile'), $contact['xchan_name']), + ], - 'refresh' => array( + 'refresh' => [ 'label' => t('Refresh Permissions'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/refresh', 'sel' => '', 'title' => t('Fetch updated permissions'), - ), + ], - 'rephoto' => array( + 'rephoto' => [ 'label' => t('Refresh Photo'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/resetphoto', 'sel' => '', 'title' => t('Fetch updated photo'), - ), + ], - 'recent' => array( + 'recent' => [ 'label' => t('Recent Activity'), 'url' => z_root() . '/network/?f=&cid=' . $contact['abook_id'], 'sel' => '', 'title' => t('View recent posts and comments'), - ), + ], - 'block' => array( + 'block' => [ 'label' => (intval($contact['abook_blocked']) ? t('Unblock') : t('Block')), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/block', 'sel' => (intval($contact['abook_blocked']) ? 'active' : ''), 'title' => t('Block (or Unblock) all communications with this connection'), - 'info' => (intval($contact['abook_blocked']) ? t('This connection is blocked!') : ''), - ), + 'info' => (intval($contact['abook_blocked']) ? t('This connection is blocked!') : ''), + ], - 'ignore' => array( + 'ignore' => [ 'label' => (intval($contact['abook_ignored']) ? t('Unignore') : t('Ignore')), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/ignore', 'sel' => (intval($contact['abook_ignored']) ? 'active' : ''), 'title' => t('Ignore (or Unignore) all inbound communications from this connection'), - 'info' => (intval($contact['abook_ignored']) ? t('This connection is ignored!') : ''), - ), + 'info' => (intval($contact['abook_ignored']) ? t('This connection is ignored!') : ''), + ], - 'archive' => array( + 'archive' => [ 'label' => (intval($contact['abook_archived']) ? t('Unarchive') : t('Archive')), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/archive', 'sel' => (intval($contact['abook_archived']) ? 'active' : ''), 'title' => t('Archive (or Unarchive) this connection - mark channel dead but keep content'), - 'info' => (intval($contact['abook_archived']) ? t('This connection is archived!') : ''), - ), + 'info' => (intval($contact['abook_archived']) ? t('This connection is archived!') : ''), + ], - 'hide' => array( + 'hide' => [ 'label' => (intval($contact['abook_hidden']) ? t('Unhide') : t('Hide')), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/hide', 'sel' => (intval($contact['abook_hidden']) ? 'active' : ''), 'title' => t('Hide or Unhide this connection from your other connections'), - 'info' => (intval($contact['abook_hidden']) ? t('This connection is hidden!') : ''), - ), + 'info' => (intval($contact['abook_hidden']) ? t('This connection is hidden!') : ''), + ], - 'delete' => array( + 'delete' => [ 'label' => t('Delete'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/drop', 'sel' => '', 'title' => t('Delete this connection'), - ), - - ); + ], + ]; - if($contact['xchan_network'] === 'zot6') { + if ($contact['xchan_network'] === 'zot6') { $tools['fetchvc'] = [ 'label' => t('Fetch Vcard'), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/fetchvc', + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/fetchvc', 'sel' => '', 'title' => t('Fetch electronic calling card for this connection') ]; @@ -671,31 +557,16 @@ class Connedit extends Controller { $sections = []; - $sections['perms'] = [ - 'label' => t('Permissions'), - 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/?f=§ion=perms', - 'sel' => '', - 'title' => t('Open Individual Permissions section by default'), - ]; - - $self = false; - - if(intval($contact['abook_self'])) { - $self = true; - $abook_prev = $abook_next = 0; - } - - $vc = get_abconfig(local_channel(),$contact['abook_xchan'],'system','vcard'); + $vc = get_abconfig(local_channel(), $contact['abook_xchan'], 'system', 'vcard'); - $vctmp = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); - $vcard = (($vctmp) ? get_vcard_array($vctmp,$contact['abook_id']) : [] ); - if(! $vcard) + $vctmp = (($vc) ? Reader::read($vc) : null); + $vcard = (($vctmp) ? get_vcard_array($vctmp, $contact['abook_id']) : []); + if (!$vcard['fn']) $vcard['fn'] = $contact['xchan_name']; - $tpl = get_markup_template("abook_edit.tpl"); - if(Apps::system_app_installed(local_channel(),'Affinity Tool')) { + if (Apps::system_app_installed(local_channel(), 'Affinity Tool')) { $sections['affinity'] = [ 'label' => t('Affinity'), @@ -711,12 +582,12 @@ class Connedit extends Controller { t('Acquaintances'), t('All') ]; - call_hooks('affinity_labels',$labels); + call_hooks('affinity_labels', $labels); $label_str = ''; - if($labels) { - foreach($labels as $l) { - if($label_str) { + if ($labels) { + foreach ($labels as $l) { + if ($label_str) { $label_str .= ", '|'"; $label_str .= ", '" . $l . "'"; } @@ -729,14 +600,14 @@ class Connedit extends Controller { $slideval = intval($contact['abook_closeness']); - $slide = replace_macros($slider_tpl,array( - '$min' => 1, - '$val' => $slideval, + $slide = replace_macros($slider_tpl, [ + '$min' => 1, + '$val' => $slideval, '$labels' => $label_str, - )); + ]); } - if(feature_enabled(local_channel(),'connfilter')) { + if (feature_enabled(local_channel(), 'connfilter')) { $sections['filter'] = [ 'label' => t('Filter'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/?f=§ion=filter', @@ -745,195 +616,148 @@ class Connedit extends Controller { ]; } - $rating_val = 0; - $rating_text = ''; - - $xl = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1", - dbesc($channel['channel_hash']), - dbesc($contact['xchan_hash']) - ); - - if($xl) { - $rating_val = intval($xl[0]['xlink_rating']); - $rating_text = $xl[0]['xlink_rating_text']; - } - - $rating_enabled = get_config('system','rating_enabled'); - - if($rating_enabled) { - $rating = replace_macros(get_markup_template('rating_slider.tpl'),array( - '$min' => -10, - '$val' => $rating_val - )); - } - else { - $rating = false; - } - - - $perms = array(); - $channel = App::get_channel(); - + $perms = []; $global_perms = Permissions::Perms(); + $existing = get_all_perms(local_channel(), $contact['abook_xchan'], false); + $unapproved = ['pending', t('Approve this contact'), '', t('Accept contact to allow communication'), [t('No'), ('Yes')]]; + $multiprofs = ((feature_enabled(local_channel(), 'multi_profiles')) ? true : false); - $existing = get_all_perms(local_channel(),$contact['abook_xchan'],false); - - $unapproved = array('pending', t('Approve this connection'), '', t('Accept connection to allow communication'), array(t('No'),('Yes'))); - - $multiprofs = ((feature_enabled(local_channel(),'multi_profiles')) ? true : false); - - if($slide && !$multiprofs) + if ($slide && !$multiprofs) $affinity = t('Set Affinity'); - if(!$slide && $multiprofs) + if (!$slide && $multiprofs) $affinity = t('Set Profile'); - if($slide && $multiprofs) + if ($slide && $multiprofs) $affinity = t('Set Affinity & Profile'); $theirs = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'their_perms'", - intval(local_channel()), - dbesc($contact['abook_xchan']) + intval(local_channel()), + dbesc($contact['abook_xchan']) ); - $their_perms = array(); - if($theirs) { - foreach($theirs as $t) { + + $their_perms = []; + if ($theirs) { + foreach ($theirs as $t) { $their_perms[$t['k']] = $t['v']; } } - foreach($global_perms as $k => $v) { - $thisperm = get_abconfig(local_channel(),$contact['abook_xchan'],'my_perms',$k); -//fixme - - $checkinherited = PermissionLimits::Get(local_channel(),$k); - - // For auto permissions (when $self is true) we don't want to look at existing - // permissions because they are enabled for the channel owner - if((! $self) && ($existing[$k])) - $thisperm = "1"; - - + foreach ($global_perms as $k => $v) { + $thisperm = $existing[$k]; + $checkinherited = PermissionLimits::Get(local_channel(), $k); + $perms[] = ['perms_' . $k, $v, ((array_key_exists($k, $their_perms)) ? intval($their_perms[$k]) : ''), $thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '0' : '1'), '', $checkinherited]; + } + $pcat = new Permcat(local_channel()); + $pcatlist = $pcat->listing(); + $default_role = get_pconfig(local_channel(), 'system', 'default_permcat'); + $current_permcat = (($contact['abook_pending']) ? $default_role : $contact['abook_role']); - $perms[] = array('perms_' . $k, $v, ((array_key_exists($k,$their_perms)) ? intval($their_perms[$k]) : ''),$thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited); + if (!$current_permcat) { + notice(t('Please select a role for this contact!') . EOL); + $permcats[] = ''; } - $pcat = new Permcat(local_channel()); - $pcatlist = $pcat->listing(); - $permcats = []; - if($pcatlist) { - foreach($pcatlist as $pc) { + if ($pcatlist) { + foreach ($pcatlist as $pc) { $permcats[$pc['name']] = $pc['localname']; } } $locstr = locations_by_netid($contact['xchan_hash']); - if(! $locstr) + if (!$locstr) { $locstr = unpunify($contact['xchan_url']); + } $clone_warn = ''; - $clonable = in_array($contact['xchan_network'], ['zot6', 'rss']); - if(! $clonable) { + $clonable = in_array($contact['xchan_network'], ['zot6', 'rss']); + if (!$clonable) { $clone_warn = '<strong>'; $clone_warn .= ((intval($contact['abook_not_here'])) - ? t('This connection is unreachable from this location.') - : t('This connection may be unreachable from other channel locations.') + ? t('This contact is unreachable from this location.') + : t('This contact may be unreachable from other channel locations.') ); $clone_warn .= '</strong><br>' . t('Location independence is not supported by their network.'); } - - - if(intval($contact['abook_not_here']) && $unclonable) - $not_here = t('This connection is unreachable from this location. Location independence is not supported by their network.'); - $o .= replace_macros($tpl, [ - '$header' => (($self) ? t('Connection Default Permissions') : sprintf( t('Connection: %s'),$contact['xchan_name'])), - '$autoperms' => array('autoperms',t('Apply these permissions automatically'), ((get_pconfig(local_channel(),'system','autoperms')) ? 1 : 0), t('Connection requests will be approved without your interaction'), $yes_no), - '$permcat' => [ 'permcat', t('Permission role'), '', '<span class="loading invisible">' . t('Loading') . '<span class="jumping-dots"><span class="dot-1">.</span><span class="dot-2">.</span><span class="dot-3">.</span></span></span>',$permcats ], - '$permcat_new' => t('Add permission role'), - '$permcat_enable' => Apps::system_app_installed(local_channel(), 'Permission Categories'), - '$addr' => unpunify($contact['xchan_addr']), - '$primeurl' => unpunify($contact['xchan_url']), - '$section' => $section, - '$sections' => $sections, - '$vcard' => $vcard, - '$addr_text' => t('This connection\'s primary address is'), - '$loc_text' => t('Available locations:'), - '$locstr' => $locstr, - '$unclonable' => $clone_warn, - '$notself' => (($self) ? '' : '1'), - '$self' => (($self) ? '1' : ''), - '$autolbl' => t('The permissions indicated on this page will be applied to all new connections.'), - '$tools_label' => t('Connection Tools'), - '$tools' => (($self) ? '' : $tools), - '$lbl_slider' => t('Slide to adjust your degree of friendship'), - '$lbl_rating' => t('Rating'), - '$lbl_rating_label' => t('Slide to adjust your rating'), - '$lbl_rating_txt' => t('Optionally explain your rating'), - '$connfilter' => feature_enabled(local_channel(),'connfilter'), + '$header' => sprintf(t('Contact: %s'), $contact['xchan_name']), + '$permcat' => ['permcat', t('Contact role'), $current_permcat, '', $permcats], + '$permcat_new' => t('Manage contact roles'), + '$permcat_value' => bin2hex($current_permcat), + '$addr' => unpunify($contact['xchan_addr']), + '$primeurl' => unpunify($contact['xchan_url']), + '$section' => $section, + '$sections' => $sections, + '$vcard' => $vcard, + '$addr_text' => t('This contacts\'s primary address is'), + '$loc_text' => t('Available locations:'), + '$locstr' => $locstr, + '$unclonable' => $clone_warn, + '$notself' => '1', + '$self' => '', + '$autolbl' => t('The permissions indicated on this page will be applied to all new connections.'), + '$tools_label' => t('Contact Tools'), + '$tools' => $tools, + '$lbl_slider' => t('Slide to adjust your degree of friendship'), + '$connfilter' => feature_enabled(local_channel(), 'connfilter'), '$connfilter_label' => t('Custom Filter'), - '$incl' => array('abook_incl',t('Only import posts with this text'), $contact['abook_incl'],t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')), - '$excl' => array('abook_excl',t('Do not import posts with this text'), $contact['abook_excl'],t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')), - '$rating_text' => array('rating_text', t('Optionally explain your rating'),$rating_text,''), - '$rating_info' => t('This information is public!'), - '$rating' => $rating, - '$rating_val' => $rating_val, - '$slide' => $slide, - '$affinity' => $affinity, - '$pending_label' => t('Connection Pending Approval'), - '$is_pending' => (intval($contact['abook_pending']) ? 1 : ''), - '$unapproved' => $unapproved, - '$inherited' => t('inherited'), - '$submit' => t('Submit'), - '$lbl_vis2' => sprintf( t('Please choose the profile you would like to display to %s when viewing your profile securely.'), $contact['xchan_name']), - '$close' => (($contact['abook_closeness']) ? $contact['abook_closeness'] : 80), - '$them' => t('Their Settings'), - '$me' => t('My Settings'), - '$perms' => $perms, - '$permlbl' => t('Individual Permissions'), - '$permnote' => t('Some permissions may be inherited from your channel\'s <a href="settings"><strong>privacy settings</strong></a>, which have higher priority than individual settings. You can <strong>not</strong> change those settings here.'), - '$permnote_self' => t('Some permissions may be inherited from your channel\'s <a href="settings"><strong>privacy settings</strong></a>, which have higher priority than individual settings. You can change those settings here but they wont have any impact unless the inherited setting changes.'), - '$lastupdtext' => t('Last update:'), - '$last_update' => relative_date($contact['abook_connected']), - '$profile_select' => contact_profile_assign($contact['abook_profile']), - '$multiprofs' => $multiprofs, - '$contact_id' => $contact['abook_id'], - '$name' => $contact['xchan_name'], - '$abook_prev' => $abook_prev, - '$abook_next' => $abook_next, - '$vcard_label' => t('Details'), - '$displayname' => $displayname, - '$name_label' => t('Name'), - '$org_label' => t('Organisation'), - '$title_label' => t('Title'), - '$tel_label' => t('Phone'), - '$email_label' => t('Email'), - '$impp_label' => t('Instant messenger'), - '$url_label' => t('Website'), - '$adr_label' => t('Address'), - '$note_label' => t('Note'), - '$mobile' => t('Mobile'), - '$home' => t('Home'), - '$work' => t('Work'), - '$other' => t('Other'), - '$add_card' => t('Add Contact'), - '$add_field' => t('Add Field'), - '$create' => t('Create'), - '$update' => t('Update'), - '$delete' => t('Delete'), - '$cancel' => t('Cancel'), - '$po_box' => t('P.O. Box'), - '$extra' => t('Additional'), - '$street' => t('Street'), - '$locality' => t('Locality'), - '$region' => t('Region'), - '$zip_code' => t('ZIP Code'), - '$country' => t('Country') + '$incl' => ['abook_incl', t('Only import posts with this text'), $contact['abook_incl'], t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')], + '$excl' => ['abook_excl', t('Do not import posts with this text'), $contact['abook_excl'], t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')], + '$slide' => $slide, + '$affinity' => $affinity, + '$pending_label' => t('Contact Pending Approval'), + '$is_pending' => (intval($contact['abook_pending']) ? 1 : ''), + '$unapproved' => $unapproved, + '$inherited' => t('inherited'), + '$submit' => ((intval($contact['abook_pending'])) ? t('Approve contact') : t('Submit')), + '$lbl_vis2' => sprintf(t('Please choose the profile you would like to display to %s when viewing your profile securely.'), $contact['xchan_name']), + '$close' => (($contact['abook_closeness']) ? $contact['abook_closeness'] : 80), + '$them' => t('Their'), + '$me' => t('My'), + '$perms' => $perms, + '$permlbl' => t('Individual Permissions'), + '$permnote' => t('Some permissions may be inherited from your channel\'s <a href="settings"><strong>privacy settings</strong></a>, which have higher priority than individual settings. You can <strong>not</strong> change those settings here.'), + '$permnote_self' => t('Some permissions may be inherited from your channel\'s <a href="settings"><strong>privacy settings</strong></a>, which have higher priority than individual settings. You can change those settings here but they wont have any impact unless the inherited setting changes.'), + '$lastupdtext' => t('Last update:'), + '$last_update' => relative_date($contact['abook_connected']), + '$profile_select' => contact_profile_assign($contact['abook_profile']), + '$multiprofs' => $multiprofs, + '$contact_id' => $contact['abook_id'], + '$name' => $contact['xchan_name'], + '$abook_prev' => $abook_prev, + '$abook_next' => $abook_next, + '$vcard_label' => t('Details'), + '$name_label' => t('Name'), + '$org_label' => t('Organisation'), + '$title_label' => t('Title'), + '$tel_label' => t('Phone'), + '$email_label' => t('Email'), + '$impp_label' => t('Instant messenger'), + '$url_label' => t('Website'), + '$adr_label' => t('Address'), + '$note_label' => t('Note'), + '$mobile' => t('Mobile'), + '$home' => t('Home'), + '$work' => t('Work'), + '$other' => t('Other'), + '$add_card' => t('Add Contact'), + '$add_field' => t('Add Field'), + '$create' => t('Create'), + '$update' => t('Update'), + '$delete' => t('Delete'), + '$cancel' => t('Cancel'), + '$po_box' => t('P.O. Box'), + '$extra' => t('Additional'), + '$street' => t('Street'), + '$locality' => t('Locality'), + '$region' => t('Region'), + '$zip_code' => t('ZIP Code'), + '$country' => t('Country') ]); - $arr = array('contact' => $contact,'output' => $o); + $arr = ['contact' => $contact, 'output' => $o]; call_hooks('contact_edit', $arr); diff --git a/Zotlabs/Module/Contactedit.php b/Zotlabs/Module/Contactedit.php new file mode 100644 index 000000000..b09b5b1ec --- /dev/null +++ b/Zotlabs/Module/Contactedit.php @@ -0,0 +1,668 @@ +<?php + +namespace Zotlabs\Module; + +/* @file Cobtactedit.php + * @brief In this file the connection-editor form is generated and evaluated. + * + * + */ + +use App; +use Sabre\VObject\Reader; +use Zotlabs\Lib\Apps; +use Zotlabs\Lib\Libzot; +use Zotlabs\Lib\Libsync; +use Zotlabs\Daemon\Master; +use Zotlabs\Web\Controller; +use Zotlabs\Access\Permissions; +use Zotlabs\Access\PermissionLimits; +use Zotlabs\Web\HTTPHeaders; +use Zotlabs\Lib\Permcat; +use Zotlabs\Lib\AccessList; + +require_once('include/socgraph.php'); +require_once('include/selectors.php'); +require_once('include/group.php'); +require_once('include/photos.php'); + +class Contactedit extends Controller { + + /* @brief Initialize the connection-editor + * + * + */ + + function init() { + + if (!local_channel()) + return; + + if ((argc() >= 2) && intval(argv(1))) { + $r = q("SELECT abook.*, xchan.* FROM abook LEFT JOIN xchan ON abook_xchan = xchan_hash + WHERE abook_channel = %d AND abook_id = %d AND abook_self = 0 AND xchan_deleted = 0", + intval(local_channel()), + intval(argv(1)) + ); + if (!$r) { + json_return_and_die([ + 'success' => false, + 'message' => t('Invalid abook_id') + ]); + } + + App::$poi = $r[0]; + + } + } + + + /* @brief Evaluate posted values and set changes + * + */ + + function post() { + + if (!local_channel()) + return; + + $contact_id = intval(argv(1)); + if (!$contact_id) + return; + + $channel = App::get_channel(); + + $contact = App::$poi; + + if (!$contact) { + notice(t('Could not access contact record.') . EOL); + killme(); + } + + call_hooks('contact_edit_post', $_REQUEST); + + if (Apps::system_app_installed(local_channel(), 'Privacy Groups')) { + $pgrp_ids = q("SELECT id FROM pgrp WHERE deleted = 0 AND uid = %d", + intval(local_channel()) + ); + + foreach($pgrp_ids as $pgrp) { + if (array_key_exists('pgrp_id_' . $pgrp['id'], $_REQUEST)) { + AccessList::member_add(local_channel(), '', $contact['abook_xchan'], $pgrp['id']); + } + else { + AccessList::member_remove(local_channel(), '', $contact['abook_xchan'], $pgrp['id']); + } + } + } + + $profile_id = ((array_key_exists('profile_assign', $_REQUEST)) ? $_REQUEST['profile_assign'] : $contact['abook_profile']); + + if ($profile_id) { + $r = q("SELECT profile_guid FROM profile WHERE profile_guid = '%s' AND uid = %d LIMIT 1", + dbesc($profile_id), + intval(local_channel()) + ); + if (!count($r)) { + notice(t('Could not locate selected profile.') . EOL); + return; + } + } + + $abook_incl = ((array_key_exists('abook_incl', $_REQUEST)) ? escape_tags($_REQUEST['abook_incl']) : $contact['abook_incl']); + $abook_excl = ((array_key_exists('abook_excl', $_REQUEST)) ? escape_tags($_REQUEST['abook_excl']) : $contact['abook_excl']); + $abook_role = ((array_key_exists('permcat', $_REQUEST)) ? escape_tags($_REQUEST['permcat']) : $contact['abook_role']); + + if (!array_key_exists('closeness', $_REQUEST)) { + $_REQUEST['closeness'] = 80; + } + + $closeness = intval($_REQUEST['closeness']); + + if ($closeness < 0 || $closeness > 99) { + $closeness = 80; + } + + $new_friend = ((intval($contact['abook_pending'])) ? true : false); + + \Zotlabs\Lib\Permcat::assign($channel, $abook_role, [$contact['abook_xchan']]); + + $abook_pending = (($new_friend) ? 0 : $contact['abook_pending']); + + $r = q("UPDATE abook SET abook_profile = '%s', abook_closeness = %d, abook_pending = %d, + abook_incl = '%s', abook_excl = '%s' + where abook_id = %d AND abook_channel = %d", + dbesc($profile_id), + intval($closeness), + intval($abook_pending), + dbesc($abook_incl), + dbesc($abook_excl), + intval($contact_id), + intval(local_channel()) + ); + + $_REQUEST['success'] = false; + + if ($r) { + $_REQUEST['success'] = true; + } + + + if (!intval($contact['abook_self'])) { + if ($new_friend) { + Master::Summon(['Notifier', 'permission_accept', $contact_id]); + } + + Master::Summon([ + 'Notifier', + (($new_friend) ? 'permission_create' : 'permission_update'), + $contact_id + ]); + } + + if ($new_friend) { + $default_group = $channel['channel_default_group']; + if ($default_group) { + $g = AccessList::by_hash(local_channel(), $default_group); + if ($g) { + AccessList::member_add(local_channel(), '', $contact['abook_xchan'], $g['id']); + } + } + + // Check if settings permit ("post new friend activity" is allowed, and + // friends in general or this friend in particular aren't hidden) + // and send out a new friend activity + + $pr = q("select * from profile where uid = %d and is_default = 1 and hide_friends = 0", + intval($channel['channel_id']) + ); + if (($pr) && (!intval($contact['abook_hidden'])) && (intval(get_pconfig($channel['channel_id'], 'system', 'post_newfriend')))) { + $xarr = []; + + $xarr['item_wall'] = 1; + $xarr['item_origin'] = 1; + $xarr['item_thread_top'] = 1; + $xarr['owner_xchan'] = $xarr['author_xchan'] = $channel['channel_hash']; + $xarr['allow_cid'] = $channel['channel_allow_cid']; + $xarr['allow_gid'] = $channel['channel_allow_gid']; + $xarr['deny_cid'] = $channel['channel_deny_cid']; + $xarr['deny_gid'] = $channel['channel_deny_gid']; + $xarr['item_private'] = (($xarr['allow_cid'] || $xarr['allow_gid'] || $xarr['deny_cid'] || $xarr['deny_gid']) ? 1 : 0); + + $xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . $contact['xchan_url'] . ']' . $contact['xchan_name'] . '[/zrl]'; + + $xarr['body'] .= "\n\n\n" . '[zrl=' . $contact['xchan_url'] . '][zmg=80x80]' . $contact['xchan_photo_m'] . '[/zmg][/zrl]'; + + post_activity_item($xarr); + + } + + // pull in a bit of content if there is any to pull in + Master::Summon(['Onepoll', $contact_id]); + + } + + // Refresh the structure in memory with the new data + $this->init(); + + if ($new_friend) { + $arr = ['channel_id' => local_channel(), 'abook' => App::$poi]; + call_hooks('accept_follow', $arr); + } + + $this->contactedit_clone(); + $this->get(); + + killme(); + + return; + + } + + + /* @brief Generate content of contact edit page + * + * + */ + + function get() { + + if (!local_channel()) { + killme(); + } + + if (!App::$poi) { + killme(); + } + + + $channel = App::get_channel(); + $contact_id = App::$poi['abook_id']; + $contact = App::$poi; + $section = ((array_key_exists('section', $_REQUEST)) ? $_REQUEST['section'] : 'roles'); + $sub_section = ((array_key_exists('sub_section', $_REQUEST)) ? $_REQUEST['sub_section'] : ''); + + + if (argc() == 3) { + $cmd = argv(2); + $ret = $this->do_action($contact, $cmd); + $contact = App::$poi; + + $tools_html = replace_macros(get_markup_template("contact_edit_tools.tpl"), [ + '$tools_label' => t('Contact Tools'), + '$tools' => $this->get_tools($contact), + ]); + + $ret['tools'] = $tools_html; + + json_return_and_die($ret); + } + + $groups = []; + + if (Apps::system_app_installed(local_channel(), 'Privacy Groups')) { + + $r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", + intval(local_channel()) + ); + + $member_of = AccessList::containing(local_channel(), $contact['xchan_hash']); + + if ($r) { + foreach ($r as $rr) { + $default_group = false; + if ($rr['hash'] === $channel['channel_default_group']) { + $default_group = true; + } + + $groups[] = [ + 'pgrp_id_' . $rr['id'], + $rr['gname'], + // if it's a new contact preset the default group if we have one + (($default_group && $contact['abook_pending']) ? 1 : in_array($rr['id'], $member_of)), + '', + [t('No'), t('Yes')] + ]; + } + } + } + + $slide = ''; + + if (Apps::system_app_installed(local_channel(), 'Affinity Tool')) { + + $labels = [ + t('Me'), + t('Family'), + t('Friends'), + t('Acquaintances'), + t('All') + ]; + call_hooks('affinity_labels', $labels); + $label_str = ''; + + if ($labels) { + foreach ($labels as $l) { + if ($label_str) { + $label_str .= ", '|'"; + $label_str .= ", '" . $l . "'"; + } + else + $label_str .= "'" . $l . "'"; + } + } + + $slider_tpl = get_markup_template('contact_slider.tpl'); + + $slideval = intval($contact['abook_closeness']); + + $slide = replace_macros($slider_tpl, [ + '$min' => 1, + '$val' => $slideval, + '$labels' => $label_str, + ]); + } + + $perms = []; + $global_perms = Permissions::Perms(); + $existing = get_all_perms(local_channel(), $contact['abook_xchan'], false); + $unapproved = ['pending', t('Approve this contact'), '', t('Accept contact to allow communication'), [t('No'), ('Yes')]]; + $multiprofs = ((feature_enabled(local_channel(), 'multi_profiles')) ? true : false); + + $theirs = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'their_perms'", + intval(local_channel()), + dbesc($contact['abook_xchan']) + ); + + $their_perms = []; + if ($theirs) { + foreach ($theirs as $t) { + $their_perms[$t['k']] = $t['v']; + } + } + + foreach ($global_perms as $k => $v) { + $thisperm = $existing[$k]; + $checkinherited = PermissionLimits::Get(local_channel(), $k); + $perms[] = ['perms_' . $k, $v, ((array_key_exists($k, $their_perms)) ? intval($their_perms[$k]) : ''), $thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '0' : '1'), '', $checkinherited]; + } + + $pcat = new Permcat(local_channel()); + $pcatlist = $pcat->listing(); + $default_role = get_pconfig(local_channel(), 'system', 'default_permcat'); + $current_permcat = (($contact['abook_pending']) ? $default_role : $contact['abook_role']); + + $roles_dict = []; + foreach ($pcatlist as $role) { + $roles_dict[$role['name']] = $role['localname']; + } + + + if (!$current_permcat) { + notice(t('Please select a role for this contact!') . EOL); + $permcats[] = ''; + } + + if ($pcatlist) { + foreach ($pcatlist as $pc) { + $permcats[$pc['name']] = $pc['localname']; + } + } + + $locstr = locations_by_netid($contact['xchan_hash']); + if (!$locstr) { + $locstr = unpunify($contact['xchan_url']); + } + + $clone_warn = ''; + $clonable = in_array($contact['xchan_network'], ['zot6', 'rss']); + if (!$clonable) { + $clone_warn = '<strong>'; + $clone_warn .= ((intval($contact['abook_not_here'])) + ? t('This contact is unreachable from this location.') + : t('This contact may be unreachable from other channel locations.') + ); + $clone_warn .= '</strong><br>' . t('Location independence is not supported by their network.'); + } + + $header_card = '<img src="' . $contact['xchan_photo_s'] . '" class="rounded" style="width: 3rem; height: 3rem;"> ' . $contact['xchan_name']; + + $header_html = replace_macros(get_markup_template("contact_edit_header.tpl"), [ + '$img_src' => $contact['xchan_photo_s'], + '$name' => $contact['xchan_name'], + '$addr' => (($contact['xchan_addr']) ? $contact['xchan_addr'] : $contact['xchan_url']), + '$href' => ((is_matrix_url($contact['xchan_url'])) ? zid($contact['xchan_url']) : $contact['xchan_url']), + '$link_label' => t('View profile'), + '$is_group' => $contact['xchan_pubforum'], + '$group_label' => t('This is a group/forum channel') + ]); + + $tools_html = replace_macros(get_markup_template("contact_edit_tools.tpl"), [ + '$tools_label' => t('Contact Tools'), + '$tools' => $this->get_tools($contact), + ]); + + $tpl = get_markup_template("contact_edit.tpl"); + + $o = replace_macros($tpl, [ + '$permcat' => ['permcat', t('Select a role for this contact'), $current_permcat, '', $permcats], + '$permcat_new' => t('Contact roles'), + '$permcat_value' => bin2hex($current_permcat), +// '$addr' => unpunify($contact['xchan_addr']), +// '$primeurl' => unpunify($contact['xchan_url']), + '$section' => $section, + '$sub_section' => $sub_section, + '$groups' => $groups, +// '$addr_text' => t('This contacts\'s primary address is'), +// '$loc_text' => t('Available locations:'), +// '$locstr' => $locstr, +// '$unclonable' => $clone_warn, + '$lbl_slider' => t('Slide to adjust your degree of friendship'), + '$connfilter' => feature_enabled(local_channel(), 'connfilter'), + '$connfilter_label' => t('Custom Filter'), + '$incl' => ['abook_incl', t('Only import posts with this text'), $contact['abook_incl'], t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')], + '$excl' => ['abook_excl', t('Do not import posts with this text'), $contact['abook_excl'], t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')], + '$slide' => $slide, +// '$pending_label' => t('Contact Pending Approval'), +// '$is_pending' => (intval($contact['abook_pending']) ? 1 : ''), +// '$unapproved' => $unapproved, + '$submit' => ((intval($contact['abook_pending'])) ? t('Approve contact') : t('Submit')), + '$close' => (($contact['abook_closeness']) ? $contact['abook_closeness'] : 80), + '$them' => t('Their'), + '$me' => t('My'), + '$perms' => $perms, +// '$lastupdtext' => t('Last update:'), +// '$last_update' => relative_date($contact['abook_connected']), + '$profile_select' => contact_profile_assign($contact['abook_profile']), + '$multiprofs' => $multiprofs, + '$contact_id' => $contact['abook_id'], +// '$name' => $contact['xchan_name'], + ]); + + $arr = ['contact' => $contact, 'output' => $o]; + + call_hooks('contact_edit', $arr); + + if (is_ajax()) { + json_return_and_die([ + 'success' => ((intval($_REQUEST['success'])) ? intval($_REQUEST['success']) : 1), + 'message' => (($_REQUEST['success']) ? t('Contact updated') : t('Contact update failed')), + 'id' => $contact_id, + 'title' => $header_html, + 'role' => ((intval($contact['abook_pending'])) ? '' : $roles_dict[$current_permcat]), + 'body' => $arr['output'], + 'tools' => $tools_html, + 'submit' => ((intval($contact['abook_pending'])) ? t('Approve connection') : t('Submit')), + 'pending' => intval($contact['abook_pending']) + ]); + } + + return $arr['output']; + + } + + function contactedit_clone() { + + if (!App::$poi) + return; + + $channel = App::get_channel(); + + $clone = App::$poi; + + 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]]); + } + + function do_action($contact, $cmd) { + $ret = [ + 'sucess' => false, + 'message' => '' + ]; + + if ($cmd === 'resetphoto') { + q("update xchan set xchan_photo_date = '2001-01-01 00:00:00' where xchan_hash = '%s'", + dbesc($contact['xchan_hash']) + ); + $cmd = 'refresh'; + } + + if ($cmd === 'refresh') { + if ($contact['xchan_network'] === 'zot6') { + if (Libzot::refresh($contact, App::get_channel())) { + $ret['success'] = true; + $ret['message'] = t('Refresh succeeded'); + } + else { + $ret['message'] = t('Refresh failed - channel is currently unavailable'); + } + } + else { + // if you are on a different network we'll force a refresh of the connection basic info + Master::Summon(['Notifier', 'permission_update', $contact['abook_id']]); + $ret['success'] = true; + $ret['message'] = t('Refresh succeeded'); + } + + return $ret; + } + + if ($cmd === 'block') { + if (abook_toggle_flag($contact, ABOOK_FLAG_BLOCKED)) { + $this->init(); // refresh data + + $this->contactedit_clone(); + $ret['success'] = true; + $ret['message'] = t('Block status updated'); + } + else { + $ret['success'] = false; + $ret['message'] = t('Block failed'); + } + return $ret; + } + + if ($cmd === 'ignore') { + if (abook_toggle_flag($contact, ABOOK_FLAG_IGNORED)) { + $this->init(); // refresh data + + $this->contactedit_clone(); + $ret['success'] = true; + $ret['message'] = t('Ignore status updated'); + } + else { + $ret['success'] = false; + $ret['message'] = t('Ignore failed'); + } + return $ret; + } + + if ($cmd === 'archive') { + if (abook_toggle_flag($contact, ABOOK_FLAG_ARCHIVED)) { + $this->init(); // refresh data + + $this->contactedit_clone(); + $ret['success'] = true; + $ret['message'] = t('Archive status updated'); + } + else { + $ret['success'] = false; + $ret['message'] = t('Archive failed'); + } + return $ret; + } + + if ($cmd === 'hide') { + if (abook_toggle_flag($contact, ABOOK_FLAG_HIDDEN)) { + $this->init(); // refresh data + + $this->contactedit_clone(); + $ret['success'] = true; + $ret['message'] = t('Hide status updated'); + } + else { + $ret['success'] = false; + $ret['message'] = t('Hide failed'); + } + return $ret; + } + + // We'll prevent somebody from unapproving an already approved contact. + // Though maybe somebody will want this eventually (??) + + //if ($cmd === 'approve') { + //if (intval($contact['abook_pending'])) { + //if (abook_toggle_flag($contact, ABOOK_FLAG_PENDING)) { + //$this->contactedit_clone(); + //} + //else + //notice(t('Unable to set address book parameters.') . EOL); + //} + //goaway(z_root() . '/connedit/' . $contact_id); + //} + + + if ($cmd === 'drop') { + + if (contact_remove(local_channel(), $contact['abook_id'])) { + + Master::Summon(['Notifier', 'purge', local_channel(), $contact['xchan_hash']]); + Libsync::build_sync_packet(0 /* use the current local_channel */, + ['abook' => [ + [ + 'abook_xchan' => $contact['abook_xchan'], + 'entry_deleted' => true + ] + ] + ]); + + $ret['success'] = true; + $ret['message'] = t('Contact removed'); + } + else { + $ret['success'] = false; + $ret['message'] = t('Delete failed'); + } + return $ret; + } + } + + function get_tools($contact) { + return [ + + 'refresh' => [ + 'label' => t('Refresh Permissions'), + 'title' => t('Fetch updated permissions'), + ], + + 'rephoto' => [ + 'label' => t('Refresh Photo'), + 'title' => t('Fetch updated photo'), + ], + + + 'block' => [ + 'label' => (intval($contact['abook_blocked']) ? t('Unblock') : t('Block')), + 'sel' => (intval($contact['abook_blocked']) ? 'active' : ''), + 'title' => t('Block (or Unblock) all communications with this connection'), + 'info' => (intval($contact['abook_blocked']) ? t('This connection is blocked!') : ''), + ], + + 'ignore' => [ + 'label' => (intval($contact['abook_ignored']) ? t('Unignore') : t('Ignore')), + 'sel' => (intval($contact['abook_ignored']) ? 'active' : ''), + 'title' => t('Ignore (or Unignore) all inbound communications from this connection'), + 'info' => (intval($contact['abook_ignored']) ? t('This connection is ignored!') : ''), + ], + + 'archive' => [ + 'label' => (intval($contact['abook_archived']) ? t('Unarchive') : t('Archive')), + 'sel' => (intval($contact['abook_archived']) ? 'active' : ''), + 'title' => t('Archive (or Unarchive) this connection - mark channel dead but keep content'), + 'info' => (intval($contact['abook_archived']) ? t('This connection is archived!') : ''), + ], + + 'hide' => [ + 'label' => (intval($contact['abook_hidden']) ? t('Unhide') : t('Hide')), + 'sel' => (intval($contact['abook_hidden']) ? 'active' : ''), + 'title' => t('Hide or Unhide this connection from your other connections'), + 'info' => (intval($contact['abook_hidden']) ? t('This connection is hidden!') : ''), + ], + + 'delete' => [ + 'label' => t('Delete'), + 'sel' => '', + 'title' => t('Delete this connection'), + ], + + ]; + } + +} diff --git a/Zotlabs/Module/Contactgroup.php b/Zotlabs/Module/Contactgroup.php index 36aaf7da0..3e88179fb 100644 --- a/Zotlabs/Module/Contactgroup.php +++ b/Zotlabs/Module/Contactgroup.php @@ -1,17 +1,17 @@ <?php namespace Zotlabs\Module; -require_once('include/group.php'); +use Zotlabs\Lib\AccessList; +use Zotlabs\Web\Controller; - -class Contactgroup extends \Zotlabs\Web\Controller { +class Contactgroup extends Controller { function get() { - + if(! local_channel()) { killme(); } - + if((argc() > 2) && (intval(argv(1))) && (argv(2))) { $r = q("SELECT abook_xchan from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1", dbesc(base64url_decode(argv(2))), @@ -20,9 +20,9 @@ class Contactgroup extends \Zotlabs\Web\Controller { if($r) $change = $r[0]['abook_xchan']; } - + if((argc() > 1) && (intval(argv(1)))) { - + $r = q("SELECT * FROM pgrp WHERE id = %d AND uid = %d AND deleted = 0 LIMIT 1", intval(argv(1)), intval(local_channel()) @@ -30,25 +30,25 @@ class Contactgroup extends \Zotlabs\Web\Controller { if(! $r) { killme(); } - + $group = $r[0]; - $members = group_get_members($group['id']); + $members = AccessList::members(local_channel(), $group['id']); $preselected = array(); if(count($members)) { foreach($members as $member) $preselected[] = $member['xchan_hash']; } - + if($change) { if(in_array($change,$preselected)) { - group_rmv_member(local_channel(),$group['gname'],$change); + AccessList::member_remove(local_channel(),$group['gname'],$change); } else { - group_add_member(local_channel(),$group['gname'],$change); + AccessList::member_add(local_channel(),$group['gname'],$change); } } } - + killme(); } } diff --git a/Zotlabs/Module/Defperms.php b/Zotlabs/Module/Defperms.php index 309a5a65a..70270d36b 100644 --- a/Zotlabs/Module/Defperms.php +++ b/Zotlabs/Module/Defperms.php @@ -8,7 +8,6 @@ use Zotlabs\Lib\Libsync; require_once('include/socgraph.php'); require_once('include/selectors.php'); -require_once('include/group.php'); require_once('include/photos.php'); class Defperms extends Controller { @@ -23,8 +22,8 @@ class Defperms extends Controller { if(! local_channel()) return; - if(! Apps::system_app_installed(local_channel(), 'Default Permissions')) - return; + //if(! Apps::system_app_installed(local_channel(), 'Default Permissions')) + // return; $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash @@ -50,8 +49,8 @@ class Defperms extends Controller { if(! local_channel()) return; - if(! Apps::system_app_installed(local_channel(), 'Default Permissions')) - return; + //if(! Apps::system_app_installed(local_channel(), 'Default Permissions')) + // return; $contact_id = intval(argv(1)); if(! $contact_id) @@ -183,12 +182,12 @@ class Defperms extends Controller { return login(); } - if(! Apps::system_app_installed(local_channel(), 'Default Permissions')) { - //Do not display any associated widgets at this point - App::$pdl = ''; - $papp = Apps::get_papp('Default Permissions'); - return Apps::app_render($papp, 'module'); - } + //~ if(! Apps::system_app_installed(local_channel(), 'Default Permissions')) { + //~ //Do not display any associated widgets at this point + //~ App::$pdl = ''; + //~ $papp = Apps::get_papp('Default Permissions'); + //~ return Apps::app_render($papp, 'module'); + //~ } $section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : ''); $channel = App::get_channel(); diff --git a/Zotlabs/Module/Follow.php b/Zotlabs/Module/Follow.php index 4fe20f56b..94daa4c70 100644 --- a/Zotlabs/Module/Follow.php +++ b/Zotlabs/Module/Follow.php @@ -108,7 +108,7 @@ class Follow extends Controller { } Libsync::build_sync_packet(0, [ 'abook' => [ $clone ] ], true); - $can_view_stream = their_perms_contains($channel['channel_id'],$clone['abook_xchan'],'view_stream'); + $can_view_stream = intval(get_abconfig($channel['channel_id'], $clone['abook_xchan'], 'their_perms', 'view_stream')); // If we can view their stream, pull in some posts @@ -117,7 +117,7 @@ class Follow extends Controller { } if ($interactive) { - goaway(z_root() . '/connedit/' . $result['abook']['abook_id'] . '?follow=1'); + goaway(z_root() . '/connections#' . $result['abook']['abook_id']); } else { json_return_and_die([ 'success' => true ]); diff --git a/Zotlabs/Module/Group.php b/Zotlabs/Module/Group.php index a2d55a325..4c6b0c838 100644 --- a/Zotlabs/Module/Group.php +++ b/Zotlabs/Module/Group.php @@ -5,8 +5,7 @@ use App; use Zotlabs\Web\Controller; use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; - -require_once('include/group.php'); +use Zotlabs\Lib\AccessList; class Group extends Controller { @@ -41,16 +40,17 @@ class Group extends Controller { $name = notags(trim($_POST['groupname'])); $public = intval($_POST['public']); - $r = group_add(local_channel(),$name,$public); + $r = AccessList::add(local_channel(),$name,$public); + $group_hash = $r; + if($r) { info( t('Privacy group created.') . EOL ); } else { notice( t('Could not create privacy group.') . EOL ); } - goaway(z_root() . '/group'); - } + if((argc() == 2) && (intval(argv(1)))) { check_form_security_token_redirectOnErr('/group', 'group_edit'); @@ -65,10 +65,11 @@ class Group extends Controller { } $group = $r[0]; $groupname = notags(trim($_POST['groupname'])); + $group_hash = $group['hash']; $public = intval($_POST['public']); $hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ]; - call_hooks ('privacygroup_extras_post',$hookinfo); + call_hooks('privacygroup_extras_post',$hookinfo); if((strlen($groupname)) && (($groupname != $group['gname']) || ($public != $group['visible']))) { $r = q("UPDATE pgrp SET gname = '%s', visible = %d WHERE uid = %d AND id = %d", @@ -79,13 +80,25 @@ class Group extends Controller { ); if($r) info( t('Privacy group updated.') . EOL ); + } + } + $channel = App::get_channel(); - Libsync::build_sync_packet(local_channel(),null,true); - } + $default_group = ((isset($_POST['set_default_group'])) ? $group_hash : (($channel['channel_default_group'] === $group_hash) ? '' : $channel['channel_default_group'])); + $default_acl = ((isset($_POST['set_default_acl'])) ? '<' . $group_hash . '>' : (($channel['channel_allow_gid'] === '<' . $group_hash . '>') ? '' : $channel['channel_allow_gid'])); + + q("update channel set channel_default_group = '%s', channel_allow_gid = '%s' + where channel_id = %d", + dbesc($default_group), + dbesc($default_acl), + intval(local_channel()) + ); + + Libsync::build_sync_packet(local_channel(),null,true); + + goaway(z_root() . '/group/' . argv(1) . ((argv(2)) ? '/' . argv(2) : '')); - goaway(z_root() . '/group/' . argv(1) . '/' . argv(2)); - } return; } @@ -127,7 +140,7 @@ class Group extends Controller { foreach($groups as $group) { $entries[$i]['name'] = $group['gname']; $entries[$i]['id'] = $group['id']; - $entries[$i]['count'] = count(group_get_members($group['id'])); + $entries[$i]['count'] = count(AccessList::members(local_channel(), $group['id'])); $i++; } @@ -135,6 +148,10 @@ class Group extends Controller { call_hooks ('privacygroup_extras',$hookinfo); $pgrp_extras = $hookinfo['pgrp_extras']; + $is_default_acl = ['set_default_acl', t('Post to this group by default'), 0, '', [t('No'), t('Yes')]]; + $is_default_group = ['set_default_group', t('Add new contacts to this group by default'), 0, '', [t('No'), t('Yes')]]; + + $tpl = get_markup_template('privacy_groups.tpl'); $o = replace_macros($tpl, [ '$title' => t('Privacy Groups'), @@ -143,16 +160,19 @@ class Group extends Controller { // new group form '$gname' => array('groupname',t('Privacy group name')), - '$public' => array('public',t('Members are visible to other channels'), false), + '$public' => array('public',t('Members are visible to other channels'), 0, '', [t('No'), t('Yes')]), '$pgrp_extras' => $pgrp_extras, '$form_security_token' => get_form_security_token("group_edit"), '$submit' => t('Submit'), + '$is_default_acl' => $is_default_acl, + '$is_default_group' => $is_default_group, // groups list '$title' => t('Privacy Groups'), '$name_label' => t('Name'), '$count_label' => t('Members'), '$entries' => $entries + ]); return $o; @@ -174,7 +194,7 @@ class Group extends Controller { intval(local_channel()) ); if($r) - $result = group_rmv(local_channel(),$r[0]['gname']); + $result = AccessList::remove(local_channel(),$r[0]['gname']); if($result) { $hookinfo = [ 'pgrp_extras' => '', 'group' => argv(2) ]; call_hooks ('privacygroup_extras_drop',$hookinfo); @@ -215,7 +235,7 @@ class Group extends Controller { $group = $r[0]; - $members = group_get_members($group['id']); + $members = AccessList::members(local_channel(), $group['id']); $preselected = array(); if(count($members)) { @@ -227,13 +247,13 @@ class Group extends Controller { if($change) { if(in_array($change,$preselected)) { - group_rmv_member(local_channel(),$group['gname'],$change); + AccessList::member_remove(local_channel(),$group['gname'],$change); } else { - group_add_member(local_channel(),$group['gname'],$change); + AccessList::member_add(local_channel(),$group['gname'],$change); } - $members = group_get_members($group['id']); + $members = AccessList::members(local_channel(), $group['id']); $preselected = array(); if(count($members)) { @@ -252,7 +272,7 @@ class Group extends Controller { '$gname' => array('groupname',t('Privacy group name: '),$group['gname'], ''), '$gid' => $group['id'], '$drop' => $drop_txt, - '$public' => array('public',t('Members are visible to other channels'), $group['visible'], ''), + '$public' => array('public',t('Members are visible to other channels'), $group['visible'], '', [t('No'), t('Yes')]), '$form_security_token_edit' => get_form_security_token('group_edit'), '$delete' => t('Delete Group'), '$form_security_token_drop' => get_form_security_token("group_drop"), @@ -280,7 +300,7 @@ class Group extends Controller { $groupeditor['members'][] = micropro($member,true,'mpgroup', $textmode); } else - group_rmv_member(local_channel(),$group['gname'],$member['xchan_hash']); + AccessList::member_remove(local_channel(),$group['gname'],$member['xchan_hash']); } $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d AND abook_self = 0 and abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 order by xchan_name asc", @@ -302,6 +322,15 @@ class Group extends Controller { $context['$desc'] = t('Click a channel to toggle membership'); $context['$pgrp_extras'] = $pgrp_extras; + $channel = App::get_channel(); + +//hz_syslog(print_r($group,true)); +//hz_syslog(print_r($channel,true)); + + $context['$is_default_acl'] = ['set_default_acl', t('Post to this group by default'), intval($group['hash'] === trim($channel['channel_allow_gid'], '<>')), '', [t('No'), t('Yes')]]; + $context['$is_default_group'] = ['set_default_group', t('Add new contacts to this group by default'), intval($group['hash'] === $channel['channel_default_group']), '', [t('No'), t('Yes')]]; + + if($change) { $tpl = get_markup_template('groupeditor.tpl'); echo replace_macros($tpl, $context); diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 9b76c7569..66629fa2b 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -416,6 +416,7 @@ class Item extends Controller { $expires = NULL_DATE; + $comments_closed = NULL_DATE; $route = ''; $parent_item = null; @@ -692,6 +693,7 @@ class Item extends Controller { $postopts = $orig_post['postopts']; $created = $orig_post['created']; $expires = $orig_post['expires']; + $comments_closed = $orig_post['comments_closed']; $mid = $orig_post['mid']; $parent_mid = $orig_post['parent_mid']; $plink = $orig_post['plink']; @@ -794,13 +796,7 @@ class Item extends Controller { // if this is a wall-to-wall post to a group, turn it into a direct message - $role = get_pconfig($profile_uid, 'system', 'permissions_role'); - - $rolesettings = PermissionRoles::role_perms($role); - - $channel_type = isset($rolesettings['channel_type']) ? $rolesettings['channel_type'] : 'normal'; - - $is_group = (($channel_type === 'group') ? true : false); + $is_group = get_pconfig($profile_uid, 'system', 'group_actor'); if (($is_group) && ($walltowall) && (!$walltowall_comment)) { $groupww = true; @@ -994,8 +990,9 @@ class Item extends Controller { $notify_type = (($parent) ? 'comment-new' : 'wall-new'); + $uuid = (($message_id) ? $message_id : item_message_id()); + if (!$mid) { - $uuid = (($message_id) ? $message_id : item_message_id()); $mid = z_root() . '/item/' . $uuid; } @@ -1015,10 +1012,22 @@ class Item extends Controller { } if ($obj) { - $obj['url'] = $mid; - $obj['attributedTo'] = channel_url($channel); - $datarray['obj'] = $obj; - $obj_type = 'Question'; + $obj['url'] = $mid; + $obj['id'] = $mid; + $obj['diaspora:guid'] = $uuid; + $obj['attributedTo'] = channel_url($channel); + $obj['published'] = $created; + + $datarray['obj'] = $obj; + + if ($obj['endTime']) { + $d = datetime_convert('UTC','UTC', $obj['endTime']); + if ($d > NULL_DATE) { + $comments_closed = $d; + } + } + + $obj_type = 'Question'; } if (!$parent_mid) { @@ -1082,6 +1091,7 @@ class Item extends Controller { $datarray['created'] = $created; $datarray['edited'] = (($orig_post) ? datetime_convert() : $created); $datarray['expires'] = $expires; + $datarray['comments_closed'] = $comments_closed; $datarray['commented'] = (($orig_post) ? datetime_convert() : $created); $datarray['received'] = (($orig_post) ? datetime_convert() : $created); $datarray['changed'] = (($orig_post) ? datetime_convert() : $created); @@ -1594,6 +1604,8 @@ class Item extends Controller { $obj['endTime'] = datetime_convert(date_default_timezone_get(), 'UTC', 'now + ' . $expire_value . ' ' . $expire_unit, ATOM_TIME); + $obj['directMessage'] = (intval($item['item_private']) === 2); + if ($item['item_private']) { $obj['to'] = Activity::map_acl($item); } diff --git a/Zotlabs/Module/Network.php b/Zotlabs/Module/Network.php index 03c56b9a2..016a0a309 100644 --- a/Zotlabs/Module/Network.php +++ b/Zotlabs/Module/Network.php @@ -1,12 +1,11 @@ <?php namespace Zotlabs\Module; -use Zotlabs\Lib\Group; +use Zotlabs\Lib\AccessList; use Zotlabs\Lib\Apps; use App; require_once('include/items.php'); -require_once('include/group.php'); require_once('include/contact_widgets.php'); require_once('include/conversation.php'); require_once('include/acl_selectors.php'); @@ -233,7 +232,7 @@ class Network extends \Zotlabs\Web\Controller { if($group) { $contact_str = ''; - $contacts = group_get_members($group); + $contacts = AccessList::members(local_channel(), $group); if($contacts) { $contact_str = ids_to_querystr($contacts, 'xchan', true); } @@ -246,7 +245,7 @@ class Network extends \Zotlabs\Web\Controller { $item_thread_top = ''; $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND (( author_xchan IN ( $contact_str ) OR owner_xchan in ( $contact_str )) or allow_gid like '" . protect_sprintf('%<' . dbesc($group_hash) . '>%') . "' ) and id = parent $item_normal ) "; - $x = group_rec_byhash(local_channel(), $group_hash); + $x = AccessList::by_hash(local_channel(), $group_hash); if($x) { $title = replace_macros(get_markup_template('section_title.tpl'), array( diff --git a/Zotlabs/Module/New_channel.php b/Zotlabs/Module/New_channel.php index 3b0b35258..5989e3da6 100644 --- a/Zotlabs/Module/New_channel.php +++ b/Zotlabs/Module/New_channel.php @@ -170,12 +170,12 @@ class New_channel extends \Zotlabs\Web\Controller { $privacy_role = ((x($_REQUEST,'permissions_role')) ? $_REQUEST['permissions_role'] : "" ); - $perm_roles = \Zotlabs\Access\PermissionRoles::roles(); + $perm_roles = \Zotlabs\Access\PermissionRoles::channel_roles(); $name = array('name', t('Channel name'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), $name_help, "*"); $nickhub = '@' . \App::get_hostname(); $nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), $nick_help, "*"); - $role = array('permissions_role' , t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role compatible with your usage needs and privacy requirements.') . '<br>' . '<a href="help/member/member_guide#Channel_Permission_Roles" target="_blank">' . t('Read more about channel permission roles') . '</a>',$perm_roles); + $role = array('permissions_role' , t('Channel role'), ($privacy_role) ? $privacy_role : 'personal', '', $perm_roles); $o = replace_macros(get_markup_template('new_channel.tpl'), array( '$title' => t('Create a Channel'), diff --git a/Zotlabs/Module/Permcats.php b/Zotlabs/Module/Permcats.php index 58566373a..a46253ec2 100644 --- a/Zotlabs/Module/Permcats.php +++ b/Zotlabs/Module/Permcats.php @@ -6,6 +6,7 @@ use App; use Zotlabs\Web\Controller; use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; +use Zotlabs\Lib\AccessList; class Permcats extends Controller { @@ -14,24 +15,59 @@ class Permcats extends Controller { if(! local_channel()) return; - if(! Apps::system_app_installed(local_channel(), 'Permission Categories')) - return; - $channel = App::get_channel(); check_form_security_token_redirectOnErr('/permcats', 'permcats'); - $all_perms = \Zotlabs\Access\Permissions::Perms(); - $name = escape_tags(trim($_POST['name'])); - if(! $name) { + $is_system_role = isset($_POST['is_system_role']); + $return_path = z_root() . '/permcats/' . $_POST['return_path']; + $group_hash = ((isset($_POST['group_select'])) ? $_POST['group_select'] : ''); + $contacts = []; + + if ($group_hash === 'all_contacts') { + $r = q("SELECT abook_xchan FROM abook WHERE abook_channel = %d and abook_self = 0 and abook_pending = 0", + intval(local_channel()) + ); + + if ($r) { + $contacts = ids_to_array($r, 'abook_xchan'); + } + } + + if (!$contacts && $group_hash) { + $group = AccessList::by_hash(local_channel(), $group_hash); + } + + if ($group) { + $contacts = AccessList::members_xchan(local_channel(), $group['id']); + } + + if(! $name ) { notice( t('Permission category name is required.') . EOL); return; } + set_pconfig(local_channel(), 'system', 'default_permcat', 'default'); + + if (isset($_POST['default_role'])) { + set_pconfig(local_channel(), 'system', 'default_permcat', $name); + } + + if ($is_system_role) { + // if we have a system role just set the default and assign if aplicable and be done with it + if ($contacts) + \Zotlabs\Lib\Permcat::assign($channel, $name, $contacts); + + info( t('Contact role saved.') . EOL); + Libsync::build_sync_packet(); + goaway($return_path); + return; + } $pcarr = []; + $all_perms = \Zotlabs\Access\Permissions::Perms(); if($all_perms) { foreach($all_perms as $perm => $desc) { @@ -41,11 +77,16 @@ class Permcats extends Controller { } } - \Zotlabs\Lib\Permcat::update(local_channel(),$name,$pcarr); + \Zotlabs\Lib\Permcat::update(local_channel(), $name, $pcarr); + + if ($contacts) { + \Zotlabs\Lib\Permcat::assign($channel, $name, $contacts); + } Libsync::build_sync_packet(); - info( t('Permission category saved.') . EOL); + info( t('Contact role saved.') . EOL); + goaway($return_path); return; } @@ -56,13 +97,6 @@ class Permcats extends Controller { if(! local_channel()) return; - if(! Apps::system_app_installed(local_channel(), 'Permission Categories')) { - //Do not display any associated widgets at this point - App::$pdl = ''; - $papp = Apps::get_papp('Permission Categories'); - return Apps::app_render($papp, 'module'); - } - $channel = App::get_channel(); if(argc() > 1) @@ -70,24 +104,49 @@ class Permcats extends Controller { if(argc() > 2 && argv(2) === 'drop') { \Zotlabs\Lib\Permcat::delete(local_channel(),$name); + + // TODO: assign all members of the deleted role to the default role + Libsync::build_sync_packet(); json_return_and_die([ 'success' => true ]); } - $desc = t('Use this form to create permission rules for various classes of people or connections.'); - $existing = []; $pcat = new \Zotlabs\Lib\Permcat(local_channel()); $pcatlist = $pcat->listing(); + +/* not yet ready + $test = $pcatlist[4]['perms']; + $role_sql = ''; + + foreach ($test as $t) + $role_sql .= "( k = '" . dbesc($t['name']) . "' AND v = '" . intval($t['value']) . "' ) OR "; + + $role_sql = rtrim($role_sql, ' OR '); + + // get all xchans belonging to a permission role + $q = q("SELECT xchan FROM abconfig WHERE chan = %d AND cat = 'my_perms' AND ( $role_sql ) GROUP BY xchan HAVING count(xchan) = %d", + intval(local_channel()), + intval(count($test)) + ); +*/ + + $is_system_role = false; $permcats = []; if($pcatlist) { foreach($pcatlist as $pc) { - if(($pc['name']) && ($name) && ($pc['name'] == $name)) + if(($pc['name']) && ($name) && ($pc['name'] == $name)) { $existing = $pc['perms']; - if(! $pc['system']) - $permcats[bin2hex($pc['name'])] = $pc['localname']; + if (isset($pc['system']) && intval($pc['system'])) + $is_system_role = $pc['name']; + } + + $permcats[bin2hex($pc['name'])] = $pc['localname']; + + if($pc['name'] == $name) + $localname = $pc['localname']; } } @@ -98,33 +157,56 @@ class Permcats extends Controller { $checkinherited = \Zotlabs\Access\PermissionLimits::Get(local_channel(),$k); if($existing[$k]) - $thisperm = "1"; - - $perms[] = array('perms_' . $k, $v, '',$thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited); + $thisperm = 1; + + $perms[] = [ + 'perms_' . $k, + $v, + '', + $thisperm, + 1, + (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), + '', + $checkinherited + ]; } + $is_default_role = (get_pconfig(local_channel(),'system','default_permcat','default') == $name); + + $group_select_options = [ + 'selected' => '', + 'form_id' => 'group_select', + 'label' => t('Assign this role to'), + 'after' => [ + 'name' => t('All my contacts'), + 'id' => 'all_contacts', + 'selected' => false + ] + ]; + $group_select = AccessList::select(local_channel(), $group_select_options); $tpl = get_markup_template("permcats.tpl"); $o .= replace_macros($tpl, array( '$form_security_token' => get_form_security_token("permcats"), - '$title' => t('Permission Categories'), - '$desc' => $desc, - '$desc2' => $desc2, + '$default_role' => array('default_role', t('Use this role as default for new contacts'), intval($is_default_role), '', [t('No'), t('Yes')]), + + '$title' => t('Contact Roles'), '$tokens' => $t, '$permcats' => $permcats, '$atoken' => $atoken, '$url1' => z_root() . '/channel/' . $channel['channel_address'], '$url2' => z_root() . '/photos/' . $channel['channel_address'], - '$name' => array('name', t('Permission category name') . ' <span class="required">*</span>', (($name) ? $name : ''), ''), - '$me' => t('My Settings'), + '$name' => ['name', t('Role name') . ' <span class="required">*</span>', (($localname) ? $localname : ''), (($is_system_role) ? t('System role - not editable') : '') , '', (($is_system_role) ? 'disabled' : '')], '$perms' => $perms, '$inherited' => t('inherited'), - '$notself' => 0, - '$self' => 1, - '$permlbl' => t('Individual Permissions'), - '$permnote' => t('Some permissions may be inherited from your channel\'s <a href="settings"><strong>privacy settings</strong></a>, which have higher priority than individual settings. You can <strong>not</strong> change those settings here.'), - '$submit' => t('Submit') + '$is_system_role' => $is_system_role, + '$permlbl' => t('Role Permissions'), + '$permnote' => t('Some permissions may be inherited from your <a href="settings">channel role</a>, which have higher priority than contact role settings.'), + '$submit' => t('Submit'), + '$return_path' => argv(1), + '$group_select' => $group_select, + )); return $o; } diff --git a/Zotlabs/Module/Profiles.php b/Zotlabs/Module/Profiles.php index 1512148b1..ca0ca4f1a 100644 --- a/Zotlabs/Module/Profiles.php +++ b/Zotlabs/Module/Profiles.php @@ -316,8 +316,6 @@ class Profiles extends \Zotlabs\Web\Controller { $work = fix_mce_lf(escape_tags(trim($_POST['work']))); $education = fix_mce_lf(escape_tags(trim($_POST['education']))); - $hide_friends = ((intval($_POST['hide_friends'])) ? 1: 0); - // start fresh and create a new vcard. TODO: preserve the original guid or whatever else needs saving // $orig_vcard = (($orig[0]['profile_vcard']) ? \Sabre\VObject\Reader::read($orig[0]['profile_vcard']) : null); @@ -513,6 +511,16 @@ class Profiles extends \Zotlabs\Web\Controller { $value = $locality . $comma1 . $region . $comma2 . $country_name; } + $hide_friends = ((intval($_POST['hide_friends'])) ? 1: 0); + + $suggestme = ((x($_POST, 'suggestme')) ? intval($_POST['suggestme']) : 0); + set_pconfig(local_channel(), 'system', 'suggestme', $suggestme); + + $show_presence = (((x($_POST, 'show_presence')) && (intval($_POST['show_presence']) == 1)) ? 1 : 0); + set_pconfig(local_channel(), 'system', 'show_online_status', $show_presence); + + $publish = ((x($_POST, 'profile_in_directory') && (intval($_POST['profile_in_directory']) == 1)) ? 1 : 0); + profile_activity($changes,$value); } @@ -551,7 +559,8 @@ class Profiles extends \Zotlabs\Web\Controller { employment = '%s', education = '%s', hide_friends = %d, - profile_vcard = '%s' + profile_vcard = '%s', + publish = %d WHERE id = %d AND uid = %d", dbesc($profile_name), dbesc($name), @@ -587,6 +596,7 @@ class Profiles extends \Zotlabs\Web\Controller { dbesc($education), intval($hide_friends), dbesc($profile_vcard), + intval($publish), intval(argv(1)), intval(local_channel()) ); @@ -675,13 +685,43 @@ class Profiles extends \Zotlabs\Web\Controller { else $fields = $profile_fields_basic; - $hide_friends = array( - 'hide_friends', - t('Hide your connections list from viewers of this profile'), - $r[0]['hide_friends'], - '', - array(t('No'),t('Yes')) - ); + $show_presence = []; + $profile_in_dir = ''; + $suggestme = ''; + $hide_friends = []; + $is_default = (($r[0]['is_default']) ? 1 : 0); + + if ($is_default) { + + $hide_friends = array( + 'hide_friends', + t('Hide my connections from viewers of this profile'), + $r[0]['hide_friends'], + '', + [t('No'), t('Yes')] + ); + + + $opt_tpl = get_markup_template("field_checkbox.tpl"); + if (get_config('system', 'publish_all')) { + $profile_in_dir = '<input type="hidden" name="profile_in_directory" value="1" />'; + } + else { + $profile_in_dir = replace_macros($opt_tpl, [ + '$field' => ['profile_in_directory', t('Publish my default profile in the network directory'), $r[0]['publish'], '', [t('No'), t('Yes')]], + ]); + } + + $suggestme = get_pconfig(local_channel(), 'system', 'suggestme'); + $suggestme = (($suggestme === false) ? '0' : $suggestme); // default if not set: 0 + + $suggestme = replace_macros($opt_tpl, [ + '$field' => ['suggestme', t('Suggest me as a potential contact to new members'), $suggestme, '', [t('No'), t('Yes')]], + ]); + + $show_presence_val = intval(get_pconfig(local_channel(), 'system', 'show_online_status')); + $show_presence = ['show_presence', t('Reveal my online status'), $show_presence_val, '', [t('No'), t('Yes')]]; + } $q = q("select * from profdef where true"); if($q) { @@ -710,7 +750,7 @@ class Profiles extends \Zotlabs\Web\Controller { if(! $f) $f = 'ymd'; - $is_default = (($r[0]['is_default']) ? 1 : 0); + $tpl = get_markup_template("profile_edit.tpl"); $o .= replace_macros($tpl,array( @@ -724,7 +764,7 @@ class Profiles extends \Zotlabs\Web\Controller { '$banner' => t('Edit Profile Details'), '$submit' => t('Submit'), '$viewprof' => t('View this profile'), - '$editvis' => t('Edit visibility'), + '$editvis' => t('Edit visibility'), '$tools_label' => t('Profile Tools'), '$coverpic' => t('Change cover photo'), '$profpic' => t('Change profile photo'), @@ -732,7 +772,7 @@ class Profiles extends \Zotlabs\Web\Controller { '$cl_prof' => t('Clone this profile'), '$del_prof' => t('Delete this profile'), '$addthing' => t('Add profile things'), - '$personal' => t('Personal'), + '$basic' => t('Basic'), '$location' => t('Location'), '$relation' => t('Relationship'), '$miscellaneous'=> t('Miscellaneous'), @@ -801,6 +841,11 @@ class Profiles extends \Zotlabs\Web\Controller { '$update' => t('Update'), '$delete' => t('Delete'), '$cancel' => t('Cancel'), + + '$show_presence' => $show_presence, + '$suggestme' => $suggestme, + '$profile_in_dir' => $profile_in_dir, + )); $arr = array('profile' => $r[0], 'entry' => $o); diff --git a/Zotlabs/Module/Regate.php b/Zotlabs/Module/Regate.php index 462c997ff..33bb8d957 100644 --- a/Zotlabs/Module/Regate.php +++ b/Zotlabs/Module/Regate.php @@ -196,7 +196,7 @@ class Regate extends \Zotlabs\Web\Controller { if ($invite_channel) { $f = Connect::connect($new_channel['channel'], $invite_channel['xchan_addr']); if ($f['success']) { - $can_view_stream = their_perms_contains($channel_id, $f['abook']['abook_xchan'], 'view_stream'); + $can_view_stream = intval(get_abconfig($channel_id, $f['abook']['abook_xchan'], 'their_perms', 'view_stream')); // If we can view their stream, pull in some posts if ($can_view_stream) { Master::Summon(['Onepoll', $f['abook']['abook_id']]); diff --git a/Zotlabs/Module/Settings/Channel.php b/Zotlabs/Module/Settings/Channel.php index e95752338..914523960 100644 --- a/Zotlabs/Module/Settings/Channel.php +++ b/Zotlabs/Module/Settings/Channel.php @@ -2,6 +2,10 @@ namespace Zotlabs\Module\Settings; +use App; +use Zotlabs\Access\PermissionLimits; +use Zotlabs\Access\PermissionRoles; +use Zotlabs\Daemon\Master; use Zotlabs\Lib\Apps; use Zotlabs\Lib\Libsync; @@ -10,602 +14,266 @@ require_once('include/selectors.php'); class Channel { - function post() { - $channel = \App::get_channel(); - check_form_security_token_redirectOnErr('/settings', 'settings'); - call_hooks('settings_post', $_POST); - $set_perms = ''; - - $role = ((x($_POST,'permissions_role')) ? notags(trim($_POST['permissions_role'])) : ''); - $oldrole = get_pconfig(local_channel(),'system','permissions_role'); - - // This mapping can be removed after 3.4 release - if($oldrole === 'social_party') { - $oldrole = 'social_federation'; - } - - if(($role != $oldrole) || ($role === 'custom')) { - - if($role === 'custom') { - $hide_presence = (((x($_POST,'hide_presence')) && (intval($_POST['hide_presence']) == 1)) ? 1: 0); - $publish = (((x($_POST,'profile_in_directory')) && (intval($_POST['profile_in_directory']) == 1)) ? 1: 0); - $def_group = ((x($_POST,'group-selection')) ? notags(trim($_POST['group-selection'])) : ''); - $r = q("update channel set channel_default_group = '%s' where channel_id = %d", - dbesc($def_group), - intval(local_channel()) - ); - - $global_perms = \Zotlabs\Access\Permissions::Perms(); - - foreach($global_perms as $k => $v) { - \Zotlabs\Access\PermissionLimits::Set(local_channel(),$k,intval($_POST[$k])); - } - $acl = new \Zotlabs\Access\AccessList($channel); - $acl->set_from_array($_POST); - $x = $acl->get(); - - $r = q("update channel set channel_allow_cid = '%s', channel_allow_gid = '%s', - channel_deny_cid = '%s', channel_deny_gid = '%s' where channel_id = %d", - dbesc($x['allow_cid']), - dbesc($x['allow_gid']), - dbesc($x['deny_cid']), - dbesc($x['deny_gid']), - intval(local_channel()) - ); - } - else { - $role_permissions = \Zotlabs\Access\PermissionRoles::role_perms($_POST['permissions_role']); - if(! $role_permissions) { - notice('Permissions category could not be found.'); - return; - } - $hide_presence = 1 - (intval($role_permissions['online'])); - if($role_permissions['default_collection']) { - $r = q("select hash from pgrp where uid = %d and gname = '%s' limit 1", - intval(local_channel()), - dbesc( t('Friends') ) - ); - if(! $r) { - require_once('include/group.php'); - group_add(local_channel(), t('Friends')); - group_add_member(local_channel(),t('Friends'),$channel['channel_hash']); - $r = q("select hash from pgrp where uid = %d and gname = '%s' limit 1", - intval(local_channel()), - dbesc( t('Friends') ) - ); - } - if($r) { - q("update channel set channel_default_group = '%s', channel_allow_gid = '%s', channel_allow_cid = '', channel_deny_gid = '', channel_deny_cid = '' where channel_id = %d", - dbesc($r[0]['hash']), - dbesc('<' . $r[0]['hash'] . '>'), - intval(local_channel()) - ); - } - else { - notice( sprintf('Default privacy group \'%s\' not found. Please create and re-submit permission change.', t('Friends')) . EOL); - return; - } - } - // no default collection - else { - q("update channel set channel_default_group = '', channel_allow_gid = '', channel_allow_cid = '', channel_deny_gid = '', - channel_deny_cid = '' where channel_id = %d", - intval(local_channel()) - ); - } - - if($role_permissions['perms_connect']) { - $x = \Zotlabs\Access\Permissions::FilledPerms($role_permissions['perms_connect']); - foreach($x as $k => $v) { - set_abconfig(local_channel(),$channel['channel_hash'],'my_perms',$k, $v); - if($role_permissions['perms_auto']) { - set_pconfig(local_channel(),'autoperms',$k,$v); - } - else { - del_pconfig(local_channel(),'autoperms',$k); - } - } - } - - if($role_permissions['limits']) { - foreach($role_permissions['limits'] as $k => $v) { - \Zotlabs\Access\PermissionLimits::Set(local_channel(),$k,$v); - } - } - if(array_key_exists('directory_publish',$role_permissions)) { - $publish = intval($role_permissions['directory_publish']); - } - } - - set_pconfig(local_channel(),'system','hide_online_status',$hide_presence); - set_pconfig(local_channel(),'system','permissions_role',$role); - } - - $username = ((x($_POST,'username')) ? notags(trim($_POST['username'])) : ''); - $timezone = ((x($_POST,'timezone_select')) ? notags(trim($_POST['timezone_select'])) : ''); - $defloc = ((x($_POST,'defloc')) ? notags(trim($_POST['defloc'])) : ''); - $openid = ((x($_POST,'openid_url')) ? notags(trim($_POST['openid_url'])) : ''); - $maxreq = ((x($_POST,'maxreq')) ? intval($_POST['maxreq']) : 0); - $expire = ((x($_POST,'expire')) ? intval($_POST['expire']) : 0); - $evdays = ((x($_POST,'evdays')) ? intval($_POST['evdays']) : 3); - $photo_path = ((x($_POST,'photo_path')) ? escape_tags(trim($_POST['photo_path'])) : ''); - $attach_path = ((x($_POST,'attach_path')) ? escape_tags(trim($_POST['attach_path'])) : ''); - - $expire_items = ((x($_POST,'expire_items')) ? intval($_POST['expire_items']) : 0); - $expire_starred = ((x($_POST,'expire_starred')) ? intval($_POST['expire_starred']) : 0); - $expire_photos = ((x($_POST,'expire_photos'))? intval($_POST['expire_photos']) : 0); - $expire_network_only = ((x($_POST,'expire_network_only'))? intval($_POST['expire_network_only']) : 0); - - $allow_location = (((x($_POST,'allow_location')) && (intval($_POST['allow_location']) == 1)) ? 1: 0); - - $blocktags = (((x($_POST,'blocktags')) && (intval($_POST['blocktags']) == 1)) ? 0: 1); // this setting is inverted! - $unkmail = (((x($_POST,'unkmail')) && (intval($_POST['unkmail']) == 1)) ? 1: 0); - $cntunkmail = ((x($_POST,'cntunkmail')) ? intval($_POST['cntunkmail']) : 0); - $suggestme = ((x($_POST,'suggestme')) ? intval($_POST['suggestme']) : 0); - $autoperms = ((x($_POST,'autoperms')) ? intval($_POST['autoperms']) : 0); - - $post_newfriend = (($_POST['post_newfriend'] == 1) ? 1: 0); - $post_joingroup = (($_POST['post_joingroup'] == 1) ? 1: 0); - $post_profilechange = (($_POST['post_profilechange'] == 1) ? 1: 0); - $adult = (($_POST['adult'] == 1) ? 1 : 0); - $defpermcat = ((x($_POST,'defpermcat')) ? notags(trim($_POST['defpermcat'])) : 'default'); - - $mailhost = ((array_key_exists('mailhost',$_POST)) ? notags(trim($_POST['mailhost'])) : ''); - - $pageflags = $channel['channel_pageflags']; - $existing_adult = (($pageflags & PAGE_ADULT) ? 1 : 0); - if($adult != $existing_adult) + $channel = App::get_channel(); + $role = ((x($_POST, 'permissions_role')) ? notags(trim($_POST['permissions_role'])) : ''); + $timezone = ((x($_POST, 'timezone_select')) ? notags(trim($_POST['timezone_select'])) : ''); + $defloc = ((x($_POST, 'defloc')) ? notags(trim($_POST['defloc'])) : ''); + $evdays = ((x($_POST, 'evdays')) ? intval($_POST['evdays']) : 3); + $photo_path = ((x($_POST, 'photo_path')) ? escape_tags(trim($_POST['photo_path'])) : ''); + $attach_path = ((x($_POST, 'attach_path')) ? escape_tags(trim($_POST['attach_path'])) : ''); + $allow_location = (((x($_POST, 'allow_location')) && (intval($_POST['allow_location']) == 1)) ? 1 : 0); + $post_newfriend = (($_POST['post_newfriend'] == 1) ? 1 : 0); + $post_joingroup = (($_POST['post_joingroup'] == 1) ? 1 : 0); + $post_profilechange = (($_POST['post_profilechange'] == 1) ? 1 : 0); + $adult = (($_POST['adult'] == 1) ? 1 : 0); + $mailhost = ((array_key_exists('mailhost', $_POST)) ? notags(trim($_POST['mailhost'])) : ''); + $pageflags = $channel['channel_pageflags']; + $existing_adult = (($pageflags & PAGE_ADULT) ? 1 : 0); + $expire = ((x($_POST, 'expire')) ? intval($_POST['expire']) : 0); + + if ($adult != $existing_adult) { $pageflags = ($pageflags ^ PAGE_ADULT); - + } $notify = 0; - - if(x($_POST,'notify1')) + if (x($_POST, 'notify1')) $notify += intval($_POST['notify1']); - if(x($_POST,'notify2')) + if (x($_POST, 'notify2')) $notify += intval($_POST['notify2']); - if(x($_POST,'notify3')) + if (x($_POST, 'notify3')) $notify += intval($_POST['notify3']); - if(x($_POST,'notify4')) + if (x($_POST, 'notify4')) $notify += intval($_POST['notify4']); - if(x($_POST,'notify5')) + if (x($_POST, 'notify5')) $notify += intval($_POST['notify5']); - if(x($_POST,'notify6')) + if (x($_POST, 'notify6')) $notify += intval($_POST['notify6']); - if(x($_POST,'notify7')) + if (x($_POST, 'notify7')) $notify += intval($_POST['notify7']); - if(x($_POST,'notify8')) + if (x($_POST, 'notify8')) $notify += intval($_POST['notify8']); $vnotify = 0; - - if(x($_POST,'vnotify1')) + if (x($_POST, 'vnotify1')) $vnotify += intval($_POST['vnotify1']); - if(x($_POST,'vnotify2')) + if (x($_POST, 'vnotify2')) $vnotify += intval($_POST['vnotify2']); - if(x($_POST,'vnotify3')) + if (x($_POST, 'vnotify3')) $vnotify += intval($_POST['vnotify3']); - if(x($_POST,'vnotify4')) + if (x($_POST, 'vnotify4')) $vnotify += intval($_POST['vnotify4']); - if(x($_POST,'vnotify5')) + if (x($_POST, 'vnotify5')) $vnotify += intval($_POST['vnotify5']); - if(x($_POST,'vnotify6')) + if (x($_POST, 'vnotify6')) $vnotify += intval($_POST['vnotify6']); - if(x($_POST,'vnotify7')) + if (x($_POST, 'vnotify7')) $vnotify += intval($_POST['vnotify7']); - if(x($_POST,'vnotify8')) + if (x($_POST, 'vnotify8')) $vnotify += intval($_POST['vnotify8']); - if(x($_POST,'vnotify9')) + if (x($_POST, 'vnotify9')) $vnotify += intval($_POST['vnotify9']); - if(x($_POST,'vnotify10')) + if (x($_POST, 'vnotify10')) $vnotify += intval($_POST['vnotify10']); - if(x($_POST,'vnotify11') && is_site_admin()) + if (x($_POST, 'vnotify11') && is_site_admin()) $vnotify += intval($_POST['vnotify11']); - if(x($_POST,'vnotify12')) + if (x($_POST, 'vnotify12')) $vnotify += intval($_POST['vnotify12']); - if(x($_POST,'vnotify13')) + if (x($_POST, 'vnotify13')) $vnotify += intval($_POST['vnotify13']); - if(x($_POST,'vnotify14')) + if (x($_POST, 'vnotify14')) $vnotify += intval($_POST['vnotify14']); - if(x($_POST,'vnotify15')) + if (x($_POST, 'vnotify15')) $vnotify += intval($_POST['vnotify15']); - $always_show_in_notices = x($_POST, 'always_show_in_notices') ? 1 : 0; - $update_notices_per_parent = x($_POST, 'update_notices_per_parent') ? 1 : 0; + $always_show_in_notices = ((x($_POST, 'always_show_in_notices')) ? 1 : 0); + $update_notices_per_parent = ((x($_POST, 'update_notices_per_parent')) ? 1 : 0); - $err = ''; + if ($timezone !== $channel['channel_timezone']) { + if (strlen($timezone)) + date_default_timezone_set($timezone); + } - $name_change = false; + if ($role !== get_pconfig(local_channel(), 'system', 'permissions_role')) { + $role_permissions = PermissionRoles::role_perms($_POST['permissions_role']); - if($username != $channel['channel_name']) { - $name_change = true; - require_once('include/channel.php'); - $err = validate_channelname($username); - if($err) { - notice($err); - return; + if (isset($role_permissions['limits'])) { + foreach ($role_permissions['limits'] as $k => $v) { + PermissionLimits::Set(local_channel(), $k, $v); + } } - } - if($timezone != $channel['channel_timezone']) { - if(strlen($timezone)) - date_default_timezone_set($timezone); + set_pconfig(local_channel(), 'system', 'group_actor', 0); + if (isset($role_permissions['channel_type']) && $role_permissions['channel_type'] === 'group') { + set_pconfig(local_channel(), 'system', 'group_actor', 1); + } } - set_pconfig(local_channel(),'system','use_browser_location',$allow_location); - set_pconfig(local_channel(),'system','suggestme', $suggestme); - set_pconfig(local_channel(),'system','post_newfriend', $post_newfriend); - set_pconfig(local_channel(),'system','post_joingroup', $post_joingroup); - set_pconfig(local_channel(),'system','post_profilechange', $post_profilechange); - set_pconfig(local_channel(),'system','blocktags',$blocktags); - set_pconfig(local_channel(),'system','vnotify',$vnotify); - set_pconfig(local_channel(),'system','always_show_in_notices',$always_show_in_notices); - set_pconfig(local_channel(),'system','update_notices_per_parent',$update_notices_per_parent); - set_pconfig(local_channel(),'system','evdays',$evdays); - set_pconfig(local_channel(),'system','photo_path',$photo_path); - set_pconfig(local_channel(),'system','attach_path',$attach_path); - set_pconfig(local_channel(),'system','default_permcat',$defpermcat); - set_pconfig(local_channel(),'system','email_notify_host',$mailhost); - set_pconfig(local_channel(),'system','autoperms',$autoperms); - - $r = q("update channel set channel_name = '%s', channel_pageflags = %d, channel_timezone = '%s', channel_location = '%s', channel_notifyflags = %d, channel_max_anon_mail = %d, channel_max_friend_req = %d, channel_expire_days = %d $set_perms where channel_id = %d", - dbesc($username), + set_pconfig(local_channel(), 'system', 'permissions_role', $role); + set_pconfig(local_channel(), 'system', 'use_browser_location', $allow_location); + set_pconfig(local_channel(), 'system', 'post_newfriend', $post_newfriend); + set_pconfig(local_channel(), 'system', 'post_joingroup', $post_joingroup); + set_pconfig(local_channel(), 'system', 'post_profilechange', $post_profilechange); + set_pconfig(local_channel(), 'system', 'vnotify', $vnotify); + set_pconfig(local_channel(), 'system', 'always_show_in_notices', $always_show_in_notices); + set_pconfig(local_channel(), 'system', 'update_notices_per_parent', $update_notices_per_parent); + set_pconfig(local_channel(), 'system', 'evdays', $evdays); + set_pconfig(local_channel(), 'system', 'photo_path', $photo_path); + set_pconfig(local_channel(), 'system', 'attach_path', $attach_path); + set_pconfig(local_channel(), 'system', 'email_notify_host', $mailhost); + + $r = q("update channel set channel_pageflags = %d, channel_timezone = '%s', + channel_location = '%s', channel_notifyflags = %d, channel_expire_days = %d + where channel_id = %d", intval($pageflags), dbesc($timezone), dbesc($defloc), intval($notify), - intval($unkmail), - intval($maxreq), intval($expire), intval(local_channel()) ); - if($r) - info( t('Settings updated.') . EOL); - - if(! is_null($publish)) { - $r = q("UPDATE profile SET publish = %d WHERE is_default = 1 AND uid = %d", - intval($publish), - intval(local_channel()) - ); - } - - if($name_change) { - // change name on all associated xchans by matching the url - $r = q("update xchan set xchan_name = '%s', xchan_name_date = '%s' where xchan_url = '%s'", - dbesc($username), - dbesc(datetime_convert()), - dbesc(z_root() . '/channel/' . $channel['channel_address']) - ); - $r = q("update profile set fullname = '%s' where uid = %d and is_default = 1", - dbesc($username), - intval($channel['channel_id']) - ); - } - - \Zotlabs\Daemon\Master::Summon(array('Directory',local_channel())); + if ($r) + info(t('Settings updated.') . EOL); + Master::Summon(['Directory', local_channel()]); Libsync::build_sync_packet(); - - if($email_changed && \App::$config['system']['register_policy'] == REGISTER_VERIFY) { + if ($email_changed && App::$config['system']['register_policy'] == REGISTER_VERIFY) { // FIXME - set to un-verified, blocked and redirect to logout // Q: Why? Are we verifying people or email addresses? // A: the policy is to verify email addresses } - goaway(z_root() . '/settings' ); + goaway(z_root() . '/settings'); return; // NOTREACHED } function get() { - require_once('include/acl_selectors.php'); - require_once('include/permissions.php'); - - - $yes_no = array(t('No'),t('Yes')); - - - $p = q("SELECT * FROM profile WHERE is_default = 1 AND uid = %d LIMIT 1", - intval(local_channel()) - ); - if(count($p)) - $profile = $p[0]; - - load_pconfig(local_channel(),'expire'); - - $channel = \App::get_channel(); - - $global_perms = \Zotlabs\Access\Permissions::Perms(); - - $permiss = array(); - - $perm_opts = array( - array( t('Nobody except yourself'), 0), - array( t('Only those you specifically allow'), PERMS_SPECIFIC), - array( t('Approved connections'), PERMS_CONTACTS), - array( t('Any connections'), PERMS_PENDING), - array( t('Anybody on this website'), PERMS_SITE), - array( t('Anybody in this network'), PERMS_NETWORK), - array( t('Anybody authenticated'), PERMS_AUTHED), - array( t('Anybody on the internet'), PERMS_PUBLIC) - ); - - $limits = \Zotlabs\Access\PermissionLimits::Get(local_channel()); - $anon_comments = get_config('system','anonymous_comments',true); - - foreach($global_perms as $k => $perm) { - $options = array(); - $can_be_public = ((strstr($k,'view') || ($k === 'post_comments' && $anon_comments)) ? true : false); - foreach($perm_opts as $opt) { - if($opt[1] == PERMS_PUBLIC && (! $can_be_public)) - continue; - $options[$opt[1]] = $opt[0]; - } - $permiss[] = array($k,$perm,$limits[$k],'',$options); - } - - // logger('permiss: ' . print_r($permiss,true)); - - $username = $channel['channel_name']; - $nickname = $channel['channel_address']; - $timezone = $channel['channel_timezone']; - $notify = $channel['channel_notifyflags']; - $defloc = $channel['channel_location']; - - $maxreq = $channel['channel_max_friend_req']; - $expire = $channel['channel_expire_days']; - $adult_flag = intval($channel['channel_pageflags'] & PAGE_ADULT); - $sys_expire = get_config('system','default_expire_days'); - -// $unkmail = \App::$user['unkmail']; -// $cntunkmail = \App::$user['cntunkmail']; - - $hide_presence = intval(get_pconfig(local_channel(), 'system','hide_online_status')); - - - $expire_items = get_pconfig(local_channel(), 'expire','items'); - $expire_items = (($expire_items===false)? '1' : $expire_items); // default if not set: 1 - - $expire_notes = get_pconfig(local_channel(), 'expire','notes'); - $expire_notes = (($expire_notes===false)? '1' : $expire_notes); // default if not set: 1 - - $expire_starred = get_pconfig(local_channel(), 'expire','starred'); - $expire_starred = (($expire_starred===false)? '1' : $expire_starred); // default if not set: 1 - - $expire_photos = get_pconfig(local_channel(), 'expire','photos'); - $expire_photos = (($expire_photos===false)? '0' : $expire_photos); // default if not set: 0 - - $expire_network_only = get_pconfig(local_channel(), 'expire','network_only'); - $expire_network_only = (($expire_network_only===false)? '0' : $expire_network_only); // default if not set: 0 - - - $suggestme = get_pconfig(local_channel(), 'system','suggestme'); - $suggestme = (($suggestme===false)? '0': $suggestme); // default if not set: 0 - - $post_newfriend = get_pconfig(local_channel(), 'system','post_newfriend'); - $post_newfriend = (($post_newfriend===false)? '0': $post_newfriend); // default if not set: 0 - - $post_joingroup = get_pconfig(local_channel(), 'system','post_joingroup'); - $post_joingroup = (($post_joingroup===false)? '0': $post_joingroup); // default if not set: 0 - - $post_profilechange = get_pconfig(local_channel(), 'system','post_profilechange'); - $post_profilechange = (($post_profilechange===false)? '0': $post_profilechange); // default if not set: 0 - - $blocktags = get_pconfig(local_channel(),'system','blocktags'); - $blocktags = (($blocktags===false) ? '0' : $blocktags); - - $timezone = date_default_timezone_get(); - - $opt_tpl = get_markup_template("field_checkbox.tpl"); - if(get_config('system','publish_all')) { - $profile_in_dir = '<input type="hidden" name="profile_in_directory" value="1" />'; - } - else { - $profile_in_dir = replace_macros($opt_tpl,array( - '$field' => array('profile_in_directory', t('Publish your default profile in the network directory'), $profile['publish'], '', $yes_no), - )); - } - - $suggestme = replace_macros($opt_tpl,array( - '$field' => array('suggestme', t('Allow us to suggest you as a potential friend to new members?'), $suggestme, '', $yes_no), - - )); - - $subdir = ((strlen(\App::get_path())) ? '<br />' . t('or') . ' ' . z_root() . '/channel/' . $nickname : ''); - - $webbie = $nickname . '@' . \App::get_hostname(); - $intl_nickname = unpunify($nickname) . '@' . unpunify(\App::get_hostname()); - - - $tpl_addr = get_markup_template("settings_nick_set.tpl"); - - $prof_addr = replace_macros($tpl_addr,array( - '$desc' => t('Your channel address is'), + load_pconfig(local_channel()); + + $channel = App::get_channel(); + $nickname = $channel['channel_address']; + $timezone = $channel['channel_timezone']; + $notify = $channel['channel_notifyflags']; + $defloc = $channel['channel_location']; + $adult_flag = intval($channel['channel_pageflags'] & PAGE_ADULT); + $post_newfriend = get_pconfig(local_channel(), 'system', 'post_newfriend'); + $post_newfriend = (($post_newfriend === false) ? '0' : $post_newfriend); // default if not set: 0 + $post_joingroup = get_pconfig(local_channel(), 'system', 'post_joingroup'); + $post_joingroup = (($post_joingroup === false) ? '0' : $post_joingroup); // default if not set: 0 + $post_profilechange = get_pconfig(local_channel(), 'system', 'post_profilechange'); + $post_profilechange = (($post_profilechange === false) ? '0' : $post_profilechange); // default if not set: 0 + $subdir = ((strlen(App::get_path())) ? '<br />' . t('or') . ' ' . z_root() . '/channel/' . $nickname : ''); + $webbie = $nickname . '@' . App::get_hostname(); + $intl_nickname = unpunify($nickname) . '@' . unpunify(App::get_hostname()); + $disable_discover_tab = intval(get_config('system', 'disable_discover_tab', 1)) == 1; + $site_firehose = intval(get_config('system', 'site_firehose', 0)) == 1; + + $expire = $channel['channel_expire_days']; + $sys_expire = get_config('system', 'default_expire_days'); + + $tpl_addr = get_markup_template("settings_nick_set.tpl"); + $prof_addr = replace_macros($tpl_addr, [ + '$desc' => t('Your channel address is'), '$nickname' => (($intl_nickname === $webbie) ? $webbie : $intl_nickname . ' (' . $webbie . ')'), - '$subdir' => $subdir, - '$davdesc' => t('Your files/photos are accessible via WebDAV at'), - '$davpath' => z_root() . '/dav/' . $nickname, - '$basepath' => \App::get_hostname() - )); - - - - $pcat = new \Zotlabs\Lib\Permcat(local_channel()); - $pcatlist = $pcat->listing(); - $permcats = []; - if($pcatlist) { - foreach($pcatlist as $pc) { - $permcats[$pc['name']] = $pc['localname']; - } - } - - $default_permcat = get_pconfig(local_channel(),'system','default_permcat','default'); - - - $stpl = get_markup_template('settings.tpl'); - - $acl = new \Zotlabs\Access\AccessList($channel); - $perm_defaults = $acl->get(); - - require_once('include/group.php'); - $group_select = mini_group_select(local_channel(),$channel['channel_default_group']); - - $evdays = get_pconfig(local_channel(),'system','evdays'); - if(! $evdays) + '$subdir' => $subdir, + '$davdesc' => t('Your files/photos are accessible via WebDAV at'), + '$davpath' => z_root() . '/dav/' . $nickname, + '$basepath' => App::get_hostname() + ]); + + $evdays = get_pconfig(local_channel(), 'system', 'evdays'); + if (!$evdays) $evdays = 3; - $permissions_role = get_pconfig(local_channel(),'system','permissions_role'); - if(! $permissions_role) - $permissions_role = 'custom'; - // compatibility mapping - can be removed after 3.4 release - if($permissions_role === 'social_party') - $permissions_role = 'social_federation'; + $always_show_in_notices = get_pconfig(local_channel(), 'system', 'always_show_in_notices'); + $update_notices_per_parent = get_pconfig(local_channel(), 'system', 'update_notices_per_parent', 1); - if(in_array($permissions_role,['forum','repository'])) - $autoperms = replace_macros(get_markup_template('field_checkbox.tpl'), [ - '$field' => [ 'autoperms',t('Automatic membership approval'), ((get_pconfig(local_channel(),'system','autoperms')) ? 1 : 0), t('If enabled, connection requests will be approved without your interaction'), $yes_no ]]); - else - $autoperms = '<input type="hidden" name="autoperms" value="' . intval(get_pconfig(local_channel(),'system','autoperms')) . '" />'; + $vnotify = get_pconfig(local_channel(), 'system', 'vnotify'); + if ($vnotify === false) + $vnotify = (-1); - $permissions_set = (($permissions_role != 'custom') ? true : false); + $permissions_role = get_pconfig(local_channel(), 'system', 'permissions_role'); + if (!$permissions_role) { + $permissions_role = 'custom'; + } - $perm_roles = \Zotlabs\Access\PermissionRoles::roles(); + $perm_roles = PermissionRoles::channel_roles(); - $always_show_in_notices = get_pconfig(local_channel(),'system','always_show_in_notices'); - $update_notices_per_parent = get_pconfig(local_channel(), 'system', 'update_notices_per_parent', 1); - $vnotify = get_pconfig(local_channel(),'system','vnotify'); + $plugin = ['basic' => '', 'notify' => '']; + call_hooks('channel_settings', $plugin); - if($vnotify === false) - $vnotify = (-1); + $yes_no = [t('No'), t('Yes')]; - $plugin = [ 'basic' => '', 'security' => '', 'notify' => '' ]; - call_hooks('channel_settings',$plugin); - - $disable_discover_tab = intval(get_config('system','disable_discover_tab',1)) == 1; - $site_firehose = intval(get_config('system','site_firehose',0)) == 1; - - - $o .= replace_macros($stpl,array( - '$ptitle' => t('Channel Settings'), - - '$submit' => t('Submit'), - '$baseurl' => z_root(), - '$uid' => local_channel(), - '$form_security_token' => get_form_security_token("settings"), - '$nickname_block' => $prof_addr, - '$h_basic' => t('Basic Settings'), - '$username' => array('username', t('Full Name:'), $username,''), - '$email' => array('email', t('Email Address:'), $email, ''), - '$timezone' => array('timezone_select' , t('Your Timezone:'), $timezone, '', get_timezones()), - '$defloc' => array('defloc', t('Default Post Location:'), $defloc, t('Geographical location to display on your posts')), - '$allowloc' => array('allow_location', t('Use Browser Location:'), ((get_pconfig(local_channel(),'system','use_browser_location')) ? 1 : ''), '', $yes_no), - - '$adult' => array('adult', t('Adult Content'), $adult_flag, t('This channel frequently or regularly publishes adult content. (Please tag any adult material and/or nudity with #NSFW)'), $yes_no), - - '$h_prv' => t('Security and Privacy Settings'), - '$permissions_set' => $permissions_set, - '$perms_set_msg' => t('Your permissions are already configured. Click to view/adjust'), - - '$hide_presence' => array('hide_presence', t('Hide my online presence'),$hide_presence, t('Prevents displaying in your profile that you are online'), $yes_no), - - '$lbl_pmacro' => t('Simple Privacy Settings:'), - '$pmacro3' => t('Very Public - <em>extremely permissive (should be used with caution)</em>'), - '$pmacro2' => t('Typical - <em>default public, privacy when desired (similar to social network permissions but with improved privacy)</em>'), - '$pmacro1' => t('Private - <em>default private, never open or public</em>'), - '$pmacro0' => t('Blocked - <em>default blocked to/from everybody</em>'), - '$permiss_arr' => $permiss, - '$blocktags' => array('blocktags',t('Allow others to tag your posts'), 1-$blocktags, t('Often used by the community to retro-actively flag inappropriate content'), $yes_no), - - '$lbl_p2macro' => t('Channel Permission Limits'), - - '$expire' => array('expire',t('Expire other channel content after this many days'),$expire, t('0 or blank to use the website limit.') . ' ' . ((intval($sys_expire)) ? sprintf( t('This website expires after %d days.'),intval($sys_expire)) : t('This website does not expire imported content.')) . ' ' . t('The website limit takes precedence if lower than your limit.')), - '$maxreq' => array('maxreq', t('Maximum Friend Requests/Day:'), intval($channel['channel_max_friend_req']) , t('May reduce spam activity')), - '$permissions' => t('Default Privacy Group'), - '$permdesc' => t("\x28click to open/close\x29"), - '$aclselect' => populate_acl($perm_defaults, false, \Zotlabs\Lib\PermissionDescription::fromDescription(t('Use my default audience setting for the type of object published'))), - - '$allow_cid' => acl2json($perm_defaults['allow_cid']), - '$allow_gid' => acl2json($perm_defaults['allow_gid']), - '$deny_cid' => acl2json($perm_defaults['deny_cid']), - '$deny_gid' => acl2json($perm_defaults['deny_gid']), - '$suggestme' => $suggestme, - '$group_select' => $group_select, - '$role' => array('permissions_role' , t('Channel role and privacy'), $permissions_role, '', $perm_roles), - '$defpermcat' => [ 'defpermcat', t('Default permissions category'), $default_permcat, '', $permcats ], - '$permcat_enable' => Apps::system_app_installed(local_channel(), 'Permission Categories'), - '$profile_in_dir' => $profile_in_dir, - '$hide_friends' => $hide_friends, - '$hide_wall' => $hide_wall, - '$unkmail' => $unkmail, - '$cntunkmail' => array('cntunkmail', t('Maximum private messages per day from unknown people:'), intval($channel['channel_max_anon_mail']) ,t("Useful to reduce spamming")), - - '$autoperms' => $autoperms, - '$h_not' => t('Notification Settings'), - '$activity_options' => t('By default post a status message when:'), - '$post_newfriend' => array('post_newfriend', t('accepting a friend request'), $post_newfriend, '', $yes_no), - '$post_joingroup' => array('post_joingroup', t('joining a forum/community'), $post_joingroup, '', $yes_no), - '$post_profilechange' => array('post_profilechange', t('making an <em>interesting</em> profile change'), $post_profilechange, '', $yes_no), - '$lbl_not' => t('Send a notification email when:'), - '$notify1' => array('notify1', t('You receive a connection request'), ($notify & NOTIFY_INTRO), NOTIFY_INTRO, '', $yes_no), - '$notify2' => array('notify2', t('Your connections are confirmed'), ($notify & NOTIFY_CONFIRM), NOTIFY_CONFIRM, '', $yes_no), - '$notify3' => array('notify3', t('Someone writes on your profile wall'), ($notify & NOTIFY_WALL), NOTIFY_WALL, '', $yes_no), - '$notify4' => array('notify4', t('Someone writes a followup comment'), ($notify & NOTIFY_COMMENT), NOTIFY_COMMENT, '', $yes_no), - '$notify5' => array('notify5', t('You receive a private message'), ($notify & NOTIFY_MAIL), NOTIFY_MAIL, '', $yes_no), - '$notify6' => array('notify6', t('You receive a friend suggestion'), ($notify & NOTIFY_SUGGEST), NOTIFY_SUGGEST, '', $yes_no), - '$notify7' => array('notify7', t('You are tagged in a post'), ($notify & NOTIFY_TAGSELF), NOTIFY_TAGSELF, '', $yes_no), - '$notify8' => array('notify8', t('You are poked/prodded/etc. in a post'), ($notify & NOTIFY_POKE), NOTIFY_POKE, '', $yes_no), - - '$notify9' => array('notify9', t('Someone likes your post/comment'), ($notify & NOTIFY_LIKE), NOTIFY_LIKE, '', $yes_no), - - - '$lbl_vnot' => t('Show visual notifications including:'), - - '$vnotify1' => array('vnotify1', t('Unseen stream activity'), ($vnotify & VNOTIFY_NETWORK), VNOTIFY_NETWORK, '', $yes_no), - '$vnotify2' => array('vnotify2', t('Unseen channel activity'), ($vnotify & VNOTIFY_CHANNEL), VNOTIFY_CHANNEL, '', $yes_no), - '$vnotify3' => array('vnotify3', t('Unseen private messages'), ($vnotify & VNOTIFY_MAIL), VNOTIFY_MAIL, t('Recommended'), $yes_no), - '$vnotify4' => array('vnotify4', t('Upcoming events'), ($vnotify & VNOTIFY_EVENT), VNOTIFY_EVENT, '', $yes_no), - '$vnotify5' => array('vnotify5', t('Events today'), ($vnotify & VNOTIFY_EVENTTODAY), VNOTIFY_EVENTTODAY, '', $yes_no), - '$vnotify6' => array('vnotify6', t('Upcoming birthdays'), ($vnotify & VNOTIFY_BIRTHDAY), VNOTIFY_BIRTHDAY, t('Not available in all themes'), $yes_no), - '$vnotify7' => array('vnotify7', t('System (personal) notifications'), ($vnotify & VNOTIFY_SYSTEM), VNOTIFY_SYSTEM, '', $yes_no), - '$vnotify8' => array('vnotify8', t('System info messages'), ($vnotify & VNOTIFY_INFO), VNOTIFY_INFO, t('Recommended'), $yes_no), - '$vnotify9' => array('vnotify9', t('System critical alerts'), ($vnotify & VNOTIFY_ALERT), VNOTIFY_ALERT, t('Recommended'), $yes_no), - '$vnotify10' => array('vnotify10', t('New connections'), ($vnotify & VNOTIFY_INTRO), VNOTIFY_INTRO, t('Recommended'), $yes_no), - '$vnotify11' => ((is_site_admin()) ? array('vnotify11', t('System Registrations'), ($vnotify & VNOTIFY_REGISTER), VNOTIFY_REGISTER, '', $yes_no) : array()), - '$vnotify12' => array('vnotify12', t('Unseen shared files'), ($vnotify & VNOTIFY_FILES), VNOTIFY_FILES, '', $yes_no), - '$vnotify13' => ((($disable_discover_tab && !$site_firehose) || !Apps::system_app_installed(local_channel(), 'Public Stream')) ? array() : array('vnotify13', t('Unseen public stream activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no)), - '$vnotify14' => array('vnotify14', t('Unseen likes and dislikes'), ($vnotify & VNOTIFY_LIKE), VNOTIFY_LIKE, '', $yes_no), - '$vnotify15' => array('vnotify15', t('Unseen forum posts'), ($vnotify & VNOTIFY_FORUMS), VNOTIFY_FORUMS, '', $yes_no), - '$mailhost' => [ 'mailhost', t('Email notification hub (hostname)'), get_pconfig(local_channel(),'system','email_notify_host',\App::get_hostname()), sprintf( t('If your channel is mirrored to multiple hubs, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'),\App::get_hostname()) ], - '$always_show_in_notices' => array('always_show_in_notices', t('Show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no), - '$update_notices_per_parent' => array('update_notices_per_parent', t('Mark all notices of the thread read if a notice is clicked'), $update_notices_per_parent, 1, t('If no, only the clicked notice will be marked read'), $yes_no), - '$desktop_notifications_info' => t('Desktop notifications are unavailable because the required browser permission has not been granted'), + $stpl = get_markup_template('settings.tpl'); + $o = replace_macros($stpl, [ + '$ptitle' => t('Channel Settings'), + '$submit' => t('Submit'), + '$baseurl' => z_root(), + '$uid' => local_channel(), + '$form_security_token' => get_form_security_token("settings"), + '$role' => ['permissions_role', t('Channel role'), $permissions_role, '', $perm_roles], + '$nickname_block' => $prof_addr, + '$h_basic' => t('Basic Settings'), + '$timezone' => ['timezone_select', t('Channel timezone:'), $timezone, '', get_timezones()], + '$defloc' => ['defloc', t('Default post location:'), $defloc, t('Geographical location to display on your posts')], + '$allowloc' => ['allow_location', t('Use browser location:'), ((get_pconfig(local_channel(), 'system', 'use_browser_location')) ? 1 : ''), '', $yes_no], + '$adult' => ['adult', t('Adult Content'), $adult_flag, t('This channel frequently or regularly publishes adult content')], + '$maxreq' => ['maxreq', t('Maximum Friend Requests/Day:'), intval($channel['channel_max_friend_req']), t('May reduce spam activity')], + '$h_not' => t('Notification Settings'), + '$activity_options' => t('By default post a status message when:'), + '$post_newfriend' => ['post_newfriend', t('accepting a friend request'), $post_newfriend, '', $yes_no], + '$post_joingroup' => ['post_joingroup', t('joining a forum/community'), $post_joingroup, '', $yes_no], + '$post_profilechange' => ['post_profilechange', t('making an <em>interesting</em> profile change'), $post_profilechange, '', $yes_no], + '$lbl_not' => t('Send a notification email when:'), + '$notify1' => ['notify1', t('You receive a connection request'), ($notify & NOTIFY_INTRO), NOTIFY_INTRO, '', $yes_no], + '$notify2' => ['notify2', t('Your connections are confirmed'), ($notify & NOTIFY_CONFIRM), NOTIFY_CONFIRM, '', $yes_no], + '$notify3' => ['notify3', t('Someone writes on your profile wall'), ($notify & NOTIFY_WALL), NOTIFY_WALL, '', $yes_no], + '$notify4' => ['notify4', t('Someone writes a followup comment'), ($notify & NOTIFY_COMMENT), NOTIFY_COMMENT, '', $yes_no], + '$notify5' => ['notify5', t('You receive a private message'), ($notify & NOTIFY_MAIL), NOTIFY_MAIL, '', $yes_no], + '$notify6' => ['notify6', t('You receive a friend suggestion'), ($notify & NOTIFY_SUGGEST), NOTIFY_SUGGEST, '', $yes_no], + '$notify7' => ['notify7', t('You are tagged in a post'), ($notify & NOTIFY_TAGSELF), NOTIFY_TAGSELF, '', $yes_no], + '$notify8' => ['notify8', t('You are poked/prodded/etc. in a post'), ($notify & NOTIFY_POKE), NOTIFY_POKE, '', $yes_no], + '$notify9' => ['notify9', t('Someone likes your post/comment'), ($notify & NOTIFY_LIKE), NOTIFY_LIKE, '', $yes_no], + '$lbl_vnot' => t('Show visual notifications including:'), + '$vnotify1' => ['vnotify1', t('Unseen stream activity'), ($vnotify & VNOTIFY_NETWORK), VNOTIFY_NETWORK, '', $yes_no], + '$vnotify2' => ['vnotify2', t('Unseen channel activity'), ($vnotify & VNOTIFY_CHANNEL), VNOTIFY_CHANNEL, '', $yes_no], + '$vnotify3' => ['vnotify3', t('Unseen private messages'), ($vnotify & VNOTIFY_MAIL), VNOTIFY_MAIL, t('Recommended'), $yes_no], + '$vnotify4' => ['vnotify4', t('Upcoming events'), ($vnotify & VNOTIFY_EVENT), VNOTIFY_EVENT, '', $yes_no], + '$vnotify5' => ['vnotify5', t('Events today'), ($vnotify & VNOTIFY_EVENTTODAY), VNOTIFY_EVENTTODAY, '', $yes_no], + '$vnotify6' => ['vnotify6', t('Upcoming birthdays'), ($vnotify & VNOTIFY_BIRTHDAY), VNOTIFY_BIRTHDAY, t('Not available in all themes'), $yes_no], + '$vnotify7' => ['vnotify7', t('System (personal) notifications'), ($vnotify & VNOTIFY_SYSTEM), VNOTIFY_SYSTEM, '', $yes_no], + '$vnotify8' => ['vnotify8', t('System info messages'), ($vnotify & VNOTIFY_INFO), VNOTIFY_INFO, t('Recommended'), $yes_no], + '$vnotify9' => ['vnotify9', t('System critical alerts'), ($vnotify & VNOTIFY_ALERT), VNOTIFY_ALERT, t('Recommended'), $yes_no], + '$vnotify10' => ['vnotify10', t('New connections'), ($vnotify & VNOTIFY_INTRO), VNOTIFY_INTRO, t('Recommended'), $yes_no], + '$vnotify11' => ((is_site_admin()) ? ['vnotify11', t('System Registrations'), ($vnotify & VNOTIFY_REGISTER), VNOTIFY_REGISTER, '', $yes_no] : []), + '$vnotify12' => ['vnotify12', t('Unseen shared files'), ($vnotify & VNOTIFY_FILES), VNOTIFY_FILES, '', $yes_no], + '$vnotify13' => ((($disable_discover_tab && !$site_firehose) || !Apps::system_app_installed(local_channel(), 'Public Stream')) ? [] : ['vnotify13', t('Unseen public stream activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no]), + '$vnotify14' => ['vnotify14', t('Unseen likes and dislikes'), ($vnotify & VNOTIFY_LIKE), VNOTIFY_LIKE, '', $yes_no], + '$vnotify15' => ['vnotify15', t('Unseen forum posts'), ($vnotify & VNOTIFY_FORUMS), VNOTIFY_FORUMS, '', $yes_no], + '$mailhost' => ['mailhost', t('Email notification hub (hostname)'), get_pconfig(local_channel(), 'system', 'email_notify_host', App::get_hostname()), sprintf(t('If your channel is mirrored to multiple hubs, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'), App::get_hostname())], + '$always_show_in_notices' => ['always_show_in_notices', t('Show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no], + '$update_notices_per_parent' => ['update_notices_per_parent', t('Mark all notices of the thread read if a notice is clicked'), $update_notices_per_parent, 1, t('If no, only the clicked notice will be marked read'), $yes_no], + '$desktop_notifications_info' => t('Desktop notifications are unavailable because the required browser permission has not been granted'), '$desktop_notifications_request' => t('Grant permission'), - '$evdays' => array('evdays', t('Notify me of events this many days in advance'), $evdays, t('Must be greater than 0')), - '$basic_addon' => $plugin['basic'], - '$sec_addon' => $plugin['security'], - '$notify_addon' => $plugin['notify'], - - '$h_advn' => t('Advanced Account/Page Type Settings'), - '$h_descadvn' => t('Change the behaviour of this account for special situations'), - '$pagetype' => $pagetype, - '$lbl_misc' => t('Miscellaneous Settings'), - '$photo_path' => array('photo_path', t('Default photo upload folder'), get_pconfig(local_channel(),'system','photo_path'), t('%Y - current year, %m - current month')), - '$attach_path' => array('attach_path', t('Default file upload folder'), get_pconfig(local_channel(),'system','attach_path'), t('%Y - current year, %m - current month')), - '$removeme' => t('Remove Channel'), - '$removechannel' => t('Remove this channel.'), - )); - - call_hooks('settings_form',$o); - - //$o .= '</form>' . "\r\n"; + '$evdays' => ['evdays', t('Notify me of events this many days in advance'), $evdays, t('Must be greater than 0')], + '$basic_addon' => $plugin['basic'], + '$notify_addon' => $plugin['notify'], + '$photo_path' => ['photo_path', t('Default photo upload folder'), get_pconfig(local_channel(), 'system', 'photo_path'), t('%Y - current year, %m - current month')], + '$attach_path' => ['attach_path', t('Default file upload folder'), get_pconfig(local_channel(), 'system', 'attach_path'), t('%Y - current year, %m - current month')], + '$removeme' => t('Remove Channel'), + '$removechannel' => t('Remove this channel.'), + '$expire' => ['expire', t('Expire other channel content after this many days'), $expire, t('0 or blank to use the website limit.') . ' ' . ((intval($sys_expire)) ? sprintf(t('This website expires after %d days.'), intval($sys_expire)) : t('This website does not expire imported content.')) . ' ' . t('The website limit takes precedence if lower than your limit.')], + ]); + + call_hooks('settings_form', $o); return $o; } diff --git a/Zotlabs/Module/Settings/Privacy.php b/Zotlabs/Module/Settings/Privacy.php new file mode 100644 index 000000000..fbda78a6f --- /dev/null +++ b/Zotlabs/Module/Settings/Privacy.php @@ -0,0 +1,127 @@ +<?php + +namespace Zotlabs\Module\Settings; + +use App; +use Zotlabs\Access\PermissionLimits; +use Zotlabs\Access\Permissions; +use Zotlabs\Daemon\Master; +use Zotlabs\Lib\Group; +use Zotlabs\Lib\Libsync; + +class Privacy { + + function post() { + + check_form_security_token_redirectOnErr('/settings/privacy', 'settings'); + call_hooks('settings_post', $_POST); + + $index_opt_out = (((x($_POST, 'index_opt_out')) && (intval($_POST['index_opt_out']) == 1)) ? 1 : 0); + set_pconfig(local_channel(), 'system', 'index_opt_out', $index_opt_out); + + $autoperms = (((x($_POST, 'autoperms')) && (intval($_POST['autoperms']) == 1)) ? 1 : 0); + set_pconfig(local_channel(), 'system', 'autoperms', $autoperms); + + $role = get_pconfig(local_channel(), 'system', 'permissions_role'); + if ($role === 'custom') { + + $global_perms = Permissions::Perms(); + + foreach ($global_perms as $k => $v) { + PermissionLimits::Set(local_channel(), $k, intval($_POST[$k])); + } + + $group_actor = (((x($_POST, 'group_actor')) && (intval($_POST['group_actor']) == 1)) ? 1 : 0); + set_pconfig(local_channel(), 'system', 'group_actor', $group_actor); + + } + + info(t('Privacy settings updated.') . EOL); + Master::Summon(['Directory', local_channel()]); + Libsync::build_sync_packet(); + + goaway(z_root() . '/settings/privacy'); + return; // NOTREACHED + } + + function get() { + + load_pconfig(local_channel()); + + $channel = App::get_channel(); + $global_perms = Permissions::Perms(); + $permiss = []; + + $perm_opts = [ + [t('Only me'), 0], + [t('Only those you specifically allow'), PERMS_SPECIFIC], + [t('Approved connections'), PERMS_CONTACTS], + [t('Any connections'), PERMS_PENDING], + [t('Anybody on this website'), PERMS_SITE], + [t('Anybody in this network'), PERMS_NETWORK], + [t('Anybody authenticated'), PERMS_AUTHED], + [t('Anybody on the internet'), PERMS_PUBLIC] + ]; + + $help = [ + 'view_stream', + 'view_wiki', + 'view_pages', + 'view_storage' + ]; + + $help_txt = t('Advise: set to "Anybody on the internet" and use privacy groups to restrict access'); + $limits = PermissionLimits::Get(local_channel()); + $anon_comments = get_config('system', 'anonymous_comments', true); + + foreach ($global_perms as $k => $perm) { + $options = []; + $can_be_public = (strstr($k, 'view') || ($k === 'post_comments' && $anon_comments)); + + foreach ($perm_opts as $opt) { + if ($opt[1] == PERMS_PUBLIC && (!$can_be_public)) + continue; + + $options[$opt[1]] = $opt[0]; + } + + $permiss[] = [ + $k, + $perm, + $limits[$k], + ((in_array($k, $help)) ? $help_txt : ''), + $options + ]; + } + + //logger('permiss: ' . print_r($permiss,true)); + + $autoperms = get_pconfig(local_channel(), 'system', 'autoperms'); + $index_opt_out = get_pconfig(local_channel(), 'system', 'index_opt_out'); + $group_actor = get_pconfig(local_channel(), 'system', 'group_actor'); + + $permissions_role = get_pconfig(local_channel(), 'system', 'permissions_role', 'custom'); + $permission_limits = ($permissions_role === 'custom'); + + $stpl = get_markup_template('settings_privacy.tpl'); + + $o = replace_macros($stpl, [ + '$ptitle' => t('Privacy Settings'), + '$submit' => t('Submit'), + '$form_security_token' => get_form_security_token("settings"), + '$permission_limits' => $permission_limits, + '$permiss_arr' => $permiss, + '$permission_limits_label' => t('Advanced configuration'), + '$permission_limits_warning' => [ + t('Proceed with caution'), + t('Changing advanced configuration settings can impact your, and your contacts channels functionality and security.'), + t('Accept the risk and continue') + ], + '$autoperms' => ['autoperms', t('Automatically approve new contacts'), $autoperms, '', [t('No'), t('Yes')]], + '$index_opt_out' => ['index_opt_out', t('Opt-out of search engine indexing'), $index_opt_out, '', [t('No'), t('Yes')]], + '$group_actor' => ['group_actor', t('Group actor'), $group_actor, t('Allow this channel to act as a forum'), [t('No'), t('Yes')]], + ]); + + return $o; + } +} diff --git a/Zotlabs/Module/Uexport.php b/Zotlabs/Module/Uexport.php index 8116f616b..870c42802 100644 --- a/Zotlabs/Module/Uexport.php +++ b/Zotlabs/Module/Uexport.php @@ -143,6 +143,10 @@ class Uexport extends Controller { function get() { + if(! local_channel()) { + return; + } + if(! Apps::system_app_installed(local_channel(), 'Channel Export')) { //Do not display any associated widgets at this point App::$pdl = ''; diff --git a/Zotlabs/Update/_1249.php b/Zotlabs/Update/_1249.php new file mode 100644 index 000000000..6d72c4de4 --- /dev/null +++ b/Zotlabs/Update/_1249.php @@ -0,0 +1,31 @@ +<?php + +namespace Zotlabs\Update; + +class _1249 { + + function run() { + + dbq("START TRANSACTION"); + + if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = dbq("ALTER TABLE abook ADD abook_role TEXT NOT NULL DEFAULT ''"); + $r2 = dbq("CREATE INDEX \"abook_role\" ON abook (\"abook_role\")"); + $r = ($r1 && $r2); + } + else { + $r = dbq("ALTER TABLE `abook` ADD `abook_role` CHAR(191) NOT NULL DEFAULT '' , + ADD INDEX `abook_role` (`abook_role`)"); + } + + if($r) { + dbq("COMMIT"); + return UPDATE_SUCCESS; + } + + dbq("ROLLBACK"); + return UPDATE_FAILED; + + } + +} diff --git a/Zotlabs/Widget/Collections.php b/Zotlabs/Widget/Collections.php index bc9c812c6..774878540 100644 --- a/Zotlabs/Widget/Collections.php +++ b/Zotlabs/Widget/Collections.php @@ -1,8 +1,8 @@ <?php namespace Zotlabs\Widget; - -require_once('include/group.php'); + +use Zotlabs\Lib\AccessList; class Collections { @@ -49,6 +49,6 @@ class Collections { break; } - return group_side($every, $each, $edit, $current, $abook_id, $wmode); + return AccessList::widget($every, $each, $edit, $current, $abook_id, $wmode); } } diff --git a/Zotlabs/Widget/Messages.php b/Zotlabs/Widget/Messages.php index c0fef9f75..71f4bd310 100644 --- a/Zotlabs/Widget/Messages.php +++ b/Zotlabs/Widget/Messages.php @@ -219,7 +219,7 @@ class Messages { $entries[$i]['info'] = ''; $entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $notice['created']); $entries[$i]['summary'] = $summary; - $entries[$i]['b64mid'] = basename($notice['link']); + $entries[$i]['b64mid'] = (($notice['ntype'] & NOTIFY_INTRO) ? '' : basename($notice['link'])); $entries[$i]['href'] = (($notice['ntype'] & NOTIFY_INTRO) ? $notice['link'] : z_root() . '/hq/' . basename($notice['link'])); $entries[$i]['icon'] = (($notice['ntype'] & NOTIFY_INTRO) ? '<i class="fa fa-user-plus"></i>' : ''); diff --git a/Zotlabs/Widget/Permcats.php b/Zotlabs/Widget/Permcats.php new file mode 100644 index 000000000..72c80ca0c --- /dev/null +++ b/Zotlabs/Widget/Permcats.php @@ -0,0 +1,79 @@ +<?php + +namespace Zotlabs\Widget; + +use Zotlabs\Lib\Permcat; +use Zotlabs\Access\PermissionLimits; + +class Permcats { + + function widget($arr) { + $pcat = new Permcat(local_channel()); + $pcatlist = $pcat->listing(); + + $list = '<b>Roles:</b><br>'; + $active = ''; + $active_role = ''; + + if($pcatlist) { + $i = 0; + foreach($pcatlist as $pc) { + if(argc() > 1) { + if($pc['name'] == hex2bin(argv(1))) { + $active = $i; + $active_role = $pc['name']; + } + } + + $list .= '<a href="permcats/' . bin2hex($pc['name']) . '">' . $pc['localname'] . '</a><br>'; + $i++; + } + } + + if(argc() > 1) { + +/* get role members based on permissions + $test = $pcatlist[$active]['perms']; + + $role_sql = ''; + $count = 0; + foreach ($test as $t) { + $checkinherited = PermissionLimits::Get(local_channel(),$t['name']); + + if($checkinherited & PERMS_SPECIFIC) { + $role_sql .= "( abconfig.k = '" . dbesc($t['name']) . "' AND abconfig.v = '" . intval($t['value']) . "' ) OR "; + $count++; + } + } + + $role_sql = rtrim($role_sql, ' OR '); + + $r = q("SELECT abconfig.xchan, xchan.xchan_name, abook.abook_id FROM abconfig LEFT JOIN xchan on abconfig.xchan = xchan.xchan_hash LEFT JOIN abook ON abconfig.xchan = abook.abook_xchan WHERE xchan.xchan_deleted = 0 and abconfig.chan = %d AND abconfig.cat = 'my_perms' AND ( $role_sql ) GROUP BY abconfig.xchan HAVING count(abconfig.xchan) = %d ORDER BY xchan.xchan_name", + intval(local_channel()), + intval($count) + ); +*/ + + // get role members based on abook_role + + $r = q("SELECT abook.abook_id, abook.abook_role, xchan.xchan_name, xchan.xchan_addr, xchan.xchan_url, xchan.xchan_photo_s FROM abook + LEFT JOIN xchan on abook.abook_xchan = xchan.xchan_hash + WHERE abook.abook_channel = %d AND abook.abook_role = '%s' AND abook_self = 0 AND xchan_deleted = 0 + ORDER BY xchan.xchan_name", + intval(local_channel()), + dbesc($active_role) + ); + + $members = '<b>Role members:</b><br>'; + $members .= '<div class="border rounded" style="height: 20rem; overflow: auto;">'; + + foreach ($r as $rr) { + $addr = (($rr['xchan_addr']) ? $rr['xchan_addr'] : $rr['xchan_url']); + $members .= '<a href="connections#' . $rr['abook_id'] . '" class="lh-sm border-bottom p-2 d-block text-truncate"><img src="' . $rr['xchan_photo_s'] . '" class="float-start rounded me-2" style="height: 2.2rem; width: 2.2rem;" loading="lazy">' . $rr['xchan_name'] . '<br><span class="text-muted small">' . $addr . '</span></a>'; + } + $members .= '</div>'; + } + return $list . '<br>' . $members. '<br>' . $others; + + } +} diff --git a/Zotlabs/Widget/Profile.php b/Zotlabs/Widget/Profile.php index 8bd624c0f..0e5444a56 100644 --- a/Zotlabs/Widget/Profile.php +++ b/Zotlabs/Widget/Profile.php @@ -2,12 +2,16 @@ namespace Zotlabs\Widget; +use App; class Profile { - function widget($args) { + if(!App::$profile['profile_uid']) { + return; + } + $block = observer_prohibited(); - return profile_sidebar(\App::$profile, $block, true, false); - } + return profile_sidebar(App::$profile, $block, true, false); + } } diff --git a/Zotlabs/Widget/Settings_menu.php b/Zotlabs/Widget/Settings_menu.php index 25b80a4b4..4d0f1d2dd 100644 --- a/Zotlabs/Widget/Settings_menu.php +++ b/Zotlabs/Widget/Settings_menu.php @@ -40,6 +40,11 @@ class Settings_menu { 'selected' => ((argv(1) === 'channel') ? 'active' : ''), ), + array( + 'label' => t('Privacy settings'), + 'url' => z_root().'/settings/privacy', + 'selected' => ((argv(1) === 'privacy') ? 'active' : '') + ) ); $tabs[] = array( diff --git a/app/group.apd b/app/group.apd index d026ed23d..bf3ddf4a1 100644 --- a/app/group.apd +++ b/app/group.apd @@ -1,7 +1,7 @@ -version: 3 +version: 4 url: $baseurl/group requires: local_channel name: Privacy Groups -photo: icon:users +photo: icon:lock categories: Networking desc: A tool to create and manage privacy groups. @@ -55,10 +55,10 @@ require_once('include/bbcode.php'); require_once('include/items.php'); define ( 'PLATFORM_NAME', 'hubzilla' ); -define ( 'STD_VERSION', '6.5.1' ); +define ( 'STD_VERSION', '6.5.9' ); define ( 'ZOT_REVISION', '6.0' ); -define ( 'DB_UPDATE_VERSION', 1248 ); +define ( 'DB_UPDATE_VERSION', 1249 ); define ( 'PROJECT_BASE', __DIR__ ); @@ -484,7 +484,7 @@ define ( 'NAMESPACE_YMEDIA', 'http://search.yahoo.com/mrss/' ); define ( 'ACTIVITYSTREAMS_JSONLD_REV', 'https://www.w3.org/ns/activitystreams' ); -define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.9' ); +define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.10' ); /** * activity stream defines */ @@ -860,7 +860,7 @@ class App { private static $baseurl; - private static $meta; + public static $meta; /** * App constructor. diff --git a/include/attach.php b/include/attach.php index 067da21ae..2109b84f1 100644 --- a/include/attach.php +++ b/include/attach.php @@ -15,10 +15,10 @@ use Zotlabs\Lib\Libsync; use Zotlabs\Lib\Activity; use Zotlabs\Access\PermissionLimits; use Zotlabs\Daemon\Master; +use Zotlabs\Lib\AccessList; require_once('include/permissions.php'); require_once('include/security.php'); -require_once('include/group.php'); /** * @brief Guess the mimetype from file ending. @@ -2208,7 +2208,7 @@ function attach_recursive_perms($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $ //lookup all channels in sharee group and add them to sharee $arr_allow_cid if($arr_allow_gid) { - $in_group = expand_groups($arr_allow_gid); + $in_group = AccessList::expand($arr_allow_gid); $arr_allow_cid = array_unique(array_merge($arr_allow_cid, $in_group)); } @@ -2280,7 +2280,7 @@ function attach_recursive_perms($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $ //check sharee arr_allow_cid against members of allow_gid of all parent folders foreach($parent_arr['allow_gid'] as $folder_arr_allow_gid) { //get the group members - $folder_arr_allow_cid = expand_groups($folder_arr_allow_gid); + $folder_arr_allow_cid = AccessList::expand($folder_arr_allow_gid); foreach($folder_arr_allow_cid as $ac_hash) { $count_values[$ac_hash]++; } diff --git a/include/channel.php b/include/channel.php index 102a03489..00d973738 100644 --- a/include/channel.php +++ b/include/channel.php @@ -15,6 +15,7 @@ use Zotlabs\Render\Comanche; use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Connect; use Zotlabs\Lib\Libsync; +use Zotlabs\Lib\AccessList; require_once('include/crypto.php'); require_once('include/menu.php'); @@ -240,7 +241,7 @@ function create_identity($arr) { // Force a few things on the short term until we can provide a theme or app with choice - $publish = 1; + $publish = 0; if(array_key_exists('publish', $arr)) $publish = intval($arr['publish']); @@ -325,6 +326,12 @@ function create_identity($arr) { if($role_permissions && array_key_exists('perms_auto',$role_permissions)) set_pconfig($r[0]['channel_id'],'system','autoperms',intval($role_permissions['perms_auto'])); + $group_actor = false; + if($role_permissions && array_key_exists('channel_type', $role_permissions) && $role_permissions['channel_type'] === 'group') { + set_pconfig($r[0]['channel_id'], 'system', 'group_actor', 1); + $group_actor = true; + } + $ret['channel'] = $r[0]; if(intval($arr['account_id'])) @@ -374,7 +381,8 @@ function create_identity($arr) { 'xchan_network' => 'zot6', 'xchan_photo_date' => datetime_convert(), 'xchan_name_date' => datetime_convert(), - 'xchan_system' => $system + 'xchan_system' => $system, + 'xchan_pubforum' => $group_actor ] ); if(! $r) @@ -399,14 +407,6 @@ function create_identity($arr) { ] ); - if($role_permissions) { - $myperms = ((array_key_exists('perms_connect',$role_permissions)) ? $role_permissions['perms_connect'] : array()); - } - else { - $x = PermissionRoles::role_perms('social'); - $myperms = $x['perms_connect']; - } - $r = abook_store_lowlevel( [ 'abook_account' => intval($ret['channel']['channel_account_id']), @@ -419,19 +419,18 @@ function create_identity($arr) { ] ); - $x = Permissions::FilledPerms($myperms); - foreach($x as $k => $v) { - set_abconfig($newuid,$hash,'my_perms',$k,$v); - } - if(intval($ret['channel']['channel_account_id'])) { - // Save our permissions role so we can perhaps call it up and modify it later. + + // Set the default permcat + set_pconfig($newuid,'system','default_permcat','default'); if($role_permissions) { + // Save our permissions role so we can perhaps call it up and modify it later. set_pconfig($newuid,'system','permissions_role',$arr['permissions_role']); + if(array_key_exists('online',$role_permissions)) - set_pconfig($newuid,'system','hide_presence',1-intval($role_permissions['online'])); + set_pconfig($newuid,'system','show_online_status', intval($role_permissions['online'])); if(array_key_exists('perms_auto',$role_permissions)) { $autoperms = intval($role_permissions['perms_auto']); set_pconfig($newuid,'system','autoperms',$autoperms); @@ -453,11 +452,10 @@ function create_identity($arr) { // Create a group with yourself as a member. This allows somebody to use it // right away as a default group for new contacts. - require_once('include/group.php'); - $group_hash = group_add($newuid, t('Friends')); + $group_hash = AccessList::add($newuid, t('Friends')); if($group_hash) { - group_add_member($newuid,t('Friends'),$ret['channel']['channel_hash']); + AccessList::member_add($newuid,t('Friends'),$ret['channel']['channel_hash']); $default_collection_str = ''; // if our role_permissions indicate that we're using a default collection ACL, add it. @@ -496,8 +494,7 @@ function create_identity($arr) { if($acct) { $f = connect_and_sync($ret['channel'], $acct); if($f['success']) { - $can_view_stream = their_perms_contains($ret['channel']['channel_id'],$f['abook']['abook_xchan'],'view_stream'); - + $can_view_stream = intval(get_abconfig($ret['channel']['channel_id'], $f['abook']['abook_xchan'], 'their_perms', 'view_stream')); // If we can view their stream, pull in some posts if(($can_view_stream) || ($f['abook']['xchan_network'] === 'rss')) { Master::Summon([ 'Onepoll',$f['abook']['abook_id'] ]); @@ -1488,6 +1485,7 @@ function profile_load($nickname, $profile = '') { if($can_view_profile) { $online = get_online_status($nickname); + App::$profile['online_status'] = $online['result']; } @@ -2060,11 +2058,12 @@ function get_online_status($nick) { return $ret; $r = q("select channel_id, channel_hash from channel where channel_address = '%s' limit 1", - dbesc(argv(1)) + dbesc($nick) ); + if($r) { - $hide = get_pconfig($r[0]['channel_id'],'system','hide_online_status'); - if($hide) + $show = get_pconfig($r[0]['channel_id'],'system','show_online_status'); + if(!$show) return $ret; $x = q("select cp_status from chatpresence where cp_xchan = '%s' and cp_room = 0 limit 1", diff --git a/include/connections.php b/include/connections.php index 0611377ab..11264e6d8 100644 --- a/include/connections.php +++ b/include/connections.php @@ -27,7 +27,8 @@ function abook_store_lowlevel($arr) { 'abook_profile' => ((array_key_exists('abook_profile',$arr)) ? $arr['abook_profile'] : ''), 'abook_incl' => ((array_key_exists('abook_incl',$arr)) ? $arr['abook_incl'] : ''), 'abook_excl' => ((array_key_exists('abook_excl',$arr)) ? $arr['abook_excl'] : ''), - 'abook_instance' => ((array_key_exists('abook_instance',$arr)) ? $arr['abook_instance'] : '') + 'abook_instance' => ((array_key_exists('abook_instance',$arr)) ? $arr['abook_instance'] : ''), + 'abook_role' => ((array_key_exists('abook_role',$arr)) ? $arr['abook_role'] : '') ]; return create_table_from_array('abook',$store); diff --git a/include/conversation.php b/include/conversation.php index a7dfa6b73..d12151fcc 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1070,7 +1070,7 @@ function thread_author_menu($item, $mode = '') { if($contact) { $poke_link = ((Apps::system_app_installed($local_channel, 'Poke')) ? z_root() . '/poke/?f=&c=' . $contact['abook_id'] : ''); if (! intval($contact['abook_self'])) - $contact_url = z_root() . '/connedit/' . $contact['abook_id']; + $contact_url = z_root() . '/connections#' . $contact['abook_id']; $posts_link = z_root() . '/network/?cid=' . $contact['abook_id']; $clean_url = normalise_link($item['author-link']); @@ -1086,7 +1086,9 @@ function thread_author_menu($item, $mode = '') { 'title' => t('View Profile'), 'icon' => 'fw', 'action' => '', - 'href' => $profile_link + 'href' => $profile_link, + 'data' => '', + 'class' => '' ]; } @@ -1096,7 +1098,9 @@ function thread_author_menu($item, $mode = '') { 'title' => t('Recent Activity'), 'icon' => 'fw', 'action' => '', - 'href' => $posts_link + 'href' => $posts_link, + 'data' => '', + 'class' => '' ]; } @@ -1107,6 +1111,8 @@ function thread_author_menu($item, $mode = '') { 'icon' => 'fw', 'action' => 'doFollowAuthor(\'' . $follow_url . '\'); return false;', 'href' => '#', + 'data' => '', + 'class' => '' ]; } @@ -1116,7 +1122,9 @@ function thread_author_menu($item, $mode = '') { 'title' => t('Edit Connection'), 'icon' => 'fw', 'action' => '', - 'href' => $contact_url + 'href' => $contact_url, + 'data' => 'data-id="' . $contact['abook_id'] . '"', + 'class' => 'contact-edit' ]; } @@ -1126,7 +1134,9 @@ function thread_author_menu($item, $mode = '') { 'title' => t('Message'), 'icon' => 'fw', 'action' => '', - 'href' => $pm_url + 'href' => $pm_url, + 'data' => '', + 'class' => '' ]; } @@ -1136,7 +1146,9 @@ function thread_author_menu($item, $mode = '') { 'title' => t('Ratings'), 'icon' => 'fw', 'action' => '', - 'href' => $ratings_url + 'href' => $ratings_url, + 'data' => '', + 'class' => '' ]; } @@ -1146,7 +1158,9 @@ function thread_author_menu($item, $mode = '') { 'title' => t('Poke'), 'icon' => 'fw', 'action' => '', - 'href' => $poke_link + 'href' => $poke_link, + 'data' => '', + 'class' => '' ]; } diff --git a/include/group.php b/include/group.php index bb1ed5ed8..4e1472900 100644 --- a/include/group.php +++ b/include/group.php @@ -326,7 +326,7 @@ function group_side($every="connections",$each="group",$edit = false, $group_id $o = replace_macros($tpl, array( '$title' => t('Privacy Groups'), '$edittext' => t('Edit group'), - '$createtext' => t('Add privacy group'), + '$createtext' => ((argv(1) == 'new' ) ? '' : t('Manage privacy groups')), '$ungrouped' => (($every === 'contacts') ? t('Channels not in any privacy group') : ''), '$groups' => $groups, '$add' => t('add'), diff --git a/include/items.php b/include/items.php index 2ee870c2f..a66703e6e 100644 --- a/include/items.php +++ b/include/items.php @@ -13,9 +13,10 @@ use Zotlabs\Lib\IConfig; use Zotlabs\Lib\Activity; use Zotlabs\Lib\Libsync; use Zotlabs\Lib\Libzot; +use Zotlabs\Lib\ActivityStreams; use Zotlabs\Access\PermissionLimits; use Zotlabs\Access\PermissionRoles; -use Zotlabs\Access\AccessList; +use Zotlabs\Lib\AccessList; use Zotlabs\Daemon\Master; require_once('include/bbcode.php'); @@ -35,8 +36,6 @@ require_once('include/permissions.php'); */ function collect_recipients($item, &$private_envelope,$include_groups = true) { - require_once('include/group.php'); - $private_envelope = ((intval($item['item_private'])) ? true : false); $recipients = array(); @@ -47,7 +46,7 @@ function collect_recipients($item, &$private_envelope,$include_groups = true) { $allow_people = expand_acl($item['allow_cid']); if($include_groups) { - $allow_groups = expand_groups(expand_acl($item['allow_gid'])); + $allow_groups = AccessList::expand(expand_acl($item['allow_gid'])); } else { $allow_groups = []; @@ -72,7 +71,7 @@ function collect_recipients($item, &$private_envelope,$include_groups = true) { } $deny_people = expand_acl($item['deny_cid']); - $deny_groups = expand_groups(expand_acl($item['deny_gid'])); + $deny_groups = AccessList::expand(expand_acl($item['deny_gid'])); $deny = array_unique(array_merge($deny_people,$deny_groups)); @@ -344,6 +343,7 @@ function can_comment_on_post($observer_xchan, $item) { return true; break; case 'any connections': + case 'specific': case 'contacts': case '': if(local_channel() && get_abconfig(local_channel(),$item['owner_xchan'],'their_perms','post_comments')) { @@ -1226,6 +1226,9 @@ function map_scope($scope, $strip = false) { return 'site: ' . App::get_hostname(); case PERMS_PENDING: return 'any connections'; +// uncomment after Hubzilla version 7.0 is running on the majority of active hubs +// case PERMS_SPECIFIC: +// return 'specific'; case PERMS_CONTACTS: default: return 'contacts'; @@ -2537,12 +2540,7 @@ function get_item_contact($item,$contacts) { */ function tag_deliver($uid, $item_id) { - $role = get_pconfig($uid,'system','permissions_role'); - $rolesettings = PermissionRoles::role_perms($role); - $channel_type = isset($rolesettings['channel_type']) ? $rolesettings['channel_type'] : 'normal'; - - $is_group = (($channel_type === 'group') ? true : false); - + $is_group = get_pconfig($uid, 'system', 'group_actor'); $mention = false; /* @@ -2579,15 +2577,18 @@ function tag_deliver($uid, $item_id) { } if ($is_group && intval($item['item_private']) === 2 && intval($item['item_thread_top'])) { - // do not turn the groups own direkt messages into group items if($item['item_wall'] && $item['author_xchan'] === $u[0]['channel_hash']) return; // group delivery via DM - if(perm_is_allowed($uid,$item['owner_xchan'],'post_wall') || perm_is_allowed($uid,$item['owner_xchan'],'tag_deliver')) { + if(perm_is_allowed($uid,$item['owner_xchan'],'post_wall')) { logger('group DM delivery for ' . $u[0]['channel_address']); start_delivery_chain($u[0], $item, $item_id, 0, true, (($item['edited'] != $item['created']) || $item['item_deleted'])); + q("update item set item_blocked = %d where id = %d", + intval(ITEM_HIDDEN), + intval($item_id) + ); } return; } @@ -2600,13 +2601,6 @@ function tag_deliver($uid, $item_id) { return; } - /* this should not be required anymore due to the check above - if (strpos($item['body'],'[/share]')) { - logger('W2W post already shared'); - return; - } - */ - // group delivery via W2W logger('rewriting W2W post for ' . $u[0]['channel_address']); start_delivery_chain($u[0], $item, $item_id, 0, true, (($item['edited'] != $item['created']) || $item['item_deleted'])); @@ -2677,9 +2671,37 @@ function tag_deliver($uid, $item_id) { intval($uid) ); - if(($x) && intval($x[0]['item_uplink'])) { - start_delivery_chain($u[0],$item,$item_id,$x[0]); + if ($x) { + + // group comments don't normally require a second delivery chain + // but we create a linked Announce so they will show up in the home timeline + // on microblog platforms and this creates a second delivery chain + + if ($is_group && intval($x[0]['item_wall'])) { + // don't let the forked delivery chain recurse + if ($item['verb'] === 'Announce' && $item['author_xchan'] === $u['channel_hash']) { + return; + } + // don't announce moderated content until it has been approved + if (intval($item['item_blocked']) === ITEM_MODERATED) { + return; + } + + // don't boost likes and other response activities as it is likely that + // few platforms will handle this in an elegant way + + if (ActivityStreams::is_response_activity($item['verb'])) { + return; + } + logger('group_comment'); + start_delivery_chain($u[0], $item, $item_id, $x[0], true, (($item['edited'] != $item['created']) || $item['item_deleted'])); + + } + elseif (intval($x[0]['item_uplink'])) { + start_delivery_chain($u,$item,$item_id,$x[0]); + } } + } @@ -2920,13 +2942,7 @@ function item_community_tag($channel,$item) { */ function tgroup_check($uid, $item) { - - $role = get_pconfig($uid,'system','permissions_role'); - $rolesettings = PermissionRoles::role_perms($role); - $channel_type = isset($rolesettings['channel_type']) ? $rolesettings['channel_type'] : 'normal'; - - $is_group = (($channel_type === 'group') ? true : false); - + $is_group = get_pconfig($uid, 'system', 'group_actor'); $mention = false; // check that the message originated elsewhere and is a top-level post @@ -3125,6 +3141,10 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false $arr = []; + q("update item set item_hidden = 1 where id = %d", + intval($item_id) + ); + if ($edit) { // process edit or delete action @@ -3155,7 +3175,7 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false } else { $arr['uuid'] = item_message_id(); - $arr['mid'] = z_root() . '/activity/' . $arr['uuid']; + $arr['mid'] = z_root() . '/item/' . $arr['uuid']; $arr['parent_mid'] = $arr['mid']; } @@ -3173,6 +3193,10 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false $arr['item_private'] = (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 1 : 0); + if ($channel['channel_allow_cid'] && empty($channel['channel_allow_gid'])) { + $arr['item_private'] = 2; + } + $arr['item_origin'] = 1; $arr['item_wall'] = 1; $arr['item_thread_top'] = 1; @@ -3192,6 +3216,29 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false $bb .= "[/share]"; $arr['body'] = $bb; + // Conversational objects shouldn't be copied, but other objects should. + if (in_array($item['obj_type'], [ 'Image', 'Event', 'Question' ])) { + $arr['obj'] = $item['obj']; + $t = json_decode($arr['obj'],true); + + if ($t !== NULL) { + $arr['obj'] = $t; + } + $arr['obj']['content'] = bbcode($bb); + $arr['obj']['source']['content'] = $bb; + $arr['obj']['id'] = $arr['mid']; + + if (! array_path_exists('obj/source/mediaType',$arr)) { + $arr['obj']['source']['mediaType'] = 'text/bbcode'; + } + + $arr['obj']['directMessage'] = (intval($arr['item_private']) === 2); + + } + + $arr['tgt_type'] = $item['tgt_type']; + $arr['target'] = $item['target']; + $arr['term'] = $item['term']; $arr['author_xchan'] = $channel['channel_hash']; @@ -3222,6 +3269,92 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false } + if ($group && $parent) { + logger('comment arrived in group', LOGGER_DEBUG); + $arr = []; + + // don't let this recurse. We checked for this before calling, but this ensures + // it doesn't sneak through another way because recursion is nasty. + + if ($item['verb'] === 'Announce' && $item['author_xchan'] === $channel['channel_hash']) { + return; + } + + // Don't send Announce activities for poll responses. + + if ($item['obj_type'] === 'Answer') { + return; + } + + if ($edit) { + if (intval($item['item_deleted'])) { + drop_item($item['id'],false,DROPITEM_PHASE1); + Master::Summon([ 'Notifier','drop',$item['id'] ]); + return; + } + return; + } + else { + $arr['uuid'] = item_message_id(); + $arr['mid'] = z_root() . '/activity/' . $arr['uuid']; + $arr['parent_mid'] = $item['parent_mid']; + //IConfig::Set($arr,'activitypub','context', str_replace('/item/','/conversation/',$item['parent_mid'])); + } + $arr['aid'] = $channel['channel_account_id']; + $arr['uid'] = $channel['channel_id']; + + $arr['verb'] = 'Announce'; + + if (is_array($item['obj'])) { + $arr['obj'] = $item['obj']; + } + elseif (is_string($item['obj']) && strlen($item['obj'])) { + $arr['obj'] = json_decode($item['obj'],true); + } + + if (! $arr['obj']) { + $arr['obj'] = $item['mid']; + } + + if (is_array($arr['obj'])) { + $obj_actor = ((isset($arr['obj']['actor'])) ? ((is_array($arr['obj']['actor'])) ? $arr['obj']['actor']['id'] : $arr['obj']['actor']) : $arr['obj']['attributedTo']); + $mention = Activity::get_actor_bbmention($obj_actor); + $arr['body'] = sprintf( t('🔁 Repeated %1$s\'s %2$s'), $mention, $arr['obj']['type']); + } + + $arr['author_xchan'] = $channel['channel_hash']; + + $arr['item_wall'] = 1; + + $arr['item_private'] = (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 1 : 0); + + $arr['item_origin'] = 1; + $arr['item_notshown'] = 1; + + $arr['item_thread_top'] = 0; + + $arr['allow_cid'] = $channel['channel_allow_cid']; + $arr['allow_gid'] = $channel['channel_allow_gid']; + $arr['deny_cid'] = $channel['channel_deny_cid']; + $arr['deny_gid'] = $channel['channel_deny_gid']; + $arr['comment_policy'] = map_scope(PermissionLimits::Get($channel['channel_id'],'post_comments')); + + $post = item_store($arr); + $post_id = $post['item_id']; + + if ($post_id) { + Master::Summon([ 'Notifier','tgroup',$post_id ]); + } + + q("update channel set channel_lastpost = '%s' where channel_id = %d", + dbesc(datetime_convert()), + intval($channel['channel_id']) + ); + + return; + } + + // Change this copy of the post to a forum head message and deliver to all the tgroup members // also reset all the privacy bits to the forum default permissions @@ -3510,12 +3643,11 @@ function compare_permissions($obj1,$obj2) { * @return array */ function enumerate_permissions($obj) { - require_once('include/group.php'); $allow_people = expand_acl($obj['allow_cid']); - $allow_groups = expand_groups(expand_acl($obj['allow_gid'])); + $allow_groups = AccessList::expand(expand_acl($obj['allow_gid'])); $deny_people = expand_acl($obj['deny_cid']); - $deny_groups = expand_groups(expand_acl($obj['deny_gid'])); + $deny_groups = AccessList::expand(expand_acl($obj['deny_gid'])); $recipients = array_unique(array_merge($allow_people,$allow_groups)); $deny = array_unique(array_merge($deny_people,$deny_groups)); $recipients = array_diff($recipients,$deny); @@ -4252,7 +4384,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C $contact_str = ''; - $contacts = group_get_members($r[0]['id']); + $contacts = AccessList::members($uid, $r[0]['id']); if ($contacts) { foreach($contacts as $c) { if($contact_str) @@ -4268,7 +4400,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND (( author_xchan IN ( $contact_str ) OR owner_xchan in ( $contact_str)) or allow_gid like '" . protect_sprintf('%<' . dbesc($r[0]['hash']) . '>%') . "' ) and id = parent $item_normal ) "; - $x = group_rec_byhash($uid,$r[0]['hash']); + $x = AccessList::by_hash($uid, $r[0]['hash']); $result['headline'] = sprintf( t('Privacy group: %s'),$x['gname']); } elseif($arr['cid'] && $uid) { diff --git a/include/nav.php b/include/nav.php index 7cc64ab1b..9278c1587 100644 --- a/include/nav.php +++ b/include/nav.php @@ -26,7 +26,7 @@ function nav($template = 'default') { intval($channel['channel_id']) ); - if (empty($_SESSION['delegate'])) { + if (empty($_SESSION['delegate']) && feature_enabled(local_channel(), 'nav_channel_select')) { $chans = q("select channel_name, channel_id from channel where channel_account_id = %d and channel_removed = 0 order by channel_name ", intval(get_account_id()) ); @@ -97,13 +97,11 @@ function nav($template = 'default') { if (empty($_SESSION['delegate'])) { $nav['manage'] = ['manage', t('Channels'), "", t('Manage your channels'), 'manage_nav_btn']; } - if (Apps::system_app_installed(local_channel(), 'Privacy Groups')) - $nav['group'] = ['group', t('Privacy Groups'), "", t('Manage your privacy groups'), 'group_nav_btn']; $nav['settings'] = ['settings', t('Settings'), "", t('Account/Channel Settings'), 'settings_nav_btn']; - if ($chans && count($chans) > 1 && feature_enabled(local_channel(), 'nav_channel_select')) + if ($chans && count($chans) > 1) $nav['channels'] = $chans; $nav['logout'] = ['logout', t('Logout'), "", t('End this session'), 'logout_nav_btn']; diff --git a/include/permissions.php b/include/permissions.php index d94b70da6..bbd13ed58 100644 --- a/include/permissions.php +++ b/include/permissions.php @@ -21,7 +21,7 @@ require_once('include/security.php'); * @param bool $default_ignored (default true) * if false, lie and pretend the ignored person has permissions you are ignoring (used in channel discovery) * - * @returns array of all permissions, key is permission name, value is true or false + * @returns array of all permissions, key is permission name, value is 1 or 0 */ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ignored = true) { @@ -61,7 +61,7 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ // The uid provided doesn't exist. This would be a big fail. if(! $r) { - $ret[$perm_name] = false; + $ret[$perm_name] = 0; continue; } @@ -70,7 +70,7 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ if($observer_xchan) { if($channel_perm & PERMS_AUTHED) { - $ret[$perm_name] = true; + $ret[$perm_name] = 1; continue; } @@ -104,7 +104,7 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ // If they're blocked - they can't read or write if(($x) && intval($x[0]['abook_blocked'])) { - $ret[$perm_name] = false; + $ret[$perm_name] = 0; continue; } @@ -115,7 +115,7 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ if(($x) && ($default_ignored) && in_array($perm_name,$blocked_anon_perms) && intval($x[0]['abook_ignored'])) { - $ret[$perm_name] = false; + $ret[$perm_name] = 0; continue; } } @@ -123,7 +123,7 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ // system is blocked to anybody who is not authenticated if(($check_siteblock) && (! $observer_xchan) && intval(get_config('system', 'block_public'))) { - $ret[$perm_name] = false; + $ret[$perm_name] = 0; continue; } @@ -133,16 +133,16 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ if(($observer_xchan) && ($r[0]['channel_hash'] === $observer_xchan)) { if($r[0]['channel_moved'] && (in_array($perm_name,$blocked_anon_perms))) - $ret[$perm_name] = false; + $ret[$perm_name] = 0; else - $ret[$perm_name] = true; + $ret[$perm_name] = 1; continue; } // Anybody at all (that wasn't blocked or ignored). They have permission. if($channel_perm & PERMS_PUBLIC) { - $ret[$perm_name] = true; + $ret[$perm_name] = 1; continue; } @@ -150,7 +150,7 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ // out, permission is denied. if(! $observer_xchan) { - $ret[$perm_name] = false; + $ret[$perm_name] = 0; continue; } @@ -158,7 +158,7 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ if($channel_perm & PERMS_NETWORK) { if($x && $x[0]['xchan_network'] === 'zot6') { - $ret[$perm_name] = true; + $ret[$perm_name] = 1; continue; } } @@ -175,9 +175,9 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ } if($c) - $ret[$perm_name] = true; + $ret[$perm_name] = 1; else - $ret[$perm_name] = false; + $ret[$perm_name] = 0; continue; } @@ -186,19 +186,19 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ // handle whether we're allowing any, approved or specific ones if(! $x) { - $ret[$perm_name] = false; + $ret[$perm_name] = 0; continue; } // They are in your address book, but haven't been approved if($channel_perm & PERMS_PENDING && (! intval($x[0]['abook_pseudo']))) { - $ret[$perm_name] = true; + $ret[$perm_name] = 1; continue; } if(intval($x[0]['abook_pending'])) { - $ret[$perm_name] = false; + $ret[$perm_name] = 0; continue; } @@ -207,11 +207,11 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ if($channel_perm & PERMS_CONTACTS) { // it was a fake abook entry, not really a connection if(array_key_exists('abook_pseudo',$x[0]) && intval($x[0]['abook_pseudo'])) { - $ret[$perm_name] = false; + $ret[$perm_name] = 0; continue; } - $ret[$perm_name] = true; + $ret[$perm_name] = 1; continue; } @@ -221,7 +221,7 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ if($abperms) { foreach($abperms as $ab) { if(($ab['cat'] == 'my_perms') && ($ab['k'] == $perm_name)) { - $ret[$perm_name] = (intval($ab['v']) ? true : false); + $ret[$perm_name] = (intval($ab['v']) ? 1 : 0); break; } } @@ -231,7 +231,7 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ // No permissions allowed. - $ret[$perm_name] = false; + $ret[$perm_name] = 0; continue; } diff --git a/include/selectors.php b/include/selectors.php index 71e2a387d..d58ef1085 100644 --- a/include/selectors.php +++ b/include/selectors.php @@ -15,7 +15,7 @@ function contact_profile_assign($current) { $select = [ 'profile_assign', - t('Profile to assign new connections'), + t('Select a profile to assign to this contact'), $current, '', $options @@ -70,7 +70,7 @@ function gender_selector($current="",$suffix="") { } $o .= '</select>'; return $o; -} +} function gender_selector_min($current="",$suffix="") { $o = ''; @@ -87,7 +87,7 @@ function gender_selector_min($current="",$suffix="") { } $o .= '</select>'; return $o; -} +} @@ -107,7 +107,7 @@ function sexpref_selector($current="",$suffix="") { } $o .= '</select>'; return $o; -} +} function sexpref_selector_min($current="",$suffix="") { @@ -125,7 +125,7 @@ function sexpref_selector_min($current="",$suffix="") { } $o .= '</select>'; return $o; -} +} @@ -144,7 +144,7 @@ function marital_selector($current="",$suffix="") { } $o .= '</select>'; return $o; -} +} function marital_selector_min($current="",$suffix="") { $o = ''; @@ -161,5 +161,5 @@ function marital_selector_min($current="",$suffix="") { } $o .= '</select>'; return $o; -} +} diff --git a/include/text.php b/include/text.php index d46c089b5..b2b3fce6e 100644 --- a/include/text.php +++ b/include/text.php @@ -12,6 +12,7 @@ use Ramsey\Uuid\Exception\UnableToBuildUuidException; use Zotlabs\Lib\Crypto; use Zotlabs\Lib\SvgSanitizer; use Zotlabs\Lib\Libzot; +use Zotlabs\Lib\AccessList; require_once("include/bbcode.php"); @@ -1753,6 +1754,7 @@ function prepare_body(&$item,$attach = false,$opts = false) { } } + $poll = (($item['obj_type'] === 'Question' && in_array($item['verb'],[ ACTIVITY_POST, ACTIVITY_UPDATE, ACTIVITY_SHARE ])) ? format_poll($item, $s, $opts) : false); if ($poll) { $s = $poll; @@ -1854,11 +1856,16 @@ function format_poll($item,$s,$opts) { return EMPTY_STR; } - $commentable = can_comment_on_post(((local_channel()) ? get_observer_hash() : EMPTY_STR),$item); + $commentable = can_comment_on_post(((local_channel()) ? get_observer_hash() : EMPTY_STR), $item); + + $activated = ((local_channel() && local_channel() == $item['uid'] && get_observer_hash() !== $item['owner_xchan']) ? true : false); + $output = $s; + + if (strpos($item['body'], '[/share]') !== false) { + $output = substr($output, 0, -12); + } - //logger('format_poll: ' . print_r($item,true)); - $activated = ((local_channel() && local_channel() == $item['uid']) ? true : false); - $output = $s . EOL. EOL; + $output .= EOL . EOL; if ($act['type'] === 'Question') { if ($activated and $commentable) { @@ -1884,6 +1891,12 @@ function format_poll($item,$s,$opts) { } } if (array_key_exists('oneOf',$act) && is_array($act['oneOf'])) { + $totalResponses = 0; + foreach ($act['oneOf'] as $poll) { + if (array_path_exists('replies/totalItems',$poll)) { + $totalResponses += intval($poll['replies']['totalItems']); + } + } foreach ($act['oneOf'] as $poll) { if (array_key_exists('name',$poll) && $poll['name']) { $text = html2plain(purify_html($poll['name']),256); @@ -1894,29 +1907,48 @@ function format_poll($item,$s,$opts) { $total = 0; } if ($activated && $commentable) { - $output .= '<input type="radio" name="answer" value="' . htmlspecialchars($text) . '"> ' . $text . '</input>' . ' (' . $total . ')' . EOL; + $output .= '<input type="radio" name="answer" value="' . htmlspecialchars($text) . '"> <strong>' . $text . '</strong>' . EOL; + $output .= '<div class="progress bg-secondary bg-opacity-25" style="height: 3px;">'; + $output .= '<div class="progress-bar bg-info" role="progressbar" style="width: ' . (($totalResponses) ? intval($total / $totalResponses * 100) : 0). '%;" aria-valuenow="" aria-valuemin="0" aria-valuemax="100"></div>'; + $output .= '</div>'; + $output .= '<div class="text-muted"><small>' . sprintf(tt('%d Vote', '%d Votes', $total, 'noun'), $total) . ' | ' . (($totalResponses) ? intval($total / $totalResponses * 100) . '%' : '0%') . '</small></div>'; + $output .= EOL; } + else { - $output .= '( ) ' . $text . ' (' . $total . ')' . EOL; + $output .= '<input type="radio" name="answer" value="' . htmlspecialchars($text) . '" disabled="disabled"> <strong>' . $text . '</strong>' . EOL; + $output .= '<div class="progress bg-secondary bg-opacity-25" style="height: 3px;">'; + $output .= '<div class="progress-bar bg-info" role="progressbar" style="width: ' . (($totalResponses) ? intval($total / $totalResponses * 100) : 0) . '%;" aria-valuenow="" aria-valuemin="0" aria-valuemax="100"></div>'; + $output .= '</div>'; + $output .= '<div class="text-muted"><small>' . sprintf(tt('%d Vote', '%d Votes', $total, 'noun'), $total) . ' | ' . (($totalResponses) ? intval($total / $totalResponses * 100) . '%' : '0%') . '</small></div>'; + $output .= EOL; } } } } + + $message = (($totalResponses) ? sprintf(tt('%d Vote in total', '%d Votes in total', $totalResponses, 'noun'), $totalResponses) . EOL : ''); + if ($item['comments_closed'] > NULL_DATE) { $t = datetime_convert('UTC',date_default_timezone_get(), $item['comments_closed'], 'Y-m-d H:i'); $closed = ((datetime_convert() > $item['comments_closed']) ? true : false); if ($closed) { - $message = t('Poll has ended.'); + $message .= t('Poll has ended'); } else { - $message = sprintf(t('Poll ends: %s'),$t); + $message .= sprintf(t('Poll ends in %s'), '<span class="autotime" title="' . $t . '"></span>'); } - $output .= EOL . '<div>' . $message . '</div>'; } - if ($activated and $commentable) { - $output .= EOL . '<input type="button" class="btn btn-std btn-success" name="vote" value="' . t("Vote") . '" onclick="submitPoll(' . $item['id'] . '); return false;">'. '</form>'; + + $output .= '<div class="mb-3">' . $message . '</div>'; + + if ($activated && $commentable && !$closed) { + $output .= '<input type="button" class="btn btn-std btn-success" name="vote" value="' . t("Vote") . '" onclick="submitPoll(' . $item['id'] . '); return false;">'. '</form>'; } + if (strpos($item['body'], '[/share]') !== false) { + $output .= '</div></div>'; + } } return $output; } @@ -2989,7 +3021,7 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) // weird - as all the other tags are linked to something. if(local_channel() && local_channel() == $profile_uid) { - $grp = group_byname($profile_uid,$name); + $grp = AccessList::byname($profile_uid,$name); if($grp) { $g = q("select hash from pgrp where id = %d and visible = 1 limit 1", @@ -3692,6 +3724,13 @@ function get_forum_channels($uid) { if(! $uid) return; + $r = q("select abook_id, xchan_pubforum, xchan_hash, xchan_network, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where xchan_deleted = 0 and abook_channel = %d and abook_pending = 0 and abook_ignored = 0 and abook_blocked = 0 and abook_archived = 0 and abook_self = 0 and xchan_pubforum = 1 order by xchan_name", + intval($uid) + ); + + +/* + if(isset(App::$data['forum_channels'])) return App::$data['forum_channels']; @@ -3763,6 +3802,7 @@ function get_forum_channels($uid) { } App::$data['forum_channels'] = $r; +*/ return $r; diff --git a/tests/unit/Access/PermissionLimitsTest.php b/tests/unit/Access/PermissionLimitsTest.php index 57ad42a19..0a90d11df 100644 --- a/tests/unit/Access/PermissionLimitsTest.php +++ b/tests/unit/Access/PermissionLimitsTest.php @@ -45,8 +45,8 @@ class PermissionLimitsTest extends UnitTestCase { * @uses ::call_hooks */ public function testStd_Limits() { - // There are 18 default perms - $permsCount = 18; + // There are 17 default perms + $permsCount = 17; // Create a stub for global function t() with expectation $t = $this->getFunctionMock('Zotlabs\Access', 't'); @@ -69,10 +69,9 @@ class PermissionLimitsTest extends UnitTestCase { $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_comments']); $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_mail']); $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_like']); - $this->assertEquals(PERMS_SPECIFIC, $stdlimits['tag_deliver']); $this->assertEquals(PERMS_SPECIFIC, $stdlimits['chat']); $this->assertEquals(PERMS_SPECIFIC, $stdlimits['republish']); $this->assertEquals(PERMS_SPECIFIC, $stdlimits['delegate']); } -}
\ No newline at end of file +} diff --git a/tests/unit/Access/PermissionRolesTest.php b/tests/unit/Access/PermissionRolesTest.php index dd55eb6e2..3d3cbbae5 100644 --- a/tests/unit/Access/PermissionRolesTest.php +++ b/tests/unit/Access/PermissionRolesTest.php @@ -40,7 +40,7 @@ class PermissionRolesTest extends UnitTestCase { use PHPMock; public function testVersion() { - $expectedVersion = 2; + $expectedVersion = 3; $this->assertEquals($expectedVersion, PermissionRoles::version()); @@ -58,23 +58,12 @@ class PermissionRolesTest extends UnitTestCase { } ); - $roles = PermissionRoles::roles(); + $roles = PermissionRoles::channel_roles(); $r = new PermissionRoles(); - $this->assertEquals($roles, $r->roles()); + $this->assertEquals($roles, $r->channel_roles()); - $socialNetworking = [ - 'social_federation' => 'Social - Federation', - 'social' => 'Social - Mostly Public', - 'social_restricted' => 'Social - Restricted', - 'social_private' => 'Social - Private' - ]; + $this->assertCount(4, $roles, 'There should be 4 channel roles.'); - Assert::assertArraySubset(['Social Networking' => $socialNetworking], $roles); - $this->assertEquals($socialNetworking, $roles['Social Networking']); - - $this->assertCount(5, $roles, 'There should be 5 permission groups.'); - - $this->assertCount(1, $roles['Other'], "In the 'Other' group should be just one permission role"); } @@ -88,12 +77,9 @@ class PermissionRolesTest extends UnitTestCase { $t = $this->getFunctionMock('Zotlabs\Access', 't'); $t = $this->getFunctionMock('Zotlabs\Access', 'get_config'); - $rp_social = PermissionRoles::role_perms('social'); - $this->assertEquals('social', $rp_social['role']); - + $rp_social = PermissionRoles::role_perms('personal'); + $this->assertEquals('personal', $rp_social['role']); - $rp_custom = PermissionRoles::role_perms('custom'); - $this->assertEquals(['role' => 'custom'], $rp_custom); $rp_nonexistent = PermissionRoles::role_perms('nonexistent'); $this->assertEquals(['role' => 'nonexistent'], $rp_nonexistent); diff --git a/tests/unit/Access/PermissionsTest.php b/tests/unit/Access/PermissionsTest.php index 40724fff8..3ab567ad0 100644 --- a/tests/unit/Access/PermissionsTest.php +++ b/tests/unit/Access/PermissionsTest.php @@ -37,7 +37,7 @@ class PermissionsTest extends UnitTestCase { use PHPMock; public function testVersion() { - $expectedVersion = 2; + $expectedVersion = 3; // static call $this->assertEquals($expectedVersion, Permissions::version()); @@ -60,8 +60,8 @@ class PermissionsTest extends UnitTestCase { * @uses ::call_hooks */ public function testPerms() { - // There are 18 default perms - $permsCount = 18; + // There are 17 default perms + $permsCount = 17; // Create a stub for global function t() with expectation $t = $this->getFunctionMock('Zotlabs\Access', 't'); @@ -94,8 +94,8 @@ class PermissionsTest extends UnitTestCase { * @uses ::call_hooks */ public function testPermsFilter() { - // There are 18 default perms - $permsCount = 18; + // There are 17 default perms + $permsCount = 17; // Create a stub for global function t() with expectation $t = $this->getFunctionMock('Zotlabs\Access', 't'); @@ -154,7 +154,6 @@ class PermissionsTest extends UnitTestCase { 'post_comments' => 0, 'post_mail' => 0, 'post_like' => 0, - 'tag_deliver' => 0, 'chat' => 0, 'republish' => 0, 'delegate' => 0 @@ -177,7 +176,6 @@ class PermissionsTest extends UnitTestCase { 'post_comments' => 0, 'post_mail' => 0, 'post_like' => 0, - 'tag_deliver' => 0, 'chat' => 0, 'republish' => 0, 'delegate' => 0 @@ -200,7 +198,6 @@ class PermissionsTest extends UnitTestCase { 'post_comments' => 0, 'post_mail' => 0, 'post_like' => 0, - 'tag_deliver' => 0, 'chat' => 0, 'republish' => 0, 'delegate' => 0 @@ -234,7 +231,6 @@ class PermissionsTest extends UnitTestCase { 'post_comments' => 0, 'post_mail' => 0, 'post_like' => 0, - 'tag_deliver' => 0, 'chat' => 0, 'republish' => 0, 'delegate' => 0 diff --git a/view/css/mod_connections.css b/view/css/mod_connections.css index 4c26193b4..6a83367b8 100644 --- a/view/css/mod_connections.css +++ b/view/css/mod_connections.css @@ -1,17 +1,8 @@ +#contacts-follow-form, #contacts-search-form { display: none; } -.section-subtitle-wrapper .btn-xs { - margin-top: -2px; -} - -.contact-search { - height: 22px; - border-top-right-radius: 0px; - border-bottom-right-radius: 0px; -} - .directory-photo-img.archived { opacity: 0.3; filter:alpha(opacity=30); @@ -39,7 +30,8 @@ } .contact-photo-wrapper .oneway-overlay { - margin-top: -25px; - margin-left: 53px; - font-size: 20px; + margin-top: -18px; + margin-left: 62px; + font-size: 1.2rem; + display: inherit; /* this is required for chromium */ } diff --git a/view/css/widgets.css b/view/css/widgets.css index df77940dd..0a091272c 100644 --- a/view/css/widgets.css +++ b/view/css/widgets.css @@ -244,9 +244,8 @@ a.wikilist { /* contact block */ .contact-block-div .oneway-overlay { font-size: .9rem; - margin-left: -1.2rem; - margin-top: 2rem; - opacity: 0.8; + margin-left: -1rem; + margin-top: 1.9rem; } #cid-filter-wrapper { diff --git a/view/js/main.js b/view/js/main.js index 0e16809e4..f4f641e33 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -525,11 +525,14 @@ function showHideComments(id) { } } -function openClose(theID) { - if(document.getElementById(theID).style.display == "block") { +function openClose(theID, display) { + if (typeof display == typeof undefined) + display = 'block'; + + if(document.getElementById(theID).style.display == display) { document.getElementById(theID).style.display = "none"; } else { - document.getElementById(theID).style.display = "block"; + document.getElementById(theID).style.display = display; } } @@ -541,9 +544,11 @@ function openCloseTR(theID) { } } -function closeOpen(theID) { +function closeOpen(theID, display) { + if (typeof display == typeof undefined) + display = 'block'; if(document.getElementById(theID).style.display == "none") { - document.getElementById(theID).style.display = "block"; + document.getElementById(theID).style.display = display; } else { document.getElementById(theID).style.display = "none"; } diff --git a/view/js/mod_connections.js b/view/js/mod_connections.js index db18e078d..848e9ed9f 100644 --- a/view/js/mod_connections.js +++ b/view/js/mod_connections.js @@ -1,5 +1,5 @@ $(document).ready(function() { $("#contacts-search").contact_autocomplete(baseurl + '/acl', 'a', true); $(".autotime").timeago(); -}); +}); diff --git a/view/pdl/mod_chat.pdl b/view/pdl/mod_chat.pdl index 808ba3d5a..8a79b4b12 100644 --- a/view/pdl/mod_chat.pdl +++ b/view/pdl/mod_chat.pdl @@ -1,5 +1,5 @@ [region=aside] -[widget=vcard][/widget] +[widget=profile][/widget] [widget=chatroom_list][/widget] [widget=bookmarkedchats][/widget] [widget=suggestedchats][/widget] diff --git a/view/pdl/mod_connections.pdl b/view/pdl/mod_connections.pdl index b777cada5..3c3dfd313 100644 --- a/view/pdl/mod_connections.pdl +++ b/view/pdl/mod_connections.pdl @@ -1,7 +1,4 @@ [region=aside] -[widget=follow][/widget] -[widget=collections][var=mode][connections[/arg][/widget] -[widget=suggestions][/widget] [widget=findpeople][/widget] [/region] [region=right_aside] diff --git a/view/pdl/mod_connedit.pdl b/view/pdl/mod_connedit.pdl index 4bdadb5e8..fa2f2969b 100644 --- a/view/pdl/mod_connedit.pdl +++ b/view/pdl/mod_connedit.pdl @@ -1,8 +1,6 @@ [region=aside] [widget=vcard][/widget] [widget=collections][var=mode]abook[/var][/widget] -[widget=suggestions][/widget] -[widget=findpeople][/widget] [/region] [region=right_aside] [widget=notifications][/widget] diff --git a/view/pdl/mod_import_items.pdl b/view/pdl/mod_import_items.pdl new file mode 100644 index 000000000..e657fa88b --- /dev/null +++ b/view/pdl/mod_import_items.pdl @@ -0,0 +1,3 @@ +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_permcats.pdl b/view/pdl/mod_permcats.pdl new file mode 100644 index 000000000..bc6c89920 --- /dev/null +++ b/view/pdl/mod_permcats.pdl @@ -0,0 +1,7 @@ +[region=aside] +[widget=permcats][/widget] +[/region] +[region=right_aside] +[widget=notifications][/widget] +[widget=newmember][/widget] +[/region] diff --git a/view/pdl/mod_profperm.pdl b/view/pdl/mod_profperm.pdl index 7e15fc869..d7e5d29f3 100644 --- a/view/pdl/mod_profperm.pdl +++ b/view/pdl/mod_profperm.pdl @@ -1,5 +1,5 @@ [region=aside] -[widget=fullprofile][/widget] +[widget=profile][/widget] [/region] [region=right_aside] [widget=notifications][/widget] diff --git a/view/pdl/mod_viewconnections.pdl b/view/pdl/mod_viewconnections.pdl index 7e15fc869..d7e5d29f3 100644 --- a/view/pdl/mod_viewconnections.pdl +++ b/view/pdl/mod_viewconnections.pdl @@ -1,5 +1,5 @@ [region=aside] -[widget=fullprofile][/widget] +[widget=profile][/widget] [/region] [region=right_aside] [widget=notifications][/widget] diff --git a/view/theme/redbasic/css/style.css b/view/theme/redbasic/css/style.css index ac5b6a80a..542940404 100644 --- a/view/theme/redbasic/css/style.css +++ b/view/theme/redbasic/css/style.css @@ -418,11 +418,6 @@ footer { filter:alpha(opacity=100); } -#profile-photo-wrapper img { - width: 100%; - height: 100%; -} - #hide-friends-yes-label, #hide-friends-no-label { margin-left: 125px; @@ -1223,24 +1218,24 @@ img.mail-conv-sender-photo { } .menu-img-3 { - width: 3.3em; - height: 3.3em; + width: 2.7rem; + height: 2.7rem; margin-right: 5px; border-radius: $radius; float: left; } .menu-img-2 { - height: 2.2em; - width: 2.2em; + height: 1.8rem; + width: 1.8rem; margin-right: 5px; border-radius: $radius; float: left; } .menu-img-1 { - height: 1.28571429em; - width: 1.28571429em; + height: 1.1rem; + width: 1.1rem; border-radius: $radius; } @@ -1784,7 +1779,7 @@ dl.bb-dl > dd > li { .oneway-overlay { position: absolute; - text-shadow: -2px 0 1px #fff, 0 2px 1px#fff, 2px 0 1px #fff, 0 -2px 1px #fff; + text-shadow: -1px 0 1px #fff, 0 1px 1px#fff, 1px 0 1px #fff, 0 -1px 1px #fff; } .cover-photo-review { diff --git a/view/theme/redbasic/schema/Focus-Boxy.css b/view/theme/redbasic/schema/Focus-Boxy.css index b9b259796..90fd51b02 100644 --- a/view/theme/redbasic/schema/Focus-Boxy.css +++ b/view/theme/redbasic/schema/Focus-Boxy.css @@ -3,13 +3,31 @@ } .wall-item-content-wrapper.comment { - border-bottom: 1px solid #dee2e6; + border-bottom: 1px solid rgba(0,0,0,.125); } .widget { - border: 1px solid #dee2e6; + border: 1px solid rgba(0,0,0,.125); } #note-text { border: 1px solid transparent; } + +.vcard-card { + border: 1px solid rgba(0,0,0,.125); + border-bottom: 0; +} + +.vcard-card .card { + border-top: 0; + border-right: 0; + border-left: 0; +} + +.vcard-card .vcard { + border: 1px solid rgba(0,0,0,.125); + border-top: 0; + border-right: 0; + border-left: 0; +} diff --git a/view/tpl/abook_edit.tpl b/view/tpl/abook_edit.tpl index 686b40a54..28631ed68 100644 --- a/view/tpl/abook_edit.tpl +++ b/view/tpl/abook_edit.tpl @@ -24,6 +24,7 @@ {{if $abook_prev || $abook_next}} <div class="btn-group"> <a href="connedit/{{$abook_prev}}{{if $section}}?f=§ion={{$section}}{{/if}}" class="btn btn-outline-secondary btn-sm{{if ! $abook_prev}} disabled{{/if}}" ><i class="fa fa-backward"></i></a> + {{if $sections}} <div class="btn-group" > <button class="btn btn-outline-secondary btn-sm{{if $is_pending}} disabled{{/if}}" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-bars"></i></button> <div class="dropdown-menu dropdown-menu-end" aria-labelledby="dLabel"> @@ -32,6 +33,7 @@ {{/foreach}} </div> </div> + {{/if}} <a href="connedit/{{$abook_next}}{{if $section}}?f=§ion={{$section}}{{/if}}" class="btn btn-outline-secondary btn-sm{{if ! $abook_next}} disabled{{/if}}" ><i class="fa fa-forward"></i></a> </div> {{/if}} @@ -40,6 +42,65 @@ <h2>{{$header}}</h2> </div> <div class="section-content-wrapper-np"> + <form id="abook-edit-form" action="connedit/{{$contact_id}}" method="post" > + + <input type="hidden" name="contact_id" value="{{$contact_id}}"> + <input type="hidden" name="section" value="{{$section}}"> + + <div class="section-content-wrapper"> + <a href="permcats/{{$permcat_value}}" class="float-end"><i class="fa fa-external-link"></i> {{$permcat_new}}</a> + {{include file="field_select.tpl" field=$permcat}} + <button type="button" class="btn btn-outline-secondary float-end" data-bs-toggle="modal" data-bs-target="#perms_modal">Permissions</button> + <button type="submit" name="done" value="{{$submit}}" class="btn btn-primary">{{$submit}}</button> + <div class="modal" id="perms_modal" tabindex="-1" aria-labelledby="perms_modal_label" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <div class="modal-title h3" id="perms_modal_label">Permissions Overview</div> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> + </div> + <div class="modal-body"> + + <table class="table table-hover table-sm"> + <thead> + <tr> + <th scope="col">Permission</th> + <th scope="col">{{$them}}</th> + <th scope="col">{{$me}}</th> + </tr> + </thead> + <tbody> + {{foreach $perms as $perm}} + <tr> + <td>{{$perm.1}}</td> + <td> + {{if $perm.2}} + <i class="fa fa-check text-success"></i> + {{else}} + <i class="fa fa-times text-danger"></i> + {{/if}} + </td> + <td> + {{if $perm.3}} + <i class="fa fa-check text-success"></i> + {{else}} + <i class="fa fa-times text-danger"></i> + {{/if}} + </td> + </tr> + {{/foreach}} + + </tbody> + </table> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> + </div> + </div> + </div> + </div> + </div> + {{if $notself}} {{foreach $tools as $tool}} {{if $tool.info}} @@ -73,33 +134,9 @@ </div> {{/if}} - <form id="abook-edit-form" action="connedit/{{$contact_id}}" method="post" > - - <input type="hidden" name="contact_id" value="{{$contact_id}}"> - <input type="hidden" name="section" value="{{$section}}"> - <div class="panel-group" id="contact-edit-tools" role="tablist" aria-multiselectable="true"> {{if $notself}} - {{if $is_pending}} - <div class="panel"> - <div class="section-subtitle-wrapper" role="tab" id="pending-tool"> - <h3> - <a data-bs-toggle="collapse" data-bs-parent="#contact-edit-tools" href="#pending-tool-collapse" aria-expanded="true" aria-controls="pending-tool-collapse"> - {{$pending_label}} - </a> - </h3> - </div> - <div id="pending-tool-collapse" class="panel-collapse collapse show" role="tabpanel" aria-labelledby="pending-tool"> - <div class="section-content-tools-wrapper"> - {{include file="field_checkbox.tpl" field=$unapproved}} - <div class="settings-submit-wrapper" > - <button type="submit" name="done" value="{{$submit}}" class="btn btn-primary">{{$submit}}</button> - </div> - </div> - </div> - </div> - {{/if}} {{if ! $is_pending}} <div id="template-form-vcard-org" class="mb-3 form-vcard-org"> <div class="mb-3 form-vcard-org"> @@ -438,85 +475,6 @@ <input type="hidden" name="{{$excl.0}}" value="{{$excl.2}}" /> {{/if}} - {{if $rating}} - <div class="panel"> - <div class="section-subtitle-wrapper" role="tab" id="rating-tool"> - <h3> - <a data-bs-toggle="collapse" data-bs-parent="#contact-edit-tools" href="#rating-tool-collapse" aria-expanded="true" aria-controls="rating-tool-collapse"> - {{$lbl_rating}} - </a> - </h3> - </div> - <div id="rating-tool-collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="rating-tool"> - <div class="section-content-tools-wrapper"> - <div class="section-content-warning-wrapper"> - {{$rating_info}} - </div> - <div class="mb-3"><strong>{{$lbl_rating_label}}</strong></div> - {{$rating}} - {{include file="field_textarea.tpl" field=$rating_text}} - <input id="contact-rating-mirror" type="hidden" name="rating" value="{{$rating_val}}" /> - <div class="settings-submit-wrapper" > - <button type="submit" name="done" value="{{$submit}}" class="btn btn-primary">{{$submit}}</button> - </div> - </div> - </div> - </div> - {{/if}} - - {{/if}} - - {{if ! $is_pending}} - <div class="panel"> - {{if $notself}} - <div class="section-subtitle-wrapper" role="tab" id="perms-tool"> - <h3> - <a data-bs-toggle="collapse" data-bs-parent="#contact-edit-tools" href="#perms-tool-collapse" aria-expanded="true" aria-controls="perms-tool-collapse"> - {{$permlbl}} - </a> - </h3> - </div> - {{/if}} - <div id="perms-tool-collapse" class="panel-collapse collapse{{if $self || $section === 'perms'}} show{{/if}}" role="tabpanel" aria-labelledby="perms-tool"> - <div class="section-content-tools-wrapper"> - <div class="section-content-warning-wrapper"> - {{if $notself}}{{$permnote}}{{/if}} - {{if $self}}{{$permnote_self}}{{/if}} - </div> - - {{if $permcat_enable}} - <a href="permcats" class="float-end"><i class="fa fa-plus"></i> {{$permcat_new}}</a> - {{include file="field_select.tpl" field=$permcat}} - {{/if}} - - <table id="perms-tool-table" class=mb-3> - <tr> - <td></td> - {{if $notself}} - <td class="abook-them">{{$them}}</td> - {{/if}} - <td colspan="2" class="abook-me">{{$me}}</td> - </tr> - {{foreach $perms as $prm}} - {{include file="field_acheckbox.tpl" field=$prm}} - {{/foreach}} - </table> - - {{if $self}} - <div> - <div class="section-content-info-wrapper"> - {{$autolbl}} - </div> - {{include file="field_checkbox.tpl" field=$autoperms}} - </div> - {{/if}} - - <div class="settings-submit-wrapper" > - <button type="submit" name="done" value="{{$submit}}" class="btn btn-primary">{{$submit}}</button> - </div> - </div> - </div> - </div> {{/if}} </div> </form> diff --git a/view/tpl/admin_site.tpl b/view/tpl/admin_site.tpl index 6882c3b8a..fa942baff 100644 --- a/view/tpl/admin_site.tpl +++ b/view/tpl/admin_site.tpl @@ -31,7 +31,7 @@ <h3>{{$registration}}</h3> {{include file="field_input.tpl" field=$register_text}} - {{include file="field_select_grouped.tpl" field=$role}} + {{include file="field_select.tpl" field=$role}} {{include file="field_select.tpl" field=$register_policy}} {{** include file="field_checkbox.tpl" field=$register_wo_email **}} {{include file="register_duty.tpl" field=$register_duty}} diff --git a/view/tpl/connection_template.tpl b/view/tpl/connection_template.tpl index e7ab559cb..5c1542111 100644 --- a/view/tpl/connection_template.tpl +++ b/view/tpl/connection_template.tpl @@ -1,23 +1,12 @@ <div id="contact-entry-wrapper-{{$contact.id}}"> <div class="section-subtitle-wrapper clearfix"> <div class="float-end"> - {{if $contact.approve && $contact.ignore}} - <form action="connedit/{{$contact.id}}" method="post" > - <button type="submit" class="btn btn-success btn-sm" name="pending" value="1" title="{{$contact.approve_hover}}"><i class="fa fa-check"></i> {{$contact.approve}}</button> - - <a href="connedit/{{$contact.id}}/ignore" class="btn btn-warning btn-sm" title="{{$contact.ignore_hover}}"><i class="fa fa-ban"></i> {{$contact.ignore}}</a> - - {{/if}} - {{if $contact.connect}} - <a href="{{$contact.follow}}" class="btn btn-success btn-sm" title="{{$contact.connect_hover}}"><i class="fa fa-plus"></i> {{$contact.connect}}</a> - {{/if}} - <a href="#" class="btn btn-danger btn-sm contact-delete-btn" title="{{$contact.delete_hover}}" onclick="dropItem('{{$contact.deletelink}}', '#contact-entry-wrapper-{{$contact.id}}'); return false;"><i class="fa fa-trash-o"></i> {{$contact.delete}}</a> - <a href="{{$contact.link}}" class="btn btn-outline-secondary btn-sm" title="{{$contact.edit_hover}}"><i class="fa fa-pencil"></i> {{$contact.edit}}</a> - {{if $contact.approve}} - </form> - {{/if}} + <span id="contact-role-{{$contact.id}}" class="badge rounded-pill bg-warning text-dark me-1" title="{{$role_label}}">{{$contact.role}}</span> + <button type="button" class="btn btn-outline-secondary btn-sm contact-edit" title="{{$contact.edit_hover}}" data-id="{{$contact.id}}"> + <i class="fa fa-pencil"></i> {{$contact.edit}} + </button> </div> - <h3>{{if $contact.public_forum}}<i class="fa fa-comments-o"></i> {{/if}}<a href="{{$contact.url}}" title="{{$contact.img_hover}}" >{{$contact.name}}</a>{{if $contact.phone}} <a class="btn btn-outline-secondary btn-sm" href="tel:{{$contact.phone}}" title="{{$contact.call}}"><i class="fa fa-phone connphone"></i></a>{{/if}}</h3> + <h3>{{if $contact.public_forum}}<i class="fa fa-comments-o" title="{{$group_label}}"></i> {{/if}}<a href="{{$contact.url}}" title="{{$contact.img_hover}}" >{{$contact.name}}</a>{{if $contact.phone}} <a class="btn btn-outline-secondary btn-sm" href="tel:{{$contact.phone}}" title="{{$contact.call}}"><i class="fa fa-phone connphone"></i></a>{{/if}}</h3> </div> <div class="section-content-tools-wrapper"> <div class="contact-photo-wrapper" > diff --git a/view/tpl/connections.tpl b/view/tpl/connections.tpl index 1719a6522..5fec38a84 100644 --- a/view/tpl/connections.tpl +++ b/view/tpl/connections.tpl @@ -1,7 +1,10 @@ <div class="generic-content-wrapper"> <div class="section-title-wrapper clearfix"> <div class="dropdown float-end"> - <button type="button" class="btn btn-primary btn-sm" onclick="openClose('contacts-search-form'); $('#contacts-search').focus();"> + <button type="button" class="btn btn-success btn-sm" onclick="openClose('contacts-follow-form'); closeMenu('contacts-search-form'); $('#contacts-follow').focus();"> + <i class="fa fa-plus"></i> Add + </button> + <button type="button" class="btn btn-primary btn-sm" onclick="openClose('contacts-search-form'); closeMenu('contacts-follow-form'); $('#contacts-search').focus();"> <i class="fa fa-search"></i> {{$label}} </button> <button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="{{$sort}}"> @@ -16,10 +19,23 @@ {{if $finding}}<h2>{{$finding}}</h2>{{else}}<h2>{{$header}}{{if $total}} ({{$total}}){{/if}}</h2>{{/if}} </div> <div id="contacts-search-form" class="section-content-tools-wrapper"> - <form action="{{$cmd}}" method="get" id="mimimi" name="contacts-search-form"> + <form action="{{$cmd}}" method="get" name="contacts-search-form"> <div class="input-group mb-3"> <input type="text" name="search" id="contacts-search" class="form-control" onfocus="this.select();" value="{{$search}}" placeholder="{{$desc}}" /> - <button id="contacts-search-submit" class="btn btn-outline-secondary" type="submit"><i class="fa fa-fw fa-search"></i></button> + <button id="contacts-search-submit" class="btn btn-primary" type="submit"><i class="fa fa-fw fa-search"></i></button> + </div> + </form> + </div> + <div id="contacts-follow-form" class="section-content-tools-wrapper"> + {{if $abook_usage_message}} + <div class="section-conten-info-wrapper"> + {{$abook_usage_message}} + </div> + {{/if}} + <form action="follow" method="post"> + <div class="input-group mb-3"> + <input class="form-control" id="contacts-follow" type="text" name="url" title="Examples: bob@example.com, https://example.com/barbara" placeholder="Enter channel address"> + <button class="btn btn-success" type="submit" name="submit" value="Connect" title="Connect"><i class="fa fa-fw fa-plus"></i></button> </div> </form> </div> @@ -34,3 +50,5 @@ <div id="page-spinner" class="spinner-wrapper"> <div class="spinner m"></div> </div> +{{include file="contact_edit_modal.tpl"}} + diff --git a/view/tpl/connstatus.tpl b/view/tpl/connstatus.tpl index 869345682..30f450820 100644 --- a/view/tpl/connstatus.tpl +++ b/view/tpl/connstatus.tpl @@ -1 +1 @@ -<i class="fa fa-comments oneway-overlay text-{{if $perminfo.connpermcount == 3}}success{{elseif $perminfo.connpermcount > 0}}warning{{else}}danger{{/if}}" title="{{$perminfo.connperms}}"></i> +<i class="fa fa-circle oneway-overlay text-{{if $perminfo.connpermcount == 3}}success{{elseif $perminfo.connpermcount > 0}}warning{{else}}danger{{/if}}" title="{{$perminfo.connperms}}"></i> diff --git a/view/tpl/contact_edit.tpl b/view/tpl/contact_edit.tpl new file mode 100644 index 000000000..3c4ed2ffe --- /dev/null +++ b/view/tpl/contact_edit.tpl @@ -0,0 +1,133 @@ +<form id="contact-edit-form" action="contactedit/{{$contact_id}}" method="post" > + <div id="contact-edit-tools" class="panel-group" role="tablist" > + <div class="panel"> + <div class="section-subtitle-wrapper" role="tab" id="roles-tool"> + <h3> + <a class="section" data-bs-toggle="collapse" data-bs-target="#roles-tool-collapse" href="#" aria-expanded="true" aria-controls="roles-tool-collapse" data-section="roles"> + Roles + </a> + </h3> + </div> + <div id="roles-tool-collapse" class="panel-collapse collapse{{if $section == 'roles'}} show{{/if}}" role="tabpanel" aria-labelledby="roles-tool" data-bs-parent="#contact-edit-tools"> + <div class="section-content-tools-wrapper"> + {{include file="field_select.tpl" field=$permcat}} + <button class="btn btn-outline-secondary btn-sm float-end sub_section{{if $sub_section == 'perms'}} sub_section_active{{/if}}" type="button" onclick="openClose('perms-table', 'table')" data-section="perms"> + Compare permissions + </button> + <a href="permcats/{{$permcat_value}}" class="btn btn-sm btn-outline-primary"> + <i class="fa fa-external-link"></i> {{$permcat_new}} + </a> + <table id="perms-table" class="table table-hover table-sm mt-3" style="display: {{if $sub_section == 'perms'}}table{{else}}none{{/if}};"> + <thead> + <tr class="w-100"> + <th scope="col">Permission</th> + <th scope="col">{{$them}}</th> + <th scope="col">{{$me}}</th> + </tr> + </thead> + <tbody> + {{foreach $perms as $perm}} + <tr> + <td>{{$perm.1}}</td> + <td> + {{if $perm.2}} + <i class="fa fa-check text-success"></i> + {{else}} + <i class="fa fa-times text-danger"></i> + {{/if}} + </td> + <td> + {{if $perm.3}} + <i class="fa fa-check text-success"></i> + {{else}} + <i class="fa fa-times text-danger"></i> + {{/if}} + </td> + </tr> + {{/foreach}} + + </tbody> + </table> + </div> + </div> + </div> + {{if $groups}} + <div class="panel"> + <div class="section-subtitle-wrapper" role="tab" id="group-tool"> + <h3> + <a class="section" data-bs-toggle="collapse" data-bs-target="#group-tool-collapse" href="#" aria-expanded="true" aria-controls="group-tool-collapse" data-section="group"> + Privacy groups + </a> + </h3> + </div> + <div id="group-tool-collapse" class="panel-collapse collapse{{if $section == 'group'}} show{{/if}}" role="tabpanel" aria-labelledby="group-tool" data-bs-parent="#contact-edit-tools"> + <div class="section-content-tools-wrapper clearfix"> + {{foreach $groups as $group}} + {{include file="field_checkbox.tpl" field=$group}} + {{/foreach}} + <a href="group/new" class="btn btn-sm btn-outline-primary"> + <i class="fa fa-external-link"></i> Privacy groups + </a> + </div> + </div> + </div> + {{/if}} + {{if $multiprofs}} + <div class="panel"> + <div class="section-subtitle-wrapper" role="tab" id="profile-tool"> + <h3> + <a class="section" data-bs-toggle="collapse" data-bs-target="#profile-tool-collapse" href="#" aria-expanded="true" aria-controls="profile-tool-collapse" data-section="profile"> + Profiles + </a> + </h3> + </div> + <div id="profile-tool-collapse" class="panel-collapse collapse{{if $section == 'profile'}} show{{/if}}" role="tabpanel" aria-labelledby="profile-tool" data-bs-parent="#contact-edit-tools"> + <div class="section-content-tools-wrapper"> + {{$profile_select}} + <a href="profiles" class="btn btn-sm btn-outline-primary"> + <i class="fa fa-external-link"></i> Profiles + </a> + </div> + </div> + </div> + {{/if}} + {{if $slide}} + <div class="panel"> + <div class="section-subtitle-wrapper" role="tab" id="affinity-tool"> + <h3> + <a class="section" data-bs-toggle="collapse" data-bs-target="#affinity-tool-collapse" href="#" aria-expanded="true" aria-controls="affinity-tool-collapse" data-section="affinity"> + Affinity + </a> + </h3> + </div> + <div id="affinity-tool-collapse" class="panel-collapse collapse{{if $section == 'affinity'}} show{{/if}}" role="tabpanel" aria-labelledby="affinity-tool" data-bs-parent="#contact-edit-tools"> + <div class="section-content-tools-wrapper"> + <div class="mb-2"><label>{{$lbl_slider}}</label></div> + {{$slide}} + <input id="contact-closeness-mirror" type="hidden" name="closeness" value="{{$close}}" /> + </div> + </div> + </div> + {{/if}} + {{if $connfilter}} + <div class="panel"> + <div class="section-subtitle-wrapper" role="tab" id="filter-tool"> + <h3> + <a class="section" data-bs-toggle="collapse" data-bs-target="#filter-tool-collapse" href="#" aria-expanded="true" aria-controls="filter-tool-collapse" data-section="filter"> + Content filter + </a> + </h3> + </div> + <div id="filter-tool-collapse" class="panel-collapse collapse{{if $section == 'filter'}} show{{/if}}" role="tabpanel" aria-labelledby="filter-tool" data-bs-parent="#contact-edit-tools"> + <div class="section-content-tools-wrapper"> + {{include file="field_textarea.tpl" field=$incl}} + {{include file="field_textarea.tpl" field=$excl}} + </div> + </div> + </div> + {{else}} + <input type="hidden" name="{{$incl.0}}" value="{{$incl.2}}" /> + <input type="hidden" name="{{$excl.0}}" value="{{$excl.2}}" /> + {{/if}} + </div> +</form> diff --git a/view/tpl/contact_edit_header.tpl b/view/tpl/contact_edit_header.tpl new file mode 100644 index 000000000..997d294bd --- /dev/null +++ b/view/tpl/contact_edit_header.tpl @@ -0,0 +1,9 @@ +<div class="float-start me-2"> + <a href="{{$href}}" title="{{$link_label}}" target="_blank"> + <img src="{{$img_src}}" class="rounded" style="width: 3rem; height: 3rem;" /> + </a> +</div> +<div class="m-1"> + <div class="text-truncate h3 m-0"><strong>{{if $is_group}}<i class="fa fa-comments-o" title="{{$group_label}}"></i> {{/if}}{{$name}}</strong></div> + <div class="text-truncate text-muted">{{$addr}}</div> +</div> diff --git a/view/tpl/contact_edit_modal.tpl b/view/tpl/contact_edit_modal.tpl new file mode 100644 index 000000000..be290c403 --- /dev/null +++ b/view/tpl/contact_edit_modal.tpl @@ -0,0 +1,152 @@ +<div id="edit-modal" class="modal" tabindex="-1"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <div id="edit-modal-title" class="modal-title w-75"> + <div class="placeholder-wave"> + <span class="placeholder placeholder-lg" style="width: 200px;"></span> + </div> + </div> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> + </div> + <div id="edit-modal-body" class="modal-body"> + <div class="placeholder-wave"> + <span class="placeholder placeholder-lg w-100 mb-4"></span> + <span class="placeholder placeholder-lg w-100 mb-4"></span> + <span class="placeholder placeholder-lg w-100 mb-4"></span> + </div> + </div> + <div class="modal-footer"> + <div id="edit-modal-tools" class="me-auto"></div> + <button id="contact-save" type="button" class="btn btn-primary"></button> + </div> + </div> + </div> +</div> +<script> + let poi; + let section = 'roles'; + let sub_section; + + + $('#edit-modal').on('hidden.bs.modal', function (e) { + if (window.location.hash) { + history.replaceState(null, '', 'connections'); + } + }) + + if (window.location.hash) { + poi = window.location.hash.substr(1); + init_contact_edit(poi); + } + + window.onhashchange = function() { + if (window.location.hash) { + poi = window.location.hash.substr(1); + init_contact_edit(poi); + } + }; + + + $(document).on('click', '.contact-edit', function (e) { + e.preventDefault(); + poi = this.dataset.id + init_contact_edit(poi); + }); + + $(document).on('click', '#contact-save', function () { + let form_data = $('#contact-edit-form').serialize() + '§ion=' + section + '&sub_section=' + sub_section; + + $.post('contactedit/' + poi, form_data, function(data) { + if (!data.success) { + $.jGrowl(data.message, {sticky: false, theme: 'notice', life: 10000}); + return; + } + activate(data); + $.jGrowl(data.message, {sticky: false, theme: ((data.success) ? 'info' : 'notice'), life: ((data.success) ? 3000 : 10000)}); + // $('#edit-modal').modal('hide'); + }); + + }); + + $(document).on('click', '.contact-tool', function (e) { + e.preventDefault(); + let cmd = this.dataset.cmd; + + $.get('contactedit/' + poi + '/' + cmd, function(data) { + $('#edit-modal-tools').html(data.tools); + $.jGrowl(data.message, {sticky: false, theme: ((data.success) ? 'info' : 'notice'), life: ((data.success) ? 3000 : 10000)}); + if (cmd === 'drop') { + if ($('#contact-entry-wrapper-' + poi).length) { + $('#contact-entry-wrapper-' + poi).fadeOut(); + } + $('#edit-modal').modal('hide'); + } + }); + }); + + $(document).on('click', '.section', function () { + section = this.dataset.section; + sub_section = ''; + }); + + $(document).on('click', '.sub_section', function () { + if ($(this).hasClass('sub_section_active')) { + $(this).removeClass('sub_section_active'); + sub_section = ''; + } + else { + $(this).addClass('sub_section_active'); + sub_section = this.dataset.section; + } + }); + + function init_contact_edit(poi) { + if (!poi) + return + $.get('contactedit/' + poi, function(data) { + if (!data.success) { + $.jGrowl(data.message, {sticky: false, theme: 'notice', life: 10000}); + return; + } + $('#edit-modal').modal('show'); + activate(data); + }); + } + + function activate(data) { + $('#edit-modal-title').css('filter', 'blur(0px)'); + $('#edit-modal-body').css('filter', 'blur(0px)'); + $('#contact-save').removeClass('disabled'); + $('#contact-tools').removeClass('disabled'); + + if (data.title) { + $('#edit-modal-title').html(data.title); + } + + if (data.body) { + $('#edit-modal-body').html(data.body); + } + + if (data.tools) { + $('#edit-modal-tools').html(data.tools); + } + + if (data.submit) { + $('#contact-save').html(data.submit); + } + + if (data.role && $('#contact-role-' + poi).length) { + $('#contact-role-' + poi).html(data.role); + } + + if (data.pending) { + $('#contact-save').removeClass('btn-primary'); + $('#contact-save').addClass('btn-success'); + } + else { + $('#contact-save').addClass('btn-primary'); + $('#contact-save').removeClass('btn-success'); + } + } +</script> diff --git a/view/tpl/contact_edit_tools.tpl b/view/tpl/contact_edit_tools.tpl new file mode 100644 index 000000000..565b9df86 --- /dev/null +++ b/view/tpl/contact_edit_tools.tpl @@ -0,0 +1,14 @@ +<button id="contact-tools" class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + <i class="fa fa-cog"></i> {{$tools_label}} +</button> +<div class="dropdown-menu"> + <a class="dropdown-item contact-tool" href="#" title="{{$tools.refresh.title}}" data-cmd="refresh">{{$tools.refresh.label}}</a> + <a class="dropdown-item contact-tool" href="#" title="{{$tools.rephoto.title}}" data-cmd="resetphoto">{{$tools.rephoto.label}}</a> + <div class="dropdown-divider"></div> + <a class="dropdown-item contact-tool" href="#" title="{{$tools.block.title}}" data-cmd="block">{{$tools.block.label}}</a> + <a class="dropdown-item contact-tool" href="#" title="{{$tools.ignore.title}}" data-cmd="ignore">{{$tools.ignore.label}}</a> + <a class="dropdown-item contact-tool" href="#" title="{{$tools.archive.title}}" data-cmd="archive">{{$tools.archive.label}}</a> + <a class="dropdown-item contact-tool" href="#" title="{{$tools.hide.title}}" data-cmd="hide">{{$tools.hide.label}}</a> + <div class="dropdown-divider"></div> + <a class="dropdown-item contact-tool" href="#" title="{{$tools.delete.title}}" data-cmd="drop">{{$tools.delete.label}}</a> +</div> diff --git a/view/tpl/conv_frame.tpl b/view/tpl/conv_frame.tpl index c28bb732a..4237c671b 100644 --- a/view/tpl/conv_frame.tpl +++ b/view/tpl/conv_frame.tpl @@ -18,3 +18,4 @@ </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div><!-- /.modal --> +{{include file="contact_edit_modal.tpl"}} diff --git a/view/tpl/conv_item.tpl b/view/tpl/conv_item.tpl index 7c3505410..0a07319c8 100644 --- a/view/tpl/conv_item.tpl +++ b/view/tpl/conv_item.tpl @@ -40,7 +40,7 @@ <i class="fa fa-caret-down wall-item-photo-caret cursor-pointer" data-bs-toggle="dropdown"></i> <div class="dropdown-menu"> {{foreach $item.thread_author_menu as $mitem}} - <a class="dropdown-item" {{if $mitem.href}}href="{{$mitem.href}}"{{/if}} {{if $mitem.action}}onclick="{{$mitem.action}}"{{/if}} {{if $mitem.title}}title="{{$mitem.title}}"{{/if}} >{{$mitem.title}}</a> + <a class="dropdown-item{{if $mitem.class}} {{$mitem.class}}{{/if}}" {{if $mitem.href}}href="{{$mitem.href}}"{{/if}} {{if $mitem.action}}onclick="{{$mitem.action}}"{{/if}} {{if $mitem.title}}title="{{$mitem.title}}"{{/if}}{{if $mitem.data}} {{$mitem.data}}{{/if}}>{{$mitem.title}}</a> {{/foreach}} </div> {{/if}} @@ -48,7 +48,7 @@ </div> {{if $item.lock}} <div class="wall-item-lock dropdown"> - <i class="fa {{if $item.locktype == 2}}fa-envelope-o{{else}}fa-lock{{/if}} lockview{{if $item.privacy_warning}} text-danger{{/if}}" data-bs-toggle="dropdown" title="{{$item.lock}}" onclick="lockview('item',{{$item.id}});" ></i> + <i class="fa {{if $item.locktype == 2}}fa-envelope-o{{else if $item.locktype == 1}}fa-lock{{else}}fa-unlock{{/if}} lockview{{if $item.privacy_warning}} text-danger{{/if}}" data-bs-toggle="dropdown" title="{{$item.lock}}" onclick="lockview('item',{{$item.id}});" ></i> <div id="panel-{{$item.id}}" class="dropdown-menu"></div> </div> {{/if}} diff --git a/view/tpl/conv_list.tpl b/view/tpl/conv_list.tpl index a20a6de8d..b4071ba16 100644 --- a/view/tpl/conv_list.tpl +++ b/view/tpl/conv_list.tpl @@ -43,7 +43,7 @@ </div> {{if $item.lock}} <div class="wall-item-lock dropdown"> - <i class="fa {{if $item.locktype == 2}}fa-envelope-o{{else}}fa-lock{{/if}} lockview" data-bs-toggle="dropdown" title="{{$item.lock}}" onclick="lockview('item',{{$item.id}});" ></i> + <i class="fa {{if $item.locktype == 2}}fa-envelope-o{{else if $item.locktype == 1}}fa-lock{{else}}fa-unlock{{/if}} lockview{{if $item.privacy_warning}} text-danger{{/if}}" data-bs-toggle="dropdown" title="{{$item.lock}}" onclick="lockview('item',{{$item.id}});" ></i> <div id="panel-{{$item.id}}" class="dropdown-menu"></div> </div> {{/if}} diff --git a/view/tpl/conversation.tpl b/view/tpl/conversation.tpl index 82c7be922..6729cf55c 100644 --- a/view/tpl/conversation.tpl +++ b/view/tpl/conversation.tpl @@ -9,10 +9,10 @@ <div id="collapsed-comments-{{$thread.id}}" class="collapsed-comments" style="display: none;"> {{/if}} {{if $item.comment_lastcollapsed}}</div>{{/if}} - + {{include file="{{$item.template}}"}} - - + + {{/foreach}} </div> {{/foreach}} diff --git a/view/tpl/field_acheckbox.tpl b/view/tpl/field_acheckbox.tpl index 4c8b62f36..44f65fefd 100644 --- a/view/tpl/field_acheckbox.tpl +++ b/view/tpl/field_acheckbox.tpl @@ -1,24 +1,31 @@ -<tr class="highlight"> +<tr> <td> - <label class="mainlabel" for='me_id_{{$field.0}}'>{{$field.1}}</label><br> - <span class='field_abook_help'>{{$field.6}}</span> + <label class="mainlabel" for="me_id_{{$field.0}}">{{$field.1}}</label> + <span class="field_abook_help">{{$field.6}}</span> </td> - {{if $notself}} - <td class="abook-them"> - {{if $field.2 == 1}}<i class="fa fa-check-square-o"></i>{{/if}} - {{if $field.2 == 0}}<i class="fa fa-square-o"></i>{{/if}} - </td> - {{/if}} - <td class="abook-me"> - {{if $self || !$field.5 }} - <input type="checkbox" name='{{$field.0}}' class='abook-edit-me' id='me_id_{{$field.0}}' value="{{$field.4}}" {{if $field.3}}checked="checked"{{/if}} /> - {{/if}} - {{if $notself && $field.5}} - <input type="hidden" name='{{$field.0}}' value="{{if $field.7}}1{{else}}0{{/if}}" /> - {{if $field.3}}<i class="fa fa-check-square-o"></i>{{else}}<i class="fa fa-square-o"></i>{{/if}} + <td> + {{if $field.5}} + <span class="text-nowrap text-danger"> + {{$inherited}} + {{if $field.7}} + <i class="fa fa-check-square-o"></i> + {{else}} + <i class="fa fa-square-o"></i> + {{/if}} + </span> {{/if}} </td> <td> - {{if $field.5}}<span class="permission-inherited">{{$inherited}}{{if $self}}{{if $field.7}} <i class="fa fa-check-square-o"></i>{{else}} <i class="fa fa-square-o"></i>{{/if}}{{/if}}</span>{{/if}} + {{if $is_system_role}} + {{if $field.3}} + <i class="fa fa-check-square-o"></i> + {{else}} + <i class="fa fa-square-o"></i> + {{/if}} + {{else}} + <input type="checkbox" name="{{$field.0}}" value="{{$field.4}}" {{if $field.3}}checked="checked"{{/if}} /> + {{/if}} + </td> + </tr> diff --git a/view/tpl/field_checkbox.tpl b/view/tpl/field_checkbox.tpl index a1ab42d5e..670bb7e29 100644 --- a/view/tpl/field_checkbox.tpl +++ b/view/tpl/field_checkbox.tpl @@ -1,6 +1,6 @@ <div id="{{$field.0}}_container" class="clearfix onoffswitch checkbox mb-3"> <label for="id_{{$field.0}}">{{$field.1}}{{if $field.6}}<sup class="required zuiqmid"> {{$field.6}}</sup>{{/if}}</label> - <div class="float-end"><input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="1" {{if $field.2}}checked="checked"{{/if}} {{if $field.5}}{{$field.5}}{{/if}} /><label class="switchlabel" for='id_{{$field.0}}'> <span class="onoffswitch-inner" data-on='{{if $field.4}}{{$field.4.1}}{{/if}}' data-off='{{if $field.4}}{{$field.4.0}}{{/if}}'></span><span class="onoffswitch-switch"></span></label></div> + <div class="float-end"><input type="checkbox" name="{{$field.0}}" id="id_{{$field.0}}" value="1" {{if $field.2}}checked="checked"{{/if}} {{if $field.5}}{{$field.5}}{{/if}} /><label class="switchlabel" for='id_{{$field.0}}'> <span class="onoffswitch-inner" data-on='{{if $field.4}}{{$field.4.1}}{{/if}}' data-off='{{if $field.4}}{{$field.4.0}}{{/if}}'></span><span class="onoffswitch-switch"></span></label></div> <small class="form-text text-muted">{{$field.3}}</small> </div> {{* diff --git a/view/tpl/group_edit.tpl b/view/tpl/group_edit.tpl index 5568179af..61203ab10 100644 --- a/view/tpl/group_edit.tpl +++ b/view/tpl/group_edit.tpl @@ -13,6 +13,8 @@ <input type='hidden' name='form_security_token' value='{{$form_security_token_edit}}'> {{include file="field_input.tpl" field=$gname}} {{include file="field_checkbox.tpl" field=$public}} + {{include file="field_checkbox.tpl" field=$is_default_acl}} + {{include file="field_checkbox.tpl" field=$is_default_group}} {{$pgrp_extras}} <a href="group/drop/{{$gid}}?t={{$form_security_token_drop}}" onclick="return confirmDelete();" class="btn btn-sm btn-danger"> {{$delete}} diff --git a/view/tpl/group_selection.tpl b/view/tpl/group_selection.tpl index a91658464..7db80e06f 100644 --- a/view/tpl/group_selection.tpl +++ b/view/tpl/group_selection.tpl @@ -1,8 +1,8 @@ <div class="mb-3 field custom"> -<label for="group-selection" id="group-selection-lbl">{{$label}}</label> -<select class="form-control" name="group-selection" id="group-selection" > -{{foreach $groups as $group}} -<option value="{{$group.id}}" {{if $group.selected}}selected="selected"{{/if}} >{{$group.name}}</option> -{{/foreach}} -</select> + <label for="id_{{$form_id}}">{{$label}}</label> + <select class="form-control" name="{{$form_id}}" id="{{$form_id}}" > + {{foreach $groups as $group}} + <option value="{{$group.id}}" {{if $group.selected}}selected="selected"{{/if}} >{{$group.name}}</option> + {{/foreach}} + </select> </div> diff --git a/view/tpl/group_side.tpl b/view/tpl/group_side.tpl index 14f714ae9..979d20aa5 100644 --- a/view/tpl/group_side.tpl +++ b/view/tpl/group_side.tpl @@ -13,9 +13,11 @@ <a class="nav-link{{if $group.selected}} active{{/if}}" href="{{$group.href}}">{{$group.text}}</a> </li> {{/foreach}} + {{if $createtext}} <li class="nav-item"> - <a class="nav-link" href="group/new" title="{{$createtext}}" ><i class="fa fa-plus-circle"></i> {{$createtext}}</a> + <a class="nav-link" href="group/new" title="{{$createtext}}" ><i class="fa fa-external-link"></i> {{$createtext}}</a> </li> + {{/if}} </ul> </div> diff --git a/view/tpl/micropro_card.tpl b/view/tpl/micropro_card.tpl index 058bfc14c..40a7967c5 100644 --- a/view/tpl/micropro_card.tpl +++ b/view/tpl/micropro_card.tpl @@ -1,5 +1,5 @@ <a class="list-group-item{{if $class}} {{$class}}{{/if}} fakelink" href="{{if $click}}#{{else}}{{$url}}{{/if}}" {{if $click}}onclick="{{$click}}"{{/if}}> - <img class="menu-img-3" src="{{$photo}}" title="{{$title}}" alt="" />{{if $perminfo}}{{include "connstatus.tpl"}}{{/if}} + <img class="menu-img-3" src="{{$photo}}" title="{{$title}}" alt="" loading="lazy" />{{if $perminfo}}{{include "connstatus.tpl"}}{{/if}} <span class="contactname">{{$name}}</span> <span class="dropdown-sub-text">{{$addr}}<br>{{$network}}</span> </a> diff --git a/view/tpl/new_channel.tpl b/view/tpl/new_channel.tpl index 8d72df55b..f11a4291f 100644 --- a/view/tpl/new_channel.tpl +++ b/view/tpl/new_channel.tpl @@ -18,17 +18,17 @@ {{if $default_role}} <input type="hidden" name="permissions_role" value="{{$default_role}}" /> {{else}} - {{include file="field_select_grouped.tpl" field=$role}} + {{include file="field_select.tpl" field=$role}} {{/if}} - + {{include file="field_input.tpl" field=$name}} - + {{include file="field_input.tpl" field=$nickname}} <button class="btn btn-primary" type="submit" id="newchannel-submit-button">{{$submit}}</button> - - + + <div id="newchannel-submit-end" class="clear"></div> - + <div id="newchannel-import-link" class="descriptive-paragraph" >{{$label_import}}</div> <div id="newchannel-import-end" class="clear"></div> </form> diff --git a/view/tpl/notes.tpl b/view/tpl/notes.tpl index 902691fae..24c3ec53c 100644 --- a/view/tpl/notes.tpl +++ b/view/tpl/notes.tpl @@ -8,7 +8,7 @@ {{else}} <div id="personal-notes" class="widget{{if $hidden}} d-none{{/if}}"> <div class="float-end rounded border border-secondary mb-1 ps-1 pe-1 text-muted small note-mode" title="Double click note for edit mode">{{$strings.read}}</div> - <h3 class="float-start">{{$banner}}</h3> + <h3 class="float-start">{{$strings.title}}</h3> {{/if}} <textarea name="note_text" id="note-text" class="form-control{{if $app}} border-0{{else}} p-1{{/if}}" style="display: none;">{{$text}}</textarea> <div id="note-text-html" class="{{if !$app}}border rounded p-1{{/if}}">{{$html}}</div> diff --git a/view/tpl/permcats.tpl b/view/tpl/permcats.tpl index 5b2d811d7..279903048 100644 --- a/view/tpl/permcats.tpl +++ b/view/tpl/permcats.tpl @@ -4,49 +4,37 @@ <div class="clear"></div> </div> <div class="section-content-tools-wrapper"> - <div class="section-content-info-wrapper"> - {{$desc}} - </div> - <form action="permcats" id="settings-permcats-form" method="post" autocomplete="off" > - <input type='hidden' name='form_security_token' value='{{$form_security_token}}'> - {{include file="field_input.tpl" field=$name}} + <input type="hidden" name="form_security_token" value="{{$form_security_token}}"> + <input type="hidden" name="return_path" value="{{$return_path}}"> - <div class="settings-submit-wrapper mb-3"> - <button type="submit" name="submit" class="btn btn-primary">{{$submit}}</button> - </div> - </div> + {{if $is_system_role}} + <input type="hidden" name="is_system_role" value="1"> + <input type="hidden" name="name" value="{{$is_system_role}}"> + {{/if}} - <div class="panel" id="permission-settings"> - <div class="section-subtitle-wrapper" role="tab" id="perms-tool"> - <h3> - <a data-bs-toggle="collapse" data-bs-parent="#permission-settings" href="#perms-tool-collapse" aria-expanded="true" aria-controls="perms-tool-collapse"> - {{$permlbl}} - </a> - </h3> - </div> - <div id="perms-tool-collapse" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="perms-tool"> - <div class="section-content-tools-wrapper"> - <div class="section-content-warning-wrapper"> - {{$permnote}} - </div> + {{include file="field_input.tpl" field=$name}} + {{include file="field_checkbox.tpl" field=$default_role}} - <table id="perms-tool-table" class=mb-3> - <tr> - <td></td><td colspan="2" class="abook-me">{{$me}}</td> - </tr> - {{foreach $perms as $prm}} - {{include file="field_acheckbox.tpl" field=$prm}} - {{/foreach}} - </table> + {{$group_select}} - <div class="settings-submit-wrapper" > - <button type="submit" name="submit" class="btn btn-primary">{{$submit}}</button> - </div> + <div class="section-subtitle-wrapper" id="perms-tool"> + <h3> + {{$permlbl}} + </h3> </div> - </div> - {{if $permcats}} - <div class="section-content-wrapper-np"> + <div class="section-content-warning-wrapper"> + {{$permnote}} + </div> + <table id="" class="table table-hover"> + {{foreach $perms as $prm}} + {{include file="field_acheckbox.tpl" field=$prm}} + {{/foreach}} + </table> + <div class="settings-submit-wrapper" > + <button type="submit" name="submit" class="btn btn-primary">{{$submit}}</button> + </div> + {{**if $permcats}} <table id="permcat-index"> {{foreach $permcats as $k => $v}} <tr class="permcat-row-{{$k}}"> @@ -55,10 +43,8 @@ </tr> {{/foreach}} </table> - </div> - {{/if}} + {{/if**}} + </form> </div> - </form> - </div> diff --git a/view/tpl/privacy_groups.tpl b/view/tpl/privacy_groups.tpl index c06f5b0ca..0bbc998a1 100644 --- a/view/tpl/privacy_groups.tpl +++ b/view/tpl/privacy_groups.tpl @@ -8,6 +8,8 @@ <input type='hidden' name='form_security_token' value='{{$form_security_token}}'> {{include file="field_input.tpl" field=$gname}} {{include file="field_checkbox.tpl" field=$public}} + {{include file="field_checkbox.tpl" field=$is_default_acl}} + {{include file="field_checkbox.tpl" field=$is_default_group}} {{$pgrp_extras}} <button type="submit" name="submit" class="btn btn-sm btn-primary float-end">{{$submit}}</button> </form> diff --git a/view/tpl/profile_edit.tpl b/view/tpl/profile_edit.tpl index 95346b3a8..3f5814c4a 100644 --- a/view/tpl/profile_edit.tpl +++ b/view/tpl/profile_edit.tpl @@ -50,7 +50,7 @@ <div class="section-subtitle-wrapper" role="tab" id="personal"> <h3> <a data-bs-toggle="collapse" data-bs-target="#personal-collapse" href="#" aria-expanded="true" aria-controls="personal-collapse"> - {{$personal}} + {{$basic}} </a> </h3> </div> @@ -84,7 +84,17 @@ {{$dob}} {{/if}} + {{$profile_in_dir}} + + {{$suggestme}} + + {{if $show_presence}} + {{include file="field_checkbox.tpl" field=$show_presence}} + {{/if}} + + {{if $hide_friends}} {{include file="field_checkbox.tpl" field=$hide_friends}} + {{/if}} <div class="mb-3" > <button type="submit" name="submit" class="btn btn-primary" value="{{$submit}}">{{$submit}}</button> diff --git a/view/tpl/profile_vcard.tpl b/view/tpl/profile_vcard.tpl index 8ebc9a311..9002d3742 100644 --- a/view/tpl/profile_vcard.tpl +++ b/view/tpl/profile_vcard.tpl @@ -1,5 +1,5 @@ -<div class="rounded mb-3 vcard-card h-card"> - <div class="card"> +<div class="rounded mb-3 vcard-card h-card"> + <div class="card mb-2"> <div class="row"> <div class="col-4" style="width: 7rem;"> {{if $editmenu}} @@ -49,10 +49,10 @@ {{if $details}} <div class="vcard ps-2 pe-2"> {{if $profile.pdesc}} - <div class="pb-3 pt-2">{{$profile.pdesc}}</div> + <div class="mb-2">{{$profile.pdesc}}</div> {{/if}} {{if $location}} - <dl class=""> + <dl class="mb-0 pb-1"> <dt class="location-label">{{$location}}</dt> <dd class="adr h-adr"> {{if $profile.address}} @@ -72,25 +72,25 @@ </dl> {{/if}} {{if $hometown}} - <dl class=""> + <dl class="mb-0 pb-1"> <dt class="hometown-label">{{$hometown}}</dt> <dd class="p-hometown">{{$profile.hometown}}</dd> </dl> {{/if}} {{if $gender}} - <dl class=""> + <dl class="mb-0 pb-1"> <dt class="gender-label">{{$gender}}</dt> <dd class="p-gender">{{if $profile.gender_icon}}<i class="fa fa-{{$profile.gender_icon}}"></i> {{/if}}{{$profile.gender}}</dd> </dl> {{/if}} {{if $marital}} - <dl class=""> + <dl class="mb-0 pb-1"> <dt class="marital-label"><span class="heart"><i class="fa fa-heart"></i> </span>{{$marital}}</dt> <dd class="marital-text">{{$profile.marital}}</dd> </dl> {{/if}} {{if $homepage}} - <dl class="pb-2"> + <dl class="mb-0 pb-1"> <dt class="homepage-label">{{$homepage}}</dt> <dd class="homepage-url u-url">{{$profile.homepage}}</dd> </dl> diff --git a/view/tpl/search_item.tpl b/view/tpl/search_item.tpl index ee89973a8..f3d0a8d03 100644 --- a/view/tpl/search_item.tpl +++ b/view/tpl/search_item.tpl @@ -36,7 +36,7 @@ </div> {{if $item.lock}} <div class="wall-item-lock dropdown"> - <i class="fa {{if $item.locktype == 2}}fa-envelope-o{{else}}fa-lock{{/if}} lockview" data-bs-toggle="dropdown" title="{{$item.lock}}" onclick="lockview('item',{{$item.id}});" ></i><ul id="panel-{{$item.id}}" class="lockview-panel dropdown-menu"></ul> + <i class="fa {{if $item.locktype == 2}}fa-envelope-o{{else if $item.locktype == 1}}fa-lock{{else}}fa-unlock{{/if}} lockview{{if $item.privacy_warning}} text-danger{{/if}}" data-bs-toggle="dropdown" title="{{$item.lock}}" onclick="lockview('item',{{$item.id}});" ></i> </div> {{/if}} <div class="wall-item-author"> diff --git a/view/tpl/settings.tpl b/view/tpl/settings.tpl index 353da9c2b..9b23d7d82 100644 --- a/view/tpl/settings.tpl +++ b/view/tpl/settings.tpl @@ -18,13 +18,14 @@ </div> <div id="basic-settings-collapse" class="collapse show" role="tabpanel" aria-labelledby="basic-settings" data-bs-parent="#settings"> <div class="section-content-tools-wrapper"> - {{include file="field_input.tpl" field=$username}} + {{include file="field_select.tpl" field=$role}} {{include file="field_select_grouped.tpl" field=$timezone}} {{include file="field_input.tpl" field=$defloc}} {{include file="field_checkbox.tpl" field=$allowloc}} {{include file="field_checkbox.tpl" field=$adult}} {{include file="field_input.tpl" field=$photo_path}} {{include file="field_input.tpl" field=$attach_path}} + {{include file="field_input.tpl" field=$expire}} {{if $basic_addon}} {{$basic_addon}} {{/if}} @@ -35,66 +36,6 @@ </div> </div> <div class="panel"> - <div class="section-subtitle-wrapper" role="tab" id="privacy-settings"> - <h3> - <a data-bs-toggle="collapse" data-bs-target="#privacy-settings-collapse" href="#"> - {{$h_prv}} - </a> - </h3> - </div> - <div id="privacy-settings-collapse" class="collapse" role="tabpanel" aria-labelledby="privacy-settings" data-bs-parent="#settings"> - <div class="section-content-tools-wrapper"> - {{include file="field_select_grouped.tpl" field=$role}} - {{$autoperms}} - <div id="advanced-perm" style="display:{{if $permissions_set}}none{{else}}block{{/if}};"> - <div class="mb-3"> - <button type="button" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#apsModal">{{$lbl_p2macro}}</button> - </div> - <div class="modal" id="apsModal"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <h4 class="modal-title">{{$lbl_p2macro}}</h4> - <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> - </div> - <div class="modal-body"> - {{foreach $permiss_arr as $permit}} - {{include file="field_select.tpl" field=$permit}} - {{/foreach}} - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button> - </div> - </div><!-- /.modal-content --> - </div><!-- /.modal-dialog --> - </div><!-- /.modal --> - - <div id="settings-default-perms" class="mb-3" > - <button type="button" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#aclModal"><i id="jot-perms-icon" class="fa"></i> {{$permissions}}</button> - </div> - {{$group_select}} - {{include file="field_checkbox.tpl" field=$hide_presence}} - {{$profile_in_dir}} - </div> - <div class="settings-common-perms"> - {{$suggestme}} - {{include file="field_checkbox.tpl" field=$blocktags}} - {{include file="field_input.tpl" field=$expire}} - </div> - {{if $permcat_enable}} - {{include file="field_select.tpl" field=$defpermcat}} - {{/if}} - - {{if $sec_addon}} - {{$sec_addon}} - {{/if}} - <div class="settings-submit-wrapper" > - <button type="submit" name="submit" class="btn btn-primary">{{$submit}}</button> - </div> - </div> - </div> - </div> - <div class="panel"> <div class="section-subtitle-wrapper" role="tab" id="notification-settings"> <h3> <a data-bs-toggle="collapse" data-bs-target="#notification-settings-collapse" href="#"> diff --git a/view/tpl/settings_privacy.tpl b/view/tpl/settings_privacy.tpl new file mode 100644 index 000000000..aff9619c3 --- /dev/null +++ b/view/tpl/settings_privacy.tpl @@ -0,0 +1,56 @@ +<div class="generic-content-wrapper"> + <div class="section-title-wrapper"> + <h2>{{$ptitle}}</h2> + </div> + {{$nickname_block}} + <form action="settings/privacy" id="settings-form" method="post" autocomplete="off"> + <input type='hidden' name='form_security_token' value='{{$form_security_token}}' /> + + <div class="section-content-tools-wrapper"> + + {{include file="field_checkbox.tpl" field=$autoperms}} + {{include file="field_checkbox.tpl" field=$index_opt_out}} + + {{if $sec_addon}} + {{$sec_addon}} + {{/if}} + {{if $permission_limits}} + <div id="permission-limits"> + <div class="modal" id="apsModal"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <div class="modal-title h3">{{$permission_limits_label}}</div> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> + </div> + <div class="modal-body"> + <div class="multi-collapse collapse show"> + <h2 class="text-danger mb-3"><i class="fa fa-warning"></i> {{$permission_limits_warning.0}}</h2> + <h3 class="mb-3">{{$permission_limits_warning.1}}</h3> + <button type="button" class="btn btn-primary" data-bs-toggle="collapse" data-bs-target=".multi-collapse" aria-expanded="false" aria-controls="collapseExample">{{$permission_limits_warning.2}}</button> + </div> + <div class="multi-collapse collapse"> + {{foreach $permiss_arr as $permit}} + {{include file="field_select.tpl" field=$permit}} + {{/foreach}} + {{include file="field_checkbox.tpl" field=$group_actor}} + </div> + + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button> + </div> + </div><!-- /.modal-content --> + </div><!-- /.modal-dialog --> + </div><!-- /.modal --> + </div> + <div class="float-end"> + <button type="button" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#apsModal">{{$permission_limits_label}}</button> + </div> + {{/if}} + <div class="settings-submit-wrapper" > + <button type="submit" name="submit" class="btn btn-primary">{{$submit}}</button> + </div> + </div> + </form> +</div> |