2021-12-03 03:01:39 +00:00
< ? php
/** @file */
2013-02-26 01:09:40 +00:00
2022-02-16 04:08:28 +00:00
namespace Code\Lib ;
2012-09-11 05:00:56 +00:00
2019-07-08 03:11:37 +00:00
use App ;
2022-02-16 04:08:28 +00:00
use Code\Extend\Hook ;
use Code\Render\Theme ;
2022-02-12 20:43:29 +00:00
2022-09-01 19:17:27 +00:00
2012-09-11 05:00:56 +00:00
/**
2016-05-21 05:52:47 +00:00
* A thread item
2012-09-11 05:00:56 +00:00
*/
2014-01-08 23:20:12 +00:00
2021-12-03 03:01:39 +00:00
class ThreadItem
{
public $data = [];
private $template = 'conv_item.tpl' ;
private $comment_box_template = 'comment_item.tpl' ;
private $commentable = false ;
// list of supported reaction emojis - a site can over-ride this via config system.reactions
// Deprecated. Use your operating system or a browser plugin.
private $reactions = [ '1f60a' , '1f44f' , '1f37e' , '1f48b' , '1f61e' , '2665' , '1f606' , '1f62e' , '1f634' , '1f61c' , '1f607' , '1f608' ];
2022-12-02 07:37:29 +00:00
private $toplevel ;
2021-12-03 03:01:39 +00:00
private $children = [];
private $parent = null ;
2022-12-02 07:37:29 +00:00
private $conversation ;
2021-12-03 03:01:39 +00:00
private $redirect_url = null ;
private $owner_url = '' ;
private $owner_photo = '' ;
private $owner_name = '' ;
2022-04-04 10:46:22 +00:00
private $owner_addr = '' ;
2021-12-03 03:01:39 +00:00
private $owner_censored = false ;
private $wall_to_wall = false ;
2022-12-02 07:37:29 +00:00
private $threaded ;
2021-12-03 03:01:39 +00:00
private $visiting = false ;
private $channel = null ;
private $display_mode = 'normal' ;
private $reload = '' ;
public function __construct ( $data )
{
$this -> data = $data ;
$this -> toplevel = ( $this -> get_id () == $this -> get_data_value ( 'parent' ));
$this -> threaded = get_config ( 'system' , 'thread_allow' , true );
$observer = App :: get_observer ();
// Prepare the children
if ( $data [ 'children' ]) {
foreach ( $data [ 'children' ] as $item ) {
/*
* Only add those that will be displayed
*/
if ( ! visible_activity ( $item )) {
continue ;
}
// this is a quick hack to hide ActivityPub DMs that we should not be allowed to see
// but may have been forwarded as part of a conversation
if ( intval ( $item [ 'item_private' ]) && ( intval ( $item [ 'item_restrict' ]) & 1 ) && $item [ 'mid' ] !== $item [ 'parent_mid' ]) {
if ( ! $observer ) {
continue ;
2021-12-02 22:33:36 +00:00
}
2021-12-03 03:01:39 +00:00
}
$child = new ThreadItem ( $item );
$this -> add_child ( $child );
}
}
// allow a site to configure the order and content of the reaction emoji list
if ( $this -> toplevel ) {
$x = get_config ( 'system' , 'reactions' );
if ( $x && is_array ( $x ) && count ( $x )) {
$this -> reactions = $x ;
}
}
}
/**
* Get data in a form usable by a conversation template
*
* Returns :
* _ The data requested on success
* _ false on failure
*/
public function get_template_data ( $conv_responses , $thread_level = 1 )
{
$result = [];
$item = $this -> get_data ();
$dropping = false ;
$star = false ;
$is_comment = false ;
$total_children = $this -> count_descendants ();
$unseen_comments = (( isset ( $item [ 'real_uid' ]) && $item [ 'real_uid' ]) ? 0 : $this -> count_unseen_descendants ());
$privacy_warning = false ;
$conv = $this -> get_conversation ();
$observer = $conv -> get_observer ();
2021-12-21 22:03:17 +00:00
$lock = t ( 'Public visibility' );
2022-08-05 21:14:47 +00:00
if ( intval ( $item [ 'item_private' ]) === 2 ) {
$lock = t ( 'Direct message (private mail)' );
}
if ( intval ( $item [ 'item_private' ]) === 1 ) {
$lock = t ( 'Restricted visibility' );
}
2021-12-21 22:03:17 +00:00
$locktype = intval ( $item [ 'item_private' ]);
2021-12-03 03:01:39 +00:00
$shareable = ((( $conv -> get_profile_owner () == local_channel () && local_channel ()) && ( ! intval ( $item [ 'item_private' ]))) ? true : false );
// allow an exemption for sharing stuff from your private feeds
if ( $item [ 'author' ][ 'xchan_network' ] === 'rss' ) {
$shareable = true ;
}
// @fixme
// Have recently added code to properly handle polls in group reshares by redirecting all of the poll responses to the group.
// Sharing a poll using a regular embedded share is harder because the poll will need to fork. This is due to comment permissions.
// The original poll author may not accept responses from strangers. Forking the poll will receive responses from the sharer's
// followers, but there's no elegant way to merge these two sets of results together. For now, we'll disable sharing polls.
if ( $item [ 'obj_type' ] === 'Question' ) {
$shareable = false ;
}
if ( $item [ 'item_restrict' ] & 2 ) {
$privacy_warning = true ;
$lock = t ( 'This comment is part of a private conversation, yet was shared with the public. Discretion advised.' );
}
$mode = $conv -> get_mode ();
$edlink = 'editpost' ;
if ( local_channel () && $observer [ 'xchan_hash' ] === $item [ 'author_xchan' ]) {
2022-12-02 07:37:29 +00:00
$edpost = [ z_root () . '/' . $edlink . '/' . $item [ 'id' ], t ( 'Edit' )];
2021-12-03 03:01:39 +00:00
} else {
$edpost = false ;
}
if ( local_channel () && $observer [ 'xchan_hash' ] === $item [ 'owner_xchan' ]) {
$myconv = true ;
} else {
$myconv = false ;
}
if ( $item [ 'verb' ] === 'Announce' ) {
$edpost = false ;
}
if (
$observer && $observer [ 'xchan_hash' ]
&& ( $observer [ 'xchan_hash' ] == $this -> get_data_value ( 'author_xchan' )
|| $observer [ 'xchan_hash' ] == $this -> get_data_value ( 'owner_xchan' )
|| $observer [ 'xchan_hash' ] == $this -> get_data_value ( 'source_xchan' )
|| $this -> get_data_value ( 'uid' ) == local_channel ())
) {
$dropping = true ;
}
if ( array_key_exists ( 'real_uid' , $item )) {
$edpost = false ;
$dropping = false ;
}
if ( $dropping ) {
2022-12-02 07:37:29 +00:00
$drop = [
2021-12-03 03:01:39 +00:00
'dropping' => $dropping ,
'delete' => t ( 'Delete' ),
2022-12-02 07:37:29 +00:00
];
2021-12-03 03:01:39 +00:00
} elseif ( is_site_admin ()) {
$drop = [ 'dropping' => true , 'delete' => t ( 'Admin Delete' ) ];
}
if ( isset ( $observer_is_pageowner ) && $observer_is_pageowner ) {
2022-12-02 07:37:29 +00:00
$multidrop = [
2021-12-03 03:01:39 +00:00
'select' => t ( 'Select' ),
2022-12-02 07:37:29 +00:00
];
2021-12-03 03:01:39 +00:00
}
$filer = ((( $conv -> get_profile_owner () == local_channel ()) && ( ! array_key_exists ( 'real_uid' , $item ))) ? t ( 'Save to Folder' ) : false );
2022-12-02 19:42:40 +00:00
$large_avatar = $item [ 'author' ][ 'xchan_photo_l' ];
2021-12-03 03:01:39 +00:00
$profile_avatar = $item [ 'author' ][ 'xchan_photo_m' ];
$profile_link = chanlink_hash ( $item [ 'author_xchan' ]);
$profile_name = $item [ 'author' ][ 'xchan_name' ];
2022-12-02 07:37:29 +00:00
$profile_addr = $item [ 'author' ][ 'xchan_addr' ] ? : $item [ 'author' ][ 'xchan_url' ];
2021-12-03 03:01:39 +00:00
$location = format_location ( $item );
$isevent = false ;
$attend = null ;
// process action responses - e.g. like/dislike/attend/agree/whatever
$response_verbs = [ 'like' , 'dislike' ];
if ( $item [ 'obj_type' ] === ACTIVITY_OBJ_EVENT ) {
$response_verbs [] = 'attendyes' ;
$response_verbs [] = 'attendno' ;
$response_verbs [] = 'attendmaybe' ;
if ( $this -> is_commentable () && $observer ) {
$isevent = true ;
2022-12-02 07:37:29 +00:00
$attend = [ t ( 'I will attend' ), t ( 'I will not attend' ), t ( 'I might attend' )];
2021-12-03 03:01:39 +00:00
$undo_attend = t ( 'Undo attendance' );
}
}
$responses = get_responses ( $conv_responses , $response_verbs , $this , $item );
$my_responses = [];
foreach ( $response_verbs as $v ) {
$my_responses [ $v ] = (( isset ( $conv_responses [ $v ][ $item [ 'mid' ] . '-m' ]) && $conv_responses [ $v ][ $item [ 'mid' ] . '-m' ]) ? 1 : 0 );
}
$like_count = (( x ( $conv_responses [ 'like' ], $item [ 'mid' ])) ? $conv_responses [ 'like' ][ $item [ 'mid' ]] : '' );
$like_list = (( x ( $conv_responses [ 'like' ], $item [ 'mid' ])) ? $conv_responses [ 'like' ][ $item [ 'mid' ] . '-l' ] : '' );
if (( $like_list ) && ( count ( $like_list ) > MAX_LIKERS )) {
$like_list_part = array_slice ( $like_list , 0 , MAX_LIKERS );
2023-01-29 19:12:42 +00:00
array_push ( $like_list_part , '<a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#likeModal-' . $this -> get_id () . '"><b>' . t ( 'View all' ) . '</b></a>' );
2021-12-03 03:01:39 +00:00
} else {
$like_list_part = '' ;
}
if ( get_config ( 'system' , 'show_like_counts' , true )) {
$like_button_label = tt ( 'Like' , 'Likes' , $like_count , 'noun' );
} else {
$like_button_label = t ( 'Likes' , 'noun' );
}
$dislike_count = (( x ( $conv_responses [ 'dislike' ], $item [ 'mid' ])) ? $conv_responses [ 'dislike' ][ $item [ 'mid' ]] : '' );
$dislike_list = (( x ( $conv_responses [ 'dislike' ], $item [ 'mid' ])) ? $conv_responses [ 'dislike' ][ $item [ 'mid' ] . '-l' ] : '' );
if ( get_config ( 'system' , 'show_like_counts' , true )) {
$dislike_button_label = tt ( 'Dislike' , 'Dislikes' , $dislike_count , 'noun' );
} else {
$dislike_button_label = t ( 'Dislikes' , 'noun' );
}
if (( $dislike_list ) && ( count ( $dislike_list ) > MAX_LIKERS )) {
$dislike_list_part = array_slice ( $dislike_list , 0 , MAX_LIKERS );
2023-01-29 19:12:42 +00:00
array_push ( $dislike_list_part , '<a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#dislikeModal-' . $this -> get_id () . '"><b>' . t ( 'View all' ) . '</b></a>' );
2021-12-03 03:01:39 +00:00
} else {
$dislike_list_part = '' ;
}
/*
* We should avoid doing this all the time , but it depends on the conversation mode
* And the conv mode may change when we change the conv , or it changes its mode
* Maybe we should establish a way to be notified about conversation changes
*/
$this -> check_wall_to_wall ();
if ( $this -> is_toplevel ()) {
2021-12-04 20:43:53 +00:00
if ( local_channel () && ( $conv -> get_profile_owner () == local_channel () || intval ( $item [ 'item_private' ]) === 0 )) {
2021-12-03 03:01:39 +00:00
$star = [
'toggle' => t ( 'Save' ),
2022-12-02 07:37:29 +00:00
'isstarred' => ( bool ) $item [ 'item_starred' ]
2021-12-03 03:01:39 +00:00
];
}
} else {
$is_comment = true ;
}
$verified = ( intval ( $item [ 'item_verified' ]) ? t ( 'Message signature validated' ) : '' );
$forged = ((( $item [ 'sig' ]) && ( ! intval ( $item [ 'item_verified' ]))) ? t ( 'Message signature incorrect' ) : '' );
$unverified = '' ; // (($this->is_wall_to_wall() && (! intval($item['item_verified']))) ? t('Message cannot be verified') : '');
if ( $conv -> get_profile_owner () == local_channel ()) {
2022-12-02 07:37:29 +00:00
$tagger = [
2021-12-03 03:01:39 +00:00
'tagit' => t ( " Add Tag " ),
'classtagger' => " " ,
2022-12-02 07:37:29 +00:00
];
2021-12-03 03:01:39 +00:00
}
$has_bookmarks = false ;
if ( isset ( $item [ 'term' ]) && is_array ( $item [ 'term' ])) {
foreach ( $item [ 'term' ] as $t ) {
if ( $t [ 'ttype' ] == TERM_BOOKMARK ) {
$has_bookmarks = true ;
}
}
}
$has_event = false ;
if (( $item [ 'obj_type' ] === ACTIVITY_OBJ_EVENT ) && $conv -> get_profile_owner () == local_channel ()) {
$has_event = true ;
}
if ( $this -> is_commentable () && $observer ) {
2022-12-02 07:37:29 +00:00
$like = [ t ( 'I like this' ), t ( 'Undo like' )];
$dislike = [ t ( 'I don\'t like this' ), t ( 'Undo dislike' )];
2021-12-03 03:01:39 +00:00
}
$share = $embed = EMPTY_STR ;
if ( $shareable ) {
$share = t ( 'Repeat This' );
$embed = t ( 'Share this' );
}
$dreport = '' ;
$keep_reports = intval ( get_config ( 'system' , 'expire_delivery_reports' ));
if ( $keep_reports === 0 ) {
$keep_reports = 10 ;
}
if (( ! get_config ( 'system' , 'disable_dreport' )) && strcmp ( datetime_convert ( 'UTC' , 'UTC' , $item [ 'created' ]), datetime_convert ( 'UTC' , 'UTC' , " now - $keep_reports days " )) > 0 ) {
$dreport = t ( 'Delivery Report' );
$dreport_link = gen_link_id ( $item [ 'mid' ]);
}
$is_new = false ;
if ( strcmp ( datetime_convert ( 'UTC' , 'UTC' , $item [ 'created' ]), datetime_convert ( 'UTC' , 'UTC' , 'now - 12 hours' )) > 0 ) {
$is_new = true ;
}
localize_item ( $item );
$opts = [];
if ( $this -> is_wall_to_wall ()) {
if ( $this -> owner_censored ) {
$opts [ 'censored' ] = true ;
}
}
$body = prepare_body ( $item , true , $opts );
// $viewthread (below) is only valid in list mode. If this is a channel page, build the thread viewing link
// since we can't depend on llink or plink pointing to the right local location.
$owner_address = substr ( $item [ 'owner' ][ 'xchan_addr' ], 0 , strpos ( $item [ 'owner' ][ 'xchan_addr' ], '@' ));
$viewthread = $item [ 'llink' ];
if ( $conv -> get_mode () === 'channel' ) {
$viewthread = z_root () . '/channel/' . $owner_address . '?f=&mid=' . urlencode ( gen_link_id ( $item [ 'mid' ]));
}
$comment_count_txt = sprintf ( tt ( '%d comment' , '%d comments' , $total_children ), $total_children );
$list_unseen_txt = (( $unseen_comments ) ? sprintf ( t ( '%d unseen' ), $unseen_comments ) : '' );
$children = $this -> get_children ();
$has_tags = (( $body [ 'tags' ] || $body [ 'categories' ] || $body [ 'mentions' ] || $body [ 'attachments' ] || $body [ 'folders' ]) ? true : false );
$dropdown_extras_arr = [ 'item' => $item , 'dropdown_extras' => '' ];
2022-02-12 08:50:48 +00:00
Hook :: call ( 'dropdown_extras' , $dropdown_extras_arr );
2021-12-03 03:01:39 +00:00
$dropdown_extras = $dropdown_extras_arr [ 'dropdown_extras' ];
// Pinned item processing
$allowed_type = ( in_array ( $item [ 'item_type' ], get_config ( 'system' , 'pin_types' , [ ITEM_TYPE_POST ])) ? true : false );
$pinned_items = ( $allowed_type ? get_pconfig ( $item [ 'uid' ], 'pinned' , $item [ 'item_type' ], []) : []);
$pinned = (( ! empty ( $pinned_items ) && in_array ( $item [ 'mid' ], $pinned_items )) ? true : false );
2022-12-01 10:22:46 +00:00
$locicon = ( $item [ 'verb' ] === 'Arrive' ) ? '<i class="fa fa-fw fa-sign-in"></i> ' : '' ;
2022-11-21 21:29:13 +00:00
if ( ! $locicon ) {
$locicon = ( $item [ 'verb' ] === 'Leave' ) ? '<i class="fa fa-fw fa-sign-out"></i> ' : '' ;
}
2022-12-02 07:37:29 +00:00
$tmp_item = [
2021-12-03 03:01:39 +00:00
'template' => $this -> get_template (),
'mode' => $mode ,
'item_type' => intval ( $item [ 'item_type' ]),
'comment_order' => $item [ 'comment_order' ],
'parent' => $this -> get_data_value ( 'parent' ),
'collapsed' => (( intval ( $item [ 'comment_order' ]) > 3 ) ? true : false ),
'type' => implode ( " " , array_slice ( explode ( " / " , $item [ 'verb' ]), - 1 )),
'body' => $body [ 'html' ],
'tags' => $body [ 'tags' ],
'categories' => $body [ 'categories' ],
'mentions' => $body [ 'mentions' ],
'attachments' => $body [ 'attachments' ],
'folders' => $body [ 'folders' ],
'text' => strip_tags ( $body [ 'html' ]),
'id' => $this -> get_id (),
'mid' => $item [ 'mid' ],
'isevent' => $isevent ,
'attend' => $attend ,
'undo_attend' => $undo_attend ,
'consensus' => '' ,
'conlabels' => '' ,
'linktitle' => sprintf ( t ( 'View %s\'s profile - %s' ), $profile_name , (( $item [ 'author' ][ 'xchan_addr' ]) ? $item [ 'author' ][ 'xchan_addr' ] : $item [ 'author' ][ 'xchan_url' ])),
2022-08-21 00:15:14 +00:00
'olinktitle' => sprintf ( t ( 'View %s\'s profile - %s' ), $this -> get_owner_name (), (( $this -> get_owner_addr ()) ? $this -> get_owner_addr () : $this -> get_owner_url ())),
2021-12-03 03:01:39 +00:00
'llink' => $item [ 'llink' ],
'viewthread' => $viewthread ,
'to' => t ( 'to' ),
'via' => t ( 'via' ),
'wall' => t ( 'Wall-to-Wall' ),
'vwall' => t ( 'via Wall-To-Wall:' ),
'profile_url' => $profile_link ,
'thread_action_menu' => thread_action_menu ( $item , $conv -> get_mode ()),
'thread_author_menu' => thread_author_menu ( $item , $conv -> get_mode ()),
'dreport' => $dreport ,
'dreport_link' => (( isset ( $dreport_link ) && $dreport_link ) ? $dreport_link : EMPTY_STR ),
'myconv' => $myconv ,
'name' => $profile_name ,
'thumb' => $profile_avatar ,
2022-12-02 19:42:40 +00:00
'large_avatar' => $large_avatar ,
2022-11-21 21:29:13 +00:00
'title' => $locicon . $item [ 'title' ],
2021-12-03 03:01:39 +00:00
'title_tosource' => get_pconfig ( $conv -> get_profile_owner (), 'system' , 'title_tosource' ),
'ago' => relative_date ( $item [ 'created' ]),
'app' => $item [ 'app' ],
'str_app' => sprintf ( t ( 'from %s' ), $item [ 'app' ]),
'isotime' => datetime_convert ( 'UTC' , date_default_timezone_get (), $item [ 'created' ], 'c' ),
'localtime' => datetime_convert ( 'UTC' , date_default_timezone_get (), $item [ 'created' ], 'r' ),
'editedtime' => (( $item [ 'edited' ] != $item [ 'created' ]) ? sprintf ( t ( 'last edited: %s' ), datetime_convert ( 'UTC' , date_default_timezone_get (), $item [ 'edited' ], 'r' )) : '' ),
'expiretime' => (( $item [ 'expires' ] > NULL_DATE ) ? sprintf ( t ( 'Expires: %s' ), datetime_convert ( 'UTC' , date_default_timezone_get (), $item [ 'expires' ], 'r' )) : '' ),
'lock' => $lock ,
'locktype' => $locktype ,
'delayed' => $item [ 'item_delayed' ],
'privacy_warning' => $privacy_warning ,
'verified' => $verified ,
'unverified' => $unverified ,
'forged' => $forged ,
'location' => $location ,
'divider' => get_pconfig ( $conv -> get_profile_owner (), 'system' , 'item_divider' ),
'attend_label' => t ( 'Attend' ),
'attend_title' => t ( 'Attendance Options' ),
'vote_label' => t ( 'Vote' ),
'vote_title' => t ( 'Voting Options' ),
'comment_lbl' => (( $this -> is_commentable () && $observer ) ? t ( 'Reply' ) : '' ),
'is_comment' => $is_comment ,
'is_new' => $is_new ,
'mod_display' => (( argv ( 0 ) === 'display' ) ? true : false ), // comments are not collapsed when using mod_display
'owner_url' => $this -> get_owner_url (),
'owner_photo' => $this -> get_owner_photo (),
'owner_name' => $this -> get_owner_name (),
2022-04-04 10:46:22 +00:00
'owner_addr' => $this -> get_owner_addr (),
2021-12-03 03:01:39 +00:00
'photo' => $body [ 'photo' ],
'event' => $body [ 'event' ],
'has_tags' => $has_tags ,
'reactions' => $this -> reactions ,
// Item toolbar buttons
'emojis' => '' , // deprecated - use your operating system or a browser plugin
'like' => $like ,
'dislike' => $dislike ,
'share' => $share ,
'embed' => $embed ,
'rawmid' => $item [ 'mid' ],
'plink' => get_plink ( $item ),
2023-02-23 21:14:15 +00:00
'edpost' => $edpost ,
2021-12-03 03:01:39 +00:00
'star' => $star ,
2023-02-23 21:14:15 +00:00
'tagger' => '' ,
'filer' => '' ,
2021-12-03 03:01:39 +00:00
'pinned' => ( $pinned ? t ( 'Pinned post' ) : '' ),
'pinnable' => (( $this -> is_toplevel () && local_channel () && $item [ 'owner_xchan' ] == $observer [ 'xchan_hash' ] && $allowed_type && $item [ 'item_private' ] == 0 && $item [ 'item_delayed' ] == 0 ) ? '1' : '' ),
'pinme' => ( $pinned ? t ( 'Unpin this post' ) : t ( 'Pin this post' )),
'isdraft' => boolval ( $item [ 'item_unpublished' ]),
'draft_txt' => t ( 'Saved draft' ),
'bookmark' => (( $conv -> get_profile_owner () == local_channel () && local_channel () && $has_bookmarks ) ? t ( 'Save Bookmarks' ) : '' ),
'addtocal' => (( $has_event && ! $item [ 'resource_id' ]) ? t ( 'Add to Calendar' ) : '' ),
'drop' => $drop ,
2022-01-25 23:20:02 +00:00
'multidrop' => (( Features :: enabled ( $conv -> get_profile_owner (), 'multi_delete' )) ? $multidrop : '' ),
2021-12-03 03:01:39 +00:00
'dropdown_extras' => $dropdown_extras ,
// end toolbar buttons
'unseen_comments' => $unseen_comments ,
'comment_count' => $total_children ,
'comment_count_txt' => $comment_count_txt ,
'list_unseen_txt' => $list_unseen_txt ,
'markseen' => t ( 'Mark all seen' ),
'responses' => $responses ,
'my_responses' => $my_responses ,
'like_count' => $like_count ,
'like_list' => $like_list ,
'like_list_part' => $like_list_part ,
'like_button_label' => $like_button_label ,
'like_modal_title' => t ( 'Likes' , 'noun' ),
'dislike_modal_title' => t ( 'Dislikes' , 'noun' ),
'dislike_count' => $dislike_count ,
'dislike_list' => $dislike_list ,
'dislike_list_part' => $dislike_list_part ,
'dislike_button_label' => $dislike_button_label ,
'modal_dismiss' => t ( 'Close' ),
'comment' => ( $item [ 'item_delayed' ] ? '' : $this -> get_comment_box ()),
'previewing' => ( $conv -> is_preview () ? true : false ),
'preview_lbl' => t ( 'This is an unsaved preview' ),
'wait' => t ( 'Please wait' ),
2022-07-24 10:31:38 +00:00
'submid' => str_replace ([ '+' , '=' ], [ '' , '' ], base64_encode ( urlencode ( $item [ 'mid' ]))),
2021-12-03 03:01:39 +00:00
'thread_level' => $thread_level ,
'indentpx' => intval ( get_pconfig ( local_channel (), 'system' , 'thread_indent_px' , get_config ( 'system' , 'thread_indent_px' , 0 ))),
'thread_max' => intval ( get_config ( 'system' , 'thread_maxlevel' , 20 )) + 1
2022-12-02 07:37:29 +00:00
];
2021-12-03 03:01:39 +00:00
2022-12-02 07:37:29 +00:00
$arr = [ 'item' => $item , 'output' => $tmp_item ];
2022-02-12 08:50:48 +00:00
Hook :: call ( 'display_item' , $arr );
2021-12-03 03:01:39 +00:00
$result = $arr [ 'output' ];
$result [ 'children' ] = [];
if ( local_channel () && get_pconfig ( local_channel (), 'system' , 'activitypub' , get_config ( 'system' , 'activitypub' , ACTIVITYPUB_ENABLED ))) {
// 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.
2022-09-01 19:17:27 +00:00
if ( ! ( $observer && ( $profile_addr === $observer [ 'xchan_hash' ] || $profile_addr === $observer [ 'xchan_addr' ]))) {
2021-12-03 03:01:39 +00:00
$result [ 'authors' ][] = $profile_addr ;
}
// Add any mentions from the immediate parent, unless they are mentions of the current viewer or duplicates
if ( isset ( $item [ 'term' ]) && is_array ( $item [ 'term' ])) {
$additional_mentions = [];
foreach ( $item [ 'term' ] as $t ) {
if ( $t [ 'ttype' ] == TERM_MENTION ) {
$additional_mentions [] = ((( $position = strpos ( $t [ 'url' ], 'url=' )) !== false ) ? urldecode ( substr ( $t [ 'url' ], $position + 4 )) : $t [ 'url' ]);
}
}
if ( $additional_mentions ) {
2022-06-17 02:46:54 +00:00
$r = q ( " select hubloc_addr, hubloc_id_url, hubloc_hash from hubloc where hubloc_deleted = 0 and hubloc_id_url in ( " . protect_sprintf ( stringify_array ( $additional_mentions , true )) . " ) " );
2021-12-03 03:01:39 +00:00
if ( $r ) {
foreach ( $r as $rv ) {
$ment = (( $r [ 0 ][ 'hubloc_addr' ]) ? $r [ 0 ][ 'hubloc_addr' ] : $r [ 0 ][ 'hubloc_id_url' ]);
if ( $ment ) {
if ( $observer && $observer [ 'xchan_hash' ] !== $rv [ 'hubloc_hash' ] && ! in_array ( $ment , $result [ 'authors' ])) {
$result [ 'authors' ][] = $ment ;
}
}
}
}
}
}
}
$nb_children = count ( $children );
$total_children = $this -> count_visible_descendants ();
$visible_comments = get_config ( 'system' , 'expanded_comments' , 3 );
if (( $this -> get_display_mode () === 'normal' ) && ( $nb_children > 0 )) {
if ( $children ) {
foreach ( $children as $child ) {
$xz = $child -> get_template_data ( $conv_responses , $thread_level + 1 );
$result [ 'children' ][] = $xz ;
}
}
// Collapse
if ( $total_children > $visible_comments && $thread_level == 1 ) {
$result [ 'children' ][ 0 ][ 'comment_firstcollapsed' ] = true ;
$result [ 'children' ][ 0 ][ 'num_comments' ] = $comment_count_txt ;
$result [ 'children' ][ 0 ][ 'hide_text' ] = sprintf ( t ( '%s show all' ), '<i class="fa fa-chevron-down"></i>' );
}
}
$result [ 'private' ] = $item [ 'item_private' ];
$result [ 'toplevel' ] = ( $this -> is_toplevel () ? 'toplevel_item' : '' );
if ( $this -> is_threaded ()) {
$result [ 'flatten' ] = false ;
$result [ 'threaded' ] = true ;
} else {
$result [ 'flatten' ] = true ;
$result [ 'threaded' ] = false ;
}
return $result ;
}
public function get_id ()
{
return $this -> get_data_value ( 'id' );
}
public function get_display_mode ()
{
return $this -> display_mode ;
}
public function set_display_mode ( $mode )
{
$this -> display_mode = $mode ;
}
public function is_threaded ()
{
return $this -> threaded ;
}
public function get_author ()
{
$xchan = $this -> get_data_value ( 'author' );
if ( $xchan [ 'xchan_addr' ]) {
return $xchan [ 'xchan_addr' ];
}
return $xchan [ 'xchan_url' ];
}
public function set_reload ( $val )
{
$this -> reload = $val ;
}
public function get_reload ()
{
return $this -> reload ;
}
public function set_commentable ( $val )
{
$this -> commentable = $val ;
foreach ( $this -> get_children () as $child ) {
$child -> set_commentable ( $val );
}
}
public function is_commentable ()
{
return $this -> commentable ;
}
/**
* Add a child item
*/
public function add_child ( $item )
{
$item_id = $item -> get_id ();
if ( ! $item_id ) {
logger ( '[ERROR] Item::add_child : Item has no ID!!' , LOGGER_DEBUG );
return false ;
}
if ( $this -> get_child ( $item -> get_id ())) {
logger ( '[WARN] Item::add_child : Item already exists (' . $item -> get_id () . ').' , LOGGER_DEBUG );
return false ;
}
/*
* Only add what will be displayed
*/
if ( activity_match ( $item -> get_data_value ( 'verb' ), ACTIVITY_LIKE ) || activity_match ( $item -> get_data_value ( 'verb' ), ACTIVITY_DISLIKE )) {
return false ;
}
$item -> set_parent ( $this );
$this -> children [] = $item ;
return end ( $this -> children );
}
/**
* Get a child by its ID
*/
public function get_child ( $id )
{
foreach ( $this -> get_children () as $child ) {
if ( $child -> get_id () == $id ) {
return $child ;
}
}
return null ;
}
/**
* Get all our children
*/
public function get_children ()
{
return $this -> children ;
}
/**
* Set our parent
*/
protected function set_parent ( $item )
{
$parent = $this -> get_parent ();
if ( $parent ) {
$parent -> remove_child ( $this );
}
$this -> parent = $item ;
$this -> set_conversation ( $item -> get_conversation ());
}
/**
* Remove our parent
*/
protected function remove_parent ()
{
$this -> parent = null ;
$this -> conversation = null ;
}
/**
* Remove a child
*/
public function remove_child ( $item )
{
$id = $item -> get_id ();
foreach ( $this -> get_children () as $key => $child ) {
if ( $child -> get_id () == $id ) {
$child -> remove_parent ();
unset ( $this -> children [ $key ]);
// Reindex the array, in order to make sure there won't be any trouble on loops using count()
$this -> children = array_values ( $this -> children );
return true ;
}
}
logger ( '[WARN] Item::remove_child : Item is not a child (' . $id . ').' , LOGGER_DEBUG );
return false ;
}
/**
* Get parent item
*/
protected function get_parent ()
{
return $this -> parent ;
}
/**
* set conversation
*/
public function set_conversation ( $conv )
{
$this -> conversation = $conv ;
// Set it on our children too
foreach ( $this -> get_children () as $child ) {
$child -> set_conversation ( $conv );
}
}
/**
* get conversation
*/
public function get_conversation ()
{
return $this -> conversation ;
}
/**
* Get raw data
*
* We shouldn ' t need this
*/
public function get_data ()
{
return $this -> data ;
}
/**
* Get a data value
*
* Returns :
* _ value on success
* _ false on failure
*/
public function get_data_value ( $name )
{
if ( ! isset ( $this -> data [ $name ])) {
return false ;
}
return $this -> data [ $name ];
}
/**
* Get template
*/
public function get_template ()
{
return $this -> template ;
}
public function set_template ( $t )
{
$this -> template = $t ;
}
/**
* Check if this is a toplevel post
*/
private function is_toplevel ()
{
return $this -> toplevel ;
}
/**
* Count the total of our descendants
*/
private function count_descendants ()
{
$children = $this -> get_children ();
$total = count ( $children );
if ( $total > 0 ) {
foreach ( $children as $child ) {
$total += $child -> count_descendants ();
}
}
return $total ;
}
public function count_visible_descendants ()
{
$total = 0 ;
$children = $this -> get_children ();
if ( $children ) {
foreach ( $children as $child ) {
if ( ! visible_activity ( $child -> data )) {
continue ;
}
$total ++ ;
$total += $child -> count_visible_descendants ();
}
}
return $total ;
}
private function count_unseen_descendants ()
{
$children = $this -> get_children ();
$total = count ( $children );
if ( $total > 0 ) {
$total = 0 ;
foreach ( $children as $child ) {
if ( ! visible_activity ( $child -> data )) {
continue ;
}
if ( intval ( $child -> data [ 'item_unseen' ])) {
$total ++ ;
}
}
}
return $total ;
}
/**
* Get the template for the comment box
*/
private function get_comment_box_template ()
{
return $this -> comment_box_template ;
}
/**
* Get the comment box
*
* Returns :
* _ The comment box string ( empty if no comment box )
* _ false on failure
*/
private function get_comment_box ( $indent = 0 )
{
if ( ! $this -> is_toplevel () && ! get_config ( 'system' , 'thread_allow' , true )) {
return '' ;
}
$comment_box = '' ;
$conv = $this -> get_conversation ();
// logger('Commentable conv: ' . $conv->is_commentable());
if ( ! $this -> is_commentable ()) {
2022-09-01 19:17:27 +00:00
return false ;
2021-12-03 03:01:39 +00:00
}
2022-02-12 20:43:29 +00:00
$template = Theme :: get_template ( $this -> get_comment_box_template ());
2021-12-03 03:01:39 +00:00
$observer = $conv -> get_observer ();
2022-12-02 07:37:29 +00:00
$arr = [ 'comment_buttons' => '' , 'id' => $this -> get_id ()];
2022-02-12 08:50:48 +00:00
Hook :: call ( 'comment_buttons' , $arr );
2021-12-03 03:01:39 +00:00
$comment_buttons = $arr [ 'comment_buttons' ];
2022-01-25 23:20:02 +00:00
$feature_auto_save_draft = (( Features :: enabled ( $conv -> get_profile_owner (), 'auto_save_draft' )) ? " true " : " false " );
2021-12-03 03:01:39 +00:00
$permanent_draft = (( intval ( $conv -> get_profile_owner ()) === intval ( local_channel ()) && Apps :: system_app_installed ( $conv -> get_profile_owner (), 'Drafts' )) ? ( 'Save draft' ) : EMPTY_STR );
2022-12-02 07:37:29 +00:00
$comment_box = replace_macros ( $template , [
2021-12-03 03:01:39 +00:00
'$return_path' => '' ,
'$threaded' => $this -> is_threaded (),
'$jsreload' => $conv -> reload ,
'$type' => (( $conv -> get_mode () === 'channel' ) ? 'wall-comment' : 'net-comment' ),
'$id' => $this -> get_id (),
'$parent' => $this -> get_id (),
'$comment_buttons' => $comment_buttons ,
'$profile_uid' => $conv -> get_profile_owner (),
'$mylink' => $observer [ 'xchan_url' ],
'$mytitle' => t ( 'This is you' ),
'$myphoto' => $observer [ 'xchan_photo_s' ],
'$comment' => t ( 'Comment' ),
'$submit' => t ( 'Submit' ),
'$edat' => EMPTY_STR ,
'$edbold' => t ( 'Bold' ),
'$editalic' => t ( 'Italic' ),
'$eduline' => t ( 'Underline' ),
'$edquote' => t ( 'Quote' ),
'$edcode' => t ( 'Code' ),
'$edimg' => t ( 'Image' ),
2023-02-18 12:17:36 +00:00
'$edatt' => t ( 'Embed a File' ),
2021-12-03 03:01:39 +00:00
'$edurl' => t ( 'Insert Link' ),
'$edvideo' => t ( 'Video' ),
'$preview' => t ( 'Preview' ),
'$reset' => t ( 'Reset' ),
'$indent' => $indent ,
'$can_upload' => ( perm_is_allowed ( $conv -> get_profile_owner (), get_observer_hash (), 'write_storage' ) && $conv -> is_uploadable ()),
2022-12-02 07:37:29 +00:00
'$feature_encrypt' => Apps :: system_app_installed ( $conv -> get_profile_owner (), 'Secrets' ),
'$feature_markup' => Apps :: system_app_installed ( $conv -> get_profile_owner (), 'Markup' ),
2021-12-03 03:01:39 +00:00
'$encrypt' => t ( 'Encrypt text' ),
'$cipher' => $conv -> get_cipher (),
'$sourceapp' => App :: $sourcename ,
'$observer' => get_observer_hash (),
2022-12-02 07:37:29 +00:00
'$anoncomments' => ( $conv -> get_mode () === 'channel' || $conv -> get_mode () === 'display' ) && perm_is_allowed ( $conv -> get_profile_owner (), '' , 'post_comments' ),
2021-12-03 03:01:39 +00:00
'$anonname' => [ 'anonname' , t ( 'Your full name (required)' ) ],
'$anonmail' => [ 'anonmail' , t ( 'Your email address (required)' ) ],
'$anonurl' => [ 'anonurl' , t ( 'Your website URL (optional)' ) ],
'$auto_save_draft' => $feature_auto_save_draft ,
'$save' => $permanent_draft ,
'$top' => $this -> is_toplevel ()
2022-12-02 07:37:29 +00:00
]);
2021-12-03 03:01:39 +00:00
return $comment_box ;
}
private function get_redirect_url ()
{
return $this -> redirect_url ;
}
/**
* Check if we are a wall to wall item and set the relevant properties
*/
protected function check_wall_to_wall ()
{
$conv = $this -> get_conversation ();
$this -> wall_to_wall = false ;
$this -> owner_url = '' ;
$this -> owner_photo = '' ;
$this -> owner_name = '' ;
$this -> owner_censored = false ;
if ( $conv -> get_mode () === 'channel' ) {
return ;
}
if ( $this -> is_toplevel () && ( $this -> get_data_value ( 'author_xchan' ) != $this -> get_data_value ( 'owner_xchan' ))) {
$this -> owner_url = chanlink_hash ( $this -> data [ 'owner' ][ 'xchan_hash' ]);
$this -> owner_photo = $this -> data [ 'owner' ][ 'xchan_photo_m' ];
$this -> owner_name = $this -> data [ 'owner' ][ 'xchan_name' ];
2022-04-04 10:46:22 +00:00
$this -> owner_addr = $this -> data [ 'owner' ][ 'xchan_addr' ];
2021-12-03 03:01:39 +00:00
$this -> wall_to_wall = true ;
}
// present friend-of-friend conversations from hyperdrive as relayed posts from the first friend
// we find among the respondents.
if ( $this -> is_toplevel () && ( ! $this -> data [ 'owner' ][ 'abook_id' ])) {
if ( $this -> data [ 'children' ]) {
$friend = $this -> find_a_friend ( $this -> data [ 'children' ]);
if ( $friend ) {
$this -> owner_url = $friend [ 'url' ];
$this -> owner_photo = $friend [ 'photo' ];
$this -> owner_name = $friend [ 'name' ];
2022-04-04 10:46:22 +00:00
$this -> owner_addr = $friend [ 'addr' ];
2021-12-03 03:01:39 +00:00
$this -> owner_censored = $friend [ 'censored' ];
$this -> wall_to_wall = true ;
}
}
}
}
private function find_a_friend ( $items )
{
$ret = null ;
if ( $items ) {
foreach ( $items as $child ) {
if ( $child [ 'author' ][ 'abook_id' ] && ( ! intval ( $child [ 'author' ][ 'abook_self' ]))) {
return [
2022-09-01 19:17:27 +00:00
'url' => chanlink_hash ( $child [ 'author' ][ 'xchan_hash' ]),
2021-12-03 03:01:39 +00:00
'photo' => $child [ 'author' ][ 'xchan_photo_m' ],
2022-09-01 19:17:27 +00:00
'name' => $child [ 'author' ][ 'xchan_name' ],
'addr' => $child [ 'author' ][ 'xchan_addr' ],
2022-12-02 07:37:29 +00:00
'censored' => $child [ 'author' ][ 'xchan_censored' ] || $child [ 'author' ][ 'abook_censor' ]
2021-12-03 03:01:39 +00:00
];
2022-09-01 19:17:27 +00:00
}
if ( $child [ 'children' ]) {
$ret = $this -> find_a_friend ( $child [ 'children' ]);
if ( $ret ) {
break ;
2021-12-03 03:01:39 +00:00
}
}
}
}
return $ret ;
}
private function is_wall_to_wall ()
{
return $this -> wall_to_wall ;
}
private function get_owner_url ()
{
return $this -> owner_url ;
}
private function get_owner_photo ()
{
return $this -> owner_photo ;
}
private function get_owner_name ()
{
return $this -> owner_name ;
}
2022-04-04 10:46:22 +00:00
private function get_owner_addr ()
{
return $this -> owner_addr ;
}
2021-12-03 03:01:39 +00:00
private function is_visiting ()
{
return $this -> visiting ;
}
2012-09-11 05:00:56 +00:00
}