2018-05-23 06:20:29 +00:00
< ? php
2015-03-21 23:06:08 +00:00
/**
* @ file include / items . php
2017-09-04 22:23:42 +00:00
* @ brief Items related functions .
2015-03-21 23:06:08 +00:00
*/
2010-07-18 13:02:55 +00:00
2024-02-10 19:22:39 +00:00
use Code\Lib\JcsEddsa2022 ;
2022-02-16 04:08:28 +00:00
use Code\Lib\Libzot ;
use Code\Lib\Libsync ;
use Code\Lib\AccessList ;
use Code\Lib\Activity ;
use Code\Lib\ActivityStreams ;
use Code\Lib\Apps ;
use Code\Lib\Enotify ;
use Code\Lib\Channel ;
2022-07-05 01:34:07 +00:00
use Code\Lib\LanguageDetect ;
2022-02-16 04:08:28 +00:00
use Code\Lib\MarkdownSoap ;
use Code\Lib\MessageFilter ;
2022-07-05 01:34:07 +00:00
use Code\Lib\Config ;
2022-02-16 04:08:28 +00:00
use Code\Lib\IConfig ;
2022-11-06 21:56:10 +00:00
use Code\Lib\ObjCache ;
2022-02-16 04:08:28 +00:00
use Code\Lib\PConfig ;
use Code\Lib\LibBlock ;
use Code\Lib\ThreadListener ;
2023-04-28 05:35:28 +00:00
use Code\Lib\Tombstone ;
2022-02-16 04:08:28 +00:00
use Code\Access\PermissionLimits ;
use Code\Access\PermissionRoles ;
use Code\Access\AccessControl ;
use Code\Daemon\Run ;
2022-04-17 08:21:18 +00:00
use Code\Extend\Hook ;
2016-05-24 08:25:13 +00:00
2016-05-13 03:21:04 +00:00
require_once ( 'include/feedutils.php' );
2019-04-16 06:43:06 +00:00
require_once ( 'include/photo_factory.php' );
2020-05-20 01:02:28 +00:00
2010-07-18 13:02:55 +00:00
2015-03-21 23:06:08 +00:00
/**
* @ brief Collects recipients .
*
* @ param array $item
2022-09-17 00:25:44 +00:00
* @ param bool $private_envelope
2015-03-21 23:06:08 +00:00
* @ return array containing the recipients
*/
2023-02-11 21:22:06 +00:00
function collect_recipients ( $item , & $private_envelope ) {
2023-08-07 09:53:14 +00:00
2022-08-14 09:20:43 +00:00
$private_envelope = ( bool ) intval ( $item [ 'item_private' ]);
2022-07-20 00:43:27 +00:00
$recipients = [];
if ( $item [ 'allow_cid' ] || $item [ 'allow_gid' ] || $item [ 'deny_cid' ] || $item [ 'deny_gid' ]) {
// it is private
2023-02-11 21:22:06 +00:00
$recipients = item_get_recipients ( $item );
2022-07-20 00:43:27 +00:00
$private_envelope = true ;
}
else {
// if the post is marked private but there are no recipients and public_policy/scope = self,
// only add the author and owner as recipients. The ACL for the post may live on the hub of
// a different clone. We need to get the post to that hub.
// The post may be private by virtue of not being visible to anybody on the internet,
2022-07-27 22:16:27 +00:00
// but there are no envelope recipients, so set this to false.
2022-07-20 00:43:27 +00:00
$private_envelope = false ;
$r = q ( " select abook_xchan, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 " ,
intval ( $item [ 'uid' ])
);
if ( $r ) {
foreach ( $r as $rv ) {
$recipients [] = $rv [ 'abook_xchan' ];
}
}
// Forward to thread listeners, *unless* there is even a remote hint that the item
// might have some privacy attached. This could be (for instance) an ActivityPub DM
// in the middle of a public thread. Unless we can guarantee beyond all doubt that
// this is public, don't allow it to go to thread listeners.
if ( ! intval ( $item [ 'item_private' ])) {
$r = array_merge ( ThreadListener :: fetch_by_target ( $item [ 'parent_mid' ]),
ThreadListener :: fetch_by_target ( str_replace ( '/activity/' , '/item/' , $item [ 'parent_mid' ]))
);
if ( $item [ 'mid' ] !== $item [ 'parent_mid' ]) {
$r = array_merge ( $r , ThreadListener :: fetch_by_target ( $item [ 'mid' ]),
ThreadListener :: fetch_by_target ( str_replace ( '/activity/' , '/item/' , $item [ 'mid' ]))
);
}
if ( $r ) {
foreach ( $r as $rv ) {
if ( ! in_array ( $rv [ 'portable_id' ], $recipients )) {
$recipients [] = $rv [ 'portable_id' ];
}
}
}
// We've determined this is public. Send it also to the system channel.
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$sys = Channel :: get_system ();
if ( $sys && intval ( $item [ 'uid' ]) !== intval ( $sys [ 'channel_id' ])) {
$recipients [] = $sys [ 'channel_hash' ];
}
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
// Add the authors of any posts in this thread, if they are known to us.
// This is specifically designed to forward wall-to-wall posts to the original author,
// in case they aren't a connection but have permission to write on our wall.
// This is important for issue tracker channels. It should be a no-op for most channels.
2023-02-11 21:22:06 +00:00
// Whether they will accept the delivery is not determined here, but should
2022-07-20 00:43:27 +00:00
// be taken into account by zot:process_delivery()
$r = q ( " select author_xchan from item where parent = %d " ,
intval ( $item [ 'parent' ])
);
if ( $r ) {
foreach ( $r as $rv ) {
if ( ! in_array ( $rv [ 'author_xchan' ], $recipients )) {
$recipients [] = $rv [ 'author_xchan' ];
}
}
}
}
// This is a somewhat expensive operation but important.
// Don't send this item to anybody who doesn't have the deliver_stream permission
2022-05-30 20:54:08 +00:00
2022-05-12 21:14:08 +00:00
$recipients = check_deliver_permissions ( $item [ 'uid' ], $recipients );
2013-01-14 02:35:12 +00:00
2022-07-20 00:43:27 +00:00
// Add both the author and owner (if different).
2015-04-09 22:28:23 +00:00
2022-07-20 00:43:27 +00:00
$recipients [] = $item [ 'author_xchan' ];
2020-04-30 00:35:55 +00:00
2022-07-20 00:43:27 +00:00
if ( $item [ 'owner_xchan' ] != $item [ 'author_xchan' ]) {
$recipients [] = $item [ 'owner_xchan' ];
}
2014-08-26 05:43:44 +00:00
2022-07-20 00:43:27 +00:00
return $recipients ;
2012-11-16 05:52:05 +00:00
}
2023-02-11 21:22:06 +00:00
function item_get_recipients ( $item ) {
$allow_people = expand_acl ( $item [ 'allow_cid' ]);
$allow_groups = AccessList :: expand ( expand_acl ( $item [ 'allow_gid' ]));
$recipients = array_values ( array_unique ( array_merge ( $allow_people , $allow_groups )));
// if you specifically deny somebody but haven't allowed anybody, we'll allow everybody in your
// address book minus the denied connections. The post is still private and can't be seen publicly
// as that would allow the denied person to see the post by logging out.
if (( ! $item [ 'allow_cid' ]) && ( ! $item [ 'allow_gid' ])) {
$r = q ( " select * from abook where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 " ,
intval ( $item [ 'uid' ])
);
if ( $r ) {
foreach ( $r as $rr ) {
$recipients [] = $rr [ 'abook_xchan' ];
}
}
}
$deny_people = expand_acl ( $item [ 'deny_cid' ]);
$deny_groups = AccessList :: expand ( expand_acl ( $item [ 'deny_gid' ]));
$deny = array_values ( array_unique ( array_merge ( $deny_people , $deny_groups )));
// Don't deny anybody if nobody was allowed (e.g. they were all filtered out)
// That would lead to array_diff doing the wrong thing.
// This will result in a private post that won't be delivered to anybody.
if ( $recipients && $deny ) {
$recipients = array_diff ( $recipients , $deny );
}
return $recipients ;
}
2014-08-28 23:56:13 +00:00
function comments_are_now_closed ( $item ) {
2019-11-02 17:10:21 +00:00
2022-07-20 00:43:27 +00:00
$x = [
'item' => $item ,
'closed' => 'unset'
];
2019-11-02 17:10:21 +00:00
2022-07-20 00:43:27 +00:00
/**
* @ hooks comments_are_now_closed
* Called to determine whether commenting should be closed
* * \e array \b item
* * \e boolean \b closed - return value
*/
2019-11-02 17:10:21 +00:00
2022-07-20 00:43:27 +00:00
Hook :: call ( 'comments_are_now_closed' , $x );
2019-11-02 17:10:21 +00:00
2022-07-20 00:43:27 +00:00
if ( $x [ 'closed' ] !== 'unset' ) {
return $x [ 'closed' ];
}
2019-11-02 17:10:21 +00:00
2022-07-20 00:43:27 +00:00
if ( $item [ 'comments_closed' ] > NULL_DATE && datetime_convert () > $item [ 'comments_closed' ]) {
return true ;
}
2015-03-21 23:06:08 +00:00
2022-07-20 00:43:27 +00:00
return false ;
2014-08-28 23:56:13 +00:00
}
2015-06-10 23:59:04 +00:00
function item_normal () {
2022-07-20 00:43:27 +00:00
return " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0
and item . item_unpublished = 0 and item . item_delayed = 0 and item . item_pending_remove = 0
and item . item_blocked = 0 and item . obj_type != '" . ACTIVITY_OBJ_FILE . "' " ;
2015-06-10 23:59:04 +00:00
}
2020-11-30 00:38:45 +00:00
function item_normal_draft () {
2022-07-20 00:43:27 +00:00
return " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0
and item . item_pending_remove = 0 and item . item_blocked = 0 and item . obj_type != '" . ACTIVITY_OBJ_FILE . "' " ;
2020-11-30 00:38:45 +00:00
}
2017-05-31 00:06:50 +00:00
function item_normal_search () {
2022-07-20 00:43:27 +00:00
return " and item.item_hidden = 0 and item.item_type in (0,3,6,7) and item.item_deleted = 0
and item . item_unpublished = 0 and item . item_delayed = 0 and item . item_pending_remove = 0
and item . item_blocked = 0 and item . obj_type != '" . ACTIVITY_OBJ_FILE . "' " ;
2017-05-31 00:06:50 +00:00
}
2017-09-03 19:33:07 +00:00
function item_normal_update () {
2022-07-20 00:43:27 +00:00
return " and item.item_hidden = 0 and item.item_type = 0
and item . item_unpublished = 0 and item . item_delayed = 0 and item . item_pending_remove = 0
and item . item_blocked = 0 and item . obj_type != '" . ACTIVITY_OBJ_FILE . "' " ;
2017-09-03 19:33:07 +00:00
}
2019-03-28 02:20:35 +00:00
function item_normal_moderate () {
2022-07-20 00:43:27 +00:00
return " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0
and item . item_unpublished = 0 and item . item_delayed = 0 and item . item_pending_remove = 0
and item . item_blocked in ( 0 , 4 ) and item . obj_type != '" . ACTIVITY_OBJ_FILE . "' " ;
2019-03-28 02:20:35 +00:00
}
2017-05-31 00:06:50 +00:00
2015-06-11 22:52:37 +00:00
/**
* @ brief
2016-10-01 22:41:25 +00:00
*
* This is a compatibility function primarily for plugins , because
2015-06-12 00:56:56 +00:00
* in earlier DB schemas this was a much simpler single integer compare
2015-06-11 22:52:37 +00:00
*
2016-10-01 22:41:25 +00:00
* @ param array $item
2015-06-11 22:52:37 +00:00
*/
2015-06-11 23:11:59 +00:00
function is_item_normal ( $item ) {
2022-07-20 00:43:27 +00:00
if ( intval ( $item [ 'item_hidden' ]) || intval ( $item [ 'item_type' ]) || intval ( $item [ 'item_deleted' ])
|| intval ( $item [ 'item_unpublished' ]) || intval ( $item [ 'item_delayed' ]) || intval ( $item [ 'item_pending_remove' ])
|| intval ( $item [ 'item_blocked' ]) || ( $item [ 'obj_type' ] == ACTIVITY_OBJ_FILE )) {
return false ;
}
2015-06-11 23:11:59 +00:00
2022-07-20 00:43:27 +00:00
return true ;
2015-06-11 23:11:59 +00:00
}
2013-09-28 11:41:12 +00:00
/**
2017-09-04 22:23:42 +00:00
* @ brief Decide if current observer has sufficient privileges to comment on item .
2013-09-28 11:41:12 +00:00
*
* This function examines the comment_policy attached to an item and decides if the current observer has
* sufficient privileges to comment . This will normally be called on a remote site where perm_is_allowed ()
* will not be suitable because the post owner does not have a local channel_id .
2016-07-14 06:05:19 +00:00
* Generally we should look at the item - in particular the author [ 'abook_flags' ] and see if ABOOK_FLAG_SELF is set .
2015-04-09 22:28:23 +00:00
* If it is , you should be able to use perm_is_allowed ( ... 'post_comments' ), and if it isn ' t you need to call
2013-09-28 11:41:12 +00:00
* can_comment_on_post ()
2015-04-09 22:28:23 +00:00
* We also check the comments_closed date / time on the item if this is set .
2015-03-21 23:06:08 +00:00
*
* @ param string $observer_xchan
* @ param array $item
2022-10-23 01:13:07 +00:00
* @ return bool
2013-09-28 12:03:58 +00:00
*/
2021-12-03 03:01:39 +00:00
function can_comment_on_post ( $observer_xchan , $item )
{
// logger('Comment_policy: ' . $item['comment_policy'], LOGGER_DEBUG);
$x = [
'observer_hash' => $observer_xchan ,
'item' => $item ,
'allowed' => 'unset'
];
/**
* @ hooks can_comment_on_post
2023-02-11 21:22:06 +00:00
* Called when deciding whether to present a comment box for a post .
2021-12-03 03:01:39 +00:00
* * \e string \b observer_hash
* * \e array \b item
* * \e boolean \b allowed - return value
*/
2022-02-12 08:50:48 +00:00
Hook :: call ( 'can_comment_on_post' , $x );
2021-12-03 03:01:39 +00:00
if ( $x [ 'allowed' ] !== 'unset' ) {
return $x [ 'allowed' ];
}
if ( ! $observer_xchan ) {
return false ;
}
2022-10-23 01:13:07 +00:00
if ( $item [ 'comment_policy' ] === 'none' || intval ( $item [ 'item_nocomment' ]) || comments_are_now_closed ( $item )) {
2021-12-03 03:01:39 +00:00
return false ;
}
if ( $observer_xchan === $item [ 'author_xchan' ] || $observer_xchan === $item [ 'owner_xchan' ]) {
return true ;
}
switch ( $item [ 'comment_policy' ]) {
2023-02-11 21:22:06 +00:00
case 'network: red' :
case 'network: activitypub' :
2021-12-03 03:01:39 +00:00
case 'public' :
case 'authenticated' :
// Anonymous folks won't ever reach this point (as $observer_xchan will be empty).
2023-02-11 21:22:06 +00:00
// This means we can identify the viewer.
2021-12-03 03:01:39 +00:00
return true ;
case 'any connections' :
case 'specific' :
case 'contacts' :
case '' :
2021-12-21 22:03:17 +00:00
// local posts only - check if the post owner granted me comment permission
2021-12-03 03:01:39 +00:00
if ( local_channel () && array_key_exists ( 'owner' , $item ) && their_perms_contains ( local_channel (), $item [ 'owner' ][ 'abook_xchan' ], 'post_comments' )) {
return true ;
}
if ( intval ( $item [ 'item_wall' ]) && perm_is_allowed ( $item [ 'uid' ], $observer_xchan , 'post_comments' )) {
return true ;
}
break ;
2022-11-20 05:28:50 +00:00
case 'self' :
2021-12-03 03:01:39 +00:00
default :
break ;
}
if ( strstr ( $item [ 'comment_policy' ], 'site:' ) && strstr ( $item [ 'comment_policy' ], App :: get_hostname ())) {
return true ;
}
return false ;
2013-06-17 03:44:29 +00:00
}
2020-06-18 03:30:18 +00:00
function absolutely_no_comments ( $item ) {
2022-07-20 00:43:27 +00:00
if ( $item [ 'comment_policy' ] === 'none' ) {
return true ;
}
2020-06-18 03:30:18 +00:00
2022-07-20 00:43:27 +00:00
if ( intval ( $item [ 'item_nocomment' ])) {
return true ;
}
2020-06-18 03:30:18 +00:00
2022-07-20 00:43:27 +00:00
if ( comments_are_now_closed ( $item )) {
return true ;
}
2020-06-18 03:30:18 +00:00
2022-07-20 00:43:27 +00:00
return false ;
2020-06-18 03:30:18 +00:00
}
2013-04-05 01:54:24 +00:00
/**
2015-03-21 23:06:08 +00:00
* @ brief Post an activity .
2013-04-05 01:54:24 +00:00
*
* In its simplest form one needs only to set $arr [ 'body' ] to post a note to the logged in channel ' s wall .
2015-04-09 22:28:23 +00:00
* Much more complex activities can be created . Permissions are checked . No filtering , tag expansion
2013-04-05 01:54:24 +00:00
* or other processing is performed .
*
2015-03-21 23:06:08 +00:00
* @ param array $arr
2017-05-04 22:23:57 +00:00
* @ param boolean $deliver ( optional ) default true
2015-03-21 23:06:08 +00:00
* @ returns array
* * \e boolean \b success true or false
* * \e array \b activity the resulting activity if successful
2013-04-05 01:54:24 +00:00
*/
2022-12-21 20:26:28 +00:00
function post_activity_item ( $arr , $deliver = true , $channel = null , $observer = null ) {
2022-12-11 19:16:17 +00:00
logger ( 'input: ' . print_r ( $arr , true ), LOGGER_DATA );
2022-07-20 00:43:27 +00:00
$ret = [ 'success' => false ];
$is_comment = false ;
if (( isset ( $arr [ 'parent' ]) && $arr [ 'parent' ] && $arr [ 'parent' ] != $arr [ 'id' ]) || ( isset ( $arr [ 'parent_mid' ]) && $arr [ 'parent_mid' ] && $arr [ 'parent_mid' ] != $arr [ 'mid' ])) {
$is_comment = true ;
}
if ( ! array_key_exists ( 'item_origin' , $arr )) {
$arr [ 'item_origin' ] = 1 ;
}
if ( ! array_key_exists ( 'item_wall' , $arr ) && ( ! $is_comment )) {
$arr [ 'item_wall' ] = 0 ;
}
if ( ! array_key_exists ( 'item_thread_top' , $arr ) && ( ! $is_comment )) {
$arr [ 'item_thread_top' ] = 1 ;
}
2022-12-04 18:15:43 +00:00
if ( ! $channel ) {
$channel = App :: get_channel ();
}
if ( ! $observer ) {
$observer = App :: get_observer ();
}
2022-07-20 00:43:27 +00:00
$arr [ 'aid' ] = (( isset ( $arr [ 'aid' ])) ? $arr [ 'aid' ] : $channel [ 'channel_account_id' ]);
$arr [ 'uid' ] = (( isset ( $arr [ 'uid' ])) ? $arr [ 'uid' ] : $channel [ 'channel_id' ]);
2024-01-26 21:24:02 +00:00
logger ( 'uid: ' . $arr [ 'uid' ]);
logger ( 'observer_hash: ' . $observer [ 'xchan_hash' ]);
logger ( 'permission: ' . (( $is_comment ) ? 'post_comments' : 'post_wall' ));
2022-11-20 05:28:50 +00:00
if ( ! perm_is_allowed ( $arr [ 'uid' ], $observer [ 'xchan_hash' ], (( $is_comment ) ? 'post_comments' : 'post_wall' ))) {
2024-01-26 21:24:02 +00:00
logger ( 'permission denied' );
2022-07-20 00:43:27 +00:00
$ret [ 'message' ] = t ( 'Permission denied' );
return $ret ;
}
if ( ! array_key_exists ( 'mimetype' , $arr )) {
$arr [ 'mimetype' ] = 'text/x-multicode' ;
}
2013-12-08 07:29:26 +00:00
2021-07-26 00:20:10 +00:00
if ( ! ( isset ( $arr [ 'mid' ]) && $arr [ 'mid' ])) {
2021-03-09 04:44:47 +00:00
$arr [ 'uuid' ] = (( isset ( $arr [ 'uuid' ])) ? $arr [ 'uuid' ] : new_uuid ());
2019-02-19 22:59:22 +00:00
}
2021-03-09 04:44:47 +00:00
$arr [ 'mid' ] = (( isset ( $arr [ 'mid' ])) ? $arr [ 'mid' ] : z_root () . '/item/' . $arr [ 'uuid' ]);
2019-02-19 22:59:22 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'parent_mid' ] = (( isset ( $arr [ 'parent_mid' ])) ? $arr [ 'parent_mid' ] : $arr [ 'mid' ]);
$arr [ 'thr_parent' ] = (( isset ( $arr [ 'thr_parent' ])) ? $arr [ 'thr_parent' ] : $arr [ 'mid' ]);
2013-04-05 01:54:24 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'owner_xchan' ] = (( isset ( $arr [ 'owner_xchan' ])) ? $arr [ 'owner_xchan' ] : $channel [ 'channel_hash' ]);
$arr [ 'author_xchan' ] = (( isset ( $arr [ 'author_xchan' ])) ? $arr [ 'author_xchan' ] : $observer [ 'xchan_hash' ]);
2013-04-05 01:54:24 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'verb' ] = (( x ( $arr , 'verb' )) ? $arr [ 'verb' ] : ACTIVITY_POST );
$arr [ 'obj_type' ] = (( x ( $arr , 'obj_type' )) ? $arr [ 'obj_type' ] : ACTIVITY_OBJ_ARTICLE );
2015-01-15 02:36:23 +00:00
2022-07-20 00:43:27 +00:00
if ( ! ( array_key_exists ( 'allow_cid' , $arr ) || array_key_exists ( 'allow_gid' , $arr )
|| array_key_exists ( 'deny_cid' , $arr ) || array_key_exists ( 'deny_gid' , $arr ))) {
$arr [ 'allow_cid' ] = $channel [ 'channel_allow_cid' ];
$arr [ 'allow_gid' ] = $channel [ 'channel_allow_gid' ];
$arr [ 'deny_cid' ] = $channel [ 'channel_deny_cid' ];
$arr [ 'deny_gid' ] = $channel [ 'channel_deny_gid' ];
}
2013-04-05 01:54:24 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'comment_policy' ] = map_scope ( PermissionLimits :: Get ( $channel [ 'channel_id' ], 'post_comments' ));
2014-01-09 23:45:17 +00:00
2022-12-11 19:16:17 +00:00
if ( empty ( $arr [ 'plink' ])) {
$arr [ 'plink' ] = $arr [ 'mid' ];
2022-07-20 00:43:27 +00:00
}
2014-01-09 23:45:17 +00:00
2024-02-12 20:06:28 +00:00
if ( ! $arr [ 'target' ]) {
$arr [ 'target' ] = [
'id' => str_replace ( '/item/' , '/conversation/' , $arr [ 'parent_mid' ]),
'type' => 'Collection' ,
'attributedTo' => z_root () . '/channel/' . $channel [ 'channel_address' ],
];
$arr [ 'tgt_type' ] = 'Collection' ;
}
2022-07-20 00:43:27 +00:00
// for the benefit of plugins, we will behave as if this is an API call rather than a normal online post
2013-05-22 04:51:02 +00:00
2022-07-20 00:43:27 +00:00
$_REQUEST [ 'api_source' ] = 1 ;
2013-05-22 04:51:02 +00:00
2022-07-20 00:43:27 +00:00
/**
* @ hooks post_local
* Called when an item has been posted on this machine via mod / item . php ( also via API ) .
*/
Hook :: call ( 'post_local' , $arr );
2013-05-22 04:51:02 +00:00
2022-07-20 00:43:27 +00:00
if ( x ( $arr , 'cancel' )) {
logger ( 'Post cancelled by plugin.' );
return $ret ;
}
2013-05-22 04:51:02 +00:00
2022-12-21 20:20:55 +00:00
$post = item_store ( $arr , $deliver );
2022-03-17 23:57:54 +00:00
$post_id = 0 ;
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( $post [ 'success' ]) {
$post_id = $post [ 'item_id' ];
$ret [ 'success' ] = true ;
$ret [ 'item_id' ] = $post_id ;
$ret [ 'activity' ] = $post [ 'item' ];
/**
* @ hooks post_local_end
* Called after a local post operation has completed .
* * \e array - the item returned from item_store ()
*/
Hook :: call ( 'post_local_end' , $ret [ 'activity' ]);
2022-03-17 23:57:54 +00:00
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( $post_id && $deliver ) {
Run :: Summon ([ 'Notifier' , 'activity' , $post_id ]);
2024-02-10 19:22:39 +00:00
if ( ! empty ( $post [ 'approval_id' ])) {
Run :: Summon ([ 'Notifier' , 'activity' , $post [ 'approval_id' ]]);
}
2022-07-20 00:43:27 +00:00
}
2013-04-05 01:54:24 +00:00
2022-07-20 00:43:27 +00:00
return $ret ;
2013-04-05 01:54:24 +00:00
}
2015-08-14 02:35:57 +00:00
function validate_item_elements ( $message , $arr ) {
2022-09-17 00:25:44 +00:00
$result = [ 'success' => false ];
2015-08-14 02:35:57 +00:00
2022-07-20 00:43:27 +00:00
if ( ! array_key_exists ( 'created' , $arr ))
$result [ 'message' ] = 'missing created, possible author/owner lookup failure' ;
2015-08-14 02:35:57 +00:00
2022-07-20 00:43:27 +00:00
if (( ! $arr [ 'mid' ]) || ( ! $arr [ 'parent_mid' ]))
$result [ 'message' ] = 'missing message-id or parent message-id' ;
2015-08-14 02:35:57 +00:00
2022-07-20 00:43:27 +00:00
if ( array_key_exists ( 'flags' , $message ) && in_array ( 'relay' , $message [ 'flags' ]) && $arr [ 'mid' ] === $arr [ 'parent_mid' ])
$result [ 'message' ] = 'relay set on top level post' ;
2015-08-14 02:35:57 +00:00
2022-07-20 00:43:27 +00:00
if ( ! $result [ 'message' ])
$result [ 'success' ] = true ;
2015-08-14 02:35:57 +00:00
2022-07-20 00:43:27 +00:00
return $result ;
2015-08-14 02:35:57 +00:00
}
2022-12-21 20:20:55 +00:00
function get_item_elements ( $x ) {
2012-07-30 05:43:51 +00:00
2022-07-20 00:43:27 +00:00
$arr = [];
2015-09-22 23:19:08 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'body' ] = $x [ 'body' ];
$arr [ 'summary' ] = $x [ 'summary' ];
2014-10-02 09:43:07 +00:00
2022-05-01 05:57:46 +00:00
$mirror = array_key_exists ( 'revision' , $x );
2022-07-20 00:43:27 +00:00
$maxlen = get_max_import_size ();
2014-08-10 06:38:33 +00:00
2022-07-20 00:43:27 +00:00
if ( $maxlen && mb_strlen ( $arr [ 'body' ]) > $maxlen ) {
$arr [ 'body' ] = mb_substr ( $arr [ 'body' ], 0 , $maxlen , 'UTF-8' );
logger ( 'get_item_elements: message length exceeds max_import_size: truncated' );
}
2014-08-10 06:38:33 +00:00
2022-07-20 00:43:27 +00:00
if ( $maxlen && mb_strlen ( $arr [ 'summary' ]) > $maxlen ) {
$arr [ 'summary' ] = mb_substr ( $arr [ 'summary' ], 0 , $maxlen , 'UTF-8' );
logger ( 'get_item_elements: message summary length exceeds max_import_size: truncated' );
}
2018-08-28 01:58:47 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'created' ] = datetime_convert ( 'UTC' , 'UTC' , $x [ 'created' ]);
$arr [ 'edited' ] = datetime_convert ( 'UTC' , 'UTC' , $x [ 'edited' ]);
2012-07-30 05:43:51 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'expires' ] = (( x ( $x , 'expires' ) && $x [ 'expires' ])
? datetime_convert ( 'UTC' , 'UTC' , $x [ 'expires' ])
: NULL_DATE );
2013-11-18 03:22:24 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'commented' ] = (( x ( $x , 'commented' ) && $x [ 'commented' ])
? datetime_convert ( 'UTC' , 'UTC' , $x [ 'commented' ])
: $arr [ 'created' ]);
$arr [ 'comments_closed' ] = (( x ( $x , 'comments_closed' ) && $x [ 'comments_closed' ])
? datetime_convert ( 'UTC' , 'UTC' , $x [ 'comments_closed' ])
: NULL_DATE );
2013-11-18 03:22:24 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'title' ] = (( $x [ 'title' ]) ? htmlspecialchars ( $x [ 'title' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
2013-07-30 00:30:46 +00:00
2022-07-20 00:43:27 +00:00
if ( mb_strlen ( $arr [ 'title' ]) > 255 )
$arr [ 'title' ] = mb_substr ( $arr [ 'title' ], 0 , 255 );
2013-07-30 00:30:46 +00:00
2019-02-19 22:59:22 +00:00
$arr [ 'uuid' ] = (( $x [ 'uuid' ]) ? htmlspecialchars ( $x [ 'uuid' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
2022-07-20 00:43:27 +00:00
$arr [ 'app' ] = (( $x [ 'app' ]) ? htmlspecialchars ( $x [ 'app' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'mid' ] = (( $x [ 'message_id' ]) ? htmlspecialchars ( $x [ 'message_id' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'parent_mid' ] = (( $x [ 'message_top' ]) ? htmlspecialchars ( $x [ 'message_top' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'thr_parent' ] = (( $x [ 'message_parent' ]) ? htmlspecialchars ( $x [ 'message_parent' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
2022-12-02 19:40:55 +00:00
$arr [ 'approved' ] = (( $x [ 'approved' ]) ? htmlspecialchars ( $x [ 'approved' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
2012-11-17 10:29:02 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'plink' ] = (( $x [ 'permalink' ]) ? htmlspecialchars ( $x [ 'permalink' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'location' ] = (( $x [ 'location' ]) ? htmlspecialchars ( $x [ 'location' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'verb' ] = (( $x [ 'verb' ]) ? htmlspecialchars ( $x [ 'verb' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'mimetype' ] = (( $x [ 'mimetype' ]) ? htmlspecialchars ( $x [ 'mimetype' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'obj_type' ] = (( $x [ 'object_type' ]) ? htmlspecialchars ( $x [ 'object_type' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'tgt_type' ] = (( $x [ 'target_type' ]) ? htmlspecialchars ( $x [ 'target_type' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
2014-08-07 00:36:07 +00:00
2022-09-16 10:57:04 +00:00
if ( $x [ 'longlat' ]) {
$coordinates = explode ( ' ' , $x [ 'longlat' ]);
if ( count ( $coordinates ) > 1 ) {
$arr [ 'lat' ] = floatval ( $coordinates [ 0 ]);
$arr [ 'lon' ] = floatval ( $coordinates [ 1 ]);
}
}
2022-07-20 00:43:27 +00:00
// convert AS1 namespaced elements to AS-JSONLD
2019-03-28 23:41:06 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'verb' ] = Activity :: activity_mapper ( $arr [ 'verb' ]);
2022-05-01 05:57:46 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'obj_type' ] = Activity :: activity_obj_mapper ( $arr [ 'obj_type' ], $mirror );
$arr [ 'tgt_type' ] = Activity :: activity_obj_mapper ( $arr [ 'tgt_type' ], $mirror );
2019-03-28 23:41:06 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'comment_policy' ] = (( $x [ 'comment_scope' ]) ? htmlspecialchars ( $x [ 'comment_scope' ], ENT_COMPAT , 'UTF-8' , false ) : 'contacts' );
2013-10-03 04:04:48 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'sig' ] = (( $x [ 'signature' ]) ? htmlspecialchars ( $x [ 'signature' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
2022-12-28 01:51:30 +00:00
2023-02-11 21:22:06 +00:00
// fix old-style signatures imported from hubzilla
2019-01-31 23:54:37 +00:00
2022-07-20 00:43:27 +00:00
if ( $arr [ 'sig' ] && ( ! strpos ( $arr [ 'sig' ], '.' ))) {
$arr [ 'sig' ] = 'sha256.' . $arr [ 'sig' ];
}
2019-01-31 23:54:37 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'obj' ] = activity_sanitise ( $x [ 'object' ]);
2019-03-28 23:41:06 +00:00
2022-07-20 00:43:27 +00:00
if ( $arr [ 'obj' ] && is_array ( $arr [ 'obj' ]) && array_key_exists ( 'asld' , $arr [ 'obj' ])) {
$arr [ 'obj' ] = $arr [ 'obj' ][ 'asld' ];
}
2019-03-28 23:41:06 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'target' ] = isset ( $x [ 'target' ]) ? activity_sanitise ( $x [ 'target' ]) : '' ;
2012-07-30 05:43:51 +00:00
2022-07-20 00:43:27 +00:00
if ( $arr [ 'target' ] && is_array ( $arr [ 'target' ]) && array_key_exists ( 'asld' , $arr [ 'target' ])) {
$arr [ 'target' ] = $arr [ 'target' ][ 'asld' ];
}
2019-03-28 23:41:06 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'attach' ] = activity_sanitise ( $x [ 'attach' ]);
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'replyto' ] = activity_sanitise ( $x [ 'replyto' ]);
$arr [ 'term' ] = isset ( $x [ 'tags' ]) ? decode_tags ( $x [ 'tags' ]) : [];
$arr [ 'iconfig' ] = decode_item_meta ( $x [ 'meta' ]);
$arr [ 'item_private' ] = 0 ;
$arr [ 'item_flags' ] = 0 ;
if ( array_key_exists ( 'flags' , $x )) {
if ( in_array ( 'consensus' , $x [ 'flags' ]))
$arr [ 'item_consensus' ] = 1 ;
if ( in_array ( 'deleted' , $x [ 'flags' ]))
$arr [ 'item_deleted' ] = 1 ;
if ( in_array ( 'notshown' , $x [ 'flags' ]))
$arr [ 'item_notshown' ] = 1 ;
if ( in_array ( 'obscured' , $x [ 'flags' ]))
$arr [ 'item_obscured' ] = 1 ;
// hidden item are no longer propagated - notshown may be a suitable alternative
if ( in_array ( 'hidden' , $x [ 'flags' ]))
$arr [ 'item_hidden' ] = 1 ;
if ( in_array ( 'private' , $x [ 'flags' ]))
$arr [ 'item_private' ] = 1 ;
if ( in_array ( 'direct' , $x [ 'flags' ]))
$arr [ 'item_private' ] = 2 ;
}
// Here's the deal - the site might be down or whatever but if there's a new person you've never
// seen before sending stuff to your stream, we MUST be able to look them up and import their data from their
// hub and verify that they are legit - or else we're going to toss the post. We only need to do this
// once, and after that your hub knows them. Sure some info is in the post, but it's only a transit identifier
// and not enough info to be able to look you up from your hash - which is the only thing stored with the post.
$xchan_hash = import_author_xchan ( $x [ 'author' ]);
if ( $xchan_hash )
$arr [ 'author_xchan' ] = $xchan_hash ;
else
return [];
$xchan_hash = import_author_xchan ( $x [ 'owner' ]);
if ( $xchan_hash ) {
$arr [ 'owner_xchan' ] = $xchan_hash ;
}
else {
return [];
}
// Check signature on the body text received.
// This presents an issue that we aren't verifying the text that is actually displayed
// on this site. We are however verifying the received text was exactly as received.
// We have every right to strip content that poses a security risk. You are welcome to
// create a plugin to verify the content after filtering if this offends you.
if ( $arr [ 'sig' ]) {
// check the supplied signature against the supplied content.
// Note that we will purify the content which could change it.
$r = q ( " select xchan_pubkey from xchan where xchan_hash = '%s' limit 1 " ,
dbesc ( $arr [ 'author_xchan' ])
);
if ( $r ) {
if ( $r [ 0 ][ 'xchan_pubkey' ]) {
if ( Libzot :: verify ( $x [ 'body' ], $arr [ 'sig' ], $r [ 0 ][ 'xchan_pubkey' ])) {
$arr [ 'item_verified' ] = 1 ;
}
else {
logger ( 'get_item_elements: message verification failed.' );
}
}
else {
2023-02-11 21:22:06 +00:00
// If we don't have a public key, strip the signature, so that it won't show as invalid.
2022-07-20 00:43:27 +00:00
// This won't happen in normal use, but could happen if import_author_xchan()
// failed to load the zot-info packet due to a server failure and had
// to create an alternate xchan with network 'unknown'
unset ( $arr [ 'sig' ]);
}
}
}
// Strip old-style hubzilla bookmarks
// Do this after signature verification
2022-09-17 00:25:44 +00:00
if ( str_contains ( $x [ 'body' ], " #^[ " )) {
2022-07-20 00:43:27 +00:00
$x [ 'body' ] = str_replace ( " #^[ " , " [ " , $x [ 'body' ]);
}
// if the input is markdown, remove one level of html escaping.
// It will be re-applied in item_store() and/or item_store_update().
// Do this after signature checking as the original signature
// was generated on the escaped content.
if ( $arr [ 'mimetype' ] === 'text/markdown' ) {
$arr [ 'summary' ] = MarkdownSoap :: unescape ( $arr [ 'summary' ]);
$arr [ 'body' ] = MarkdownSoap :: unescape ( $arr [ 'body' ]);
}
if ( $mirror ) {
// extended export encoding
$arr [ 'revision' ] = $x [ 'revision' ];
$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 [ 'layout_mid' ] = $x [ 'layout_mid' ];
$arr [ 'postopts' ] = $x [ 'postopts' ];
$arr [ 'resource_id' ] = $x [ 'resource_id' ];
$arr [ 'resource_type' ] = $x [ 'resource_type' ];
$arr [ 'item_origin' ] = intval ( $x [ 'item_origin' ]);
$arr [ 'item_unseen' ] = intval ( $x [ 'item_unseen' ]);
$arr [ 'item_starred' ] = intval ( $x [ 'item_starred' ]);
$arr [ 'item_uplink' ] = intval ( $x [ 'item_uplink' ]);
$arr [ 'item_consensus' ] = intval ( $x [ 'item_consensus' ]);
$arr [ 'item_wall' ] = intval ( $x [ 'item_wall' ]);
$arr [ 'item_thread_top' ] = intval ( $x [ 'item_thread_top' ]);
$arr [ 'item_notshown' ] = intval ( $x [ 'item_notshown' ]);
$arr [ 'item_nsfw' ] = intval ( $x [ 'item_nsfw' ]);
// local only $arr['item_relay'] = $x['item_relay'];
$arr [ 'item_mentionsme' ] = intval ( $x [ 'item_mentionsme' ]);
$arr [ 'item_nocomment' ] = intval ( $x [ 'item_nocomment' ]);
$arr [ 'item_obscured' ] = intval ( $x [ 'item_obscured' ]);
// local only $arr['item_verified'] = $x['item_verified'];
$arr [ 'item_retained' ] = intval ( $x [ 'item_retained' ]);
$arr [ 'item_rss' ] = intval ( $x [ 'item_rss' ]);
$arr [ 'item_deleted' ] = intval ( $x [ 'item_deleted' ]);
$arr [ 'item_type' ] = intval ( $x [ 'item_type' ]);
$arr [ 'item_hidden' ] = intval ( $x [ 'item_hidden' ]);
$arr [ 'item_unpublished' ] = intval ( $x [ 'item_unpublished' ]);
$arr [ 'item_delayed' ] = intval ( $x [ 'item_delayed' ]);
$arr [ 'item_pending_remove' ] = intval ( $x [ 'item_pending_remove' ]);
$arr [ 'item_blocked' ] = intval ( $x [ 'item_blocked' ]);
$arr [ 'item_restrict' ] = isset ( $x [ 'item_restrict' ]) ? intval ( $x [ 'item_restrict' ]) : 0 ;
$arr [ 'item_flags' ] = isset ( $x [ 'item_flags' ]) ? intval ( $x [ 'item_flags' ]) : 0 ;
}
return $arr ;
2012-07-30 05:43:51 +00:00
}
2012-12-06 00:44:07 +00:00
2012-11-17 20:55:59 +00:00
function import_author_xchan ( $x ) {
2013-01-29 04:51:37 +00:00
2022-07-20 00:43:27 +00:00
if ( ! $x ) {
return false ;
}
$arr = [
'xchan' => $x ,
'xchan_hash' => ''
];
/**
* @ hooks import_author_xchan
* Called when looking up an author of a post by xchan_hash to ensure they have an xchan record on our site .
* * \e array \b xchan
* * \e string \b xchan_hash - The returned value
*/
Hook :: call ( 'import_author_xchan' , $arr );
if ( $arr [ 'xchan_hash' ])
return $arr [ 'xchan_hash' ];
$y = false ;
if (( ! array_key_exists ( 'network' , $x )) || in_array ( $x [ 'network' ], [ 'nomad' , 'zot6' ])) {
$y = Libzot :: import_author_zot ( $x );
}
// if we were told that it's a zot connection, don't probe/import anything else
if ( array_key_exists ( 'network' , $x ) && in_array ( $x [ 'network' ], [ 'nomad' , 'zot6' ])) {
return $y ;
}
if ( ! $y ) {
$y = import_author_activitypub ( $x );
}
if ( ! $y ) {
$y = import_author_unknown ( $x );
}
return ( $y );
2014-02-17 22:30:02 +00:00
}
2018-08-23 06:04:37 +00:00
function import_author_activitypub ( $x ) {
2022-07-20 00:43:27 +00:00
if ( ! $x [ 'url' ])
return false ;
2018-08-23 06:04:37 +00:00
2018-12-12 04:30:47 +00:00
// let somebody upgrade from an 'unknown' connection which has no xchan_addr and resolve issues with identities from multiple protocols using the same url
2022-06-17 02:46:54 +00:00
$r = q ( " select xchan_hash, xchan_url, xchan_network, xchan_name, xchan_photo_s from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' and hubloc_deleted = 0 " ,
2018-08-23 06:04:37 +00:00
dbesc ( $x [ 'url' ])
);
if ( ! $r ) {
2018-12-12 04:30:47 +00:00
$r = q ( " select xchan_hash, xchan_url, xchan_network, xchan_name, xchan_photo_s from xchan where xchan_hash = '%s' " ,
2018-08-23 06:04:37 +00:00
dbesc ( $x [ 'url' ])
);
}
if ( $r ) {
2022-07-20 00:43:27 +00:00
$ptr = null ;
foreach ( $r as $rv ) {
2022-09-17 00:25:44 +00:00
if ( str_contains ( $rv [ 'xchan_network' ], 'zot' ) || str_contains ( $rv [ 'xchan_network' ], 'nomad' )) {
2022-07-20 00:43:27 +00:00
$ptr = $rv ;
}
}
if ( ! $ptr ) {
$ptr = $r [ 0 ];
}
2018-12-12 04:30:47 +00:00
logger ( 'in_cache: ' . $ptr [ 'xchan_name' ], LOGGER_DATA );
return $ptr [ 'xchan_hash' ];
2018-08-23 06:04:37 +00:00
}
2024-02-09 20:37:26 +00:00
$z = discover_resource ( $x [ 'url' ], verify : false );
2018-08-23 06:04:37 +00:00
if ( $z ) {
2022-07-20 00:43:27 +00:00
$r = q ( " select xchan_hash, xchan_url, xchan_network, xchan_name, xchan_photo_s from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' and hubloc_deleted = 0 " ,
2018-08-23 06:04:37 +00:00
dbesc ( $x [ 'url' ])
);
if ( ! $r ) {
2018-12-12 04:30:47 +00:00
$r = q ( " select xchan_hash, xchan_url, xchan_name, xchan_photo_s from xchan where xchan_hash = '%s' " ,
2018-08-23 06:04:37 +00:00
dbesc ( $x [ 'url' ])
);
}
if ( $r ) {
2022-07-20 00:43:27 +00:00
foreach ( $r as $rv ) {
2022-09-17 00:25:44 +00:00
if ( str_contains ( $rv [ 'xchan_network' ], 'zot' )) {
2022-07-20 00:43:27 +00:00
return $rv [ 'xchan_hash' ];
}
}
2018-08-23 06:04:37 +00:00
return $r [ 0 ][ 'xchan_hash' ];
}
}
return false ;
}
2014-09-12 00:27:49 +00:00
function import_author_unknown ( $x ) {
2022-07-20 00:43:27 +00:00
$arr = [
'author' => $x ,
'result' => false
];
/**
* @ hooks import_author
* * \e array \b author
* * \e boolean | string \b result - Return value , default false
*/
Hook :: call ( 'import_author' , $arr );
if ( $arr [ 'result' ])
return $arr [ 'result' ];
if ( ! $x [ 'url' ])
return false ;
$r = q ( " select xchan_hash from xchan where xchan_network = 'unknown' and xchan_url = '%s' limit 1 " ,
dbesc ( $x [ 'url' ])
);
if ( $r ) {
logger ( 'In cache' , LOGGER_DEBUG );
return $r [ 0 ][ 'xchan_hash' ];
}
$name = trim ( $x [ 'name' ]);
$r = xchan_store_lowlevel (
[
'xchan_hash' => $x [ 'url' ],
'xchan_guid' => $x [ 'url' ],
'xchan_url' => $x [ 'url' ],
'xchan_updated' => datetime_convert (),
2022-08-14 09:20:43 +00:00
'xchan_name' => (( $name ) ? : t ( '(Unknown)' )),
2022-07-20 00:43:27 +00:00
'xchan_name_date' => datetime_convert (),
'xchan_network' => 'unknown'
]
);
if ( $r && $x [ 'photo' ]) {
$photos = import_remote_xchan_photo ( $x [ 'photo' ][ 'src' ], $x [ 'url' ]);
if ( $photos ) {
$r = q ( " update xchan set xchan_updated = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s' and xchan_network = 'unknown' " ,
dbesc ( datetime_convert ()),
dbesc ( datetime_convert ()),
dbesc ( $photos [ 0 ]),
dbesc ( $photos [ 1 ]),
dbesc ( $photos [ 2 ]),
dbesc ( $photos [ 3 ]),
dbesc ( $x [ 'url' ])
);
if ( $r )
return $x [ 'url' ];
}
}
return false ;
2014-09-12 00:27:49 +00:00
}
2018-03-12 22:47:33 +00:00
function empty_acl ( $item ) {
2022-08-14 09:20:43 +00:00
return $item [ 'allow_cid' ] === EMPTY_STR && $item [ 'allow_gid' ] === EMPTY_STR && $item [ 'deny_cid' ] === EMPTY_STR && $item [ 'deny_gid' ] === EMPTY_STR ;
2018-03-12 22:47:33 +00:00
}
2014-09-16 10:33:48 +00:00
function encode_item ( $item , $mirror = false ) {
2022-07-20 00:43:27 +00:00
$x = [];
$x [ 'type' ] = 'activity' ;
$x [ 'encoding' ] = 'zot' ;
2023-02-11 21:22:06 +00:00
// If we're trying to back up an item so that it's recoverable or for export/imprt,
2022-07-20 00:43:27 +00:00
// add all the attributes we need to recover it
if ( $mirror ) {
$x [ 'id' ] = $item [ 'id' ];
$x [ 'parent' ] = $item [ 'parent' ];
$x [ 'uid' ] = $item [ 'uid' ];
$x [ 'allow_cid' ] = $item [ 'allow_cid' ];
$x [ 'allow_gid' ] = $item [ 'allow_gid' ];
$x [ 'deny_cid' ] = $item [ 'deny_cid' ];
$x [ 'deny_gid' ] = $item [ 'deny_gid' ];
$x [ 'revision' ] = $item [ 'revision' ];
$x [ 'layout_mid' ] = $item [ 'layout_mid' ];
$x [ 'postopts' ] = $item [ 'postopts' ];
$x [ 'resource_id' ] = $item [ 'resource_id' ];
$x [ 'resource_type' ] = $item [ 'resource_type' ];
$x [ 'attach' ] = $item [ 'attach' ];
$x [ 'item_origin' ] = $item [ 'item_origin' ];
$x [ 'item_unseen' ] = $item [ 'item_unseen' ];
$x [ 'item_starred' ] = $item [ 'item_starred' ];
$x [ 'item_uplink' ] = $item [ 'item_uplink' ];
$x [ 'item_consensus' ] = $item [ 'item_consensus' ];
$x [ 'item_wall' ] = $item [ 'item_wall' ];
$x [ 'item_thread_top' ] = $item [ 'item_thread_top' ];
$x [ 'item_notshown' ] = $item [ 'item_notshown' ];
$x [ 'item_nsfw' ] = $item [ 'item_nsfw' ];
$x [ 'item_relay' ] = $item [ 'item_relay' ];
$x [ 'item_mentionsme' ] = $item [ 'item_mentionsme' ];
$x [ 'item_nocomment' ] = $item [ 'item_nocomment' ];
$x [ 'item_obscured' ] = $item [ 'item_obscured' ];
$x [ 'item_verified' ] = $item [ 'item_verified' ];
$x [ 'item_retained' ] = $item [ 'item_retained' ];
$x [ 'item_rss' ] = $item [ 'item_rss' ];
$x [ 'item_deleted' ] = $item [ 'item_deleted' ];
$x [ 'item_type' ] = $item [ 'item_type' ];
$x [ 'item_hidden' ] = $item [ 'item_hidden' ];
$x [ 'item_unpublished' ] = $item [ 'item_unpublished' ];
$x [ 'item_delayed' ] = $item [ 'item_delayed' ];
$x [ 'item_pending_remove' ] = $item [ 'item_pending_remove' ];
$x [ 'item_blocked' ] = $item [ 'item_blocked' ];
}
$x [ 'uuid' ] = $item [ 'uuid' ];
$x [ 'message_id' ] = $item [ 'mid' ];
2022-12-02 19:40:55 +00:00
$x [ 'approved' ] = $item [ 'approved' ];
2022-07-20 00:43:27 +00:00
$x [ 'message_top' ] = $item [ 'parent_mid' ];
$x [ 'message_parent' ] = $item [ 'thr_parent' ];
$x [ 'created' ] = $item [ 'created' ];
$x [ 'edited' ] = $item [ 'edited' ];
// always send 0's over the wire
2023-10-25 10:02:15 +00:00
$x [ 'expires' ] = (( $item [ 'expires' ] === '0001-01-01 00:00:00' ) ? '0000-00-00 00:00:00' : $item [ 'expires' ]);
2022-07-20 00:43:27 +00:00
$x [ 'commented' ] = $item [ 'commented' ];
$x [ 'mimetype' ] = $item [ 'mimetype' ];
$x [ 'title' ] = $item [ 'title' ];
$x [ 'summary' ] = $item [ 'summary' ];
$x [ 'body' ] = $item [ 'body' ];
$x [ 'app' ] = $item [ 'app' ];
$x [ 'verb' ] = $item [ 'verb' ];
$x [ 'object_type' ] = $item [ 'obj_type' ];
$x [ 'target_type' ] = $item [ 'tgt_type' ];
$x [ 'permalink' ] = $item [ 'plink' ];
$x [ 'location' ] = $item [ 'location' ];
2022-09-16 10:57:04 +00:00
$x [ 'longlat' ] = ( $item [ 'lat' ] || $item [ 'lon' ]) ? $item [ 'lat' ] . ' ' . $item [ 'lon' ] : 0.0 ;
2022-07-20 00:43:27 +00:00
$x [ 'signature' ] = $item [ 'sig' ];
$x [ 'replyto' ] = $item [ 'replyto' ];
$x [ 'owner' ] = encode_item_xchan ( $item [ 'owner' ]);
$x [ 'author' ] = encode_item_xchan ( $item [ 'author' ]);
if ( $item [ 'obj' ])
$x [ 'object' ] = json_decode ( $item [ 'obj' ], true );
if ( $item [ 'target' ])
$x [ 'target' ] = json_decode ( $item [ 'target' ], true );
if ( $item [ 'attach' ])
$x [ 'attach' ] = json_decode ( $item [ 'attach' ], true );
if ( $y = encode_item_flags ( $item ))
$x [ 'flags' ] = $y ;
if ( $item [ 'comments_closed' ] > NULL_DATE ) {
$x [ 'comments_closed' ] = $item [ 'comments_closed' ];
2022-03-20 21:39:58 +00:00
}
2014-08-28 23:56:13 +00:00
2022-03-20 21:39:58 +00:00
$x [ 'comment_scope' ] = $item [ 'comment_policy' ];
2013-06-17 02:14:01 +00:00
2022-07-20 00:43:27 +00:00
if ( $item [ 'term' ])
$x [ 'tags' ] = encode_item_terms ( $item [ 'term' ], $mirror );
2012-11-15 07:09:25 +00:00
2022-07-20 00:43:27 +00:00
if ( $item [ 'iconfig' ])
$x [ 'meta' ] = encode_item_meta ( $item [ 'iconfig' ], $mirror );
2016-02-17 04:49:32 +00:00
2017-03-09 19:51:21 +00:00
2022-07-20 00:43:27 +00:00
logger ( 'encode_item: ' . print_r ( $x , true ), LOGGER_DATA );
2013-10-03 04:04:48 +00:00
2022-07-20 00:43:27 +00:00
return $x ;
2012-08-16 06:15:29 +00:00
}
2015-04-09 22:28:23 +00:00
/**
* @ brief
*
* @ param int $scope
* @ param boolean $strip ( optional ) default false
* @ return string
*/
function map_scope ( $scope , $strip = false ) {
2022-07-20 00:43:27 +00:00
switch ( $scope ) {
case 0 :
return 'self' ;
case PERMS_PUBLIC :
if ( $strip )
return '' ;
return 'public' ;
case PERMS_NETWORK :
return 'network: red' ;
case PERMS_AUTHED :
return 'authenticated' ;
case PERMS_SITE :
return 'site: ' . App :: get_hostname ();
case PERMS_PENDING :
return 'any connections' ;
2022-10-12 11:00:23 +00:00
case PERMS_SPECIFIC :
return 'specific' ;
2022-07-20 00:43:27 +00:00
case PERMS_CONTACTS :
default :
return 'contacts' ;
}
2015-03-21 23:06:08 +00:00
}
2013-02-15 04:17:30 +00:00
2015-04-09 22:28:23 +00:00
/**
* @ brief Returns a descriptive text for a given $scope .
*
* @ param string $scope
* @ return string translated string describing the scope
*/
2014-08-07 02:24:46 +00:00
function translate_scope ( $scope ) {
2022-07-20 00:43:27 +00:00
if ( ! $scope || $scope === 'public' )
return t ( 'Visible to anybody on the internet.' );
2022-09-17 00:25:44 +00:00
if ( str_starts_with ( $scope , 'self' ))
2022-07-20 00:43:27 +00:00
return t ( 'Visible to you only.' );
2022-09-17 00:25:44 +00:00
if ( str_starts_with ( $scope , 'network:' ))
2022-07-20 00:43:27 +00:00
return t ( 'Visible to anybody in this network.' );
2022-09-17 00:25:44 +00:00
if ( str_starts_with ( $scope , 'authenticated' ))
2022-07-20 00:43:27 +00:00
return t ( 'Visible to anybody authenticated.' );
2022-09-17 00:25:44 +00:00
if ( str_starts_with ( $scope , 'site:' ))
2022-07-20 00:43:27 +00:00
return sprintf ( t ( 'Visible to anybody on %s.' ), strip_tags ( substr ( $scope , 6 )));
2022-09-17 00:25:44 +00:00
if ( str_starts_with ( $scope , 'any connections' ))
2022-07-20 00:43:27 +00:00
return t ( 'Visible to all connections.' );
2022-09-17 00:25:44 +00:00
if ( str_starts_with ( $scope , 'contacts' ))
2022-07-20 00:43:27 +00:00
return t ( 'Visible to approved connections.' );
2022-09-17 00:25:44 +00:00
if ( str_starts_with ( $scope , 'specific' ))
2022-07-20 00:43:27 +00:00
return t ( 'Visible to specific connections.' );
2022-08-14 09:20:43 +00:00
return '' ; // This shouldn't happen.
2014-08-07 02:24:46 +00:00
}
2013-02-15 04:17:30 +00:00
2015-04-09 22:28:23 +00:00
/**
* @ brief
*
* @ param array $xchan
* @ return array an associative array
*/
2012-11-16 05:52:05 +00:00
function encode_item_xchan ( $xchan ) {
2022-07-20 00:43:27 +00:00
$ret = [];
$ret [ 'name' ] = $xchan [ 'xchan_name' ];
$ret [ 'address' ] = $xchan [ 'xchan_addr' ];
$ret [ 'url' ] = $xchan [ 'xchan_url' ];
$ret [ 'network' ] = $xchan [ 'xchan_network' ];
2022-11-20 01:27:35 +00:00
$ret [ 'photo' ] = [
'mimetype' => $xchan [ 'xchan_photo_mimetype' ],
'src' => $xchan [ 'xchan_photo_m' ]
];
2022-07-20 00:43:27 +00:00
$ret [ 'id' ] = $xchan [ 'xchan_guid' ];
$ret [ 'id_sig' ] = $xchan [ 'xchan_guid_sig' ];
$ret [ 'key' ] = $xchan [ 'xchan_pubkey' ];
return $ret ;
2012-11-16 05:52:05 +00:00
}
2012-08-16 06:15:29 +00:00
2015-09-09 02:14:29 +00:00
function encode_item_terms ( $terms , $mirror = false ) {
2022-07-20 00:43:27 +00:00
$ret = [];
2012-11-16 05:52:05 +00:00
2022-09-17 00:25:44 +00:00
$allowed_export_terms = [ TERM_UNKNOWN , TERM_HASHTAG , TERM_MENTION , TERM_CATEGORY , TERM_BOOKMARK , TERM_COMMUNITYTAG , TERM_FORUM ];
2012-11-16 05:52:05 +00:00
2022-07-20 00:43:27 +00:00
if ( $mirror ) {
$allowed_export_terms [] = TERM_PCATEGORY ;
$allowed_export_terms [] = TERM_FILE ;
}
2015-09-09 02:14:29 +00:00
2022-07-20 00:43:27 +00:00
if ( $terms ) {
foreach ( $terms as $term ) {
if ( in_array ( $term [ 'ttype' ], $allowed_export_terms ))
2022-09-17 00:25:44 +00:00
$ret [] = [ 'tag' => $term [ 'term' ], 'url' => $term [ 'url' ], 'type' => termtype ( $term [ 'ttype' ])];
2022-07-20 00:43:27 +00:00
}
}
2015-04-09 22:28:23 +00:00
2022-07-20 00:43:27 +00:00
return $ret ;
2012-11-16 05:52:05 +00:00
}
2016-02-17 04:49:32 +00:00
function encode_item_meta ( $meta , $mirror = false ) {
2022-07-20 00:43:27 +00:00
$ret = [];
2016-02-17 04:49:32 +00:00
2022-07-20 00:43:27 +00:00
if ( $meta ) {
foreach ( $meta as $m ) {
if ( $m [ 'sharing' ] || $mirror )
2022-09-17 00:25:44 +00:00
$ret [] = [ 'family' => $m [ 'cat' ], 'key' => $m [ 'k' ], 'value' => $m [ 'v' ], 'sharing' => intval ( $m [ 'sharing' ])];
2022-07-20 00:43:27 +00:00
}
}
2016-02-17 04:49:32 +00:00
2022-07-20 00:43:27 +00:00
return $ret ;
2016-02-17 04:49:32 +00:00
}
function decode_item_meta ( $meta ) {
2022-07-20 00:43:27 +00:00
$ret = [];
if ( is_array ( $meta ) && $meta ) {
foreach ( $meta as $m ) {
2022-09-17 00:25:44 +00:00
$ret [] = [ 'cat' => escape_tags ( $m [ 'family' ]), 'k' => escape_tags ( $m [ 'key' ]), 'v' => $m [ 'value' ], 'sharing' => $m [ 'sharing' ]];
2022-07-20 00:43:27 +00:00
}
}
return $ret ;
2016-02-17 04:49:32 +00:00
}
2015-04-09 22:28:23 +00:00
/**
* @ brief
*
* @ param int $t
* @ return string
*/
2012-11-16 05:52:05 +00:00
function termtype ( $t ) {
2022-11-20 01:27:35 +00:00
$types = [ 'unknown' , 'hashtag' , 'mention' , 'category' , 'personal_category' , 'file' ,
'search' , 'thing' , 'bookmark' , 'hierarchy' , 'communitytag' , 'forum' ];
2015-04-09 22:28:23 +00:00
2022-08-14 09:20:43 +00:00
return (( $types [ $t ]) ? : 'unknown' );
2012-11-16 05:52:05 +00:00
}
2010-07-19 03:49:10 +00:00
2015-04-09 22:28:23 +00:00
/**
* @ brief
*
* @ param array $t
* @ return array | string empty string or array containing associative arrays with
* * \e string \b term
* * \e string \b url
* * \e int \b type
*/
2012-11-17 20:55:59 +00:00
function decode_tags ( $t ) {
2022-07-20 00:43:27 +00:00
if ( $t ) {
$ret = [];
foreach ( $t as $x ) {
$tag = [];
$tag [ 'term' ] = htmlspecialchars ( $x [ 'tag' ], ENT_COMPAT , 'UTF-8' , false );
$tag [ 'url' ] = htmlspecialchars ( $x [ 'url' ], ENT_COMPAT , 'UTF-8' , false );
switch ( $x [ 'type' ]) {
case 'hashtag' :
$tag [ 'ttype' ] = TERM_HASHTAG ;
break ;
case 'mention' :
$tag [ 'ttype' ] = TERM_MENTION ;
break ;
case 'category' :
$tag [ 'ttype' ] = TERM_CATEGORY ;
break ;
case 'personal_category' :
$tag [ 'ttype' ] = TERM_PCATEGORY ;
break ;
case 'file' :
$tag [ 'ttype' ] = TERM_FILE ;
break ;
case 'search' :
2022-08-14 09:20:43 +00:00
$tag [ 'ttype' ] = TERM_SAVEDSEARCH ;
2022-07-20 00:43:27 +00:00
break ;
case 'thing' :
$tag [ 'ttype' ] = TERM_THING ;
break ;
case 'bookmark' :
$tag [ 'ttype' ] = TERM_BOOKMARK ;
break ;
case 'communitytag' :
$tag [ 'ttype' ] = TERM_COMMUNITYTAG ;
break ;
case 'forum' :
$tag [ 'ttype' ] = TERM_FORUM ;
break ;
default :
case 'unknown' :
$tag [ 'ttype' ] = TERM_UNKNOWN ;
break ;
}
$ret [] = $tag ;
}
return $ret ;
}
return '' ;
2012-11-17 20:55:59 +00:00
}
2021-02-23 01:12:41 +00:00
function purify_imported_object ( $obj ) {
2022-07-20 00:43:27 +00:00
$ret = null ;
if ( is_array ( $obj )) {
foreach ( $obj as $k => $v ) {
if ( is_array ( $v )) {
$ret [ $k ] = purify_imported_object ( $v );
}
elseif ( is_string ( $v )) {
$ret [ $k ] = purify_html ( $v );
}
}
}
elseif ( is_string ( $obj )) {
$ret = purify_html ( $obj );
}
return $ret ;
2021-02-23 01:12:41 +00:00
}
2015-03-21 23:06:08 +00:00
/**
2021-02-23 01:12:41 +00:00
* @ brief Sanitise a potentially complex array .
*
* Walks the array and applies htmlspecialchars to the content unless it is a known HTML element ,
* in which case the result is purified
2015-03-21 23:06:08 +00:00
*
* @ param array $arr
* @ return array | string
*/
2022-07-27 22:16:27 +00:00
2012-11-17 20:55:59 +00:00
function activity_sanitise ( $arr ) {
2022-07-20 00:43:27 +00:00
if ( $arr ) {
if ( is_array ( $arr )) {
$ret = [];
foreach ( $arr as $k => $x ) {
if ( $k === 'source' && array_path_exists ( 'source/mediaType' , $arr )) {
if ( array_path_exists ( 'source/content' , $arr ) && isset ( $arr [ 'source' ][ 'mediaType' ]) && in_array ( $arr [ 'source' ][ 'mediaType' ], [ 'text/bbcode' , 'text/x-multicode' ])) {
// @FIXME multicode
$ret [ $k ] = [ 'mediaType' => 'text/x-multicode' ];
if ( is_string ( $arr [ 'source' ][ 'content' ])) {
$ret [ $k ][ 'content' ] = multicode_purify ( $arr [ 'source' ][ 'content' ]);
}
if ( array_path_exists ( 'source/summary' , $arr ) && is_string ( $arr [ 'source' ][ 'summary' ])) {
$ret [ $k ][ 'summary' ] = multicode_purify ( $arr [ 'source' ][ 'summary' ]);
}
continue ;
}
}
if ( in_array ( $k , [ 'content' , 'summary' , 'contentMap' , 'summaryMap' ])) {
2022-08-14 09:20:43 +00:00
$ret [ $k ] = purify_imported_object ( $x );
2022-07-20 00:43:27 +00:00
continue ;
}
if ( is_array ( $x ))
$ret [ $k ] = activity_sanitise ( $x );
else
2023-11-04 22:15:53 +00:00
$ret [ $k ] = htmlspecialchars (( isset ( $x ) ? $x : '' ), ENT_COMPAT , 'UTF-8' , false );
2022-07-20 00:43:27 +00:00
}
return $ret ;
}
else {
return htmlspecialchars ( $arr , ENT_COMPAT , 'UTF-8' , false );
}
}
return '' ;
2012-11-17 20:55:59 +00:00
}
2012-11-16 21:57:55 +00:00
2015-03-21 23:06:08 +00:00
/**
* @ brief Sanitise a simple linear array .
*
* @ param array $arr
* @ return array | string
*/
2012-12-22 12:33:32 +00:00
function array_sanitise ( $arr ) {
2022-07-20 00:43:27 +00:00
if ( $arr ) {
$ret = [];
foreach ( $arr as $x ) {
$ret [] = htmlspecialchars ( $x , ENT_COMPAT , 'UTF-8' , false );
}
return $ret ;
}
return '' ;
2012-12-22 12:33:32 +00:00
}
2012-11-16 21:57:55 +00:00
function encode_item_flags ( $item ) {
2022-07-20 00:43:27 +00:00
// most of item_flags and item_restrict are local settings which don't apply when transmitted.
2012-11-16 21:57:55 +00:00
// We may need those for the case of syncing other hub locations which you are attached to.
2022-07-20 00:43:27 +00:00
$ret = [];
if ( intval ( $item [ 'item_deleted' ]))
$ret [] = 'deleted' ;
if ( intval ( $item [ 'item_hidden' ]))
$ret [] = 'hidden' ;
if ( intval ( $item [ 'item_notshown' ]))
$ret [] = 'notshown' ;
if ( intval ( $item [ 'item_thread_top' ]))
$ret [] = 'thread_parent' ;
if ( intval ( $item [ 'item_nsfw' ]))
$ret [] = 'nsfw' ;
if ( intval ( $item [ 'item_consensus' ]))
$ret [] = 'consensus' ;
if ( intval ( $item [ 'item_obscured' ]))
$ret [] = 'obscured' ;
if ( intval ( $item [ 'item_private' ]))
$ret [] = 'private' ;
if ( intval ( $item [ 'item_private' ]) === 2 )
2021-05-21 01:01:42 +00:00
$ret [] = 'direct' ;
2015-03-21 23:06:08 +00:00
2022-07-20 00:43:27 +00:00
return $ret ;
2012-11-16 21:57:55 +00:00
}
2023-07-22 23:01:55 +00:00
// Superceded by Libzotdir::import_directory_profile
// This is probably no longer needed.
2012-12-06 00:44:07 +00:00
2023-07-22 12:06:41 +00:00
function get_profile_elements ( $profile ) {
2012-12-22 12:33:32 +00:00
2022-07-20 00:43:27 +00:00
$arr = [];
2012-12-22 12:33:32 +00:00
2023-07-22 23:01:55 +00:00
$xchan_hash = import_author_xchan ( $profile [ 'from' ]);
if ( $xchan_hash ) {
2022-07-20 00:43:27 +00:00
$arr [ 'xprof_hash' ] = $xchan_hash ;
2023-07-22 23:01:55 +00:00
}
else {
2022-07-20 00:43:27 +00:00
return [];
2023-07-22 23:01:55 +00:00
}
2012-12-22 12:33:32 +00:00
2023-07-22 12:06:41 +00:00
$arr [ 'desc' ] = (( $profile [ 'title' ]) ? htmlspecialchars ( $profile [ 'title' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
2012-12-22 12:33:32 +00:00
2023-07-22 12:06:41 +00:00
$arr [ 'dob' ] = datetime_convert ( 'UTC' , 'UTC' , $profile [ 'birthday' ], 'Y-m-d' );
$arr [ 'age' ] = (( $profile [ 'age' ]) ? intval ( $profile [ 'age' ]) : 0 );
2012-12-22 12:33:32 +00:00
2023-07-22 12:06:41 +00:00
$arr [ 'gender' ] = (( $profile [ 'gender' ]) ? htmlspecialchars ( $profile [ 'gender' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'marital' ] = (( $profile [ 'marital' ]) ? htmlspecialchars ( $profile [ 'marital' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'sexual' ] = (( $profile [ 'sexual' ]) ? htmlspecialchars ( $profile [ 'sexual' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'locale' ] = (( $profile [ 'locale' ]) ? htmlspecialchars ( $profile [ 'locale' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'region' ] = (( $profile [ 'region' ]) ? htmlspecialchars ( $profile [ 'region' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'postcode' ] = (( $profile [ 'postcode' ]) ? htmlspecialchars ( $profile [ 'postcode' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
$arr [ 'country' ] = (( $profile [ 'country' ]) ? htmlspecialchars ( $profile [ 'country' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
2012-12-22 12:33:32 +00:00
2023-07-22 12:06:41 +00:00
$arr [ 'keywords' ] = (( $profile [ 'keywords' ] && is_array ( $profile [ 'keywords' ])) ? array_sanitise ( $profile [ 'keywords' ]) : []);
2012-12-22 12:33:32 +00:00
2022-07-20 00:43:27 +00:00
return $arr ;
2012-12-22 12:33:32 +00:00
}
2010-11-03 23:48:21 +00:00
2017-09-04 22:23:42 +00:00
/**
* @ brief Signs an item body .
*
* @ param [ in , out ] array $item
*/
2017-03-17 01:19:03 +00:00
function item_sign ( & $item ) {
2022-07-20 00:43:27 +00:00
if ( array_key_exists ( 'sig' , $item ) && $item [ 'sig' ])
return ;
2017-03-17 01:19:03 +00:00
2022-07-20 00:43:27 +00:00
$r = q ( " select channel_prvkey from channel where channel_id = %d and channel_hash = '%s' " ,
intval ( $item [ 'uid' ]),
dbesc ( $item [ 'author_xchan' ])
);
if ( ! $r )
return ;
2017-03-17 01:19:03 +00:00
2022-07-20 00:43:27 +00:00
$item [ 'sig' ] = Libzot :: sign ( $item [ 'body' ], $r [ 0 ][ 'channel_prvkey' ]);
$item [ 'item_verified' ] = 1 ;
2017-03-17 01:19:03 +00:00
}
2021-06-19 23:04:32 +00:00
// packs json data for storage.
// if it is a string, check if it is already json encoded.
2023-02-11 21:22:06 +00:00
// Otherwise, json encode it
2021-06-19 23:04:32 +00:00
// If it is an array, sanitise it and then json_encode it.
function item_json_encapsulate ( $arr , $k ) {
2022-07-20 00:43:27 +00:00
$retval = null ;
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ $k ])) {
if ( is_string ( $arr [ $k ])) {
// determine if it is json encoded already
$test = json_decode ( $arr [ $k ]);
// assume it is json encoded already
$retval = $arr [ $k ];
if ( $test === NULL ) {
$retval = json_encode ( $arr [ $k ], JSON_UNESCAPED_SLASHES );
}
}
else {
activity_sanitise ( $arr [ $k ]);
$retval = json_encode ( $arr [ $k ], JSON_UNESCAPED_SLASHES );
}
}
return $retval ;
2021-06-19 23:04:32 +00:00
}
2017-03-17 01:19:03 +00:00
2015-04-09 22:28:23 +00:00
/**
2017-09-04 22:23:42 +00:00
* @ brief Stores an item type record .
2015-04-09 22:28:23 +00:00
*
* @ param array $arr
2016-10-01 22:41:25 +00:00
* @ param boolean $deliver ( optional ) default true
*
2015-04-09 22:28:23 +00:00
* @ return array
* * \e boolean \b success
* * \e int \b item_id
*/
2024-02-10 19:22:39 +00:00
function item_store ( $arr , $deliver = true , $addAndSync = true ) {
2010-07-19 03:49:10 +00:00
2022-07-20 00:43:27 +00:00
$d = [
'item' => $arr ,
];
/**
* @ hooks item_store_before
* Called when item_store () stores a record of type item .
* * \e array \b item
*/
Hook :: call ( 'item_store_before' , $d );
$arr = $d [ 'item' ];
2013-08-02 02:18:05 +00:00
2022-09-17 00:25:44 +00:00
$ret = [ 'success' => false , 'item_id' => 0 ];
2016-09-22 23:52:25 +00:00
2022-07-20 00:43:27 +00:00
if ( array_key_exists ( 'cancel' , $arr ) && $arr [ 'cancel' ]) {
logger ( 'cancelled by plugin' );
return $ret ;
}
2013-08-02 01:50:36 +00:00
2022-07-20 00:43:27 +00:00
if ( ! $arr [ 'uid' ]) {
logger ( 'item_store: no uid' );
$ret [ 'message' ] = 'No uid.' ;
return $ret ;
}
2017-03-17 01:19:03 +00:00
2022-07-20 00:43:27 +00:00
//$uplinked_comment = false;
2013-06-17 03:44:29 +00:00
2022-07-20 00:43:27 +00:00
// If a page layout is provided, ensure it exists and belongs to us.
2010-12-05 07:16:16 +00:00
2022-07-20 00:43:27 +00:00
if ( array_key_exists ( 'layout_mid' , $arr ) && $arr [ 'layout_mid' ]) {
$l = q ( " select item_type from item where mid = '%s' and uid = %d limit 1 " ,
dbesc ( $arr [ 'layout_mid' ]),
intval ( $arr [ 'uid' ])
);
if (( ! $l ) || ( $l [ 0 ][ 'item_type' ] != ITEM_TYPE_PDL ))
unset ( $arr [ 'layout_mid' ]);
}
2010-09-26 23:30:21 +00:00
2022-07-20 00:43:27 +00:00
// Don't let anybody set these, either intentionally or accidentally
2013-02-07 04:29:17 +00:00
2022-07-20 00:43:27 +00:00
if ( array_key_exists ( 'id' , $arr ))
unset ( $arr [ 'id' ]);
if ( array_key_exists ( 'parent' , $arr ))
unset ( $arr [ 'parent' ]);
$arr [ 'mimetype' ] = (( x ( $arr , 'mimetype' )) ? notags ( trim ( $arr [ 'mimetype' ])) : 'text/x-multicode' );
$arr [ 'title' ] = (( array_key_exists ( 'title' , $arr ) && strlen ( $arr [ 'title' ])) ? trim ( escape_tags ( $arr [ 'title' ])) : '' );
$arr [ 'summary' ] = (( array_key_exists ( 'summary' , $arr ) && strlen ( $arr [ 'summary' ])) ? trim ( $arr [ 'summary' ]) : '' );
$arr [ 'body' ] = (( array_key_exists ( 'body' , $arr ) && strlen ( $arr [ 'body' ])) ? trim ( $arr [ 'body' ]) : '' );
$arr [ 'allow_cid' ] = (( x ( $arr , 'allow_cid' )) ? trim ( $arr [ 'allow_cid' ]) : '' );
$arr [ 'allow_gid' ] = (( x ( $arr , 'allow_gid' )) ? trim ( $arr [ 'allow_gid' ]) : '' );
$arr [ 'deny_cid' ] = (( x ( $arr , 'deny_cid' )) ? trim ( $arr [ 'deny_cid' ]) : '' );
$arr [ 'deny_gid' ] = (( x ( $arr , 'deny_gid' )) ? trim ( $arr [ 'deny_gid' ]) : '' );
$arr [ 'postopts' ] = (( x ( $arr , 'postopts' )) ? trim ( $arr [ 'postopts' ]) : '' );
$arr [ 'uuid' ] = (( x ( $arr , 'uuid' )) ? trim ( $arr [ 'uuid' ]) : '' );
2022-12-02 19:40:55 +00:00
$arr [ 'approved' ] = (( x ( $arr , 'approved' )) ? trim ( $arr [ 'approved' ]) : '' );
2022-07-20 00:43:27 +00:00
$arr [ 'item_private' ] = (( x ( $arr , 'item_private' )) ? intval ( $arr [ 'item_private' ]) : 0 );
$arr [ 'item_wall' ] = (( x ( $arr , 'item_wall' )) ? intval ( $arr [ 'item_wall' ]) : 0 );
$arr [ 'item_type' ] = (( x ( $arr , 'item_type' )) ? intval ( $arr [ 'item_type' ]) : 0 );
$arr [ 'item_verified' ] = (( x ( $arr , 'item_verified' )) ? intval ( $arr [ 'item_verified' ]) : 0 );
// obsolete, but needed so as not to throw not-null constraints on some database driveres
$arr [ 'item_flags' ] = (( x ( $arr , 'item_flags' )) ? intval ( $arr [ 'item_flags' ]) : 0 );
2013-08-11 23:56:06 +00:00
2022-07-05 01:34:07 +00:00
$languagetext = prepare_text ( $arr [ 'body' ],(( isset ( $arr [ 'mimetype' ])) ? $arr [ 'mimetype' ] : 'text/x-multicode' ));
$languagetext = html2plain (( isset ( $arr [ 'title' ]) && $arr [ 'title' ]) ? $arr [ 'title' ] . ' ' . $languagetext : $languagetext );
$detector = new LanguageDetect ();
$arr [ 'lang' ] = $detector -> detect ( $languagetext );
2015-03-21 23:06:08 +00:00
2022-07-20 00:43:27 +00:00
// apply the input filter here
2018-03-24 09:22:24 +00:00
2022-12-21 20:20:55 +00:00
$arr [ 'summary' ] = trim ( z_input_filter ( $arr [ 'summary' ], $arr [ 'mimetype' ]));
$arr [ 'body' ] = trim ( z_input_filter ( $arr [ 'body' ], $arr [ 'mimetype' ]));
2018-03-24 09:22:24 +00:00
2022-07-20 00:43:27 +00:00
item_sign ( $arr );
2018-03-24 09:22:24 +00:00
2022-07-20 00:43:27 +00:00
if ( ! array_key_exists ( 'sig' , $arr ))
$arr [ 'sig' ] = '' ;
$allowed_languages = get_pconfig ( $arr [ 'uid' ], 'system' , 'allowed_languages' );
if (( is_array ( $allowed_languages )) && ( $arr [ 'lang' ]) && ( ! array_key_exists ( $arr [ 'lang' ], $allowed_languages ))) {
$translate = [
'item' => $arr ,
'from' => $arr [ 'lang' ],
'to' => $allowed_languages ,
'translated' => false
];
/**
* @ hooks item_translate
* Called from item_store and item_store_update after the post language has been autodetected .
* * \e array \b item
* * \e string \b from
* * \e string \b to
* * \e boolean \b translated
*/
Hook :: call ( 'item_translate' , $translate );
if (( ! $translate [ 'translated' ]) && ( intval ( get_pconfig ( $arr [ 'uid' ], 'system' , 'reject_disallowed_languages' )))) {
logger ( 'language ' . $arr [ 'lang' ] . ' not accepted for uid ' . $arr [ 'uid' ]);
$ret [ 'message' ] = 'language not accepted' ;
return $ret ;
}
$arr = $translate [ 'item' ];
}
2022-12-31 19:04:18 +00:00
if ( isset ( $arr [ 'obj' ])) {
2022-07-20 00:43:27 +00:00
$arr [ 'obj' ] = item_json_encapsulate ( $arr , 'obj' );
}
2022-12-31 19:04:18 +00:00
if ( isset ( $arr [ 'target' ])) {
2022-07-20 00:43:27 +00:00
$arr [ 'target' ] = item_json_encapsulate ( $arr , 'target' );
}
2022-12-31 19:04:18 +00:00
if ( isset ( $arr [ 'attach' ])) {
2022-07-20 00:43:27 +00:00
$arr [ 'attach' ] = item_json_encapsulate ( $arr , 'attach' );
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'aid' ] = (( x ( $arr , 'aid' )) ? intval ( $arr [ 'aid' ]) : 0 );
$arr [ 'mid' ] = (( x ( $arr , 'mid' )) ? notags ( trim ( $arr [ 'mid' ])) : random_string ());
$arr [ 'revision' ] = (( x ( $arr , 'revision' ) && intval ( $arr [ 'revision' ]) > 0 ) ? intval ( $arr [ 'revision' ]) : 0 );
$arr [ 'author_xchan' ] = (( x ( $arr , 'author_xchan' )) ? notags ( trim ( $arr [ 'author_xchan' ])) : '' );
$arr [ 'owner_xchan' ] = (( x ( $arr , 'owner_xchan' )) ? notags ( trim ( $arr [ 'owner_xchan' ])) : '' );
$arr [ 'created' ] = (( x ( $arr , 'created' ) !== false ) ? datetime_convert ( 'UTC' , 'UTC' , $arr [ 'created' ]) : datetime_convert ());
$arr [ 'edited' ] = (( x ( $arr , 'edited' ) !== false ) ? datetime_convert ( 'UTC' , 'UTC' , $arr [ 'edited' ]) : datetime_convert ());
$arr [ 'expires' ] = (( x ( $arr , 'expires' ) !== false ) ? datetime_convert ( 'UTC' , 'UTC' , $arr [ 'expires' ]) : NULL_DATE );
$arr [ 'commented' ] = (( x ( $arr , 'commented' ) !== false ) ? datetime_convert ( 'UTC' , 'UTC' , $arr [ 'commented' ]) : datetime_convert ());
$arr [ 'comments_closed' ] = (( x ( $arr , 'comments_closed' ) !== false ) ? datetime_convert ( 'UTC' , 'UTC' , $arr [ 'comments_closed' ]) : NULL_DATE );
$arr [ 'html' ] = (( array_key_exists ( 'html' , $arr )) ? $arr [ 'html' ] : '' );
if ( $deliver ) {
$arr [ 'received' ] = datetime_convert ();
$arr [ 'changed' ] = datetime_convert ();
}
else {
// When deliver flag is false, we are *probably* performing an import or bulk migration.
// If one updates the changed timestamp it will be made available to zotfeed and delivery
// will still take place through backdoor methods. Since these fields are rarely used
// otherwise, just preserve the original timestamp.
$arr [ 'received' ] = (( x ( $arr , 'received' ) !== false ) ? datetime_convert ( 'UTC' , 'UTC' , $arr [ 'received' ]) : datetime_convert ());
$arr [ 'changed' ] = (( x ( $arr , 'changed' ) !== false ) ? datetime_convert ( 'UTC' , 'UTC' , $arr [ 'changed' ]) : datetime_convert ());
}
$arr [ 'location' ] = (( x ( $arr , 'location' )) ? notags ( trim ( $arr [ 'location' ])) : '' );
2022-09-16 10:57:04 +00:00
$arr [ 'lat' ] = (( x ( $arr , 'lat' )) ? floatval ( $arr [ 'lat' ]) : 0.0 );
$arr [ 'lon' ] = (( x ( $arr , 'lon' )) ? floatval ( $arr [ 'lon' ]) : 0.0 );
2022-07-20 00:43:27 +00:00
$arr [ 'parent_mid' ] = (( x ( $arr , 'parent_mid' )) ? notags ( trim ( $arr [ 'parent_mid' ])) : '' );
$arr [ 'thr_parent' ] = (( x ( $arr , 'thr_parent' )) ? notags ( trim ( $arr [ 'thr_parent' ])) : $arr [ 'parent_mid' ]);
$arr [ 'verb' ] = (( x ( $arr , 'verb' )) ? notags ( trim ( $arr [ 'verb' ])) : ACTIVITY_POST );
$arr [ 'obj_type' ] = (( x ( $arr , 'obj_type' )) ? notags ( trim ( $arr [ 'obj_type' ])) : ACTIVITY_OBJ_NOTE );
$arr [ 'obj' ] = (( x ( $arr , 'obj' )) ? trim ( $arr [ 'obj' ]) : '' );
$arr [ 'tgt_type' ] = (( x ( $arr , 'tgt_type' )) ? notags ( trim ( $arr [ 'tgt_type' ])) : '' );
$arr [ 'target' ] = (( x ( $arr , 'target' )) ? trim ( $arr [ 'target' ]) : '' );
$arr [ 'plink' ] = (( x ( $arr , 'plink' )) ? notags ( trim ( $arr [ 'plink' ])) : '' );
$arr [ 'attach' ] = (( x ( $arr , 'attach' )) ? notags ( trim ( $arr [ 'attach' ])) : '' );
$arr [ 'app' ] = (( x ( $arr , 'app' )) ? notags ( trim ( $arr [ 'app' ])) : '' );
$arr [ 'replyto' ] = (( x ( $arr , 'replyto' )) ? serialise ( $arr [ 'replyto' ]) : '' );
// No longer used but needs to be set to something or the database will complain.
$arr [ 'route' ] = '' ;
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'public_policy' ] = '' ;
$arr [ 'comment_policy' ] = (( x ( $arr , 'comment_policy' )) ? notags ( trim ( $arr [ 'comment_policy' ])) : 'contacts' );
$arr [ 'item_unseen' ] = (( array_key_exists ( 'item_unseen' , $arr )) ? intval ( $arr [ 'item_unseen' ]) : 1 );
$arr [ 'item_restrict' ] = (( array_key_exists ( 'item_restrict' , $arr )) ? intval ( $arr [ 'item_restrict' ]) : 0 );
if (( ! array_key_exists ( 'item_nocomment' , $arr )) && ( $arr [ 'comment_policy' ] == 'none' ))
$arr [ 'item_nocomment' ] = 1 ;
// handle time travelers
// Allow a bit of fudge in case somebody just has a slightly slow/fast clock
$d1 = new DateTime ( 'now +10 minutes' , new DateTimeZone ( 'UTC' ));
$d2 = new DateTime ( $arr [ 'created' ] . '+00:00' );
if ( $d2 > $d1 )
$arr [ 'item_delayed' ] = 1 ;
2022-07-20 11:26:28 +00:00
$arr [ 'llink' ] = z_root () . '/display/?mid=' . gen_link_id ( $arr [ 'mid' ]);
2022-07-20 00:43:27 +00:00
2022-07-20 08:30:58 +00:00
if ( ! $arr [ 'plink' ]) {
2022-07-20 00:43:27 +00:00
$arr [ 'plink' ] = $arr [ 'llink' ];
2022-07-20 08:30:58 +00:00
}
2022-07-20 00:43:27 +00:00
if ( $arr [ 'parent_mid' ] === $arr [ 'mid' ]) {
$parent_id = 0 ;
$parent_deleted = 0 ;
$allow_cid = $arr [ 'allow_cid' ];
$allow_gid = $arr [ 'allow_gid' ];
$deny_cid = $arr [ 'deny_cid' ];
$deny_gid = $arr [ 'deny_gid' ];
$comments_closed = $arr [ 'comments_closed' ];
$arr [ 'item_thread_top' ] = 1 ;
$arr [ 'item_level' ] = 0 ;
}
else {
// find the parent and snarf the item id and ACL's
// and anything else we need to inherit
2023-09-29 22:31:07 +00:00
$r = q ( " SELECT item_level FROM item WHERE (mid = '%s' OR mid = '%s') AND uid = %d ORDER BY id ASC LIMIT 1 " ,
2022-07-20 00:43:27 +00:00
dbesc ( $arr [ 'thr_parent' ]),
2023-09-29 22:31:07 +00:00
dbesc ( reverse_activity_mid ( $arr [ 'thr_parent' ])),
2022-07-20 00:43:27 +00:00
intval ( $arr [ 'uid' ])
);
if ( $r ) {
$arr [ 'item_level' ] = intval ( $r [ 0 ][ 'item_level' ]) + 1 ;
}
else {
$arr [ 'item_level' ] = 1 ;
}
2023-09-29 22:31:07 +00:00
$r = q ( " SELECT * FROM item WHERE (mid = '%s' OR mid = '%s') AND uid = %d ORDER BY id ASC LIMIT 1 " ,
2022-07-20 00:43:27 +00:00
dbesc ( $arr [ 'parent_mid' ]),
2023-09-29 22:31:07 +00:00
dbesc ( reverse_activity_mid ( $arr [ 'parent_mid' ])),
2022-07-20 00:43:27 +00:00
intval ( $arr [ 'uid' ])
);
// We may have this parent_mid without a token, so try that if we find a token
if ( ! $r ) {
if ( strpos ( $arr [ 'parent_mid' ], 'token=' )) {
$r = q ( " SELECT * FROM item WHERE mid = '%s' AND uid = %d ORDER BY id ASC LIMIT 1 " ,
dbesc ( substr ( $arr [ 'parent_mid' ], 0 , strpos ( $arr [ 'parent_mid' ], '?' ))),
intval ( $arr [ 'uid' ])
);
if ( $r ) {
$arr [ 'parent_mid' ] = $arr [ 'thr_parent' ] = substr ( $arr [ 'parent_mid' ], 0 , strpos ( $arr [ 'parent_mid' ], '?' ));
}
}
}
if ( $r ) {
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$parent_item = array_shift ( $r );
// in case item_store was killed before the parent's parent attribute got set,
// set it now. This happens with some regularity on Dreamhost. This will keep
// us from getting notifications for threads that exist but which we can't see.
if (( $parent_item [ 'mid' ] === $parent_item [ 'parent_mid' ]) && ( ! intval ( $parent_item [ 'parent' ]))) {
q ( " update item set parent = id where id = %d " ,
intval ( $parent_item [ 'id' ])
);
}
if ( comments_are_now_closed ( $parent_item )) {
logger ( 'item_store: comments closed' );
$ret [ 'message' ] = 'Comments closed.' ;
return $ret ;
}
if (( $arr [ 'obj_type' ] == ACTIVITY_OBJ_NOTE ) && ( ! $arr [ 'obj' ]))
$arr [ 'obj_type' ] = ACTIVITY_OBJ_COMMENT ;
// is the new message multi-level threaded?
// even though we don't support it now, preserve the info
// and re-attach to the conversation parent.
if ( $parent_item [ 'mid' ] != $parent_item [ 'parent_mid' ]) {
$arr [ 'parent_mid' ] = $parent_item [ 'parent_mid' ];
$z = q ( " SELECT * FROM item WHERE mid = '%s' AND parent_mid = '%s' AND uid = %d
ORDER BY id ASC LIMIT 1 " ,
dbesc ( $parent_item [ 'parent_mid' ]),
dbesc ( $parent_item [ 'parent_mid' ]),
intval ( $arr [ 'uid' ])
);
if ( $z && count ( $z ))
$r = $z ;
}
$parent_id = $parent_item [ 'id' ];
$parent_deleted = $parent_item [ 'item_deleted' ];
2022-07-27 22:16:27 +00:00
// item_restrict indicates an activitypub reply with a different
2022-07-20 00:43:27 +00:00
// delivery algorithm than the "parent-relay" or inherited privacy mode
if ( ! ( intval ( $arr [ 'item_restrict' ]) & 1 )) {
$allow_cid = $parent_item [ 'allow_cid' ];
$allow_gid = $parent_item [ 'allow_gid' ];
$deny_cid = $parent_item [ 'deny_cid' ];
$deny_gid = $parent_item [ 'deny_gid' ];
}
$comments_closed = $parent_item [ 'comments_closed' ];
if ( intval ( $parent_item [ 'item_wall' ]))
$arr [ 'item_wall' ] = 1 ;
// An uplinked comment might arrive with a downstream owner.
// Fix it.
if ( $parent_item [ 'owner_xchan' ] !== $arr [ 'owner_xchan' ]) {
$arr [ 'owner_xchan' ] = $parent_item [ 'owner_xchan' ];
// $uplinked_comment = true;
}
// if the parent is private and the child is not, force privacy for the entire conversation
// if the child is private, leave it alone regardless of the parent privacy state
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( intval ( $parent_item [ 'item_private' ]) && ( ! intval ( $arr [ 'item_private' ]))) {
$arr [ 'item_private' ] = $parent_item [ 'item_private' ];
}
2023-02-11 21:22:06 +00:00
// Edge case. We host a public forum that was originally posted privately.
2022-07-20 00:43:27 +00:00
// The original author commented, but as this is a comment, the permissions
2023-02-11 21:22:06 +00:00
// weren't fixed up and will still show the comment as private unless we fix it here.
2022-07-20 00:43:27 +00:00
if ( intval ( $parent_item [ 'item_uplink' ]) && ( ! $parent_item [ 'item_private' ]))
$arr [ 'item_private' ] = 0 ;
if ( in_array ( $arr [ 'obj_type' ], [ 'Note' , 'Answer' ]) && $parent_item [ 'obj_type' ] === 'Question' && intval ( $parent_item [ 'item_wall' ])) {
2023-08-11 19:43:19 +00:00
Activity :: update_poll ( $parent_item , $arr );
2022-07-20 00:43:27 +00:00
}
}
else {
logger ( 'item_store: item parent was not found - ignoring item' );
$ret [ 'message' ] = 'parent not found.' ;
return $ret ;
}
}
if ( $parent_deleted )
$arr [ 'item_deleted' ] = 1 ;
$r = q ( " SELECT id FROM item WHERE mid = '%s' AND uid = %d and revision = %d LIMIT 1 " ,
dbesc ( $arr [ 'mid' ]),
intval ( $arr [ 'uid' ]),
intval ( $arr [ 'revision' ])
);
if ( $r ) {
logger ( 'duplicate item ignored. ' . print_r ( $arr , true ));
$ret [ 'message' ] = 'duplicate post.' ;
return $ret ;
}
if ( LibBlock :: fetch_by_entity ( $arr [ 'uid' ], $arr [ 'owner_xchan' ]) || LibBlock :: fetch_by_entity ( $arr [ 'uid' ], $arr [ 'author_xchan' ])) {
logger ( 'Post cancelled by block rule.' );
$ret [ 'message' ] = 'cancelled.' ;
return $ret ;
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
/**
* @ hooks item_store
* Called when item_store () stores a record of type item .
*/
Hook :: call ( 'item_store' , $arr );
/**
* @ hooks post_remote
* Called when an activity arrives from another site .
* This hook remains for backward compatibility .
*/
Hook :: call ( 'post_remote' , $arr );
if ( x ( $arr , 'cancel' )) {
logger ( 'Post cancelled by plugin.' );
$ret [ 'message' ] = 'cancelled.' ;
return $ret ;
}
// pull out all the taxonomy stuff for separate storage
$terms = null ;
if ( array_key_exists ( 'term' , $arr )) {
$terms = $arr [ 'term' ];
unset ( $arr [ 'term' ]);
}
$meta = null ;
if ( array_key_exists ( 'iconfig' , $arr )) {
$meta = $arr [ 'iconfig' ];
unset ( $arr [ 'iconfig' ]);
}
$private = intval ( $arr [ 'item_private' ]);
if ( ! $private ) {
if ( strlen ( $allow_cid ) || strlen ( $allow_gid ) || strlen ( $deny_cid ) || strlen ( $deny_gid )) {
$private = 1 ;
}
}
$arr [ 'parent' ] = $parent_id ;
$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 [ 'comments_closed' ] = $comments_closed ;
logger ( 'item_store: ' . print_r ( $arr , true ), LOGGER_DATA );
2023-07-09 08:28:02 +00:00
logger ( 'item_store_terms: ' . print_r ( $terms , true ), LOGGER_DATA );
2022-07-20 00:43:27 +00:00
create_table_from_array ( 'item' , $arr );
// find the item we just created
$r = q ( " SELECT * FROM item WHERE mid = '%s' AND uid = %d and revision = %d ORDER BY id ASC " ,
dbesc ( $arr [ 'mid' ]),
intval ( $arr [ 'uid' ]),
intval ( $arr [ 'revision' ])
);
if ( $r ) {
2023-02-11 21:22:06 +00:00
// This will give us a fresh copy of what's now in the DB and undo the db escaping,
2022-07-20 00:43:27 +00:00
// which really messes up the notifications
$current_post = $r [ 0 ][ 'id' ];
$arr = $r [ 0 ];
logger ( 'item_store: created item ' . $current_post , LOGGER_DEBUG );
}
else {
logger ( 'item_store: could not locate stored item' );
$ret [ 'message' ] = 'unable to retrieve.' ;
return $ret ;
}
if ( count ( $r ) > 1 ) {
logger ( 'item_store: duplicated post occurred. Removing duplicates.' );
q ( " DELETE FROM item WHERE mid = '%s' AND uid = %d AND id != %d " ,
$arr [ 'mid' ],
intval ( $arr [ 'uid' ]),
intval ( $current_post )
);
}
$arr [ 'id' ] = $current_post ;
if ( ! intval ( $r [ 0 ][ 'parent' ])) {
2022-08-14 09:20:43 +00:00
q ( " update item set parent = id where id = %d " ,
2022-07-20 00:43:27 +00:00
intval ( $r [ 0 ][ 'id' ])
);
$arr [ 'parent' ] = $r [ 0 ][ 'id' ];
}
// Store taxonomy
if (( $terms ) && ( is_array ( $terms ))) {
foreach ( $terms as $t ) {
q ( " insert into term (uid,oid,otype,ttype,term,url)
values ( % d , % d , % d , % d , '%s' , '%s' ) " ,
intval ( $arr [ 'uid' ]),
intval ( $current_post ),
intval ( TERM_OBJ_POST ),
intval ( $t [ 'ttype' ]),
dbesc ( $t [ 'term' ]),
dbesc ( $t [ 'url' ])
);
}
$arr [ 'term' ] = $terms ;
}
if ( $meta ) {
foreach ( $meta as $m ) {
set_iconfig ( $current_post , $m [ 'cat' ], $m [ 'k' ], $m [ 'v' ], $m [ 'sharing' ]);
}
$arr [ 'iconfig' ] = $meta ;
}
$ret [ 'item' ] = $arr ;
/**
* @ hooks post_remote_end
* Called after processing a remote post .
*/
Hook :: call ( 'post_remote_end' , $arr );
item_update_parent_commented ( $arr );
2022-09-17 00:25:44 +00:00
if (( str_contains ( $arr [ 'body' ], '[embed]' )) || ( str_contains ( $arr [ 'body' ], '[/img]' )) || ( str_contains ( $arr [ 'body' ], '[/zmg]' ))) {
2022-07-20 00:43:27 +00:00
Run :: Summon ([ 'Cache_embeds' , $current_post ]);
}
2024-02-10 19:22:39 +00:00
$ret [ 'success' ] = true ;
$ret [ 'item_id' ] = $current_post ;
if ( $addAndSync ) {
$ret = addToCollectionAndSync ( $ret );
}
2022-07-20 00:43:27 +00:00
// If _creating_ a deleted item, don't propagate it further or send out notifications.
// We need to store the item details just in case the delete came in before the original post,
// so that we have an item in the DB that's marked deleted and won't store a fresh post
// that isn't aware that we were already told to delete it.
if (( $deliver ) && ( ! intval ( $arr [ 'item_deleted' ]))) {
2022-08-14 09:20:43 +00:00
send_status_notifications ( $arr );
2022-07-20 00:43:27 +00:00
tag_deliver ( $arr [ 'uid' ], $current_post );
}
2024-02-10 19:22:39 +00:00
// experimental future use
2022-07-20 00:43:27 +00:00
// if($linkid) {
// $li = [ $ret['item'] ];
// xchan_query($li);
// $sync_item = fetch_post_tags($li);
// Libsync::build_link_packet($arr['uid'],[ 'item' => [ encode_item($sync_item[0],true) ] ]);
// }
return $ret ;
}
2024-02-10 19:22:39 +00:00
function addToCollectionAndSync ( $ret )
{
if ( ! $ret [ 'success' ]) {
return $ret ;
}
$channel = Channel :: from_id ( $ret [ 'item' ][ 'uid' ]);
if ( $channel && $channel [ 'channel_hash' ] === $ret [ 'item' ][ 'owner_xchan' ]) {
$items = [ $ret [ 'item' ]];
xchan_query ( $items );
$items = fetch_post_tags ( $items );
$sync_items = [];
$sync_items [] = encode_item ( $items [ 0 ], true );
if ( ! in_array ( $ret [ 'item' ][ 'verb' ], [ 'Add' , 'Remove' ])) {
$items [ 0 ] = array_merge ( Activity :: ap_context (), Activity :: encode_activity ( $items [ 0 ]));
$items [ 0 ][ 'proof' ] = ( new JcsEddsa2022 ()) -> sign ( $items [ 0 ], $channel );
$approval = Activity :: addToCollection ( $channel ,
$items [ 0 ],
$ret [ 'item' ][ 'parent_mid' ], $ret [ 'item' ], deliver : false );
if ( $approval [ 'success' ]) {
$ret [ 'approval_id' ] = $approval [ 'item_id' ];
$ret [ 'approval' ] = $approval [ 'activity' ];
$addItems = [ $approval [ 'activity' ]];
xchan_query ( $addItems );
$addItems = fetch_post_tags ( $addItems );
$sync_items [] = encode_item ( $addItems [ 0 ], true );
}
}
$resource_type = $ret [ 'item' ][ 'resource_type' ];
if ( $resource_type === 'event' ) {
$z = q (
" select * from event where event_hash = '%s' and uid = %d limit 1 " ,
dbesc ( $ret [ 'item' ][ 'resource_id' ]),
intval ( $channel [ 'channel_id' ])
);
if ( $z ) {
Libsync :: build_sync_packet ( $channel [ 'channel_id' ], [ 'event_item' => $sync_items , 'event' => $z ]);
}
}
elseif ( $resource_type === 'photo' ) {
// reserved for future use, currently handled in the photo upload workflow
}
else {
Libsync :: build_sync_packet ( $ret [ 'item' ][ 'uid' ], [ 'item' => $sync_items ]);
}
}
return $ret ;
}
2022-07-20 00:43:27 +00:00
/**
* @ brief Update a stored item .
*
* @ param array $arr an item
* @ param boolean $deliver ( optional ) default true
* @ return array
*/
2024-02-10 19:22:39 +00:00
function item_store_update ( $arr , $deliver = true , $addAndSync = true ) {
2022-07-20 00:43:27 +00:00
$d = [
'item' => $arr ,
];
/**
* @ hooks item_store_update_before
* Called when item_store_update () is called to update a stored item . It
2022-12-21 20:20:55 +00:00
* overwrites the function ' s parameters $arr
2022-07-20 00:43:27 +00:00
* * \e array \b item
*/
Hook :: call ( 'item_store_update_before' , $d );
$arr = $d [ 'item' ];
2022-09-17 00:25:44 +00:00
$ret = [ 'success' => false , 'item_id' => 0 ];
2022-07-20 00:43:27 +00:00
if ( array_key_exists ( 'cancel' , $arr ) && $arr [ 'cancel' ]) {
logger ( 'cancelled by plugin' );
return $ret ;
}
if ( ! intval ( $arr [ 'uid' ])) {
logger ( 'no uid' );
$ret [ 'message' ] = 'no uid.' ;
return $ret ;
}
if ( ! intval ( $arr [ 'id' ])) {
logger ( 'no id' );
$ret [ 'message' ] = 'no id.' ;
return $ret ;
}
$orig_post_id = $arr [ 'id' ];
$uid = $arr [ 'uid' ];
$orig = q ( " select * from item where id = %d and uid = %d limit 1 " ,
intval ( $orig_post_id ),
intval ( $uid )
);
if ( ! $orig ) {
logger ( 'Original post not found: ' . $orig_post_id );
$ret [ 'message' ] = 'no original' ;
return $ret ;
}
// override the unseen flag with the original
$arr [ 'item_unseen' ] = $orig [ 0 ][ 'item_unseen' ];
$arr [ 'title' ] = (( array_key_exists ( 'title' , $arr ) && strlen ( $arr [ 'title' ])) ? trim ( escape_tags ( $arr [ 'title' ])) : '' );
$arr [ 'body' ] = (( array_key_exists ( 'body' , $arr ) && strlen ( $arr [ 'body' ])) ? trim ( $arr [ 'body' ]) : '' );
$arr [ 'html' ] = (( array_key_exists ( 'html' , $arr ) && strlen ( $arr [ 'html' ])) ? trim ( $arr [ 'html' ]) : '' );
if ( array_key_exists ( 'edit' , $arr ))
unset ( $arr [ 'edit' ]);
$arr [ 'mimetype' ] = (( x ( $arr , 'mimetype' )) ? notags ( trim ( $arr [ 'mimetype' ])) : 'text/x-multicode' );
$languagetext = prepare_text ( $arr [ 'body' ],(( isset ( $arr [ 'mimetype' ])) ? $arr [ 'mimetype' ] : 'text/x-multicode' ));
$languagetext = html2plain (( isset ( $arr [ 'title' ]) && $arr [ 'title' ]) ? $arr [ 'title' ] . ' ' . $languagetext : $languagetext );
$detector = new LanguageDetect ();
$arr [ 'lang' ] = $detector -> detect ( $languagetext );
// apply the input filter here
2022-12-21 20:20:55 +00:00
$arr [ 'summary' ] = trim ( z_input_filter ( $arr [ 'summary' ], $arr [ 'mimetype' ]));
$arr [ 'body' ] = trim ( z_input_filter ( $arr [ 'body' ], $arr [ 'mimetype' ]));
2022-07-20 00:43:27 +00:00
item_sign ( $arr );
$allowed_languages = get_pconfig ( $arr [ 'uid' ], 'system' , 'allowed_languages' );
if (( is_array ( $allowed_languages )) && ( $arr [ 'lang' ]) && ( ! array_key_exists ( $arr [ 'lang' ], $allowed_languages ))) {
$translate = [
'item' => $arr ,
'from' => $arr [ 'lang' ],
'to' => $allowed_languages ,
'translated' => false
];
/**
* @ hooks item_translate
* Called from item_store () and item_store_update () after the post language has been autodetected .
* * \e array \b item - returned value
* * \e string \b from
* * \e string \b to
* * \e boolean \b translated - default false , set true if hook translated it and provide it in item
*/
Hook :: call ( 'item_translate' , $translate );
if (( ! $translate [ 'translated' ]) && ( intval ( get_pconfig ( $arr [ 'uid' ], 'system' , 'reject_disallowed_languages' )))) {
logger ( 'item_store: language ' . $arr [ 'lang' ] . ' not accepted for uid ' . $arr [ 'uid' ]);
$ret [ 'message' ] = 'language not accepted' ;
return $ret ;
}
$arr = $translate [ 'item' ];
}
2022-12-31 19:04:18 +00:00
if ( isset ( $arr [ 'obj' ])) {
2022-07-20 00:43:27 +00:00
$arr [ 'obj' ] = item_json_encapsulate ( $arr , 'obj' );
}
2022-12-31 19:04:18 +00:00
if ( isset ( $arr [ 'target' ])) {
2022-07-20 00:43:27 +00:00
$arr [ 'target' ] = item_json_encapsulate ( $arr , 'target' );
}
2022-12-31 19:04:18 +00:00
if ( isset ( $arr [ 'attach' ])) {
2022-07-20 00:43:27 +00:00
$arr [ 'attach' ] = item_json_encapsulate ( $arr , 'attach' );
}
unset ( $arr [ 'id' ]);
unset ( $arr [ 'uid' ]);
unset ( $arr [ 'aid' ]);
unset ( $arr [ 'uuid' ]);
unset ( $arr [ 'mid' ]);
unset ( $arr [ 'parent' ]);
unset ( $arr [ 'parent_mid' ]);
unset ( $arr [ 'author_xchan' ]);
unset ( $arr [ 'owner_xchan' ]);
unset ( $arr [ 'source_xchan' ]);
unset ( $arr [ 'thr_parent' ]);
unset ( $arr [ 'llink' ]);
if ( intval ( $orig [ 0 ][ 'item_unpublished' ])) {
$arr [ 'created' ] = (( x ( $arr , 'created' ) !== false ) ? datetime_convert ( 'UTC' , 'UTC' , $arr [ 'created' ]) : datetime_convert ());
$arr [ 'edited' ] = $arr [ 'created' ];
$arr [ 'expires' ] = (( x ( $arr , 'expires' ) !== false ) ? datetime_convert ( 'UTC' , 'UTC' , $arr [ 'expires' ]) : NULL_DATE );
if ( array_key_exists ( 'comments_closed' , $arr ) && $arr [ 'comments_closed' ] > NULL_DATE )
$arr [ 'comments_closed' ] = datetime_convert ( 'UTC' , 'UTC' , $arr [ 'comments_closed' ]);
else
$arr [ 'comments_closed' ] = NULL_DATE ;
$arr [ 'commented' ] = $arr [ 'created' ];
$arr [ 'received' ] = $arr [ 'created' ];
$arr [ 'changed' ] = $arr [ 'created' ];
}
else {
unset ( $arr [ 'created' ]);
$arr [ 'expires' ] = (( x ( $arr , 'expires' ) !== false ) ? datetime_convert ( 'UTC' , 'UTC' , $arr [ 'expires' ]) : $orig [ 0 ][ 'expires' ]);
if ( array_key_exists ( 'comments_closed' , $arr ) && $arr [ 'comments_closed' ] > NULL_DATE )
$arr [ 'comments_closed' ] = datetime_convert ( 'UTC' , 'UTC' , $arr [ 'comments_closed' ]);
else
$arr [ 'comments_closed' ] = $orig [ 0 ][ 'comments_closed' ];
$arr [ 'commented' ] = $orig [ 0 ][ 'commented' ];
$arr [ 'received' ] = $orig [ 0 ][ 'received' ];
$arr [ 'changed' ] = $orig [ 0 ][ 'changed' ];
}
$arr [ 'edited' ] = (( x ( $arr , 'edited' ) !== false ) ? datetime_convert ( 'UTC' , 'UTC' , $arr [ 'edited' ]) : datetime_convert ());
$arr [ 'revision' ] = (( x ( $arr , 'revision' ) && $arr [ 'revision' ] > 0 ) ? intval ( $arr [ 'revision' ]) : 0 );
$arr [ 'location' ] = (( x ( $arr , 'location' )) ? notags ( trim ( $arr [ 'location' ])) : $orig [ 0 ][ 'location' ]);
$arr [ 'uuid' ] = (( x ( $arr , 'uuid' )) ? notags ( trim ( $arr [ 'uuid' ])) : $orig [ 0 ][ 'uuid' ]);
2022-12-28 01:51:30 +00:00
$arr [ 'approved' ] = (( x ( $arr , 'approved' )) ? notags ( trim ( $arr [ 'approved' ])) : '' );
2022-09-16 10:57:04 +00:00
$arr [ 'lat' ] = (( x ( $arr , 'lat' )) ? floatval ( $arr [ 'lat' ]) : $orig [ 0 ][ 'lat' ]);
$arr [ 'lon' ] = (( x ( $arr , 'lon' )) ? floatval ( $arr [ 'lon' ]) : $orig [ 0 ][ 'lon' ]);
2022-07-20 00:43:27 +00:00
$arr [ 'verb' ] = (( x ( $arr , 'verb' )) ? notags ( trim ( $arr [ 'verb' ])) : $orig [ 0 ][ 'verb' ]);
$arr [ 'obj_type' ] = (( x ( $arr , 'obj_type' )) ? notags ( trim ( $arr [ 'obj_type' ])) : $orig [ 0 ][ 'obj_type' ]);
$arr [ 'obj' ] = (( x ( $arr , 'obj' )) ? trim ( $arr [ 'obj' ]) : $orig [ 0 ][ 'obj' ]);
$arr [ 'tgt_type' ] = (( x ( $arr , 'tgt_type' )) ? notags ( trim ( $arr [ 'tgt_type' ])) : $orig [ 0 ][ 'tgt_type' ]);
$arr [ 'target' ] = (( x ( $arr , 'target' )) ? trim ( $arr [ 'target' ]) : $orig [ 0 ][ 'target' ]);
$arr [ 'plink' ] = (( x ( $arr , 'plink' )) ? notags ( trim ( $arr [ 'plink' ])) : $orig [ 0 ][ 'plink' ]);
$arr [ 'replyto' ] = (( x ( $arr , 'replyto' )) ? serialise ( $arr [ 'replyto' ]) : $orig [ 0 ][ 'replyto' ]);
$arr [ 'allow_cid' ] = (( array_key_exists ( 'allow_cid' , $arr )) ? trim ( $arr [ 'allow_cid' ]) : $orig [ 0 ][ 'allow_cid' ]);
$arr [ 'allow_gid' ] = (( array_key_exists ( 'allow_gid' , $arr )) ? trim ( $arr [ 'allow_gid' ]) : $orig [ 0 ][ 'allow_gid' ]);
$arr [ 'deny_cid' ] = (( array_key_exists ( 'deny_cid' , $arr )) ? trim ( $arr [ 'deny_cid' ]) : $orig [ 0 ][ 'deny_cid' ]);
$arr [ 'deny_gid' ] = (( array_key_exists ( 'deny_gid' , $arr )) ? trim ( $arr [ 'deny_gid' ]) : $orig [ 0 ][ 'deny_gid' ]);
$arr [ 'item_private' ] = (( array_key_exists ( 'item_private' , $arr )) ? intval ( $arr [ 'item_private' ]) : $orig [ 0 ][ 'item_private' ]);
$arr [ 'attach' ] = (( array_key_exists ( 'attach' , $arr )) ? notags ( trim ( $arr [ 'attach' ])) : $orig [ 0 ][ 'attach' ]);
$arr [ 'app' ] = (( array_key_exists ( 'app' , $arr )) ? notags ( trim ( $arr [ 'app' ])) : $orig [ 0 ][ 'app' ]);
$arr [ 'item_origin' ] = (( array_key_exists ( 'item_origin' , $arr )) ? intval ( $arr [ 'item_origin' ]) : $orig [ 0 ][ 'item_origin' ] );
$arr [ 'item_unseen' ] = (( array_key_exists ( 'item_unseen' , $arr )) ? intval ( $arr [ 'item_unseen' ]) : $orig [ 0 ][ 'item_unseen' ] );
$arr [ 'item_starred' ] = (( array_key_exists ( 'item_starred' , $arr )) ? intval ( $arr [ 'item_starred' ]) : $orig [ 0 ][ 'item_starred' ] );
$arr [ 'item_uplink' ] = (( array_key_exists ( 'item_uplink' , $arr )) ? intval ( $arr [ 'item_uplink' ]) : $orig [ 0 ][ 'item_uplink' ] );
$arr [ 'item_consensus' ] = (( array_key_exists ( 'item_consensus' , $arr )) ? intval ( $arr [ 'item_consensus' ]) : $orig [ 0 ][ 'item_consensus' ] );
$arr [ 'item_wall' ] = (( array_key_exists ( 'item_wall' , $arr )) ? intval ( $arr [ 'item_wall' ]) : $orig [ 0 ][ 'item_wall' ] );
$arr [ 'item_thread_top' ] = (( array_key_exists ( 'item_thread_top' , $arr )) ? intval ( $arr [ 'item_thread_top' ]) : $orig [ 0 ][ 'item_thread_top' ] );
$arr [ 'item_notshown' ] = (( array_key_exists ( 'item_notshown' , $arr )) ? intval ( $arr [ 'item_notshown' ]) : $orig [ 0 ][ 'item_notshown' ] );
$arr [ 'item_nsfw' ] = (( array_key_exists ( 'item_nsfw' , $arr )) ? intval ( $arr [ 'item_nsfw' ]) : $orig [ 0 ][ 'item_nsfw' ] );
$arr [ 'item_relay' ] = (( array_key_exists ( 'item_relay' , $arr )) ? intval ( $arr [ 'item_relay' ]) : $orig [ 0 ][ 'item_relay' ] );
$arr [ 'item_mentionsme' ] = (( array_key_exists ( 'item_mentionsme' , $arr )) ? intval ( $arr [ 'item_mentionsme' ]) : $orig [ 0 ][ 'item_mentionsme' ] );
$arr [ 'item_nocomment' ] = (( array_key_exists ( 'item_nocomment' , $arr )) ? intval ( $arr [ 'item_nocomment' ]) : $orig [ 0 ][ 'item_nocomment' ] );
$arr [ 'item_obscured' ] = (( array_key_exists ( 'item_obscured' , $arr )) ? intval ( $arr [ 'item_obscured' ]) : $orig [ 0 ][ 'item_obscured' ] );
$arr [ 'item_verified' ] = (( array_key_exists ( 'item_verified' , $arr )) ? intval ( $arr [ 'item_verified' ]) : $orig [ 0 ][ 'item_verified' ] );
$arr [ 'item_retained' ] = (( array_key_exists ( 'item_retained' , $arr )) ? intval ( $arr [ 'item_retained' ]) : $orig [ 0 ][ 'item_retained' ] );
$arr [ 'item_rss' ] = (( array_key_exists ( 'item_rss' , $arr )) ? intval ( $arr [ 'item_rss' ]) : $orig [ 0 ][ 'item_rss' ] );
$arr [ 'item_deleted' ] = (( array_key_exists ( 'item_deleted' , $arr )) ? intval ( $arr [ 'item_deleted' ]) : $orig [ 0 ][ 'item_deleted' ] );
$arr [ 'item_type' ] = (( array_key_exists ( 'item_type' , $arr )) ? intval ( $arr [ 'item_type' ]) : $orig [ 0 ][ 'item_type' ] );
$arr [ 'item_hidden' ] = (( array_key_exists ( 'item_hidden' , $arr )) ? intval ( $arr [ 'item_hidden' ]) : $orig [ 0 ][ 'item_hidden' ] );
$arr [ 'item_unpublished' ] = (( array_key_exists ( 'item_unpublished' , $arr )) ? intval ( $arr [ 'item_unpublished' ]) : $orig [ 0 ][ 'item_unpublished' ] );
$arr [ 'item_delayed' ] = (( array_key_exists ( 'item_delayed' , $arr )) ? intval ( $arr [ 'item_delayed' ]) : $orig [ 0 ][ 'item_delayed' ] );
$arr [ 'item_pending_remove' ] = (( array_key_exists ( 'item_pending_remove' , $arr )) ? intval ( $arr [ 'item_pending_remove' ]) : $orig [ 0 ][ 'item_pending_remove' ] );
$arr [ 'item_blocked' ] = (( array_key_exists ( 'item_blocked' , $arr )) ? intval ( $arr [ 'item_blocked' ]) : $orig [ 0 ][ 'item_blocked' ] );
$arr [ 'sig' ] = (( x ( $arr , 'sig' )) ? $arr [ 'sig' ] : '' );
$arr [ 'layout_mid' ] = (( array_key_exists ( 'layout_mid' , $arr )) ? dbesc ( $arr [ 'layout_mid' ]) : $orig [ 0 ][ 'layout_mid' ] );
$arr [ 'public_policy' ] = '' ;
$arr [ 'comment_policy' ] = (( x ( $arr , 'comment_policy' )) ? notags ( trim ( $arr [ 'comment_policy' ])) : $orig [ 0 ][ 'comment_policy' ] );
/**
* @ hooks post_remote_update
* Called when processing a remote post that involved an edit or update .
*/
Hook :: call ( 'post_remote_update' , $arr );
if ( x ( $arr , 'cancel' )) {
logger ( 'Post cancelled by plugin.' );
$ret [ 'message' ] = 'cancelled.' ;
return $ret ;
}
// pull out all the taxonomy stuff for separate storage
$terms = null ;
if ( array_key_exists ( 'term' , $arr )) {
$terms = $arr [ 'term' ];
unset ( $arr [ 'term' ]);
}
$meta = null ;
if ( array_key_exists ( 'iconfig' , $arr )) {
$meta = $arr [ 'iconfig' ];
unset ( $arr [ 'iconfig' ]);
}
logger ( 'item_store_update: ' . print_r ( $arr , true ), LOGGER_DATA );
2023-07-09 08:28:02 +00:00
logger ( 'item_store_update_terms: ' . print_r ( $terms , true ), LOGGER_DATA );
2022-07-20 00:43:27 +00:00
$str = '' ;
foreach ( $arr as $k => $v ) {
if ( $str )
$str .= " , " ;
$str .= " " . TQUOT . dbesc ( $k ) . TQUOT . " = ' " . dbesc ( $v ) . " ' " ;
}
$r = dbq ( " update item set " . $str . " where id = " . $orig_post_id );
if ( $r )
logger ( 'Updated item ' . $orig_post_id , LOGGER_DEBUG );
else {
logger ( 'Could not update item' );
$ret [ 'message' ] = 'DB update failed.' ;
return $ret ;
}
// fetch an unescaped complete copy of the stored item
$r = q ( " select * from item where id = %d " ,
intval ( $orig_post_id )
);
if ( $r )
$arr = $r [ 0 ];
$r = q ( " delete from term where oid = %d and otype = %d " ,
intval ( $orig_post_id ),
intval ( TERM_OBJ_POST )
);
if ( is_array ( $terms )) {
foreach ( $terms as $t ) {
q ( " insert into term (uid,oid,otype,ttype,term,url)
values ( % d , % d , % d , % d , '%s' , '%s' ) " ,
intval ( $uid ),
intval ( $orig_post_id ),
intval ( TERM_OBJ_POST ),
intval ( $t [ 'ttype' ]),
dbesc ( $t [ 'term' ]),
dbesc ( $t [ 'url' ])
);
}
$arr [ 'term' ] = $terms ;
}
$r = q ( " delete from iconfig where iid = %d " ,
intval ( $orig_post_id )
);
if ( $meta ) {
foreach ( $meta as $m ) {
set_iconfig ( $orig_post_id , $m [ 'cat' ], $m [ 'k' ], $m [ 'v' ], $m [ 'sharing' ]);
}
$arr [ 'iconfig' ] = $meta ;
}
$ret [ 'item' ] = $arr ;
/**
* @ hooks post_remote_update_end
* Called after processing a remote post that involved an edit or update .
*/
Hook :: call ( 'post_remote_update_end' , $arr );
2022-09-17 00:25:44 +00:00
if (( str_contains ( $arr [ 'body' ], '[embed]' )) || ( str_contains ( $arr [ 'body' ], '[/img]' ))) {
2022-07-20 00:43:27 +00:00
Run :: Summon ([ 'Cache_embeds' , $orig_post_id ]);
}
2024-02-10 19:22:39 +00:00
$ret [ 'success' ] = true ;
$ret [ 'item_id' ] = $orig_post_id ;
if ( $addAndSync ) {
$ret = addToCollectionAndSync ( $ret );
}
2022-07-20 00:43:27 +00:00
if ( $deliver ) {
// don't send notify_comment for edits
// send_status_notifications($orig_post_id,$arr);
tag_deliver ( $uid , $orig_post_id );
}
return $ret ;
}
function item_update_parent_commented ( $item ) {
$update_parent = true ;
2022-07-27 22:16:27 +00:00
// update the commented timestamp on the parent
2022-07-20 00:43:27 +00:00
// - unless this is a moderated comment or a potential clone of an older item
2022-07-27 22:16:27 +00:00
// which we don't wish to bring to the surface. As the queue only holds deliveries
// for 3 days, it's suspected of being an older cloned item if the creation time
2022-07-20 00:43:27 +00:00
// is older than that.
if ( intval ( $item [ 'item_blocked' ]) === ITEM_MODERATED )
$update_parent = false ;
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( $item [ 'created' ] < datetime_convert ( '' , '' , 'now - 4 days' ))
$update_parent = false ;
if ( $update_parent ) {
$z = q ( " select max(created) as commented from item where parent_mid = '%s' and uid = %d and item_delayed = 0 and item_deleted = 0 " ,
dbesc ( $item [ 'parent_mid' ]),
intval ( $item [ 'uid' ])
);
q ( " UPDATE item set commented = '%s', changed = '%s' WHERE id = %d " ,
dbesc (( $z ) ? $z [ 0 ][ 'commented' ] : datetime_convert ()),
dbesc ( datetime_convert ()),
intval ( $item [ 'parent' ])
);
}
}
2015-06-20 05:21:25 +00:00
2022-09-22 01:33:20 +00:00
function send_status_notifications ( $item )
{
// logger('notifications: ' . print_r($item, true));
$original_author = false ;
2022-09-22 05:27:30 +00:00
if ( $item [ 'mid' ] === $item [ 'parent_mid' ]) {
if ( intval ( $item [ 'item_blocked' ]) === ITEM_MODERATED ) {
if ( str_contains ( $item [ 'body' ], '[/share]' )) {
preg_match ( " /portable_id='(.*?)'/ism " , $item [ 'body' ], $matches );
logger ( 'matches: ' . print_r ( $matches , true ));
if ( $matches ) {
$s = q ( " select * from xchan where xchan_hash = '%s' " ,
dbesc ( urldecode ( $matches [ 1 ]))
);
if ( $s ) {
$original_author = array_shift ( $s );
}
2022-09-22 01:33:20 +00:00
}
}
}
2022-09-22 05:27:30 +00:00
else {
return ;
}
2022-09-22 01:33:20 +00:00
}
2022-07-20 00:43:27 +00:00
$notify = false ;
$unfollowed = false ;
$parent = 0 ;
$thr_parent_id = 0 ;
2022-07-27 22:16:27 +00:00
2022-09-22 01:33:20 +00:00
if ( array_key_exists ( 'verb' , $item ) && ( activity_match ( $item [ 'verb' ], ACTIVITY_LIKE ) || activity_match ( $item [ 'verb' ], ACTIVITY_DISLIKE ))) {
2022-07-20 00:43:27 +00:00
$r = q ( " select id from item where mid = '%s' and uid = %d limit 1 " ,
dbesc ( $item [ 'thr_parent' ]),
intval ( $item [ 'uid' ])
);
$thr_parent_id = $r [ 0 ][ 'id' ];
}
2022-09-22 05:27:30 +00:00
$r = Channel :: from_id ( $item [ 'uid' ]);
if ( ! $r ) {
2022-07-20 00:43:27 +00:00
return ;
2022-09-22 05:27:30 +00:00
}
2022-07-20 00:43:27 +00:00
// my own post - no notification needed
2022-09-22 05:27:30 +00:00
if ( $item [ 'author_xchan' ] === $r [ 'channel_hash' ] && intval ( $item [ 'item_blocked' ]) !== ITEM_MODERATED ) {
2022-07-20 00:43:27 +00:00
return ;
2022-09-22 01:33:20 +00:00
}
2022-07-20 00:43:27 +00:00
// I'm the owner - notify me
2022-09-22 05:27:30 +00:00
if ( $item [ 'owner_xchan' ] === $r [ 'channel_hash' ]) {
2022-07-20 00:43:27 +00:00
$notify = true ;
2022-09-22 05:27:30 +00:00
}
2022-07-20 00:43:27 +00:00
// Was I involved in this conversation?
$x = q ( " select * from item where parent_mid = '%s' and uid = %d " ,
dbesc ( $item [ 'parent_mid' ]),
intval ( $item [ 'uid' ])
);
2022-09-22 05:27:30 +00:00
if ( $x ) {
foreach ( $x as $xx ) {
2022-09-29 21:56:59 +00:00
if ( $xx [ 'author_xchan' ] === $r [ 'channel_hash' ]) {
2022-07-20 00:43:27 +00:00
$notify = true ;
// 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.
2022-09-22 05:27:30 +00:00
if (( $xx [ 'verb' ] === ACTIVITY_IGNORE )
&& ( $xx [ 'obj_type' ] === ACTIVITY_OBJ_NOTE || $xx [ 'obj_type' ] === ACTIVITY_OBJ_PHOTO )
&& ( $xx [ 'parent' ] != $xx [ 'id' ])) {
2022-07-20 00:43:27 +00:00
$unfollowed = true ;
2022-09-22 05:27:30 +00:00
}
2022-07-20 00:43:27 +00:00
}
2022-09-22 05:27:30 +00:00
if ( $xx [ 'id' ] == $xx [ 'parent' ]) {
2022-07-20 00:43:27 +00:00
$parent = $xx [ 'parent' ];
}
}
}
2022-09-22 05:27:30 +00:00
if ( $unfollowed ) {
2022-07-20 00:43:27 +00:00
return ;
2022-09-22 05:27:30 +00:00
}
2022-07-20 00:43:27 +00:00
if ( intval ( $item [ 'item_private' ]) === 2 ) {
$notify_type = NOTIFY_MAIL ;
}
2022-09-22 01:33:20 +00:00
elseif ( intval ( $item [ 'item_blocked' ]) === ITEM_MODERATED ) {
$notify_type = NOTIFY_MODERATE ;
}
2022-07-20 00:43:27 +00:00
elseif ( $item [ 'verb' ] === 'Announce' ) {
$notify_type = NOTIFY_RESHARE ;
}
else {
$notify_type = NOTIFY_COMMENT ;
}
2022-09-29 21:56:59 +00:00
2022-09-22 05:27:30 +00:00
$link = z_root () . (( $notify_type === NOTIFY_MODERATE ) ? '/moderate' : '/display' )
. '/?mid=' . gen_link_id ( $item [ 'mid' ]);
2022-09-29 21:56:59 +00:00
2022-09-22 01:33:20 +00:00
$y = q ( " select id from notify where link = '%s' and uid = %d limit 1 " ,
dbesc ( $link ),
intval ( $item [ 'uid' ])
);
2022-07-20 00:43:27 +00:00
2022-09-29 21:56:59 +00:00
2022-09-22 01:33:20 +00:00
if ( $y ) {
$notify = false ;
}
2022-07-20 00:43:27 +00:00
if ( ! $notify ) {
return ;
}
2022-08-14 09:20:43 +00:00
Enotify :: submit ([
2022-07-20 00:43:27 +00:00
'type' => $notify_type ,
2022-09-22 01:33:20 +00:00
'from_xchan' => $original_author ? $original_author [ 'xchan_hash' ] : $item [ 'author_xchan' ],
2022-09-22 05:27:30 +00:00
'to_xchan' => $r [ 'channel_hash' ],
2022-07-20 00:43:27 +00:00
'item' => $item ,
'link' => $link ,
'verb' => $item [ 'verb' ],
'otype' => 'item' ,
2022-08-14 09:20:43 +00:00
'parent' => $thr_parent_id ? : $parent ,
2022-07-20 00:43:27 +00:00
'parent_mid' => $thr_parent_id ? $item [ 'thr_parent' ] : $item [ 'parent_mid' ]
2022-08-14 09:20:43 +00:00
]);
2022-07-20 00:43:27 +00:00
}
/**
* @ brief Called when we deliver things that might be tagged in ways that require delivery processing .
*
* Handles community tagging of posts and also look for mention tags and sets up
* a second delivery chain if appropriate .
*
* @ param int $uid
* @ param int $item_id
*/
function tag_deliver ( $uid , $item_id ) {
$role = get_pconfig ( $uid , 'system' , 'permissions_role' );
$rolesettings = PermissionRoles :: role_perms ( $role );
2023-11-04 22:15:53 +00:00
$channel_type = isset ( $rolesettings [ 'channel_type' ]) ? $rolesettings [ 'channel_type' ] : 'normal' ;
2022-07-20 00:43:27 +00:00
2023-09-16 22:39:10 +00:00
$is_group = ( $channel_type === 'group' );
$is_collection = ( $channel_type === 'collection' );
2022-07-20 00:43:27 +00:00
2023-05-03 21:13:45 +00:00
logger ( 'checking tags' );
2023-09-16 22:39:10 +00:00
if ( $is_group ) {
logger ( 'Channel is a group.' );
}
2022-07-20 00:43:27 +00:00
/*
* Fetch stuff we need - a channel and an item
*/
$u = Channel :: from_id ( $uid );
if ( ! $u ) {
return ;
}
$i = q ( " select * from item where id = %d and uid = %d limit 1 " ,
intval ( $item_id ),
intval ( $uid )
);
if ( ! $i ) {
return ;
}
xchan_query ( $i , true );
$i = fetch_post_tags ( $i );
$item = array_shift ( $i );
if ( intval ( $item [ 'item_uplink' ]) && $item [ 'source_xchan' ]
&& intval ( $item [ 'item_thread_top' ]) && ( $item [ 'edited' ] != $item [ 'created' ])) {
// this is an update (edit) to a post which was already processed by us and has a second delivery chain
// Just start the second delivery chain to deliver the updated post
// after resetting ownership and permission bits
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
logger ( 'updating edited tag_deliver post for ' . $u [ 'channel_address' ]);
2022-08-14 09:20:43 +00:00
start_delivery_chain ( $u , $item , $item_id , false , false , true );
2022-07-20 00:43:27 +00:00
return ;
}
// send mail (DM) notifications here, but only for top level posts.
// followups will be processed by send_status_notifications()
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$mail_notify = false ;
if (( ! $item [ 'item_wall' ]) && intval ( $item [ 'item_thread_top' ]) && $item [ 'author_xchan' ] !== $u [ 'channel_hash' ] && intval ( $item [ 'item_private' ]) === 2 ) {
2022-09-17 00:25:44 +00:00
Enotify :: submit ([
2022-07-20 00:43:27 +00:00
'to_xchan' => $u [ 'channel_hash' ],
'from_xchan' => $item [ 'author_xchan' ],
'type' => NOTIFY_MAIL ,
'item' => $item ,
'link' => $item [ 'llink' ],
'verb' => 'DM' ,
'otype' => 'item'
2022-09-17 00:25:44 +00:00
]);
2022-07-20 00:43:27 +00:00
$mail_notify = true ;
}
// important - ignore wall posts here else dm's by group owner will be sent to group.
if ( $is_group && intval ( $item [ 'item_private' ]) === 2 && intval ( $item [ 'item_thread_top' ]) && ( ! intval ( $item [ 'item_wall' ]))) {
// group delivery via DM - use post_wall permission since send_stream is probably turned off
// and this will be turned into an embedded wall-to-wall post
2022-11-20 05:28:50 +00:00
if ( perm_is_allowed ( $uid , $item [ 'author_xchan' ], 'post_wall' )) {
2022-07-20 00:43:27 +00:00
logger ( 'group DM delivery for ' . $u [ 'channel_address' ]);
2022-08-14 09:20:43 +00:00
start_delivery_chain ( $u , $item , $item_id , false , true , (( $item [ 'edited' ] != $item [ 'created' ]) || $item [ 'item_deleted' ]));
2022-07-20 00:43:27 +00:00
q ( " update item set item_blocked = %d where id = %d " ,
intval ( ITEM_HIDDEN ),
intval ( $item_id )
);
}
return ;
}
// Deliver to group via target collection
2022-09-17 00:25:44 +00:00
if ( $is_group && intval ( $item [ 'item_thread_top' ]) && ( ! intval ( $item [ 'item_wall' ])) && ( str_contains ( $item [ 'tgt_type' ], 'Collection' )) && $item [ 'target' ]) {
2022-07-20 00:43:27 +00:00
// group delivery via target - use post_wall permission since send_stream is probably turned off
// and this will be turned into an embedded wall-to-wall post
if ( is_array ( $item [ 'target' ])) {
$a = $item [ 'target' ];
}
else {
$a = json_decode ( $item [ 'target' ], true );
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( $a ) {
$id = (( is_string ( $a )) ? $a : EMPTY_STR );
if ( is_array ( $a ) && isset ( $a [ 'id' ])) {
$id = $a [ 'id' ];
}
if ( $id == z_root () . '/outbox/' . $u [ 'channel_address' ]) {
2022-11-20 05:28:50 +00:00
if ( perm_is_allowed ( $uid , $item [ 'author_xchan' ], 'post_wall' )) {
2022-07-20 00:43:27 +00:00
logger ( 'group collection delivery for ' . $u [ 'channel_address' ]);
2022-08-14 09:20:43 +00:00
start_delivery_chain ( $u , $item , $item_id , false , true , (( $item [ 'edited' ] != $item [ 'created' ]) || $item [ 'item_deleted' ]));
2022-07-20 00:43:27 +00:00
q ( " update item set item_blocked = %d where id = %d " ,
intval ( ITEM_HIDDEN ),
intval ( $item_id )
);
}
return ;
}
}
}
if ( $is_group && intval ( $item [ 'item_thread_top' ]) && intval ( $item [ 'item_wall' ]) && $item [ 'author_xchan' ] !== $item [ 'owner_xchan' ]) {
if ( strpos ( $item [ 'body' ], '[/share]' )) {
logger ( 'W2W post already shared' );
return ;
}
// group delivery via W2W
logger ( 'rewriting W2W post for ' . $u [ 'channel_address' ]);
2022-08-14 09:20:43 +00:00
start_delivery_chain ( $u , $item , $item_id , false , true , (( $item [ 'edited' ] != $item [ 'created' ]) || $item [ 'item_deleted' ]));
2022-07-20 00:43:27 +00:00
q ( " update item set item_wall = 0 where id = %d " ,
intval ( $item_id )
);
return ;
}
/*
* A " union " is a message which our channel has sourced from another channel .
* This sets up a second delivery chain just like forum tags do .
* Find out if this is a source - able post .
*/
$union = check_item_source ( $uid , $item );
if ( $union ) {
logger ( 'check_item_source returns true' );
}
// This might be a followup (e.g. comment) to an already uplinked item
// If so setup a second delivery chain
if ( ! intval ( $item [ 'item_thread_top' ])) {
$x = q ( " select * from item where id = parent and parent = %d and uid = %d limit 1 " ,
intval ( $item [ 'parent' ]),
intval ( $uid )
);
if ( $x ) {
// group comments don't normally require a second delivery chain
// but we create a linked Announce so they will show up in the home timeline
// on microblog platforms and this creates a second delivery chain
2022-07-27 22:16:27 +00:00
2023-05-02 23:02:10 +00:00
if (( $is_group || $union ) && intval ( $x [ 0 ][ 'item_wall' ])) {
2022-07-20 00:43:27 +00:00
// don't let the forked delivery chain recurse
if ( $item [ 'verb' ] === 'Announce' && $item [ 'author_xchan' ] === $u [ 'channel_hash' ]) {
return ;
}
// don't boost moderated content until it has been approved
if ( intval ( $item [ 'item_blocked' ]) === ITEM_MODERATED ) {
return ;
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
// don't boost likes and other response activities as it is likely that
// few platforms will handle this in an elegant way
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( ActivityStreams :: is_response_activity ( $item [ 'verb' ])) {
return ;
}
logger ( 'group_comment' );
start_delivery_chain ( $u , $item , $item_id , $x [ 0 ], true , (( $item [ 'edited' ] != $item [ 'created' ]) || $item [ 'item_deleted' ]));
}
elseif ( intval ( $x [ 0 ][ 'item_uplink' ])) {
start_delivery_chain ( $u , $item , $item_id , $x [ 0 ]);
}
}
}
/*
* Now we 've got those out of the way. Let' s see if this is a post that ' s tagged for re - delivery
*/
2022-09-27 21:27:09 +00:00
$terms = (( isset ( $item [ 'term' ])) ? get_terms_oftype ( $item [ 'term' ],[ TERM_MENTION , TERM_FORUM ]) : false );
2022-07-20 00:43:27 +00:00
$pterms = (( isset ( $item [ 'term' ])) ? get_terms_oftype ( $item [ 'term' ], [ TERM_PCATEGORY , TERM_HASHTAG ] ) : false );
if ( $terms ) {
logger ( 'Post mentions: ' . print_r ( $terms , true ), LOGGER_DATA );
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( $pterms ) {
logger ( 'Post collections: ' . print_r ( $pterms , true ), LOGGER_DATA );
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$link = normalise_link ( $u [ 'xchan_url' ]);
if ( $terms ) {
foreach ( $terms as $term ) {
if ( ! link_compare ( $term [ 'url' ], $link )) {
continue ;
}
logger ( 'Mention found for ' . $u [ 'channel_name' ]);
2022-08-14 09:20:43 +00:00
q ( " update item set item_mentionsme = 1 where id = %d " ,
2022-07-20 00:43:27 +00:00
intval ( $item_id )
);
// At this point we've determined that the person receiving this post was mentioned in it or it is a union.
// Now let's check if this mention was inside a reshare so we don't spam a forum
2023-10-04 10:59:30 +00:00
$body = strip_share_content ( $item [ 'body' ]);
2022-07-20 00:43:27 +00:00
$tagged = false ;
$matches = [];
2022-09-17 00:25:44 +00:00
$pattern = '/[!@]!?\[[uz]rl=' . preg_quote ( $term [ 'url' ], '/' ) . '](.*?)\[\/[uz]rl]/' ;
2022-07-20 00:43:27 +00:00
if ( preg_match ( $pattern , $body , $matches ))
$tagged = true ;
2022-09-17 00:25:44 +00:00
$pattern = '/\[[uz]rl=' . preg_quote ( $term [ 'url' ], '/' ) . '][!@](.*?)\[\/[uz]rl]/' ;
2022-07-20 00:43:27 +00:00
if ( preg_match ( $pattern , $body , $matches ))
$tagged = true ;
if ( ! $tagged ) {
logger ( 'Mention was in a reshare - ignoring' );
continue ;
}
$arr = [
'channel_id' => $uid ,
'item' => $item ,
'body' => $body
];
/**
* @ hooks tagged
* Called when a delivery is processed which results in you being tagged .
* * \e number \b channel_id
* * \e array \b item
* * \e string \b body
*/
Hook :: call ( 'tagged' , $arr );
/**
2022-09-27 21:27:09 +00:00
* post to a group via group tags ( ! group ) or normal @- mentions * only if * the group is public
2022-07-20 00:43:27 +00:00
* but let the owner change this with a hidden pconfig and either allow
* or deny this option regardless of the type of group
*/
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( $is_group && intval ( $item [ 'item_thread_top' ]) && ( ! intval ( $item [ 'item_wall' ]))) {
2022-11-20 05:28:50 +00:00
if (( intval ( $term [ 'ttype' ]) === TERM_FORUM || get_pconfig ( $uid , 'system' , 'post_via_mentions' , in_array ( $role ,[ 'forum' , 'forum_moderated' ]))) && perm_is_allowed ( $uid , $item [ 'author_xchan' ], 'post_wall' )) {
2022-07-20 00:43:27 +00:00
logger ( 'group mention delivery for ' . $u [ 'channel_address' ]);
2022-08-14 09:20:43 +00:00
start_delivery_chain ( $u , $item , $item_id , false , true , (( $item [ 'edited' ] != $item [ 'created' ]) || $item [ 'item_deleted' ]));
2022-07-20 00:43:27 +00:00
q ( " update item set item_blocked = %d where id = %d " ,
intval ( ITEM_HIDDEN ),
intval ( $item_id )
);
}
}
/*
* Send a mention notification - unless we just sent a mail notification for the same item
*/
if ( ! $mail_notify ) {
2022-09-17 00:25:44 +00:00
Enotify :: submit ([
2022-07-20 00:43:27 +00:00
'to_xchan' => $u [ 'channel_hash' ],
'from_xchan' => $item [ 'author_xchan' ],
'type' => NOTIFY_TAGSELF ,
'item' => $item ,
'link' => $item [ 'llink' ],
'verb' => ACTIVITY_TAG ,
'otype' => 'item'
2022-09-17 00:25:44 +00:00
]);
2022-07-20 00:43:27 +00:00
}
}
}
if ( $is_collection && $pterms ) {
foreach ( $pterms as $term ) {
$ptagged = false ;
if ( link_compare ( $term [ 'url' ], $link )) {
$ptagged = true ;
}
elseif ( $term [ 'ttype' ] === TERM_HASHTAG && strtolower ( $term [ 'term' ]) === strtolower ( $u [ 'channel_address' ])) {
$ptagged = true ;
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( ! $ptagged ) {
continue ;
}
logger ( 'Collection post found for ' . $u [ 'channel_name' ]);
// ptagged - keep going, next check permissions
2022-11-20 05:28:50 +00:00
if (( ! perm_is_allowed ( $uid , $item [ 'author_xchan' ], 'write_collection' )) && ( $item [ 'author_xchan' ] !== $u [ 'channel_parent' ])) {
2022-07-20 00:43:27 +00:00
logger ( 'tag_delivery denied for uid ' . $uid . ' and xchan ' . $item [ 'author_xchan' ]);
continue ;
}
2023-02-11 21:22:06 +00:00
// tgroup delivery - set up a second delivery chain
2022-07-20 00:43:27 +00:00
// prevent delivery looping - only proceed
// if the message originated elsewhere and is a top-level post
if ( intval ( $item [ 'item_wall' ]) || intval ( $item [ 'item_origin' ]) || ( ! intval ( $item [ 'item_thread_top' ])) || ( $item [ 'id' ] != $item [ 'parent' ])) {
logger ( 'Item was local or a comment. rejected.' );
continue ;
}
logger ( 'Creating second delivery chain.' );
2022-08-14 09:20:43 +00:00
start_delivery_chain ( $u , $item , $item_id , false );
2022-07-20 00:43:27 +00:00
}
}
if ( $union ) {
if ( intval ( $item [ 'item_wall' ]) || intval ( $item [ 'item_origin' ]) || ( ! intval ( $item [ 'item_thread_top' ])) || ( $item [ 'id' ] != $item [ 'parent' ])) {
logger ( 'Item was local or a comment. rejected.' );
return ;
}
logger ( 'Creating second delivery chain.' );
2022-08-14 09:20:43 +00:00
start_delivery_chain ( $u , $item , $item_id , false );
2022-07-20 00:43:27 +00:00
}
}
/**
* @ brief This function is called pre - deliver to see if a post matches the criteria to be tag delivered .
*
* We don ' t actually do anything except check that it matches the criteria .
* This is so that the channel with tag_delivery enabled can receive the post even if they turn off
* permissions for the sender to send their stream . tag_deliver () can ' t be called until the post is actually stored .
* By then it would be too late to reject it .
*
* @ param number $uid A chnnel_id
* @ param array $item
* @ return boolean
*/
function tgroup_check ( $uid , $item ) {
$role = get_pconfig ( $uid , 'system' , 'permissions_role' );
$rolesettings = PermissionRoles :: role_perms ( $role );
2023-11-04 22:15:53 +00:00
$channel_type = isset ( $rolesettings [ 'channel_type' ]) ? $rolesettings [ 'channel_type' ] : 'normal' ;
2022-07-20 00:43:27 +00:00
2023-02-11 21:22:06 +00:00
$is_group = $channel_type === 'group' ;
$is_collection = $channel_type === 'collection' ;
2022-07-20 00:43:27 +00:00
// If a comment, check if we have already accepted the top level post as an uplink
// Applies to collections only at this time
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( $item [ 'mid' ] !== $item [ 'parent_mid' ]) {
2024-02-18 02:13:10 +00:00
$r = q ( " select id from item where mid = '%s' and uid = %d and (item_uplink = 1 or item_wall = 1) limit 1 " ,
2022-07-20 00:43:27 +00:00
dbesc ( $item [ 'parent_mid' ]),
intval ( $uid )
);
if ( $r ) {
return true ;
}
}
$u = Channel :: from_id ( $uid );
if ( ! $u ) {
return false ;
}
// post to group via DM
if ( $is_group ) {
if ( intval ( $item [ 'item_private' ]) === 2 && $item [ 'mid' ] === $item [ 'parent_mid' ]) {
return true ;
}
2022-09-17 00:25:44 +00:00
if ( $item [ 'mid' ] === $item [ 'parent_mid' ] && ( ! intval ( $item [ 'item_wall' ])) && ( str_contains ( $item [ 'tgt_type' ], 'Collection' )) && $item [ 'target' ]) {
2022-07-20 00:43:27 +00:00
// accept posts to collections only if the collection belongs to us
2022-09-17 00:25:44 +00:00
if (( is_string ( $item [ 'target' ]) && str_contains ( $item [ 'target' ], z_root ()))
|| ( isset ( $item [ 'target' ][ 'id' ]) && str_contains ( $item [ 'target' ][ 'id' ], z_root ()))) {
2022-07-20 00:43:27 +00:00
return true ;
}
}
if ( get_pconfig ( $uid , 'system' , 'post_via_mentions' , in_array ( $role , [ 'forum' , 'forum_moderated' ])) && i_am_mentioned ( $u , $item )) {
return true ;
}
2022-09-27 21:27:09 +00:00
elseif ( i_am_mentioned ( $u , $item , true )) {
return true ;
}
2022-07-20 00:43:27 +00:00
}
// see if we already have this item. Maybe it is being updated.
$r = q ( " select id from item where mid = '%s' and uid = %d limit 1 " ,
dbesc ( $item [ 'mid' ]),
intval ( $uid )
);
if ( $r ) {
return true ;
}
2022-07-27 22:16:27 +00:00
2022-11-20 05:28:50 +00:00
if (( $is_collection ) && ( perm_is_allowed ( $uid , $item [ 'author_xchan' ], 'write_collection' ) || $item [ 'author_xchan' ] === $u [ 'channel_parent' ])) {
2022-07-20 00:43:27 +00:00
return true ;
}
2023-02-11 21:22:06 +00:00
// return true if we are mentioned, and we permit delivery of mentions from strangers
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( PConfig :: Get ( $uid , 'system' , 'permit_all_mentions' ) && i_am_mentioned ( $u , $item )) {
return true ;
}
2023-03-13 02:41:15 +00:00
if ( PConfig :: Get ( $uid , 'system' , 'permit_all_likes' ) && $item [ 'verb' ] === 'Like' && $item [ 'obj_type' ] === 'Note' ) {
2022-10-18 09:16:33 +00:00
return true ;
}
2022-07-20 00:43:27 +00:00
$tag_result = false ;
$terms = (( isset ( $item [ 'term' ])) ? get_terms_oftype ( $item [ 'term' ], TERM_HASHTAG ) : false );
if ( $terms ) {
$followed_tags = PConfig :: Get ( $uid , 'system' , 'followed_tags' );
if ( ! ( is_array ( $followed_tags ) && $followed_tags )) {
return false ;
}
foreach ( $terms as $term ) {
foreach ( $followed_tags as $tag ) {
if ( strcasecmp ( $term [ 'term' ], $tag ) === 0 ) {
$tag_result = true ;
}
}
}
2022-08-14 09:20:43 +00:00
$unless = intval ( PConfig :: Get ( $uid , 'system' , 'unless_tag_count' ,
Config :: Get ( 'system' , 'unless_tag_count' , 20 )));
2022-07-20 00:43:27 +00:00
if ( $unless && count ( $terms ) > $unless ) {
$tag_result = false ;
}
}
return $tag_result ;
}
2022-09-27 21:27:09 +00:00
function i_am_mentioned ( $channel , $item , $check_groups = false ) {
2022-07-20 00:43:27 +00:00
$link = $channel [ 'xchan_url' ];
2022-09-17 00:25:44 +00:00
$body = preg_replace ( '/\[share(.*?)\[\/share]/' , '' , $item [ 'body' ]);
2022-07-20 00:43:27 +00:00
$tagged = false ;
$matches = [];
2022-09-27 21:27:09 +00:00
$tagtype = $check_groups ? TERM_FORUM : TERM_MENTION ;
$terms = (( isset ( $item [ 'term' ])) ? get_terms_oftype ( $item [ 'term' ], $tagtype ) : false );
2022-07-20 00:43:27 +00:00
if ( $terms ) {
foreach ( $terms as $term ) {
if ( $link === $term [ 'url' ]) {
2022-09-17 00:25:44 +00:00
$pattern = '/[!@]!?\[[uz]rl=' . preg_quote ( $term [ 'url' ], '/' ) . '](.*?)\[\/[uz]rl]/' ;
2022-07-20 00:43:27 +00:00
if ( preg_match ( $pattern , $body , $matches )) {
$tagged = true ;
}
2022-09-17 00:25:44 +00:00
$pattern = '/\[[uz]rl=' . preg_quote ( $term [ 'url' ], '/' ) . '][!@](.*?)\[\/[uz]rl]/' ;
2022-07-20 00:43:27 +00:00
if ( preg_match ( $pattern , $body , $matches )) {
$tagged = true ;
}
}
}
}
$unless = intval ( get_pconfig ( $channel [ 'channel_id' ], 'system' , 'unless_mention_count' , get_config ( 'system' , 'unless_mention_count' , 20 )));
if ( $unless && $terms && count ( $terms ) > $unless ) {
$tagged = false ;
}
return $tagged ;
}
/**
* Sourced and tag - delivered posts are re - targetted for delivery to the connections of the channel
* receiving the post . This starts the second delivery chain , by resetting permissions and ensuring
* that ITEM_UPLINK is set on the parent post , and storing the current owner_xchan as the source_xchan .
* We ' ll become the new owner . If called without $parent , this * is * the parent post .
*
* @ param array $channel
* @ param array $item
* @ param int $item_id
2022-08-14 09:20:43 +00:00
* @ param bool | array $parent
2022-07-20 00:43:27 +00:00
*/
2022-08-14 09:20:43 +00:00
function start_delivery_chain ( $channel , $item , $item_id , bool | array $parent , $group = false , $edit = false ) {
2022-07-20 00:43:27 +00:00
2023-05-03 10:20:57 +00:00
// btlogger('start_chain: ' . $channel['channel_id'] . ' item: ' . $item_id);
2022-09-22 01:33:20 +00:00
$moderated = perm_is_allowed ( $channel [ 'channel_id' ], $item [ 'author_xchan' ], 'moderated' );
2022-07-27 22:16:27 +00:00
2023-05-02 20:52:34 +00:00
$source = check_item_source ( $channel [ 'channel_id' ], $item );
2022-07-20 00:43:27 +00:00
2024-02-16 19:14:53 +00:00
if ( $item [ 'author_xchan' ] === $channel [ 'channel_hash' ] && in_array ( $item [ 'verb' ], [ 'Add' , 'Remove' ])) {
logger ( 'delivery chain already started' );
return ;
}
2022-07-20 00:43:27 +00:00
// This creates an embedded share authored by the group actor.
// The original message is no longer needed and its presence can cause
// confusion so make it hidden.
2023-05-02 20:52:34 +00:00
if ( ! $parent ) {
2022-07-20 00:43:27 +00:00
$arr = [];
// hide original message
q ( " update item set item_hidden = 1 where id = %d " ,
intval ( $item_id )
);
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( $edit ) {
// process edit or delete action
$r = q ( " select * from item where source_xchan = '%s' and body like '%s' and uid = %d limit 1 " ,
dbesc ( $item [ 'owner_xchan' ]),
dbesc ( " %message_id=' " . $item [ 'mid' ] . " '% " ),
intval ( $channel [ 'channel_id' ])
);
if ( $r ) {
if ( intval ( $item [ 'item_deleted' ])) {
drop_item ( $r [ 0 ][ 'id' ], DROPITEM_PHASE1 );
Run :: Summon ([ 'Notifier' , 'drop' , $r [ 0 ][ 'id' ] ]);
return ;
}
$arr [ 'id' ] = intval ( $r [ 0 ][ 'id' ]);
$arr [ 'parent' ] = intval ( $r [ 0 ][ 'parent' ]);
$arr [ 'mid' ] = $r [ 0 ][ 'mid' ];
$arr [ 'parent_mid' ] = $r [ 0 ][ 'parent_mid' ];
$arr [ 'edited' ] = datetime_convert ();
}
else {
logger ( 'unable to locate original group post ' . $item [ 'mid' ]);
return ;
}
}
else {
$arr [ 'mid' ] = item_message_id ();
$arr [ 'parent_mid' ] = $arr [ 'mid' ];
IConfig :: Set ( $arr , 'activitypub' , 'context' , str_replace ( '/item/' , '/conversation/' , $arr [ 'mid' ]));
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'aid' ] = $channel [ 'channel_account_id' ];
$arr [ 'uid' ] = $channel [ 'channel_id' ];
// WARNING: the presence of both source_xchan and non-zero item_uplink here will cause a delivery loop
$arr [ 'item_uplink' ] = 0 ;
2023-05-29 20:25:57 +00:00
$arr [ 'source_xchan' ] = $item [ 'author_xchan' ];
2022-07-20 00:43:27 +00:00
$arr [ 'item_private' ] = (( $channel [ 'channel_allow_cid' ] || $channel [ 'channel_allow_gid' ] || $channel [ 'channel_deny_cid' ] || $channel [ 'channel_deny_gid' ]) ? 1 : 0 );
$arr [ 'item_origin' ] = 1 ;
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'item_wall' ] = 1 ;
$arr [ 'item_thread_top' ] = 1 ;
2022-07-27 22:16:27 +00:00
2022-09-22 01:33:20 +00:00
if ( $moderated ) {
$arr [ 'item_blocked' ] = ITEM_MODERATED ;
}
2022-07-20 00:43:27 +00:00
$bb = " [share author=' " . urlencode ( $item [ 'author' ][ 'xchan_name' ]) .
" ' profile=' " . $item [ 'author' ][ 'xchan_url' ] .
2022-07-27 22:16:27 +00:00
" ' portable_id=' " . $item [ 'author' ][ 'xchan_hash' ] .
2022-07-20 00:43:27 +00:00
" ' avatar=' " . $item [ 'author' ][ 'xchan_photo_s' ] .
" ' link=' " . $item [ 'plink' ] .
2023-11-14 07:11:15 +00:00
" ' auth=' " . (( isOWAEnabled ( $item [ 'author' ][ 'xchan_url' ])) ? 'true' : 'false' ) .
2022-07-20 00:43:27 +00:00
" ' posted=' " . $item [ 'created' ] .
" ' message_id=' " . $item [ 'mid' ] .
" '] " ;
if ( $item [ 'title' ]) {
$bb .= '[b]' . $item [ 'title' ] . '[/b]' . " \r \n " ;
$arr [ 'title' ] = $item [ 'title' ];
}
$bb .= $item [ 'body' ];
2024-01-14 05:59:46 +00:00
$bb .= " \n [/share] " ;
2022-07-20 00:43:27 +00:00
$arr [ 'body' ] = $bb ;
$tagpref = intval ( PConfig :: Get ( $channel [ 'channel_id' ], 'system' , 'tag_username' , Config :: Get ( 'system' , 'tag_username' , false )));
$mention = $item [ 'author' ][ 'xchan_name' ];
if ( $tagpref === 1 && $item [ 'author' ][ 'xchan_addr' ]) {
$mention = $item [ 'author' ][ 'xchan_addr' ];
}
if ( $tagpref === 2 && $item [ 'author' ][ 'xchan_addr' ]) {
$mention = sprintf ( t ( '%1$s (%2$s)' ), $item [ 'author' ][ 'xchan_name' ], $item [ 'author' ][ 'xchan_addr' ]);
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'body' ] .= " \n " . '@[zrl=' . $item [ 'author' ][ 'xchan_url' ] . ']' . $mention . '[/zrl]' ;
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
// Conversational objects shouldn't be copied, but other objects should.
2023-08-20 00:23:21 +00:00
if ( in_array ( $item [ 'obj_type' ], [ 'Image' , 'Event' , 'Question' ])) {
2022-07-20 00:43:27 +00:00
$arr [ 'obj' ] = $item [ 'obj' ];
$t = json_decode ( $arr [ 'obj' ], true );
if ( $t !== NULL ) {
$arr [ 'obj' ] = $t ;
}
$arr [ 'obj' ][ 'content' ] = bbcode ( $bb , [ 'export' => true ]);
$arr [ 'obj' ][ 'source' ][ 'content' ] = $bb ;
$arr [ 'obj' ][ 'id' ] = $arr [ 'mid' ];
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( ! array_path_exists ( 'obj/source/mediaType' , $arr )) {
$arr [ 'obj' ][ 'source' ][ 'mediaType' ] = 'text/x-multicode' ;
}
}
2024-02-18 02:13:10 +00:00
$arr [ 'tgt_type' ] = 'Collection' ;
$arr [ 'target' ] = [
'id' => str_replace ( '/item/' , '/conversation/' , $arr [ 'parent_mid' ]),
'type' => 'Collection' ,
'attributedTo' => z_root () . '/channel/' . $channel [ 'channel_address' ],
];
2022-07-20 00:43:27 +00:00
// Add a mention for the author.
2023-02-11 21:22:06 +00:00
$arr [ 'term' ] = ( $item [ 'term' ]) ? : [];
2022-07-20 00:43:27 +00:00
$arr [ 'term' ][] = [
'uid' => $channel [ 'channel_id' ],
'ttype' => TERM_MENTION ,
'otype' => TERM_OBJ_POST ,
'term' => '@' . $item [ 'author' ][ 'xchan_addr' ],
'url' => $item [ 'author' ][ 'xchan_url' ]
];
$arr [ 'author_xchan' ] = $channel [ 'channel_hash' ];
$arr [ 'owner_xchan' ] = $channel [ 'channel_hash' ];
$arr [ 'obj_type' ] = $item [ 'obj_type' ];
2023-08-04 20:11:24 +00:00
$arr [ 'verb' ] = 'Create' ;
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'allow_cid' ] = $channel [ 'channel_allow_cid' ];
$arr [ 'allow_gid' ] = $channel [ 'channel_allow_gid' ];
$arr [ 'deny_cid' ] = $channel [ 'channel_deny_cid' ];
$arr [ 'deny_gid' ] = $channel [ 'channel_deny_gid' ];
$arr [ 'comment_policy' ] = map_scope ( PermissionLimits :: Get ( $channel [ 'channel_id' ], 'post_comments' ));
2023-02-11 21:22:06 +00:00
$merge = (( $item [ 'attach' ]) ? : []);
2022-12-12 03:07:17 +00:00
if ( is_string ( $merge )) {
$merge = json_decode ( $merge , true );
}
2023-05-29 20:50:03 +00:00
if ( $merge ) {
$arr [ 'attach' ] = array_merge ( $merge , [[ 'type' => 'application/activity+json' , 'href' => $item [ 'mid' ]]]);
}
else {
$arr [ 'attach' ] = [[ 'type' => 'application/activity+json' , 'href' => $item [ 'mid' ]]];
}
2022-07-20 00:43:27 +00:00
$arr [ 'replyto' ] = z_root () . '/channel/' . $channel [ 'channel_address' ];
2024-02-18 02:13:10 +00:00
if ( ! array_key_exists ( 'obj' , $arr )) {
$copy = $arr ;
$copy [ 'author' ] = $channel ;
$arr [ 'obj' ] = Activity :: encode_item ( $copy , (( get_config ( 'system' , 'activitypub' , ACTIVITYPUB_ENABLED )) ? true : false ));
$recips = [];
$i = $arr [ 'obj' ];
if ( $i [ 'to' ]) {
$arr [ 'to' ] = $i [ 'to' ];
}
if ( $i [ 'cc' ]) {
$arr [ 'cc' ] = $i [ 'cc' ];
}
IConfig :: Set ( $arr , 'activitypub' , 'recips' , $recips );
}
2022-07-20 00:43:27 +00:00
if ( $arr [ 'id' ]) {
$post = item_store_update ( $arr );
}
else {
$post = item_store ( $arr );
}
$post_id = $post [ 'item_id' ];
2023-05-02 20:52:34 +00:00
if ( $source && $post_id && ! $edit ) {
$t = trim ( $source [ 'src_tag' ]);
if ( $t ) {
$tags = explode ( ',' , $t );
if ( $tags ) {
foreach ( $tags as $tt ) {
$tt = trim ( $tt );
if ( $tt ) {
q ( " insert into term (uid,oid,otype,ttype,term,url)
values ( % d , % d , % d , % d , '%s' , '%s' ) " ,
intval ( $channel [ 'channel_id' ]),
intval ( $post_id ),
intval ( TERM_OBJ_POST ),
intval ( TERM_CATEGORY ),
dbesc ( $tt ),
dbesc ( z_root () . '/channel/' . $channel [ 'channel_address' ] . '?f=&cat=' . urlencode ( $tt ))
);
}
}
}
}
}
2024-02-16 19:14:53 +00:00
$post_id = $post [ 'success' ] ? $post [ 'item_id' ] : 0 ;
2022-07-20 00:43:27 +00:00
if ( $post_id ) {
2024-02-02 10:48:49 +00:00
Run :: Summon ([ 'Notifier' , 'tgroup' , $post_id ]);
2024-02-10 19:22:39 +00:00
if ( ! empty ( $post [ 'approval_id' ])) {
Run :: Summon ([ 'Notifier' , 'tgroup' , $post [ 'approval_id' ]]);
}
2022-07-20 00:43:27 +00:00
}
q ( " update channel set channel_lastpost = '%s' where channel_id = %d " ,
dbesc ( datetime_convert ()),
intval ( $channel [ 'channel_id' ])
);
return ;
}
2022-09-22 05:27:30 +00:00
// Send Announce activities for group comments, so they will show up in microblog streams
2022-07-20 00:43:27 +00:00
2023-05-02 20:52:34 +00:00
if ( $parent ) {
logger ( 'comment arrived for new delivery chain' , LOGGER_DEBUG );
2022-07-20 00:43:27 +00:00
$arr = [];
// don't let this recurse. We checked for this before calling, but this ensures
// it doesn't sneak through another way because recursion is nasty.
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( $item [ 'verb' ] === 'Announce' && $item [ 'author_xchan' ] === $channel [ 'channel_hash' ]) {
return ;
}
// Don't send Announce activities for poll responses.
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( $item [ 'obj_type' ] === 'Answer' ) {
return ;
}
if ( $edit ) {
if ( intval ( $item [ 'item_deleted' ])) {
drop_item ( $item [ 'id' ], DROPITEM_PHASE1 );
2022-09-22 05:27:30 +00:00
Run :: Summon ([ 'Notifier' , 'drop' , $item [ 'id' ] ]);
2022-07-20 00:43:27 +00:00
return ;
}
return ;
}
else {
$arr [ 'mid' ] = item_message_id ();
$arr [ 'parent_mid' ] = $item [ 'parent_mid' ];
IConfig :: Set ( $arr , 'activitypub' , 'context' , str_replace ( '/item/' , '/conversation/' , $item [ 'parent_mid' ]));
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'aid' ] = $channel [ 'channel_account_id' ];
$arr [ 'uid' ] = $channel [ 'channel_id' ];
$arr [ 'verb' ] = 'Announce' ;
if ( is_array ( $item [ 'obj' ])) {
$arr [ 'obj' ] = $item [ 'obj' ];
}
elseif ( is_string ( $item [ 'obj' ]) && strlen ( $item [ 'obj' ])) {
$arr [ 'obj' ] = json_decode ( $item [ 'obj' ], true );
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( ! $arr [ 'obj' ]) {
$arr [ 'obj' ] = $item [ 'mid' ];
}
if ( is_array ( $arr [ 'obj' ])) {
$obj_actor = (( isset ( $arr [ 'obj' ][ 'actor' ])) ? $arr [ 'obj' ][ 'actor' ] : $arr [ 'obj' ][ 'attributedTo' ]);
$mention = Activity :: get_actor_bbmention ( $obj_actor );
2023-10-09 19:08:54 +00:00
$arr [ 'body' ] = sprintf ( t ( '📢 Repeated %1$s\'s %2$s' ), $mention , $arr [ 'obj' ][ 'type' ]);
2022-07-20 00:43:27 +00:00
}
$arr [ 'author_xchan' ] = $channel [ 'channel_hash' ];
$arr [ 'item_wall' ] = 1 ;
$arr [ 'item_private' ] = (( $channel [ 'channel_allow_cid' ] || $channel [ 'channel_allow_gid' ] || $channel [ 'channel_deny_cid' ] || $channel [ 'channel_deny_gid' ]) ? 1 : 0 );
$arr [ 'item_origin' ] = 1 ;
$arr [ 'item_thread_top' ] = 0 ;
$arr [ 'allow_cid' ] = $channel [ 'channel_allow_cid' ];
$arr [ 'allow_gid' ] = $channel [ 'channel_allow_gid' ];
$arr [ 'deny_cid' ] = $channel [ 'channel_deny_cid' ];
$arr [ 'deny_gid' ] = $channel [ 'channel_deny_gid' ];
$arr [ 'comment_policy' ] = map_scope ( PermissionLimits :: Get ( $channel [ 'channel_id' ], 'post_comments' ));
$arr [ 'replyto' ] = z_root () . '/channel/' . $channel [ 'channel_address' ];
2024-02-18 02:13:10 +00:00
$post = item_store ( $arr , deliver : false , addAndSync : false );
2022-07-20 00:43:27 +00:00
$post_id = $post [ 'item_id' ];
if ( $post_id ) {
Run :: Summon ([ 'Notifier' , 'tgroup' , $post_id ]);
}
q ( " update channel set channel_lastpost = '%s' where channel_id = %d " ,
dbesc ( datetime_convert ()),
intval ( $channel [ 'channel_id' ])
);
}
}
/**
* @ brief
*
* Checks to see if this item owner is referenced as a source for this channel and if the post
* matches the rules for inclusion in this channel . Returns true if we should create a second delivery
* chain and false if none of the rules apply , or if the item is private .
*
* @ param int $uid
* @ param array $item
*/
function check_item_source ( $uid , $item ) {
logger ( 'source: uid: ' . $uid , LOGGER_DEBUG );
$xchan = (( isset ( $item [ 'source_xchan' ]) && $item [ 'source_xchan' ] && isset ( $item [ 'item_uplink' ]) && intval ( $item [ 'item_uplink' ])) ? $item [ 'source_xchan' ] : $item [ 'owner_xchan' ]);
2023-05-06 19:13:12 +00:00
$linkedIds = null ;
$channel = Channel :: from_id ( $uid );
if ( $channel ) {
$linkedIds = q ( " select * from linkid where ident = '%s' and sigtype = %d " ,
dbesc ( $channel [ 'channel_hash' ]),
intval ( IDLINK_RELME )
);
}
2022-07-20 00:43:27 +00:00
$r = q ( " select * from source where src_channel_id = %d and ( src_xchan = '%s' or src_xchan = '*' ) limit 1 " ,
intval ( $uid ),
dbesc ( $xchan )
);
if ( ! $r ) {
logger ( 'source: no source record for this channel and source' , LOGGER_DEBUG );
return false ;
}
$x = q ( " select abook_feed from abook where abook_channel = %d and abook_xchan = '%s' limit 1 " ,
intval ( $uid ),
dbesc ( $xchan )
);
if ( ! $x ) {
logger ( 'source: not connected to this channel.' );
return false ;
}
if ( ! their_perms_contains ( $uid , $xchan , 'republish' )) {
2023-05-06 19:13:12 +00:00
if ( $linkedIds ) {
foreach ( $linkedIds as $linkId ) {
2023-08-07 08:51:38 +00:00
if ( $linkId [ 'link' ] === $item [ 'owner_xchan' ] || $linkId [ 'link' ] === $item [ 'owner' ][ 'xchan_url' ]) {
logger ( 'source: success - linked identity' );
2023-05-06 19:13:12 +00:00
return true ;
}
}
}
2022-07-20 00:43:27 +00:00
logger ( 'source: no republish permission' );
return false ;
}
if ( $item [ 'item_private' ] && ( ! intval ( $x [ 0 ][ 'abook_feed' ]))) {
logger ( 'source: item is private' );
return false ;
}
if ( $r [ 0 ][ 'src_channel_xchan' ] === $xchan ) {
logger ( 'source: cannot source yourself' );
return false ;
}
if ( ! $r [ 0 ][ 'src_patt' ]) {
logger ( 'source: success' );
2023-05-02 20:52:34 +00:00
return array_shift ( $r );
2022-07-20 00:43:27 +00:00
}
if ( MessageFilter :: evaluate ( $item , $r [ 0 ][ 'src_patt' ], EMPTY_STR )) {
logger ( 'source: text filter success' );
2023-05-02 20:52:34 +00:00
return array_shift ( $r );
2022-07-20 00:43:27 +00:00
}
logger ( 'source: filter fail' );
return false ;
}
// Checks an incoming item against the per-channel and per-connection content filter.
// This implements the backend of the 'Content Filter' system app
function post_is_importable ( $channel_id , $item , $abook ) {
if ( ! $item ) {
return false ;
}
if ( ! Apps :: system_app_installed ( $channel_id , 'Content Filter' )) {
return true ;
}
$text = prepare_text ( $item [ 'body' ],(( isset ( $item [ 'mimetype' ])) ? $item [ 'mimetype' ] : 'text/x-multicode' ));
$text = html2plain (( isset ( $item [ 'title' ]) && $item [ 'title' ]) ? $item [ 'title' ] . ' ' . $text : $text );
2022-07-27 22:16:27 +00:00
2023-02-11 21:22:06 +00:00
$incl = PConfig :: Get ( $channel_id , 'system' , 'message_filter_incl' , EMPTY_STR );
$excl = PConfig :: Get ( $channel_id , 'system' , 'message_filter_excl' , EMPTY_STR );
2022-07-20 00:43:27 +00:00
if ( $incl || $excl ) {
$x = MessageFilter :: evaluate ( $item , $incl , $excl , [ 'plaintext' => $text ]);
if ( ! $x ) {
logger ( 'MessageFilter: channel blocked content' , LOGGER_DEBUG , LOG_INFO );
return false ;
}
}
if ( ! $abook ) {
return true ;
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
foreach ( $abook as $ab ) {
// check eligibility
if ( intval ( $ab [ 'abook_self' ])) {
continue ;
}
if ( ! ( $ab [ 'abook_incl' ] || $ab [ 'abook_excl' ]) ) {
continue ;
}
$evaluator = MessageFilter :: evaluate ( $item , $ab [ 'abook_incl' ], $ab [ 'abook_excl' ], [ 'plaintext' => $text ]);
// A negative assessment for any individual connections is an instant fail.
if ( ! $evaluator ) {
return false ;
}
}
2022-07-27 22:16:27 +00:00
return true ;
2022-07-20 00:43:27 +00:00
}
function has_permissions ( $obj ) {
2023-02-11 21:22:06 +00:00
return ( ! ( empty ( $obj [ 'allow_cid' ]) && empty ( $obj [ 'allow_gid' ])
&& empty ( $obj [ 'deny_cid' ]) && empty ( $obj [ 'deny_gid' ])));
2022-07-20 00:43:27 +00:00
}
function compare_permissions ( $obj1 , $obj2 ) {
// first part is easy. Check that these are exactly the same.
if (( $obj1 [ 'allow_cid' ] == $obj2 [ 'allow_cid' ])
&& ( $obj1 [ 'allow_gid' ] == $obj2 [ 'allow_gid' ])
&& ( $obj1 [ 'deny_cid' ] == $obj2 [ 'deny_cid' ])
&& ( $obj1 [ 'deny_gid' ] == $obj2 [ 'deny_gid' ])) {
return true ;
}
// This is harder. Parse all the permissions and compare the resulting set.
$recipients1 = enumerate_permissions ( $obj1 );
$recipients2 = enumerate_permissions ( $obj2 );
sort ( $recipients1 );
sort ( $recipients2 );
if ( $recipients1 == $recipients2 ) {
return true ;
}
return false ;
}
/**
* @ brief Returns an array of connection identifiers that are allowed to see this object .
*
* @ param object $obj
* @ return array
*/
function enumerate_permissions ( $obj ) {
$allow_people = expand_acl ( $obj [ 'allow_cid' ]);
$allow_groups = AccessList :: expand ( expand_acl ( $obj [ 'allow_gid' ]));
$deny_people = expand_acl ( $obj [ 'deny_cid' ]);
$deny_groups = AccessList :: expand ( expand_acl ( $obj [ 'deny_gid' ]));
$recipients = array_unique ( array_merge ( $allow_people , $allow_groups ));
$deny = array_unique ( array_merge ( $deny_people , $deny_groups ));
$recipients = array_diff ( $recipients , $deny );
return $recipients ;
}
function item_expire ( $uid , $days , $comment_days = 7 ) {
if (( ! $uid ) || ( $days < 1 )) {
return ;
}
if ( ! $comment_days ) {
$comment_days = 7 ;
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
// $expire_stream_only = save your own wall posts
// and just expire conversations started by others
// do not enable this until we can pass bulk delete messages through zot
// $expire_stream_only = get_pconfig($uid,'expire','stream_only');
$expire_stream_only = 1 ;
$sql_extra = (( intval ( $expire_stream_only )) ? " AND item_wall = 0 " : " " );
// We need to set an arbitrary limit on the number of records to be expired to
// prevent memory exhaustion. The site admin can configure it to something else
// if they desire/require.
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$expire_limit = get_config ( 'system' , 'expire_limit' );
if ( ! intval ( $expire_limit )) {
$expire_limit = 5000 ;
}
$item_normal = item_normal ();
$r = q ( " SELECT id FROM item
WHERE uid = % d
AND created < % s - INTERVAL % s
AND commented < % s - INTERVAL % s
AND item_retained = 0
AND item_thread_top = 1
AND resource_type = ''
AND item_starred = 0
$sql_extra $item_normal LIMIT $expire_limit " ,
intval ( $uid ),
db_utcnow (),
db_quoteinterval ( intval ( $days ) . ' DAY' ),
db_utcnow (),
db_quoteinterval ( intval ( $comment_days ) . ' DAY' )
);
if ( ! $r ) {
return ;
}
2022-10-23 00:50:31 +00:00
$r = fetch_post_tags ( $r );
2022-07-20 00:43:27 +00:00
foreach ( $r as $item ) {
// don't expire filed items
$terms = get_terms_oftype ( $item [ 'term' ], TERM_FILE );
if ( $terms ) {
retain_item ( $item [ 'id' ]);
continue ;
}
// don't expire pinned items either
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$pinned = PConfig :: Get ( $item [ 'uid' ], 'pinned' , $item [ 'item_type' ], []);
if ( in_array ( $item [ 'mid' ], $pinned )) {
retain_item ( $item [ 'id' ]);
continue ;
}
2023-04-27 22:26:09 +00:00
drop_item ( $item [ 'id' ], expire : true );
2022-07-20 00:43:27 +00:00
}
}
function retain_item ( $id ) {
$r = q ( " update item set item_retained = 1 where id = %d " ,
intval ( $id )
);
}
// Items is array of item.id
2023-04-27 22:26:09 +00:00
function drop_items ( $items , $stage = DROPITEM_NORMAL , $force = false , $expire = false ) {
2022-07-20 00:43:27 +00:00
$uid = 0 ;
if ( count ( $items )) {
foreach ( $items as $item ) {
2023-04-27 22:26:09 +00:00
$owner = drop_item ( $item , $stage , $force , $expire );
2022-07-20 00:43:27 +00:00
if ( $owner && ( ! $uid )) {
$uid = $owner ;
}
}
}
// multiple threads may have been deleted, send an expire notification
if ( $uid ) {
Run :: Summon ([ 'Notifier' , 'expire' , $uid ]);
}
}
// Delete item with given item $id. $interactive means we're running interactively, and must check
// permissions to carry out this act. If it is non-interactive, we are deleting something at the
// system's request and do not check permission. This is very important to know.
// Some deletion requests (those coming from remote sites) must be staged.
// $stage = 0 => unstaged
// $stage = 1 => set deleted flag on the item and perform intial notifications
// $stage = 2 => perform low level delete at a later stage
2023-04-27 22:26:09 +00:00
function drop_item ( $id , $stage = DROPITEM_NORMAL , $force = false , $uid = 0 , $observer_hash = '' , $expire = false ) {
2022-07-20 00:43:27 +00:00
// locate item to be deleted
$r = q ( " SELECT * FROM item WHERE id = %d " ,
intval ( $id )
);
if (( ! $r ) || ( intval ( $r [ 0 ][ 'item_deleted' ]) && ( $stage === DROPITEM_NORMAL ))) {
return false ;
}
$item = array_shift ( $r );
$ok_to_delete = false ;
// admin deletion
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( is_site_admin ()) {
$ok_to_delete = true ;
}
// owner deletion
if ( local_channel () && local_channel () == $item [ 'uid' ]) {
$ok_to_delete = true ;
}
// remote delete when nobody is authenticated (called from Libzot)
2023-08-02 21:40:41 +00:00
if ( $uid && intval ( $uid ) === intval ( $item [ 'uid' ])) {
2022-07-20 00:43:27 +00:00
$ok_to_delete = true ;
}
// author deletion
2023-03-27 04:56:05 +00:00
if ( $observer_hash ) {
$observer = [ 'xchan_hash' => $observer_hash ];
}
else {
$observer = App :: get_observer ();
}
2022-07-20 00:43:27 +00:00
if ( $observer && $observer [ 'xchan_hash' ] && ( $observer [ 'xchan_hash' ] === $item [ 'author_xchan' ])) {
$ok_to_delete = true ;
}
if ( $observer && $observer [ 'xchan_hash' ] && ( $observer [ 'xchan_hash' ] === $item [ 'owner_xchan' ])) {
$ok_to_delete = true ;
}
if ( $ok_to_delete ) {
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$r = q ( " UPDATE item SET item_deleted = 1 WHERE id = %d " ,
intval ( $item [ 'id' ])
);
if ( $item [ 'resource_type' ] === 'event' ) {
q ( " delete from event where event_hash = '%s' and uid = %d " ,
dbesc ( $item [ 'resource_id' ]),
intval ( $item [ 'uid' ])
);
}
if ( $item [ 'resource_type' ] === 'photo' ) {
attach_delete ( $item [ 'uid' ], $item [ 'resource_id' ], true );
}
$arr = [
'item' => $item ,
'stage' => $stage
];
/**
* @ hooks drop_item
* Called when an 'item' is removed .
*/
Hook :: call ( 'drop_item' , $arr );
2016-09-19 21:42:56 +00:00
2022-07-20 00:43:27 +00:00
$notify_id = intval ( $item [ 'id' ]);
2016-09-19 21:42:56 +00:00
2022-07-20 00:43:27 +00:00
$items = q ( " select * from item where parent = %d and uid = %d " ,
intval ( $item [ 'id' ]),
intval ( $item [ 'uid' ])
);
if ( $items ) {
foreach ( $items as $i ) {
2023-04-28 05:35:28 +00:00
delete_item_lowlevel ( $i , $stage , $force , $expire );
2022-07-20 00:43:27 +00:00
}
}
else {
2023-04-28 05:35:28 +00:00
delete_item_lowlevel ( $item , $stage , $force , $expire );
2022-07-20 00:43:27 +00:00
}
2015-11-03 22:59:14 +00:00
2022-07-20 00:43:27 +00:00
return true ;
}
else {
return false ;
}
2013-02-07 04:29:17 +00:00
}
2015-03-21 23:06:08 +00:00
/**
2022-07-20 00:43:27 +00:00
* @ warning This function does not check for permission and does not send
* notifications and does not check recursion .
* It merely destroys all resources associated with an item .
* Please do not use without a suitable wrapper .
2015-03-21 23:06:08 +00:00
*
2022-07-20 00:43:27 +00:00
* @ param array $item
* @ param int $stage
2023-02-11 21:22:06 +00:00
* @ param bool $force
* @ return bool
2015-03-21 23:06:08 +00:00
*/
2023-04-28 05:35:28 +00:00
function delete_item_lowlevel ( $item , $stage = DROPITEM_NORMAL , $force = false , $expire = false ) {
2011-11-16 04:30:34 +00:00
2018-04-03 02:32:22 +00:00
2022-07-20 00:43:27 +00:00
logger ( 'item: ' . $item [ 'id' ] . ' stage: ' . $stage . ' force: ' . ( $force ) ? 'true' : 'false' , LOGGER_DATA );
2023-04-28 05:35:28 +00:00
if ( ! $expire ) {
Tombstone :: store ( $item [ 'mid' ], $item [ 'uid' ]);
}
2023-02-11 21:22:06 +00:00
match ( $stage ) {
DROPITEM_PHASE2 => q ( " UPDATE item SET item_pending_remove = 1, body = '', title = '',
2022-07-20 00:43:27 +00:00
changed = '%s' , edited = '%s' WHERE id = % d " ,
2023-02-11 21:22:06 +00:00
dbesc ( datetime_convert ()),
dbesc ( datetime_convert ()),
intval ( $item [ 'id' ])
),
DROPITEM_PHASE1 => q ( " UPDATE item set item_deleted = 1, changed = '%s', edited = '%s' where id = %d " ,
dbesc ( datetime_convert ()),
dbesc ( datetime_convert ()),
intval ( $item [ 'id' ])
),
default => q ( " DELETE FROM item WHERE id = %d " ,
intval ( $item [ 'id' ])
),
};
2012-09-21 00:04:22 +00:00
2022-07-20 00:43:27 +00:00
// immediately remove any undesired profile likes.
2012-09-21 00:04:22 +00:00
2022-07-20 00:43:27 +00:00
q ( " delete from likes where iid = %d and channel_id = %d " ,
intval ( $item [ 'id' ]),
intval ( $item [ 'uid' ])
);
2020-04-08 02:01:14 +00:00
2022-07-20 00:43:27 +00:00
// network deletion request. Keep the message structure so that we can deliver delete notifications.
// Come back after several days to do the lowlevel delete (DROPITEM_PHASE2).
if ( $stage == DROPITEM_PHASE1 ) {
return true ;
2022-06-23 10:47:35 +00:00
}
2012-09-21 00:04:22 +00:00
2022-07-20 00:43:27 +00:00
$r = q ( " delete from term where otype = %d and oid = %d " ,
intval ( TERM_OBJ_POST ),
intval ( $item [ 'id' ])
);
2020-02-04 23:44:26 +00:00
2022-07-20 00:43:27 +00:00
q ( " delete from iconfig where iid = %d " ,
intval ( $item [ 'id' ])
);
2020-02-04 23:44:26 +00:00
2022-11-06 21:56:10 +00:00
$n = q ( " select id from item where mid = '%s' " ,
dbesc ( $item [ 'mid' ])
);
if ( ! $n ) {
ObjCache :: Delete ( $item [ 'mid' ]);
}
2022-07-20 00:43:27 +00:00
q ( " delete from term where oid = %d and otype = %d " ,
intval ( $item [ 'id' ]),
intval ( TERM_OBJ_POST )
);
// remove delivery reports
$c = q ( " select channel_hash from channel where channel_id = %d limit 1 " ,
intval ( $item [ 'uid' ])
);
if ( $c ) {
q ( " delete from dreport where dreport_xchan = '%s' and dreport_mid = '%s' " ,
dbesc ( $c [ 0 ][ 'channel_hash' ]),
dbesc ( $item [ 'mid' ])
);
}
// The threadlistener interface does not support uid/channel_id, so
// only remove threadlisteners when doing the second stage of a federated delete.
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( $stage === DROPITEM_PHASE2 ) {
ThreadListener :: delete_by_target ( $item [ 'mid' ]);
2022-06-23 10:47:35 +00:00
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
q ( " delete from notify where uid = %d and link = '%s' " ,
intval ( $item [ 'uid' ]),
dbesc ( $item [ 'mid' ])
);
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
return true ;
2020-02-04 23:44:26 +00:00
}
2022-07-20 00:43:27 +00:00
/**
* @ brief Return the first post date .
*
* @ param int $uid
* @ param boolean $wall ( optional ) no longer used
* @ return string | boolean date string , otherwise false
*/
function first_post_date ( $uid , $wall = false ) {
$sql_extra = '' ;
switch ( App :: $module ) {
case 'articles' :
$sql_extra .= " and item_type = 7 " ;
$item_normal = " and item.item_hidden = 0 and item.item_type = 7 and item.item_deleted = 0
and item . item_unpublished = 0 and item . item_delayed = 0 and item . item_pending_remove = 0
and item . item_blocked = 0 " ;
break ;
case 'channel' :
$sql_extra = " and item_wall = 1 " ;
default :
$item_normal = item_normal ();
break ;
}
$r = q ( " select id, created from item
where uid = % d and id = parent $item_normal $sql_extra
order by created asc limit 1 " ,
intval ( $uid )
);
if ( $r ) {
return datetime_convert ( 'UTC' , date_default_timezone_get (), $r [ 0 ][ 'created' ], 'Y-m-d' );
}
2020-02-04 23:44:26 +00:00
2022-07-20 00:43:27 +00:00
return false ;
}
2020-02-04 23:44:26 +00:00
2014-08-21 06:01:25 +00:00
/**
2022-07-20 00:43:27 +00:00
* modified posted_dates () { below } to arrange the list in years , which we ' ll eventually
* use to make a menu of years with collapsible sub - menus for the months instead of the
* current flat list of all representative dates .
2015-03-21 23:06:08 +00:00
*
2022-07-20 00:43:27 +00:00
* @ param int $uid
* @ param boolean $wall
* @ param string $mindate
* @ return array
2014-08-21 06:01:25 +00:00
*/
2022-07-20 00:43:27 +00:00
function list_post_dates ( $uid , $wall , $mindate ) {
2014-08-21 06:01:25 +00:00
2022-07-20 00:43:27 +00:00
$ret = [];
2022-06-14 22:36:43 +00:00
2022-07-20 00:43:27 +00:00
$dnow = datetime_convert ( '' , date_default_timezone_get (), 'now' , 'Y-m-d' );
2022-06-14 22:36:43 +00:00
2022-07-20 00:43:27 +00:00
if ( $mindate ) {
$dthen = datetime_convert ( '' , date_default_timezone_get (), $mindate );
}
else {
$dthen = first_post_date ( $uid , $wall );
}
if ( ! $dthen ) {
return [];
}
2022-06-14 22:36:43 +00:00
2022-07-20 00:43:27 +00:00
// If it's near the end of a long month, backup to the 28th so that in
// consecutive loops we'll always get a whole month difference.
2022-02-16 02:18:00 +00:00
2022-07-20 00:43:27 +00:00
if ( intval ( substr ( $dnow , 8 )) > 28 ) {
$dnow = substr ( $dnow , 0 , 8 ) . '28' ;
}
if ( intval ( substr ( $dthen , 8 )) > 28 ) {
$dthen = substr ( $dthen , 0 , 8 ) . '28' ;
}
// Starting with the current month, get the first and last days of every
// month down to and including the month of the first post
while ( substr ( $dnow , 0 , 7 ) >= substr ( $dthen , 0 , 7 )) {
$dyear = intval ( substr ( $dnow , 0 , 4 ));
$dstart = substr ( $dnow , 0 , 8 ) . '01' ;
$dend = substr ( $dnow , 0 , 8 ) . get_dim ( intval ( $dnow ), intval ( substr ( $dnow , 5 )));
$start_month = datetime_convert ( '' , '' , $dstart , 'Y-m-d' );
$end_month = datetime_convert ( '' , '' , $dend , 'Y-m-d' );
$str = day_translate ( datetime_convert ( '' , '' , $dnow , 'F' ));
if ( ! $ret [ $dyear ]) {
$ret [ $dyear ] = [];
2022-02-16 02:18:00 +00:00
}
2022-07-20 00:43:27 +00:00
$ret [ $dyear ][] = [ $str , $end_month , $start_month ];
$dnow = datetime_convert ( '' , '' , $dnow . ' -1 month' , 'Y-m-d' );
}
2021-01-08 21:53:28 +00:00
2022-07-20 00:43:27 +00:00
return $ret ;
}
function posted_dates ( $uid , $wall ) {
2022-06-14 22:36:43 +00:00
2022-07-20 00:43:27 +00:00
$dnow = datetime_convert ( 'UTC' , date_default_timezone_get (), 'now' , 'Y-m-d' );
$dthen = first_post_date ( $uid , $wall );
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( ! $dthen ) {
return [];
}
// If it's near the end of a long month, backup to the 28th so that in
// consecutive loops we'll always get a whole month difference.
if ( intval ( substr ( $dnow , 8 )) > 28 ) {
$dnow = substr ( $dnow , 0 , 8 ) . '28' ;
}
if ( intval ( substr ( $dthen , 8 )) > 28 ) {
$dthen = substr ( $dthen , 0 , 8 ) . '28' ;
}
2020-07-06 06:18:31 +00:00
2022-07-20 00:43:27 +00:00
$ret = [];
2021-06-19 05:12:23 +00:00
2022-07-20 00:43:27 +00:00
// Starting with the current month, get the first and last days of every
// month down to and including the month of the first post
2019-10-11 23:45:04 +00:00
2022-07-20 00:43:27 +00:00
while ( substr ( $dnow , 0 , 7 ) >= substr ( $dthen , 0 , 7 )) {
$dstart = substr ( $dnow , 0 , 8 ) . '01' ;
$dend = substr ( $dnow , 0 , 8 ) . get_dim ( intval ( $dnow ), intval ( substr ( $dnow , 5 )));
$start_month = datetime_convert ( '' , '' , $dstart , 'Y-m-d' );
$end_month = datetime_convert ( '' , '' , $dend , 'Y-m-d' );
$str = day_translate ( datetime_convert ( '' , '' , $dnow , 'F Y' ));
2022-09-17 00:25:44 +00:00
$ret [] = [ $str , $end_month , $start_month ];
2022-07-20 00:43:27 +00:00
$dnow = datetime_convert ( '' , '' , $dnow . ' -1 month' , 'Y-m-d' );
}
return $ret ;
}
2020-07-06 06:18:31 +00:00
2022-07-20 00:43:27 +00:00
/**
* @ brief Extend an item array with the associated tags of the posts .
*
* @ param array $items
* @ return array Return the provided $items array after extended the posts with tags
*/
2022-10-23 00:50:31 +00:00
function fetch_post_tags ( $items ) {
2022-07-20 00:43:27 +00:00
$tag_finder = [];
if ( $items ) {
foreach ( $items as $item ) {
if ( is_array ( $item )) {
if ( array_key_exists ( 'item_id' , $item )) {
if ( ! in_array ( $item [ 'item_id' ], $tag_finder )) {
$tag_finder [] = $item [ 'item_id' ];
}
}
else {
if ( ! in_array ( $item [ 'id' ], $tag_finder )) {
$tag_finder [] = $item [ 'id' ];
}
}
}
}
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$tag_finder_str = implode ( ', ' , $tag_finder );
if ( strlen ( $tag_finder_str )) {
$tags = q ( " select * from term where oid in ( %s ) and otype = %d " ,
dbesc ( $tag_finder_str ),
intval ( TERM_OBJ_POST )
);
$imeta = q ( " select * from iconfig where iid in ( %s ) " ,
dbesc ( $tag_finder_str )
);
}
2023-05-25 21:30:31 +00:00
for ( $x = 0 ; $x < count (( array ) $items ); $x ++ ) {
2022-07-20 00:43:27 +00:00
if ( $tags ) {
foreach ( $tags as $t ) {
if ( array_key_exists ( 'item_id' , $items [ $x ])) {
if ( $t [ 'oid' ] == $items [ $x ][ 'item_id' ]) {
if ( ! ( isset ( $items [ $x ][ 'term' ]) && is_array ( $items [ $x ][ 'term' ]))) {
$items [ $x ][ 'term' ] = [];
}
$items [ $x ][ 'term' ][] = $t ;
}
}
else {
if ( $t [ 'oid' ] == $items [ $x ][ 'id' ]) {
if ( ! ( isset ( $items [ $x ][ 'term' ]) && is_array ( $items [ $x ][ 'term' ]))) {
$items [ $x ][ 'term' ] = [];
}
$items [ $x ][ 'term' ][] = $t ;
}
}
}
}
if ( $imeta ) {
foreach ( $imeta as $i ) {
if ( array_key_exists ( 'item_id' , $items [ $x ])) {
if ( $i [ 'iid' ] == $items [ $x ][ 'item_id' ]) {
if ( ! ( isset ( $items [ $x ][ 'iconfig' ]) && is_array ( $items [ $x ][ 'iconfig' ]))) {
$items [ $x ][ 'iconfig' ] = [];
}
$i [ 'v' ] = unserialise ( $i [ 'v' ]);
$items [ $x ][ 'iconfig' ][] = $i ;
}
}
else {
if ( $i [ 'iid' ] == $items [ $x ][ 'id' ]) {
if ( ! ( isset ( $items [ $x ][ 'iconfig' ]) && is_array ( $items [ $x ][ 'iconfig' ]))) {
$items [ $x ][ 'iconfig' ] = [];
}
$i [ 'v' ] = unserialise ( $i [ 'v' ]);
$items [ $x ][ 'iconfig' ][] = $i ;
}
}
}
}
}
return $items ;
2014-08-21 06:01:25 +00:00
}
2022-07-20 00:43:27 +00:00
2013-09-27 02:34:45 +00:00
/**
2015-03-21 23:06:08 +00:00
* @ brief
2013-09-27 02:34:45 +00:00
*
2015-03-21 23:06:08 +00:00
* @ param int $uid
2022-07-20 00:43:27 +00:00
* @ param string $observer_hash
* @ param array $arr
* @ return array
2013-09-27 02:34:45 +00:00
*/
2022-07-20 00:43:27 +00:00
function zot_feed ( $uid , $observer_hash , $arr ) {
2018-05-05 08:43:14 +00:00
2022-07-20 00:43:27 +00:00
$result = [];
$mindate = null ;
$message_id = null ;
$wall = true ;
2015-06-29 04:16:56 +00:00
2022-07-20 00:43:27 +00:00
require_once ( 'include/security.php' );
2021-03-14 08:35:03 +00:00
2022-07-20 00:43:27 +00:00
$encoding = (( array_key_exists ( 'encoding' , $arr )) ? $arr [ 'encoding' ] : 'zot' );
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
if ( array_key_exists ( 'mindate' , $arr )) {
$mindate = datetime_convert ( 'UTC' , 'UTC' , $arr [ 'mindate' ]);
}
2015-06-29 04:16:56 +00:00
2022-07-20 00:43:27 +00:00
if ( array_key_exists ( 'message_id' , $arr )) {
$message_id = $arr [ 'message_id' ];
}
2015-06-30 03:35:56 +00:00
2022-07-20 00:43:27 +00:00
if ( array_key_exists ( 'wall' , $arr )) {
$wall = intval ( $arr [ 'wall' ]);
}
2019-01-23 03:11:53 +00:00
2022-07-20 00:43:27 +00:00
if ( ! $mindate )
$mindate = NULL_DATE ;
2011-05-01 00:24:37 +00:00
2022-07-20 00:43:27 +00:00
$mindate = dbesc ( $mindate );
2011-05-01 00:24:37 +00:00
2022-07-20 00:43:27 +00:00
logger ( 'zot_feed: requested for uid ' . $uid . ' from observer ' . $observer_hash , LOGGER_DEBUG );
if ( $message_id )
logger ( 'message_id: ' . $message_id , LOGGER_DEBUG );
2012-05-28 04:01:58 +00:00
2022-11-20 05:28:50 +00:00
if ( ! perm_is_allowed ( $uid , $observer_hash , 'view_stream' )) {
2022-07-20 00:43:27 +00:00
logger ( 'zot_feed: permission denied.' );
return $result ;
}
if ( ! Channel :: is_system ( $uid ))
$sql_extra = item_permissions_sql ( $uid , $observer_hash );
$limit = " LIMIT 5000 " ;
if ( $mindate > NULL_DATE ) {
$sql_extra .= " and ( created > ' $mindate ' or changed > ' $mindate ' ) " ;
}
if ( $message_id ) {
$sql_extra .= " and mid = ' " . dbesc ( $message_id ) . " ' " ;
$limit = '' ;
}
if ( $wall ) {
$sql_extra .= " and item_wall = 1 " ;
}
$items = [];
$item_normal = item_normal ();
if ( Channel :: is_system ( $uid )) {
$nonsys_uids = q ( " SELECT channel_id FROM channel WHERE channel_system = 0 " );
$nonsys_uids_str = ids_to_querystr ( $nonsys_uids , 'channel_id' );
$r = q ( " SELECT parent FROM item
WHERE uid IN ( % s )
AND item_private = 0
$item_normal
$sql_extra ORDER BY created ASC $limit " ,
intval ( $nonsys_uids_str )
);
}
else {
$r = q ( " SELECT parent FROM item
WHERE uid = % d
$item_normal
$sql_extra ORDER BY created ASC $limit " ,
intval ( $uid )
);
}
$parents = [];
if ( $r ) {
foreach ( $r as $rv ) {
if ( array_key_exists ( $rv [ 'parent' ], $parents ))
continue ;
$parents [ $rv [ 'parent' ]] = $rv ;
if ( count ( $parents ) > 200 )
break ;
}
$parents_str = ids_to_querystr ( $parents , 'parent' );
$sys_query = (( Channel :: is_system ( $uid )) ? $sql_extra : '' );
$item_normal = item_normal ();
$items = q ( " SELECT item.*, item.id AS item_id FROM item
WHERE item . parent IN ( % s ) $item_normal $sys_query " ,
dbesc ( $parents_str )
);
}
if ( $items ) {
xchan_query ( $items );
$items = fetch_post_tags ( $items );
require_once ( 'include/conversation.php' );
$items = conv_sort ( $items , 'ascending' );
}
else
$items = [];
logger ( 'zot_feed: number items: ' . count ( $items ), LOGGER_DEBUG );
foreach ( $items as $item ) {
if ( $encoding === 'zot' )
$result [] = encode_item ( $item );
2022-08-14 09:20:43 +00:00
elseif ( $encoding === 'activitystreams' )
2023-12-19 10:08:08 +00:00
$result [] = Activity :: encode_activity ( $item , true );
2022-07-20 00:43:27 +00:00
}
return $result ;
2012-05-28 04:01:58 +00:00
}
2015-03-21 23:06:08 +00:00
2022-07-20 00:43:27 +00:00
function items_fetch ( $arr , $channel = null , $observer_hash = null , $client_mode = CLIENT_MODE_NORMAL , $module = 'stream' ) {
$result = [ 'success' => false ];
$uid = 0 ;
$sql_extra = '' ;
$sql_nets = '' ;
$sql_options = '' ;
$sql_extra2 = '' ;
$sql_extra3 = '' ;
$def_acl = '' ;
2011-05-01 00:24:37 +00:00
2022-07-20 00:43:27 +00:00
$item_uids = ' true ' ;
$item_normal = item_normal ();
2012-07-11 11:29:47 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'uid' ]) && $arr [ 'uid' ]) {
$uid = $arr [ 'uid' ];
}
2011-04-06 00:41:02 +00:00
2022-07-20 00:43:27 +00:00
if ( $channel ) {
$uid = $channel [ 'channel_id' ];
$uidhash = $channel [ 'channel_hash' ];
$item_uids = " item.uid = " . intval ( $uid ) . " " ;
}
2021-03-14 08:35:03 +00:00
2022-07-20 00:43:27 +00:00
if ( ! ( isset ( $arr [ 'include_follow' ]) && intval ( $arr [ 'include_follow' ]))) {
$item_normal .= " and not verb in ( 'Follow' , 'Ignore' ) " ;
}
2021-03-14 08:35:03 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'star' ]) && $arr [ 'star' ]) {
$sql_options .= " and item_starred = 1 " ;
}
2021-03-14 08:35:03 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'wall' ]) && $arr [ 'wall' ]) {
$sql_options .= " and item_wall = 1 " ;
}
2011-04-07 02:41:16 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'item_id' ]) && $arr [ 'item_id' ]) {
$sql_options .= " and parent = " . intval ( $arr [ 'item_id' ]) . " " ;
}
2011-04-06 00:41:02 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'mid' ]) && $arr [ 'mid' ]) {
$sql_options .= " and parent_mid = ' " . dbesc ( $arr [ 'mid' ]) . " ' " ;
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$sql_extra = " AND item.parent IN ( SELECT parent FROM item WHERE item_thread_top = 1 $sql_options $item_normal ) " ;
2011-03-16 00:31:49 +00:00
2022-08-14 09:20:43 +00:00
if ( ! empty ( $arr [ 'since_id' ])) {
$sql_extra .= " and item.id > " . $arr [ 'since_id' ] . " " ;
2022-07-20 00:43:27 +00:00
}
2022-07-27 22:16:27 +00:00
2022-08-14 09:20:43 +00:00
if ( ! empty ( $arr [ 'cat' ])) {
2022-07-20 00:43:27 +00:00
$sql_extra .= protect_sprintf ( term_query ( 'item' , $arr [ 'cat' ], TERM_CATEGORY ));
}
2022-07-27 22:16:27 +00:00
2022-08-14 09:20:43 +00:00
if ( $uid && ! empty ( $arr [ 'gid' ])) {
2022-07-20 00:43:27 +00:00
$r = q ( " SELECT * FROM pgrp WHERE id = %d AND uid = %d LIMIT 1 " ,
intval ( $arr [ 'group' ]),
intval ( $uid )
);
if ( ! $r ) {
$result [ 'message' ] = t ( 'Access list not found.' );
return $result ;
}
2011-03-16 00:31:49 +00:00
2022-07-20 00:43:27 +00:00
$contact_str = '' ;
2011-06-11 03:39:46 +00:00
2022-07-20 00:43:27 +00:00
$contacts = AccessList :: members ( $uid , $r [ 0 ][ 'id' ]);
if ( $contacts ) {
foreach ( $contacts as $c ) {
if ( $contact_str ) {
$contact_str .= ',' ;
}
$contact_str .= " ' " . $c [ 'xchan' ] . " ' " ;
}
}
else {
$contact_str = ' 0 ' ;
$result [ 'message' ] = t ( 'Privacy group is empty.' );
return $result ;
}
2011-06-16 03:43:39 +00:00
2022-07-20 00:43:27 +00:00
$sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND (( author_xchan IN ( $contact_str ) OR owner_xchan in ( $contact_str )) or allow_gid like ' " . protect_sprintf ( '%<' . dbesc ( $r [ 0 ][ 'hash' ]) . '>%' ) . " ' ) and id = parent $item_normal ) " ;
2019-07-11 01:43:20 +00:00
2022-07-20 00:43:27 +00:00
$x = AccessList :: rec_byhash ( $uid , $r [ 0 ][ 'hash' ]);
$result [ 'headline' ] = sprintf ( t ( 'Access list: %s' ), $x [ 'gname' ]);
}
elseif ( isset ( $arr [ 'cid' ]) && $arr [ 'cid' ] && $uid ) {
2021-03-14 08:35:03 +00:00
2022-07-20 00:43:27 +00:00
$r = q ( " SELECT abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d and abook_blocked = 0 limit 1 " ,
intval ( $arr [ 'cid' ]),
intval ( local_channel ())
);
if ( $r ) {
$sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND uid = " . intval ( $arr [ 'uid' ]) . " AND ( author_xchan = ' " . dbesc ( $r [ 0 ][ 'abook_xchan' ]) . " ' or owner_xchan = ' " . dbesc ( $r [ 0 ][ 'abook_xchan' ]) . " ' ) $item_normal ) " ;
$result [ 'headline' ] = sprintf ( t ( 'Connection: %s' ), $r [ 0 ][ 'xchan_name' ]);
}
else {
$result [ 'message' ] = t ( 'Channel not found.' );
return $result ;
}
}
2011-06-16 03:43:39 +00:00
2022-07-20 00:43:27 +00:00
if ( $channel && intval ( $arr [ 'compat' ]) === 1 ) {
$sql_extra = " AND author_xchan = ' " . $channel [ 'channel_hash' ] . " ' and item_private = 0 $item_normal " ;
}
2012-01-25 02:59:55 +00:00
2022-07-20 00:43:27 +00:00
if ( $arr [ 'datequery' ]) {
$sql_extra3 .= protect_sprintf ( sprintf ( " AND item.created <= '%s' " , dbesc ( datetime_convert ( 'UTC' , 'UTC' , $arr [ 'datequery' ]))));
}
if ( $arr [ 'datequery2' ]) {
$sql_extra3 .= protect_sprintf ( sprintf ( " AND item.created >= '%s' " , dbesc ( datetime_convert ( 'UTC' , 'UTC' , $arr [ 'datequery2' ]))));
}
2011-06-16 03:43:39 +00:00
2022-07-20 00:43:27 +00:00
if ( $arr [ 'search' ]) {
2022-09-17 00:25:44 +00:00
if ( str_starts_with ( $arr [ 'search' ], '#' )) {
2022-07-20 00:43:27 +00:00
$sql_extra .= term_query ( 'item' , substr ( $arr [ 'search' ], 1 ), TERM_HASHTAG , TERM_COMMUNITYTAG );
2011-06-16 03:43:39 +00:00
2022-07-20 00:43:27 +00:00
}
else {
$sql_extra .= sprintf ( " AND item.body like '%s' " ,
dbesc ( protect_sprintf ( '%' . $arr [ 'search' ] . '%' ))
);
}
}
2011-06-16 03:43:39 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'file' ]) && strlen ( $arr [ 'file' ])) {
$sql_extra .= term_query ( 'item' , $arr [ 'file' ], TERM_FILE );
}
2011-06-16 03:43:39 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'conv' ]) && $arr [ 'conv' ] && $channel ) {
$sql_extra .= sprintf ( " AND parent IN (SELECT distinct parent from item where ( author_xchan like '%s' or item_mentionsme = 1 )) " ,
dbesc ( protect_sprintf ( $uidhash ))
);
}
2013-01-25 00:44:10 +00:00
2022-07-20 00:43:27 +00:00
if (( $client_mode & CLIENT_MODE_UPDATE ) && ( ! ( $client_mode & CLIENT_MODE_LOAD ))) {
// only setup pagination on initial page view
$pager_sql = '' ;
}
else {
2022-08-05 21:12:53 +00:00
if ( ! ( isset ( $arr [ 'total' ]) && $arr [ 'total' ]) ) {
$itemspage = (( isset ( $arr [ 'records' ])) ? $arr [ 'records' ] : 0 );
if ( ! $itemspage ) {
$itemspage = (( $channel ) ? get_pconfig ( $uid , 'system' , 'itemspage' ) : 20 );
App :: set_pager_itemspage ((( intval ( $itemspage )) ? $itemspage : 20 ));
$pager_sql = sprintf ( " LIMIT %d OFFSET %d " , intval ( App :: $pager [ 'itemspage' ]), intval ( App :: $pager [ 'start' ]));
}
2022-07-20 00:43:27 +00:00
}
}
2014-03-28 03:28:48 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'start' ]) && isset ( $arr [ 'records' ])) {
$pager_sql = sprintf ( " LIMIT %d OFFSET %d " , intval ( $arr [ 'records' ]), intval ( $arr [ 'start' ]));
}
2011-06-16 03:43:39 +00:00
2022-07-20 00:43:27 +00:00
if ( array_key_exists ( 'cmin' , $arr ) || array_key_exists ( 'cmax' , $arr )) {
if (( $arr [ 'cmin' ] != 0 ) || ( $arr [ 'cmax' ] != 99 )) {
2011-06-16 03:43:39 +00:00
2022-07-20 00:43:27 +00:00
// Not everybody who shows up in the stream will be in your address book.
2023-02-11 21:22:06 +00:00
// By default, those that aren't are assumed to have closeness = 99; but this isn't
2022-07-20 00:43:27 +00:00
// recorded anywhere. So if cmax is 99, we'll open the search up to anybody in
// the stream with a NULL address book entry.
2011-06-16 03:43:39 +00:00
2022-07-20 00:43:27 +00:00
$sql_nets .= " AND " ;
2011-06-16 03:43:39 +00:00
2022-07-20 00:43:27 +00:00
if ( $arr [ 'cmax' ] == 99 )
$sql_nets .= " ( " ;
2012-05-05 14:05:32 +00:00
2022-07-20 00:43:27 +00:00
$sql_nets .= " ( abook.abook_closeness >= " . intval ( $arr [ 'cmin' ]) . " " ;
$sql_nets .= " AND abook.abook_closeness <= " . intval ( $arr [ 'cmax' ]) . " ) " ;
if ( $arr [ 'cmax' ] == 99 )
$sql_nets .= " OR abook.abook_closeness IS NULL ) " ;
}
2022-06-26 08:50:02 +00:00
}
2012-06-13 03:46:30 +00:00
2022-07-20 00:43:27 +00:00
$simple_update = (( $client_mode & CLIENT_MODE_UPDATE ) ? " and item.item_unseen = 1 " : '' );
if ( $client_mode & CLIENT_MODE_LOAD ) {
$simple_update = '' ;
}
2013-01-25 00:44:10 +00:00
2022-07-20 00:43:27 +00:00
$sql_extra .= item_permissions_sql ( $channel [ 'channel_id' ], $observer_hash );
2014-03-28 03:28:48 +00:00
2013-02-18 23:15:55 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'pages' ]) && $arr [ 'pages' ]) {
$item_restrict = " AND item_type = " . ITEM_TYPE_WEBPAGE . " " ;
}
else {
$item_restrict = " AND item_type = 0 " ;
2022-07-11 22:10:52 +00:00
}
2013-01-25 00:44:10 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'item_type' ]) && $arr [ 'item_type' ] === '*' ) {
$item_restrict = '' ;
}
2012-10-08 01:44:06 +00:00
2022-07-20 00:43:27 +00:00
if ((( isset ( $arr [ 'compat' ]) && $arr [ 'compat' ]) || (( isset ( $arr [ 'nouveau' ]) && $arr [ 'nouveau' ]) && ( $client_mode & CLIENT_MODE_LOAD ))) && $channel ) {
2012-06-13 03:46:30 +00:00
2022-07-20 00:43:27 +00:00
// "New Item View" - show all items unthreaded in reverse created date order
2021-03-14 08:35:03 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'total' ]) && $arr [ 'total' ]) {
$items = q ( " SELECT count(item.id) AS total FROM item
WHERE $item_uids $item_restrict
$simple_update
$sql_extra $sql_nets $sql_extra3 "
);
if ( $items ) {
return intval ( $items [ 0 ][ 'total' ]);
}
return 0 ;
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$items = q ( " SELECT item.*, item.id AS item_id FROM item
WHERE $item_uids $item_restrict
$simple_update
$sql_extra $sql_nets $sql_extra3
ORDER BY item . received DESC $pager_sql "
);
2014-05-01 01:45:46 +00:00
2022-07-20 00:43:27 +00:00
xchan_query ( $items );
2022-10-23 00:50:31 +00:00
$items = fetch_post_tags ( $items );
2021-03-14 08:35:03 +00:00
2022-07-20 00:43:27 +00:00
}
else {
2012-06-13 03:46:30 +00:00
2022-07-20 00:43:27 +00:00
// Normal conversation view
2012-07-11 02:28:02 +00:00
2022-07-20 00:43:27 +00:00
if ( isset ( $arr [ 'order' ]) && $arr [ 'order' ] === 'post' ) {
$ordering = " created " ;
}
else {
$ordering = " commented " ;
}
2013-01-04 03:34:04 +00:00
2022-07-20 00:43:27 +00:00
if (( $client_mode & CLIENT_MODE_LOAD ) || ( $client_mode == CLIENT_MODE_NORMAL )) {
2013-01-04 03:34:04 +00:00
2022-07-20 00:43:27 +00:00
// Fetch a page full of parent items for this page
2013-01-04 03:34:04 +00:00
2022-07-20 00:43:27 +00:00
$r = q ( " SELECT distinct item.id AS item_id, item. $ordering FROM item
left join abook on item . author_xchan = abook . abook_xchan
WHERE $item_uids $item_restrict
AND item . parent = item . id
and ( abook . abook_blocked = 0 or abook . abook_flags is null )
$sql_extra3 $sql_extra $sql_nets
ORDER BY item . $ordering DESC $pager_sql "
);
}
else {
// update
$r = q ( " SELECT item.parent AS item_id FROM item
left join abook on item . author_xchan = abook . abook_xchan
WHERE $item_uids $item_restrict $simple_update
and ( abook . abook_blocked = 0 or abook . abook_flags is null )
$sql_extra3 $sql_extra $sql_nets "
);
}
2013-05-23 04:54:02 +00:00
2022-07-20 00:43:27 +00:00
// Then fetch all the children of the parents that are on this page
2013-05-23 04:54:02 +00:00
2023-04-27 05:37:54 +00:00
if ( $r ) {
2013-05-23 04:54:02 +00:00
2022-07-20 00:43:27 +00:00
$parents_str = ids_to_querystr ( $r , 'item_id' );
if ( isset ( $arr [ 'top' ]) && $arr [ 'top' ]) {
$sql_extra = ' and id = parent ' . $sql_extra ;
}
$items = q ( " SELECT item.*, item.id AS item_id FROM item
WHERE $item_uids $item_restrict
AND item . parent IN ( % s )
$sql_extra " ,
2023-04-27 05:37:54 +00:00
dbesc ( $parents_str ? : '0' )
2022-07-20 00:43:27 +00:00
);
xchan_query ( $items );
2022-10-23 00:50:31 +00:00
$items = fetch_post_tags ( $items );
2022-07-20 00:43:27 +00:00
require_once ( 'include/conversation.php' );
$items = conv_sort ( $items , $ordering );
}
else {
$items = [];
}
}
return $items ;
2013-09-25 19:43:21 +00:00
}
2014-01-04 21:44:43 +00:00
2016-06-14 02:58:24 +00:00
function webpage_to_namespace ( $webpage ) {
2022-07-20 00:43:27 +00:00
if ( $webpage == ITEM_TYPE_WEBPAGE )
$page_type = 'WEBPAGE' ;
elseif ( $webpage == ITEM_TYPE_BLOCK )
$page_type = 'BUILDBLOCK' ;
elseif ( $webpage == ITEM_TYPE_PDL )
$page_type = 'PDL' ;
elseif ( $webpage == ITEM_TYPE_CARD )
$page_type = 'CARD' ;
elseif ( $webpage == ITEM_TYPE_ARTICLE )
$page_type = 'ARTICLE' ;
elseif ( $webpage == ITEM_TYPE_DOC )
$page_type = 'docfile' ;
else
$page_type = 'unknown' ;
return $page_type ;
2016-06-14 02:58:24 +00:00
}
2014-01-04 21:44:43 +00:00
function update_remote_id ( $channel , $post_id , $webpage , $pagetitle , $namespace , $remote_id , $mid ) {
2022-07-20 00:43:27 +00:00
if ( ! $post_id ) {
return ;
}
$page_type = webpage_to_namespace ( $webpage );
if ( $page_type == 'unknown' && $namespace && $remote_id ) {
$page_type = $namespace ;
$pagetitle = $remote_id ;
}
else {
$page_type = '' ;
}
if ( $page_type ) {
// store page info as an alternate message_id so we can access it via
// https://sitename/page/$channelname/$pagetitle
// if no pagetitle was given or it couldn't be transliterated into a url, use the first
// sixteen bytes of the mid - which makes the link portable and not quite as daunting
// as the entire mid. If it were the post_id the link would be less portable.
2022-08-14 09:20:43 +00:00
$post_id = intval ( $post_id );
2022-07-20 00:43:27 +00:00
IConfig :: Set (
2022-08-14 09:20:43 +00:00
$post_id ,
2022-07-20 00:43:27 +00:00
'system' ,
$page_type ,
2023-02-11 21:22:06 +00:00
( $pagetitle ) ? : substr ( $mid , 0 , 16 ),
2022-07-20 00:43:27 +00:00
false
);
}
2014-01-04 21:44:43 +00:00
}
2014-06-18 02:21:46 +00:00
/**
2015-03-21 23:06:08 +00:00
* @ brief Change access control for item with message_id $mid and channel_id $uid .
*
* @ param string $xchan_hash
* @ param string $mid
* @ param int $uid
2014-06-18 02:21:46 +00:00
*/
2015-03-21 23:06:08 +00:00
function item_add_cid ( $xchan_hash , $mid , $uid ) {
2022-07-20 00:43:27 +00:00
$r = q ( " select id from item where mid = '%s' and uid = %d and allow_cid like '%s' " ,
dbesc ( $mid ),
intval ( $uid ),
dbesc ( '<' . $xchan_hash . '>' )
);
if ( ! $r ) {
$r = q ( " update item set allow_cid = concat(allow_cid,'%s') where mid = '%s' and uid = %d " ,
dbesc ( '<' . $xchan_hash . '>' ),
dbesc ( $mid ),
intval ( $uid )
);
}
2014-06-18 02:21:46 +00:00
}
function item_remove_cid ( $xchan_hash , $mid , $uid ) {
2022-07-20 00:43:27 +00:00
$r = q ( " select allow_cid from item where mid = '%s' and uid = %d and allow_cid like '%s' " ,
dbesc ( $mid ),
intval ( $uid ),
dbesc ( '<' . $xchan_hash . '>' )
);
if ( $r ) {
$x = q ( " update item set allow_cid = '%s' where mid = '%s' and uid = %d " ,
dbesc ( str_replace ( '<' . $xchan_hash . '>' , '' , $r [ 0 ][ 'allow_cid' ])),
dbesc ( $mid ),
intval ( $uid )
);
}
2014-06-18 02:21:46 +00:00
}
2015-01-12 19:01:07 +00:00
// Set item permissions based on results obtained from linkify_tags()
2022-08-14 09:20:43 +00:00
function set_linkified_perms ( $linkified , & $str_contact_allow , & $str_group_allow , $profile_uid , $parent_item , & $private ) {
2022-07-20 00:43:27 +00:00
$first_access_tag = true ;
foreach ( $linkified as $x ) {
$access_tag = $x [ 'success' ][ 'access_tag' ];
if (( $access_tag ) && ( ! $parent_item )) {
2022-08-14 09:20:43 +00:00
logger ( 'access_tag: ' . print_r ( $access_tag , true ), LOGGER_DATA );
2022-07-20 00:43:27 +00:00
if ( $first_access_tag && ( ! get_pconfig ( $profile_uid , 'system' , 'no_private_mention_acl_override' ))) {
// This is a tough call, hence configurable. The issue is that one can type in a @!privacy mention
// and also have a default ACL (perhaps from viewing a collection) and could be suprised that the
// privacy mention wasn't the only recipient. So the default is to wipe out the existing ACL if a
// private mention is found. This can be over-ridden if you wish private mentions to be in
// addition to the current ACL settings.
$str_contact_allow = '' ;
$str_group_allow = '' ;
$first_access_tag = false ;
}
2022-09-17 00:25:44 +00:00
if ( str_starts_with ( $access_tag , 'cid:' )) {
2022-07-20 00:43:27 +00:00
$str_contact_allow .= '<' . substr ( $access_tag , 4 ) . '>' ;
$access_tag = '' ;
$private = 2 ;
}
2022-09-17 00:25:44 +00:00
elseif ( str_starts_with ( $access_tag , 'gid:' )) {
2022-07-20 00:43:27 +00:00
$str_group_allow .= '<' . substr ( $access_tag , 4 ) . '>' ;
$access_tag = '' ;
$private = 2 ;
}
}
}
2015-01-12 19:01:07 +00:00
}
2015-02-26 00:51:39 +00:00
2015-03-21 23:06:08 +00:00
/**
* We can ' t trust ITEM_ORIGIN to tell us if this is a local comment
2015-02-26 00:51:39 +00:00
* which needs to be relayed , because it was misconfigured at one point for several
* months and set for some remote items ( in alternate delivery chains ) . This could
2015-04-09 22:28:23 +00:00
* cause looping , so use this hackish but accurate method .
2015-03-21 23:06:08 +00:00
*
* @ param array $item
* @ return boolean
2015-02-26 00:51:39 +00:00
*/
function comment_local_origin ( $item ) {
2022-07-20 00:43:27 +00:00
if ( stripos ( $item [ 'mid' ], App :: get_hostname ()) && ( $item [ 'parent' ] != $item [ 'id' ]))
return true ;
2015-03-21 23:06:08 +00:00
2022-07-20 00:43:27 +00:00
return false ;
2015-03-20 23:06:18 +00:00
}
2015-06-01 01:35:35 +00:00
2016-02-15 00:15:55 +00:00
function send_profile_photo_activity ( $channel , $photo , $profile ) {
2022-07-20 00:43:27 +00:00
// for now only create activities for the default profile
2016-02-15 00:15:55 +00:00
2022-07-20 00:43:27 +00:00
if ( ! ( isset ( $profile ) && isset ( $profile [ 'is_default' ]) && intval ( $profile [ 'is_default' ]))) {
return ;
}
2018-08-27 06:24:27 +00:00
2022-07-20 00:43:27 +00:00
$arr = [];
$arr [ 'item_thread_top' ] = 1 ;
$arr [ 'item_origin' ] = 1 ;
$arr [ 'item_wall' ] = 1 ;
$arr [ 'obj_type' ] = ACTIVITY_OBJ_NOTE ;
$arr [ 'verb' ] = ACTIVITY_CREATE ;
$arr [ 'uuid' ] = new_uuid ();
$arr [ 'mid' ] = z_root () . '/item/' . $arr [ 'uuid' ];
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 ;
$arr [ 'obj' ] = [
'type' => ACTIVITY_OBJ_NOTE ,
'published' => datetime_convert ( 'UTC' , 'UTC' , 'now' , ATOM_TIME ),
'updated' => datetime_convert ( 'UTC' , 'UTC' , 'now' , ATOM_TIME ),
'id' => $arr [ 'mid' ],
'url' => [ 'type' => 'Link' , 'mediaType' => $photo [ 'mimetype' ], 'href' => z_root () . '/photo/profile/l/' . $channel [ 'channel_id' ] ],
'source' => [ 'content' => $arr [ 'body' ], 'mediaType' => 'text/x-multicode' ],
'content' => bbcode ( $arr [ 'body' ]),
2024-01-11 18:34:22 +00:00
'actor' => Activity :: actorEncode ( $channel , false ),
2022-07-20 00:43:27 +00:00
];
2018-07-18 04:56:51 +00:00
2022-07-20 00:43:27 +00:00
$acl = new AccessControl ( $channel );
$x = $acl -> get ();
$arr [ 'allow_cid' ] = $x [ 'allow_cid' ];
2016-02-15 00:15:55 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'allow_gid' ] = $x [ 'allow_gid' ];
$arr [ 'deny_cid' ] = $x [ 'deny_cid' ];
$arr [ 'deny_gid' ] = $x [ 'deny_gid' ];
2016-02-15 00:15:55 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'uid' ] = $channel [ 'channel_id' ];
$arr [ 'aid' ] = $channel [ 'channel_account_id' ];
2016-02-15 00:15:55 +00:00
2022-07-20 00:43:27 +00:00
$arr [ 'owner_xchan' ] = $channel [ 'channel_hash' ];
$arr [ 'author_xchan' ] = $channel [ 'channel_hash' ];
2016-02-15 00:15:55 +00:00
2022-07-20 00:43:27 +00:00
post_activity_item ( $arr );
2016-02-15 00:15:55 +00:00
}
2016-04-28 04:26:52 +00:00
function sync_an_item ( $channel_id , $item_id ) {
2022-07-20 00:43:27 +00:00
$r = q ( " select * from item where id = %d " ,
intval ( $item_id )
);
if ( $r ) {
2022-03-31 19:21:52 +00:00
if ( intval ( $r [ 0 ][ 'parent' ]) !== intval ( $r [ 0 ][ 'id' ])) {
// sync the parent also. This prevents mis-deliveries from sync packets arriving out of order.
$y = q ( " select * from item where id = %d " ,
intval ( $r [ 0 ][ 'parent' ])
);
if ( $y ) {
$r = array_merge ( $y , $r );
}
}
$encoded = [];
2022-07-20 00:43:27 +00:00
xchan_query ( $r );
$sync_items = fetch_post_tags ( $r );
2022-03-31 19:21:52 +00:00
if ( $sync_items ) {
foreach ( $sync_items as $i ) {
$encoded [] = encode_item ( $i , true );
}
}
2022-09-17 00:25:44 +00:00
Libsync :: build_sync_packet ( $channel_id , [ 'item' => $encoded ]);
2022-07-20 00:43:27 +00:00
}
2016-05-09 09:56:42 +00:00
}
2016-08-15 20:18:25 +00:00
2020-02-02 23:12:20 +00:00
function list_attached_local_files ( $body ) {
2016-08-15 20:18:25 +00:00
2022-07-20 00:43:27 +00:00
$files = [];
$match = [];
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
// match img and zmg image links
2022-09-17 00:25:44 +00:00
if ( preg_match_all ( " / \ [[zi]mg(.*?)](.*?) \ [ \ /[zi]mg]/ " , $body , $match )) {
2022-07-20 00:43:27 +00:00
$images = $match [ 2 ];
if ( $images ) {
foreach ( $images as $image ) {
if ( ! stristr ( $image , z_root () . '/photo/' )) {
continue ;
}
$image_uri = substr ( $image , strrpos ( $image , '/' ) + 1 );
2022-09-17 00:25:44 +00:00
if ( str_contains ( $image_uri , '-' )) {
2022-07-20 00:43:27 +00:00
$image_uri = substr ( $image_uri , 0 , strrpos ( $image_uri , '-' ));
}
2022-09-17 00:25:44 +00:00
if ( str_contains ( $image_uri , '.' )) {
2022-07-20 00:43:27 +00:00
$image_uri = substr ( $image_uri , 0 , strpos ( $image_uri , '.' ));
}
if ( $image_uri ) {
$files [] = $image_uri ;
}
}
}
}
2022-09-17 00:25:44 +00:00
if ( preg_match_all ( " / \ [attachment](.*?) \ [ \ /attachment]/ " , $body , $match )) {
2022-07-20 00:43:27 +00:00
$attaches = $match [ 1 ];
if ( $attaches ) {
foreach ( $attaches as $attach ) {
$hash = substr ( $attach , 0 , strpos ( $attach , ',' ));
if ( $hash ) {
$files [] = $hash ;
}
}
}
}
return $files ;
2016-08-15 20:18:25 +00:00
}
2016-10-01 22:41:25 +00:00
2020-02-02 23:12:20 +00:00
function fix_attached_permissions ( $uid , $body , $str_contact_allow , $str_group_allow , $str_contact_deny , $str_group_deny , $token = EMPTY_STR ) {
2016-10-01 22:41:25 +00:00
2022-07-20 00:43:27 +00:00
$files = list_attached_local_files ( $body );
if ( ! $files ) {
return ;
}
foreach ( $files as $file ) {
$attach_q = q ( " select id, hash, flags, is_photo, allow_cid, allow_gid, deny_cid, deny_gid from attach where hash = '%s' and uid = %d " ,
dbesc ( $file ),
intval ( $uid )
);
if ( ! $attach_q ) {
continue ;
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
$attach = array_shift ( $attach_q );
2022-08-14 09:20:43 +00:00
$new_public = ! (( $str_contact_allow || $str_group_allow || $str_contact_deny || $str_group_deny ));
$existing_public = ! (( $attach [ 'allow_cid' ] || $attach [ 'allow_gid' ] || $attach [ 'deny_cid' ] || $attach [ 'deny_gid' ]));
2022-07-20 00:43:27 +00:00
if ( $existing_public ) {
// permissions have already been fixed and they are public. There's nothing for us to do.
continue ;
}
2022-07-27 22:16:27 +00:00
2022-07-20 00:43:27 +00:00
// if flags & 1, the attachment was uploaded directly into a post and needs to have permissions corrected
// or - if it is a private file and a new token was generated, we'll need to add the token to the ACL.
if ((( intval ( $attach [ 'flags' ]) & 1 ) !== 1 ) && ( ! $token )) {
continue ;
}
$item_private = 0 ;
if ( $new_public === false ) {
2022-07-27 22:16:27 +00:00
2022-01-15 02:50:13 +00:00
$item_private = (( $str_group_allow || ( $str_contact_allow && substr_count ( $str_contact_allow , '<' ) > 2 )) ? 1 : 2 );
2020-02-02 23:12:20 +00:00
2022-07-20 00:43:27 +00:00
// preserve any existing tokens that may have been set for this file
$token_matches = null ;
2022-09-17 00:25:44 +00:00
if ( preg_match_all ( '/<token:(.*?)>/' , $attach [ 'allow_cid' ], $token_matches , PREG_SET_ORDER )) {
2022-07-20 00:43:27 +00:00
foreach ( $token_matches as $m ) {
$tok = '<token:' . $m [ 1 ] . '>' ;
2022-09-17 00:25:44 +00:00
if ( ! str_contains ( $str_contact_allow , $tok )) {
2022-07-20 00:43:27 +00:00
$str_contact_allow .= $tok ;
}
}
}
if ( $token ) {
$str_contact_allow .= '<token:' . $token . '>' ;
}
}
q ( " update attach SET allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', flags = 0
WHERE id = % d AND uid = % d " ,
dbesc ( $str_contact_allow ),
dbesc ( $str_group_allow ),
dbesc ( $str_contact_deny ),
dbesc ( $str_group_deny ),
intval ( $attach [ 'id' ]),
intval ( $uid )
);
if ( $attach [ 'is_photo' ]) {
$r = q ( " UPDATE photo SET allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s'
WHERE resource_id = '%s' AND uid = % d " ,
dbesc ( $str_contact_allow ),
dbesc ( $str_group_allow ),
dbesc ( $str_contact_deny ),
dbesc ( $str_group_deny ),
dbesc ( $file ),
intval ( $uid )
);
$r = q ( " UPDATE item SET allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d
WHERE resource_id = '%s' AND 'resource_type' = 'photo' AND uid = % d " ,
dbesc ( $str_contact_allow ),
dbesc ( $str_group_allow ),
dbesc ( $str_contact_deny ),
dbesc ( $str_group_deny ),
intval ( $item_private ),
dbesc ( $file ),
intval ( $uid )
);
}
}
2016-08-15 20:18:25 +00:00
}
2016-10-25 23:21:56 +00:00
function item_create_edit_activity ( $post ) {
2022-07-20 00:43:27 +00:00
// obsolete and not maintained, left in case it is ever needed again
if (( ! $post ) || ( ! $post [ 'item' ]) || ( $post [ 'item' ][ 'item_type' ] != ITEM_TYPE_POST ))
return ;
$update_item = $post [ 'item' ];
$new_item = $update_item ;
$author = q ( " select * from xchan where xchan_hash = '%s' limit 1 " ,
dbesc ( $new_item [ 'author_xchan' ])
);
if ( $author )
$item_author = $author [ 0 ];
$new_item [ 'id' ] = 0 ;
$new_item [ 'parent' ] = 0 ;
$new_item [ 'uuid' ] = new_uuid ();
$new_item [ 'mid' ] = z_root () . '/item/' . $new_item [ 'uuid' ];
$new_item [ 'body' ] = sprintf ( t ( '[Edited %s]' ), (( $update_item [ 'item_thread_top' ]) ? t ( 'Post' , 'edit_activity' ) : t ( 'Comment' , 'edit_activity' )));
$new_item [ 'body' ] .= " \n \n " ;
$new_item [ 'body' ] .= $update_item [ 'body' ];
$new_item [ 'sig' ] = '' ;
$new_item [ 'verb' ] = ACTIVITY_UPDATE ;
$new_item [ 'item_thread_top' ] = 0 ;
$new_item [ 'created' ] = $new_item [ 'edited' ] = datetime_convert ();
$new_item [ 'obj_type' ] = (( $update_item [ 'item_thread_top' ]) ? ACTIVITY_OBJ_NOTE : ACTIVITY_OBJ_COMMENT );
2022-09-17 00:25:44 +00:00
$new_item [ 'obj' ] = json_encode ([
2022-07-20 00:43:27 +00:00
'type' => $new_item [ 'obj_type' ],
'id' => $update_item [ 'mid' ],
'parent' => $update_item [ 'parent_mid' ],
2022-09-17 00:25:44 +00:00
'link' => [[ 'rel' => 'alternate' , 'type' => 'text/html' , 'href' => $update_item [ 'plink' ]]],
2022-07-20 00:43:27 +00:00
'title' => $update_item [ 'title' ],
'content' => $update_item [ 'body' ],
'created' => $update_item [ 'created' ],
'edited' => $update_item [ 'edited' ],
2022-09-17 00:25:44 +00:00
'author' => [
2022-07-20 00:43:27 +00:00
'name' => $item_author [ 'xchan_name' ],
'address' => $item_author [ 'xchan_addr' ],
'guid' => $item_author [ 'xchan_guid' ],
'guid_sig' => $item_author [ 'xchan_guid_sig' ],
2022-09-17 00:25:44 +00:00
'link' => [
[ 'rel' => 'alternate' , 'type' => 'text/html' , 'href' => $item_author [ 'xchan_url' ]],
[ 'rel' => 'photo' , 'type' => $item_author [ 'xchan_photo_mimetype' ], 'href' => $item_author [ 'xchan_photo_m' ]]],
],
], JSON_UNESCAPED_SLASHES );
2022-07-20 00:43:27 +00:00
$x = post_activity_item ( $new_item );
$post_id = $x [ 'id' ];
if ( $post_id ) {
$r = q ( " select * from item where id = %d " ,
intval ( $post_id )
);
if ( $r ) {
xchan_query ( $r );
$sync_item = fetch_post_tags ( $r );
2022-09-17 00:25:44 +00:00
Libsync :: build_sync_packet ( $new_item [ 'uid' ], [ 'item' => [ encode_item ( $sync_item [ 0 ], true )]]);
2022-07-20 00:43:27 +00:00
}
}
Run :: Summon ([ 'Notifier' , 'edit_activity' , $post_id ]);
2016-10-30 14:54:06 +00:00
}
2017-11-27 02:29:24 +00:00
/**
* @ brief copies an entire conversation from the pubstream to this channel ' s stream
* which will allow you to interact with it .
*/
function copy_of_pubitem ( $channel , $mid ) {
2022-07-20 00:43:27 +00:00
$result = null ;
$syschan = Channel :: get_system ();
logger ( 'copy_of_pubitem: ' . $channel [ 'channel_id' ] . ' mid: ' . $mid );
$r = q ( " select * from item where mid = '%s' and uid = %d limit 1 " ,
dbesc ( $mid ),
intval ( $channel [ 'channel_id' ])
);
if ( $r ) {
logger ( 'exists' );
2022-10-23 00:50:31 +00:00
$item = fetch_post_tags ( $r );
2022-09-17 00:25:44 +00:00
return array_shift ( $item );
2022-07-20 00:43:27 +00:00
}
// this query is used for the global public stream
$r = q ( " select * from item where parent_mid = ( select parent_mid from item where mid = '%s' and uid = %d ) order by id " ,
dbesc ( $mid ),
intval ( $syschan [ 'channel_id' ])
);
// if that failed, try to find entries that would have been posted in the local public stream
if ( ! $r ) {
$r = q ( " select * from item where parent_mid = ( select distinct (parent_mid) from item where mid = '%s' and item_wall = 1 and item_private = 0 ) order by id " ,
dbesc ( $mid ),
intval ( $syschan [ 'channel_id' ])
);
}
if ( $r ) {
2022-10-23 00:50:31 +00:00
$items = fetch_post_tags ( $r );
2022-07-20 00:43:27 +00:00
foreach ( $items as $rv ) {
$d = q ( " select id from item where mid = '%s' and uid = %d limit 1 " ,
dbesc ( $rv [ 'mid' ]),
intval ( $channel [ 'channel_id' ])
);
if ( $d ) {
logger ( 'mid: ' . $rv [ 'mid' ] . ' already copied. Continuing.' );
continue ;
}
unset ( $rv [ 'id' ]);
unset ( $rv [ 'parent' ]);
$rv [ 'aid' ] = $channel [ 'channel_account_id' ];
$rv [ 'uid' ] = $channel [ 'channel_id' ];
$rv [ 'item_wall' ] = 0 ;
$rv [ 'item_origin' ] = 0 ;
2024-02-10 19:22:39 +00:00
$x = item_store ( $rv , deliver : false , addAndSync : false );
2022-07-20 00:43:27 +00:00
if ( $x [ 'item_id' ] && $x [ 'item' ][ 'mid' ] === $mid ) {
$result = $x [ 'item' ];
2022-03-20 21:39:58 +00:00
sync_an_item ( $channel [ 'channel_id' ], $x [ 'item_id' ]);
2022-07-20 00:43:27 +00:00
}
}
}
else {
logger ( 'copy query failed.' );
}
return $result ;
2017-11-27 02:29:24 +00:00
}
2023-09-29 22:31:07 +00:00
function reverse_activity_mid ( $string )
{
return str_replace ( z_root () . '/activity/' , z_root () . '/item/' , $string );
}
function set_activity_mid ( $string )
{
return str_replace ( z_root () . '/item/' , z_root () . '/activity/' , $string );
}