2016-04-19 03:38:38 +00:00
< ? php
2016-06-14 02:58:24 +00:00
2016-04-19 03:38:38 +00:00
namespace Zotlabs\Module ;
/**
*
2019-07-11 01:43:20 +00:00
* As a GET request , this module answers to activitypub and zot6 item fetches and
* acts as a permalink for local content .
*
* Otherwise this is the POST destination for most all locally posted
2016-04-19 03:38:38 +00:00
* text stuff . This function handles status , wall - to - wall status ,
* local comments , and remote coments that are posted on this site
* ( as opposed to being delivered in a feed ) .
2019-07-11 01:43:20 +00:00
* Also processed here are posts and comments coming through the API .
2016-04-19 03:38:38 +00:00
* All of these become an " item " which is our basic unit of
* information .
* Posts that originate externally or do not fall into the above
* posting categories go through item_store () instead of this function .
*
*/
2018-06-05 01:40:11 +00:00
use Zotlabs\Lib\Libsync ;
2018-08-02 00:58:54 +00:00
use Zotlabs\Lib\Activity ;
2018-08-23 06:04:37 +00:00
use Zotlabs\Lib\ActivityStreams ;
use Zotlabs\Lib\LDSignatures ;
use Zotlabs\Web\HTTPSig ;
2018-11-16 04:52:08 +00:00
use Zotlabs\Web\Controller ;
2018-09-10 06:23:42 +00:00
use Zotlabs\Lib\Libzot ;
2018-09-11 10:36:31 +00:00
use Zotlabs\Lib\ThreadListener ;
2019-03-12 00:45:44 +00:00
use Zotlabs\Lib\Config ;
2018-11-16 04:52:08 +00:00
use Zotlabs\Lib\IConfig ;
use Zotlabs\Lib\Enotify ;
2019-03-25 05:46:31 +00:00
use Zotlabs\Lib\Apps ;
2019-07-11 01:43:20 +00:00
use Zotlabs\Access\PermissionLimits ;
2019-10-14 00:30:28 +00:00
use Zotlabs\Access\PermissionRoles ;
2019-07-11 01:43:20 +00:00
use Zotlabs\Access\AccessControl ;
2021-01-08 21:51:40 +00:00
use Zotlabs\Daemon\Run ;
2018-11-12 03:13:11 +00:00
use App ;
2019-07-11 01:43:20 +00:00
use URLify ;
2018-06-01 04:05:09 +00:00
2016-04-19 03:38:38 +00:00
require_once ( 'include/attach.php' );
2016-10-06 22:59:27 +00:00
require_once ( 'include/bbcode.php' );
2016-12-26 22:17:40 +00:00
require_once ( 'include/security.php' );
2016-10-06 22:59:27 +00:00
2016-04-19 03:38:38 +00:00
2018-11-16 04:52:08 +00:00
use Zotlabs\Lib as Zlib ;
2016-04-19 03:38:38 +00:00
2018-11-16 04:52:08 +00:00
class Item extends Controller {
2016-04-19 03:38:38 +00:00
2018-08-23 06:04:37 +00:00
function init () {
2018-09-10 06:23:42 +00:00
2020-09-25 02:30:07 +00:00
2021-03-09 22:35:41 +00:00
if ( ActivityStreams :: is_as_request ()) {
2018-08-23 06:04:37 +00:00
$item_id = argv ( 1 );
2021-03-09 22:35:41 +00:00
if ( ! $item_id ) {
2018-08-23 06:04:37 +00:00
http_status_exit ( 404 , 'Not found' );
2021-03-09 22:35:41 +00:00
}
2019-03-13 02:13:48 +00:00
$portable_id = EMPTY_STR ;
$item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 " ;
$i = null ;
// do we have the item (at all)?
2021-02-17 03:10:47 +00:00
// add preferential bias to item owners (item_wall = 1)
2019-03-13 02:13:48 +00:00
2021-03-18 01:30:37 +00:00
$r = q ( " select * from item where (mid = '%s' or uuid = '%s') $item_normal order by item_wall desc limit 1 " ,
2021-02-17 03:10:47 +00:00
dbesc ( z_root () . '/item/' . $item_id ),
dbesc ( $item_id )
2019-03-13 02:13:48 +00:00
);
if ( ! $r ) {
http_status_exit ( 404 , 'Not found' );
}
// process an authenticated fetch
2021-02-17 03:10:47 +00:00
$sigdata = HTTPSig :: verify ( EMPTY_STR );
2019-03-12 00:45:44 +00:00
if ( $sigdata [ 'portable_id' ] && $sigdata [ 'header_valid' ]) {
2019-03-09 21:13:04 +00:00
$portable_id = $sigdata [ 'portable_id' ];
2019-03-12 00:45:44 +00:00
if ( ! check_channelallowed ( $portable_id )) {
2019-03-11 00:11:35 +00:00
http_status_exit ( 403 , 'Permission denied' );
}
2019-03-12 00:45:44 +00:00
if ( ! check_siteallowed ( $sigdata [ 'signer' ])) {
2019-03-11 00:11:35 +00:00
http_status_exit ( 403 , 'Permission denied' );
}
2019-03-09 21:13:04 +00:00
observer_auth ( $portable_id );
2019-03-13 02:13:48 +00:00
2021-02-17 03:10:47 +00:00
$i = q ( " select id as item_id from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1 " ,
2019-03-13 02:13:48 +00:00
dbesc ( $r [ 0 ][ 'parent_mid' ]),
dbesc ( $portable_id )
);
2019-03-09 21:13:04 +00:00
}
2019-11-04 23:26:05 +00:00
elseif ( Config :: get ( 'system' , 'require_authenticated_fetch' , false )) {
2019-03-12 00:45:44 +00:00
http_status_exit ( 403 , 'Permission denied' );
}
2019-03-09 21:13:04 +00:00
2019-03-13 02:13:48 +00:00
// if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access
2019-06-18 01:32:08 +00:00
// with a bias towards those items owned by channels on this site (item_wall = 1)
2021-02-17 03:10:47 +00:00
2018-08-23 06:04:37 +00:00
$sql_extra = item_permissions_sql ( 0 );
2019-03-13 02:13:48 +00:00
if ( ! $i ) {
2019-06-18 01:32:08 +00:00
$i = q ( " select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1 " ,
2019-03-13 02:13:48 +00:00
dbesc ( $r [ 0 ][ 'parent_mid' ])
2018-08-23 06:04:37 +00:00
);
}
2021-03-09 22:35:41 +00:00
if ( ! $i ) {
2019-03-13 02:13:48 +00:00
http_status_exit ( 403 , 'Forbidden' );
}
2021-02-17 03:10:47 +00:00
// If we get to this point we have determined we can access the original in $r (fetched much further above), so use it.
2020-09-25 02:30:07 +00:00
2021-02-17 03:10:47 +00:00
xchan_query ( $r , true );
$items = fetch_post_tags ( $r , false );
2020-09-25 02:30:07 +00:00
2018-08-23 06:04:37 +00:00
$chan = channelx_by_n ( $items [ 0 ][ 'uid' ]);
2021-01-24 23:40:25 +00:00
if ( ! $chan ) {
2018-08-23 06:04:37 +00:00
http_status_exit ( 404 , 'Not found' );
2021-01-24 23:40:25 +00:00
}
if ( ! perm_is_allowed ( $chan [ 'channel_id' ], get_observer_hash (), 'view_stream' )) {
2018-08-23 06:04:37 +00:00
http_status_exit ( 403 , 'Forbidden' );
2021-01-24 23:40:25 +00:00
}
2018-08-23 06:04:37 +00:00
2021-02-17 03:10:47 +00:00
$i = Activity :: encode_item ( $items [ 0 ], true );
2020-09-25 02:30:07 +00:00
2021-02-17 03:10:47 +00:00
if ( ! $i ) {
http_status_exit ( 404 , 'Not found' );
}
2021-01-24 23:40:25 +00:00
if ( $portable_id && ( ! intval ( $items [ 0 ][ 'item_private' ]))) {
2021-02-17 03:10:47 +00:00
$c = q ( " select abook_id from abook where abook_channel = %d and abook_xchan = '%s' " ,
intval ( $items [ 0 ][ 'uid' ]),
dbesc ( $portable_id )
);
if ( ! $c ) {
ThreadListener :: store ( z_root () . '/item/' . $item_id , $portable_id );
}
2020-09-25 02:30:07 +00:00
}
2021-02-17 03:10:47 +00:00
as_return_and_die ( $i , $chan );
}
2018-09-10 06:23:42 +00:00
2021-03-09 22:35:41 +00:00
if ( Libzot :: is_zot_request ()) {
2018-08-23 06:04:37 +00:00
2021-02-17 03:10:47 +00:00
$conversation = false ;
2018-09-10 06:23:42 +00:00
2018-09-10 03:55:51 +00:00
$item_id = argv ( 1 );
2021-02-17 03:10:47 +00:00
2021-03-09 22:35:41 +00:00
if ( ! $item_id ) {
2018-09-10 03:55:51 +00:00
http_status_exit ( 404 , 'Not found' );
2021-03-09 22:35:41 +00:00
}
2018-09-11 01:36:52 +00:00
$portable_id = EMPTY_STR ;
2021-03-14 07:14:32 +00:00
$item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 and not verb in ( 'Follow', 'Unfollow' ) " ;
2019-03-13 02:13:48 +00:00
$i = null ;
// do we have the item (at all)?
2021-03-18 01:30:37 +00:00
$r = q ( " select * from item where (mid = '%s' or uuid = '%s') $item_normal limit 1 " ,
dbesc ( z_root () . '/item/' . $item_id ),
dbesc ( $item_id )
2019-03-13 02:13:48 +00:00
);
if ( ! $r ) {
http_status_exit ( 404 , 'Not found' );
}
// process an authenticated fetch
2021-02-17 03:10:47 +00:00
$sigdata = HTTPSig :: verify (( $_SERVER [ 'REQUEST_METHOD' ] === 'POST' ) ? file_get_contents ( 'php://input' ) : EMPTY_STR );
2019-03-12 00:45:44 +00:00
if ( $sigdata [ 'portable_id' ] && $sigdata [ 'header_valid' ]) {
2018-09-11 01:36:52 +00:00
$portable_id = $sigdata [ 'portable_id' ];
2019-03-12 00:45:44 +00:00
if ( ! check_channelallowed ( $portable_id )) {
2019-03-11 00:11:35 +00:00
http_status_exit ( 403 , 'Permission denied' );
}
2019-03-12 00:45:44 +00:00
if ( ! check_siteallowed ( $sigdata [ 'signer' ])) {
2019-03-11 00:11:35 +00:00
http_status_exit ( 403 , 'Permission denied' );
}
2019-03-09 21:13:04 +00:00
observer_auth ( $portable_id );
2019-03-13 02:13:48 +00:00
2021-02-17 03:10:47 +00:00
$i = q ( " select id as item_id from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1 " ,
2019-03-13 02:13:48 +00:00
dbesc ( $r [ 0 ][ 'parent_mid' ]),
dbesc ( $portable_id )
);
2018-09-11 01:36:52 +00:00
}
2019-11-04 23:26:05 +00:00
elseif ( Config :: get ( 'system' , 'require_authenticated_fetch' , false )) {
2019-03-12 00:45:44 +00:00
http_status_exit ( 403 , 'Permission denied' );
}
2018-09-11 01:36:52 +00:00
2019-03-13 02:13:48 +00:00
// if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access
2019-06-18 01:32:08 +00:00
// with a bias towards those items owned by channels on this site (item_wall = 1)
2021-02-17 03:10:47 +00:00
2018-09-10 03:55:51 +00:00
$sql_extra = item_permissions_sql ( 0 );
2019-03-13 02:13:48 +00:00
if ( ! $i ) {
2019-06-18 01:32:08 +00:00
$i = q ( " select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1 " ,
2019-03-13 02:13:48 +00:00
dbesc ( $r [ 0 ][ 'parent_mid' ])
2018-09-10 03:55:51 +00:00
);
}
2021-03-09 22:35:41 +00:00
if ( ! $i ) {
2019-03-13 02:13:48 +00:00
http_status_exit ( 403 , 'Forbidden' );
2018-09-10 03:55:51 +00:00
}
2021-02-17 03:10:47 +00:00
$parents_str = ids_to_querystr ( $i , 'item_id' );
$items = q ( " SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal order by item.id asc " ,
dbesc ( $parents_str )
);
2018-08-23 06:04:37 +00:00
2021-02-17 03:10:47 +00:00
if ( ! $items ) {
http_status_exit ( 404 , 'Not found' );
}
2018-12-05 05:51:56 +00:00
2021-02-17 03:10:47 +00:00
xchan_query ( $items , true );
$items = fetch_post_tags ( $items , true );
if ( ! $items ) {
http_status_exit ( 404 , 'Not found' );
}
2020-04-16 23:24:01 +00:00
$chan = channelx_by_n ( $items [ 0 ][ 'uid' ]);
2018-09-10 03:55:51 +00:00
2021-01-24 23:40:25 +00:00
if ( ! $chan ) {
2018-09-10 03:55:51 +00:00
http_status_exit ( 404 , 'Not found' );
2021-01-24 23:40:25 +00:00
}
if ( ! perm_is_allowed ( $chan [ 'channel_id' ], get_observer_hash (), 'view_stream' )) {
2018-09-10 03:55:51 +00:00
http_status_exit ( 403 , 'Forbidden' );
2021-01-24 23:40:25 +00:00
}
2018-09-10 03:55:51 +00:00
2021-02-17 03:10:47 +00:00
$i = Activity :: encode_item_collection ( $items , 'conversation/' . $item_id , 'OrderedCollection' , true );
2021-01-24 23:40:25 +00:00
if ( $portable_id && ( ! intval ( $items [ 0 ][ 'item_private' ]))) {
2021-02-17 03:10:47 +00:00
ThreadListener :: store ( z_root () . '/item/' . $item_id , $portable_id );
2021-01-24 23:40:25 +00:00
}
2018-09-20 00:53:49 +00:00
2021-03-09 22:35:41 +00:00
if ( ! $i ) {
2021-02-17 03:10:47 +00:00
http_status_exit ( 404 , 'Not found' );
2021-03-09 22:35:41 +00:00
}
2021-02-17 03:10:47 +00:00
$x = array_merge ([ '@context' => [
ACTIVITYSTREAMS_JSONLD_REV ,
'https://w3id.org/security/v1' ,
Activity :: ap_schema ()
]], $i );
$headers = [];
$headers [ 'Content-Type' ] = 'application/x-zot+json' ;
$x [ 'signature' ] = LDSignatures :: sign ( $x , $chan );
$ret = json_encode ( $x , JSON_UNESCAPED_SLASHES );
$headers [ 'Digest' ] = HTTPSig :: generate_digest_header ( $ret );
$headers [ '(request-target)' ] = strtolower ( $_SERVER [ 'REQUEST_METHOD' ]) . ' ' . $_SERVER [ 'REQUEST_URI' ];
$h = HTTPSig :: create_sig ( $headers , $chan [ 'channel_prvkey' ], channel_url ( $chan ));
HTTPSig :: set_headers ( $h );
echo $ret ;
killme ();
}
2020-09-25 02:30:07 +00:00
2019-07-11 01:43:20 +00:00
// if it isn't a drop command and isn't a post method and wasn't handled already,
// the default action is a browser request for a persistent uri and this should return
// the text/html page of the item.
if ( argc () > 1 && argv ( 1 ) !== 'drop' ) {
2021-03-19 01:53:29 +00:00
$x = q ( " select uid, item_wall, llink, mid from item where mid = '%s' or mid = '%s' or uuid = '%s' " ,
2019-08-30 04:19:00 +00:00
dbesc ( z_root () . '/item/' . argv ( 1 )),
2021-03-19 01:53:29 +00:00
dbesc ( z_root () . '/activity/' . argv ( 1 )),
dbesc ( argv ( 1 ))
2018-09-20 00:53:49 +00:00
);
2019-07-11 01:43:20 +00:00
if ( $x ) {
foreach ( $x as $xv ) {
2019-03-15 00:20:36 +00:00
if ( intval ( $xv [ 'item_wall' ])) {
$c = channelx_by_n ( $xv [ 'uid' ]);
if ( $c ) {
2019-03-18 04:18:32 +00:00
goaway ( $c [ 'xchan_url' ] . '?mid=' . gen_link_id ( $xv [ 'mid' ]));
2019-03-15 00:20:36 +00:00
}
}
}
goaway ( $x [ 0 ][ 'llink' ]);
2018-09-20 00:53:49 +00:00
}
2019-03-15 00:20:36 +00:00
http_status_exit ( 404 , 'Not found' );
2018-09-20 00:53:49 +00:00
}
2018-09-10 03:55:51 +00:00
}
2018-08-23 06:04:37 +00:00
2016-04-19 03:38:38 +00:00
function post () {
2018-09-04 11:54:24 +00:00
2021-03-09 22:35:41 +00:00
if (( ! local_channel ()) && ( ! remote_channel ()) && ( ! isset ( $_REQUEST [ 'anonname' ]))) {
2016-04-19 03:38:38 +00:00
return ;
2021-03-09 22:35:41 +00:00
}
2016-12-26 22:17:40 +00:00
2019-07-11 01:43:20 +00:00
// drop an array of items.
2021-03-09 22:35:41 +00:00
if ( isset ( $_REQUEST [ 'dropitems' ])) {
2019-07-11 01:43:20 +00:00
$arr_drop = explode ( ',' , $_REQUEST [ 'dropitems' ]);
drop_items ( $arr_drop );
$json = array ( 'success' => 1 );
echo json_encode ( $json );
killme ();
}
2016-04-19 03:38:38 +00:00
$uid = local_channel ();
2019-10-16 03:16:25 +00:00
$channel = null ;
2016-04-19 03:38:38 +00:00
$observer = null ;
2019-10-16 03:16:25 +00:00
$token = EMPTY_STR ;
2018-02-13 05:43:04 +00:00
$datarray = [];
2021-03-12 04:00:59 +00:00
$item_starred = false ;
$item_uplink = false ;
$item_notshown = false ;
$item_nsfw = false ;
$item_relay = false ;
$item_mentionsme = false ;
$item_verified = false ;
$item_retained = false ;
$item_rss = false ;
$item_deleted = false ;
$item_hidden = false ;
$item_delayed = false ;
$item_pending_remove = false ;
$item_blocked = false ;
$post_tags = false ;
2016-04-19 03:38:38 +00:00
/**
* Is this a reply to something ?
*/
2021-03-09 22:35:41 +00:00
$parent = (( isset ( $_REQUEST [ 'parent' ])) ? intval ( $_REQUEST [ 'parent' ]) : 0 );
$parent_mid = (( isset ( $_REQUEST [ 'parent_mid' ])) ? trim ( $_REQUEST [ 'parent_mid' ]) : '' );
2016-04-19 03:38:38 +00:00
2021-03-09 22:35:41 +00:00
$hidden_mentions = (( isset ( $_REQUEST [ 'hidden_mentions' ])) ? trim ( $_REQUEST [ 'hidden_mentions' ]) : '' );
2020-11-28 21:27:24 +00:00
2018-06-04 00:49:48 +00:00
2019-07-11 01:43:20 +00:00
/**
* Who is viewing this page and posting this thing
*/
2021-03-09 22:35:41 +00:00
$remote_xchan = (( isset ( $_REQUEST [ 'remote_xchan' ])) ? trim ( $_REQUEST [ 'remote_xchan' ]) : false );
2018-06-04 00:49:48 +00:00
$remote_observer = xchan_match ( [ 'xchan_hash' => $remote_xchan ] );
2019-07-11 01:43:20 +00:00
if ( ! $remote_observer ) {
2016-04-19 03:38:38 +00:00
$remote_xchan = $remote_observer = false ;
2018-06-04 00:49:48 +00:00
}
2019-07-11 01:43:20 +00:00
// This is the local channel representing who the posted item will belong to.
2021-03-09 22:35:41 +00:00
$profile_uid = (( isset ( $_REQUEST [ 'profile_uid' ])) ? intval ( $_REQUEST [ 'profile_uid' ]) : 0 );
2017-11-27 02:29:24 +00:00
2021-04-07 02:49:44 +00:00
// *If* you are logged in as the site admin you are allowed to create top-level items for the sys channel.
2019-07-11 01:43:20 +00:00
// This would typically be a webpage or webpage element.
2021-04-07 02:49:44 +00:00
// Comments and replies are excluded because further below we also check for sys channel ownership and
// will make a copy of the parent that you can interact with in your own stream
2019-07-11 01:43:20 +00:00
2016-04-19 03:38:38 +00:00
$sys = get_sys_channel ();
2021-04-07 02:49:44 +00:00
if ( $sys && $profile_uid && ( $sys [ 'channel_id' ] == $profile_uid ) && is_site_admin () && ! $parent ) {
2016-04-19 03:38:38 +00:00
$uid = intval ( $sys [ 'channel_id' ]);
$channel = $sys ;
$observer = $sys ;
}
2019-07-11 01:43:20 +00:00
2016-04-19 03:38:38 +00:00
call_hooks ( 'post_local_start' , $_REQUEST );
2017-06-19 05:25:41 +00:00
// logger('postvars ' . print_r($_REQUEST,true), LOGGER_DATA);
2016-04-19 03:38:38 +00:00
2021-03-09 22:35:41 +00:00
$api_source = (( isset ( $_REQUEST [ 'api_source' ]) && $_REQUEST [ 'api_source' ]) ? true : false );
2016-04-19 03:38:38 +00:00
2020-11-25 03:22:59 +00:00
$nocomment = 0 ;
if ( isset ( $_REQUEST [ 'comments_enabled' ])) {
$nocomment = 1 - intval ( $_REQUEST [ 'comments_enabled' ]);
}
2020-11-28 21:27:24 +00:00
$channel_comments_closed = get_pconfig ( $profile_uid , 'system' , 'close_comments' );
if ( ! intval ( $channel_comments_closed )) {
$channel_comments_closed = NULL_DATE ;
}
2021-03-12 04:36:42 +00:00
$comments_closed = (( isset ( $_REQUEST [ 'comments_closed' ]) && $_REQUEST [ 'comments_closed' ]) ? datetime_convert ( date_default_timezone_get (), 'UTC' , $_REQUEST [ 'comments_closed' ]) : $channel_comments_closed );
2020-11-28 21:27:24 +00:00
2020-02-27 04:48:28 +00:00
$is_poll = (( trim ( $_REQUEST [ 'poll_answers' ][ 0 ]) != '' && trim ( $_REQUEST [ 'poll_answers' ][ 1 ]) != '' ) ? true : false );
2019-07-07 23:32:48 +00:00
2016-04-19 03:38:38 +00:00
// 'origin' (if non-zero) indicates that this network is where the message originated,
// for the purpose of relaying comments to other conversation members.
// If using the API from a device (leaf node) you must set origin to 1 (default) or leave unset.
// If the API is used from another network with its own distribution
// and deliveries, you may wish to set origin to 0 or false and allow the other
// network to relay comments.
// If you are unsure, it is prudent (and important) to leave it unset.
$origin = (( $api_source && array_key_exists ( 'origin' , $_REQUEST )) ? intval ( $_REQUEST [ 'origin' ]) : 1 );
2016-07-08 01:47:18 +00:00
// To represent message-ids on other networks - this will create an iconfig record
2016-04-19 03:38:38 +00:00
$namespace = (( $api_source && array_key_exists ( 'namespace' , $_REQUEST )) ? strip_tags ( $_REQUEST [ 'namespace' ]) : '' );
$remote_id = (( $api_source && array_key_exists ( 'remote_id' , $_REQUEST )) ? strip_tags ( $_REQUEST [ 'remote_id' ]) : '' );
$owner_hash = null ;
$message_id = (( x ( $_REQUEST , 'message_id' ) && $api_source ) ? strip_tags ( $_REQUEST [ 'message_id' ]) : '' );
$created = (( x ( $_REQUEST , 'created' )) ? datetime_convert ( date_default_timezone_get (), 'UTC' , $_REQUEST [ 'created' ]) : datetime_convert ());
2020-05-03 23:52:44 +00:00
// Because somebody will probably try this and create a mess
if ( $created <= NULL_DATE ) {
$created = datetime_convert ();
}
2020-11-30 00:38:45 +00:00
2016-04-19 03:38:38 +00:00
$post_id = (( x ( $_REQUEST , 'post_id' )) ? intval ( $_REQUEST [ 'post_id' ]) : 0 );
2020-11-30 00:38:45 +00:00
2016-04-19 03:38:38 +00:00
$app = (( x ( $_REQUEST , 'source' )) ? strip_tags ( $_REQUEST [ 'source' ]) : '' );
$return_path = (( x ( $_REQUEST , 'return' )) ? $_REQUEST [ 'return' ] : '' );
$preview = (( x ( $_REQUEST , 'preview' )) ? intval ( $_REQUEST [ 'preview' ]) : 0 );
$categories = (( x ( $_REQUEST , 'category' )) ? escape_tags ( $_REQUEST [ 'category' ]) : '' );
$webpage = (( x ( $_REQUEST , 'webpage' )) ? intval ( $_REQUEST [ 'webpage' ]) : 0 );
2017-04-21 03:19:15 +00:00
$item_obscured = (( x ( $_REQUEST , 'obscured' )) ? intval ( $_REQUEST [ 'obscured' ]) : 0 );
2016-04-19 03:38:38 +00:00
$pagetitle = (( x ( $_REQUEST , 'pagetitle' )) ? escape_tags ( urlencode ( $_REQUEST [ 'pagetitle' ])) : '' );
$layout_mid = (( x ( $_REQUEST , 'layout_mid' )) ? escape_tags ( $_REQUEST [ 'layout_mid' ]) : '' );
$plink = (( x ( $_REQUEST , 'permalink' )) ? escape_tags ( $_REQUEST [ 'permalink' ]) : '' );
$obj_type = (( x ( $_REQUEST , 'obj_type' )) ? escape_tags ( $_REQUEST [ 'obj_type' ]) : ACTIVITY_OBJ_NOTE );
2020-01-22 04:54:05 +00:00
2020-11-27 21:30:48 +00:00
$item_unpublished = (( isset ( $_REQUEST [ 'draft' ])) ? intval ( $_REQUEST [ 'draft' ]) : 0 );
2020-05-13 03:58:37 +00:00
// allow API to bulk load a bunch of imported items without sending out a bunch of posts.
2016-04-19 03:38:38 +00:00
$nopush = (( x ( $_REQUEST , 'nopush' )) ? intval ( $_REQUEST [ 'nopush' ]) : 0 );
/*
* Check service class limits
*/
if ( $uid && ! ( x ( $_REQUEST , 'parent' )) && ! ( x ( $_REQUEST , 'post_id' ))) {
$ret = $this -> item_check_service_class ( $uid ,(( $_REQUEST [ 'webpage' ] == ITEM_TYPE_WEBPAGE ) ? true : false ));
if ( ! $ret [ 'success' ]) {
notice ( t ( $ret [ 'message' ]) . EOL ) ;
2016-12-03 21:31:56 +00:00
if ( $api_source )
return ( [ 'success' => false , 'message' => 'service class exception' ] );
2016-04-19 03:38:38 +00:00
if ( x ( $_REQUEST , 'return' ))
goaway ( z_root () . " / " . $return_path );
killme ();
}
}
2019-07-11 01:43:20 +00:00
if ( $pagetitle ) {
$pagetitle = strtolower ( URLify :: transliterate ( $pagetitle ));
2016-04-19 03:38:38 +00:00
}
$item_flags = $item_restrict = 0 ;
2017-01-11 20:31:55 +00:00
$expires = NULL_DATE ;
2016-04-19 03:38:38 +00:00
$route = '' ;
$parent_item = null ;
$parent_contact = null ;
$thr_parent = '' ;
$parid = 0 ;
$r = false ;
2019-07-11 01:43:20 +00:00
// If this is a comment, find the parent and preset some stuff
if ( $parent || $parent_mid ) {
2016-04-19 03:38:38 +00:00
2019-07-11 01:43:20 +00:00
if ( ! x ( $_REQUEST , 'type' )) {
2016-04-19 03:38:38 +00:00
$_REQUEST [ 'type' ] = 'net-comment' ;
2019-07-11 01:43:20 +00:00
}
if ( $obj_type == ACTIVITY_OBJ_NOTE ) {
2016-04-19 03:38:38 +00:00
$obj_type = ACTIVITY_OBJ_COMMENT ;
2019-07-11 01:43:20 +00:00
}
// fetch the parent item
if ( $parent ) {
2016-10-04 04:48:53 +00:00
$r = q ( " SELECT * FROM item WHERE id = %d LIMIT 1 " ,
2016-04-19 03:38:38 +00:00
intval ( $parent )
);
}
2019-07-11 01:43:20 +00:00
elseif ( $parent_mid && $uid ) {
2016-04-19 03:38:38 +00:00
// This is coming from an API source, and we are logged in
2016-10-04 04:48:53 +00:00
$r = q ( " SELECT * FROM item WHERE mid = '%s' AND uid = %d LIMIT 1 " ,
2016-04-19 03:38:38 +00:00
dbesc ( $parent_mid ),
intval ( $uid )
);
}
// if this isn't the real parent of the conversation, find it
2019-07-11 01:43:20 +00:00
if ( $r ) {
2016-04-19 03:38:38 +00:00
$parid = $r [ 0 ][ 'parent' ];
$parent_mid = $r [ 0 ][ 'mid' ];
2019-07-11 01:43:20 +00:00
if ( $r [ 0 ][ 'id' ] != $r [ 0 ][ 'parent' ]) {
2016-10-04 04:48:53 +00:00
$r = q ( " SELECT * FROM item WHERE id = parent AND parent = %d LIMIT 1 " ,
2016-04-19 03:38:38 +00:00
intval ( $parid )
);
}
2017-11-27 02:29:24 +00:00
2019-07-11 01:43:20 +00:00
// if interacting with a pubstream item (owned by the sys channel),
2017-11-27 02:29:24 +00:00
// create a copy of the parent in your stream
2019-07-11 01:43:20 +00:00
if ( $r [ 0 ][ 'uid' ] === $sys [ 'channel_id' ] && local_channel ()) {
2018-11-12 03:13:11 +00:00
$r = [ copy_of_pubitem ( App :: get_channel (), $r [ 0 ][ 'mid' ]) ];
2017-11-27 02:29:24 +00:00
}
2016-04-19 03:38:38 +00:00
}
2017-11-27 02:29:24 +00:00
2019-07-11 01:43:20 +00:00
if ( ! $r ) {
2016-04-19 03:38:38 +00:00
notice ( t ( 'Unable to locate original post.' ) . EOL );
2019-07-11 01:43:20 +00:00
if ( $api_source ) {
return ( [ 'success' => false , 'message' => 'invalid post id' ] );
}
if ( x ( $_REQUEST , 'return' )) {
2016-04-19 03:38:38 +00:00
goaway ( z_root () . " / " . $return_path );
2019-07-11 01:43:20 +00:00
}
2016-04-19 03:38:38 +00:00
killme ();
}
2016-07-21 06:04:07 +00:00
2017-11-27 02:29:24 +00:00
xchan_query ( $r , true );
2016-04-19 03:38:38 +00:00
$parent_item = $r [ 0 ];
$parent = $r [ 0 ][ 'id' ];
2017-11-27 02:29:24 +00:00
2016-04-19 03:38:38 +00:00
// multi-level threading - preserve the info but re-parent to our single level threading
$thr_parent = $parent_mid ;
$route = $parent_item [ 'route' ];
}
2017-06-19 05:25:41 +00:00
$moderated = false ;
2016-04-19 03:38:38 +00:00
2019-07-11 01:43:20 +00:00
if ( ! $observer ) {
2018-11-12 03:13:11 +00:00
$observer = App :: get_observer ();
2019-07-11 01:43:20 +00:00
if ( ! $observer ) {
// perhaps we're allowing moderated comments from anonymous viewers
2017-06-19 05:25:41 +00:00
$observer = anon_identity_init ( $_REQUEST );
2019-07-11 01:43:20 +00:00
if ( $observer ) {
2017-06-19 05:25:41 +00:00
$moderated = true ;
$remote_xchan = $remote_observer = $observer ;
}
}
}
2016-04-19 03:38:38 +00:00
2019-07-11 01:43:20 +00:00
if ( ! $observer ) {
2017-06-19 05:25:41 +00:00
notice ( t ( 'Permission denied.' ) . EOL ) ;
2019-07-11 01:43:20 +00:00
if ( $api_source ) {
return ( [ 'success' => false , 'message' => 'permission denied' ] );
}
if ( x ( $_REQUEST , 'return' )) {
2017-06-19 05:25:41 +00:00
goaway ( z_root () . " / " . $return_path );
2019-07-11 01:43:20 +00:00
}
2017-06-19 05:25:41 +00:00
killme ();
}
2019-07-11 01:43:20 +00:00
if ( $parent ) {
2016-04-19 03:38:38 +00:00
logger ( 'mod_item: item_post parent=' . $parent );
$can_comment = false ;
2018-09-04 11:54:24 +00:00
$can_comment = can_comment_on_post ( $observer [ 'xchan_hash' ], $parent_item );
2019-02-28 23:14:22 +00:00
if ( ! $can_comment ) {
if (( array_key_exists ( 'owner' , $parent_item )) && intval ( $parent_item [ 'owner' ][ 'abook_self' ]) == 1 ) {
$can_comment = perm_is_allowed ( $profile_uid , $observer [ 'xchan_hash' ], 'post_comments' );
}
}
2016-04-19 03:38:38 +00:00
if ( ! $can_comment ) {
notice ( t ( 'Permission denied.' ) . EOL ) ;
2016-12-03 21:31:56 +00:00
if ( $api_source )
return ( [ 'success' => false , 'message' => 'permission denied' ] );
2016-04-19 03:38:38 +00:00
if ( x ( $_REQUEST , 'return' ))
goaway ( z_root () . " / " . $return_path );
killme ();
}
}
else {
2019-07-11 01:43:20 +00:00
// fixme - $webpage could also be a wiki page or article and require a different permission to be checked.
2016-04-19 03:38:38 +00:00
if ( ! perm_is_allowed ( $profile_uid , $observer [ 'xchan_hash' ],( $webpage ) ? 'write_pages' : 'post_wall' )) {
notice ( t ( 'Permission denied.' ) . EOL ) ;
2016-12-03 21:31:56 +00:00
if ( $api_source )
return ( [ 'success' => false , 'message' => 'permission denied' ] );
2016-04-19 03:38:38 +00:00
if ( x ( $_REQUEST , 'return' ))
goaway ( z_root () . " / " . $return_path );
killme ();
}
}
2019-07-11 01:43:20 +00:00
// check if this author is being moderated through the 'moderated' (negative) permission
// when posting wall-to-wall
2019-02-28 23:14:22 +00:00
if ( $moderated === false && intval ( $uid ) !== intval ( $profile_uid )) {
$moderated = perm_is_allowed ( $profile_uid , $observer [ 'xchan_hash' ], 'moderated' );
}
2019-07-11 01:43:20 +00:00
// If this is a comment, check the moderated permission of the parent; who may be on another site
2019-02-28 23:14:22 +00:00
$remote_moderated = (( $parent ) ? their_perms_contains ( $profile_uid , $parent_item [ 'owner_xchan' ], 'moderated' ) : false );
2019-07-11 01:43:20 +00:00
if ( $remote_moderated ) {
2019-02-28 23:14:22 +00:00
notice ( t ( 'Comment may be moderated.' ) . EOL );
}
2016-04-19 03:38:38 +00:00
// is this an edited post?
$orig_post = null ;
2019-07-11 01:43:20 +00:00
if ( $namespace && $remote_id ) {
2016-04-19 03:38:38 +00:00
// It wasn't an internally generated post - see if we've got an item matching this remote service id
2016-06-14 02:58:24 +00:00
$i = q ( " select iid from iconfig where cat = 'system' and k = '%s' and v = '%s' limit 1 " ,
2016-04-19 03:38:38 +00:00
dbesc ( $namespace ),
dbesc ( $remote_id )
);
if ( $i )
$post_id = $i [ 0 ][ 'iid' ];
}
$iconfig = null ;
if ( $post_id ) {
2016-10-04 04:48:53 +00:00
$i = q ( " SELECT * FROM item WHERE uid = %d AND id = %d LIMIT 1 " ,
2016-04-19 03:38:38 +00:00
intval ( $profile_uid ),
intval ( $post_id )
);
if ( ! count ( $i ))
killme ();
$orig_post = $i [ 0 ];
$iconfig = q ( " select * from iconfig where iid = %d " ,
intval ( $post_id )
);
}
if ( ! $channel ) {
if ( $uid && $uid == $profile_uid ) {
2018-11-12 03:13:11 +00:00
$channel = App :: get_channel ();
2016-04-19 03:38:38 +00:00
}
else {
// posting as yourself but not necessarily to a channel you control
$r = q ( " select * from channel left join account on channel_account_id = account_id where channel_id = %d LIMIT 1 " ,
intval ( $profile_uid )
);
if ( $r )
$channel = $r [ 0 ];
}
}
if ( ! $channel ) {
logger ( " mod_item: no channel. " );
2016-12-03 21:31:56 +00:00
if ( $api_source )
return ( [ 'success' => false , 'message' => 'no channel' ] );
2016-04-19 03:38:38 +00:00
if ( x ( $_REQUEST , 'return' ))
goaway ( z_root () . " / " . $return_path );
killme ();
}
$owner_xchan = null ;
$r = q ( " select * from xchan where xchan_hash = '%s' limit 1 " ,
dbesc ( $channel [ 'channel_hash' ])
);
if ( $r && count ( $r )) {
$owner_xchan = $r [ 0 ];
}
else {
logger ( " mod_item: no owner. " );
2016-12-03 21:31:56 +00:00
if ( $api_source )
return ( [ 'success' => false , 'message' => 'no owner' ] );
2016-04-19 03:38:38 +00:00
if ( x ( $_REQUEST , 'return' ))
goaway ( z_root () . " / " . $return_path );
killme ();
}
$walltowall = false ;
$walltowall_comment = false ;
2017-06-19 05:25:41 +00:00
if ( $remote_xchan && ! $moderated )
2016-04-19 03:38:38 +00:00
$observer = $remote_observer ;
if ( $observer ) {
logger ( 'mod_item: post accepted from ' . $observer [ 'xchan_name' ] . ' for ' . $owner_xchan [ 'xchan_name' ], LOGGER_DEBUG );
// wall-to-wall detection.
// For top-level posts, if the author and owner are different it's a wall-to-wall
// For comments, We need to additionally look at the parent and see if it's a wall post that originated locally.
if ( $observer [ 'xchan_name' ] != $owner_xchan [ 'xchan_name' ]) {
if (( $parent_item ) && ( $parent_item [ 'item_wall' ] && $parent_item [ 'item_origin' ])) {
$walltowall_comment = true ;
$walltowall = true ;
}
if ( ! $parent ) {
$walltowall = true ;
}
}
}
2019-07-11 01:43:20 +00:00
$acl = new AccessControl ( $channel );
2016-07-20 03:49:54 +00:00
2019-07-11 01:43:20 +00:00
$view_policy = PermissionLimits :: Get ( $channel [ 'channel_id' ], 'view_stream' );
2021-03-12 04:36:42 +00:00
$comment_policy = (( isset ( $_REQUEST [ 'comments_from' ]) && intval ( $_REQUEST [ 'comments_from' ])) ? intval ( $_REQUEST [ 'comments_from' ]) : PermissionLimits :: Get ( $channel [ 'channel_id' ], 'post_comments' ));
2020-11-24 03:14:44 +00:00
2016-07-20 03:49:54 +00:00
$public_policy = (( x ( $_REQUEST , 'public_policy' )) ? escape_tags ( $_REQUEST [ 'public_policy' ]) : map_scope ( $view_policy , true ));
2016-04-19 03:38:38 +00:00
if ( $webpage )
$public_policy = '' ;
if ( $public_policy )
$private = 1 ;
if ( $orig_post ) {
2020-11-30 00:38:45 +00:00
2016-04-19 03:38:38 +00:00
$private = 0 ;
2021-03-28 19:14:47 +00:00
// webpages and unpublished drafts are allowed to change ACLs after the fact. Normal conversation items aren't.
if ( $webpage || intval ( $orig_post [ 'item_unpublished' ])) {
2016-04-19 03:38:38 +00:00
$acl -> set_from_array ( $_REQUEST );
}
else {
$acl -> set ( $orig_post );
$public_policy = $orig_post [ 'public_policy' ];
$private = $orig_post [ 'item_private' ];
}
2019-06-19 02:21:31 +00:00
if ( $public_policy || $acl -> is_private ()) {
$private = (( $private ) ? $private : 1 );
}
2016-04-19 03:38:38 +00:00
$location = $orig_post [ 'location' ];
$coord = $orig_post [ 'coord' ];
$verb = $orig_post [ 'verb' ];
$app = $orig_post [ 'app' ];
$title = escape_tags ( trim ( $_REQUEST [ 'title' ]));
2018-08-16 01:56:18 +00:00
$summary = trim ( $_REQUEST [ 'summary' ]);
2016-04-19 03:38:38 +00:00
$body = trim ( $_REQUEST [ 'body' ]);
2020-11-27 21:30:48 +00:00
$item_flags = $orig_post [ 'item_flags' ];
$item_origin = $orig_post [ 'item_origin' ];
$item_unseen = $orig_post [ 'item_unseen' ];
$item_starred = $orig_post [ 'item_starred' ];
$item_uplink = $orig_post [ 'item_uplink' ];
$item_wall = $orig_post [ 'item_wall' ];
2016-04-19 03:38:38 +00:00
$item_thread_top = $orig_post [ 'item_thread_top' ];
2020-11-27 21:30:48 +00:00
$item_notshown = $orig_post [ 'item_notshown' ];
$item_nsfw = $orig_post [ 'item_nsfw' ];
$item_relay = $orig_post [ 'item_relay' ];
2016-04-19 03:38:38 +00:00
$item_mentionsme = $orig_post [ 'item_mentionsme' ];
2020-11-27 21:30:48 +00:00
$item_nocomment = $orig_post [ 'item_nocomment' ];
$item_obscured = $orig_post [ 'item_obscured' ];
$item_verified = $orig_post [ 'item_verified' ];
$item_retained = $orig_post [ 'item_retained' ];
$item_rss = $orig_post [ 'item_rss' ];
$item_deleted = $orig_post [ 'item_deleted' ];
$item_type = $orig_post [ 'item_type' ];
$item_hidden = $orig_post [ 'item_hidden' ];
$item_delayed = $orig_post [ 'item_delayed' ];
$item_pending_remove = $orig_post [ 'item_pending_remove' ];
$item_blocked = $orig_post [ 'item_blocked' ];
2016-04-19 03:38:38 +00:00
$postopts = $orig_post [ 'postopts' ];
2020-11-30 00:38:45 +00:00
$created = (( intval ( $orig_post [ 'item_unpublished' ])) ? $created : $orig_post [ 'created' ]);
$expires = (( intval ( $orig_post [ 'item_unpublished' ])) ? NULL_DATE : $orig_post [ 'expires' ]);
2016-04-19 03:38:38 +00:00
$mid = $orig_post [ 'mid' ];
$parent_mid = $orig_post [ 'parent_mid' ];
$plink = $orig_post [ 'plink' ];
2020-11-30 00:38:45 +00:00
2016-04-19 03:38:38 +00:00
}
else {
if ( ! $walltowall ) {
if (( array_key_exists ( 'contact_allow' , $_REQUEST ))
|| ( array_key_exists ( 'group_allow' , $_REQUEST ))
|| ( array_key_exists ( 'contact_deny' , $_REQUEST ))
|| ( array_key_exists ( 'group_deny' , $_REQUEST ))) {
$acl -> set_from_array ( $_REQUEST );
}
elseif ( ! $api_source ) {
// if no ACL has been defined and we aren't using the API, the form
// didn't send us any parameters. This means there's no ACL or it has
// been reset to the default audience.
// If $api_source is set and there are no ACL parameters, we default
// to the channel permissions which were set in the ACL contructor.
$acl -> set ( array ( 'allow_cid' => '' , 'allow_gid' => '' , 'deny_cid' => '' , 'deny_gid' => '' ));
}
}
2021-03-12 01:09:22 +00:00
$location = (( isset ( $_REQUEST [ 'location' ])) ? notags ( trim ( $_REQUEST [ 'location' ])) : EMPTY_STR );
$coord = (( isset ( $_REQUEST [ 'coord' ])) ? notags ( trim ( $_REQUEST [ 'coord' ])) : EMPTY_STR );
$verb = (( isset ( $_REQUEST [ 'verb' ])) ? notags ( trim ( $_REQUEST [ 'verb' ])) : EMPTY_STR );
$title = (( isset ( $_REQUEST [ 'title' ])) ? escape_tags ( trim ( $_REQUEST [ 'title' ])) : EMPTY_STR );
$summary = (( isset ( $_REQUEST [ 'summary' ])) ? trim ( $_REQUEST [ 'summary' ]) : EMPTY_STR );;
$body = (( isset ( $_REQUEST [ 'body' ])) ? trim ( $_REQUEST [ 'body' ]) : EMPTY_STR );
$body .= (( isset ( $_REQUEST [ 'attachment' ])) ? trim ( $_REQUEST [ 'attachment' ]) : EMPTY_STR );
2016-04-19 03:38:38 +00:00
$postopts = '' ;
2017-01-18 01:41:19 +00:00
$allow_empty = (( array_key_exists ( 'allow_empty' , $_REQUEST )) ? intval ( $_REQUEST [ 'allow_empty' ]) : 0 );
2021-03-12 01:09:22 +00:00
$private = (( isset ( $private ) && $private ) ? $private : intval ( $acl -> is_private () || ( $public_policy )));
2016-04-19 03:38:38 +00:00
// If this is a comment, set the permissions from the parent.
if ( $parent_item ) {
$private = 0 ;
$acl -> set ( $parent_item );
2019-06-19 02:21:31 +00:00
$private = (( intval ( $parent_item [ 'item_private' ]) ? $parent_item [ 'item_private' ] : $acl -> is_private ()));
2016-04-19 03:38:38 +00:00
$public_policy = $parent_item [ 'public_policy' ];
$owner_hash = $parent_item [ 'owner_xchan' ];
2018-04-05 23:58:37 +00:00
$webpage = $parent_item [ 'item_type' ];
2016-04-19 03:38:38 +00:00
}
2017-01-18 01:41:19 +00:00
if (( ! $allow_empty ) && ( ! strlen ( $body ))) {
2016-04-19 03:38:38 +00:00
if ( $preview )
killme ();
info ( t ( 'Empty post discarded.' ) . EOL );
2016-12-03 21:31:56 +00:00
if ( $api_source )
return ( [ 'success' => false , 'message' => 'no content' ] );
2016-04-19 03:38:38 +00:00
if ( x ( $_REQUEST , 'return' ))
goaway ( z_root () . " / " . $return_path );
killme ();
}
}
2019-03-25 05:46:31 +00:00
if ( Apps :: system_app_installed ( $profile_uid , 'Expire Posts' )) {
2016-04-19 03:38:38 +00:00
if ( x ( $_REQUEST , 'expire' )) {
$expires = datetime_convert ( date_default_timezone_get (), 'UTC' , $_REQUEST [ 'expire' ]);
if ( $expires <= datetime_convert ())
$expires = NULL_DATE ;
}
}
2017-01-11 20:31:55 +00:00
2016-04-19 03:38:38 +00:00
$mimetype = notags ( trim ( $_REQUEST [ 'mimetype' ]));
if ( ! $mimetype )
$mimetype = 'text/bbcode' ;
2017-03-15 00:07:29 +00:00
$execflag = (( intval ( $uid ) == intval ( $profile_uid )
&& ( $channel [ 'channel_pageflags' ] & PAGE_ALLOWCODE )) ? true : false );
2016-04-19 03:38:38 +00:00
if ( $preview ) {
2018-08-16 01:56:18 +00:00
$summary = z_input_filter ( $summary , $mimetype , $execflag );
2017-03-15 00:07:29 +00:00
$body = z_input_filter ( $body , $mimetype , $execflag );
2016-04-19 03:38:38 +00:00
}
2020-04-30 00:35:55 +00:00
2018-01-15 02:22:58 +00:00
2018-08-16 01:56:18 +00:00
$arr = [ 'profile_uid' => $profile_uid , 'summary' => $summary , 'content' => $body , 'mimetype' => $mimetype ];
2018-01-15 02:22:58 +00:00
call_hooks ( 'post_content' , $arr );
2018-08-16 01:56:18 +00:00
$summary = $arr [ 'summary' ];
2018-01-15 02:22:58 +00:00
$body = $arr [ 'content' ];
$mimetype = $arr [ 'mimetype' ];
2016-04-19 03:38:38 +00:00
$gacl = $acl -> get ();
$str_contact_allow = $gacl [ 'allow_cid' ];
$str_group_allow = $gacl [ 'allow_gid' ];
$str_contact_deny = $gacl [ 'deny_cid' ];
$str_group_deny = $gacl [ 'deny_gid' ];
2020-04-30 00:35:55 +00:00
2020-08-28 11:10:34 +00:00
// if the acl contains a single contact and it's a group, add a mention. This is for compatibility
// with other groups implementations which require a mention to trigger group delivery.
if (( $str_contact_allow ) && ( ! $str_group_allow ) && ( ! $str_contact_deny ) && ( ! $str_group_deny )) {
$cida = expand_acl ( $str_contact_allow );
if ( count ( $cida ) === 1 ) {
$netgroups = get_forum_channels ( $profile_uid , 1 );
if ( $netgroups ) {
foreach ( $netgroups as $ng ) {
if ( $ng [ 'xchan_hash' ] == $cida [ 0 ]) {
2021-03-12 04:00:59 +00:00
if ( ! is_array ( $post_tags )) {
2020-08-28 11:10:34 +00:00
$post_tags = [];
}
$post_tags [] = array (
'uid' => $profile_uid ,
'ttype' => TERM_MENTION ,
'otype' => TERM_OBJ_POST ,
'term' => $ng [ 'xchan_name' ],
'url' => $ng [ 'xchan_url' ]
);
}
}
}
}
}
2020-04-30 00:35:55 +00:00
$groupww = false ;
// if this is a wall-to-wall post to a group, turn it into a direct message
$role = get_pconfig ( $profile_uid , 'system' , 'permissions_role' );
$rolesettings = PermissionRoles :: role_perms ( $role );
$channel_type = isset ( $rolesettings [ 'channel_type' ]) ? $rolesettings [ 'channel_type' ] : 'normal' ;
$is_group = (( $channel_type === 'group' ) ? true : false );
if (( $is_group ) && ( $walltowall ) && ( ! $walltowall_comment )) {
$groupww = true ;
$str_contact_allow = $owner_xchan [ 'xchan_hash' ];
$str_group_allow = '' ;
}
2016-04-19 03:38:38 +00:00
if ( $mimetype === 'text/bbcode' ) {
2020-05-13 03:58:37 +00:00
2016-04-19 03:38:38 +00:00
// BBCODE alert: the following functions assume bbcode input
// and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.)
// we may need virtual or template classes to implement the possible alternatives
2020-01-22 04:54:05 +00:00
2018-10-13 07:37:22 +00:00
if ( strpos ( $body , '[/summary]' ) !== false ) {
$match = '' ;
$cnt = preg_match ( " / \ [summary \ ](.*?) \ [ \ /summary \ ]/ism " , $body , $match );
if ( $cnt ) {
$summary .= $match [ 1 ];
}
2020-05-06 04:34:31 +00:00
$body_content = preg_replace ( " /^(.*?) \ [summary \ ](.*?) \ [ \ /summary \ ]/ism " , '' , $body );
2018-10-13 07:37:22 +00:00
$body = trim ( $body_content );
}
2018-08-16 01:56:18 +00:00
$summary = cleanup_bbcode ( $summary );
2016-12-14 04:01:38 +00:00
$body = cleanup_bbcode ( $body );
2016-04-19 03:38:38 +00:00
// Look for tags and linkify them
2020-05-13 03:58:37 +00:00
$summary_tags = linkify_tags ( $summary , ( $uid ) ? $uid : $profile_uid );
$body_tags = linkify_tags ( $body , ( $uid ) ? $uid : $profile_uid );
2020-05-14 00:39:48 +00:00
$comment_tags = linkify_tags ( $hidden_mentions , ( $uid ) ? $uid : $profile_uid );
foreach ( [ $summary_tags , $body_tags , $comment_tags ] as $results ) {
2018-08-16 01:56:18 +00:00
2020-05-13 03:58:37 +00:00
if ( $results ) {
2016-04-19 03:38:38 +00:00
2020-05-13 03:58:37 +00:00
// Set permissions based on tag replacements
set_linkified_perms ( $results , $str_contact_allow , $str_group_allow , $profile_uid , $parent_item , $private );
2016-04-19 03:38:38 +00:00
2020-05-13 03:58:37 +00:00
if ( ! isset ( $post_tags )) {
$post_tags = [];
}
foreach ( $results as $result ) {
$success = $result [ 'success' ];
if ( $success [ 'replaced' ]) {
2020-05-14 01:07:35 +00:00
// suppress duplicate mentions/tags
$already_tagged = false ;
foreach ( $post_tags as $pt ) {
2020-05-14 01:48:51 +00:00
if ( $pt [ 'term' ] === $success [ 'term' ] && $pt [ 'url' ] === $success [ 'url' ] && intval ( $pt [ 'ttype' ]) === intval ( $success [ 'termtype' ])) {
2020-05-14 01:07:35 +00:00
$already_tagged = true ;
break ;
}
}
if ( $already_tagged ) {
continue ;
}
2020-05-13 03:58:37 +00:00
$post_tags [] = array (
'uid' => $profile_uid ,
'ttype' => $success [ 'termtype' ],
'otype' => TERM_OBJ_POST ,
'term' => $success [ 'term' ],
'url' => $success [ 'url' ]
2020-03-24 01:35:19 +00:00
);
2020-05-13 03:58:37 +00:00
// support #collection syntax to post to a collection
// this is accomplished by adding a pcategory tag for each collection target
// this is checked inside tag_deliver() to create a second delivery chain
if ( $success [ 'termtype' ] === TERM_HASHTAG ) {
$r = q ( " select xchan_url from channel left join xchan on xchan_hash = channel_hash where channel_address = '%s' and channel_parent = '%s' and channel_removed = 0 " ,
dbesc ( $success [ 'term' ]),
dbesc ( get_observer_hash ())
);
if ( $r ) {
$post_tags [] = [
'uid' => $profile_uid ,
'ttype' => TERM_PCATEGORY ,
'otype' => TERM_OBJ_POST ,
'term' => $success [ 'term' ] . '@' . App :: get_hostname (),
'url' => $r [ 0 ][ 'xchan_url' ]
];
}
2020-03-24 01:35:19 +00:00
}
}
}
}
}
/**
* process collections selected manually
*/
if ( array_key_exists ( 'collections' , $_REQUEST ) && is_array ( $_REQUEST [ 'collections' ]) && count ( $_REQUEST [ 'collections' ])) {
foreach ( $_REQUEST [ 'collections' ] as $clct ) {
$r = q ( " select xchan_url, xchan_hash from xchan left join hubloc on hubloc_hash = xchan_hash where hubloc_addr = '%s' limit 1 " ,
dbesc ( $clct )
);
if ( $r ) {
if ( ! isset ( $post_tags )) {
$post_tags = [];
}
$post_tags [] = [
'uid' => $profile_uid ,
'ttype' => TERM_PCATEGORY ,
'otype' => TERM_OBJ_POST ,
'term' => $clct ,
'url' => $r [ 0 ][ 'xchan_url' ]
];
2016-04-19 03:38:38 +00:00
}
}
}
2019-06-28 01:39:08 +00:00
if (( $str_contact_allow ) && ( ! $str_group_allow )) {
// direct message - private between individual channels but not groups
$private = 2 ;
}
2019-10-16 03:16:25 +00:00
if ( $private ) {
2020-02-01 21:32:12 +00:00
// for edited posts, re-use any existing OCAP token (if found).
// Otherwise generate a new one.
if ( $iconfig ) {
foreach ( $iconfig as $cfg ) {
2020-02-01 21:34:21 +00:00
if ( $cfg [ 'cat' ] === 'ocap' && $cfg [ 'k' ] === 'relay' ) {
2020-02-01 21:32:12 +00:00
$token = $cfg [ 'v' ];
}
}
}
if ( ! $token ) {
$token = new_token ();
}
2019-10-16 03:16:25 +00:00
}
2016-04-19 03:38:38 +00:00
/**
*
* When a photo was uploaded into the message using the ( profile wall ) ajax
* uploader , The permissions are initially set to disallow anybody but the
* owner from seeing it . This is because the permissions may not yet have been
* set for the post . If it ' s private , the photo permissions should be set
* appropriately . But we didn ' t know the final permissions on the post until
* now . So now we ' ll look for links of uploaded photos and attachments that are in the
* post and set them to the same permissions as the post itself .
*
* If the post was end - to - end encrypted we can ' t find images and attachments in the body ,
* use our media_str input instead which only contains these elements - but only do this
* when encrypted content exists because the photo / attachment may have been removed from
* the post and we should keep it private . If it ' s encrypted we have no way of knowing
* so we ' ll set the permissions regardless and realise that the media may not be
* referenced in the post .
*
*/
if ( ! $preview ) {
2020-02-02 23:12:20 +00:00
fix_attached_permissions ( $profile_uid ,(( strpos ( $body , '[/crypt]' )) ? $_POST [ 'media_str' ] : $body ), $str_contact_allow , $str_group_allow , $str_contact_deny , $str_group_deny , $token );
2016-04-19 03:38:38 +00:00
}
$attachments = '' ;
$match = false ;
if ( preg_match_all ( '/(\[attachment\](.*?)\[\/attachment\])/' , $body , $match )) {
2021-03-15 05:10:44 +00:00
$attachments = [];
2016-04-19 03:38:38 +00:00
$i = 0 ;
foreach ( $match [ 2 ] as $mtch ) {
$attach_link = '' ;
$hash = substr ( $mtch , 0 , strpos ( $mtch , ',' ));
$rev = intval ( substr ( $mtch , strpos ( $mtch , ',' )));
2017-08-03 19:36:26 +00:00
$r = attach_by_hash_nodata ( $hash , $observer [ 'xchan_hash' ], $rev );
2016-04-19 03:38:38 +00:00
if ( $r [ 'success' ]) {
$attachments [] = array (
'href' => z_root () . '/attach/' . $r [ 'data' ][ 'hash' ],
'length' => $r [ 'data' ][ 'filesize' ],
'type' => $r [ 'data' ][ 'filetype' ],
'title' => urlencode ( $r [ 'data' ][ 'filename' ]),
'revision' => $r [ 'data' ][ 'revision' ]
);
}
$body = str_replace ( $match [ 1 ][ $i ], $attach_link , $body );
$i ++ ;
}
}
2018-02-13 05:43:04 +00:00
if ( preg_match_all ( '/(\[share=(.*?)\](.*?)\[\/share\])/' , $body , $match )) {
// process share by id
$i = 0 ;
foreach ( $match [ 2 ] as $mtch ) {
2018-02-14 04:07:57 +00:00
$reshare = new \Zotlabs\Lib\Share ( $mtch );
$body = str_replace ( $match [ 1 ][ $i ], $reshare -> bbcode (), $body );
2018-02-13 05:43:04 +00:00
$i ++ ;
}
}
2016-04-19 03:38:38 +00:00
}
// BBCODE end alert
if ( strlen ( $categories )) {
2019-04-03 23:22:03 +00:00
if ( ! isset ( $post_tags )) {
$post_tags = [];
}
2016-04-19 03:38:38 +00:00
$cats = explode ( ',' , $categories );
foreach ( $cats as $cat ) {
2017-08-30 02:38:07 +00:00
if ( $webpage == ITEM_TYPE_CARD ) {
$catlink = z_root () . '/cards/' . $channel [ 'channel_address' ] . '?f=&cat=' . urlencode ( trim ( $cat ));
}
2017-11-22 23:39:06 +00:00
elseif ( $webpage == ITEM_TYPE_ARTICLE ) {
$catlink = z_root () . '/articles/' . $channel [ 'channel_address' ] . '?f=&cat=' . urlencode ( trim ( $cat ));
}
2017-08-30 02:38:07 +00:00
else {
$catlink = $owner_xchan [ 'xchan_url' ] . '?f=&cat=' . urlencode ( trim ( $cat ));
}
2016-04-19 03:38:38 +00:00
$post_tags [] = array (
'uid' => $profile_uid ,
2016-06-01 04:45:33 +00:00
'ttype' => TERM_CATEGORY ,
2016-04-19 03:38:38 +00:00
'otype' => TERM_OBJ_POST ,
'term' => trim ( $cat ),
2017-08-30 02:38:07 +00:00
'url' => $catlink
2016-04-19 03:38:38 +00:00
);
}
}
if ( $orig_post ) {
// preserve original tags
2016-06-01 04:45:33 +00:00
$t = q ( " select * from term where oid = %d and otype = %d and uid = %d and ttype in ( %d, %d, %d ) " ,
2016-04-19 03:38:38 +00:00
intval ( $orig_post [ 'id' ]),
intval ( TERM_OBJ_POST ),
intval ( $profile_uid ),
intval ( TERM_UNKNOWN ),
intval ( TERM_FILE ),
intval ( TERM_COMMUNITYTAG )
);
if ( $t ) {
2019-04-03 23:22:03 +00:00
if ( ! isset ( $post_tags )) {
$post_tags = [];
}
2016-04-19 03:38:38 +00:00
foreach ( $t as $t1 ) {
$post_tags [] = array (
'uid' => $profile_uid ,
2017-10-02 10:54:25 +00:00
'ttype' => $t1 [ 'ttype' ],
2016-04-19 03:38:38 +00:00
'otype' => TERM_OBJ_POST ,
'term' => $t1 [ 'term' ],
'url' => $t1 [ 'url' ],
);
}
}
}
$item_unseen = (( local_channel () != $profile_uid ) ? 1 : 0 );
2021-03-12 01:09:22 +00:00
$item_wall = (( isset ( $_REQUEST [ 'type' ]) && ( $_REQUEST [ 'type' ] === 'wall' || $_REQUEST [ 'type' ] === 'wall-comment' )) ? 1 : 0 );
2016-04-19 03:38:38 +00:00
$item_origin = (( $origin ) ? 1 : 0 );
2016-08-14 10:41:12 +00:00
$item_nocomment = (( $nocomment ) ? 1 : 0 );
2016-04-19 03:38:38 +00:00
// determine if this is a wall post
if ( $parent ) {
$item_wall = $parent_item [ 'item_wall' ];
}
else {
if ( ! $webpage ) {
$item_wall = 1 ;
}
}
if ( $moderated )
$item_blocked = ITEM_MODERATED ;
if ( ! strlen ( $verb ))
$verb = ACTIVITY_POST ;
$notify_type = (( $parent ) ? 'comment-new' : 'wall-new' );
2021-03-12 01:09:22 +00:00
if ( ! ( isset ( $mid ) && $mid )) {
2019-03-18 04:18:32 +00:00
if ( $message_id ) {
$mid = $message_id ;
}
else {
$uuid = new_uuid ();
$mid = z_root () . '/item/' . $uuid ;
}
2016-04-19 03:38:38 +00:00
}
2017-01-19 23:37:30 +00:00
2020-02-27 04:48:28 +00:00
if ( $is_poll ) {
$poll = [
'question' => $body ,
'answers' => $_REQUEST [ 'poll_answers' ],
'multiple_answers' => $_REQUEST [ 'poll_multiple_answers' ],
'expire_value' => $_REQUEST [ 'poll_expire_value' ],
'expire_unit' => $_REQUEST [ 'poll_expire_unit' ]
];
$obj = $this -> extract_poll_data ( $poll , [ 'item_private' => $private , 'allow_cid' => $str_contact_allow , 'allow_gid' => $str_contact_deny ]);
}
else {
$obj = $this -> extract_bb_poll_data ( $body ,[ 'item_private' => $private , 'allow_cid' => $str_contact_allow , 'allow_gid' => $str_contact_deny ]);
}
2020-10-20 23:13:30 +00:00
2020-02-06 05:58:50 +00:00
if ( $obj ) {
2020-02-07 02:50:18 +00:00
$obj [ 'url' ] = $mid ;
2020-02-06 05:58:50 +00:00
$obj [ 'attributedTo' ] = channel_url ( $channel );
$datarray [ 'obj' ] = $obj ;
$obj_type = 'Question' ;
2020-10-20 23:13:30 +00:00
if ( $obj [ 'endTime' ]) {
$d = datetime_convert ( 'UTC' , 'UTC' , $obj [ 'endTime' ]);
if ( $d > NULL_DATE ) {
$comments_closed = $d ;
}
}
2020-02-06 05:58:50 +00:00
}
2016-04-19 03:38:38 +00:00
if ( ! $parent_mid ) {
$parent_mid = $mid ;
}
if ( $parent_item )
$parent_mid = $parent_item [ 'mid' ];
2017-08-24 23:58:39 +00:00
2017-08-25 01:15:19 +00:00
// Fallback so that we alway have a thr_parent
if ( ! $thr_parent )
$thr_parent = $mid ;
$item_thread_top = (( ! $parent ) ? 1 : 0 );
2018-07-31 06:55:09 +00:00
// fix permalinks for cards, etc.
2016-04-19 03:38:38 +00:00
2017-08-25 01:15:19 +00:00
if ( $webpage == ITEM_TYPE_CARD ) {
2019-02-19 22:59:22 +00:00
$plink = z_root () . '/cards/' . $channel [ 'channel_address' ] . '/' . (( $pagetitle ) ? $pagetitle : $uuid );
2017-08-24 02:18:44 +00:00
}
2017-08-24 23:58:39 +00:00
if (( $parent_item ) && ( $parent_item [ 'item_type' ] == ITEM_TYPE_CARD )) {
$r = q ( " select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.iid = %d limit 1 " ,
intval ( $parent_item [ 'id' ])
);
if ( $r ) {
$plink = z_root () . '/cards/' . $channel [ 'channel_address' ] . '/' . $r [ 0 ][ 'v' ];
}
}
2017-08-24 02:18:44 +00:00
2017-11-22 23:39:06 +00:00
if ( $webpage == ITEM_TYPE_ARTICLE ) {
2019-02-19 22:59:22 +00:00
$plink = z_root () . '/articles/' . $channel [ 'channel_address' ] . '/' . (( $pagetitle ) ? $pagetitle : $uuid );
2017-11-22 23:39:06 +00:00
}
if (( $parent_item ) && ( $parent_item [ 'item_type' ] == ITEM_TYPE_ARTICLE )) {
$r = q ( " select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.iid = %d limit 1 " ,
intval ( $parent_item [ 'id' ])
);
if ( $r ) {
$plink = z_root () . '/articles/' . $channel [ 'channel_address' ] . '/' . $r [ 0 ][ 'v' ];
}
}
2021-03-12 04:00:59 +00:00
if (( ! ( isset ( $plink ) && $plink )) && $item_thread_top ) {
2019-03-22 04:57:43 +00:00
$plink = z_root () . '/item/' . $uuid ;
2016-04-19 03:38:38 +00:00
}
2019-06-19 02:21:31 +00:00
2021-03-12 04:00:59 +00:00
if ( array_path_exists ( 'obj/id' , $datarray )) {
2020-01-22 04:54:05 +00:00
$datarray [ 'obj' ][ 'id' ] = $mid ;
}
2019-10-01 02:25:48 +00:00
2016-09-20 22:53:30 +00:00
$datarray [ 'aid' ] = $channel [ 'channel_account_id' ];
$datarray [ 'uid' ] = $profile_uid ;
2019-02-19 22:59:22 +00:00
$datarray [ 'uuid' ] = $uuid ;
2016-09-20 22:53:30 +00:00
$datarray [ 'owner_xchan' ] = (( $owner_hash ) ? $owner_hash : $owner_xchan [ 'xchan_hash' ]);
$datarray [ 'author_xchan' ] = $observer [ 'xchan_hash' ];
$datarray [ 'created' ] = $created ;
2020-11-30 00:38:45 +00:00
$datarray [ 'edited' ] = (( $orig_post && ( ! intval ( $orig_post [ 'item_unpublished' ]))) ? datetime_convert () : $created );
2016-09-20 22:53:30 +00:00
$datarray [ 'expires' ] = $expires ;
2020-11-30 00:38:45 +00:00
$datarray [ 'commented' ] = (( $orig_post && ( ! intval ( $orig_post [ 'item_unpublished' ]))) ? datetime_convert () : $created );
$datarray [ 'received' ] = (( $orig_post && ( ! intval ( $orig_post [ 'item_unpublished' ]))) ? datetime_convert () : $created );
$datarray [ 'changed' ] = (( $orig_post && ( ! intval ( $orig_post [ 'item_unpublished' ]))) ? datetime_convert () : $created );
2020-10-20 23:13:30 +00:00
$datarray [ 'comments_closed' ] = $comments_closed ;
2016-09-20 22:53:30 +00:00
$datarray [ 'mid' ] = $mid ;
$datarray [ 'parent_mid' ] = $parent_mid ;
$datarray [ 'mimetype' ] = $mimetype ;
$datarray [ 'title' ] = $title ;
2018-08-16 01:56:18 +00:00
$datarray [ 'summary' ] = $summary ;
2016-09-20 22:53:30 +00:00
$datarray [ 'body' ] = $body ;
$datarray [ 'app' ] = $app ;
$datarray [ 'location' ] = $location ;
$datarray [ 'coord' ] = $coord ;
$datarray [ 'verb' ] = $verb ;
$datarray [ 'obj_type' ] = $obj_type ;
$datarray [ 'allow_cid' ] = $str_contact_allow ;
$datarray [ 'allow_gid' ] = $str_group_allow ;
$datarray [ 'deny_cid' ] = $str_contact_deny ;
$datarray [ 'deny_gid' ] = $str_group_deny ;
$datarray [ 'attach' ] = $attachments ;
$datarray [ 'thr_parent' ] = $thr_parent ;
$datarray [ 'postopts' ] = $postopts ;
$datarray [ 'item_unseen' ] = intval ( $item_unseen );
$datarray [ 'item_wall' ] = intval ( $item_wall );
$datarray [ 'item_origin' ] = intval ( $item_origin );
$datarray [ 'item_type' ] = $webpage ;
$datarray [ 'item_private' ] = intval ( $private );
$datarray [ 'item_thread_top' ] = intval ( $item_thread_top );
$datarray [ 'item_unseen' ] = intval ( $item_unseen );
$datarray [ 'item_starred' ] = intval ( $item_starred );
$datarray [ 'item_uplink' ] = intval ( $item_uplink );
2020-08-28 11:10:34 +00:00
$datarray [ 'item_consensus' ] = 0 ;
2016-09-20 22:53:30 +00:00
$datarray [ 'item_notshown' ] = intval ( $item_notshown );
$datarray [ 'item_nsfw' ] = intval ( $item_nsfw );
$datarray [ 'item_relay' ] = intval ( $item_relay );
$datarray [ 'item_mentionsme' ] = intval ( $item_mentionsme );
$datarray [ 'item_nocomment' ] = intval ( $item_nocomment );
$datarray [ 'item_obscured' ] = intval ( $item_obscured );
$datarray [ 'item_verified' ] = intval ( $item_verified );
$datarray [ 'item_retained' ] = intval ( $item_retained );
$datarray [ 'item_rss' ] = intval ( $item_rss );
$datarray [ 'item_deleted' ] = intval ( $item_deleted );
$datarray [ 'item_hidden' ] = intval ( $item_hidden );
$datarray [ 'item_unpublished' ] = intval ( $item_unpublished );
$datarray [ 'item_delayed' ] = intval ( $item_delayed );
$datarray [ 'item_pending_remove' ] = intval ( $item_pending_remove );
$datarray [ 'item_blocked' ] = intval ( $item_blocked );
$datarray [ 'layout_mid' ] = $layout_mid ;
$datarray [ 'public_policy' ] = $public_policy ;
$datarray [ 'comment_policy' ] = map_scope ( $comment_policy );
$datarray [ 'term' ] = $post_tags ;
$datarray [ 'plink' ] = $plink ;
$datarray [ 'route' ] = $route ;
2018-08-02 00:58:54 +00:00
2018-03-12 22:47:33 +00:00
// A specific ACL over-rides public_policy completely
if ( ! empty_acl ( $datarray ))
$datarray [ 'public_policy' ] = '' ;
2019-10-01 02:25:48 +00:00
if ( $iconfig ) {
2016-04-19 03:38:38 +00:00
$datarray [ 'iconfig' ] = $iconfig ;
2019-10-01 02:25:48 +00:00
}
if ( $private ) {
2019-10-16 03:16:25 +00:00
IConfig :: set ( $datarray , 'ocap' , 'relay' , $token );
2019-10-01 02:25:48 +00:00
}
2019-10-22 05:46:53 +00:00
if ( ! array_key_exists ( 'obj' , $datarray )) {
$copy = $datarray ;
$copy [ 'author' ] = $observer ;
2020-11-27 00:17:07 +00:00
$datarray [ 'obj' ] = Activity :: encode_item ( $copy ,(( get_config ( 'system' , 'activitypub' , ACTIVITYPUB_ENABLED )) ? true : false ));
2019-10-22 05:46:53 +00:00
}
2019-10-01 02:25:48 +00:00
2020-11-20 01:42:55 +00:00
Activity :: rewrite_mentions ( $datarray );
2016-04-19 03:38:38 +00:00
// preview mode - prepare the body for display and send it via json
if ( $preview ) {
require_once ( 'include/conversation.php' );
$datarray [ 'owner' ] = $owner_xchan ;
$datarray [ 'author' ] = $observer ;
$datarray [ 'attach' ] = json_encode ( $datarray [ 'attach' ]);
2017-06-20 22:53:01 +00:00
$o = conversation ( array ( $datarray ), 'search' , false , 'preview' );
2016-04-19 03:38:38 +00:00
// logger('preview: ' . $o, LOGGER_DEBUG);
echo json_encode ( array ( 'preview' => $o ));
killme ();
}
if ( $orig_post )
$datarray [ 'edit' ] = true ;
2016-07-08 01:47:18 +00:00
// suppress duplicates, *unless* you're editing an existing post. This could get picked up
// as a duplicate if you're editing it very soon after posting it initially and you edited
// some attribute besides the content, such as title or categories.
2016-04-19 03:38:38 +00:00
if ( feature_enabled ( $profile_uid , 'suppress_duplicates' ) && ( ! $orig_post )) {
2016-07-08 01:47:18 +00:00
$z = q ( " select created from item where uid = %d and created > %s - INTERVAL %s and body = '%s' limit 1 " ,
2016-04-19 03:38:38 +00:00
intval ( $profile_uid ),
2016-07-08 01:53:09 +00:00
db_utcnow (),
2016-07-08 01:47:18 +00:00
db_quoteinterval ( '2 MINUTE' ),
2016-07-08 01:51:56 +00:00
dbesc ( $body )
2016-04-19 03:38:38 +00:00
);
if ( $z ) {
2016-07-08 01:47:18 +00:00
$datarray [ 'cancel' ] = 1 ;
notice ( t ( 'Duplicate post suppressed.' ) . EOL );
2020-08-28 11:10:34 +00:00
logger ( 'Duplicate post. Cancelled.' );
2016-04-19 03:38:38 +00:00
}
}
call_hooks ( 'post_local' , $datarray );
if ( x ( $datarray , 'cancel' )) {
logger ( 'mod_item: post cancelled by plugin or duplicate suppressed.' );
if ( $return_path )
goaway ( z_root () . " / " . $return_path );
2016-12-03 21:31:56 +00:00
if ( $api_source )
return ( [ 'success' => false , 'message' => 'operation cancelled' ] );
2016-04-19 03:38:38 +00:00
$json = array ( 'cancel' => 1 );
$json [ 'reload' ] = z_root () . '/' . $_REQUEST [ 'jsreload' ];
echo json_encode ( $json );
killme ();
}
2017-07-11 03:18:33 +00:00
if ( mb_strlen ( $datarray [ 'title' ]) > 191 )
$datarray [ 'title' ] = mb_substr ( $datarray [ 'title' ], 0 , 191 );
2016-04-19 03:38:38 +00:00
2016-06-14 02:58:24 +00:00
if ( $webpage ) {
2018-11-16 04:52:08 +00:00
IConfig :: Set ( $datarray , 'system' , webpage_to_namespace ( $webpage ),
(( $pagetitle ) ? $pagetitle : basename ( $datarray [ 'mid' ])), true );
2016-06-14 02:58:24 +00:00
}
elseif ( $namespace ) {
2018-11-16 04:52:08 +00:00
IConfig :: Set ( $datarray , 'system' , $namespace ,
(( $remote_id ) ? $remote_id : basename ( $datarray [ 'mid' ])), true );
2016-06-14 02:58:24 +00:00
}
2020-11-30 00:38:45 +00:00
if ( intval ( $datarray [ 'item_unpublished' ])) {
$draft_msg = t ( 'Draft saved. Use <a href="stream?draft=1">Drafts</a> app to continue editing.' );
}
2016-06-14 02:58:24 +00:00
2016-04-19 03:38:38 +00:00
if ( $orig_post ) {
$datarray [ 'id' ] = $post_id ;
2016-06-22 01:18:06 +00:00
$x = item_store_update ( $datarray , $execflag );
2016-04-19 03:38:38 +00:00
if ( ! $parent ) {
$r = q ( " select * from item where id = %d " ,
intval ( $post_id )
);
if ( $r ) {
xchan_query ( $r );
$sync_item = fetch_post_tags ( $r );
2018-06-05 01:40:11 +00:00
Libsync :: build_sync_packet ( $profile_uid , array ( 'item' => array ( encode_item ( $sync_item [ 0 ], true ))));
2016-04-19 03:38:38 +00:00
}
}
if ( ! $nopush )
2021-01-08 21:51:40 +00:00
Run :: Summon ( array ( 'Notifier' , 'edit_post' , $post_id ));
2016-04-19 03:38:38 +00:00
2016-12-03 21:31:56 +00:00
if ( $api_source )
return ( $x );
2020-11-30 00:38:45 +00:00
if ( intval ( $datarray [ 'item_unpublished' ])) {
info ( $draft_msg );
}
2016-04-19 03:38:38 +00:00
if (( x ( $_REQUEST , 'return' )) && strlen ( $return_path )) {
logger ( 'return: ' . $return_path );
goaway ( z_root () . " / " . $return_path );
}
killme ();
}
else
$post_id = 0 ;
$post = item_store ( $datarray , $execflag );
$post_id = $post [ 'item_id' ];
2016-09-24 23:20:25 +00:00
$datarray = $post [ 'item' ];
2016-04-19 03:38:38 +00:00
if ( $post_id ) {
logger ( 'mod_item: saved item ' . $post_id );
if ( $parent ) {
2017-07-11 03:18:33 +00:00
// prevent conversations which you are involved from being expired
if ( local_channel ())
retain_item ( $parent );
2020-08-30 09:24:39 +00:00
// only send comment notification if this is a wall-to-wall comment and not a DM,
2016-04-19 03:38:38 +00:00
// otherwise it will happen during delivery
2020-08-30 09:24:39 +00:00
if (( $datarray [ 'owner_xchan' ] != $datarray [ 'author_xchan' ]) && ( intval ( $parent_item [ 'item_wall' ])) && intval ( $datarray [ 'item_private' ]) != 2 ) {
2018-11-16 04:52:08 +00:00
Enotify :: submit ( array (
2016-04-19 03:38:38 +00:00
'type' => NOTIFY_COMMENT ,
'from_xchan' => $datarray [ 'author_xchan' ],
'to_xchan' => $datarray [ 'owner_xchan' ],
'item' => $datarray ,
2017-01-19 23:37:30 +00:00
'link' => z_root () . '/display/' . gen_link_id ( $datarray [ 'mid' ]),
2016-04-19 03:38:38 +00:00
'verb' => ACTIVITY_POST ,
'otype' => 'item' ,
'parent' => $parent ,
'parent_mid' => $parent_item [ 'mid' ]
));
}
}
else {
$parent = $post_id ;
if (( $datarray [ 'owner_xchan' ] != $datarray [ 'author_xchan' ]) && ( $datarray [ 'item_type' ] == ITEM_TYPE_POST )) {
2018-11-16 04:52:08 +00:00
Enotify :: submit ( array (
2016-04-19 03:38:38 +00:00
'type' => NOTIFY_WALL ,
'from_xchan' => $datarray [ 'author_xchan' ],
'to_xchan' => $datarray [ 'owner_xchan' ],
'item' => $datarray ,
2017-01-19 23:37:30 +00:00
'link' => z_root () . '/display/' . gen_link_id ( $datarray [ 'mid' ]),
2016-04-19 03:38:38 +00:00
'verb' => ACTIVITY_POST ,
'otype' => 'item'
));
}
if ( $uid && $uid == $profile_uid && ( is_item_normal ( $datarray ))) {
q ( " update channel set channel_lastpost = '%s' where channel_id = %d " ,
dbesc ( datetime_convert ()),
intval ( $uid )
);
}
}
// photo comments turn the corresponding item visible to the profile wall
// This way we don't see every picture in your new photo album posted to your wall at once.
// They will show up as people comment on them.
if ( intval ( $parent_item [ 'item_hidden' ])) {
$r = q ( " UPDATE item SET item_hidden = 0 WHERE id = %d " ,
intval ( $parent_item [ 'id' ])
);
}
}
else {
logger ( 'mod_item: unable to retrieve post that was just stored.' );
notice ( t ( 'System error. Post not saved.' ) . EOL );
2016-12-03 21:31:56 +00:00
if ( $return_path )
goaway ( z_root () . " / " . $return_path );
if ( $api_source )
return ( [ 'success' => false , 'message' => 'system error' ] );
killme ();
2016-04-19 03:38:38 +00:00
}
2016-06-14 02:58:24 +00:00
2016-04-19 03:38:38 +00:00
if (( $parent ) && ( $parent != $post_id )) {
// Store the comment signature information in case we need to relay to Diaspora
2016-04-21 02:54:26 +00:00
//$ditem = $datarray;
//$ditem['author'] = $observer;
//store_diaspora_comment_sig($ditem,$channel,$parent_item, $post_id, (($walltowall_comment) ? 1 : 0));
2016-04-19 03:38:38 +00:00
}
else {
$r = q ( " select * from item where id = %d " ,
intval ( $post_id )
);
if ( $r ) {
xchan_query ( $r );
$sync_item = fetch_post_tags ( $r );
2018-06-05 01:40:11 +00:00
Libsync :: build_sync_packet ( $profile_uid , array ( 'item' => array ( encode_item ( $sync_item [ 0 ], true ))));
2016-04-19 03:38:38 +00:00
}
}
$datarray [ 'id' ] = $post_id ;
2017-01-19 23:37:30 +00:00
$datarray [ 'llink' ] = z_root () . '/display/' . gen_link_id ( $datarray [ 'mid' ]);
2016-04-19 03:38:38 +00:00
call_hooks ( 'post_local_end' , $datarray );
2019-10-13 23:11:11 +00:00
2020-04-30 00:35:55 +00:00
if ( $groupww ) {
2019-10-13 23:11:11 +00:00
$nopush = false ;
}
2020-04-30 00:35:55 +00:00
if ( ! $nopush ) {
2021-01-08 21:51:40 +00:00
Run :: Summon ( array ( 'Notifier' , $notify_type , $post_id ));
2020-04-30 00:35:55 +00:00
}
2016-04-19 03:38:38 +00:00
logger ( 'post_complete' );
2017-06-19 05:25:41 +00:00
if ( $moderated ) {
2019-02-28 23:14:22 +00:00
info ( t ( 'Your post/comment is awaiting approval.' ) . EOL );
2017-06-19 05:25:41 +00:00
}
2016-04-19 03:38:38 +00:00
// figure out how to return, depending on from whence we came
if ( $api_source )
return $post ;
2020-11-30 00:38:45 +00:00
if ( intval ( $datarray [ 'item_unpublished' ])) {
info ( $draft_msg );
}
2016-04-19 03:38:38 +00:00
if ( $return_path ) {
goaway ( z_root () . " / " . $return_path );
}
$json = array ( 'success' => 1 );
if ( x ( $_REQUEST , 'jsreload' ) && strlen ( $_REQUEST [ 'jsreload' ]))
$json [ 'reload' ] = z_root () . '/' . $_REQUEST [ 'jsreload' ];
logger ( 'post_json: ' . print_r ( $json , true ), LOGGER_DEBUG );
echo json_encode ( $json );
killme ();
// NOTREACHED
}
function get () {
if (( ! local_channel ()) && ( ! remote_channel ()))
return ;
2020-04-01 19:42:20 +00:00
// allow pinned items to be dropped. 'pin-' was prepended to the id of these
// items so that they would have a unique html id even if the pinned item
// was also displayed in a normal conversation on the same web page.
2016-12-26 22:17:40 +00:00
2020-04-01 19:42:20 +00:00
$drop_id = str_replace ( 'pin-' , '' , argv ( 2 ));
if (( argc () == 3 ) && ( argv ( 1 ) === 'drop' ) && intval ( $drop_id )) {
2016-04-19 03:38:38 +00:00
2019-06-17 02:18:49 +00:00
$i = q ( " select * from item where id = %d limit 1 " ,
2020-04-01 19:42:20 +00:00
intval ( $drop_id )
2016-04-19 03:38:38 +00:00
);
if ( $i ) {
$can_delete = false ;
$local_delete = false ;
2019-06-17 02:18:49 +00:00
$regular_delete = false ;
2018-05-03 01:23:42 +00:00
if ( local_channel () && local_channel () == $i [ 0 ][ 'uid' ]) {
2016-04-19 03:38:38 +00:00
$local_delete = true ;
2018-05-03 01:23:42 +00:00
}
2016-04-19 03:38:38 +00:00
2018-05-03 01:23:42 +00:00
$ob_hash = get_observer_hash ();
if ( $ob_hash && ( $ob_hash === $i [ 0 ][ 'author_xchan' ] || $ob_hash === $i [ 0 ][ 'owner_xchan' ] || $ob_hash === $i [ 0 ][ 'source_xchan' ])) {
$can_delete = true ;
2019-06-17 02:18:49 +00:00
$regular_delete = true ;
2018-05-03 01:23:42 +00:00
}
2018-05-03 00:39:12 +00:00
// The site admin can delete any post/item on the site.
// If the item originated on this site+channel the deletion will propagate downstream.
// Otherwise just the local copy is removed.
if ( is_site_admin ()) {
$local_delete = true ;
if ( intval ( $i [ 0 ][ 'item_origin' ]))
$can_delete = true ;
}
2016-04-19 03:38:38 +00:00
if ( ! ( $can_delete || $local_delete )) {
notice ( t ( 'Permission denied.' ) . EOL );
return ;
}
2019-06-12 02:36:55 +00:00
if ( $i [ 0 ][ 'resource_type' ] === 'event' ) {
// delete and sync the event separately
$r = q ( " SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1 " ,
dbesc ( $i [ 0 ][ 'resource_id' ]),
intval ( $i [ 0 ][ 'uid' ])
);
2019-06-17 02:18:49 +00:00
if ( $r && $regular_delete ) {
2019-06-12 02:36:55 +00:00
$sync_event = $r [ 0 ];
q ( " delete from event WHERE event_hash = '%s' AND uid = %d LIMIT 1 " ,
dbesc ( $i [ 0 ][ 'resource_id' ]),
intval ( $i [ 0 ][ 'uid' ])
);
$sync_event [ 'event_deleted' ] = 1 ;
Libsync :: build_sync_packet ( $i [ 0 ][ 'uid' ], array ( 'event' => array ( $sync_event )));
}
}
2019-06-17 02:18:49 +00:00
if ( $i [ 0 ][ 'resource_type' ] === 'photo' ) {
attach_delete ( $i [ 0 ][ 'uid' ], $i [ 0 ][ 'resource_id' ], true );
$ch = channelx_by_n ( $i [ 0 ][ 'uid' ]);
if ( $ch && $regular_delete ) {
$sync = attach_export_data ( $ch , $i [ 0 ][ 'resource_id' ], true );
if ( $sync ) {
Libsync :: build_sync_packet ( $i [ 0 ][ 'uid' ], array ( 'file' => array ( $sync )));
}
}
}
2016-04-19 03:38:38 +00:00
// if this is a different page type or it's just a local delete
// but not by the item author or owner, do a simple deletion
2017-10-02 23:26:33 +00:00
$complex = false ;
2016-04-19 03:38:38 +00:00
if ( intval ( $i [ 0 ][ 'item_type' ]) || ( $local_delete && ( ! $can_delete ))) {
drop_item ( $i [ 0 ][ 'id' ]);
}
else {
// complex deletion that needs to propagate and be performed in phases
drop_item ( $i [ 0 ][ 'id' ], true , DROPITEM_PHASE1 );
2017-10-02 23:26:33 +00:00
$complex = true ;
}
$r = q ( " select * from item where id = %d " ,
intval ( $i [ 0 ][ 'id' ])
);
if ( $r ) {
xchan_query ( $r );
$sync_item = fetch_post_tags ( $r );
2018-06-05 01:40:11 +00:00
Libsync :: build_sync_packet ( $i [ 0 ][ 'uid' ], array ( 'item' => array ( encode_item ( $sync_item [ 0 ], true ))));
2017-10-02 23:26:33 +00:00
}
if ( $complex ) {
2016-04-19 03:38:38 +00:00
tag_deliver ( $i [ 0 ][ 'uid' ], $i [ 0 ][ 'id' ]);
}
}
}
}
function item_check_service_class ( $channel_id , $iswebpage ) {
$ret = array ( 'success' => false , 'message' => '' );
if ( $iswebpage ) {
$r = q ( " select count(i.id) as total from item i
right join channel c on ( i . author_xchan = c . channel_hash and i . uid = c . channel_id )
and i . parent = i . id and i . item_type = % d and i . item_deleted = 0 and i . uid = % d " ,
intval ( ITEM_TYPE_WEBPAGE ),
intval ( $channel_id )
);
}
else {
$r = q ( " select count(id) as total from item where parent = id and item_wall = 1 and uid = %d " . item_normal (),
intval ( $channel_id )
);
}
if ( ! $r ) {
$ret [ 'message' ] = t ( 'Unable to obtain post information from database.' );
return $ret ;
}
2017-12-11 00:41:03 +00:00
2016-04-19 03:38:38 +00:00
if ( ! $iswebpage ) {
2016-04-29 04:02:27 +00:00
$max = engr_units_to_bytes ( service_class_fetch ( $channel_id , 'total_items' ));
2016-04-19 03:38:38 +00:00
if ( ! service_class_allows ( $channel_id , 'total_items' , $r [ 0 ][ 'total' ])) {
$result [ 'message' ] .= upgrade_message () . sprintf ( t ( 'You have reached your limit of %1$.0f top level posts.' ), $max );
return $result ;
}
}
else {
2016-04-29 04:02:27 +00:00
$max = engr_units_to_bytes ( service_class_fetch ( $channel_id , 'total_pages' ));
2016-04-19 03:38:38 +00:00
if ( ! service_class_allows ( $channel_id , 'total_pages' , $r [ 0 ][ 'total' ])) {
$result [ 'message' ] .= upgrade_message () . sprintf ( t ( 'You have reached your limit of %1$.0f webpages.' ), $max );
return $result ;
}
}
$ret [ 'success' ] = true ;
return $ret ;
}
2020-02-27 04:48:28 +00:00
function extract_bb_poll_data ( & $body , $item ) {
2020-01-22 04:54:05 +00:00
$multiple = false ;
2020-01-29 00:42:38 +00:00
if ( strpos ( $body , '[/question]' ) === false && strpos ( $body , '[/answer]' ) === false ) {
return false ;
}
if ( strpos ( $body , '[nobb]' ) !== false ) {
2020-01-22 04:54:05 +00:00
return false ;
}
$obj = [];
$ptr = [];
$matches = null ;
$obj [ 'type' ] = 'Question' ;
2020-02-12 22:14:08 +00:00
if ( preg_match_all ( '/\[answer\](.*?)\[\/answer\]/ism' , $body , $matches , PREG_SET_ORDER )) {
2020-01-22 04:54:05 +00:00
foreach ( $matches as $match ) {
$ptr [] = [ 'name' => $match [ 1 ], 'type' => 'Note' , 'replies' => [ 'type' => 'Collection' , 'totalItems' => 0 ]];
2020-01-29 00:42:38 +00:00
$body = str_replace ( '[answer]' . $match [ 1 ] . '[/answer]' , EMPTY_STR , $body );
2020-01-22 04:54:05 +00:00
}
}
$matches = null ;
2020-02-12 22:14:08 +00:00
if ( preg_match ( '/\[question\](.*?)\[\/question\]/ism' , $body , $matches )) {
2020-01-22 04:54:05 +00:00
$obj [ 'content' ] = bbcode ( $matches [ 1 ]);
$body = str_replace ( '[question]' . $matches [ 1 ] . '[/question]' , $matches [ 1 ], $body );
$obj [ 'oneOf' ] = $ptr ;
}
$matches = null ;
2020-02-12 22:14:08 +00:00
if ( preg_match ( '/\[question=multiple\](.*?)\[\/question\]/ism' , $body , $matches )) {
2020-01-22 04:54:05 +00:00
$obj [ 'content' ] = bbcode ( $matches [ 1 ]);
$body = str_replace ( '[question=multiple]' . $matches [ 1 ] . '[/question]' , $matches [ 1 ], $body );
$obj [ 'anyOf' ] = $ptr ;
}
$matches = null ;
2020-02-12 22:14:08 +00:00
if ( preg_match ( '/\[ends\](.*?)\[\/ends\]/ism' , $body , $matches )) {
2020-01-22 04:54:05 +00:00
$obj [ 'endTime' ] = datetime_convert ( date_default_timezone_get (), 'UTC' , $matches [ 1 ], ATOM_TIME );
2020-02-06 05:58:50 +00:00
$body = str_replace ( '[ends]' . $matches [ 1 ] . '[/ends]' , EMPTY_STR , $body );
2020-01-22 04:54:05 +00:00
}
2020-02-06 05:58:50 +00:00
if ( $item [ 'item_private' ]) {
$obj [ 'to' ] = Activity :: map_acl ( $item );
}
else {
$obj [ 'to' ] = [ ACTIVITY_PUBLIC_INBOX ];
}
2020-01-22 04:54:05 +00:00
return $obj ;
}
2020-02-27 04:48:28 +00:00
function extract_poll_data ( $poll , $item ) {
$multiple = intval ( $poll [ 'multiple_answers' ]);
$expire_value = intval ( $poll [ 'expire_value' ]);
$expire_unit = $poll [ 'expire_unit' ];
$question = $poll [ 'question' ];
$answers = $poll [ 'answers' ];
$obj = [];
$ptr = [];
$obj [ 'type' ] = 'Question' ;
$obj [ 'content' ] = bbcode ( $question );
foreach ( $answers as $answer ) {
if ( trim ( $answer ))
$ptr [] = [ 'name' => escape_tags ( $answer ), 'type' => 'Note' , 'replies' => [ 'type' => 'Collection' , 'totalItems' => 0 ]];
}
if ( $multiple ) {
$obj [ 'anyOf' ] = $ptr ;
}
else {
$obj [ 'oneOf' ] = $ptr ;
}
$obj [ 'endTime' ] = datetime_convert ( date_default_timezone_get (), 'UTC' , 'now + ' . $expire_value . ' ' . $expire_unit , ATOM_TIME );
if ( $item [ 'item_private' ]) {
$obj [ 'to' ] = Activity :: map_acl ( $item );
}
else {
$obj [ 'to' ] = [ ACTIVITY_PUBLIC_INBOX ];
}
return $obj ;
}
2016-04-19 03:38:38 +00:00
}