diff options
41 files changed, 563 insertions, 1092 deletions
@@ -1,3 +1,7 @@ +Hubzilla 8.8.8 (2024-02-29) + - Streams compatibility fixes + + Hubzilla 8.8.7 (2024-01-19) - Fix regression in Activity::actor_store() diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index 948aba80c..4e7ca3911 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -276,7 +276,7 @@ class Notifier { } // follow/unfollow is for internal use only - if (in_array($target_item['verb'], [ACTIVITY_FOLLOW, ACTIVITY_UNFOLLOW])) { + if (in_array($target_item['verb'], ['Follow', 'Ignore', ACTIVITY_FOLLOW, ACTIVITY_UNFOLLOW])) { logger('not fowarding follow/unfollow note activity'); return; } diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 844dc5905..1e0ce6ae2 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -172,10 +172,6 @@ class Activity { } static function fetch_person($x) { - return self::fetch_profile($x); - } - - static function fetch_profile($x) { $r = q("select * from xchan where xchan_url = '%s' limit 1", dbesc($x['id']) ); @@ -189,7 +185,14 @@ class Activity { return []; return self::encode_person($r[0]); + } + static function fetch_profile($x) { + if (isset($x['describes'])) { + return $x; + } + + return []; } static function fetch_thing($x) { @@ -448,13 +451,7 @@ class Activity { $ret = []; - if ($i['verb'] === ACTIVITY_FRIEND) { - // Hubzilla 'make-friend' activity, no direct mapping from AS1 to AS2 - make it a note - $objtype = 'Note'; - } - else { - $objtype = self::activity_obj_mapper($i['obj_type']); - } + $objtype = self::activity_obj_mapper($i['obj_type']); if (intval($i['item_deleted'])) { $ret['type'] = 'Tombstone'; @@ -789,11 +786,6 @@ class Activity { $ret = []; $reply = false; - if ($i['verb'] === ACTIVITY_FRIEND) { - // Hubzilla 'make-friend' activity, no direct mapping from AS1 to AS2 - make it a note - $ret['obj'] = []; - } - $ret['type'] = self::activity_mapper($i['verb']); if ((isset($i['item_deleted']) && intval($i['item_deleted'])) && !$recurse) { @@ -989,6 +981,7 @@ class Activity { $ret['to'] = [ACTIVITY_PUBLIC_INBOX]; } + $hookinfo = [ 'item' => $i, 'encoded' => $ret @@ -1227,20 +1220,6 @@ class Activity { return $acts[$verb]; } - // Reactions will just map to normal activities - - if (strpos($verb, ACTIVITY_REACT) !== false) - return 'Create'; - - if (strpos($verb, ACTIVITY_MOOD) !== false) - return 'Create'; - - if (strpos($verb, ACTIVITY_FRIEND) !== false) - return 'Create'; - - if (strpos($verb, ACTIVITY_POKE) !== false) - return 'Activity'; - // We should return false, however this will trigger an uncaught execption and crash // the delivery system if encountered by the JSON-LDSignature library @@ -2074,6 +2053,10 @@ class Activity { $s['mid'] = $act->obj['data']['id']; } + if ($act->objprop('type') === 'Profile') { + $s['mid'] = $act->id; + } + if (!$s['mid']) { return false; } @@ -2178,6 +2161,7 @@ class Activity { $content['content'] = sprintf(t('🔁 Repeated %1$s\'s %2$s'), $mention, $act->obj['type']); } + // TODO: Deprecated if ($act->type === 'emojiReaction') { $content['content'] = (($act->tgt && $act->tgt['type'] === 'Image') ? '[img=32x32]' . $act->tgt['url'] . '[/img]' : '&#x' . $act->tgt['name'] . ';'); } @@ -2317,6 +2301,12 @@ class Activity { if (!$response_activity) { + if ($act->objprop('type') === 'Profile') { + $s['parent_mid'] = $s['mid']; + $s['item_thread_top'] = 1; + } + + // we will need a hook here to extract magnet links e.g. peertube // right now just link to the largest mp4 we find that will fit in our // standard content region @@ -2462,7 +2452,6 @@ class Activity { } } - if ($act->objprop('type') === 'Page' && !$s['body']) { $ptr = null; @@ -2503,7 +2492,7 @@ class Activity { } } - if (in_array($act->objprop('type', ''), ['Note', 'Article', 'Page'])) { + if (in_array($act->objprop('type'), ['Note', 'Article', 'Page'])) { $ptr = null; if (array_key_exists('url', $act->obj)) { diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php index 00e65479e..1c05d69b1 100644 --- a/Zotlabs/Lib/Apps.php +++ b/Zotlabs/Lib/Apps.php @@ -352,8 +352,6 @@ class Apps { 'Directory' => t('Directory'), 'Help' => t('Help'), 'Mail' => t('Mail'), - 'Mood' => t('Mood'), - 'Poke' => t('Poke'), 'Chat' => t('Chat'), 'Search' => t('Search'), 'Probe' => t('Probe'), diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index a98ae8f20..942c3082a 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -1143,7 +1143,6 @@ class Libzot { if ($env['encoding'] === 'activitystreams') { $AS = new ActivityStreams($data); - if (!$AS->is_valid()) { logger('Activity rejected: ' . print_r($data, true)); return; diff --git a/Zotlabs/Module/Activity.php b/Zotlabs/Module/Activity.php index b2f0ce498..133312e28 100644 --- a/Zotlabs/Module/Activity.php +++ b/Zotlabs/Module/Activity.php @@ -25,7 +25,7 @@ class Activity extends Controller { $portable_id = EMPTY_STR; - $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ", + $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", dbesc(ACTIVITY_FOLLOW), dbesc(ACTIVITY_UNFOLLOW) ); @@ -186,7 +186,7 @@ class Activity extends Controller { } } - $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ", + $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", dbesc(ACTIVITY_FOLLOW), dbesc(ACTIVITY_UNFOLLOW) ); diff --git a/Zotlabs/Module/Contactedit.php b/Zotlabs/Module/Contactedit.php index e20e90872..3527e9380 100644 --- a/Zotlabs/Module/Contactedit.php +++ b/Zotlabs/Module/Contactedit.php @@ -177,22 +177,8 @@ class Contactedit extends Controller { intval($channel['channel_id']) ); if (($pr) && (!intval($contact['abook_hidden'])) && (intval(get_pconfig($channel['channel_id'], 'system', 'post_newfriend')))) { - $xarr = []; - - $xarr['item_wall'] = 1; - $xarr['item_origin'] = 1; - $xarr['item_thread_top'] = 1; - $xarr['owner_xchan'] = $xarr['author_xchan'] = $channel['channel_hash']; - $xarr['allow_cid'] = $channel['channel_allow_cid']; - $xarr['allow_gid'] = $channel['channel_allow_gid']; - $xarr['deny_cid'] = $channel['channel_deny_cid']; - $xarr['deny_gid'] = $channel['channel_deny_gid']; - $xarr['item_private'] = (($xarr['allow_cid'] || $xarr['allow_gid'] || $xarr['deny_cid'] || $xarr['deny_gid']) ? 1 : 0); - $xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . $contact['xchan_url'] . ']' . $contact['xchan_name'] . '[/zrl]'; - - $xarr['body'] .= "\n\n\n" . '[zrl=' . $contact['xchan_url'] . '][zmg=80x80]' . $contact['xchan_photo_m'] . '[/zmg][/zrl]'; - + $xarr['body'] .= "\n\n\n" . '[zrl=' . $contact['xchan_url'] . '][zmg=' . $contact['xchan_photo_m'] . ']' . $contact['xchan_name'] . '[/zmg][/zrl]'; post_activity_item($xarr); } diff --git a/Zotlabs/Module/Conversation.php b/Zotlabs/Module/Conversation.php index 86ce66caa..aa8349f55 100644 --- a/Zotlabs/Module/Conversation.php +++ b/Zotlabs/Module/Conversation.php @@ -25,7 +25,7 @@ class Conversation extends Controller { $portable_id = EMPTY_STR; - $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ", + $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", dbesc(ACTIVITY_FOLLOW), dbesc(ACTIVITY_UNFOLLOW) ); diff --git a/Zotlabs/Module/Cover_photo.php b/Zotlabs/Module/Cover_photo.php index 1ecbfce3e..f4f9480c0 100644 --- a/Zotlabs/Module/Cover_photo.php +++ b/Zotlabs/Module/Cover_photo.php @@ -93,8 +93,6 @@ class Cover_photo extends \Zotlabs\Web\Controller { $image_id = substr($image_id,0,-2); } - - $srcX = intval($_POST['xstart']); $srcY = intval($_POST['ystart']); $srcW = intval($_POST['xfinal']) - $srcX; @@ -228,7 +226,7 @@ class Cover_photo extends \Zotlabs\Web\Controller { return; } - $this->send_cover_photo_activity($channel,$base_image,$profile); + profile_activity([t('Cover Photo')], $base_image['resource_id']); $sync = attach_export_data($channel,$base_image['resource_id']); if($sync) @@ -245,7 +243,6 @@ class Cover_photo extends \Zotlabs\Web\Controller { } - $hash = photo_new_resource(); $smallest = 0; @@ -287,45 +284,6 @@ class Cover_photo extends \Zotlabs\Web\Controller { } - function send_cover_photo_activity($channel,$photo,$profile) { - - $arr = array(); - $arr['item_thread_top'] = 1; - $arr['item_origin'] = 1; - $arr['item_wall'] = 1; - - if($profile && stripos($profile['gender'],t('female')) !== false) - $t = t('%1$s updated her %2$s'); - elseif($profile && stripos($profile['gender'],t('male')) !== false) - $t = t('%1$s updated his %2$s'); - else - $t = t('%1$s updated their %2$s'); - - $ptext = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' . t('cover photo') . '[/zrl]'; - - $ltext = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-8[/zmg][/zrl]'; - - $arr['body'] = sprintf($t,$channel['channel_name'],$ptext) . "\n\n" . $ltext; - - $acl = new \Zotlabs\Access\AccessList($channel); - $x = $acl->get(); - $arr['allow_cid'] = $x['allow_cid']; - - $arr['allow_gid'] = $x['allow_gid']; - $arr['deny_cid'] = $x['deny_cid']; - $arr['deny_gid'] = $x['deny_gid']; - - $arr['uid'] = $channel['channel_id']; - $arr['aid'] = $channel['channel_account_id']; - - $arr['owner_xchan'] = $channel['channel_hash']; - $arr['author_xchan'] = $channel['channel_hash']; - - post_activity_item($arr); - - - } - /** * @brief Generate content of profile-photo view @@ -334,7 +292,6 @@ class Cover_photo extends \Zotlabs\Web\Controller { * */ - function get() { if(! local_channel()) { diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 7d71edc99..9c967cb88 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -52,7 +52,7 @@ class Item extends Controller { $portable_id = EMPTY_STR; - $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ", + $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", dbesc(ACTIVITY_FOLLOW), dbesc(ACTIVITY_UNFOLLOW) ); @@ -168,7 +168,7 @@ class Item extends Controller { $portable_id = EMPTY_STR; - $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ", + $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", dbesc(ACTIVITY_FOLLOW), dbesc(ACTIVITY_UNFOLLOW) ); diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index e19a74a23..86ae48365 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -22,9 +22,6 @@ class Like extends Controller { 'like' => 'Like', 'dislike' => 'Dislike', 'announce' => ACTIVITY_SHARE, - 'agree' => ACTIVITY_AGREE, - 'disagree' => ACTIVITY_DISAGREE, - 'abstain' => ACTIVITY_ABSTAIN, 'attendyes' => 'Accept', 'attendno' => 'Reject', 'attendmaybe' => 'TentativeAccept' @@ -325,6 +322,8 @@ class Like extends Controller { // parent, copy that as well. if ($r) { + $obj_type = $r[0]['obj_type']; + if ($r[0]['uid'] === $sys_channel['channel_id'] && local_channel()) { $r = [copy_of_pubitem(App::get_channel(), $r[0]['mid'])]; } @@ -372,17 +371,13 @@ class Like extends Controller { $multi_undo = false; - // event participation and consensus items are essentially radio toggles. If you make a subsequent choice, + // event participation items are essentially radio toggles. If you make a subsequent choice, // we need to eradicate your first choice. if (in_array($activity, ['Accept', 'Reject', 'TentativeAccept', ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE])) { $verbs = "'Accept','Reject','TentativeAccept','" . dbesc(ACTIVITY_ATTEND) . "','" . dbesc(ACTIVITY_ATTENDNO) . "','" . dbesc(ACTIVITY_ATTENDMAYBE) . "' "; $multi_undo = true; } - if ($activity === ACTIVITY_AGREE || $activity === ACTIVITY_DISAGREE || $activity === ACTIVITY_ABSTAIN) { - $verbs = " '" . dbesc(ACTIVITY_AGREE) . "','" . dbesc(ACTIVITY_DISAGREE) . "','" . dbesc(ACTIVITY_ABSTAIN) . "' "; - $multi_undo = true; - } $item_normal = item_normal(); @@ -439,7 +434,7 @@ class Like extends Controller { } } - $uuid = item_message_id(); + $uuid = new_uuid(); $arr = array(); @@ -452,17 +447,17 @@ class Like extends Controller { $arr['item_wall'] = 1; } else { - switch ($item['resource_type']) { - case 'photo': - $obj_type = 'Image'; - $post_type = t('photo'); + switch ($item['object_type']) { + case 'Image': + $post_type = t('image'); break; - case 'event': - $obj_type = 'Invite'; + case 'Invite': $post_type = t('event'); break; + case 'Profile': + $post_type = t('profile'); + break; default: - $obj_type = 'Note'; $post_type = t('status'); break; } @@ -493,12 +488,6 @@ class Like extends Controller { $bodyverb = t('%1$s likes %2$s\'s %3$s'); if ($verb === 'dislike') $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); - if ($verb === 'agree') - $bodyverb = t('%1$s agrees with %2$s\'s %3$s'); - if ($verb === 'disagree') - $bodyverb = t('%1$s doesn\'t agree with %2$s\'s %3$s'); - if ($verb === 'abstain') - $bodyverb = t('%1$s abstains from a decision on %2$s\'s %3$s'); if ($verb === 'attendyes') $bodyverb = t('%1$s is attending %2$s\'s %3$s'); if ($verb === 'attendno') @@ -540,7 +529,7 @@ class Like extends Controller { if ($obj_type === 'thing' && $r[0]['imgurl']) { $arr['body'] .= "\n\n[zmg=80x80]" . $r[0]['imgurl'] . '[/zmg]'; } - if ($obj_type === 'profile') { + if ($obj_type === 'Profile') { if ($public) { $arr['body'] .= "\n\n" . '[embed]' . z_root() . '/profile/' . $ch[0]['channel_address'] . '[/embed]'; } @@ -594,6 +583,7 @@ class Like extends Controller { Libsync::build_sync_packet($profile_uid, ['item' => [encode_item($sync_item[0], true)]]); } + if ($extended_like) { $r = q("insert into likes (channel_id,liker,likee,iid,i_mid,verb,target_type,target_id,target) values (%d,'%s','%s',%d,'%s','%s','%s','%s','%s')", intval($ch[0]['channel_id']), diff --git a/Zotlabs/Module/Mood.php b/Zotlabs/Module/Mood.php deleted file mode 100644 index edd3f0e1a..000000000 --- a/Zotlabs/Module/Mood.php +++ /dev/null @@ -1,163 +0,0 @@ -<?php -namespace Zotlabs\Module; - -use App; -use Zotlabs\Lib\Apps; -use Zotlabs\Web\Controller; - -require_once('include/security.php'); -require_once('include/bbcode.php'); -require_once('include/items.php'); - - - -class Mood extends Controller { - - function init() { - - if(! local_channel()) - return; - - if(! Apps::system_app_installed(local_channel(), 'Mood')) { - return; - } - - $uid = local_channel(); - $channel = App::get_channel(); - $verb = ((isset($_GET['verb'])) ? notags(trim($_GET['verb'])) : ''); - - if(! $verb) - return; - - $verbs = get_mood_verbs(); - - if(! array_key_exists($verb,$verbs)) - return; - - $activity = ACTIVITY_MOOD . '#' . urlencode($verb); - - $parent = ((x($_GET,'parent')) ? intval($_GET['parent']) : 0); - - - logger('mood: verb ' . $verb, LOGGER_DEBUG); - - - if($parent) { - $r = q("select mid, owner_xchan, private, allow_cid, allow_gid, deny_cid, deny_gid - from item where id = %d and parent = %d and uid = %d limit 1", - intval($parent), - intval($parent), - intval($uid) - ); - if(count($r)) { - $parent_mid = $r[0]['mid']; - $private = $r[0]['item_private']; - $allow_cid = $r[0]['allow_cid']; - $allow_gid = $r[0]['allow_gid']; - $deny_cid = $r[0]['deny_cid']; - $deny_gid = $r[0]['deny_gid']; - } - } - else { - - $private = 0; - - $allow_cid = $channel['channel_allow_cid']; - $allow_gid = $channel['channel_allow_gid']; - $deny_cid = $channel['channel_deny_cid']; - $deny_gid = $channel['channel_deny_gid']; - } - - $poster = App::get_observer(); - - $uuid = item_message_id(); - $mid = z_root() . '/item/' . $uuid; - - $action = sprintf( t('%1$s is %2$s','mood'), '[zrl=' . $poster['xchan_url'] . ']' . $poster['xchan_name'] . '[/zrl]' , $verbs[$verb]); - - $arr = array(); - - $arr['aid'] = get_account_id(); - $arr['uid'] = $uid; - $arr['uuid'] = $uuid; - $arr['mid'] = $mid; - $arr['parent_mid'] = (($parent_mid) ? $parent_mid : $mid); - $arr['author_xchan'] = $poster['xchan_hash']; - $arr['owner_xchan'] = (($parent_mid) ? $r[0]['owner_xchan'] : $poster['xchan_hash']); - $arr['title'] = ''; - $arr['allow_cid'] = $allow_cid; - $arr['allow_gid'] = $allow_gid; - $arr['deny_cid'] = $deny_cid; - $arr['deny_gid'] = $deny_gid; - $arr['item_private'] = $private; - $arr['verb'] = $activity; - $arr['body'] = $action; - $arr['item_origin'] = 1; - $arr['item_wall'] = 1; - $arr['item_unseen'] = 1; - if(! $parent_mid) - $item['item_thread_top'] = 1; - - if ((! $arr['plink']) && intval($arr['item_thread_top'])) { - $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); - } - - - $post = item_store($arr); - $item_id = $post['item_id']; - - if($item_id) { - \Zotlabs\Daemon\Master::Summon(array('Notifier','activity', $item_id)); - } - - call_hooks('post_local_end', $arr); - - if($_SESSION['return_url']) - goaway(z_root() . '/' . $_SESSION['return_url']); - - return; - } - - - - function get() { - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } - - if(! Apps::system_app_installed(local_channel(), 'Mood')) { - //Do not display any associated widgets at this point - App::$pdl = ''; - $papp = Apps::get_papp('Mood'); - return Apps::app_render($papp, 'module'); - } - - nav_set_selected('Mood'); - - $parent = ((x($_GET,'parent')) ? intval($_GET['parent']) : '0'); - - $verbs = get_mood_verbs(); - - $shortlist = array(); - foreach($verbs as $k => $v) - if($v !== 'NOTRANSLATION') - $shortlist[] = array($k,$v); - - - $tpl = get_markup_template('mood_content.tpl'); - - $o = replace_macros($tpl,array( - '$title' => t('Mood'), - '$desc' => t('Set your current mood and tell your friends'), - '$verbs' => $shortlist, - '$parent' => $parent, - '$submit' => t('Submit'), - )); - - return $o; - - } - -} diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php index 0a490d1db..870a2cb79 100644 --- a/Zotlabs/Module/Photos.php +++ b/Zotlabs/Module/Photos.php @@ -1108,7 +1108,6 @@ class Photos extends \Zotlabs\Web\Controller { $conv_responses = array( 'like' => array('title' => t('Likes','title')),'dislike' => array('title' => t('Dislikes','title')), - 'agree' => array('title' => t('Agree','title')),'disagree' => array('title' => t('Disagree','title')), 'abstain' => array('title' => t('Abstain','title')), 'attendyes' => array('title' => t('Attending','title')), 'attendno' => array('title' => t('Not attending','title')), 'attendmaybe' => array('title' => t('Might attend','title')) ); diff --git a/Zotlabs/Module/Poke.php b/Zotlabs/Module/Poke.php deleted file mode 100644 index ee355f2a9..000000000 --- a/Zotlabs/Module/Poke.php +++ /dev/null @@ -1,205 +0,0 @@ -<?php -namespace Zotlabs\Module; /** @file */ - -use App; -use Zotlabs\Lib\Apps; -use Zotlabs\Lib\Activity; -use Zotlabs\Web\Controller; - -/** - * - * Poke, prod, finger, or otherwise do unspeakable things to somebody - who must be a connection in your address book - * This function can be invoked with the required arguments (verb and cid and private and possibly parent) silently via ajax or - * other web request. You must be logged in and connected to a channel. - * If the required arguments aren't present, we'll display a simple form to choose a recipient and a verb. - * parent is a special argument which let's you attach this activity as a comment to an existing conversation, which - * may have started with somebody else poking (etc.) somebody, but this isn't necessary. This can be used in the adult - * plugin version to have entire conversations where Alice poked Bob, Bob fingered Alice, Alice hugged Bob, etc. - * - * private creates a private conversation with the recipient. Otherwise your channel's default post privacy is used. - * - */ - -require_once('include/items.php'); - - -class Poke extends Controller { - - function init() { - - if(! local_channel()) - return; - - if(! Apps::system_app_installed(local_channel(), 'Poke')) { - return; - } - - $uid = local_channel(); - $channel = App::get_channel(); - - $verb = ((isset($_GET['verb'])) ? notags(trim($_GET['verb'])) : ''); - - if(! $verb) - return; - - $verbs = get_poke_verbs(); - - if(! array_key_exists($verb,$verbs)) - return; - - $contact_id = intval($_REQUEST['cid']); - - $xchan = trim($_REQUEST['xchan']); - - if(! ($contact_id || $xchan)) - return; - - $parent = ((x($_REQUEST,'parent')) ? intval($_REQUEST['parent']) : 0); - - logger('poke: verb ' . $verb . ' contact ' . $contact_id, LOGGER_DEBUG); - - - if($contact_id) { - $r = q("SELECT * FROM abook left join xchan on xchan_hash = abook_xchan where abook_id = %d and abook_channel = %d LIMIT 1", - intval($contact_id), - intval($uid) - ); - } - if($xchan) { - $r = q("SELECT * FROM xchan where xchan_hash like ( '%s' ) LIMIT 1", - dbesc($xchan . '%') - ); - } - - if(! $r) { - logger('poke: no target.'); - return; - } - - $target = $r[0]; - $parent_item = null; - - if($parent) { - $r = q("select mid, item_private, owner_xchan, allow_cid, allow_gid, deny_cid, deny_gid - from item where id = %d and parent = %d and uid = %d limit 1", - intval($parent), - intval($parent), - intval($uid) - ); - if($r) { - $parent_item = $r[0]; - $parent_mid = $r[0]['mid']; - $item_private = $r[0]['item_private']; - $allow_cid = $r[0]['allow_cid']; - $allow_gid = $r[0]['allow_gid']; - $deny_cid = $r[0]['deny_cid']; - $deny_gid = $r[0]['deny_gid']; - } - } - elseif($contact_id) { - - $item_private = ((x($_GET,'private')) ? intval($_GET['private']) : 0); - - $allow_cid = (($item_private) ? '<' . $target['abook_xchan']. '>' : $channel['channel_allow_cid']); - $allow_gid = (($item_private) ? '' : $channel['channel_allow_gid']); - $deny_cid = (($item_private) ? '' : $channel['channel_deny_cid']); - $deny_gid = (($item_private) ? '' : $channel['channel_deny_gid']); - } - - $arr['item_wall'] = 1; - $arr['owner_xchan'] = (($parent_item) ? $parent_item['owner_xchan'] : $channel['channel_hash']); - $arr['parent_mid'] = (($parent_mid) ? $parent_mid : ''); - $arr['title'] = ''; - $arr['allow_cid'] = $allow_cid; - $arr['allow_gid'] = $allow_gid; - $arr['deny_cid'] = $deny_cid; - $arr['deny_gid'] = $deny_gid; - $arr['verb'] = 'Create'; - $arr['item_private'] = $item_private; - $arr['obj_type'] = 'Note'; - $arr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t($verbs[$verb][0]) . ' ' . '[zrl=' . $target['xchan_url'] . ']' . $target['xchan_name'] . '[/zrl]'; - $arr['item_origin'] = 1; - $arr['item_unseen'] = 1; - if(! $parent_item) - $arr['item_thread_top'] = 1; - - $arr['obj'] = Activity::encode_item($arr); - - - post_activity_item($arr); - - return; - } - - - - function get() { - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } - - if(! Apps::system_app_installed(local_channel(), 'Poke')) { - //Do not display any associated widgets at this point - App::$pdl = ''; - $papp = Apps::get_papp('Poke'); - return Apps::app_render($papp, 'module'); - } - - nav_set_selected('Poke'); - - $name = ''; - $id = ''; - - if(isset($_REQUEST['c']) && intval($_REQUEST['c'])) { - $r = q("select abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash - where abook_id = %d and abook_channel = %d limit 1", - intval($_REQUEST['c']), - intval(local_channel()) - ); - if($r) { - $name = $r[0]['xchan_name']; - $id = $r[0]['abook_id']; - } - } - - $parent = ((x($_REQUEST,'parent')) ? intval($_REQUEST['parent']) : '0'); - - $verbs = get_poke_verbs(); - - $shortlist = array(); - foreach($verbs as $k => $v) - if($v[1] !== 'NOTRANSLATION') - $shortlist[] = array($k,$v[1]); - - - $poke_basic = get_config('system','poke_basic'); - if($poke_basic) { - $title = t('Poke'); - $desc = t('Poke somebody'); - } - else { - $title = t('Poke'); - $desc = t('Poke or ping somebody'); - } - - $o = replace_macros(get_markup_template('poke_content.tpl'),array( - '$title' => $title, - '$poke_basic' => $poke_basic, - '$desc' => $desc, - '$clabel' => t('Recipient'), - '$choice' => t('Choose action'), - '$verbs' => $shortlist, - '$parent' => $parent, - '$prv_desc' => t('Make this post private'), - '$private' => array('private', t('Make this post private'), false, ''), - '$submit' => t('Submit'), - '$name' => $name, - '$id' => $id - )); - - return $o; - - } -} diff --git a/Zotlabs/Module/Profile_photo.php b/Zotlabs/Module/Profile_photo.php index d7e2bbce1..dc47d213b 100644 --- a/Zotlabs/Module/Profile_photo.php +++ b/Zotlabs/Module/Profile_photo.php @@ -223,7 +223,7 @@ class Profile_photo extends Controller { intval(local_channel()) ); - send_profile_photo_activity($channel, $base_image, $profile); + profile_activity([t('Profile Photo')], $base_image['resource_id']); } else { q("update profile set photo = '%s', thumb = '%s' where id = %d and uid = %d", @@ -269,7 +269,6 @@ class Profile_photo extends Controller { // Update directory in background Master::Summon(['Directory', $channel['channel_id']]); - } else notice(t('Unable to process image') . EOL); diff --git a/Zotlabs/Module/Profiles.php b/Zotlabs/Module/Profiles.php index ce496252b..15252d6e6 100644 --- a/Zotlabs/Module/Profiles.php +++ b/Zotlabs/Module/Profiles.php @@ -3,10 +3,6 @@ namespace Zotlabs\Module; use Zotlabs\Lib\Libsync; -require_once('include/channel.php'); -require_once('include/selectors.php'); - - class Profiles extends \Zotlabs\Web\Controller { function init() { @@ -492,7 +488,7 @@ class Profiles extends \Zotlabs\Web\Controller { $publish = ((x($_POST, 'profile_in_directory') && (intval($_POST['profile_in_directory']) == 1)) ? 1 : 0); - profile_activity($changes,$value); + profile_activity($changes, $value); } diff --git a/Zotlabs/Module/React.php b/Zotlabs/Module/React.php index f80b04a3f..6a3b525b2 100644 --- a/Zotlabs/Module/React.php +++ b/Zotlabs/Module/React.php @@ -2,82 +2,79 @@ namespace Zotlabs\Module; +use App; +use Zotlabs\Web\Controller; +use Zotlabs\Lib\Activity; +use Zotlabs\Daemon\Master; -class React extends \Zotlabs\Web\Controller { +class React extends Controller { function get() { - if(! local_channel()) + if (!local_channel()) { return; + } $sys = get_sys_channel(); - $channel = \App::get_channel(); + $channel = App::get_channel(); $postid = $_REQUEST['postid']; - if(! $postid) + if (!$postid) { return; + } $emoji = $_REQUEST['emoji']; + if (!$emoji) { + return; + } - if($_REQUEST['emoji']) { + $i = q("select * from item where id = %d and uid = %d", + intval($postid), + intval(local_channel()) + ); + if (!$i) { $i = q("select * from item where id = %d and uid = %d", intval($postid), - intval(local_channel()) + intval($sys['channel_id']) ); - if(! $i) { - $i = q("select * from item where id = %d and uid = %d", - intval($postid), - intval($sys['channel_id']) - ); - - if($i) { - $i = [ copy_of_pubitem($channel, $i[0]['mid']) ]; - $postid = (($i) ? $i[0]['id'] : 0); - } - } - - if(! $i) { - return; - } - - $uuid = item_message_id(); - - $n = array(); - $n['aid'] = $channel['channel_account_id']; - $n['uid'] = $channel['channel_id']; - $n['item_origin'] = true; - $n['item_type'] = $i[0]['item_type']; - $n['parent'] = $postid; - $n['parent_mid'] = $i[0]['mid']; - $n['uuid'] = $uuid; - $n['mid'] = z_root() . '/item/' . $uuid; - $n['verb'] = ACTIVITY_REACT . '#' . $emoji; - $n['body'] = "\n\n[zmg=32x32]" . z_root() . '/images/emoji/' . $emoji . '.png[/zmg]' . "\n\n"; - $n['author_xchan'] = $channel['channel_hash']; - - $n['tgt_type'] = 'Image'; - $n['target'] = [ - 'type' => 'Image', - 'name' => $emoji, - 'url' => z_root() . '/images/emoji/' . $emoji . '.png' - ]; - - - $x = item_store($n); - - retain_item($postid); - - if($x['success']) { - $nid = $x['item_id']; - \Zotlabs\Daemon\Master::Summon(array('Notifier','like',$nid)); + if ($i) { + $i = [ copy_of_pubitem($channel, $i[0]['mid']) ]; + $postid = (($i) ? $i[0]['id'] : 0); } + } + if (!$i) { + return; } + $uuid = item_message_id(); + + $n['aid'] = $channel['channel_account_id']; + $n['uid'] = $channel['channel_id']; + $n['item_origin'] = true; + $n['item_type'] = $i[0]['item_type']; + $n['parent'] = $postid; + $n['parent_mid'] = $i[0]['mid']; + $n['uuid'] = $uuid; + $n['mid'] = z_root() . '/item/' . $uuid; + $n['verb'] = 'Create'; + $n['body'] = '[zmg=32x32]' . z_root() . '/images/emoji/' . $emoji . '.png[/zmg]'; + $n['author_xchan'] = $channel['channel_hash']; + $n['obj'] = Activity::fetch_item(['id' => $item['mid']]); + $n['obj_type'] = ((array_path_exists('obj/type', $n)) ? $n['obj']['type'] : EMPTY_STR); + + $x = item_store($n); + + retain_item($postid); + + if ($x['success']) { + $nid = $x['item_id']; + Master::Summon(['Notifier', 'like', $nid]); + } } -}
\ No newline at end of file +} diff --git a/Zotlabs/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php index a621f3608..71fc16aae 100644 --- a/Zotlabs/Module/Sse_bs.php +++ b/Zotlabs/Module/Sse_bs.php @@ -194,7 +194,7 @@ class Sse_bs extends Controller { $item_normal = item_normal(); // Filter internal follow activities and strerams add/remove activities - $item_normal .= " AND verb NOT IN ('Add', 'Remove', '" . dbesc(ACTIVITY_FOLLOW) . "') "; + $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; if ($notifications) { $items = q("SELECT * FROM item @@ -277,7 +277,7 @@ class Sse_bs extends Controller { $item_normal = item_normal(); // Filter internal follow activities and strerams add/remove activities - $item_normal .= " AND verb NOT IN ('Add', 'Remove', '" . dbesc(ACTIVITY_FOLLOW) . "') "; + $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; if ($notifications) { $items = q("SELECT * FROM item @@ -360,7 +360,7 @@ class Sse_bs extends Controller { $item_normal = item_normal(); // Filter internal follow activities and strerams add/remove activities - $item_normal .= " AND verb NOT IN ('Add', 'Remove', '" . dbesc(ACTIVITY_FOLLOW) . "') "; + $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; if ($notifications) { $items = q("SELECT * FROM item @@ -467,7 +467,7 @@ class Sse_bs extends Controller { $item_normal = item_normal(); // Filter internal follow activities and strerams add/remove activities - $item_normal .= " AND verb NOT IN ('Add', 'Remove', '" . dbesc(ACTIVITY_FOLLOW) . "') "; + $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; if ($notifications) { $items = q("SELECT * FROM item @@ -663,7 +663,7 @@ class Sse_bs extends Controller { $item_normal = item_normal(); // Filter internal follow activities and strerams add/remove activities - $item_normal .= " AND verb NOT IN ('Add', 'Remove', '" . dbesc(ACTIVITY_FOLLOW) . "') "; + $item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') "; $r = q("SELECT * FROM item WHERE (verb = 'Create' OR verb = '%s') diff --git a/Zotlabs/Module/Subthread.php b/Zotlabs/Module/Subthread.php index 5ddcaffc6..b927ee480 100644 --- a/Zotlabs/Module/Subthread.php +++ b/Zotlabs/Module/Subthread.php @@ -24,9 +24,9 @@ class Subthread extends \Zotlabs\Web\Controller { $item_id = ((argc() > 2) ? notags(trim(argv(2))) : 0); if(argv(1) === 'sub') - $activity = ACTIVITY_FOLLOW; + $activity = 'Follow'; elseif(argv(1) === 'unsub') - $activity = ACTIVITY_UNFOLLOW; + $activity = 'Ignore'; $i = q("select * from item where id = %d and uid = %d", @@ -121,9 +121,9 @@ class Subthread extends \Zotlabs\Web\Controller { if(! intval($item['item_thread_top'])) $post_type = 'comment'; - if($activity === ACTIVITY_FOLLOW) + if($activity === 'Follow') $bodyverb = t('%1$s is following %2$s\'s %3$s'); - if($activity === ACTIVITY_UNFOLLOW) + if($activity === 'Ignore') $bodyverb = t('%1$s stopped following %2$s\'s %3$s'); $arr = array(); diff --git a/Zotlabs/Storage/Directory.php b/Zotlabs/Storage/Directory.php index 683887b31..333251f69 100644 --- a/Zotlabs/Storage/Directory.php +++ b/Zotlabs/Storage/Directory.php @@ -173,7 +173,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo intval($this->auth->owner_id) ); - $x = attach_syspaths($this->auth->owner_id,$this->folder_hash); + $x = attach_syspaths($this->auth->owner_id, $this->folder_hash); $y = q("update attach set display_path = '%s' where hash = '%s' and uid = %d", dbesc($x['path']), @@ -181,6 +181,20 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo intval($this->auth->owner_id) ); + $z = q("select hash from attach where folder = '%s' and uid = %d", + dbesc($this->folder_hash), + intval($this->auth->owner_id) + ); + + if($z) { + foreach ($z as $zz) { + $rs = attach_move($this->auth->owner_id, $zz['hash'], $this->folder_hash, '', false); + if(!$rs['success']) { + break; + } + } + } + $ch = channelx_by_n($this->auth->owner_id); if ($ch) { $sync = attach_export_data($ch, $this->folder_hash); @@ -481,7 +495,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo } - public function moveInto($targetName,$sourcePath, DAV\INode $sourceNode) { + public function moveInto($targetName, $sourcePath, DAV\INode $sourceNode) { $channel_id = $this->auth->owner_id; // Files have $sourceNode->data['hash'] set. For directories rely on $sourceNode->folder_hash. diff --git a/Zotlabs/Update/_1263.php b/Zotlabs/Update/_1263.php new file mode 100644 index 000000000..bd12e7321 --- /dev/null +++ b/Zotlabs/Update/_1263.php @@ -0,0 +1,26 @@ +<?php + +namespace Zotlabs\Update; + +class _1263 { + + function run() { + + dbq("START TRANSACTION"); + + $poke = hash('whirlpool', 'Poke'); + $mood = hash('whirlpool', 'Mood'); + + $r = dbq("DELETE FROM app WHERE (app_id = '$poke' OR app_id = '$mood') AND app_system = 1"); + + if($r) { + dbq("COMMIT"); + return UPDATE_SUCCESS; + } + + dbq("ROLLBACK"); + return UPDATE_FAILED; + + } + +} diff --git a/Zotlabs/Widget/Messages.php b/Zotlabs/Widget/Messages.php index 519bb27fe..8654d8e8f 100644 --- a/Zotlabs/Widget/Messages.php +++ b/Zotlabs/Widget/Messages.php @@ -62,7 +62,7 @@ class Messages { $channel = App::get_channel(); $item_normal = item_normal(); // Filter internal follow activities and strerams add/remove activities - $item_normal .= " and item.verb not in ('Add', 'Remove', '" . ACTIVITY_FOLLOW . "') "; + $item_normal .= " and item.verb not in ('Add', 'Remove', 'Follow', 'Ignore', '" . ACTIVITY_FOLLOW . "') "; $item_normal_i = str_replace('item.', 'i.', $item_normal); $item_normal_c = str_replace('item.', 'c.', $item_normal); $entries = []; @@ -85,7 +85,7 @@ class Messages { } if($author) { - $author_sql = " AND (i.owner_xchan = '" . protect_sprintf(dbesc($author)) . "' OR i.source_xchan = '" . protect_sprintf(dbesc($author)) . "') "; + $author_sql = " AND (i.owner_xchan = '" . protect_sprintf(dbesc($author)) . "') "; } switch($type) { diff --git a/Zotlabs/Widget/Pinned.php b/Zotlabs/Widget/Pinned.php index 3c6484b36..1380c156b 100644 --- a/Zotlabs/Widget/Pinned.php +++ b/Zotlabs/Widget/Pinned.php @@ -77,17 +77,6 @@ class Pinned { } } - $consensus = (intval($item['item_consensus']) ? true : false); - if($consensus) { - $conv_responses['agree'] = [ 'title' => t('Agree','title') ]; - $conv_responses['disagree'] = [ 'title' => t('Disagree','title') ]; - $conv_responses['abstain'] = [ 'title' => t('Abstain','title') ]; - if($commentable && $observer) { - $conlabels = [ t('I agree'), t('I disagree'), t('I abstain') ]; - $canvote = true; - } - } - $this->activity($item, $conv_responses); $verified = (intval($item['item_verified']) ? t('Message signature validated') : ''); @@ -234,15 +223,6 @@ class Pinned { case 'dislike': $verb_sql = " AND verb IN ('Dislike', '" . ACTIVITY_DISLIKE . "') "; break; - case 'agree': - $verb_sql = " AND verb = '" . ACTIVITY_AGREE . "' "; - break; - case 'disagree': - $verb_sql = " AND verb = '" . ACTIVITY_DISAGREE . "' "; - break; - case 'abstain': - $verb_sql = " AND verb = '" . ACTIVITY_ABSTAIN . "' "; - break; case 'attendyes': $verb_sql = " AND verb IN ('Accept', '" . ACTIVITY_ATTEND . "') "; break; diff --git a/app/mood.apd b/app/mood.apd deleted file mode 100644 index 81b53a2ce..000000000 --- a/app/mood.apd +++ /dev/null @@ -1,7 +0,0 @@ -version: 3 -url: $baseurl/mood -requires: local_channel -name: Mood -photo: icon:smile-o -categories: Social -desc: Set your current mood and tell your friends. diff --git a/app/poke.apd b/app/poke.apd deleted file mode 100644 index 490f98740..000000000 --- a/app/poke.apd +++ /dev/null @@ -1,7 +0,0 @@ -version: 3 -url: $baseurl/poke -requires: local_channel -name: Poke -photo: icon:hand-o-right -categories: Social -desc: Poke somebody in your addressbook. @@ -60,12 +60,14 @@ require_once('include/bbcode.php'); require_once('include/items.php'); require_once('include/conversation.php'); require_once('include/acl_selectors.php'); +require_once('include/selectors.php'); +require_once('include/activities.php'); define('PLATFORM_NAME', 'hubzilla'); -define('STD_VERSION', '8.9.8'); +define('STD_VERSION', '8.9.9'); define('ZOT_REVISION', '6.0'); -define('DB_UPDATE_VERSION', 1262); +define('DB_UPDATE_VERSION', 1263); define('PROJECT_BASE', __DIR__); @@ -488,45 +490,46 @@ define('NAMESPACE_YMEDIA', 'http://search.yahoo.com/mrss/'); define('ACTIVITYSTREAMS_JSONLD_REV', 'https://www.w3.org/ns/activitystreams'); define('ZOT_APSCHEMA_REV', '/apschema/v1.11'); + /** * activity stream defines */ define('ACTIVITY_PUBLIC_INBOX', 'https://www.w3.org/ns/activitystreams#Public'); -define('ACTIVITY_REACT', NAMESPACE_ZOT . '/activity/react'); +define('ACTIVITY_REACT', NAMESPACE_ZOT . '/activity/react'); // deprecated define('ACTIVITY_LIKE', NAMESPACE_ACTIVITY_SCHEMA . 'like'); // AS2 Like define('ACTIVITY_DISLIKE', NAMESPACE_ZOT . '/activity/dislike'); // AS2 Dislike -define('ACTIVITY_AGREE', NAMESPACE_ZOT . '/activity/agree'); // deprecated - remove from code? -define('ACTIVITY_DISAGREE', NAMESPACE_ZOT . '/activity/disagree'); // deprecated - remove from code? -define('ACTIVITY_ABSTAIN', NAMESPACE_ZOT . '/activity/abstain'); // deprecated - remove from code? +define('ACTIVITY_AGREE', NAMESPACE_ZOT . '/activity/agree'); // deprecated +define('ACTIVITY_DISAGREE', NAMESPACE_ZOT . '/activity/disagree'); // deprecated +define('ACTIVITY_ABSTAIN', NAMESPACE_ZOT . '/activity/abstain'); // deprecated define('ACTIVITY_ATTEND', NAMESPACE_ZOT . '/activity/attendyes'); // AS2 Accept define('ACTIVITY_ATTENDNO', NAMESPACE_ZOT . '/activity/attendno'); // AS2 Reject define('ACTIVITY_ATTENDMAYBE', NAMESPACE_ZOT . '/activity/attendmaybe'); // AS2 TentativeAccept -define('ACTIVITY_FRIEND', NAMESPACE_ACTIVITY_SCHEMA . 'make-friend'); // deprecated - remove from code? +define('ACTIVITY_FRIEND', NAMESPACE_ACTIVITY_SCHEMA . 'make-friend'); // deprecated -define('ACTIVITY_FOLLOW', NAMESPACE_ACTIVITY_SCHEMA . 'follow'); -define('ACTIVITY_UNFOLLOW', NAMESPACE_ACTIVITY_SCHEMA . 'stop-following'); +define('ACTIVITY_FOLLOW', NAMESPACE_ACTIVITY_SCHEMA . 'follow'); // AS2 Follow +define('ACTIVITY_UNFOLLOW', NAMESPACE_ACTIVITY_SCHEMA . 'stop-following'); // AS2 Ignore define('ACTIVITY_POST', NAMESPACE_ACTIVITY_SCHEMA . 'post'); // AS2 Create -define('ACTIVITY_UPDATE', NAMESPACE_ACTIVITY_SCHEMA . 'update'); +define('ACTIVITY_UPDATE', NAMESPACE_ACTIVITY_SCHEMA . 'update'); // AS2 Update define('ACTIVITY_TAG', NAMESPACE_ACTIVITY_SCHEMA . 'tag'); // unused define('ACTIVITY_SHARE', 'Announce'); -define('ACTIVITY_CREATE', NAMESPACE_ACTIVITY_SCHEMA . 'create'); // unused +define('ACTIVITY_CREATE', NAMESPACE_ACTIVITY_SCHEMA . 'create'); // deprecated define('ACTIVITY_DELETE', NAMESPACE_ACTIVITY_SCHEMA . 'delete'); // AS2 Delete -define('ACTIVITY_POKE', NAMESPACE_ZOT . '/activity/poke'); -define('ACTIVITY_MOOD', NAMESPACE_ZOT . '/activity/mood'); +define('ACTIVITY_POKE', NAMESPACE_ZOT . '/activity/poke'); // deprecated +define('ACTIVITY_MOOD', NAMESPACE_ZOT . '/activity/mood'); // deprecated define('ACTIVITY_OBJ_COMMENT', NAMESPACE_ACTIVITY_SCHEMA . 'comment'); // AS2 Note define('ACTIVITY_OBJ_NOTE', NAMESPACE_ACTIVITY_SCHEMA . 'note'); // AS2 Note @@ -536,7 +539,7 @@ define('ACTIVITY_OBJ_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'photo'); // AS2 Image define('ACTIVITY_OBJ_EVENT', NAMESPACE_ACTIVITY_SCHEMA . 'event'); // AS2 Event define('ACTIVITY_OBJ_TAGTERM', NAMESPACE_ZOT . '/activity/tagterm'); // unused -define('ACTIVITY_OBJ_PROFILE', NAMESPACE_ZOT . '/activity/profile'); // AS2 Profile (broken) +define('ACTIVITY_OBJ_PROFILE', NAMESPACE_ZOT . '/activity/profile'); // AS2 Profile define('ACTIVITY_OBJ_THING', NAMESPACE_ZOT . '/activity/thing'); // AS2 Page??? (broken) diff --git a/include/activities.php b/include/activities.php index f5f0e55da..c06a8f6c4 100644 --- a/include/activities.php +++ b/include/activities.php @@ -1,100 +1,83 @@ <?php /** @file */ -function profile_activity($changed, $value) { - - // TODO: we should probably send an Update/Profile or Person activity here. - // We could also just send a Note with some changed info? - // Profiles are currently a hubzilla specific thing. - return; +use Zotlabs\Lib\Activity; +use Zotlabs\Daemon\Master; - if(! local_channel() || ! is_array($changed) || ! count($changed)) - return; +function profile_activity($changed, $value) { - if(! get_pconfig(local_channel(),'system','post_profilechange')) + if (!local_channel() || !is_array($changed) || !count($changed)) { return; + } - require_once('include/items.php'); - - $self = App::get_channel(); - - if(! count($self)) + if (!get_pconfig(local_channel(), 'system', 'post_profilechange')) { return; + } - $arr = array(); - $arr['uuid'] = item_message_id(); - $arr['mid'] = $arr['parent_mid'] = z_root() . '/item/' . $arr['uuid']; - $arr['uid'] = local_channel(); - $arr['aid'] = $self['channel_account_id']; - $arr['owner_xchan'] = $arr['author_xchan'] = $self['xchan_hash']; - - $arr['item_wall'] = 1; - $arr['item_origin'] = 1; - $arr['item_thread_top'] = 1; - $arr['verb'] = ACTIVITY_UPDATE; - $arr['obj_type'] = 'Profile'; - - $arr['plink'] = z_root() . '/channel/' . $self['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); + $channel = App::get_channel(); - $A = '[url=' . z_root() . '/channel/' . $self['channel_address'] . ']' . $self['channel_name'] . '[/url]'; + $arr['verb'] = 'Update'; + $arr['obj_type'] = 'Profile'; + $channel_link = '[url=' . z_root() . '/channel/' . $channel['channel_address'] . ']' . $channel['channel_name'] . '[/url]'; $changes = ''; $t = count($changed); $z = 0; - foreach($changed as $ch) { - if(strlen($changes)) { - if ($z == ($t - 1)) + $photo = false; + foreach ($changed as $ch) { + if (strlen($changes)) { + if ($z == ($t - 1)) { $changes .= t(' and '); - else - $changes .= ', '; + } else { + $changes .= t(', '); + } } - $z ++; + + if (in_array($ch, [t('Profile Photo'), t('Cover Photo')])) { + $photo = true; + $photo_size = (($ch === t('Profile Photo')) ? 4 : 8); + } + + $z++; $changes .= $ch; } - $prof = '[url=' . z_root() . '/profile/' . $self['channel_address'] . ']' . t('public profile') . '[/url]'; + $profile_link = '[url=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . t('public profile') . '[/url]'; - if($t == 1 && strlen($value)) { + if ($t == 1 && strlen($value)) { // if it's a url, the HTML quotes will mess it up, so link it and don't try and zidify it because we don't know what it points to. - $value = preg_replace_callback("/([^\]\='".'"'."]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\+\,]+)/ismu", 'red_zrl_callback', $value); + $value = preg_replace_callback("/([^='" . '"' . "]|^|#\^)(https?:\/\/[a-zA-Z0-9\pL:\/\-?&;.=@_~#%\$!+,]+)/ismu", 'red_zrl_callback', $value); // take out the bookmark indicator - if(substr($value,0,2) === '#^') - $value = str_replace('#^','',$value); - - $message = sprintf( t('%1$s changed %2$s to “%3$s”'), $A, $changes, $value); - $message .= "\n\n" . sprintf( t('Visit %1$s\'s %2$s'), $A, $prof); - } - else - $message = sprintf( t('%1$s has an updated %2$s, changing %3$s.'), $A, $prof, $changes); - - - $arr['body'] = $message; - - $links = array(); - $links[] = array('rel' => 'alternate', 'type' => 'text/html', - 'href' => z_root() . '/profile/' . $self['channel_address']); - $links[] = array('rel' => 'photo', 'type' => $self['xchan_photo_mimetype'], - 'href' => $self['xchan_photo_l']); + if (str_starts_with($value, '#^')) { + $value = str_replace('#^', '', $value); + } - $arr['object'] = json_encode(array( - 'type' => 'Profile', - 'title' => $self['channel_name'], - 'id' => $self['xchan_url'] . '/' . $self['xchan_hash'], - 'link' => $links - )); + if ($photo) { + $value = "\n\n" . '[zmg=' . z_root() . '/photo/' . $value . '-' . $photo_size . ']' . $ch . ' ' . $channel['xchan_name'] . '[/zmg]'; + } + else { + $value = '"' . $value . '"'; + } + $message = sprintf(t('%1$s %2$s has been updated to %3$s.'), $channel_link . '\'s' . (($photo) ? '' : ' ' . $profile_link), strtolower($changes), $value); - $arr['allow_cid'] = $self['channel_allow_cid']; - $arr['allow_gid'] = $self['channel_allow_gid']; - $arr['deny_cid'] = $self['channel_deny_cid']; - $arr['deny_gid'] = $self['channel_deny_gid']; + } else { + $message = sprintf(t('%1$s updated the %2$s. Changed %3$s.'), $channel_link, $profile_link, strtolower($changes)); + } - $res = item_store($arr); - $i = $res['item_id']; + $arr['body'] = $message; - if($i) { - // FIXME - limit delivery in notifier.php to those specificed in the perms argument - Zotlabs\Daemon\Master::Summon(array('Notifier','activity', $i, 'PERMS_R_PROFILE')); - } + $arr['obj'] = [ + 'type' => 'Profile', + 'content' => bbcode($message), + 'source' => [ + 'content' => $message, + 'mediaType' => 'text/bbcode' + ], + 'describes' => Activity::encode_person($channel), + 'url' => z_root() . '/profile/' . $channel['channel_address'] + ]; + + post_activity_item($arr); } diff --git a/include/channel.php b/include/channel.php index b8affa3ca..a82794bfd 100644 --- a/include/channel.php +++ b/include/channel.php @@ -1771,6 +1771,7 @@ function advanced_profile() { if(App::$profile['gender']) $profile['gender'] = array( t('Gender:'), App::$profile['gender'] ); $ob_hash = get_observer_hash(); +/* TODO: AS2 compatibility if($ob_hash && perm_is_allowed(App::$profile['profile_uid'],$ob_hash,'post_like')) { $profile['canlike'] = true; $profile['likethis'] = t('Like this channel'); @@ -1790,7 +1791,7 @@ function advanced_profile() { foreach($likers as $l) $profile['likers'][] = array('name' => $l['xchan_name'],'photo' => zid($l['xchan_photo_s']), 'url' => zid($l['xchan_url'])); } - +*/ if((App::$profile['dob']) && (App::$profile['dob'] != '0000-00-00')) { $val = ''; diff --git a/include/contact_widgets.php b/include/contact_widgets.php index 182f674ca..c05ecaf7c 100644 --- a/include/contact_widgets.php +++ b/include/contact_widgets.php @@ -85,7 +85,7 @@ function categories_widget($baseurl,$selected = '') { AND term.otype = %d AND item.owner_xchan = '%s' AND item.item_wall = 1 - AND item.verb != '%s' + AND item.verb NOT IN ('Update', '%s') $item_normal $sql_extra ORDER BY term.term ASC", diff --git a/include/conversation.php b/include/conversation.php index 7074e1c36..79fe12d54 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -217,96 +217,6 @@ function localize_item(&$item){ } - if (activity_match($item['verb'], ACTIVITY_FRIEND)) { - - if ($item['obj_type'] == "" || !in_array($item['obj_type'], ['Person', ACTIVITY_OBJ_PERSON])) - return; - - $Aname = $item['author']['xchan_name']; - $Alink = $item['author']['xchan_url']; - - - $obj= json_decode($item['obj'],true); - - $Blink = $Bphoto = ''; - - if($obj['link']) { - $Blink = get_rel_link($obj['link'],'alternate'); - $Bphoto = get_rel_link($obj['link'],'photo'); - } - $Bname = $obj['title']; - - - $A = '[zrl=' . chanlink_url($Alink) . '][bdi]' . $Aname . '[/bdi][/zrl]'; - $B = '[zrl=' . chanlink_url($Blink) . '][bdi]' . $Bname . '[/bdi][/zrl]'; - if ($Bphoto!="") $Bphoto = '[zrl=' . chanlink_url($Blink) . '][zmg=80x80]' . $Bphoto . '[/zmg][/zrl]'; - - $item['shortlocalize'] = sprintf( t('%1$s is now connected with %2$s'), '[bdi]' . $Aname . '[/bdi]', '[bdi]' . $Bname . '[/bdi]'); - - $item['body'] = $item['localize'] = sprintf( t('%1$s is now connected with %2$s'), $A, $B); - $item['body'] .= "\n\n\n" . $Bphoto; - } - - if (stristr($item['verb'], ACTIVITY_POKE)) { - - /** @FIXME for obscured private posts, until then leave untranslated */ - return; - - $verb = urldecode(substr($item['verb'],strpos($item['verb'],'#')+1)); - if(! $verb) - return; - - if ($item['obj_type']=="" || !in_array($item['obj_type'], ['Person', ACTIVITY_OBJ_PERSON])) - return; - - $Aname = $item['author']['xchan_name']; - $Alink = $item['author']['xchan_url']; - - $obj= json_decode($item['obj'],true); - - $Blink = $Bphoto = ''; - - if($obj['link']) { - $Blink = get_rel_link($obj['link'],'alternate'); - $Bphoto = get_rel_link($obj['link'],'photo'); - } - $Bname = $obj['title']; - - $A = '[zrl=' . chanlink_url($Alink) . '][bdi]' . $Aname . '[/bdi][/zrl]'; - $B = '[zrl=' . chanlink_url($Blink) . '][bdi]' . $Bname . '[/bdi][/zrl]'; - if ($Bphoto!="") $Bphoto = '[zrl=' . chanlink_url($Blink) . '][zmg=80x80]' . $Bphoto . '[/zmg][/zrl]'; - - // we can't have a translation string with three positions but no distinguishable text - // So here is the translate string. - - $txt = t('%1$s poked %2$s'); - - // now translate the verb - - $txt = str_replace( t('poked'), t($verb), $txt); - - // then do the sprintf on the translation string - - $item['shortlocalize'] = sprintf($txt, '[bdi]' . $Aname . '[/bdi]', '[bdi]' . $Bname . '[/bdi]'); - - $item['body'] = $item['localize'] = sprintf($txt, $A, $B); - $item['body'] .= "\n\n\n" . $Bphoto; - } - if (stristr($item['verb'],ACTIVITY_MOOD)) { - $verb = urldecode(substr($item['verb'],strpos($item['verb'],'#')+1)); - if(! $verb) - return; - - $Aname = $item['author']['xchan_name']; - $Alink = $item['author']['xchan_url']; - - $A = '[zrl=' . chanlink_url($Alink) . '][bdi]' . $Aname . '[/bdi][/zrl]'; - - $txt = t('%1$s is %2$s','mood'); - - $item['body'] = sprintf($txt, $A, t($verb)); - } - } /** @@ -345,7 +255,7 @@ function count_descendants($item) { * @return boolean */ function visible_activity($item) { - $hidden_activities = ['Like', 'Dislike', 'Accept', 'Reject', 'TentativeAccept', ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_SHARE, ACTIVITY_AGREE, ACTIVITY_DISAGREE, ACTIVITY_ABSTAIN, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE]; + $hidden_activities = ['Like', 'Dislike', 'Accept', 'Reject', 'TentativeAccept', ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_SHARE, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE]; if(intval($item['item_notshown'])) return false; @@ -553,9 +463,6 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa $conv_responses = [ 'like' => ['title' => t('Likes','title')], 'dislike' => ['title' => t('Dislikes','title')], - 'agree' => ['title' => t('Agree','title')], - 'disagree' => ['title' => t('Disagree','title')], - 'abstain' => ['title' => t('Abstain','title')], 'attendyes' => ['title' => t('Attending','title')], 'attendno' => ['title' => t('Not attending','title')], 'attendmaybe' => ['title' => t('Might attend','title')], @@ -973,13 +880,10 @@ function thread_author_menu($item, $mode = '') { } } - $contact_url = ''; $posts_link = ''; - $poke_link = ''; if($contact) { - $poke_link = ((Apps::system_app_installed($local_channel, 'Poke')) ? z_root() . '/poke/?f=&c=' . $contact['abook_id'] : ''); if (isset($contact['abook_self']) && !intval($contact['abook_self'])) $contact_url = z_root() . '/connections#' . $contact['abook_id']; $posts_link = z_root() . '/network/?cid=' . $contact['abook_id']; @@ -1033,18 +937,6 @@ function thread_author_menu($item, $mode = '') { ]; } - if($poke_link) { - $menu[] = [ - 'menu' => 'poke', - 'title' => t('Poke'), - 'icon' => 'fw', - 'action' => '', - 'href' => $poke_link, - 'data' => '', - 'class' => '' - ]; - } - $args = [ 'item' => $item, 'mode' => $mode, 'menu' => $menu ]; call_hooks('thread_author_menu', $args); @@ -1082,15 +974,6 @@ function builtin_activity_puller($item, &$conv_responses) { case 'dislike': $verb = ['Dislike', ACTIVITY_DISLIKE]; break; - case 'agree': - $verb = ACTIVITY_AGREE; - break; - case 'disagree': - $verb = ACTIVITY_DISAGREE; - break; - case 'abstain': - $verb = ACTIVITY_ABSTAIN; - break; case 'attendyes': $verb = ['Accept', ACTIVITY_ATTEND]; break; @@ -1112,6 +995,7 @@ function builtin_activity_puller($item, &$conv_responses) { } if((activity_match($item['verb'], $verb)) && ($item['id'] != $item['parent'])) { + $name = (($item['author']['xchan_name']) ? $item['author']['xchan_name'] : t('Unknown')); $moderate = ((intval($item['item_blocked']) === ITEM_MODERATED) ? '<a href="moderate/' . $item['id'] . '/approve" onclick="moderate_approve(' . $item['id'] . '); return false;" class="text-success pe-2" title="' . t('Approve this item') . '"><i class="fa fa-check" ></i></a><a href="moderate/' . $item['id'] . '/drop" onclick="moderate_drop(' . $item['id'] . '); return false;" class="text-danger pe-2" title="' . t('Delete this item') . '"><i class="fa fa-trash-o" ></i></a>' : ''); @@ -1680,15 +1564,6 @@ function get_response_button_text($v,$count) { case 'attendmaybe': return ['label' => tt('Undecided','Undecided',$count,'noun'), 'icon' => 'calendar-o', 'class' => 'attendmaybe']; break; - case 'agree': - return ['label' => tt('Agree','Agrees',$count,'noun'), 'icon' => '', 'class' => '']; - break; - case 'disagree': - return ['label' => tt('Disagree','Disagrees',$count,'noun'), 'icon' => '', 'class' => '']; - break; - case 'abstain': - return ['label' => tt('Abstain','Abstains',$count,'noun'), 'icon' => '', 'class' => '']; - break; default: return ''; break; diff --git a/include/dba/dba_transaction.php b/include/dba/dba_transaction.php new file mode 100644 index 000000000..02e9945ca --- /dev/null +++ b/include/dba/dba_transaction.php @@ -0,0 +1,64 @@ +<?php +/** + * Class to represent a database transaction. + * + * A database transaction is initiated upon construction of an object of this + * class. The transaction will be automatically rolled back upon destruction + * unless it has been explicitly committed by calling the `commit` method. + * + * Wrapping multiple database operation within a transaction ensures that all + * (or none) of the operations are successfully completed at the same time. + * + * If a transaction is already active when constructing an object of this + * class, it will _not_ try to initiate a transaction, but constructs an object + * that will in practice be a stub. This prevents that "nested" transactions + * will cause problems with the existing active transaction. + * + * It also means that any rollbacks or commits perfomed on the "nested" + * transaction will be ignored, and postponed to the outer transaction is + * committed or rolled back. + * + * Also note that any modification to the database schema will implicitly + * commit active transactions in most cases, so be careful about relying on + * transactions in those cases. + * + * @Note This class assumes the actual underlying database driver is PDO. + */ +class DbaTransaction { + private bool $committed = false; + private bool $active = false; + + /** + * Creates a database transaction object. + * + * If a transaction is already active for this db connection, + * no transaction is initiated, and the constructed object will + * not perform any commit or rollback actions. + */ + public function __construct(private dba_driver $dba) { + if (! $this->dba->db->inTransaction()) { + $this->active = $this->dba->db->beginTransaction(); + } + } + + /** + * Roll back the transaction if it is active and not already committed. + */ + public function __destruct() { + if ($this->active && ! $this->committed) { + $this->dba->db->rollBack(); + } + } + + /** + * Commit the transaction if active. + * + * This will also mark the transaction as committed, preventing it from + * being attempted rolled back on destruction. + */ + public function commit(): void { + if ($this->active && ! $this->committed) { + $this->committed = $this->dba->db->commit(); + } + } +} diff --git a/include/items.php b/include/items.php index f689cc7b5..e26366af5 100644 --- a/include/items.php +++ b/include/items.php @@ -459,7 +459,7 @@ function post_activity_item($arr, $allow_code = false, $deliver = true) { if(! $arr['mid']) { - $arr['uuid'] = ((x($arr,'uuid')) ? $arr['uuid'] : item_message_id()); + $arr['uuid'] = ((x($arr,'uuid')) ? $arr['uuid'] : new_uuid()); } $arr['mid'] = ((x($arr,'mid')) ? $arr['mid'] : z_root() . '/item/' . $arr['uuid']); $arr['parent_mid'] = ((x($arr,'parent_mid')) ? $arr['parent_mid'] : $arr['mid']); @@ -520,7 +520,7 @@ function post_activity_item($arr, $allow_code = false, $deliver = true) { return $ret; if($post_id && $deliver) { - Master::Summon([ 'Notifier','activity',$post_id ]); + Master::Summon(['Notifier','activity', $post_id]); } $ret['success'] = true; @@ -2487,7 +2487,7 @@ function send_status_notifications($post_id,$item) { // check for an unfollow thread activity - we should probably decode the obj and check the id // but it will be extremely rare for this to be wrong. - if(($xx['verb'] === ACTIVITY_UNFOLLOW) + if((in_array($xx['verb'], ['Ignore', ACTIVITY_UNFOLLOW])) && (in_array($xx['obj_type'], ['Note', 'Image', ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_PHOTO])) && ($xx['parent'] != $xx['id'])) $unfollowed = true; @@ -2514,7 +2514,6 @@ function send_status_notifications($post_id,$item) { if(! $notify) return; - Enotify::submit(array( 'type' => $type, 'from_xchan' => $item['author_xchan'], @@ -2607,7 +2606,6 @@ function tag_deliver($uid, $item_id) { return; } - if ($is_group && intval($item['item_thread_top']) && intval($item['item_wall']) && $item['author_xchan'] !== $item['owner_xchan']) { if($item['resource_type'] === 'group_item') { @@ -2625,39 +2623,6 @@ function tag_deliver($uid, $item_id) { } /* - * Seems like a good place to plug in a poke notification. - */ - - if (stristr($item['verb'],ACTIVITY_POKE)) { - $poke_notify = true; - - if(($item['obj_type'] == "") || (!in_array($item['obj_type'], ['Person', ACTIVITY_OBJ_PERSON])) || (! $item['obj'])) - $poke_notify = false; - - $obj = json_decode($item['obj'],true); - if($obj) { - if($obj['id'] !== $u[0]['channel_hash']) - $poke_notify = false; - } - if(intval($item['item_deleted'])) - $poke_notify = false; - - $verb = urldecode(substr($item['verb'],strpos($item['verb'],'#')+1)); - if($poke_notify) { - Enotify::submit(array( - 'to_xchan' => $u[0]['channel_hash'], - 'from_xchan' => $item['author_xchan'], - 'type' => NOTIFY_POKE, - 'item' => $item, - 'link' => $i[0]['llink'], - 'verb' => ACTIVITY_POKE, - 'activity' => $verb, - 'otype' => 'item' - )); - } - } - - /* * Do community tagging */ @@ -4411,7 +4376,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C $item_normal = item_normal(); if (! (isset($arr['include_follow']) && intval($arr['include_follow']))) { - $item_normal .= sprintf(" and not verb in ('%s', '%s') ", + $item_normal .= sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", dbesc(ACTIVITY_FOLLOW), dbesc(ACTIVITY_UNFOLLOW) ); @@ -4824,54 +4789,7 @@ function comment_local_origin($item) { return false; } - - -function send_profile_photo_activity($channel,$photo,$profile) { - - // for now only create activities for the default profile - - if(! intval($profile['is_default'])) - return; - - $arr = array(); - $arr['item_thread_top'] = 1; - $arr['item_origin'] = 1; - $arr['item_wall'] = 1; - - if(stripos($profile['gender'],t('female')) !== false) - $t = t('%1$s updated her %2$s'); - elseif(stripos($profile['gender'],t('male')) !== false) - $t = t('%1$s updated his %2$s'); - else - $t = t('%1$s updated their %2$s'); - - $ptext = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' . t('profile photo') . '[/zrl]'; - - $ltext = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . '[zmg=150x150]' . z_root() . '/photo/' . $photo['resource_id'] . '-4[/zmg][/zrl]'; - - $arr['body'] = sprintf($t,$channel['channel_name'],$ptext) . "\n\n" . $ltext; - - $acl = new Zotlabs\Access\AccessList($channel); - $x = $acl->get(); - - $arr['allow_cid'] = $x['allow_cid']; - - $arr['allow_gid'] = $x['allow_gid']; - $arr['deny_cid'] = $x['deny_cid']; - $arr['deny_gid'] = $x['deny_gid']; - - $arr['uid'] = $channel['channel_id']; - $arr['aid'] = $channel['channel_account_id']; - - $arr['owner_xchan'] = $channel['channel_hash']; - $arr['author_xchan'] = $channel['channel_hash']; - - post_activity_item($arr); -} - - function sync_an_item($channel_id,$item_id) { - $r = q("select * from item where id = %d", intval($item_id) ); diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index 522e638de..4394d3238 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -117,7 +117,14 @@ function guess_image_type($filename, $data = '') { $body = $data['body']; if ($body) { $image = new Imagick(); - $image->readImageBlob($body); + + try{ + $image->readImageBlob($body); + } catch (\Exception $e) { + logger('Imagick readImageBlob() exception:' . print_r($e, true)); + return $type; + } + $r = $image->identifyImage(); if ($r && is_array($r) && array_key_exists($r['mimetype'], $types)) $type = $r['mimetype']; diff --git a/include/taxonomy.php b/include/taxonomy.php index cfec8414a..90ccb6142 100644 --- a/include/taxonomy.php +++ b/include/taxonomy.php @@ -58,7 +58,7 @@ function term_item_parent_query($uid,$table,$s,$type = TERM_UNKNOWN, $type2 = '' $s = str_replace('*','%',$s); if($type2) { - $r = q("select parent from item left join term on term.oid = item.id where term.ttype in (%d, %d) and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb != '%s'", + $r = q("select parent from item left join term on term.oid = item.id where term.ttype in (%d, %d) and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb NOT IN ('Update', '%s')", intval($type), intval($type2), dbesc($s), @@ -67,7 +67,7 @@ function term_item_parent_query($uid,$table,$s,$type = TERM_UNKNOWN, $type2 = '' ); } else { - $r = q("select parent from item left join term on term.oid = item.id where term.ttype = %d and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb != '%s'", + $r = q("select parent from item left join term on term.oid = item.id where term.ttype = %d and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb NOT IN ('Update', '%s')", intval($type), dbesc($s), intval($uid), diff --git a/include/text.php b/include/text.php index ea9cf45e1..aa9650a25 100644 --- a/include/text.php +++ b/include/text.php @@ -1270,77 +1270,6 @@ function sslify($s) { return $s; } -/** - * @brief Get an array of poke verbs. - * - * @return array - * * \e index is present tense verb - * * \e value is array containing past tense verb, translation of present, translation of past - */ -function get_poke_verbs() { - - $arr = [ - 'poke' => ['poked', t('poke'), t('poked')], - 'ping' => ['pinged', t('ping'), t('pinged')], - - // Those might be better suited for a nsfw poke addon - - // 'prod' => ['prodded', t('prod'), t('prodded')], - // 'slap' => ['slapped', t('slap'), t('slapped')], - // 'finger' => ['fingered', t('finger'), t('fingered')], - // 'rebuff' => ['rebuffed', t('rebuff'), t('rebuffed')] - ]; - - /** - * @hooks poke_verbs - * * \e array associative array with another array as value - */ - call_hooks('poke_verbs', $arr); - - return $arr; -} - -/** - * @brief Get an array of mood verbs. - * - * @return array - * * \e index is the verb - * * \e value is the translated verb - */ -function get_mood_verbs() { - - $arr = [ - 'happy' => t('happy'), - 'sad' => t('sad'), - 'mellow' => t('mellow'), - 'tired' => t('tired'), - 'perky' => t('perky'), - 'angry' => t('angry'), - 'stupefied' => t('stupefied'), - 'puzzled' => t('puzzled'), - 'interested' => t('interested'), - 'bitter' => t('bitter'), - 'cheerful' => t('cheerful'), - 'alive' => t('alive'), - 'annoyed' => t('annoyed'), - 'anxious' => t('anxious'), - 'cranky' => t('cranky'), - 'disturbed' => t('disturbed'), - 'frustrated' => t('frustrated'), - 'depressed' => t('depressed'), - 'motivated' => t('motivated'), - 'relaxed' => t('relaxed'), - 'surprised' => t('surprised'), - ]; - - /** - * @hooks mood_verbs - * * \e array associative array with mood verbs - */ - call_hooks('mood_verbs', $arr); - - return $arr; -} /** * @brief Function to list all smilies, both internal and from addons. @@ -1821,7 +1750,7 @@ function prepare_body(&$item,$attach = false,$opts = false) { } - $poll = (($item['obj_type'] === 'Question' && in_array($item['verb'],['Create', ACTIVITY_POST, ACTIVITY_UPDATE, ACTIVITY_SHARE])) ? format_poll($item, $s, $opts) : false); + $poll = (($item['obj_type'] === 'Question' && in_array($item['verb'],['Create', 'Update', ACTIVITY_POST, ACTIVITY_UPDATE, ACTIVITY_SHARE])) ? format_poll($item, $s, $opts) : false); if ($poll) { $s = $poll; } diff --git a/tests/fakes/fake_dba.php b/tests/fakes/fake_dba.php new file mode 100644 index 000000000..2289f5c80 --- /dev/null +++ b/tests/fakes/fake_dba.php @@ -0,0 +1,18 @@ +<?php +namespace Zotlabs\Tests\Fakes; + +require_once 'include/dba/dba_pdo.php'; + +/** + * Fake dba_driver implementation. + * + * This is a subclass of the dba_pdo class, that essentially lets us inject a + * stub for the PDO class that is the actual database driver. + */ +class FakeDba extends \dba_pdo { + public function __construct($stub) { + $this->db = $stub; + $this->connected = true; + } +} + diff --git a/tests/unit/UnitTestCase.php b/tests/unit/UnitTestCase.php index 0bf7b547a..18467d91e 100644 --- a/tests/unit/UnitTestCase.php +++ b/tests/unit/UnitTestCase.php @@ -23,6 +23,7 @@ namespace Zotlabs\Tests\Unit; use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestResult; /* * Make sure global constants and the global App object is available to the @@ -41,10 +42,39 @@ require_once 'include/dba/dba_driver.php' ; * @author Klaus Weidenbach */ class UnitTestCase extends TestCase { - private bool $in_transaction = false; protected array $fixtures = array(); - public static function setUpBeforeClass() : void { + /** + * Override the PHPUnit\Framework\TestCase::run method, so we can + * wrap it in a database transaction. + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function run(TestResult $result = null): TestResult { + // $myclass = get_class($this); + // logger("[*] Running test: {$myclass}::{$this->getName(true)}", LOGGER_DEBUG); + + if (! \DBA::$dba) { + //logger('[*] Connecting to test db...'); + $this->connect_to_test_db(); + } + + // The $transactuion variable is needed to hold the transaction until the + // function returns. + $transaction = new \DbaTransaction(\DBA::$dba); + + $this->loadFixtures(); + + // Make sure app config is reset and loaded from fixtures + \App::$config = array(); + \Zotlabs\Lib\Config::Load('system'); + + $result = parent::run($result); + + return $result; + } + + protected function connect_to_test_db() : void { if ( !\DBA::$dba ) { \DBA::dba_factory( getenv('HZ_TEST_DB_HOST') ?: 'db', @@ -71,36 +101,6 @@ class UnitTestCase extends TestCase { } } - protected function setUp() : void { - $myclass = get_class($this); - logger("[*] Running test: {$myclass}::{$this->getName(true)}", LOGGER_DEBUG); - if ( \DBA::$dba->connected ) { - // Create a transaction, so that any actions taken by the - // tests does not change the actual contents of the database. - $this->in_transaction = \DBA::$dba->db->beginTransaction(); - - $this->loadFixtures(); - } - - // Make sure app config is reset and loaded from fixtures - \App::$config = array(); - \Zotlabs\Lib\Config::Load('system'); - } - - protected function tearDown() : void { - if ( \DBA::$dba->connected && $this->in_transaction ) { - // Roll back the transaction, restoring the db to the - // state it was before the test was run. - if ( \DBA::$dba->db->rollBack() ) { - $this->in_transaction = false; - } else { - throw new \Exception( - "Transaction rollback failed! Error is: " - . \DBA::$dba->db->errorInfo()); - } - } - } - private static function dbtype(string $type): int { if (trim(strtolower($type)) === 'postgres') { return DBTYPE_POSTGRES; diff --git a/tests/unit/includes/dba/TransactionTest.php b/tests/unit/includes/dba/TransactionTest.php new file mode 100644 index 000000000..99e3f459d --- /dev/null +++ b/tests/unit/includes/dba/TransactionTest.php @@ -0,0 +1,207 @@ +<?php +/* + * Copyright (c) 2024 Hubzilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +require_once 'tests/fakes/fake_dba.php'; +require_once 'include/dba/dba_transaction.php'; + +use \PHPUnit\Framework\TestCase; +use \Zotlabs\Tests\Fakes\FakeDba; + +/** + * Test database transactions. + * + * This class subclass the base PHPUnit TestCase class, since we do _not_ + * want a real database connection for these tests. We're testing functionality + * of the database adapter itself, so we choose to stub the underlying db driver + * to be able to assert that the adapter behaves as it should. + */ +class DbaTransactionTest extends TestCase { + private $pdo_stub; + + public function setUp(): void { + $this->pdo_stub = $this->createStub(PDO::class); + } + + + /** + * Test that creating a DbaTransaction object initiates a database transaction. + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function test_transaction_initialized_on_construction(): void { + // Stub PDO::inTransaction() + // Expect that it's called once, and return false to simulate that no + // transactions are active. + $this->pdo_stub + ->expects($this->once()) + ->method('inTransaction') + ->willReturn(false); + + // Stub PDO::beginTransaction to ensure that it is being called. + $this->pdo_stub + ->expects($this->once()) + ->method('beginTransaction') + ->willReturn(true); + + $dba = new FakeDba($this->pdo_stub); + + $transaction = new DbaTransaction($dba); + } + + /** + * Test that a transaction is rolled back when the DbaTransaction object + * is destroyed. + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function test_uncommitted_transaction_is_rolled_back_on_destruction(): void { + // Stub PDO::inTransaction() + // Expect that it's called once, and return false to simulate that no + // transactions are active. + $this->pdo_stub + ->expects($this->once()) + ->method('inTransaction') + ->willReturn(false); + + // Stub PDO::beginTransaction to ensure that it is being called. + $this->pdo_stub + ->expects($this->once()) + ->method('beginTransaction') + ->willReturn(true); + + // Stub PDO::rollBack to make sure we test it is being called. + $this->pdo_stub + ->expects($this->once()) + ->method('rollBack') + ->willReturn(true); + + $dba = new FakeDba($this->pdo_stub); + + $transaction = new DbaTransaction($dba); + } + + /** + * Test that a committed transaction is not rolled back when the + * DbaTransaction object goes out of scope. + */ + public function test_committed_transaction_is_not_rolled_back(): void { + // Stub PDO::inTransaction() + // Return false to simulate that no transaction is active when called. + $this->pdo_stub + ->expects($this->once()) + ->method('inTransaction') + ->willReturn(false); + + // Stub PDO::beginTransaction to ensure that it is being called. + $this->pdo_stub + ->expects($this->once()) + ->method('beginTransaction') + ->willReturn(true); + + // Stub PDO::rollBack to ensure it is _not_ called + $this->pdo_stub + ->expects($this->never()) + ->method('rollBack'); + + // Stub PDO::commit to make the test check that it is being called + $this->pdo_stub + ->expects($this->once()) + ->method('commit') + ->willReturn(true); + + $dba = new FakeDba($this->pdo_stub); + + $transaction = new DbaTransaction($dba); + $transaction->commit(); + } + + /** + * Test that commiting a transaction more than once is a no-op. + */ + public function test_that_committing_an_already_committed_transaction_does_nothing(): void { + // Stub PDO::inTransaction() + // Return false to simulate that no transaction is active when called. + $this->pdo_stub + ->expects($this->once()) + ->method('inTransaction') + ->willReturn(false); + + // Stub PDO::beginTransaction to ensure that it is being called. + $this->pdo_stub + ->expects($this->once()) + ->method('beginTransaction') + ->willReturn(true); + + // Stub PDO::rollBack to ensure it is _not_ called + $this->pdo_stub + ->expects($this->never()) + ->method('rollBack'); + + // Stub PDO::commit to make the test check that it is being called + $this->pdo_stub + ->expects($this->once()) + ->method('commit') + ->willReturn(true); + + $dba = new FakeDba($this->pdo_stub); + + $transaction = new DbaTransaction($dba); + $transaction->commit(); + $transaction->commit(); + } + + /** + * Test simulating constructing a DbaTransaction object when a transaction + * is already active. + * + * This should _not_ initiate an actual DB transaction, not call the rollBack + * method on destruction. + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function test_that_nesting_a_transaction_does_not_create_a_new_transaction_in_db(): void { + // Stub PDO::inTransaction() + // We simulate that a transaction is already active, by returning true from + // this method. + $this->pdo_stub + ->expects($this->once()) + ->method('inTransaction') + ->willReturn(true); + + // Stub PDO::beginTransaction + // Since a transaction is already active, we should _not_ initiate + // a new transaction when the DbaTransaction object is constructed. + $this->pdo_stub + ->expects($this->never()) + ->method('beginTransaction'); + + // Stub PDO::rollBack to ensure it is _not_ called + $this->pdo_stub + ->expects($this->never()) + ->method('rollBack'); + + $dba = new FakeDba($this->pdo_stub); + + $transaction = new DbaTransaction($dba); + } +} diff --git a/view/js/mod_poke.js b/view/js/mod_poke.js deleted file mode 100644 index 88fa9f7c2..000000000 --- a/view/js/mod_poke.js +++ /dev/null @@ -1,5 +0,0 @@ -$(document).ready(function() { - $("#poke-recip").contact_autocomplete(baseurl + '/acl', 'a', false, function(data) { - $("#poke-recip-complete").val(data.id); - }); -}); diff --git a/view/tpl/mood_content.tpl b/view/tpl/mood_content.tpl deleted file mode 100644 index 4cb5207f6..000000000 --- a/view/tpl/mood_content.tpl +++ /dev/null @@ -1,33 +0,0 @@ - -<div id="mood-content" class="generic-content-wrapper"> - <div class="section-title-wrapper"> - <h2>{{$title}}</h2> - </div> - <div class="section-content-wrapper"> - - <div id="mood-desc">{{$desc}}</div> - - <br /> - <br /> - - - <form action="mood" method="get"> - <br /> - <br /> - - <input id="mood-parent" type="hidden" value="{{$parent}}" name="parent" /> - - <div class="mb-3 field custom"> - <select name="verb" id="mood-verb-select" class="form-control" > - {{foreach $verbs as $v}} - <option value="{{$v.0}}">{{$v.1}}</option> - {{/foreach}} - </select> - </div> - <br /> - <br /> - - <input type="submit" name="submit" value="{{$submit}}" /> - </form> - </div> -</div> diff --git a/view/tpl/poke_content.tpl b/view/tpl/poke_content.tpl deleted file mode 100644 index 3872d21ff..000000000 --- a/view/tpl/poke_content.tpl +++ /dev/null @@ -1,48 +0,0 @@ - -<div id="poke-content" class="generic-content-wrapper"> - <div class="section-title-wrapper"> - <h2>{{$title}}</h2> - </div> - <div class="section-content-wrapper"> - - <div id="poke-desc">{{$desc}}</div> - -<br /> -<br /> - - -<form action="poke" method="get"> - - -<div class="mb-3 field input"> - <label id="poke-recip-label" for="poke-recip">{{$clabel}}</label> - <input class="form-control" id="poke-recip" type="text" value="{{$name}}" name="pokename" autocomplete="off" /> -</div> - - <input id="poke-recip-complete" type="hidden" value="{{$id}}" name="cid" /> - <input id="poke-parent" type="hidden" value="{{$parent}}" name="parent" /> - - -{{if $poke_basic}} -<input type="hidden" name="verb" value="poke" /> -{{else}} -<div class="mb-3 field custom"> - <label for="poke-verb-select" id="poke-verb-lbl">{{$choice}}</label> - <select class="form-control" name="verb" id="poke-verb-select" > - {{foreach $verbs as $v}} - <option value="{{$v.0}}">{{$v.1}}</option> - {{/foreach}} - </select> -</div> -{{/if}} - -{{if ! $parent}} -{{include file="field_checkbox.tpl" field=$private}} -{{/if}} - -<input type="submit" name="submit" value="{{$submit}}" /> -</form> - - - </div> -</div> |