mirror of
https://codeberg.org/streams/streams.git
synced 2024-09-20 00:55:19 +00:00
more ocap stuff
This commit is contained in:
parent
c8ab4adf76
commit
67c9632d0b
8 changed files with 206 additions and 583 deletions
|
@ -1015,20 +1015,11 @@ class Activity {
|
|||
}
|
||||
}
|
||||
else {
|
||||
|
||||
$ret['inbox'] = z_root() . '/nullbox';
|
||||
if ($c) {
|
||||
$ret['outbox'] = z_root() . '/outbox/' . $c['channel_address'];
|
||||
}
|
||||
else {
|
||||
$ret['outbox'] = z_root() . '/nullbox';
|
||||
}
|
||||
$ret['publicKey'] = [
|
||||
'id' => $p['xchan_url'],
|
||||
'owner' => $p['xchan_url'],
|
||||
'publicKeyPem' => $p['xchan_pubkey']
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
$arr = [ 'xchan' => $p, 'encoded' => $ret, 'activitypub' => $activitypub ];
|
||||
|
@ -1603,219 +1594,6 @@ class Activity {
|
|||
return (($a['width'] > $b['width']) ? -1 : 1);
|
||||
}
|
||||
|
||||
static function create_note($channel,$observer_hash,$act) {
|
||||
|
||||
$s = [];
|
||||
|
||||
// Mastodon only allows visibility in public timelines if the public inbox is listed in the 'to' field.
|
||||
// They are hidden in the public timeline if the public inbox is listed in the 'cc' field.
|
||||
// This is not part of the activitypub protocol - we might change this to show all public posts in pubstream at some point.
|
||||
$pubstream = ((is_array($act->obj) && array_key_exists('to', $act->obj) && in_array(ACTIVITY_PUBLIC_INBOX, $act->obj['to'])) ? true : false);
|
||||
$is_sys_channel = is_sys_channel($channel['channel_id']);
|
||||
|
||||
$parent = ((array_key_exists('inReplyTo',$act->obj)) ? urldecode($act->obj['inReplyTo']) : '');
|
||||
if ($parent) {
|
||||
|
||||
$r = q("select * from item where uid = %d and ( mid = '%s' or mid = '%s' ) limit 1",
|
||||
intval($channel['channel_id']),
|
||||
dbesc($parent),
|
||||
dbesc(basename($parent))
|
||||
);
|
||||
|
||||
if (! $r) {
|
||||
logger('parent not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($r[0]['owner_xchan'] === $channel['channel_hash']) {
|
||||
if (! perm_is_allowed($channel['channel_id'],$observer_hash,'send_stream') && ! ($is_sys_channel && $pubstream)) {
|
||||
logger('no comment permission.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$s['parent_mid'] = $r[0]['mid'];
|
||||
$s['owner_xchan'] = $r[0]['owner_xchan'];
|
||||
$s['author_xchan'] = $observer_hash;
|
||||
|
||||
}
|
||||
else {
|
||||
if (! perm_is_allowed($channel['channel_id'],$observer_hash,'send_stream') && ! ($is_sys_channel && $pubstream)) {
|
||||
logger('no permission');
|
||||
return;
|
||||
}
|
||||
$s['owner_xchan'] = $s['author_xchan'] = $observer_hash;
|
||||
}
|
||||
|
||||
$abook = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
|
||||
dbesc($observer_hash),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
|
||||
if (is_array($act->obj)) {
|
||||
$content = self::get_content($act->obj);
|
||||
}
|
||||
|
||||
if (! $content) {
|
||||
logger('no content');
|
||||
return;
|
||||
}
|
||||
|
||||
$s['aid'] = $channel['channel_account_id'];
|
||||
$s['uid'] = $channel['channel_id'];
|
||||
$s['mid'] = urldecode($act->obj['id']);
|
||||
$s['plink'] = urldecode($act->obj['id']);
|
||||
|
||||
|
||||
if ($act->data['published']) {
|
||||
$s['created'] = datetime_convert('UTC','UTC',$act->data['published']);
|
||||
}
|
||||
elseif ($act->obj['published']) {
|
||||
$s['created'] = datetime_convert('UTC','UTC',$act->obj['published']);
|
||||
}
|
||||
if ($act->data['updated']) {
|
||||
$s['edited'] = datetime_convert('UTC','UTC',$act->data['updated']);
|
||||
}
|
||||
elseif ($act->obj['updated']) {
|
||||
$s['edited'] = datetime_convert('UTC','UTC',$act->obj['updated']);
|
||||
}
|
||||
|
||||
if (! $s['created'])
|
||||
$s['created'] = datetime_convert();
|
||||
|
||||
if (! $s['edited'])
|
||||
$s['edited'] = $s['created'];
|
||||
|
||||
|
||||
if (! $s['parent_mid'])
|
||||
$s['parent_mid'] = $s['mid'];
|
||||
|
||||
|
||||
$s['title'] = self::bb_content($content,'name');
|
||||
$s['summary'] = self::bb_content($content,'summary');
|
||||
$s['body'] = self::bb_content($content,'content');
|
||||
$s['verb'] = ACTIVITY_POST;
|
||||
$s['obj_type'] = ACTIVITY_OBJ_NOTE;
|
||||
|
||||
$generator = $act->get_property_obj('generator');
|
||||
if (! $generator)
|
||||
$generator = $act->get_property_obj('generator',$act->obj);
|
||||
|
||||
if ($generator && array_key_exists('type',$generator)
|
||||
&& in_array($generator['type'], ['Application', 'Service' ] ) && array_key_exists('name',$generator)) {
|
||||
$s['app'] = escape_tags($generator['name']);
|
||||
}
|
||||
|
||||
if ($channel['channel_system']) {
|
||||
if (! MessageFilter::evaluate($s,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) {
|
||||
logger('post is filtered');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (! post_is_importable($channel['channel_id'],$s,$abook[0])) {
|
||||
logger('post is filtered');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($act->obj['conversation']) {
|
||||
set_iconfig($s,'ostatus','conversation',$act->obj['conversation'],1);
|
||||
}
|
||||
|
||||
$a = self::decode_taxonomy($act->obj);
|
||||
if ($a) {
|
||||
$s['term'] = $a;
|
||||
}
|
||||
|
||||
$a = self::decode_attachment($act->obj);
|
||||
if ($a) {
|
||||
$s['attach'] = $a;
|
||||
}
|
||||
|
||||
if ($act->obj['type'] === 'Note' && $s['attach']) {
|
||||
$s['body'] .= self::bb_attach($s['attach'],$s['body']);
|
||||
}
|
||||
|
||||
// we will need a hook here to extract magnet links e.g. peertube
|
||||
// right now just link to the largest mp4 we find that will fit in our
|
||||
// standard content region
|
||||
|
||||
if ($act->obj['type'] === 'Video') {
|
||||
|
||||
$vtypes = [
|
||||
'video/mp4',
|
||||
'video/ogg',
|
||||
'video/webm'
|
||||
];
|
||||
|
||||
$mps = [];
|
||||
if (array_key_exists('url',$act->obj) && is_array($act->obj['url'])) {
|
||||
foreach ($act->obj['url'] as $vurl) {
|
||||
if (in_array($vurl['mimeType'], $vtypes)) {
|
||||
if (! array_key_exists('width',$vurl)) {
|
||||
$vurl['width'] = 0;
|
||||
}
|
||||
$mps[] = $vurl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($mps) {
|
||||
usort($mps,[ __CLASS__, 'vid_sort' ]);
|
||||
foreach ($mps as $m) {
|
||||
if (intval($m['width']) < 500) {
|
||||
$s['body'] .= "\n\n" . '[video]' . $m['href'] . '[/video]';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips)))
|
||||
$s['item_private'] = 1;
|
||||
|
||||
set_iconfig($s,'activitypub','recips',$act->raw_recips);
|
||||
if ($parent) {
|
||||
set_iconfig($s,'activitypub','rawmsg',$act->raw,1);
|
||||
}
|
||||
|
||||
$x = null;
|
||||
|
||||
$r = q("select created, edited from item where mid = '%s' and uid = %d limit 1",
|
||||
dbesc($s['mid']),
|
||||
intval($s['uid'])
|
||||
);
|
||||
if ($r) {
|
||||
if ($s['edited'] > $r[0]['edited']) {
|
||||
$x = item_store_update($s);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$x = item_store($s);
|
||||
}
|
||||
|
||||
if (is_array($x) && $x['item_id']) {
|
||||
if ($parent) {
|
||||
if ($s['owner_xchan'] === $channel['channel_hash']) {
|
||||
// We are the owner of this conversation, so send all received comments back downstream
|
||||
Master::Summon(array('Notifier','comment-import',$x['item_id']));
|
||||
}
|
||||
$r = q("select * from item where id = %d limit 1",
|
||||
intval($x['item_id'])
|
||||
);
|
||||
if ($r) {
|
||||
send_status_notifications($x['item_id'],$r[0]);
|
||||
}
|
||||
}
|
||||
sync_an_item($channel['channel_id'],$x['item_id']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static function share_bb($obj) {
|
||||
// @fixme - error check and set defaults
|
||||
|
||||
|
@ -2253,12 +2031,14 @@ class Activity {
|
|||
intval($channel['channel_id'])
|
||||
);
|
||||
if ($p) {
|
||||
$allowed = perm_is_allowed($channel['channel_id'],$observer_hash,'post_comments');
|
||||
// check permissions against the author, not the sender
|
||||
$allowed = perm_is_allowed($channel['channel_id'],$item['author_xchan'],'post_comments');
|
||||
if (! $allowed) {
|
||||
logger('rejected comment from ' . $observer_hash . ' for ' . $channel['channel_address']);
|
||||
logger('rejected comment from ' . $item['author_xchan'] . ' for ' . $channel['channel_address']);
|
||||
logger('rejected: ' . print_r($item,true), LOGGER_DATA);
|
||||
// let the sender know we received their comment but we don't permit spam here.
|
||||
self::send_rejection_activity($channel,$observer_hash,$item);
|
||||
self::send_rejection_activity($channel,$item['author_xchan'],$item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -2482,6 +2262,13 @@ class Activity {
|
|||
break;
|
||||
}
|
||||
$a = new ActivityStreams($n);
|
||||
if ($a->type === 'Announce' && is_array($a->obj)
|
||||
&& array_key_exists('object',$a->obj) && array_key_exists('actor',$a->obj)) {
|
||||
// This is a relayed/forwarded Activity (as opposed to a shared/boosted object)
|
||||
// Reparse the encapsulated Activity and use that instead
|
||||
logger('relayed activity',LOGGER_DEBUG);
|
||||
$a = new ActivityStreams($a->obj);
|
||||
}
|
||||
|
||||
logger($a->debug());
|
||||
|
||||
|
@ -2532,278 +2319,6 @@ class Activity {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static function announce_note($channel,$observer_hash,$act) {
|
||||
|
||||
$s = [];
|
||||
|
||||
$is_sys_channel = is_sys_channel($channel['channel_id']);
|
||||
|
||||
// Mastodon only allows visibility in public timelines if the public inbox is listed in the 'to' field.
|
||||
// They are hidden in the public timeline if the public inbox is listed in the 'cc' field.
|
||||
// This is not part of the activitypub protocol - we might change this to show all public posts in pubstream at some point.
|
||||
$pubstream = ((is_array($act->obj) && array_key_exists('to', $act->obj) && in_array(ACTIVITY_PUBLIC_INBOX, $act->obj['to'])) ? true : false);
|
||||
|
||||
if (! perm_is_allowed($channel['channel_id'],$observer_hash,'send_stream') && ! ($is_sys_channel && $pubstream)) {
|
||||
logger('no permission');
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_array($act->obj)) {
|
||||
$content = self::get_content($act->obj);
|
||||
}
|
||||
if (! $content) {
|
||||
logger('no content');
|
||||
return;
|
||||
}
|
||||
|
||||
$s['owner_xchan'] = $s['author_xchan'] = $observer_hash;
|
||||
|
||||
$s['aid'] = $channel['channel_account_id'];
|
||||
$s['uid'] = $channel['channel_id'];
|
||||
$s['mid'] = urldecode($act->obj['id']);
|
||||
$s['plink'] = urldecode($act->obj['id']);
|
||||
|
||||
if (! $s['created'])
|
||||
$s['created'] = datetime_convert();
|
||||
|
||||
if (! $s['edited'])
|
||||
$s['edited'] = $s['created'];
|
||||
|
||||
|
||||
$s['parent_mid'] = $s['mid'];
|
||||
|
||||
$s['verb'] = ACTIVITY_POST;
|
||||
$s['obj_type'] = ACTIVITY_OBJ_NOTE;
|
||||
$s['app'] = t('ActivityPub');
|
||||
|
||||
if ($channel['channel_system']) {
|
||||
if (! MessageFilter::evaluate($s,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) {
|
||||
logger('post is filtered');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$abook = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
|
||||
dbesc($observer_hash),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
|
||||
if (! post_is_importable($channel['channel_id'],$s,$abook[0])) {
|
||||
logger('post is filtered');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($act->obj['conversation']) {
|
||||
set_iconfig($s,'ostatus','conversation',$act->obj['conversation'],1);
|
||||
}
|
||||
|
||||
$a = self::decode_taxonomy($act->obj);
|
||||
if ($a) {
|
||||
$s['term'] = $a;
|
||||
}
|
||||
|
||||
$a = self::decode_attachment($act->obj);
|
||||
if ($a) {
|
||||
$s['attach'] = $a;
|
||||
}
|
||||
|
||||
$body = "[share author='" . urlencode($act->sharee['name']) .
|
||||
"' profile='" . $act->sharee['url'] .
|
||||
"' avatar='" . $act->sharee['photo_s'] .
|
||||
"' link='" . ((is_array($act->obj['url'])) ? $act->obj['url']['href'] : $act->obj['url']) .
|
||||
"' auth='" . ((is_matrix_url($act->obj['url'])) ? 'true' : 'false' ) .
|
||||
"' posted='" . $act->obj['published'] .
|
||||
"' message_id='" . $act->obj['id'] .
|
||||
"']";
|
||||
|
||||
if ($content['name'])
|
||||
$body .= self::bb_content($content,'name') . "\r\n";
|
||||
|
||||
$body .= self::bb_content($content,'content');
|
||||
|
||||
if ($act->obj['type'] === 'Note' && $s['attach']) {
|
||||
$body .= self::bb_attach($s['attach'],body);
|
||||
}
|
||||
|
||||
$body .= "[/share]";
|
||||
|
||||
$s['title'] = self::bb_content($content,'name');
|
||||
$s['body'] = $body;
|
||||
|
||||
if ($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips)))
|
||||
$s['item_private'] = 1;
|
||||
|
||||
set_iconfig($s,'activitypub','recips',$act->raw_recips);
|
||||
|
||||
$r = q("select created, edited from item where mid = '%s' and uid = %d limit 1",
|
||||
dbesc($s['mid']),
|
||||
intval($s['uid'])
|
||||
);
|
||||
if ($r) {
|
||||
if ($s['edited'] > $r[0]['edited']) {
|
||||
$x = item_store_update($s);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$x = item_store($s);
|
||||
}
|
||||
|
||||
|
||||
if (is_array($x) && $x['item_id']) {
|
||||
if ($parent) {
|
||||
if ($s['owner_xchan'] === $channel['channel_hash']) {
|
||||
// We are the owner of this conversation, so send all received comments back downstream
|
||||
Master::Summon(array('Notifier','comment-import',$x['item_id']));
|
||||
}
|
||||
$r = q("select * from item where id = %d limit 1",
|
||||
intval($x['item_id'])
|
||||
);
|
||||
if ($r) {
|
||||
send_status_notifications($x['item_id'],$r[0]);
|
||||
}
|
||||
}
|
||||
sync_an_item($channel['channel_id'],$x['item_id']);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static function like_note($channel,$observer_hash,$act) {
|
||||
|
||||
$s = [];
|
||||
|
||||
$parent = $act->obj['id'];
|
||||
|
||||
if ($act->type === 'Like')
|
||||
$s['verb'] = ACTIVITY_LIKE;
|
||||
if ($act->type === 'Dislike')
|
||||
$s['verb'] = ACTIVITY_DISLIKE;
|
||||
|
||||
if (! $parent)
|
||||
return;
|
||||
|
||||
$r = q("select * from item where uid = %d and ( mid = '%s' or mid = '%s' ) limit 1",
|
||||
intval($channel['channel_id']),
|
||||
dbesc($parent),
|
||||
dbesc(urldecode(basename($parent)))
|
||||
);
|
||||
|
||||
if (! $r) {
|
||||
logger('parent not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
xchan_query($r);
|
||||
$parent_item = $r[0];
|
||||
|
||||
if ($parent_item['owner_xchan'] === $channel['channel_hash']) {
|
||||
if (! perm_is_allowed($channel['channel_id'],$observer_hash,'post_comments')) {
|
||||
logger('no comment permission.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($parent_item['mid'] === $parent_item['parent_mid']) {
|
||||
$s['parent_mid'] = $parent_item['mid'];
|
||||
}
|
||||
else {
|
||||
$s['thr_parent'] = $parent_item['mid'];
|
||||
$s['parent_mid'] = $parent_item['parent_mid'];
|
||||
}
|
||||
|
||||
$s['owner_xchan'] = $parent_item['owner_xchan'];
|
||||
$s['author_xchan'] = $observer_hash;
|
||||
|
||||
$s['aid'] = $channel['channel_account_id'];
|
||||
$s['uid'] = $channel['channel_id'];
|
||||
$s['mid'] = $act->id;
|
||||
|
||||
if (! $s['parent_mid'])
|
||||
$s['parent_mid'] = $s['mid'];
|
||||
|
||||
|
||||
$post_type = (($parent_item['resource_type'] === 'photo') ? t('photo') : t('status'));
|
||||
|
||||
$links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => $parent_item['plink']));
|
||||
$objtype = (($parent_item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE );
|
||||
|
||||
$body = $parent_item['body'];
|
||||
|
||||
$z = q("select * from xchan where xchan_hash = '%s' limit 1",
|
||||
dbesc($parent_item['author_xchan'])
|
||||
);
|
||||
if ($z)
|
||||
$item_author = $z[0];
|
||||
|
||||
$object = json_encode(array(
|
||||
'type' => $post_type,
|
||||
'id' => $parent_item['mid'],
|
||||
'parent' => (($parent_item['thr_parent']) ? $parent_item['thr_parent'] : $parent_item['parent_mid']),
|
||||
'link' => $links,
|
||||
'title' => $parent_item['title'],
|
||||
'content' => $parent_item['body'],
|
||||
'created' => $parent_item['created'],
|
||||
'edited' => $parent_item['edited'],
|
||||
'author' => array(
|
||||
'name' => $item_author['xchan_name'],
|
||||
'address' => $item_author['xchan_addr'],
|
||||
'guid' => $item_author['xchan_guid'],
|
||||
'guid_sig' => $item_author['xchan_guid_sig'],
|
||||
'link' => array(
|
||||
array('rel' => 'alternate', 'type' => 'text/html', 'href' => $item_author['xchan_url']),
|
||||
array('rel' => 'photo', 'type' => $item_author['xchan_photo_mimetype'], 'href' => $item_author['xchan_photo_m'])),
|
||||
),
|
||||
), JSON_UNESCAPED_SLASHES
|
||||
);
|
||||
|
||||
if ($act->type === 'Like')
|
||||
$bodyverb = t('%1$s likes %2$s\'s %3$s');
|
||||
if ($act->type === 'Dislike')
|
||||
$bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s');
|
||||
|
||||
$ulink = '[url=' . $item_author['xchan_url'] . ']' . $item_author['xchan_name'] . '[/url]';
|
||||
$alink = '[url=' . $parent_item['author']['xchan_url'] . ']' . $parent_item['author']['xchan_name'] . '[/url]';
|
||||
$plink = '[url='. z_root() . '/display/' . urlencode($act->id) . ']' . $post_type . '[/url]';
|
||||
$s['body'] = sprintf( $bodyverb, $ulink, $alink, $plink );
|
||||
|
||||
$s['app'] = t('ActivityPub');
|
||||
|
||||
// set the route to that of the parent so downstream hubs won't reject it.
|
||||
|
||||
$s['route'] = $parent_item['route'];
|
||||
$s['item_private'] = $parent_item['item_private'];
|
||||
$s['obj_type'] = $objtype;
|
||||
$s['obj'] = $object;
|
||||
|
||||
if ($act->obj['conversation']) {
|
||||
set_iconfig($s,'ostatus','conversation',$act->obj['conversation'],1);
|
||||
}
|
||||
|
||||
if ($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips)))
|
||||
$s['item_private'] = 1;
|
||||
|
||||
set_iconfig($s,'activitypub','recips',$act->raw_recips);
|
||||
|
||||
$result = item_store($s);
|
||||
|
||||
if ($result['success']) {
|
||||
// if the message isn't already being relayed, notify others
|
||||
if (intval($parent_item['item_origin']))
|
||||
Master::Summon(array('Notifier','comment-import',$result['item_id']));
|
||||
sync_an_item($channel['channel_id'],$result['item_id']);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static function bb_attach($attach,$body) {
|
||||
|
||||
$ret = false;
|
||||
|
@ -2973,17 +2488,28 @@ class Activity {
|
|||
|
||||
}
|
||||
|
||||
static function bear_from_request() {
|
||||
// Find either an Authorization: Bearer token or 'token' request variable
|
||||
// in the current web request and return it
|
||||
|
||||
static function token_from_request() {
|
||||
|
||||
foreach ( [ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $s ) {
|
||||
$auth = ((array_key_exists($s,$_SERVER) && strpos($_SERVER[$s],'Bearer ') === 0)
|
||||
? str_replace('Bearer ', EMPTY_STR, $_SERVER[$s])
|
||||
: EMPTY_STR
|
||||
);
|
||||
break;
|
||||
if ($auth) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (($auth) ? 'bear:?u=' . z_root() . $_SERVER['REQUEST_URI'] . '&t=' . $auth : EMPTY_STR);
|
||||
if (! $auth) {
|
||||
if (array_key_exists('token',$_REQUEST) && $_REQUEST['token']) {
|
||||
$auth = $_REQUEST['token'];
|
||||
}
|
||||
}
|
||||
|
||||
return $auth;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -45,38 +45,43 @@ class ActivityStreams {
|
|||
$this->raw = $string;
|
||||
$this->hub = $hub;
|
||||
|
||||
if(is_array($string)) {
|
||||
if (is_array($string)) {
|
||||
$this->data = $string;
|
||||
}
|
||||
else {
|
||||
$this->data = json_decode($string, true);
|
||||
}
|
||||
|
||||
if($this->data) {
|
||||
if ($this->data) {
|
||||
|
||||
// verify and unpack JSalmon signature if present
|
||||
// This will only be the case for Zot6 packets
|
||||
|
||||
if(is_array($this->data) && array_key_exists('signed',$this->data)) {
|
||||
if (is_array($this->data) && array_key_exists('signed',$this->data)) {
|
||||
$ret = JSalmon::verify($this->data);
|
||||
$tmp = JSalmon::unpack($this->data['data']);
|
||||
if($ret && $ret['success']) {
|
||||
if($ret['signer']) {
|
||||
if ($ret && $ret['success']) {
|
||||
if ($ret['signer']) {
|
||||
logger('Unpacked: ' . json_encode($tmp,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT),LOGGER_DATA,LOG_DEBUG);
|
||||
$saved = json_encode($this->data,JSON_UNESCAPED_SLASHES);
|
||||
$this->data = $tmp;
|
||||
$this->data['signer'] = $ret['signer'];
|
||||
$this->data['signed_data'] = $saved;
|
||||
if($ret['hubloc']) {
|
||||
if ($ret['hubloc']) {
|
||||
$this->data['hubloc'] = $ret['hubloc'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This indicates only that we have sucessfully decoded JSON.
|
||||
$this->valid = true;
|
||||
|
||||
if(array_key_exists('type',$this->data) && array_key_exists('actor',$this->data) && array_key_exists('object',$this->data)) {
|
||||
if($this->data['type'] === 'Delete' && $this->data['actor'] === $this->data['object']) {
|
||||
// Special handling for Mastodon "delete actor" activities which will often fail to verify
|
||||
// because the key cannot be fetched. We will catch this condition elsewhere.
|
||||
|
||||
if (array_key_exists('type',$this->data) && array_key_exists('actor',$this->data) && array_key_exists('object',$this->data)) {
|
||||
if ($this->data['type'] === 'Delete' && $this->data['actor'] === $this->data['object']) {
|
||||
$this->deleted = $this->data['actor'];
|
||||
$this->valid = false;
|
||||
}
|
||||
|
@ -84,7 +89,9 @@ class ActivityStreams {
|
|||
|
||||
}
|
||||
|
||||
if($this->is_valid()) {
|
||||
// Attempt to assemble an Activity from what we were given.
|
||||
|
||||
if ($this->is_valid()) {
|
||||
$this->id = $this->get_property_obj('id');
|
||||
$this->type = $this->get_primary_type();
|
||||
$this->actor = $this->get_actor('actor','','');
|
||||
|
@ -95,32 +102,41 @@ class ActivityStreams {
|
|||
$this->replyto = $this->get_compound_property('replyTo');
|
||||
|
||||
$this->ldsig = $this->get_compound_property('signature');
|
||||
if($this->ldsig) {
|
||||
if ($this->ldsig) {
|
||||
$this->signer = $this->get_compound_property('creator',$this->ldsig);
|
||||
if($this->signer && is_array($this->signer) && array_key_exists('publicKey',$this->signer) && is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) {
|
||||
if ($this->signer && is_array($this->signer) && array_key_exists('publicKey',$this->signer)
|
||||
&& is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) {
|
||||
$this->sigok = LDSignatures::verify($this->data,$this->signer['publicKey']['publicKeyPem']);
|
||||
}
|
||||
}
|
||||
|
||||
if(! $this->obj) {
|
||||
// Implied create activity required by specification if no object is present
|
||||
|
||||
if (! $this->obj) {
|
||||
$this->obj = $this->data;
|
||||
$this->type = 'Create';
|
||||
if(! $this->actor) {
|
||||
if (! $this->actor) {
|
||||
$this->actor = $this->get_actor('attributedTo',$this->obj);
|
||||
}
|
||||
}
|
||||
|
||||
if($this->obj && is_array($this->obj) && $this->obj['actor'])
|
||||
$this->obj['actor'] = $this->get_actor('actor',$this->obj);
|
||||
if($this->tgt && is_array($this->tgt) && $this->tgt['actor'])
|
||||
$this->tgt['actor'] = $this->get_actor('actor',$this->tgt);
|
||||
|
||||
// Enumerate and store actors in referenced objects
|
||||
|
||||
if ($this->obj && is_array($this->obj) && $this->obj['actor']) {
|
||||
$this->obj['actor'] = $this->get_actor('actor',$this->obj);
|
||||
}
|
||||
if ($this->tgt && is_array($this->tgt) && $this->tgt['actor']) {
|
||||
$this->tgt['actor'] = $this->get_actor('actor',$this->tgt);
|
||||
}
|
||||
|
||||
// Determine if this is a followup or response activity
|
||||
|
||||
$this->parent_id = $this->get_property_obj('inReplyTo');
|
||||
|
||||
if((! $this->parent_id) && is_array($this->obj)) {
|
||||
if ((! $this->parent_id) && is_array($this->obj)) {
|
||||
$this->parent_id = $this->obj['inReplyTo'];
|
||||
}
|
||||
if((! $this->parent_id) && is_array($this->obj)) {
|
||||
if ((! $this->parent_id) && is_array($this->obj)) {
|
||||
$this->parent_id = $this->obj['id'];
|
||||
}
|
||||
}
|
||||
|
@ -149,19 +165,21 @@ class ActivityStreams {
|
|||
function collect_recips($base = '', $namespace = '') {
|
||||
$x = [];
|
||||
$fields = [ 'to', 'cc', 'bto', 'bcc', 'audience'];
|
||||
foreach($fields as $f) {
|
||||
foreach ($fields as $f) {
|
||||
$y = $this->get_compound_property($f, $base, $namespace);
|
||||
if($y) {
|
||||
if(! is_array($y)) {
|
||||
if ($y) {
|
||||
if (! is_array($y)) {
|
||||
$y = [ $y ];
|
||||
}
|
||||
$x = array_merge($x, $y);
|
||||
if(! is_array($this->raw_recips))
|
||||
if (! is_array($this->raw_recips)) {
|
||||
$this->raw_recips = [];
|
||||
}
|
||||
|
||||
$this->raw_recips[$f] = $x;
|
||||
}
|
||||
}
|
||||
|
||||
// not yet ready for prime time
|
||||
// $x = $this->expand($x,$base,$namespace);
|
||||
return $x;
|
||||
|
@ -172,15 +190,15 @@ class ActivityStreams {
|
|||
|
||||
// right now use a hardwired recursion depth of 5
|
||||
|
||||
for($z = 0; $z < 5; $z ++) {
|
||||
if(is_array($arr) && $arr) {
|
||||
foreach($arr as $a) {
|
||||
if(is_array($a)) {
|
||||
for ($z = 0; $z < 5; $z ++) {
|
||||
if (is_array($arr) && $arr) {
|
||||
foreach ($arr as $a) {
|
||||
if (is_array($a)) {
|
||||
$ret[] = $a;
|
||||
}
|
||||
else {
|
||||
$x = $this->get_compound_property($a,$base,$namespace);
|
||||
if($x) {
|
||||
if ($x) {
|
||||
$ret = array_merge($ret,$x);
|
||||
}
|
||||
}
|
||||
|
@ -200,35 +218,39 @@ class ActivityStreams {
|
|||
* @param string $namespace if not set return empty string
|
||||
* @return string|NULL
|
||||
*/
|
||||
|
||||
function get_namespace($base, $namespace) {
|
||||
|
||||
if(! $namespace)
|
||||
return '';
|
||||
|
||||
if (! $namespace) {
|
||||
return EMPTY_STR;
|
||||
}
|
||||
|
||||
$key = null;
|
||||
|
||||
foreach( [ $this->data, $base ] as $b ) {
|
||||
if(! $b)
|
||||
foreach ( [ $this->data, $base ] as $b ) {
|
||||
if (! $b) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(array_key_exists('@context', $b)) {
|
||||
if(is_array($b['@context'])) {
|
||||
foreach($b['@context'] as $ns) {
|
||||
if(is_array($ns)) {
|
||||
foreach($ns as $k => $v) {
|
||||
if($namespace === $v)
|
||||
if (array_key_exists('@context', $b)) {
|
||||
if (is_array($b['@context'])) {
|
||||
foreach ($b['@context'] as $ns) {
|
||||
if (is_array($ns)) {
|
||||
foreach ($ns as $k => $v) {
|
||||
if ($namespace === $v) {
|
||||
$key = $k;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if($namespace === $ns) {
|
||||
if ($namespace === $ns) {
|
||||
$key = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if($namespace === $b['@context']) {
|
||||
if ($namespace === $b['@context']) {
|
||||
$key = '';
|
||||
}
|
||||
}
|
||||
|
@ -246,15 +268,17 @@ class ActivityStreams {
|
|||
* @param string $namespace (optional) default empty
|
||||
* @return NULL|mixed
|
||||
*/
|
||||
|
||||
function get_property_obj($property, $base = '', $namespace = '') {
|
||||
$prefix = $this->get_namespace($base, $namespace);
|
||||
if($prefix === null)
|
||||
if ($prefix === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$base = (($base) ? $base : $this->data);
|
||||
$propname = (($prefix) ? $prefix . ':' : '') . $property;
|
||||
|
||||
if(! is_array($base)) {
|
||||
if (! is_array($base)) {
|
||||
btlogger('not an array: ' . print_r($base,true));
|
||||
return null;
|
||||
}
|
||||
|
@ -293,7 +317,7 @@ class ActivityStreams {
|
|||
|
||||
function get_actor($property,$base='',$namespace = '') {
|
||||
$x = $this->get_property_obj($property, $base, $namespace);
|
||||
if($this->is_url($x)) {
|
||||
if ($this->is_url($x)) {
|
||||
|
||||
// SECURITY: If we have already stored the actor profile, re-generate it
|
||||
// from cached data - don't refetch it from the network
|
||||
|
@ -301,7 +325,8 @@ class ActivityStreams {
|
|||
$r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' limit 1",
|
||||
dbesc($x)
|
||||
);
|
||||
if($r) {
|
||||
if ($r) {
|
||||
// indicate that this is a cached record
|
||||
$y = Activity::encode_person($r[0]);
|
||||
$y['cached'] = true;
|
||||
$y['updated'] = datetime_convert('UTC','UTC',$r[0]['hubloc_updated'],ATOM_TIME);
|
||||
|
@ -309,8 +334,8 @@ class ActivityStreams {
|
|||
}
|
||||
}
|
||||
$actor = $this->get_compound_property($property,$base,$namespace,true);
|
||||
if(is_array($actor) && self::is_an_actor($actor['type'])) {
|
||||
if(array_key_exists('id',$actor) && (! array_key_exists('inbox',$actor))) {
|
||||
if (is_array($actor) && self::is_an_actor($actor['type'])) {
|
||||
if (array_key_exists('id',$actor) && (! array_key_exists('inbox',$actor))) {
|
||||
$actor = $this->fetch_property($actor['id']);
|
||||
}
|
||||
return $actor;
|
||||
|
@ -328,9 +353,10 @@ class ActivityStreams {
|
|||
* @param boolean $first (optional) default false, if true and result is a sequential array return only the first element
|
||||
* @return NULL|mixed
|
||||
*/
|
||||
|
||||
function get_compound_property($property, $base = '', $namespace = '', $first = false) {
|
||||
$x = $this->get_property_obj($property, $base, $namespace);
|
||||
if($this->is_url($x)) {
|
||||
if ($this->is_url($x)) {
|
||||
$y = $this->fetch_property($x);
|
||||
if (is_array($y)) {
|
||||
$x = $y;
|
||||
|
@ -338,23 +364,25 @@ class ActivityStreams {
|
|||
}
|
||||
|
||||
// verify and unpack JSalmon signature if present
|
||||
|
||||
if(is_array($x) && array_key_exists('signed',$x)) {
|
||||
// This may be present in Zot6 packets
|
||||
|
||||
if (is_array($x) && array_key_exists('signed',$x)) {
|
||||
$ret = JSalmon::verify($x);
|
||||
$tmp = JSalmon::unpack($x['data']);
|
||||
if($ret && $ret['success']) {
|
||||
if($ret['signer']) {
|
||||
if ($ret && $ret['success']) {
|
||||
if ($ret['signer']) {
|
||||
logger('Unpacked: ' . json_encode($tmp,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT),LOGGER_DATA,LOG_DEBUG);
|
||||
$saved = json_encode($x,JSON_UNESCAPED_SLASHES);
|
||||
$x = $tmp;
|
||||
$x['signer'] = $ret['signer'];
|
||||
$x['signed_data'] = $saved;
|
||||
if($ret['hubloc']) {
|
||||
if ($ret['hubloc']) {
|
||||
$x['hubloc'] = $ret['hubloc'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if($first && is_array($x) && array_key_exists(0,$x)) {
|
||||
if ($first && is_array($x) && array_key_exists(0,$x)) {
|
||||
return $x[0];
|
||||
}
|
||||
|
||||
|
@ -367,8 +395,9 @@ class ActivityStreams {
|
|||
* @param string $url
|
||||
* @return boolean
|
||||
*/
|
||||
|
||||
function is_url($url) {
|
||||
if(($url) && (! is_array($url)) && ((strpos($url, 'http') === 0) || (strpos($url,'x-zot') === 0))) {
|
||||
if (($url) && (! is_array($url)) && ((strpos($url, 'http') === 0) || (strpos($url,'x-zot') === 0) || (strpos($url,'bear') === 0))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -382,14 +411,15 @@ class ActivityStreams {
|
|||
* @param string $namespace (optional) default empty
|
||||
* @return NULL|mixed
|
||||
*/
|
||||
|
||||
function get_primary_type($base = '', $namespace = '') {
|
||||
if(! $base)
|
||||
if (! $base) {
|
||||
$base = $this->data;
|
||||
|
||||
}
|
||||
$x = $this->get_property_obj('type', $base, $namespace);
|
||||
if(is_array($x)) {
|
||||
foreach($x as $y) {
|
||||
if(strpos($y, ':') === false) {
|
||||
if (is_array($x)) {
|
||||
foreach ($x as $y) {
|
||||
if (strpos($y, ':') === false) {
|
||||
return $y;
|
||||
}
|
||||
}
|
||||
|
@ -413,8 +443,5 @@ class ActivityStreams {
|
|||
]);
|
||||
|
||||
return(($x) ? true : false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1183,6 +1183,14 @@ class Libzot {
|
|||
if ($env['encoding'] === 'activitystreams') {
|
||||
|
||||
$AS = new ActivityStreams($data);
|
||||
if ($AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj)
|
||||
&& array_key_exists('object',$AS->obj) && array_key_exists('actor',$AS->obj)) {
|
||||
// This is a relayed/forwarded Activity (as opposed to a shared/boosted object)
|
||||
// Reparse the encapsulated Activity and use that instead
|
||||
logger('relayed activity',LOGGER_DEBUG);
|
||||
$AS = new ActivityStreams($AS->obj);
|
||||
}
|
||||
|
||||
if (! $AS->is_valid()) {
|
||||
logger('Activity rejected: ' . print_r($data,true));
|
||||
return;
|
||||
|
@ -2020,6 +2028,14 @@ class Libzot {
|
|||
foreach ($a['data']['orderedItems'] as $activity) {
|
||||
|
||||
$AS = new ActivityStreams($activity);
|
||||
if ($AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj)
|
||||
&& array_key_exists('object',$AS->obj) && array_key_exists('actor',$AS->obj)) {
|
||||
// This is a relayed/forwarded Activity (as opposed to a shared/boosted object)
|
||||
// Reparse the encapsulated Activity and use that instead
|
||||
logger('relayed activity',LOGGER_DEBUG);
|
||||
$AS = new ActivityStreams($AS->obj);
|
||||
}
|
||||
|
||||
if (! $AS->is_valid()) {
|
||||
logger('FOF Activity rejected: ' . print_r($activity,true));
|
||||
continue;
|
||||
|
|
|
@ -510,12 +510,24 @@ class ThreadItem {
|
|||
if (get_config('system','activitypub') && local_channel() && get_pconfig(local_channel(),'system','activitypub',true)) {
|
||||
// place to store all the author addresses (links if not available) in the thread so we can auto-mention them in JS.
|
||||
$result['authors'] = [];
|
||||
// fix to add in sub-replies if replying to a comment on your own post from the top level.
|
||||
if ($observer && ($profile_addr === $observer['xchan_hash'] || $profile_addr === $observer['xchan_addr'])) {
|
||||
// ignore it
|
||||
}
|
||||
else {
|
||||
$result['authors'][] = $profile_addr;
|
||||
}
|
||||
if ($children) {
|
||||
foreach ($children as $child) {
|
||||
$cdata = $child->get_data();
|
||||
if ($cdata['author']['xchan_addr']) {
|
||||
if (! in_array($cdata['author']['xchan_addr'],$result['authors'])) {
|
||||
$result['authors'][] = $cdata['author']['xchan_addr'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add any mentions from the immediate parent, unless they are mentions of the current viewer or duplicates
|
||||
if ($item['term']) {
|
||||
foreach ($item['term'] as $t) {
|
||||
|
|
|
@ -17,18 +17,40 @@ class Activity extends Controller {
|
|||
return;
|
||||
}
|
||||
|
||||
$bear = ZlibActivity::bear_from_request();
|
||||
$ob_authorise = false;
|
||||
$item_uid = 0;
|
||||
|
||||
$bear = ZlibActivity::token_from_request();
|
||||
if ($bear) {
|
||||
logger('bear: ' . $bear, LOGGER_DEBUG);
|
||||
$t = q("select item.uid, iconfig.v from iconfig left join item on iid = item.id where cat = 'ocaps' and item.uuid = '%s'",
|
||||
dbesc($item_id)
|
||||
);
|
||||
if ($t) {
|
||||
foreach ($t as $token) {
|
||||
if ($token['v'] === $bear) {
|
||||
$ob_authorize = true;
|
||||
$item_uid = $token['uid'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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 ";
|
||||
|
||||
// if passed an owner_id of 0, we force "guest access" or observer checking
|
||||
$sql_extra = item_permissions_sql(0);
|
||||
|
||||
$r = q("select * from item where uuid = '%s' $item_normal $sql_extra limit 1",
|
||||
// if passed an owner_id of 0 to item_permissions_sql(), we force "guest access" or observer checking
|
||||
// Give ocap tokens priority
|
||||
|
||||
if ($ob_authorize) {
|
||||
$sql_extra = " and item.uid = " . intval($token['uid']) . " ";
|
||||
}
|
||||
else {
|
||||
$sql_extra = item_permissions_sql(0);
|
||||
}
|
||||
|
||||
$r = q("select * from item where uuid = '%s' $item_normal $sql_extra and item_deleted = 0 limit 1",
|
||||
dbesc($item_id)
|
||||
);
|
||||
if (! $r) {
|
||||
|
@ -54,7 +76,6 @@ class Activity extends Controller {
|
|||
]], ZlibActivity::encode_activity($items[0]));
|
||||
|
||||
|
||||
|
||||
$headers = [];
|
||||
$headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ;
|
||||
$x['signature'] = LDSignatures::sign($x,$channel);
|
||||
|
|
|
@ -43,6 +43,13 @@ class Inbox extends Controller {
|
|||
$hsig = HTTPSig::verify($data);
|
||||
|
||||
$AS = new ActivityStreams($data);
|
||||
if ($AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj)
|
||||
&& array_key_exists('object',$AS->obj) && array_key_exists('actor',$AS->obj)) {
|
||||
// This is a relayed/forwarded Activity (as opposed to a shared/boosted object)
|
||||
// Reparse the encapsulated Activity and use that instead
|
||||
logger('relayed activity',LOGGER_DEBUG);
|
||||
$AS = new ActivityStreams($AS->obj);
|
||||
}
|
||||
|
||||
//logger('debug: ' . $AS->debug());
|
||||
|
||||
|
@ -59,6 +66,7 @@ class Inbox extends Controller {
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// $observer_hash in this case is the sender
|
||||
|
||||
|
|
|
@ -138,6 +138,15 @@ class Linkinfo extends Controller {
|
|||
$x = Activity::fetch($url);
|
||||
if (is_array($x)) {
|
||||
$y = new ActivityStreams($x);
|
||||
if ($y->is_valid() && $y->type === 'Announce' && is_array($y->obj)
|
||||
&& array_key_exists('object',$y->obj) && array_key_exists('actor',$y->obj)) {
|
||||
// This is a relayed/forwarded Activity (as opposed to a shared/boosted object)
|
||||
// Reparse the encapsulated Activity and use that instead
|
||||
logger('relayed activity',LOGGER_DEBUG);
|
||||
$y = new ActivityStreams($y->obj);
|
||||
}
|
||||
|
||||
|
||||
if ($y->is_valid()) {
|
||||
$z = Activity::decode_note($y);
|
||||
$r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_hash = '%s' OR hubloc_id_url = '%s'",
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
|
||||
class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
|
||||
class Viewsrc extends Controller {
|
||||
|
||||
function get() {
|
||||
|
||||
|
@ -15,45 +18,46 @@ class Viewsrc extends \Zotlabs\Web\Controller {
|
|||
$json = ((argc() > 2 && argv(2) === 'json') ? true : false);
|
||||
$dload = ((argc() > 2 && argv(2) === 'download') ? true : false);
|
||||
|
||||
if(! local_channel()) {
|
||||
if (! local_channel()) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
}
|
||||
|
||||
|
||||
if(! $item_id) {
|
||||
\App::$error = 404;
|
||||
if (! $item_id) {
|
||||
App::$error = 404;
|
||||
notice( t('Item not found.') . EOL);
|
||||
}
|
||||
|
||||
$item_normal = item_normal_search();
|
||||
|
||||
if(local_channel() && $item_id) {
|
||||
if (local_channel() && $item_id) {
|
||||
$r = q("select id, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1",
|
||||
intval(local_channel()),
|
||||
intval($sys['channel_id']),
|
||||
intval($item_id)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
if(intval($r[0]['item_obscured']))
|
||||
if ($r) {
|
||||
if (intval($r[0]['item_obscured']))
|
||||
$dload = true;
|
||||
|
||||
if($dload) {
|
||||
if ($dload) {
|
||||
header('Content-type: ' . $r[0]['mimetype']);
|
||||
header('Content-disposition: attachment; filename="' . t('item') . '-' . $item_id . '"' );
|
||||
echo $r[0]['body'];
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
$content = escape_tags($r[0]['body']);
|
||||
$o = (($json) ? json_encode($content) : str_replace("\n",'<br />',$content));
|
||||
}
|
||||
}
|
||||
|
||||
if(is_ajax()) {
|
||||
|
||||
$inspect = ((is_site_admin()) ? '| <a href="' . z_root() . '/inspect/item/' . $r[0]['id'] . '" target="_blank">' . t('inspect') . '</a>' : EMPTY_STR);
|
||||
|
||||
|
||||
if (is_ajax()) {
|
||||
echo '<div class="p-1">';
|
||||
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a></div>';
|
||||
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a>' . $inspect . '</div>';
|
||||
echo '<hr>';
|
||||
echo '<pre class="p-1">' . $o . '</pre>';
|
||||
echo '</div>';
|
||||
|
|
Loading…
Reference in a new issue