diff options
Diffstat (limited to 'Zotlabs/Lib')
-rw-r--r-- | Zotlabs/Lib/AccessList.php | 240 | ||||
-rw-r--r-- | Zotlabs/Lib/Activity.php | 135 | ||||
-rw-r--r-- | Zotlabs/Lib/ActivityStreams.php | 2 | ||||
-rw-r--r-- | Zotlabs/Lib/Apps.php | 8 | ||||
-rw-r--r-- | Zotlabs/Lib/Connect.php | 5 | ||||
-rw-r--r-- | Zotlabs/Lib/Enotify.php | 4 | ||||
-rw-r--r-- | Zotlabs/Lib/Group.php | 405 | ||||
-rw-r--r-- | Zotlabs/Lib/Libsync.php | 83 | ||||
-rw-r--r-- | Zotlabs/Lib/Libzot.php | 43 | ||||
-rw-r--r-- | Zotlabs/Lib/NativeWiki.php | 48 | ||||
-rw-r--r-- | Zotlabs/Lib/NativeWikiPage.php | 579 | ||||
-rw-r--r-- | Zotlabs/Lib/PConfig.php | 85 | ||||
-rw-r--r-- | Zotlabs/Lib/Permcat.php | 130 | ||||
-rw-r--r-- | Zotlabs/Lib/ThreadItem.php | 32 | ||||
-rw-r--r-- | Zotlabs/Lib/ZotURL.php | 2 |
15 files changed, 840 insertions, 961 deletions
diff --git a/Zotlabs/Lib/AccessList.php b/Zotlabs/Lib/AccessList.php index 3c008f8c7..03052fab5 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 privacy group with this name was revived. Existing item permissions <strong>may</strong> apply to this privacy group and any future members. If this is not what you intended, please create another privacy group 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,124 @@ 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 + )); - $o = ''; + return $o; + } + +/* deprecated + 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('Privacy Groups'), + '$edittext' => t('Edit group'), + '$createtext' => t('Create new group'), + '$ungrouped' => (($every === 'contacts') ? t('Channels not in any privacy group') : ''), + '$groups' => $groups, + '$add' => t('Add'), ]); } - +*/ static function expand($g) { - if (! (is_array($g) && count($g))) { + if (!(is_array($g) && count($g))) { return []; } @@ -350,8 +368,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 +384,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 +401,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 +410,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 +422,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 96b747c30..cacf4d979 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) { @@ -1115,7 +1114,33 @@ class Activity { 'height' => 300, 'width' => 300, ]; - $ret['url'] = $p['xchan_url']; + +/* This could be used to distinguish actors by protocol instead of tags, + * array urls are not supported by some AP projects (pixelfed) though. + * + $ret['url'] = [ + [ + 'type' => 'Link', + 'rel' => 'alternate', + 'mediaType' => 'application/x-zot+json', + 'href' => $p['xchan_url'] + ], + [ + 'type' => 'Link', + 'rel' => 'alternate', + 'mediaType' => 'application/activity+json', + 'href' => $p['xchan_url'] + ], + [ + 'type' => 'Link', + 'rel' => 'alternate', // 'me'? + 'mediaType' => 'text/html', + 'href' => $p['xchan_url'] + ] + ]; +*/ + + $ret['url'] = $p['xchan_url']; $ret['publicKey'] = [ 'id' => $p['xchan_url'], @@ -1124,6 +1149,12 @@ class Activity { ]; if ($c) { + $ret['tag'][] = [ + 'type' => 'PropertyValue', + 'name' => 'Protocol', + 'value' => 'zot6' + ]; + $ret['outbox'] = z_root() . '/outbox/' . $c['channel_address']; } @@ -1488,7 +1519,7 @@ class Activity { 'type' => NOTIFY_INTRO, 'from_xchan' => $ret['xchan_hash'], 'to_xchan' => $channel['channel_hash'], - 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'], + 'link' => z_root() . '/connections#' . $new_connection[0]['abook_id'], ] ); @@ -1522,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']); } @@ -1625,6 +1656,19 @@ class Activity { $name = t('Unknown'); } + $webfinger_addr = ''; + + $m = parse_url($url); + if ($m) { + $hostname = $m['host']; + $baseurl = $m['scheme'] . '://' . $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); + $site_url = $m['scheme'] . '://' . $m['host']; + } + + if (!empty($person_obj['preferredUsername']) && isset($parsed_url['host'])) { + $webfinger_addr = escape_tags($person_obj['preferredUsername']) . '@' . $hostname; + } + $icon = z_root() . '/' . get_default_profile_photo(300); if ($person_obj['icon']) { if (is_array($person_obj['icon'])) { @@ -1659,7 +1703,7 @@ class Activity { if ($links) { foreach ($links as $link) { - if (array_key_exists('mediaType', $link) && $link['mediaType'] === 'text/html') { + if (is_array($link) && array_key_exists('mediaType', $link) && $link['mediaType'] === 'text/html') { $profile = $link['href']; } } @@ -1684,13 +1728,6 @@ class Activity { } } - $m = parse_url($url); - if ($m) { - $hostname = $m['host']; - $baseurl = $m['scheme'] . '://' . $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); - $site_url = $m['scheme'] . '://' . $m['host']; - } - $r = q("select * from xchan join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s'", dbesc($url) ); @@ -1709,17 +1746,19 @@ class Activity { ); // update existing xchan record - q("update xchan set xchan_name = '%s', xchan_guid = '%s', xchan_pubkey = '%s', xchan_network = 'activitypub', xchan_name_date = '%s' where xchan_hash = '%s'", + q("update xchan set xchan_name = '%s', xchan_guid = '%s', xchan_pubkey = '%s', xchan_addr = '%s', xchan_network = 'activitypub', xchan_name_date = '%s' where xchan_hash = '%s'", dbesc(escape_tags($name)), dbesc(escape_tags($url)), dbesc(escape_tags($pubkey)), + dbesc(escape_tags($webfinger_addr)), dbescdate(datetime_convert()), dbesc($url) ); // update existing hubloc record - q("update hubloc set hubloc_guid = '%s', hubloc_network = 'activitypub', hubloc_url = '%s', hubloc_host = '%s', hubloc_callback = '%s', hubloc_updated = '%s', hubloc_id_url = '%s' where hubloc_hash = '%s'", + q("update hubloc set hubloc_guid = '%s', hubloc_addr = '%s', hubloc_network = 'activitypub', hubloc_url = '%s', hubloc_host = '%s', hubloc_callback = '%s', hubloc_updated = '%s', hubloc_id_url = '%s' where hubloc_hash = '%s'", dbesc(escape_tags($url)), + dbesc(escape_tags($webfinger_addr)), dbesc(escape_tags($baseurl)), dbesc(escape_tags($hostname)), dbesc(escape_tags($inbox)), @@ -1736,7 +1775,7 @@ class Activity { 'xchan_hash' => escape_tags($url), 'xchan_guid' => escape_tags($url), 'xchan_pubkey' => escape_tags($pubkey), - 'xchan_addr' => '', + 'xchan_addr' => $webfinger_addr, 'xchan_url' => escape_tags($profile), 'xchan_name' => escape_tags($name), 'xchan_name_date' => datetime_convert(), @@ -1748,7 +1787,7 @@ class Activity { [ 'hubloc_guid' => escape_tags($url), 'hubloc_hash' => escape_tags($url), - 'hubloc_addr' => '', + 'hubloc_addr' => $webfinger_addr, 'hubloc_network' => 'activitypub', 'hubloc_url' => escape_tags($baseurl), 'hubloc_host' => escape_tags($hostname), @@ -1760,6 +1799,19 @@ class Activity { ); } + // We store all ActivityPub actors we can resolve. Some of them may be able to communicate over Zot6. Find them. + // Adding zot discovery urls to the actor record will cause federation to fail with the 20-30 projects which don't accept arrays in the url field. + + $actor_protocols = self::get_actor_protocols($person_obj); + if (in_array('zot6', $actor_protocols)) { + $zx = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6'", + dbesc($url) + ); + if (!$zx && $webfinger_addr) { + Master::Summon(['Gprobe', bin2hex($webfinger_addr)]); + } + } + $photos = import_xchan_photo($icon, $url); q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", dbescdate(datetime_convert('UTC', 'UTC', $photos[5])), @@ -2063,6 +2115,7 @@ class Activity { } static function update_poll($item, $post) { + $multi = false; $mid = $post['mid']; $content = $post['title']; @@ -2147,7 +2200,8 @@ class Activity { dbesc(datetime_convert()), intval($item['id']) ); - Master::Summon(['Notifier', 'wall-new', $item['id']]); + + Master::Summon(['Notifier', 'wall-new', $item['id'], $post['mid'] /* trick queueworker de-duplication */ ]); return true; } @@ -2639,6 +2693,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*/) { @@ -3633,4 +3698,24 @@ class Activity { return $ret; } + + + static function get_actor_protocols($actor) { + $ret = []; + + if (!array_key_exists('tag', $actor) || empty($actor['tag']) || !is_array($actor['tag'])) { + return $ret; + } + + foreach ($tag as $t) { + if ((isset($t['type']) && $t['type'] === 'PropertyValue') && + (isset($t['name']) && $t['name'] === 'Protocol') && + (isset($t['value']) && in_array($t['value'], ['zot6', 'activitypub', 'diaspora'])) + ) { + $ret[] = $t['value']; + } + } + + return $ret; + } } diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php index fa38c569e..09e1679ac 100644 --- a/Zotlabs/Lib/ActivityStreams.php +++ b/Zotlabs/Lib/ActivityStreams.php @@ -285,7 +285,7 @@ class ActivityStreams { if (!$s) { return false; } - return (in_array($s, ['Like', 'Dislike', 'Flag', 'Block', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact'])); + return (in_array($s, ['Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact'])); } /** diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php index c4ddcff1b..6ce052b06 100644 --- a/Zotlabs/Lib/Apps.php +++ b/Zotlabs/Lib/Apps.php @@ -67,17 +67,15 @@ class Apps { static public function get_base_apps() { $x = get_config('system','base_apps',[ 'Connections', + 'Contact Roles', 'Network', - 'Settings', 'Files', 'Channel Home', - 'View Profile', 'Photos', 'Calendar', 'Directory', 'Search', 'Help', - 'Profile Photo', 'HQ', 'Post' ]); @@ -377,10 +375,10 @@ class Apps { 'OAuth Apps Manager' => t('OAuth Apps Manager'), 'OAuth2 Apps Manager' => t('OAuth2 Apps Manager'), 'PDL Editor' => t('PDL Editor'), - 'Permission Categories' => t('Permission Categories'), + 'Contact Roles' => t('Contact Roles'), 'Public Stream' => t('Public Stream'), 'My Chatrooms' => t('My Chatrooms'), - 'Channel Export' => t('Channel Export'), + 'Channel Export' => t('Channel Export') ); if(array_key_exists('name',$arr)) { 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 d02dab739..2e483cb92 100644 --- a/Zotlabs/Lib/Enotify.php +++ b/Zotlabs/Lib/Enotify.php @@ -256,7 +256,7 @@ class Enotify { $itemlink = $params['link']; - if (array_key_exists('item',$params) && (! activity_match($params['item']['verb'],ACTIVITY_LIKE))) { + if (array_key_exists('item',$params) && activity_match($params['item']['verb'],ACTIVITY_LIKE)) { if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) { logger('notification: not a visible activity. Ignoring.'); pop_lang(); @@ -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/Libsync.php b/Zotlabs/Lib/Libsync.php index c4f1b20ea..36a0a044c 100644 --- a/Zotlabs/Lib/Libsync.php +++ b/Zotlabs/Lib/Libsync.php @@ -230,8 +230,35 @@ class Libsync { if (array_key_exists('config', $arr) && is_array($arr['config']) && count($arr['config'])) { foreach ($arr['config'] as $cat => $k) { - foreach ($arr['config'][$cat] as $k => $v) - set_pconfig($channel['channel_id'], $cat, $k, $v); + $pconfig_updated = []; + + foreach($arr['config'][$cat] as $k => $v) { + if ($cat === 'hz_delpconfig' && strpos($k, 'b64.') === 0) { + $delpconfig = explode(':', unpack_link_id($k)); + + // delete the provided pconfig + del_pconfig($channel['channel_id'], $delpconfig[0], $delpconfig[1], $v); + + // delete the messenger pconfig + del_pconfig($channel['channel_id'], 'hz_delpconfig', $k); + } + + if (strpos($k,'pcfgud:') === 0) { + $realk = substr($k,7); + $pconfig_updated[$realk] = $v; + unset($arr['config'][$cat][$k]); + } + } + + foreach($arr['config'][$cat] as $k => $v) { + if (!isset($pconfig_updated[$k])) { + $pconfig_updated[$k] = NULL; + } + + if ($cat !== 'hz_delpconfig') { + set_pconfig($channel['channel_id'],$cat,$k,$v,$pconfig_updated[$k]); + } + } } } @@ -384,19 +411,42 @@ class Libsync { // This relies on the undocumented behaviour that red sites send xchan info with the abook // and import_author_xchan will look them up on all federated networks - if ($abook['abook_xchan'] && $abook['xchan_addr']) { + $found = false; + if ($abook['abook_xchan'] && $abook['xchan_addr'] && (! in_array($abook['xchan_network'], [ 'token', 'unknown' ]))) { $h = Libzot::get_hublocs($abook['abook_xchan']); - if (!$h) { + if ($h) { + $found = true; + } + else { $xhash = import_author_xchan(encode_item_xchan($abook)); - if (!$xhash) { + if ($xhash) { + $found = true; + } + else { logger('Import of ' . $abook['xchan_addr'] . ' failed.'); - continue; } } } + if (!$found && !in_array($abook['xchan_network'], ['zot6', 'activitypub', 'diaspora'])) { + // just import the record. + $xc = []; + foreach ($abook as $k => $v) { + if (strpos($k,'xchan_') === 0) { + $xc[$k] = $v; + } + } + $r = q("select * from xchan where xchan_hash = '%s'", + dbesc($xc['xchan_hash']) + ); + if (! $r) { + xchan_store_lowlevel($xc); + } + } + + foreach ($abook as $k => $v) { - if (in_array($k, $disallowed) || (strpos($k, 'abook') !== 0)) { + if (in_array($k, $disallowed) || (strpos($k, 'abook_') !== 0)) { continue; } if (!in_array($k, $fields)) { @@ -410,6 +460,13 @@ class Libsync { if (array_key_exists('abook_instance', $clean) && $clean['abook_instance'] && strpos($clean['abook_instance'], z_root()) === false) { $clean['abook_not_here'] = 1; + + // guest pass or access token - don't try to probe since it is one-way + // we are relying on the undocumented behaviour that the abook record also contains the xchan + if ($abook['xchan_network'] === 'token') { + $clean['abook_instance'] .= ','; + $clean['abook_instance'] .= z_root(); + } } @@ -768,14 +825,13 @@ class Libsync { // match as many fields as possible in case anything at all changed. - $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_id_url = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_site_id = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ", + $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_id_url = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ", dbesc($sender['hash']), dbesc($sender['id']), dbesc($sender['id_sig']), dbesc($location['id_url']), dbesc($location['url']), dbesc($location['url_sig']), - dbesc($location['site_id']), dbesc($location['host']), dbesc($location['address']), dbesc($location['callback']), @@ -784,6 +840,15 @@ class Libsync { if ($r) { logger('Hub exists: ' . $location['url'], LOGGER_DEBUG); + // generate a new hubloc_site_id if it's wrong due to historical bugs 2021-11-30 + + if ($r[0]['hubloc_site_id'] !== $location['site_id']) { + q("update hubloc set hubloc_site_id = '%s' where hubloc_id = %d", + dbesc(Libzot::make_xchan_hash($location['url'], $location['sitekey'])), + intval($r[0]['hubloc_id']) + ); + } + // update connection timestamp if this is the site we're talking to // This only happens when called from import_xchan 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/NativeWiki.php b/Zotlabs/Lib/NativeWiki.php index 9e6a3ac85..bf4ac8e87 100644 --- a/Zotlabs/Lib/NativeWiki.php +++ b/Zotlabs/Lib/NativeWiki.php @@ -12,8 +12,8 @@ class NativeWiki { public static function listwikis($channel, $observer_hash) { $sql_extra = item_permissions_sql($channel['channel_id'], $observer_hash); - $wikis = q("SELECT * FROM item - WHERE resource_type = '%s' AND mid = parent_mid AND uid = %d AND item_deleted = 0 $sql_extra", + $wikis = q("SELECT * FROM item + WHERE resource_type = '%s' AND mid = parent_mid AND uid = %d AND item_deleted = 0 $sql_extra", dbesc(NWIKI_ITEM_RESOURCE_TYPE), intval($channel['channel_id']) ); @@ -49,7 +49,7 @@ class NativeWiki { $mid = z_root() . '/item/' . $uuid; $arr = array(); // Initialize the array of parameters for the post - $item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0); + $item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0); $wiki_url = z_root() . '/wiki/' . $channel['channel_address'] . '/' . $wiki['urlName']; $arr['aid'] = $channel['channel_account_id']; $arr['uuid'] = $uuid; @@ -61,8 +61,8 @@ class NativeWiki { $arr['resource_id'] = $resource_id; $arr['owner_xchan'] = $channel['channel_hash']; $arr['author_xchan'] = $observer_hash; - $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); - $arr['llink'] = $arr['plink']; + $arr['plink'] = $mid; + $arr['llink'] = z_root() . '/display/' . gen_link_id($mid); $arr['title'] = $wiki['htmlName']; // name of new wiki; $arr['allow_cid'] = $ac['allow_cid']; $arr['allow_gid'] = $ac['allow_gid']; @@ -133,13 +133,13 @@ class NativeWiki { // update acl for any existing wiki pages q("update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d where resource_type = 'nwikipage' and resource_id = '%s'", - dbesc($item['allow_cid']), - dbesc($item['allow_gid']), - dbesc($item['deny_cid']), - dbesc($item['deny_gid']), - dbesc($item['item_private']), + dbesc($item['allow_cid']), + dbesc($item['allow_gid']), + dbesc($item['deny_cid']), + dbesc($item['deny_gid']), + dbesc($item['item_private']), dbesc($arr['resource_id']) - ); + ); if($update['item_id']) { @@ -211,12 +211,12 @@ class NativeWiki { public static function get_wiki($channel_id, $observer_hash, $resource_id) { - + $sql_extra = item_permissions_sql($channel_id,$observer_hash); - $item = q("SELECT * FROM item WHERE uid = %d AND resource_type = '%s' AND resource_id = '%s' AND item_deleted = 0 + $item = q("SELECT * FROM item WHERE uid = %d AND resource_type = '%s' AND resource_id = '%s' AND item_deleted = 0 $sql_extra ORDER BY id LIMIT 1", - intval($channel_id), + intval($channel_id), dbesc(NWIKI_ITEM_RESOURCE_TYPE), dbesc($resource_id) ); @@ -224,7 +224,7 @@ class NativeWiki { return [ 'wiki' => null ]; } else { - + $w = $item[0]; // wiki item table record // Get wiki metadata $rawName = get_iconfig($w, 'wiki', 'rawName'); @@ -246,20 +246,20 @@ class NativeWiki { public static function exists_by_name($uid, $urlName) { - $sql_extra = item_permissions_sql($uid); + $sql_extra = item_permissions_sql($uid); - $item = q("SELECT item.id, resource_id FROM item left join iconfig on iconfig.iid = item.id - WHERE resource_type = '%s' AND iconfig.v = '%s' AND uid = %d - AND item_deleted = 0 $sql_extra limit 1", - dbesc(NWIKI_ITEM_RESOURCE_TYPE), - //dbesc(urldecode($urlName)), + $item = q("SELECT item.id, resource_id FROM item left join iconfig on iconfig.iid = item.id + WHERE resource_type = '%s' AND iconfig.v = '%s' AND uid = %d + AND item_deleted = 0 $sql_extra limit 1", + dbesc(NWIKI_ITEM_RESOURCE_TYPE), + //dbesc(urldecode($urlName)), dbesc(self::name_decode($urlName)), intval($uid) ); if($item) { return array('id' => $item[0]['id'], 'resource_id' => $item[0]['resource_id']); - } + } else { return array('id' => null, 'resource_id' => null); } @@ -277,7 +277,7 @@ class NativeWiki { $r = q("SELECT * FROM item WHERE uid = %d and resource_type = '%s' AND resource_id = '%s' $sql_extra LIMIT 1", intval($owner_id), - dbesc(NWIKI_ITEM_RESOURCE_TYPE), + dbesc(NWIKI_ITEM_RESOURCE_TYPE), dbesc($resource_id) ); @@ -285,8 +285,6 @@ class NativeWiki { return array('read' => false, 'write' => false, 'success' => true); } else { - // TODO: Create a new permission setting for wiki analogous to webpages. Until - // then, use webpage permissions $write = perm_is_allowed($owner_id, $observer_hash,'write_wiki'); return array('read' => true, 'write' => $write, 'success' => true); } diff --git a/Zotlabs/Lib/NativeWikiPage.php b/Zotlabs/Lib/NativeWikiPage.php index 3c61ea800..1e944f7ac 100644 --- a/Zotlabs/Lib/NativeWikiPage.php +++ b/Zotlabs/Lib/NativeWikiPage.php @@ -2,14 +2,15 @@ namespace Zotlabs\Lib; -use \Zotlabs\Lib as Zlib; +use App; +use Zotlabs\Access\PermissionLimits; class NativeWikiPage { - static public function page_list($channel_id,$observer_hash, $resource_id) { + static public function page_list($channel_id, $observer_hash, $resource_id) { // TODO: Create item table records for pages so that metadata like title can be applied - $w = Zlib\NativeWiki::get_wiki($channel_id,$observer_hash,$resource_id); + $w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); $pages[] = [ 'resource_id' => '', @@ -18,134 +19,149 @@ class NativeWikiPage { 'link_id' => 'id_wiki_home_0' ]; - $sql_extra = item_permissions_sql($channel_id,$observer_hash); + $sql_extra = item_permissions_sql($channel_id, $observer_hash); - $r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and item_deleted = 0 + $r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and item_deleted = 0 $sql_extra order by title asc", dbesc($resource_id), intval($channel_id) ); - if($r) { + if ($r) { $x = []; $y = []; - foreach($r as $rv) { - if(! in_array($rv['mid'],$x)) { + foreach ($r as $rv) { + if (!in_array($rv['mid'], $x)) { $y[] = $rv; $x[] = $rv['mid']; } } - $items = fetch_post_tags($y,true); + $items = fetch_post_tags($y, true); - foreach($items as $page_item) { - $title = get_iconfig($page_item['id'],'nwikipage','pagetitle',t('(No Title)')); - if(urldecode($title) !== 'Home') { + foreach ($items as $page_item) { + $title = get_iconfig($page_item['id'], 'nwikipage', 'pagetitle', t('(No Title)')); + if (urldecode($title) !== 'Home') { $pages[] = [ 'resource_id' => $resource_id, 'title' => escape_tags($title), //'url' => str_replace('%2F','/',urlencode(str_replace('%2F','/',urlencode($title)))), - 'url' => Zlib\NativeWiki::name_encode($title), + 'url' => NativeWiki::name_encode($title), 'link_id' => 'id_' . substr($resource_id, 0, 10) . '_' . $page_item['id'] ]; } } } - return array('pages' => $pages, 'wiki' => $w); + return ['pages' => $pages, 'wiki' => $w]; } - static public function create_page($channel_id, $observer_hash, $name, $resource_id, $mimetype = 'text/bbcode') { + static public function create_page($channel, $observer_hash, $name, $resource_id, $mimetype = 'text/bbcode') { logger('mimetype: ' . $mimetype); - if(! in_array($mimetype,[ 'text/markdown','text/bbcode','text/plain','text/html' ])) + if (!in_array($mimetype, ['text/markdown', 'text/bbcode', 'text/plain', 'text/html'])) $mimetype = 'text/markdown'; - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + $w = NativeWiki::get_wiki($channel['channel_id'], $observer_hash, $resource_id); - if (! $w['wiki']) { - return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); + if (!$w['wiki']) { + return ['content' => null, 'message' => 'Error reading wiki', 'success' => false]; } // backslashes won't work well in the javascript functions - $name = str_replace('\\','',$name); - - // create an empty activity + $name = str_replace('\\', '', $name); - $arr = []; - $arr['uid'] = $channel_id; - $arr['author_xchan'] = $observer_hash; - $arr['mimetype'] = $mimetype; - $arr['title'] = $name; - $arr['resource_type'] = 'nwikipage'; - $arr['resource_id'] = $resource_id; - $arr['allow_cid'] = $w['wiki']['allow_cid']; - $arr['allow_gid'] = $w['wiki']['allow_gid']; - $arr['deny_cid'] = $w['wiki']['deny_cid']; - $arr['deny_gid'] = $w['wiki']['deny_gid']; + $uuid = new_uuid(); + $mid = z_root() . '/item/' . $uuid; - $arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel_id,'view_wiki'),true); + // create an empty activity + $arr = []; + $arr['aid'] = $channel['channel_account_id']; + $arr['uid'] = $channel['channel_id']; + $arr['mid'] = $mid; + $arr['parent_mid'] = $w['wiki']['mid']; + $arr['parent'] = $w['wiki']['parent']; + $arr['uuid'] = $uuid; + $arr['item_hidden'] = $w['wiki']['item_hidden']; + $arr['plink'] = $mid; + $arr['llink'] = z_root() . '/display/' . gen_link_id($mid); + $arr['author_xchan'] = $observer_hash; + $arr['mimetype'] = $mimetype; + $arr['title'] = $name; + $arr['resource_type'] = 'nwikipage'; + $arr['resource_id'] = $resource_id; + $arr['allow_cid'] = $w['wiki']['allow_cid']; + $arr['allow_gid'] = $w['wiki']['allow_gid']; + $arr['deny_cid'] = $w['wiki']['deny_cid']; + $arr['deny_gid'] = $w['wiki']['deny_gid']; + $arr['item_private'] = $w['wiki']['item_private']; + $arr['item_wall'] = 1; + $arr['item_origin'] = 1; + $arr['item_thread_top'] = 1; + $arr['verb'] = ACTIVITY_CREATE; + $arr['obj_type'] = 'Document'; + // TODO: add an object? + $arr['public_policy'] = map_scope(PermissionLimits::Get($channel['channel_id'], 'view_wiki'), true); // We may wish to change this some day. $arr['item_unpublished'] = 1; - set_iconfig($arr,'nwikipage','pagetitle',(($name) ? $name : t('(No Title)')),true); - - $p = post_activity_item($arr, false, false); + set_iconfig($arr, 'nwikipage', 'pagetitle', (($name) ? $name : t('(No Title)')), true); + $p = item_store($arr, false, false); - if($p['item_id']) { - $page = [ + if ($p['item_id']) { + $page = [ 'rawName' => $name, 'htmlName' => escape_tags($name), - //'urlName' => urlencode($name), - 'urlName' => Zlib\NativeWiki::name_encode($name) + //'urlName' => urlencode($name), + 'urlName' => NativeWiki::name_encode($name) ]; - return array('page' => $page, 'item_id' => $p['item_id'], 'item' => $p['activity'], 'wiki' => $w, 'message' => '', 'success' => true); + return ['page' => $page, 'item_id' => $p['item_id'], 'item' => $p['activity'], 'wiki' => $w, 'message' => '', 'success' => true]; } - return [ 'success' => false, 'message' => t('Wiki page create failed.') ]; + return ['success' => false, 'message' => t('Wiki page create failed.')]; } static public function rename_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $pageNewName = ((array_key_exists('pageNewName',$arr)) ? $arr['pageNewName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); - $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + $pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : ''); + $pageNewName = ((array_key_exists('pageNewName', $arr)) ? $arr['pageNewName'] : ''); + $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0); - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); - if(! $w['wiki']) { - return array('message' => t('Wiki not found.'), 'success' => false); + $w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if (!$w['wiki']) { + return ['message' => t('Wiki not found.'), 'success' => false]; } - $ic = q("select * from iconfig left join item on iconfig.iid = item.id + $ic = q("select * from iconfig left join item on iconfig.iid = item.id where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'", intval($channel_id), dbesc($pageNewName) ); - if($ic) { - return [ 'success' => false, 'message' => t('Destination name already exists') ]; + if ($ic) { + return ['success' => false, 'message' => t('Destination name already exists')]; } $ids = []; - $ic = q("select *, item.id as item_id from iconfig left join item on iconfig.iid = item.id + $ic = q("select *, item.id as item_id from iconfig left join item on iconfig.iid = item.id where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'", intval($channel_id), dbesc($pageUrlName) ); - if($ic) { - foreach($ic as $c) { - set_iconfig($c['item_id'],'nwikipage','pagetitle',$pageNewName); + if ($ic) { + foreach ($ic as $c) { + set_iconfig($c['item_id'], 'nwikipage', 'pagetitle', $pageNewName); $ids[] = $c['item_id']; } @@ -154,105 +170,101 @@ class NativeWikiPage { dbesc($pageNewName) ); - $page = [ - 'rawName' => $pageNewName, - 'htmlName' => escape_tags($pageNewName), + $page = [ + 'rawName' => $pageNewName, + 'htmlName' => escape_tags($pageNewName), //'urlName' => urlencode(escape_tags($pageNewName)) - 'urlName' => Zlib\NativeWiki::name_encode($pageNewName) + 'urlName' => NativeWiki::name_encode($pageNewName) ]; - return [ 'success' => true, 'page' => $page ]; + return ['success' => true, 'page' => $page]; } - return [ 'success' => false, 'message' => t('Page not found') ]; - + return ['success' => false, 'message' => t('Page not found')]; + } static public function get_page_content($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); - $channel_id = ((array_key_exists('channel_id',$arr)) ? intval($arr['channel_id']) : 0); - $revision = ((array_key_exists('revision',$arr)) ? intval($arr['revision']) : (-1)); - + $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id', $arr)) ? intval($arr['channel_id']) : 0); - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); - if (! $w['wiki']) { - return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); + $w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if (!$w['wiki']) { + return ['content' => null, 'message' => 'Error reading wiki', 'success' => false]; } $item = self::load_page($arr); - if($item) { + if ($item) { $content = $item['body']; - return [ + return [ 'content' => $content, 'mimeType' => $w['mimeType'], - 'pageMimeType' => $item['mimetype'], - 'message' => '', + 'pageMimeType' => $item['mimetype'], + 'message' => '', 'success' => true ]; } - - return array('content' => null, 'message' => t('Error reading page content'), 'success' => false); + + return ['content' => null, 'message' => t('Error reading page content'), 'success' => false]; } static public function page_history($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); - $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0); - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + $w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); if (!$w['wiki']) { - return array('history' => null, 'message' => 'Error reading wiki', 'success' => false); + return ['history' => null, 'message' => 'Error reading wiki', 'success' => false]; } $items = self::load_page_history($arr); $history = []; - if($items) { + if ($items) { $processed = 0; - foreach($items as $item) { - if($processed > 1000) + foreach ($items as $item) { + if ($processed > 1000) break; - $processed ++; - $history[] = [ + $processed++; + $history[] = [ 'revision' => $item['revision'], - 'date' => datetime_convert('UTC',date_default_timezone_get(),$item['edited']), - 'name' => $item['author']['xchan_name'], - 'title' => get_iconfig($item,'nwikipage','commit_msg') + 'date' => datetime_convert('UTC', date_default_timezone_get(), $item['edited']), + 'name' => $item['author']['xchan_name'], + 'title' => get_iconfig($item, 'nwikipage', 'commit_msg') ]; } - return [ 'success' => true, 'history' => $history ]; + return ['success' => true, 'history' => $history]; } - return [ 'success' => false ]; + return ['success' => false]; } - + static public function load_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); - $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); - $revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : (-1)); + $pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0); + $revision = ((array_key_exists('revision', $arr)) ? $arr['revision'] : (-1)); - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + $w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); - if (! $w['wiki']) { - return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); + if (!$w['wiki']) { + return ['content' => null, 'message' => 'Error reading wiki', 'success' => false]; } $ids = ''; @@ -262,32 +274,32 @@ class NativeWikiPage { dbesc($pageUrlName) ); - if($ic) { - foreach($ic as $c) { - if($ids) + if ($ic) { + foreach ($ic as $c) { + if ($ids) $ids .= ','; $ids .= intval($c['iid']); } } - $sql_extra = item_permissions_sql($channel_id,$observer_hash); + $sql_extra = item_permissions_sql($channel_id, $observer_hash); - if($revision == (-1)) + if ($revision == (-1)) $sql_extra .= " order by revision desc "; - elseif($revision) + elseif ($revision) $sql_extra .= " and revision = " . intval($revision) . " "; $r = null; - if($ids) { + if ($ids) { $r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) $sql_extra limit 1", dbesc($resource_id), intval($channel_id) ); - if($r) { - $items = fetch_post_tags($r,true); + if ($r) { + $items = fetch_post_tags($r, true); return $items[0]; } } @@ -298,15 +310,14 @@ class NativeWikiPage { static public function load_page_history($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); - $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); - $revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : (-1)); + $pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0); - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); - if (! $w['wiki']) { - return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); + $w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if (!$w['wiki']) { + return ['content' => null, 'message' => 'Error reading wiki', 'success' => false]; } $ids = ''; @@ -315,28 +326,28 @@ class NativeWikiPage { intval($channel_id), dbesc($pageUrlName) ); - - if($ic) { - foreach($ic as $c) { - if($ids) + + if ($ic) { + foreach ($ic as $c) { + if ($ids) $ids .= ','; $ids .= intval($c['iid']); } } - $sql_extra = item_permissions_sql($channel_id,$observer_hash); + $sql_extra = item_permissions_sql($channel_id, $observer_hash); $sql_extra .= " order by revision desc "; $r = null; - if($ids) { + if ($ids) { $r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) and item_deleted = 0 $sql_extra", dbesc($resource_id), intval($channel_id) ); - if($r) { + if ($r) { xchan_query($r); - $items = fetch_post_tags($r,true); + $items = fetch_post_tags($r, true); return $items; } } @@ -346,31 +357,30 @@ class NativeWikiPage { static public function save_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $content = ((array_key_exists('content',$arr)) ? $arr['content'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); - $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); - $revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : 0); + $pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : ''); + $content = ((array_key_exists('content', $arr)) ? $arr['content'] : ''); + $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0); - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + $w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); if (!$w['wiki']) { - return array('message' => t('Error reading wiki'), 'success' => false); + return ['message' => t('Error reading wiki'), 'success' => false]; } - - // fetch the most recently saved revision. + + // fetch the most recently saved revision. $item = self::load_page($arr); - if(! $item) { - return array('message' => t('Page not found'), 'success' => false); + if (!$item) { + return ['message' => t('Page not found'), 'success' => false]; } $mimetype = $item['mimetype']; - // change just the fields we need to change to create a revision; + // change just the fields we need to change to create a revision; unset($item['id']); unset($item['author']); @@ -381,8 +391,8 @@ class NativeWikiPage { $item['edited'] = datetime_convert(); $item['mimetype'] = $mimetype; - if($item['iconfig'] && is_array($item['iconfig']) && count($item['iconfig'])) { - for($x = 0; $x < count($item['iconfig']); $x ++) { + if ($item['iconfig'] && is_array($item['iconfig']) && count($item['iconfig'])) { + for ($x = 0; $x < count($item['iconfig']); $x++) { unset($item['iconfig'][$x]['id']); unset($item['iconfig'][$x]['iid']); } @@ -390,168 +400,164 @@ class NativeWikiPage { $ret = item_store($item, false, false); - if($ret['item_id']) - return array('message' => '', 'item_id' => $ret['item_id'], 'filename' => $pageUrlName, 'success' => true); + if ($ret['item_id']) + return ['message' => '', 'item_id' => $ret['item_id'], 'filename' => $pageUrlName, 'success' => true]; else - return array('message' => t('Page update failed.'), 'success' => false); - } + return ['message' => t('Page update failed.'), 'success' => false]; + } static public function delete_page($arr) { - $pageUrlName = (array_key_exists('pageUrlName',$arr) ? $arr['pageUrlName'] : ''); - $resource_id = (array_key_exists('resource_id',$arr) ? $arr['resource_id'] : ''); - $observer_hash = (array_key_exists('observer_hash',$arr) ? $arr['observer_hash'] : ''); - $channel_id = (array_key_exists('channel_id',$arr) ? $arr['channel_id'] : 0); + $pageUrlName = (array_key_exists('pageUrlName', $arr) ? $arr['pageUrlName'] : ''); + $resource_id = (array_key_exists('resource_id', $arr) ? $arr['resource_id'] : ''); + $observer_hash = (array_key_exists('observer_hash', $arr) ? $arr['observer_hash'] : ''); + $channel_id = (array_key_exists('channel_id', $arr) ? $arr['channel_id'] : 0); - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); - if(! $w['wiki']) { - return [ 'success' => false, 'message' => t('Error reading wiki') ]; + $w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if (!$w['wiki']) { + return ['success' => false, 'message' => t('Error reading wiki')]; } $ids = []; - $ic = q("select * from iconfig left join item on iconfig.iid = item.id + $ic = q("select * from iconfig left join item on iconfig.iid = item.id where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'", intval($channel_id), dbesc($pageUrlName) ); - if($ic) { - foreach($ic as $c) { + if ($ic) { + foreach ($ic as $c) { $ids[] = intval($c['iid']); } } - if($ids) { + if ($ids) { drop_items($ids, true, DROPITEM_PHASE1); - return [ 'success' => true ]; + return ['success' => true]; } - return [ 'success' => false, 'message' => t('Nothing deleted') ]; + return ['success' => false, 'message' => t('Nothing deleted')]; } - + static public function revert_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $commitHash = ((array_key_exists('commitHash',$arr)) ? $arr['commitHash'] : null); - $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); - $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); + $commitHash = ((array_key_exists('commitHash', $arr)) ? $arr['commitHash'] : null); + $observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0); - if (! $commitHash) { - return array('message' => 'No commit was provided', 'success' => false); + if (!$commitHash) { + return ['message' => 'No commit was provided', 'success' => false]; } - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + $w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); if (!$w['wiki']) { - return array('message' => 'Error reading wiki', 'success' => false); + return ['message' => 'Error reading wiki', 'success' => false]; } $x = $arr; - if(intval($commitHash) > 0) { + if (intval($commitHash) > 0) { unset($x['commitHash']); $x['revision'] = intval($commitHash) - 1; - $loaded = self::load_page($x); + $loaded = self::load_page($x); - if($loaded) { + if ($loaded) { $content = $loaded['body']; - return [ 'content' => $content, 'success' => true ]; + return ['content' => $content, 'success' => true]; } - return [ 'success' => false ]; + return ['success' => false]; } } - + static public function compare_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $currentCommit = ((array_key_exists('currentCommit',$arr)) ? $arr['currentCommit'] : (-1)); - $compareCommit = ((array_key_exists('compareCommit',$arr)) ? $arr['compareCommit'] : 0); - $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); - $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); + $compareCommit = ((array_key_exists('compareCommit', $arr)) ? $arr['compareCommit'] : 0); + $observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0); - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + $w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); if (!$w['wiki']) { - return array('message' => t('Error reading wiki'), 'success' => false); + return ['message' => t('Error reading wiki'), 'success' => false]; } - $x = $arr; + $x = $arr; $x['revision'] = (-1); $currpage = self::load_page($x); - if($currpage) + if ($currpage) $currentContent = $currpage['body']; $x['revision'] = $compareCommit; - $comppage = self::load_page($x); - if($comppage) + $comppage = self::load_page($x); + if ($comppage) $compareContent = $comppage['body']; - if($currpage && $comppage) { + if ($currpage && $comppage) { require_once('library/class.Diff.php'); $diff = \Diff::toTable(\Diff::compare($currentContent, $compareContent)); - return [ 'success' => true, 'diff' => $diff ]; + return ['success' => true, 'diff' => $diff]; } - return [ 'success' => false, 'message' => t('Compare: object not found.') ]; + return ['success' => false, 'message' => t('Compare: object not found.')]; } - + static public function commit($arr) { - $commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : t('Page updated')); - $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); - $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : t('Untitled')); + $commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : t('Page updated')); + $observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0); - if(array_key_exists('resource_id', $arr)) { + if (array_key_exists('resource_id', $arr)) { $resource_id = $arr['resource_id']; } else { - return array('message' => t('Wiki resource_id required for git commit'), 'success' => false); + return ['message' => t('Wiki resource_id required for git commit'), 'success' => false]; } - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); - if (! $w['wiki']) { - return array('message' => t('Error reading wiki'), 'success' => false); + $w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if (!$w['wiki']) { + return ['message' => t('Error reading wiki'), 'success' => false]; } $page = self::load_page($arr); - if($page) { - set_iconfig($page['id'],'nwikipage','commit_msg',escape_tags($commit_msg),true); - return [ 'success' => true, 'item_id' => $page['id'], 'page' => $page ]; + if ($page) { + set_iconfig($page['id'], 'nwikipage', 'commit_msg', escape_tags($commit_msg), true); + return ['success' => true, 'item_id' => $page['id'], 'page' => $page]; } - return [ 'success' => false, 'message' => t('Page not found.') ]; + return ['success' => false, 'message' => t('Page not found.')]; } - + static public function convert_links($s, $wikiURL) { - - if (strpos($s,'[[') !== false) { + + if (strpos($s, '[[') !== false) { preg_match_all("/\[\[(.*?)\]\]/", $s, $match); - $pages = $pageURLs = array(); + $pages = $pageURLs = []; foreach ($match[1] as $m) { // TODO: Why do we need to double urlencode for this to work? //$pageURLs[] = urlencode(urlencode(escape_tags($m))); - $titleUri = explode('|',$m); - $page = $titleUri[0] ?? ''; - $title = $titleUri[1] ?? $page; - $pageURLs[] = Zlib\NativeWiki::name_encode(escape_tags($page)); - $pages[] = $title; + $titleUri = explode('|', $m); + $page = $titleUri[0] ?? ''; + $title = $titleUri[1] ?? $page; + $pageURLs[] = NativeWiki::name_encode(escape_tags($page)); + $pages[] = $title; } $idx = 0; - while(strpos($s,'[[') !== false) { - $replace = '<a href="'.$wikiURL.'/'.$pageURLs[$idx].'">'.$pages[$idx].'</a>'; - $s = preg_replace("/\[\[(.*?)\]\]/", $replace, $s, 1); + while (strpos($s, '[[') !== false) { + $replace = '<a href="' . $wikiURL . '/' . $pageURLs[$idx] . '">' . $pages[$idx] . '</a>'; + $s = preg_replace("/\[\[(.*?)\]\]/", $replace, $s, 1); $idx++; } } @@ -564,21 +570,21 @@ class NativeWikiPage { $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); $pageHistory = self::page_history([ - 'channel_id' => \App::$profile_uid, + 'channel_id' => App::$profile_uid, 'observer_hash' => get_observer_hash(), 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName ]); - return replace_macros(get_markup_template('nwiki_page_history.tpl'), array( + return replace_macros(get_markup_template('nwiki_page_history.tpl'), [ '$pageHistory' => $pageHistory['history'], '$permsWrite' => $arr['permsWrite'], '$name_lbl' => t('Name'), - '$msg_label' => t('Message','wiki_history'), + '$msg_label' => t('Message', 'wiki_history'), '$date_lbl' => t('Date'), '$revert_btn' => t('Revert'), '$compare_btn' => t('Compare') - )); + ]); } @@ -590,14 +596,14 @@ class NativeWikiPage { * @return string */ static public function generate_toc($s) { - if (strpos($s,'[toc]') !== false) { + if (strpos($s, '[toc]') !== false) { //$toc_md = wiki_toc($s); // Generate Markdown-formatted list prior to HTML render $toc_md = '<ul id="wiki-toc"></ul>'; // use the available jQuery plugin http://ndabas.github.io/toc/ - $s = preg_replace("/\[toc\]/", $toc_md, $s, -1); + $s = preg_replace("/\[toc\]/", $toc_md, $s, -1); } return $s; } - + /** * Converts a select set of bbcode tags. Much of the code is copied from include/bbcode.php @@ -605,27 +611,27 @@ class NativeWikiPage { * @return string */ static public function bbcode($s) { - - $s = str_replace(array('[baseurl]', '[sitename]'), array(z_root(), get_config('system', 'sitename')), $s); - - $s = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_callback', $s); - $s = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_necallback', $s); + $s = str_replace(['[baseurl]', '[sitename]'], [z_root(), get_config('system', 'sitename')], $s); + + $s = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism", 'oblanguage_callback', $s); + $s = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism", 'oblanguage_necallback', $s); - $observer = \App::get_observer(); + + $observer = App::get_observer(); if ($observer) { - $s1 = '<span class="bb_observer" title="' . t('Different viewers will see this text differently') . '">'; - $s2 = '</span>'; + $s1 = '<span class="bb_observer" title="' . t('Different viewers will see this text differently') . '">'; + $s2 = '</span>'; $obsBaseURL = $observer['xchan_connurl']; $obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL); - $s = str_replace('[observer.baseurl]', $obsBaseURL, $s); - $s = str_replace('[observer.url]', $observer['xchan_url'], $s); - $s = str_replace('[observer.name]', $s1 . $observer['xchan_name'] . $s2, $s); - $s = str_replace('[observer.address]', $s1 . $observer['xchan_addr'] . $s2, $s); - $s = str_replace('[observer.webname]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $s); - $s = str_replace('[observer.photo]', '', $s); - } + $s = str_replace('[observer.baseurl]', $obsBaseURL, $s); + $s = str_replace('[observer.url]', $observer['xchan_url'], $s); + $s = str_replace('[observer.name]', $s1 . $observer['xchan_name'] . $s2, $s); + $s = str_replace('[observer.address]', $s1 . $observer['xchan_addr'] . $s2, $s); + $s = str_replace('[observer.webname]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $s); + $s = str_replace('[observer.photo]', '', $s); + } else { $s = str_replace('[observer.baseurl]', '', $s); $s = str_replace('[observer.url]', '', $s); @@ -637,62 +643,63 @@ class NativeWikiPage { return $s; } - + static public function get_file_ext($arr) { - if($arr['mimetype'] === 'text/bbcode') + if ($arr['mimetype'] === 'text/bbcode') return '.bb'; - elseif($arr['mimetype'] === 'text/markdown') + elseif ($arr['mimetype'] === 'text/markdown') return '.md'; - elseif($arr['mimetype'] === 'text/plain') + elseif ($arr['mimetype'] === 'text/plain') return '.txt'; } - - // This function is derived from + + // This function is derived from // http://stackoverflow.com/questions/32068537/generate-table-of-contents-from-markdown-in-php static public function toc($content) { - // ensure using only "\n" as line-break - $source = str_replace(["\r\n", "\r"], "\n", $content); - - // look for markdown TOC items - preg_match_all( - '/^(?:=|-|#).*$/m', - $source, - $matches, - PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE - ); - - // preprocess: iterate matched lines to create an array of items - // where each item is an array(level, text) - $file_size = strlen($source); - foreach ($matches[0] as $item) { - $found_mark = substr($item[0], 0, 1); - if ($found_mark == '#') { - // text is the found item - $item_text = $item[0]; - $item_level = strrpos($item_text, '#') + 1; - $item_text = substr($item_text, $item_level); - } else { - // text is the previous line (empty if <hr>) - $item_offset = $item[1]; - $prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2)); - $item_text = - substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1); - $item_text = trim($item_text); - $item_level = $found_mark == '=' ? 1 : 2; - } - if (!trim($item_text) OR strpos($item_text, '|') !== FALSE) { - // item is an horizontal separator or a table header, don't mind - continue; - } - $raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)]; - } + // ensure using only "\n" as line-break + $source = str_replace(["\r\n", "\r"], "\n", $content); + + // look for markdown TOC items + preg_match_all( + '/^(?:=|-|#).*$/m', + $source, + $matches, + PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE + ); + + // preprocess: iterate matched lines to create an array of items + // where each item is an array(level, text) + $file_size = strlen($source); + foreach ($matches[0] as $item) { + $found_mark = substr($item[0], 0, 1); + if ($found_mark == '#') { + // text is the found item + $item_text = $item[0]; + $item_level = strrpos($item_text, '#') + 1; + $item_text = substr($item_text, $item_level); + } + else { + // text is the previous line (empty if <hr>) + $item_offset = $item[1]; + $prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2)); + $item_text = + substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1); + $item_text = trim($item_text); + $item_level = $found_mark == '=' ? 1 : 2; + } + if (!trim($item_text) or strpos($item_text, '|') !== FALSE) { + // item is an horizontal separator or a table header, don't mind + continue; + } + $raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)]; + } $o = ''; - foreach($raw_toc as $t) { + foreach ($raw_toc as $t) { $level = intval($t['level']); - $text = $t['text']; + $text = $t['text']; switch ($level) { case 1: $li = '* '; @@ -712,7 +719,7 @@ class NativeWikiPage { } $o .= $li . $text . "\n"; } - return $o; + return $o; } } diff --git a/Zotlabs/Lib/PConfig.php b/Zotlabs/Lib/PConfig.php index 765131f0d..80340f501 100644 --- a/Zotlabs/Lib/PConfig.php +++ b/Zotlabs/Lib/PConfig.php @@ -2,6 +2,8 @@ namespace Zotlabs\Lib; +use App; + /** * @brief Class for handling channel specific configurations. * @@ -32,15 +34,15 @@ class PConfig { if(is_null($uid) || $uid === false) return false; - if(! is_array(\App::$config)) { + if(! is_array(App::$config)) { btlogger('App::$config not an array'); } - if(! array_key_exists($uid, \App::$config)) { - \App::$config[$uid] = array(); + if(! array_key_exists($uid, App::$config)) { + App::$config[$uid] = array(); } - if(! is_array(\App::$config[$uid])) { + if(! is_array(App::$config[$uid])) { btlogger('App::$config[$uid] not an array: ' . $uid); } @@ -52,12 +54,12 @@ class PConfig { foreach($r as $rr) { $k = $rr['k']; $c = $rr['cat']; - if(! array_key_exists($c, \App::$config[$uid])) { - \App::$config[$uid][$c] = array(); - \App::$config[$uid][$c]['config_loaded'] = true; + if(! array_key_exists($c, App::$config[$uid])) { + App::$config[$uid][$c] = array(); + App::$config[$uid][$c]['config_loaded'] = true; } - \App::$config[$uid][$c][$k] = $rr['v']; - \App::$config[$uid][$c]['pcfgud:'.$k] = $rr['updated']; + App::$config[$uid][$c][$k] = $rr['v']; + App::$config[$uid][$c]['pcfgud:'.$k] = $rr['updated']; } } } @@ -86,15 +88,15 @@ class PConfig { if(is_null($uid) || $uid === false) return $default; - if(! array_key_exists($uid, \App::$config)) + if(! array_key_exists($uid, App::$config)) self::Load($uid); - if((! array_key_exists($family, \App::$config[$uid])) || (! array_key_exists($key, \App::$config[$uid][$family]))) + if((! array_key_exists($family, App::$config[$uid])) || (! array_key_exists($key, App::$config[$uid][$family]))) return $default; - return ((! is_array(\App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$uid][$family][$key])) - ? unserialize(\App::$config[$uid][$family][$key]) - : \App::$config[$uid][$family][$key] + return ((! is_array(App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', App::$config[$uid][$family][$key])) + ? unserialize(App::$config[$uid][$family][$key]) + : App::$config[$uid][$family][$key] ); } @@ -133,6 +135,7 @@ class PConfig { $dbvalue = ((is_array($value)) ? serialize($value) : $value); $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); $new = false; + $update = false; $now = datetime_convert(); if (! $updated) { @@ -143,23 +146,22 @@ class PConfig { $updated = datetime_convert('UTC','UTC','-2 seconds'); } - $hash = hash('sha256',$family.':'.$key); + $hash = gen_link_id($family.':'.$key); if (self::Get($uid, 'hz_delpconfig', $hash) !== false) { if (self::Get($uid, 'hz_delpconfig', $hash) > $now) { logger('Refusing to update pconfig with outdated info (Item deleted more recently).', LOGGER_NORMAL, LOG_ERR); return self::Get($uid,$family,$key); } else { - self::Delete($uid,'hz_delpconfig',$hash); + self::Delete($uid, 'hz_delpconfig', $hash); } } if(self::Get($uid, $family, $key) === false) { - if(! array_key_exists($uid, \App::$config)) - \App::$config[$uid] = array(); - if(! array_key_exists($family, \App::$config[$uid])) - \App::$config[$uid][$family] = array(); - + if(! array_key_exists($uid, App::$config)) + App::$config[$uid] = array(); + if(! array_key_exists($family, App::$config[$uid])) + App::$config[$uid][$family] = array(); $ret = q("INSERT INTO pconfig ( uid, cat, k, v, updated ) VALUES ( %d, '%s', '%s', '%s', '%s' ) ", intval($uid), @@ -177,13 +179,14 @@ class PConfig { logger("Error: Insert to pconfig failed.",LOGGER_NORMAL, LOG_ERR); } - \App::$config[$uid][$family]['pcfgud:'.$key] = $updated; + $new = true; + App::$config[$uid][$family]['pcfgud:'.$key] = $updated; } else { - $new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now); + $update = (App::$config[$uid][$family]['pcfgud:'.$key] < $now); - if ($new) { + if ($update) { // @NOTE There is still a possible race condition under limited circumstances // where a value will be updated by another thread with more current data than @@ -198,7 +201,7 @@ class PConfig { dbesc($key) ); - \App::$config[$uid][$family]['pcfgud:'.$key] = $updated; + App::$config[$uid][$family]['pcfgud:'.$key] = $updated; } else { logger('Refusing to update pconfig with outdated info.', LOGGER_NORMAL, LOG_ERR); @@ -211,16 +214,16 @@ class PConfig { // set in the life of this page. We need this to // synchronise channel clones. - if(! array_key_exists('transient', \App::$config[$uid])) - \App::$config[$uid]['transient'] = array(); - if(! array_key_exists($family, \App::$config[$uid]['transient'])) - \App::$config[$uid]['transient'][$family] = array(); + if(! array_key_exists('transient', App::$config[$uid])) + App::$config[$uid]['transient'] = array(); + if(! array_key_exists($family, App::$config[$uid]['transient'])) + App::$config[$uid]['transient'][$family] = array(); - \App::$config[$uid][$family][$key] = $value; + App::$config[$uid][$family][$key] = $value; - if ($new) { - \App::$config[$uid]['transient'][$family][$key] = $value; - \App::$config[$uid]['transient'][$family]['pcfgud:'.$key] = $updated; + if ($new || $update) { + App::$config[$uid]['transient'][$family][$key] = $value; + App::$config[$uid]['transient'][$family]['pcfgud:'.$key] = $updated; } if($ret) @@ -253,7 +256,7 @@ class PConfig { $updated = ($updated) ? $updated : datetime_convert('UTC','UTC','-2 seconds'); $now = datetime_convert(); - $newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now); + $newer = (App::$config[$uid][$family]['pcfgud:'.$key] < $now); if (! $newer) { logger('Refusing to delete pconfig with outdated delete request.', LOGGER_NORMAL, LOG_ERR); @@ -262,12 +265,12 @@ class PConfig { $ret = false; - if (isset(\App::$config[$uid][$family][$key])) { - unset(\App::$config[$uid][$family][$key]); + if (isset(App::$config[$uid][$family][$key])) { + unset(App::$config[$uid][$family][$key]); } - if (isset(\App::$config[$uid][$family]['pcfgud:'.$key])) { - unset(\App::$config[$uid][$family]['pcfgud:'.$key]); + if (isset(App::$config[$uid][$family]['pcfgud:'.$key])) { + unset(App::$config[$uid][$family]['pcfgud:'.$key]); } $ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'", @@ -278,9 +281,9 @@ class PConfig { // Synchronize delete with clones. - if ($family != 'hz_delpconfig') { - $hash = hash('sha256',$family.':'.$key); - set_pconfig($uid,'hz_delpconfig',$hash,$updated); + if ($family !== 'hz_delpconfig') { + $hash = gen_link_id($family.':'.$key); + set_pconfig($uid, 'hz_delpconfig', $hash, $updated); } return $ret; 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..4675df04e 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'); @@ -401,6 +424,7 @@ class ThreadItem { 'mids' => $json_mids, 'parent' => $item['parent'], 'author_id' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']), + 'author_is_group_actor' => (($item['author']['xchan_pubforum']) ? t('Forum') : ''), 'isevent' => $isevent, 'attend' => $attend, 'consensus' => $consensus, diff --git a/Zotlabs/Lib/ZotURL.php b/Zotlabs/Lib/ZotURL.php index 6bb01fd7a..db0071f72 100644 --- a/Zotlabs/Lib/ZotURL.php +++ b/Zotlabs/Lib/ZotURL.php @@ -87,4 +87,4 @@ class ZotURL { return ids_to_array($r,'hubloc_url'); } -}
\ No newline at end of file +} |