diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/api_auth.php | 3 | ||||
-rw-r--r-- | include/api_zot.php | 11 | ||||
-rw-r--r-- | include/attach.php | 82 | ||||
-rw-r--r-- | include/bbcode.php | 36 | ||||
-rw-r--r-- | include/channel.php | 13 | ||||
-rw-r--r-- | include/contact_widgets.php | 87 | ||||
-rw-r--r-- | include/conversation.php | 57 | ||||
-rw-r--r-- | include/crypto.php | 4 | ||||
-rw-r--r-- | include/event.php | 6 | ||||
-rw-r--r-- | include/features.php | 25 | ||||
-rw-r--r-- | include/feedutils.php | 42 | ||||
-rw-r--r-- | include/follow.php | 3 | ||||
-rw-r--r-- | include/html2bbcode.php | 31 | ||||
-rw-r--r-- | include/import.php | 74 | ||||
-rwxr-xr-x | include/items.php | 86 | ||||
-rw-r--r-- | include/markdown.php | 2 | ||||
-rw-r--r-- | include/message.php | 2 | ||||
-rw-r--r-- | include/nav.php | 28 | ||||
-rw-r--r-- | include/network.php | 3 | ||||
-rwxr-xr-x | include/plugin.php | 88 | ||||
-rw-r--r-- | include/socgraph.php | 5 | ||||
-rw-r--r-- | include/taxonomy.php | 113 | ||||
-rw-r--r-- | include/text.php | 133 | ||||
-rw-r--r-- | include/zid.php | 4 | ||||
-rw-r--r-- | include/zot.php | 13 |
25 files changed, 776 insertions, 175 deletions
diff --git a/include/api_auth.php b/include/api_auth.php index 0818fa54b..5c0bcb317 100644 --- a/include/api_auth.php +++ b/include/api_auth.php @@ -52,6 +52,7 @@ function api_login(&$a){ /* Signature authentication */ if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,9) === 'Signature') { + if($head !== 'HTTP_AUTHORIZATION') { $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head]; continue; @@ -59,7 +60,7 @@ function api_login(&$a){ $sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); if($sigblock) { - $keyId = $sigblock['keyId']; + $keyId = str_replace('acct:','',$sigblock['keyId']); if($keyId) { $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", dbesc($keyId) diff --git a/include/api_zot.php b/include/api_zot.php index aaa9ee497..54f905b4c 100644 --- a/include/api_zot.php +++ b/include/api_zot.php @@ -150,7 +150,11 @@ $start = ((array_key_exists('start',$_REQUEST)) ? intval($_REQUEST['start']) : 0); $records = ((array_key_exists('records',$_REQUEST)) ? intval($_REQUEST['records']) : 0); - $x = attach_list_files(api_user(),get_observer_hash(),$hash,$filename,$filetype,'created asc',$start,$records); + $since = ((array_key_exists('since',$_REQUEST)) ? datetime_convert(date_default_timezone_get(),'UTC',$_REQUEST['since']) : NULL_DATE); + $until = ((array_key_exists('until',$_REQUEST)) ? datetime_convert(date_default_timezone_get(),'UTC',$_REQUEST['until']) : datetime_convert()); + + $x = attach_list_files(api_user(),get_observer_hash(),$hash,$filename,$filetype,'created asc',$start,$records, $since, $until); + if($start || $records) { $x['start'] = $start; $x['records'] = count($x['results']); @@ -226,7 +230,10 @@ if(! $_REQUEST['file_id']) return false; - $ret = attach_export_data(api_user(),$_REQUEST['file_id']); + $channel = channelx_by_n(api_user()); + + $ret = attach_export_data($channel,$_REQUEST['file_id']); + if($ret) { json_return_and_die($ret); } diff --git a/include/attach.php b/include/attach.php index 7988286c9..39269eb03 100644 --- a/include/attach.php +++ b/include/attach.php @@ -189,7 +189,7 @@ function attach_count_files($channel_id, $observer, $hash = '', $filename = '', * * \e array|boolean \b results array with results, or false * * \e string \b message with error messages if any */ -function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $filetype = '', $orderby = 'created desc', $start = 0, $entries = 0) { +function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $filetype = '', $orderby = 'created desc', $start = 0, $entries = 0, $since = '', $until = '') { $ret = array('success' => false); @@ -198,6 +198,7 @@ function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $ return $ret; } + require_once('include/security.php'); $sql_extra = permissions_sql($channel_id); @@ -213,14 +214,22 @@ function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $ if($entries) $limit = " limit " . intval($start) . ", " . intval($entries) . " "; - // Retrieve all columns except 'data' + if(! $since) + $since = NULL_DATE; + + if(! $until) + $until = datetime_convert(); + + $sql_extra .= " and created >= '" . dbesc($since) . "' and created <= '" . dbesc($until) . "' "; + + // Retrieve all columns except 'content' $r = q("select id, aid, uid, hash, filename, filetype, filesize, revision, folder, os_path, display_path, os_storage, is_dir, is_photo, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where uid = %d $sql_extra ORDER BY $orderby $limit", intval($channel_id) ); $ret['success'] = ((is_array($r)) ? true : false); - $ret['results'] = ((is_array($r)) ? $r : false); + $ret['results'] = ((is_array($r)) ? $r : []); return $ret; } @@ -276,6 +285,8 @@ function attach_by_hash($hash, $observer_hash, $rev = 0) { return $ret; } + $r[0]['content'] = dbunescbin($r[0]['content']); + if($r[0]['folder']) { $x = attach_can_view_folder($r[0]['uid'],$observer_hash,$r[0]['folder']); if(! $x) { @@ -297,6 +308,11 @@ function attach_can_view_folder($uid,$ob_hash,$folder_hash) { $hash = $folder_hash; $result = false; + if(! $folder_hash) { + return perm_is_allowed($uid,$ob_hash,'view_storage'); + } + + do { $r = q("select folder from attach where hash = '%s' and uid = %d $sql_extra", dbesc($hash), @@ -534,6 +550,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $type = $f['type']; } else { if(! x($_FILES,'userfile')) { + logger('No source file'); $ret['message'] = t('No source file.'); return $ret; } @@ -574,6 +591,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { intval($channel_id) ); if(! $x) { + logger('update file source not found'); $ret['message'] = t('Cannot locate file to revise/update'); return $ret; } @@ -715,6 +733,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $maxfilesize = get_config('system','maxfilesize'); if(($maxfilesize) && ($filesize > $maxfilesize)) { + logger('quota_exceeded'); $ret['message'] = sprintf( t('File exceeds size limit of %d'), $maxfilesize); if($remove_when_processed) @unlink($src); @@ -735,6 +754,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { intval($channel['channel_account_id']) ); if(($r) && (($r[0]['total'] + $filesize) > ($limit - $existing_size))) { + logger('service_class limit exceeded'); $ret['message'] = upgrade_message(true) . sprintf(t("You have reached your limit of %1$.0f Mbytes attachment storage."), $limit / 1024000); if($remove_when_processed) @unlink($src); @@ -767,8 +787,15 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $os_path = $os_relpath; $display_path = ltrim($pathname . '/' . $filename,'/'); - if($src) - @file_put_contents($os_basepath . $os_relpath,@file_get_contents($src)); + if($src) { + $istream = @fopen($src,'rb'); + $ostream = @fopen($os_basepath . $os_relpath,'wb'); + if($istream && $ostream) { + pipe_streams($istream,$ostream,65535); + fclose($istream); + fclose($ostream); + } + } if(array_key_exists('created', $arr)) $created = $arr['created']; @@ -1639,7 +1666,7 @@ function find_filename_by_hash($channel_id, $attachHash) { * @param int $bufsize size of chunk, default 16384 * @return number with the size */ -function pipe_streams($in, $out, $bufize = 16384) { +function pipe_streams($in, $out, $bufsize = 16384) { $size = 0; while (!feof($in)) $size += fwrite($out, fread($in, $bufsize)); @@ -2037,6 +2064,7 @@ function attach_export_data($channel, $resource_id, $deleted = false) { if($hash_ptr === $resource_id) { $attach_ptr = $r[0]; } + $r[0]['content'] = dbunescbin($r[0]['content']); $hash_ptr = $r[0]['folder']; $paths[] = $r[0]; @@ -2049,6 +2077,9 @@ function attach_export_data($channel, $resource_id, $deleted = false) { if($attach_ptr['is_photo']) { + + // This query could potentially result in a few megabytes of data use. + $r = q("select * from photo where resource_id = '%s' and uid = %d order by imgscale asc", dbesc($resource_id), intval($channel['channel_id']) @@ -2060,6 +2091,17 @@ function attach_export_data($channel, $resource_id, $deleted = false) { $ret['photo'] = $r; } +// This query can be used instead in memory starved environments. There will be a corresponding +// performance hit during sync because the data will need to be fetched over the network. +// $r = q("select aid,uid,xchan,resource_id,created,edited,title,description,album,filename,mimetype,height,width,filesize,imgscale,photo_usage,profile,is_nsfw,os_storage,display_path,photo_flags,allow_cid,allow_gid,deny_cid,deny_gid from photo where resource_id = '%s' and uid = %d order by imgscale asc", +// dbesc($resource_id), +// intval($channel['channel_id']) +// ); + +// if($r) { +// $ret['photo'] = $r; +// } + $r = q("select * from item where resource_id = '%s' and resource_type = 'photo' and uid = %d ", dbesc($resource_id), intval($channel['channel_id']) @@ -2222,7 +2264,6 @@ function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpat * the attach.hash of the new parent folder, which must already exist. If $new_folder_hash is blank or empty, * the file is relocated to the root of the channel's storage area. * - * @fixme: this operation is currently not synced to clones !! * * @param int $channel_id * @param int $resource_id @@ -2232,7 +2273,7 @@ function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpat function attach_move($channel_id, $resource_id, $new_folder_hash) { $c = channelx_by_n($channel_id); - if(! $c) + if(! ($c && $resource_id)) return false; $r = q("select * from attach where hash = '%s' and uid = %d limit 1", @@ -2244,13 +2285,32 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) { $oldstorepath = dbunescbin($r[0]['content']); + if($r[0]['is_dir']) { + $move_success = true; + $x = q("select hash from attach where folder = '%s' and uid = %d", + dbesc($r[0]['hash']), + intval($channel_id) + ); + if($x) { + foreach($x as $xv) { + $rs = attach_move($channel_id,$xv['hash'],$r[0]['hash']); + if(! $rs) { + $move_success = false; + break; + } + } + } + return $move_success; + } + + if($new_folder_hash) { - $n = q("select * from attach where hash = '%s' and uid = %d limit 1", + $n = q("select * from attach where hash = '%s' and uid = %d and is_dir = 1 limit 1", dbesc($new_folder_hash), intval($channel_id) ); if(! $n) - return; + return false; $newdirname = $n[0]['filename']; $newstorepath = dbunescbin($n[0]['content']) . '/' . $resource_id; @@ -2506,7 +2566,7 @@ function save_chunk($channel,$start,$end,$len) { } if(($len - 1) == $end) { unlink($tmp_path); - $result['name'] = $_FILES['files']['tmp_name']; + $result['name'] = $_FILES['files']['name']; $result['type'] = $_FILES['files']['type']; $result['tmp_name'] = $new_path; $result['error'] = 0; diff --git a/include/bbcode.php b/include/bbcode.php index 050ab2d29..de32bd57a 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -246,8 +246,8 @@ function bb_parse_element($match) { $j = json_decode(base64url_decode($match[1]),true); if ($j && local_channel()) { - $text = sprintf( t('Install %s element: '), translate_design_element($j['type'])) . $j['pagetitle']; - $o = EOL . '<a href="#" onclick="importElement(\'' . $match[1] . '\'); return false;" >' . $text . '</a>' . EOL; + $text = sprintf( t('Install %1$s element %2$s'), translate_design_element($j['type']), $j['pagetitle']); + $o = EOL . '<button class="btn btn-primary" onclick="importElement(\'' . $match[1] . '\'); return false;" >' . $text . '</button>' . EOL; } else { $text = sprintf( t('This post contains an installable %s element, however you lack permissions to install it on this site.' ), translate_design_element($j['type'])) . $j['pagetitle']; @@ -329,6 +329,8 @@ function bb_ShareAttributes($match) { if(strpos($link,'/cards/')) $type = t('card'); + elseif(strpos($link,'/articles/')) + $type = t('article'); else $type = t('post'); @@ -426,6 +428,16 @@ function bb_spoilertag($match) { return '<div onclick="openClose(\'opendiv-' . $rnd . '\'); return false;" class="fakelink">' . $openclose . '</div><blockquote id="opendiv-' . $rnd . '" style="display: none;">' . $text . '</blockquote>'; } +function bb_summary($match) { + $rnd1 = mt_rand(); + $rnd2 = mt_rand(); + $rnd3 = mt_rand(); + $rnd4 = mt_rand(); + + return $match[1] . '<div style="display: block;" id="opendiv-' . $rnd2 . '">' . $match[2] . '</div><div style="display: block;" id="opendiv-' . $rnd3 . '" onclick="openClose(\'opendiv-' . $rnd1 . '\'); openClose(\'opendiv-' . $rnd2 . '\'); openClose(\'opendiv-' . $rnd3 . '\'); openClose(\'opendiv-' . $rnd4 . '\'); return false;" class="fakelink">' . t('View article') . '</div><div style="display: none;" id="opendiv-' . $rnd4 . '" onclick="openClose(\'opendiv-' . $rnd1 . '\'); openClose(\'opendiv-' . $rnd2 . '\'); openClose(\'opendiv-' . $rnd3 . '\'); openClose(\'opendiv-' . $rnd4 . '\'); return false;" class="fakelink">' . t('View summary') . '</div><div id="opendiv-' . $rnd1 . '" style="display: none;">' . $match[3] . '</div>'; +} + + function bb_definitionList($match) { // $match[1] is the markup styles for the "terms" in the definition list. // $match[2] is the content between the [dl]...[/dl] tags @@ -705,10 +717,12 @@ function parseIdentityAwareHTML($Text) { return $Text; } - // BBcode 2 HTML was written by WAY2WEB.net - // extended to work with Mistpark/Friendica/Redmatrix/Hubzilla - Mike Macgirvin -function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) { +function bbcode($Text, $options = []) { + + $preserve_nl = ((array_key_exists('preserve_nl',$options)) ? $options['preserve_nl'] : false); + $tryoembed = ((array_key_exists('tryoembed',$options)) ? $options['tryoembed'] : true); + $cache = ((array_key_exists('cache',$options)) ? $options['cache'] : false); call_hooks('bbcode_filter', $Text); @@ -937,27 +951,34 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) // Check for h1 if (strpos($Text,'[h1]') !== false) { $Text = preg_replace("(\[h1\](.*?)\[\/h1\])ism",'<h1>$1</h1>',$Text); + $Text = str_replace('</h1><br />', '</h1>', $Text); } // Check for h2 if (strpos($Text,'[h2]') !== false) { $Text = preg_replace("(\[h2\](.*?)\[\/h2\])ism",'<h2>$1</h2>',$Text); + $Text = str_replace('</h2><br />', '</h2>', $Text); } // Check for h3 if (strpos($Text,'[h3]') !== false) { $Text = preg_replace("(\[h3\](.*?)\[\/h3\])ism",'<h3>$1</h3>',$Text); + $Text = str_replace('</h3><br />', '</h3>', $Text); } // Check for h4 if (strpos($Text,'[h4]') !== false) { $Text = preg_replace("(\[h4\](.*?)\[\/h4\])ism",'<h4>$1</h4>',$Text); + $Text = str_replace('</h4><br />', '</h4>', $Text); } // Check for h5 if (strpos($Text,'[h5]') !== false) { $Text = preg_replace("(\[h5\](.*?)\[\/h5\])ism",'<h5>$1</h5>',$Text); + $Text = str_replace('</h5><br />', '</h5>', $Text); } // Check for h6 if (strpos($Text,'[h6]') !== false) { $Text = preg_replace("(\[h6\](.*?)\[\/h6\])ism",'<h6>$1</h6>',$Text); + $Text = str_replace('</h6><br />', '</h6>', $Text); } + // Check for table of content without params while(strpos($Text,'[toc]') !== false) { $toc_id = 'toc-' . random_string(10); @@ -1051,6 +1072,11 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", 'bb_code_options', $Text); } + + if(strpos($Text,'[/summary]') !== false) { + $Text = preg_replace_callback("/^(.*?)\[summary\](.*?)\[\/summary\](.*?)$/ism", 'bb_summary', $Text); + } + // Check for [spoiler] text $endlessloop = 0; while ((strpos($Text, "[/spoiler]")!== false) and (strpos($Text, "[spoiler]") !== false) and (++$endlessloop < 20)) { diff --git a/include/channel.php b/include/channel.php index 708e74176..b9adc588b 100644 --- a/include/channel.php +++ b/include/channel.php @@ -325,7 +325,7 @@ function create_identity($arr) { 'hubloc_guid_sig' => $sig, 'hubloc_hash' => $hash, 'hubloc_addr' => channel_reddress($ret['channel']), - 'hubloc_primary' => $primary, + 'hubloc_primary' => intval($primary), 'hubloc_url' => z_root(), 'hubloc_url_sig' => base64url_encode(rsa_sign(z_root(),$ret['channel']['channel_prvkey'])), 'hubloc_host' => App::get_hostname(), @@ -1490,7 +1490,7 @@ function gender_icon($gender) { } -function advanced_profile(&$a) { +function advanced_profile() { require_once('include/text.php'); if(! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),'view_profile')) return ''; @@ -1982,12 +1982,17 @@ function get_channel_default_perms($uid) { } -function profiles_build_sync($channel_id) { +function profiles_build_sync($channel_id,$send = true) { $r = q("select * from profile where uid = %d", intval($channel_id) ); if($r) { - build_sync_packet($channel_id,array('profile' => $r)); + if($send) { + build_sync_packet($channel_id,array('profile' => $r)); + } + else { + return $r; + } } } diff --git a/include/contact_widgets.php b/include/contact_widgets.php index 9cc9f0baf..a105bca19 100644 --- a/include/contact_widgets.php +++ b/include/contact_widgets.php @@ -72,21 +72,66 @@ function categories_widget($baseurl,$selected = '') { $item_normal = item_normal(); $terms = array(); + $r = q("select distinct(term.term) from term join item on term.oid = item.id + where item.uid = %d + and term.uid = item.uid + and term.ttype = %d + and term.otype = %d + and item.owner_xchan = '%s' + and item.item_wall = 1 + and item.verb != '%s' + $item_normal + $sql_extra + order by term.term asc", + intval(App::$profile['profile_uid']), + intval(TERM_CATEGORY), + intval(TERM_OBJ_POST), + dbesc(App::$profile['channel_hash']), + dbesc(ACTIVITY_UPDATE) + ); + if($r && count($r)) { + foreach($r as $rr) + $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); + + return replace_macros(get_markup_template('categories_widget.tpl'),array( + '$title' => t('Categories'), + '$desc' => '', + '$sel_all' => (($selected == '') ? 'selected' : ''), + '$all' => t('Everything'), + '$terms' => $terms, + '$base' => $baseurl, + + )); + } + return ''; +} + +function cardcategories_widget($baseurl,$selected = '') { + + if(! feature_enabled(App::$profile['profile_uid'],'categories')) + return ''; + + $sql_extra = item_permissions_sql(App::$profile['profile_uid']); + + $item_normal = "and item.item_hidden = 0 and item.item_type = 6 and item.item_deleted = 0 + and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 + and item.item_blocked = 0 "; + + $terms = array(); $r = q("select distinct(term.term) - from term join item on term.oid = item.id - where item.uid = %d - and term.uid = item.uid - and term.ttype = %d + from term join item on term.oid = item.id + where item.uid = %d + and term.uid = item.uid + and term.ttype = %d and term.otype = %d - and item.owner_xchan = '%s' - and item.item_wall = 1 + and item.owner_xchan = '%s' $item_normal $sql_extra - order by term.term asc", + order by term.term asc", intval(App::$profile['profile_uid']), - intval(TERM_CATEGORY), + intval(TERM_CATEGORY), intval(TERM_OBJ_POST), - dbesc(App::$profile['channel_hash']) + dbesc(App::$profile['channel_hash']) ); if($r && count($r)) { foreach($r as $rr) @@ -105,32 +150,33 @@ function categories_widget($baseurl,$selected = '') { return ''; } -function cardcategories_widget($baseurl,$selected = '') { + +function articlecategories_widget($baseurl,$selected = '') { if(! feature_enabled(App::$profile['profile_uid'],'categories')) return ''; $sql_extra = item_permissions_sql(App::$profile['profile_uid']); - $item_normal = "and item.item_hidden = 0 and item.item_type = 6 and item.item_deleted = 0 + $item_normal = "and item.item_hidden = 0 and item.item_type = 7 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 and item.item_blocked = 0 "; $terms = array(); $r = q("select distinct(term.term) - from term join item on term.oid = item.id - where item.uid = %d - and term.uid = item.uid - and term.ttype = %d + from term join item on term.oid = item.id + where item.uid = %d + and term.uid = item.uid + and term.ttype = %d and term.otype = %d - and item.owner_xchan = '%s' + and item.owner_xchan = '%s' $item_normal $sql_extra - order by term.term asc", + order by term.term asc", intval(App::$profile['profile_uid']), - intval(TERM_CATEGORY), + intval(TERM_CATEGORY), intval(TERM_OBJ_POST), - dbesc(App::$profile['channel_hash']) + dbesc(App::$profile['channel_hash']) ); if($r && count($r)) { foreach($r as $rr) @@ -151,6 +197,9 @@ function cardcategories_widget($baseurl,$selected = '') { + + + function common_friends_visitor_widget($profile_uid,$cnt = 25) { if(local_channel() == $profile_uid) diff --git a/include/conversation.php b/include/conversation.php index 1cbd9116c..77694deb3 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -482,10 +482,10 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa $previewing = (($preview) ? ' preview ' : ''); $preview_lbl = t('This is an unsaved preview'); - if ($mode === 'network') { + if (in_array($mode, [ 'network', 'pubstream'])) { $profile_owner = local_channel(); - $page_writeable = true; + $page_writeable = ((local_channel()) ? true : false); if (!$update) { // The special div is needed for liveUpdate to kick in for this page. @@ -513,6 +513,12 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa } } + elseif ($mode === 'hq') { + $profile_owner = local_channel(); + $page_writeable = true; + $live_update_div = '<div id="live-hq"></div>' . "\r\n"; + } + elseif ($mode === 'channel') { $profile_owner = App::$profile['profile_uid']; $page_writeable = ($profile_owner == local_channel()); @@ -539,6 +545,15 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa $jsreload = $_SESSION['return_url']; } + elseif ($mode === 'articles') { + $profile_owner = App::$profile['profile_uid']; + $page_writeable = ($profile_owner == local_channel()); + $live_update_div = '<div id="live-articles"></div>' . "\r\n" + . "<script> var profile_uid = " . App::$profile['profile_uid'] + . "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . "; </script>\r\n"; + $jsreload = $_SESSION['return_url']; + } + elseif ($mode === 'display') { $profile_owner = local_channel(); @@ -619,13 +634,14 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa // "New Item View" on network page or search page results // - just loop through the items and format them minimally for display - - //$tpl = get_markup_template('search_item.tpl'); $tpl = 'search_item.tpl'; foreach($items as $item) { - $x = [ 'mode' => $mode, 'item' => $item ]; + $x = [ + 'mode' => $mode, + 'item' => $item + ]; call_hooks('stream_item',$x); if($x['item']['blocked']) @@ -646,14 +662,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) && ($item['id'] != $item['parent'])) continue; -// $nickname = $item['nickname']; } -// else -// $nickname = App::$user['nickname']; - -// $profile_name = ((strlen($item['author-name'])) ? $item['author-name'] : $item['name']); -// if($item['author-link'] && (! $item['author-name'])) -// $profile_name = $item['author-link']; $sp = false; $profile_link = best_link_url($item,$sp); @@ -662,8 +671,6 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa else $profile_link = zid($profile_link); -// $normalised = normalise_link((strlen($item['author-link'])) ? $item['author-link'] : $item['url']); - $profile_name = $item['author']['xchan_name']; $profile_link = $item['author']['xchan_url']; $profile_avatar = $item['author']['xchan_photo_m']; @@ -714,7 +721,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa $conv_link_mid = (($mode == 'moderate') ? $item['parent_mid'] : $item['mid']); - $conv_link = (($item['item_type'] == ITEM_TYPE_CARD) ? $item['plink'] : z_root() . '/display/' . gen_link_id($conv_link_mid)); + $conv_link = ((in_array($item['item_type'],[ ITEM_TYPE_CARD, ITEM_TYPE_ARTICLE] )) ? $item['plink'] : z_root() . '/display/' . gen_link_id($conv_link_mid)); $tmp_item = array( @@ -748,8 +755,8 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa 'forged' => $forged, 'txt_cats' => t('Categories:'), 'txt_folders' => t('Filed under:'), - 'has_cats' => ((count($body['categories'])) ? 'true' : ''), - 'has_folders' => ((count($body['folders'])) ? 'true' : ''), + 'has_cats' => (($body['categories']) ? 'true' : ''), + 'has_folders' => (($body['folders']) ? 'true' : ''), 'text' => strip_tags($body['html']), 'ago' => relative_date($item['created']), 'app' => $item['app'], @@ -759,6 +766,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa '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')):''), 'location' => $location, + 'divider' => false, 'indent' => '', 'owner_name' => $owner_name, 'owner_url' => $owner_url, @@ -1465,7 +1473,7 @@ function sort_item_children($items) { $result = $items; usort($result,'sort_thr_created_rev'); foreach($result as $k => $i) { - if(count($result[$k]['children'])) { + if($result[$k]['children']) { $result[$k]['children'] = sort_item_children($result[$k]['children']); } } @@ -1475,7 +1483,7 @@ function sort_item_children($items) { function add_children_to_list($children, &$arr) { foreach($children as $y) { $arr[] = $y; - if(count($y['children'])) + if($y['children']) add_children_to_list($y['children'], $arr); } } @@ -1887,6 +1895,17 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ 'icon' => 'list' ); } + + if(feature_enabled($uid,'articles')) { + $tabs[] = array( + 'label' => t('articles'), + 'url' => z_root() . '/articles/' . $nickname, + 'sel' => ((argv(0) == 'articles') ? 'active' : ''), + 'title' => t('View Articles'), + 'id' => 'articles-tab', + 'icon' => 'file-text-o' + ); + } if($has_webpages && feature_enabled($uid,'webpages')) { $tabs[] = array( diff --git a/include/crypto.php b/include/crypto.php index 622add4dc..b990b18d9 100644 --- a/include/crypto.php +++ b/include/crypto.php @@ -22,13 +22,13 @@ function rsa_verify($data,$sig,$key,$alg = 'sha256') { $alg = OPENSSL_ALGO_SHA256; $verify = @openssl_verify($data,$sig,$key,$alg); - if(! $verify) { + if($verify === (-1)) { while($msg = openssl_error_string()) logger('openssl_verify: ' . $msg,LOGGER_NORMAL,LOG_ERR); btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); } - return $verify; + return (($verify > 0) ? true : false); } function pkcs5_pad ($text, $blocksize) diff --git a/include/event.php b/include/event.php index 282c1a9d7..c1cf59425 100644 --- a/include/event.php +++ b/include/event.php @@ -6,6 +6,8 @@ use Sabre\VObject; +require_once('include/bbcode.php'); + /** * @brief Returns an event as HTML. * @@ -14,7 +16,6 @@ use Sabre\VObject; */ function format_event_html($ev) { - require_once('include/bbcode.php'); if(! ((is_array($ev)) && count($ev))) return ''; @@ -192,7 +193,7 @@ function format_todo_ical($ev) { function format_ical_text($s) { - require_once('include/bbcode.php'); + require_once('include/html2plain.php'); $s = html2plain(bbcode($s)); @@ -983,7 +984,6 @@ function event_store_item($arr, $event) { require_once('include/datetime.php'); require_once('include/items.php'); - require_once('include/bbcode.php'); $item = null; diff --git a/include/features.php b/include/features.php index d8d98dbaa..36eb74a9d 100644 --- a/include/features.php +++ b/include/features.php @@ -126,6 +126,16 @@ function get_features($filtered = true) { feature_level('cards',1), ], + + [ + 'articles', + t('Articles'), + t('Create interactive articles'), + false, + get_config('feature_lock','articles'), + feature_level('articles',1), + ], + [ 'nav_channel_select', t('Navigation Channel Select'), @@ -364,15 +374,6 @@ function get_features($filtered = true) { t('Post/Comment Tools'), [ - 'markdown', - t('Markdown'), - t('Use markdown for editing posts'), - false, - get_config('feature_lock','markdown'), - feature_level('markdown',2), - ], - - [ 'commtag', t('Community Tagging'), t('Ability to tag existing posts'), @@ -480,6 +481,8 @@ function get_features($filtered = true) { else { $narr = $arr; } - call_hooks('get_features',$narr); - return $narr; + + $x = [ 'features' => $narr, 'filtered' => $filtered, 'techlevel' => $techlevel ]; + call_hooks('get_features',$x); + return $x['features']; } diff --git a/include/feedutils.php b/include/feedutils.php index 50f76b550..5e48cb1ee 100644 --- a/include/feedutils.php +++ b/include/feedutils.php @@ -65,15 +65,34 @@ function get_feed_for($channel, $observer_hash, $params) { if(! $channel) http_status_exit(401); + + // logger('params: ' . print_r($params,true)); + + + $interactive = ((is_array($params) && array_key_exists('interactive',$params)) ? intval($params['interactive']) : 0); + + if($params['pages']) { - if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_pages')) - http_status_exit(403); - } else { - if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_stream')) - http_status_exit(403); + if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_pages')) { + if($interactive) { + return ''; + } + else { + http_status_exit(403); + } + } + } + else { + if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_stream')) { + if($interactive) { + return ''; + } + else { + http_status_exit(403); + } + } } - // logger('params: ' . print_r($params,true)); $feed_template = get_markup_template('atom_feed.tpl'); @@ -1286,7 +1305,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // allow likes of comments - if($item_parent_mid && activity_match($datarray['verb'],ACTVITY_LIKE)) { + if($item_parent_mid && activity_match($datarray['verb'],ACTIVITY_LIKE)) { $datarray['thr_parent'] = $item_parent_mid[0]['parent_mid']; } @@ -1782,12 +1801,17 @@ function compat_photos_list($s) { if($found) { foreach($matches as $match) { - $ret[] = [ + $entry = [ 'href' => $match[2], - 'length' => 0, 'type' => guess_image_type($match[2]) ]; + $sizer = new \Zotlabs\Lib\Img_filesize($match[2]); + $size = $sizer->getSize(); + if(intval($size)) { + $entry['length'] = intval($size); + } + $ret[] = $entry; } } diff --git a/include/follow.php b/include/follow.php index 56d8294c5..0843802c5 100644 --- a/include/follow.php +++ b/include/follow.php @@ -226,6 +226,8 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) } + $profile_assign = get_pconfig($uid,'system','profile_assign',''); + $r = q("select abook_id, abook_xchan, abook_pending, abook_instance from abook where abook_xchan = '%s' and abook_channel = %d limit 1", @@ -265,6 +267,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) 'abook_channel' => intval($uid), 'abook_closeness' => intval($closeness), 'abook_xchan' => $xchan_hash, + 'abook_profile' => $profile_assign, 'abook_feed' => intval(($xchan['xchan_network'] === 'rss') ? 1 : 0), 'abook_created' => datetime_convert(), 'abook_updated' => datetime_convert(), diff --git a/include/html2bbcode.php b/include/html2bbcode.php index d7fbd8660..4166299db 100644 --- a/include/html2bbcode.php +++ b/include/html2bbcode.php @@ -188,20 +188,21 @@ function html2bbcode($message) node2bbcode($doc, 'hr', array(), "[hr]", ""); - node2bbcode($doc, 'table', array(), "", ""); - node2bbcode($doc, 'tr', array(), "\n", ""); - node2bbcode($doc, 'td', array(), "\t", ""); - //node2bbcode($doc, 'table', array(), "[table]", "[/table]"); - //node2bbcode($doc, 'th', array(), "[th]", "[/th]"); - //node2bbcode($doc, 'tr', array(), "[tr]", "[/tr]"); - //node2bbcode($doc, 'td', array(), "[td]", "[/td]"); - - node2bbcode($doc, 'h1', array(), "\n\n[size=xx-large][b]", "[/b][/size]\n"); - node2bbcode($doc, 'h2', array(), "\n\n[size=x-large][b]", "[/b][/size]\n"); - node2bbcode($doc, 'h3', array(), "\n\n[size=large][b]", "[/b][/size]\n"); - node2bbcode($doc, 'h4', array(), "\n\n[size=medium][b]", "[/b][/size]\n"); - node2bbcode($doc, 'h5', array(), "\n\n[size=small][b]", "[/b][/size]\n"); - node2bbcode($doc, 'h6', array(), "\n\n[size=x-small][b]", "[/b][/size]\n"); +// node2bbcode($doc, 'table', array(), "", ""); +// node2bbcode($doc, 'tr', array(), "\n", ""); +// node2bbcode($doc, 'td', array(), "\t", ""); + + node2bbcode($doc, 'table', array(), "[table]", "[/table]"); + node2bbcode($doc, 'th', array(), "[th]", "[/th]"); + node2bbcode($doc, 'tr', array(), "[tr]", "[/tr]"); + node2bbcode($doc, 'td', array(), "[td]", "[/td]"); + + node2bbcode($doc, 'h1', array(), "\n\n[h1]", "[/h1]\n"); + node2bbcode($doc, 'h2', array(), "\n\n[h2]", "[/h2]\n"); + node2bbcode($doc, 'h3', array(), "\n\n[h3]", "[/h3]\n"); + node2bbcode($doc, 'h4', array(), "\n\n[h4]", "[/h4]\n"); + node2bbcode($doc, 'h5', array(), "\n\n[h5]", "[/h5]\n"); + node2bbcode($doc, 'h6', array(), "\n\n[h6]", "[/h6]\n"); node2bbcode($doc, 'a', array('href'=>'/(.+)/'), '[url=$1]', '[/url]'); @@ -211,7 +212,7 @@ function html2bbcode($message) node2bbcode($doc, 'video', array('src'=>'/(.+)/'), '[video]$1', '[/video]'); node2bbcode($doc, 'audio', array('src'=>'/(.+)/'), '[audio]$1', '[/audio]'); - node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), '[iframe]$1', '[/iframe]'); +// node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), '[iframe]$1', '[/iframe]'); node2bbcode($doc, 'code', array(), '[code]', '[/code]'); diff --git a/include/import.php b/include/import.php index 702fa7e54..51791347a 100644 --- a/include/import.php +++ b/include/import.php @@ -162,13 +162,17 @@ function import_profiles($channel, $profiles) { convert_oldfields($profile,'work','employment'); /** - * @TODO we are going to reset all profile photos to the original - * somebody will have to fix this later and put all the applicable - * photos into the export. + * @TODO put all the applicable photos into the export. */ - $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id']; - $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id']; + if((strpos($profile['thumb'],'/photo/profile/l/') !== false) || intval($profile['is_default'])) { + $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id']; + $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id']; + } + else { + $profile['photo'] = z_root() . '/photo/' . basename($profile['photo']); + $profile['thumb'] = z_root() . '/photo/' . basename($profile['thumb']); + } create_table_from_array('profile', $profile); } @@ -180,7 +184,7 @@ function import_profiles($channel, $profiles) { * * @param array $channel * @param array $hublocs - * @param unknown $seize + * @param boolean $seize * @param boolean $moving (optional) default false */ function import_hublocs($channel, $hublocs, $seize, $moving = false) { @@ -1081,6 +1085,7 @@ function sync_files($channel, $files) { logger('sync_files duplicate check: attach_by_hash() returned ' . print_r($x,true), LOGGER_DEBUG); if($x['success']) { + $orig_attach = $x[0]; $attach_exists = true; $attach_id = $x[0]['id']; } @@ -1145,12 +1150,18 @@ function sync_files($channel, $files) { // If the hash ever contains any escapable chars this could cause // problems. Currently it does not. - /// @TODO implement os_path if(!isset($att['os_path'])) $att['os_path'] = ''; if($attach_exists) { logger('sync_files attach exists: ' . print_r($att,true), LOGGER_DEBUG); + + // process/sync a remote rename/move operation + + if($orig_attach['content'] !== $newfname) { + rename($orig_attach['content'],$newfname); + } + if(! dbesc_array($att)) continue; @@ -1199,7 +1210,14 @@ function sync_files($channel, $files) { continue; } $redirects = 0; - $x = z_post_url($fetch_url,$parr,$redirects,array('filep' => $fp)); + + + $headers = []; + $headers['Accept'] = 'application/x-zot+json' ; + $headers['Sigtoken'] = random_string(); + $headers = \Zotlabs\Web\HTTPSig::create_sig('',$headers,$channel['channel_prvkey'], 'acct:' . $channel['channel_address'] . '@' . \App::get_hostname(),false,true,'sha512'); + + $x = z_post_url($fetch_url,$parr,$redirects,[ 'filep' => $fp, 'headers' => $headers]); fclose($fp); if($x['success']) { @@ -1249,10 +1267,43 @@ function sync_files($channel, $files) { ); } - if($p['imgscale'] === 0 && $p['os_storage']) + if(intval($p['imgscale']) === 0 && $p['os_storage']) $p['content'] = $store_path; else - $p['content'] = base64_decode($p['content']); + $p['content'] = (($p['content'])? base64_decode($p['content']) : ''); + + if(intval($p['imgscale']) && (! $p['content'])) { + + $time = datetime_convert(); + + $parr = array('hash' => $channel['channel_hash'], + 'time' => $time, + 'resource' => $att['hash'], + 'revision' => 0, + 'signature' => base64url_encode(rsa_sign($channel['channel_hash'] . '.' . $time, $channel['channel_prvkey'])), + 'resolution' => $p['imgscale'] + ); + + $stored_image = $newfname . '-' . intval($p['imgscale']); + + $fp = fopen($stored_image,'w'); + if(! $fp) { + logger('failed to open storage file.',LOGGER_NORMAL,LOG_ERR); + continue; + } + $redirects = 0; + + + $headers = []; + $headers['Accept'] = 'application/x-zot+json' ; + $headers['Sigtoken'] = random_string(); + $headers = \Zotlabs\Web\HTTPSig::create_sig('',$headers,$channel['channel_prvkey'], 'acct:' . $channel['channel_address'] . '@' . \App::get_hostname(),false,true,'sha512'); + + $x = z_post_url($fetch_url,$parr,$redirects,[ 'filep' => $fp, 'headers' => $headers]); + fclose($fp); + $p['content'] = file_get_contents($stored_image); + unlink($stored_image); + } if(!isset($p['display_path'])) $p['display_path'] = ''; @@ -1281,6 +1332,9 @@ function sync_files($channel, $files) { } } } + + \Zotlabs\Daemon\Master::Summon([ 'Thumbnail' , $att['hash'] ]); + if($f['item']) { sync_items($channel,$f['item'], ['channel_address' => $original_channel,'url' => $oldbase] diff --git a/include/items.php b/include/items.php index b1b40e977..d0b9cffc9 100755 --- a/include/items.php +++ b/include/items.php @@ -2694,8 +2694,8 @@ function tag_deliver($uid, $item_id) { } - if((! $mention) && (! $union)) { - logger('No mention for ' . $u[0]['channel_name'] . ' and no union.'); + if(! $mention) { + logger('No mention for ' . $u[0]['channel_name']); continue; } @@ -2714,6 +2714,18 @@ function tag_deliver($uid, $item_id) { } } + + if($union) { + if(intval($item['item_wall']) || intval($item['item_origin']) || (! intval($item['item_thread_top'])) || ($item['id'] != $item['parent'])) { + logger('Item was local or a comment. rejected.'); + return; + } + + logger('Creating second delivery chain.'); + start_delivery_chain($u[0],$item,$item_id,null); + + } + } /** @@ -4051,8 +4063,9 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C $item_uids = ' true '; $item_normal = item_normal(); - - if ($arr['uid']) $uid= $arr['uid']; + if($arr['uid']) { + $uid = $arr['uid']; + } if($channel) { $uid = $channel['channel_id']; @@ -4127,7 +4140,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C } if($channel && intval($arr['compat']) === 1) { - $sql_extra = " AND author_xchan = '" . $channel['channel_hash'] . "' and item_private = 0 "; + $sql_extra = " AND author_xchan = '" . $channel['channel_hash'] . "' and item_private = 0 $item_normal "; } if ($arr['datequery']) { @@ -4214,7 +4227,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C $items = q("SELECT item.*, item.id AS item_id FROM item WHERE $item_uids $item_restrict $simple_update - $sql_extra $sql_nets + $sql_extra $sql_nets $sql_extra3 ORDER BY item.received DESC $pager_sql" ); @@ -4309,6 +4322,8 @@ function webpage_to_namespace($webpage) { $page_type = 'PDL'; elseif($webpage == ITEM_TYPE_CARD) $page_type = 'CARD'; + elseif($webpage == ITEM_TYPE_ARTICLE) + $page_type = 'ARTICLE'; elseif($webpage == ITEM_TYPE_DOC) $page_type = 'docfile'; else @@ -4707,3 +4722,62 @@ function item_create_edit_activity($post) { \Zotlabs\Daemon\Master::Summon(array('Notifier', 'edit_activity', $post_id)); } + +/** + * @brief copies an entire conversation from the pubstream to this channel's stream + * which will allow you to interact with it. + */ + + + +function copy_of_pubitem($channel,$mid) { + + $result = null; + $syschan = get_sys_channel(); + + // logger('copy_of_pubitem: ' . $channel['channel_id'] . ' mid: ' . $mid); + + $r = q("select * from item where mid = '%s' and uid = %d limit 1", + dbesc($mid), + intval($channel['channel_id']) + ); + + if($r) { + logger('exists'); + $item = fetch_post_tags($r,true); + return $item[0]; + } + + + $r = q("select * from item where parent_mid = (select parent_mid from item where mid = '%s' and uid = %d ) order by id ", + dbesc($mid), + intval($syschan['channel_id']) + ); + + if($r) { + $items = fetch_post_tags($r,true); + foreach($items as $rv) { + $d = q("select id from item where mid = '%s' and uid = %d limit 1", + dbesc($rv['mid']), + intval($channel['channel_id']) + ); + if($d) { + continue; + } + + unset($rv['id']); + unset($rv['parent']); + $rv['aid'] = $channel['channel_account_id']; + $rv['uid'] = $channel['channel_id']; + $rv['item_wall'] = 0; + $rv['item_origin'] = 0; + + $x = item_store($rv); + if($x['item_id'] && $x['item']['mid'] === $mid) { + $result = $x['item']; + } + + } + } + return $result; +} diff --git a/include/markdown.php b/include/markdown.php index f398d279e..e4a35e3c3 100644 --- a/include/markdown.php +++ b/include/markdown.php @@ -204,7 +204,7 @@ function bb_to_markdown($Text, $options = []) { $Text = $x['bbcode']; // Convert it to HTML - don't try oembed - $Text = bbcode($Text, $preserve_nl, false); + $Text = bbcode($Text, [ 'tryoembed' => false ]); // Markdownify does not preserve previously escaped html entities such as <> and &. $Text = str_replace(array('<','>','&'),array('&_lt_;','&_gt_;','&_amp_;'),$Text); diff --git a/include/message.php b/include/message.php index b57d2e068..4a673b961 100644 --- a/include/message.php +++ b/include/message.php @@ -215,7 +215,7 @@ function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $rep return $ret; } - if(count($images)) { + if($images) { foreach($images as $image) { if(! stristr($image,z_root() . '/photo/')) continue; diff --git a/include/nav.php b/include/nav.php index 2dcf68bc8..8566cc58c 100644 --- a/include/nav.php +++ b/include/nav.php @@ -73,6 +73,11 @@ EOT; // nav links: array of array('href', 'text', 'extra css classes', 'title') $nav = []; + $disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false; + + if(! $disable_discover_tab) + $nav['pubs'] = true; + /** * Display login or logout */ @@ -171,13 +176,15 @@ EOT; } - - $homelink = get_my_url(); - if(! $homelink) { + $my_url = get_my_url(); + if(! $my_url) { $observer = App::get_observer(); - $homelink = (($observer) ? $observer['xchan_url'] : ''); + $my_url = (($observer) ? $observer['xchan_url'] : ''); } + $homelink_arr = parse_url($my_url); + $homelink = $homelink_arr['scheme'] . '://' . $homelink_arr['host']; + if(! $is_owner) { $nav['rusermenu'] = array( $homelink, @@ -308,7 +315,7 @@ EOT; '$sitelocation' => $sitelocation, '$nav' => $x['nav'], '$banner' => $banner, - '$emptynotifications' => t('Loading...'), + '$emptynotifications' => t('Loading'), '$userinfo' => $x['usermenu'], '$localuser' => local_channel(), '$is_owner' => $is_owner, @@ -491,6 +498,17 @@ function channel_apps($is_owner = false, $nickname = null) { ]; } + if($p['view_pages'] && feature_enabled($uid,'articles')) { + $tabs[] = [ + 'label' => t('Articles'), + 'url' => z_root() . '/articles/' . $nickname , + 'sel' => ((argv(0) == 'articles') ? 'active' : ''), + 'title' => t('View Articles'), + 'id' => 'articles-tab', + 'icon' => 'file-text-o' + ]; + } + if($has_webpages && feature_enabled($uid,'webpages')) { $tabs[] = [ diff --git a/include/network.php b/include/network.php index 3569874be..79a8c6578 100644 --- a/include/network.php +++ b/include/network.php @@ -752,13 +752,14 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) if($orig_width > 1024 || $orig_height > 1024) { $tag = (($match[1] == 'z') ? 'zmg' : 'img'); + $linktag = (($match[1] == 'z') ? 'zrl' : 'url'); $ph->scaleImage(1024); $new_width = $ph->getWidth(); $new_height = $ph->getHeight(); logger('data: ' . $orig_width . '->' . $new_width . 'w ' . $orig_height . '->' . $new_height . 'h' . ' match: ' . $mtch[0], LOGGER_DEBUG); $s = str_replace($mtch[0],'[' . $tag . '=' . $new_width . 'x' . $new_height. ']' . $scaled . '[/' . $tag . ']' . "\n" . (($include_link) - ? '[zrl=' . $mtch[2] . ']' . t('view full size') . '[/zrl]' . "\n" + ? '[' . $linktag . '=' . $mtch[3] . ']' . t('view full size') . '[/' . $linktag . ']' . "\n" : ''),$s); logger('new string: ' . $s, LOGGER_DEBUG); } diff --git a/include/plugin.php b/include/plugin.php index 379d8e799..67157dee7 100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -47,7 +47,10 @@ function uninstall_plugin($plugin) { } /** - * @brief installs an addon. + * @brief Installs an addon. + * + * This function is called once to install the addon (either from the cli or via + * the web admin). This will also call load_plugin() once. * * @param string $plugin name of the addon * @return bool @@ -188,7 +191,9 @@ function visible_plugin_list() { /** - * @brief registers a hook. + * @brief Registers a hook. + * + * @see ::Zotlabs::Extend::Hook::register() * * @param string $hook the name of the hook * @param string $file the name of the file that hooks into @@ -219,6 +224,8 @@ function register_hook($hook, $file, $function, $priority = 0) { /** * @brief unregisters a hook. * + * @see ::Zotlabs::Extend::Hook::unregister + * * @param string $hook the name of the hook * @param string $file the name of the file that hooks into * @param string $function the name of the function that the hook called @@ -397,6 +404,83 @@ function get_plugin_info($plugin){ return $info; } +/** + * @brief Parse widget comment in search of widget info. + * + * like + * \code + * * Name: MyWidget + * * Description: A widget + * * Version: 1.2.3 + * * Author: John <profile url> + * * Author: Jane <email> + * * + *\endcode + * @param string $widget the name of the widget + * @return array with the information + */ +function get_widget_info($widget){ + $m = array(); + $info = array( + 'name' => $widget, + 'description' => '', + 'author' => array(), + 'maintainer' => array(), + 'version' => '', + 'requires' => '' + ); + + $ucwidget = ucfirst($widget); + + $checkpaths = [ + "Zotlabs/SiteWidget/$ucwidget.php", + "Zotlibs/Widget/$ucwidget.php", + "addon/$ucwidget/$ucwidget.php", + "addon/$widget.php" + ]; + + $widget_found = false; + + foreach ($checkpaths as $path) { + if (is_file($path)) { + $widget_found = true; + $f = file_get_contents($path); + break; + } + } + + if(! ($widget_found && $f)) + return $info; + + $f = escape_tags($f); + $r = preg_match("|/\*.*\*/|msU", $f, $m); + + if ($r) { + $ll = explode("\n", $m[0]); + foreach( $ll as $l ) { + $l = trim($l, "\t\n\r */"); + if ($l != ""){ + list($k, $v) = array_map("trim", explode(":", $l, 2)); + $k = strtolower($k); + if ($k == 'author' || $k == 'maintainer'){ + $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); + if ($r) { + $info[$k][] = array('name' => $m[1], 'link' => $m[2]); + } else { + $info[$k][] = array('name' => $v); + } + } + else { + $info[$k] = $v; + } + } + } + } + + return $info; +} + + function check_plugin_versions($info) { if(! is_array($info)) diff --git a/include/socgraph.php b/include/socgraph.php index 3c7a893c6..87a880202 100644 --- a/include/socgraph.php +++ b/include/socgraph.php @@ -178,9 +178,12 @@ function poco_load($xchan = '', $url = null) { ); if(! $r) { - q("insert into xlink ( xlink_xchan, xlink_link, xlink_updated, xlink_static ) values ( '%s', '%s', '%s', 0 ) ", + q("insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_rating_text, xlink_sig, xlink_updated, xlink_static ) values ( '%s', '%s', %d, '%s', '%s', '%s', 0 ) ", dbesc($xchan), dbesc($hash), + intval(0), + dbesc(''), + dbesc(''), dbesc(datetime_convert()) ); } diff --git a/include/taxonomy.php b/include/taxonomy.php index 23acaa24d..278925391 100644 --- a/include/taxonomy.php +++ b/include/taxonomy.php @@ -55,18 +55,20 @@ function term_item_parent_query($uid,$table,$s,$type = TERM_UNKNOWN, $type2 = '' $s = str_replace('*','%',$s); if($type2) { - $r = q("select parent from item left join term on term.oid = item.id where term.ttype in (%d, %d) and term.term like '%s' and term.uid = %d and term.otype = 1", + $r = q("select parent from item left join term on term.oid = item.id where term.ttype in (%d, %d) and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb != '%s'", intval($type), intval($type2), dbesc($s), - intval($uid) + intval($uid), + dbesc(ACTIVITY_UPDATE) ); } else { - $r = q("select parent from item left join term on term.oid = item.id where term.ttype = %d and term.term like '%s' and term.uid = %d and term.otype = 1", + $r = q("select parent from item left join term on term.oid = item.id where term.ttype = %d and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb != '%s'", intval($type), dbesc($s), - intval($uid) + intval($uid), + dbesc(ACTIVITY_UPDATE) ); } @@ -253,23 +255,94 @@ function card_tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0 } +function article_tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) { + require_once('include/security.php'); + if(! perm_is_allowed($uid,get_observer_hash(),'view_pages')) + return array(); -function dir_tagadelic($count = 0) { - + $item_normal = item_normal(); + $sql_options = item_permissions_sql($uid); $count = intval($count); + if($flags) { + if($flags === 'wall') + $sql_options .= " and item_wall = 1 "; + } + + if($authors) { + if(! is_array($authors)) + $authors = array($authors); + + stringify_array_elms($authors,true); + $sql_options .= " and author_xchan in (" . implode(',',$authors) . ") "; + } + + if($owner) { + $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' "; + } + + // Fetch tags - $r = q("select xtag_term as term, count(xtag_term) as total from xtag where xtag_flags = 0 - group by xtag_term order by total desc %s", + $r = q("select term, count(term) as total from term left join item on term.oid = item.id + where term.uid = %d and term.ttype = %d + and otype = %d and item_type = %d and item_private = 0 + $sql_options $item_normal + group by term order by total desc %s", + intval($uid), + intval($type), + intval(TERM_OBJ_POST), + intval($restrict), ((intval($count)) ? "limit $count" : '') ); if(! $r) return array(); + return Zotlabs\Text\Tagadelic::calc($r); + +} + + + + + +function dir_tagadelic($count = 0, $hub = '') { + + $count = intval($count); + + $dirmode = get_config('system','directory_mode'); + + if(($dirmode == DIRECTORY_MODE_STANDALONE) && (! $hub)) { + $hub = \App::get_hostname(); + } + + if($hub) + $hub_query = " and xtag_hash in (select hubloc_hash from hubloc where hubloc_host = '" . protect_sprintf(dbesc($hub)) . "') "; + else + $hub_query = ''; + + if($hub_query) { + // Fetch tags + $r = q("select xtag_term as term, count(xtag_term) as total from xtag + left join hubloc on xtag_hash = hubloc_hash + where xtag_flags = 0 $hub_query + group by xtag_term order by total desc %s", + ((intval($count)) ? "limit $count" : '') + ); + } + else { + // Fetch tags + $r = q("select xtag_term as term, count(xtag_term) as total from xtag where xtag_flags = 0 + group by xtag_term order by total desc %s", + ((intval($count)) ? "limit $count" : '') + ); + } + if(! $r) + return array(); + return Zotlabs\Text\Tagadelic::calc($r); @@ -395,13 +468,31 @@ function card_catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$re } +function article_catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_CATEGORY) { + $o = ''; + + $r = article_tagadelic($uid,$count,$authors,$owner,$flags,$restrict,$type); + + if($r) { + $c = q("select channel_address from channel where channel_id = %d limit 1", + intval($uid) + ); + + $o = '<div class="tagblock widget"><h3>' . t('Categories') . '</h3><div class="tags" align="center">'; + foreach($r as $rr) { + $o .= '<a href="articles/' . $c[0]['channel_address']. '?f=&cat=' . urlencode($rr[0]).'" class="tag'.$rr[2].'">'.$rr[0].'</a> ' . "\r\n"; + } + $o .= '</div></div>'; + } + + return $o; +} + + function dir_tagblock($link,$r) { $o = ''; $observer = get_observer_hash(); - if(! get_directory_setting($observer, 'globaldir')) - return $o; - if(! $r) $r = App::$data['directory_keywords']; diff --git a/include/text.php b/include/text.php index 343e3f00b..956f42f7d 100644 --- a/include/text.php +++ b/include/text.php @@ -24,12 +24,20 @@ define('RANDOM_STRING_TEXT', 0x01 ); * @return string substituted string */ function replace_macros($s, $r) { + $arr = [ + 'template' => $s, + 'params' => $r + ]; - $arr = array('template' => $s, 'params' => $r); + /** + * @hooks replace_macros + * * \e string \b template + * * \e array \b params + */ call_hooks('replace_macros', $arr); $t = App::template_engine(); - $output = $t->replace_macros($arr['template'],$arr['params']); + $output = $t->replace_macros($arr['template'], $arr['params']); return $output; } @@ -301,12 +309,16 @@ function purify_html($s, $allow_position = false) { /** - * @brief generate a string that's random, but usually pronounceable. + * @brief Generate a string that's random, but usually pronounceable. * * Used to generate initial passwords. * - * @param int $len - * @return string + * @note In order to create "pronounceable" strings some consonant pairs or + * letters that does not make a very good word ending are chopped off, so that + * the returned string length can be lower than $len. + * + * @param int $len max length of generated string + * @return string Genereated random, but usually pronounceable string */ function autoname($len) { @@ -343,6 +355,7 @@ function autoname($len) { $midcons = array('ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp', 'nd','ng','nk','nt','rn','rp','rt'); + // avoid these consonant pairs at the end of the string $noend = array('bl', 'br', 'cl','cr','dr','fl','fr','gl','gr', 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh'); @@ -355,7 +368,7 @@ function autoname($len) { $word = ''; for ($x = 0; $x < $len; $x ++) { - $r = mt_rand(0,count($table) - 1); + $r = mt_rand(0, count($table) - 1); $word .= $table[$r]; if ($table == $vowels) @@ -364,14 +377,15 @@ function autoname($len) { $table = $vowels; } - $word = substr($word,0,$len); + $word = substr($word, 0, $len); foreach ($noend as $noe) { - if ((strlen($word) > 2) && (substr($word,-2) == $noe)) { - $word = substr($word,0,-1); + if ((strlen($word) > 2) && (substr($word, -2) == $noe)) { + $word = substr($word, 0, -1); break; } } + // avoid the letter 'q' as it does not make a very good word ending if (substr($word, -1) == 'q') $word = substr($word, 0, -1); @@ -1094,17 +1108,19 @@ function sslify($s) { return $s; } - +/** + * @brief Get an array of poke verbs. + * + * @return array + * * \e index is present tense verb + * * \e value is array containing past tense verb, translation of present, translation of past + */ function get_poke_verbs() { - // index is present tense verb - // value is array containing past tense verb, translation of present, translation of past - - if(get_config('system','poke_basic')) { + if (get_config('system', 'poke_basic')) { $arr = array( - 'poke' => array( 'poked', t('poke'), t('poked')), + 'poke' => array('poked', t('poke'), t('poked')), ); - } - else { + } else { $arr = array( 'poke' => array( 'poked', t('poke'), t('poked')), 'ping' => array( 'pinged', t('ping'), t('pinged')), @@ -1114,15 +1130,26 @@ function get_poke_verbs() { 'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')), ); + /** + * @hooks poke_verbs + * * \e array associative array with another array as value + */ call_hooks('poke_verbs', $arr); } return $arr; } +/** + * @brief Get an array of mood verbs. + * + * @return array + * * \e index is the verb + * * \e value is the translated verb + */ function get_mood_verbs() { - $arr = array( + $arr = [ 'happy' => t('happy'), 'sad' => t('sad'), 'mellow' => t('mellow'), @@ -1144,9 +1171,14 @@ function get_mood_verbs() { 'motivated' => t('motivated'), 'relaxed' => t('relaxed'), 'surprised' => t('surprised'), - ); + ]; + /** + * @hooks mood_verbs + * * \e array associative array with mood verbs + */ call_hooks('mood_verbs', $arr); + return $arr; } @@ -1513,14 +1545,37 @@ function format_filer(&$item) { function generate_map($coord) { $coord = trim($coord); $coord = str_replace(array(',','/',' '),array(' ',' ',' '),$coord); - $arr = array('lat' => trim(substr($coord,0,strpos($coord,' '))), 'lon' => trim(substr($coord,strpos($coord,' ')+1)), 'html' => ''); - call_hooks('generate_map',$arr); + + $arr = [ + 'lat' => trim(substr($coord, 0, strpos($coord, ' '))), + 'lon' => trim(substr($coord, strpos($coord, ' ')+1)), + 'html' => '' + ]; + + /** + * @hooks generate_map + * * \e string \b lat + * * \e string \b lon + * * \e string \b html the parsed HTML to return + */ + call_hooks('generate_map', $arr); + return (($arr['html']) ? $arr['html'] : $coord); } function generate_named_map($location) { - $arr = array('location' => $location, 'html' => ''); - call_hooks('generate_named_map',$arr); + $arr = [ + 'location' => $location, + 'html' => '' + ]; + + /** + * @hooks generate_named_map + * * \e string \b location + * * \e string \b html the parsed HTML to return + */ + call_hooks('generate_named_map', $arr); + return (($arr['html']) ? $arr['html'] : $location); } @@ -1626,13 +1681,11 @@ function prepare_binary($item) { } - - /** * @brief Given a text string, convert from bbcode to html and add smilie icons. * * @param string $text - * @param sting $content_type (optional) default text/bbcode + * @param string $content_type (optional) default text/bbcode * @param boolean $cache (optional) default false * * @return string @@ -1679,9 +1732,9 @@ function prepare_text($text, $content_type = 'text/bbcode', $cache = false) { require_once('include/bbcode.php'); if(stristr($text,'[nosmile]')) - $s = bbcode($text,false,true,$cache); + $s = bbcode($text, [ 'cache' => $cache ]); else - $s = smilies(bbcode($text,false,true,$cache)); + $s = smilies(bbcode($text, [ 'cache' => $cache ])); $s = zidify_links($s); @@ -1754,9 +1807,14 @@ function get_plink($item,$conversation_mode = true) { else $key = 'llink'; + $zidify = true; + + if(array_key_exists('author',$item) && $item['author']['xchan_network'] !== 'zot') + $zidify = false; + if(x($item,$key)) { return array( - 'href' => zid($item[$key]), + 'href' => (($zidify) ? zid($item[$key]) : $item[$key]), 'title' => t('Link to Source'), ); } @@ -3028,8 +3086,19 @@ function text_highlight($s, $lang) { $s = jindent($s); } - $arr = [ 'text' => $s, 'language' => $lang, 'success' => false ]; - call_hooks('text_highlight',$arr); + $arr = [ + 'text' => $s, + 'language' => $lang, + 'success' => false + ]; + + /** + * @hooks text_highlight + * * \e string \b text + * * \e string \b language + * * \e boolean \b success default false + */ + call_hooks('text_highlight', $arr); if($arr['success']) $o = $arr['text']; @@ -3112,7 +3181,6 @@ function share_unshield($m) { function cleanup_bbcode($body) { - /** * fix naked links by passing through a callback to see if this is a hubzilla site * (already known to us) which will get a zrl, otherwise link with url, add bookmark tag to both. @@ -3150,7 +3218,6 @@ function cleanup_bbcode($body) { return $body; - } function gen_link_id($mid) { diff --git a/include/zid.php b/include/zid.php index d1a0fa88a..6ebc9a6ab 100644 --- a/include/zid.php +++ b/include/zid.php @@ -105,8 +105,8 @@ function strip_zats($s) { -function clean_query_string() { - $x = strip_zids(\App::$query_string); +function clean_query_string($s = '') { + $x = strip_zids(($s) ? $s : \App::$query_string); $x = strip_owt($x); $x = strip_zats($x); diff --git a/include/zot.php b/include/zot.php index 40e303bf1..8e3d03ad8 100644 --- a/include/zot.php +++ b/include/zot.php @@ -394,6 +394,7 @@ function zot_refresh($them, $channel = null, $force = false) { $next_birthday = NULL_DATE; } + $profile_assign = get_pconfig($channel['channel_id'],'system','profile_assign',''); // Keep original perms to check if we need to notify them $previous_perms = get_all_perms($channel['channel_id'],$x['hash']); @@ -455,6 +456,7 @@ function zot_refresh($them, $channel = null, $force = false) { 'abook_channel' => intval($channel['channel_id']), 'abook_closeness' => intval($closeness), 'abook_xchan' => $x['hash'], + 'abook_profile' => $profile_assign, 'abook_created' => datetime_convert(), 'abook_updated' => datetime_convert(), 'abook_dob' => $next_birthday, @@ -2550,7 +2552,7 @@ function sync_locations($sender, $arr, $absolute = false) { 'hubloc_hash' => $sender['hash'], 'hubloc_addr' => $location['address'], 'hubloc_network' => 'zot', - 'hubloc_primary' => $location['primary'], + 'hubloc_primary' => intval($location['primary']), 'hubloc_url' => $location['url'], 'hubloc_url_sig' => $location['url_sig'], 'hubloc_host' => $location['host'], @@ -3743,6 +3745,15 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { * @TODO * We also need to import local photos if a custom photo is selected */ + + if((strpos($profile['thumb'],'/photo/profile/l/') !== false) || intval($profile['is_default'])) { + $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id']; + $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id']; + } + else { + $profile['photo'] = z_root() . '/photo/' . basename($profile['photo']); + $profile['thumb'] = z_root() . '/photo/' . basename($profile['thumb']); + } } if(count($clean)) { |