= 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 LIMIT 1", intval(local_channel()), intval(argv(1)) ); if ($r) { App::$poi = $r[0]; } } $channel = App::get_channel(); if ($channel) { head_set_icon($channel['xchan_photo_s']); } } /* @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(); $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); 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) ? 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']); 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', $_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']); if (!array_key_exists('closeness', $_POST)) { $_POST['closeness'] = 80; } $closeness = intval($_POST['closeness']); if ($closeness < 0 || $closeness > 99) { $closeness = 80; } $new_friend = ((intval($orig_record[0]['abook_pending'])) ? true : false); /* $perms = []; $permcats = new Permcat(local_channel()); $role_perms = $permcats->fetch($abook_role); $all_perms = Permissions::Perms(); // 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; } // 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; } 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); } } } */ \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' 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()) ); if ($r) info(t('Connection updated.') . EOL); else notice(t('Failed to update connection record.') . EOL); if (!intval(App::$poi['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(), '', App::$poi['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($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_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=' . App::$poi['xchan_url'] . ']' . App::$poi['xchan_name'] . '[/zrl]'; $xarr['body'] .= "\n\n\n" . '[zrl=' . App::$poi['xchan_url'] . '][zmg=80x80]' . App::$poi['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 $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($contact_id) ); if ($r) { App::$poi = $r[0]; } if ($new_friend) { $arr = ['channel_id' => local_channel(), 'abook' => App::$poi]; call_hooks('accept_follow', $arr); } $this->connedit_clone(); if (($_REQUEST['pending']) && (!$_REQUEST['done'])) goaway(z_root() . '/connections/ifpending'); return; } /* @brief Clone connection * * */ function connedit_clone() { if (!App::$poi) return; $channel = App::get_channel(); $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 = $r[0]; } $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]]); } /* @brief Generate content of connection edit page * * */ function get() { $o = ''; if (!local_channel()) { notice(t('Permission denied.') . EOL); return login(); } $section = ((array_key_exists('section', $_REQUEST)) ? $_REQUEST['section'] : ''); if (argc() == 3) { $contact_id = intval(argv(1)); if (!$contact_id) return; $cmd = argv(2); $orig_record = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_id = %d AND abook_channel = %d AND abook_self = 0 and xchan_deleted = 0 LIMIT 1", intval($contact_id), intval(local_channel()) ); if (!count($orig_record)) { notice(t('Could not access address book record.') . EOL); goaway(z_root() . '/connections'); } if ($cmd === 'update') { // pull feed and consume it, which should subscribe to the hub. 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'; $recurse = 0; $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 = Reader::read($x['body']); $vcard = $vc->serialize(); if ($vcard) { set_abconfig(local_channel(), $orig_record[0]['abook_xchan'], 'system', 'vcard', $vcard); $this->connedit_clone(); } } } } } } goaway(z_root() . '/connedit/' . $contact_id); } 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.')); } else { // if you are on a different network we'll force a refresh of the connection basic info 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(); } 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(); } 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(); } 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(); } else notice(t('Unable to set address book parameters.') . EOL); goaway(z_root() . '/connedit/' . $contact_id); } // 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(); } else notice(t('Unable to set address book parameters.') . EOL); } goaway(z_root() . '/connedit/' . $contact_id); } if ($cmd === 'drop') { contact_remove(local_channel(), $orig_record[0]['abook_id']); Master::Summon(['Notifier', 'purge', local_channel(), $orig_record[0]['xchan_hash']]); Libsync::build_sync_packet(0 /* use the current local_channel */, ['abook' => [[ 'abook_xchan' => $orig_record[0]['abook_xchan'], 'entry_deleted' => true]] ] ); 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) { $abook_prev = 0; $abook_next = 0; $contact_id = App::$poi['abook_id']; $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) { $pntotal = count($cn); 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) $abook_next = 0; else $abook_next = $cn[$x + 1]['abook_id']; } } } $tools = [ 'view' => [ 'label' => t('View Profile'), 'url' => chanlink_cid($contact['abook_id']), 'sel' => '', 'title' => sprintf(t('View %s\'s profile'), $contact['xchan_name']), ], 'refresh' => [ 'label' => t('Refresh Permissions'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/refresh', 'sel' => '', 'title' => t('Fetch updated permissions'), ], 'rephoto' => [ 'label' => t('Refresh Photo'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/resetphoto', 'sel' => '', 'title' => t('Fetch updated photo'), ], 'recent' => [ 'label' => t('Recent Activity'), 'url' => z_root() . '/network/?f=&cid=' . $contact['abook_id'], 'sel' => '', 'title' => t('View recent posts and comments'), ], '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!') : ''), ], '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!') : ''), ], '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!') : ''), ], '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!') : ''), ], 'delete' => [ 'label' => t('Delete'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/drop', 'sel' => '', 'title' => t('Delete this connection'), ], ]; if ($contact['xchan_network'] === 'zot6') { $tools['fetchvc'] = [ 'label' => t('Fetch Vcard'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/fetchvc', 'sel' => '', 'title' => t('Fetch electronic calling card for this connection') ]; } $sections = []; $vc = get_abconfig(local_channel(), $contact['abook_xchan'], 'system', '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')) { $sections['affinity'] = [ 'label' => t('Affinity'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/?f=§ion=affinity', 'sel' => '', 'title' => t('Open Set Affinity section by default'), ]; $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, ]); } if (feature_enabled(local_channel(), 'connfilter')) { $sections['filter'] = [ 'label' => t('Filter'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/?f=§ion=filter', 'sel' => '', 'title' => t('Open Custom Filter section by default'), ]; } $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); if ($slide && !$multiprofs) $affinity = t('Set Affinity'); if (!$slide && $multiprofs) $affinity = t('Set Profile'); 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']) ); $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']); 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 = ''; $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 .= '
' . t('Location independence is not supported by their network.'); } $o .= replace_macros($tpl, [ '$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' => ['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 privacy settings, which have higher priority than individual settings. You can not change those settings here.'), '$permnote_self' => t('Some permissions may be inherited from your channel\'s privacy settings, 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 = ['contact' => $contact, 'output' => $o]; call_hooks('contact_edit', $arr); return $arr['output']; } } }