diff options
Diffstat (limited to 'include')
40 files changed, 2585 insertions, 2008 deletions
diff --git a/include/Import/import_diaspora.php b/include/Import/import_diaspora.php deleted file mode 100644 index c6dae0117..000000000 --- a/include/Import/import_diaspora.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php - -require_once('include/bb2diaspora.php'); -require_once('include/group.php'); -require_once('include/follow.php'); -require_once('include/photo/photo_driver.php'); - -function import_diaspora($data) { - - $account = App::get_account(); - if(! $account) - return false; - - $address = escape_tags($data['user']['username']); - if(! $address) { - notice( t('No username found in import file.') . EOL); - return false; - } - - $r = q("select * from channel where channel_address = '%s' limit 1", - dbesc($address) - ); - if($r) { - // try at most ten times to generate a unique address. - $x = 0; - $found_unique = false; - do { - $tmp = $address . mt_rand(1000,9999); - $r = q("select * from channel where channel_address = '%s' limit 1", - dbesc($tmp) - ); - if(! $r) { - $address = $tmp; - $found_unique = true; - break; - } - $x ++; - } while ($x < 10); - if(! $found_unique) { - logger('import_diaspora: duplicate channel address. randomisation failed.'); - notice( t('Unable to create a unique channel address. Import failed.') . EOL); - return; - } - } - - - $c = create_identity(array( - 'name' => escape_tags($data['user']['name']), - 'nickname' => $address, - 'account_id' => $account['account_id'], - 'permissions_role' => 'social' - )); - - if(! $c['success']) - return; - - $channel_id = $c['channel']['channel_id']; - - // Hubzilla only: Turn on the Diaspora protocol so that follow requests will be sent. - - set_pconfig($channel_id,'system','diaspora_allowed','1'); - - // todo - add auto follow settings, (and strip exif in hubzilla) - - $location = escape_tags($data['user']['profile']['location']); - if(! $location) - $location = ''; - - - q("update channel set channel_location = '%s' where channel_id = %d", - dbesc($location), - intval($channel_id) - ); - - if($data['user']['profile']['nsfw']) { - q("update channel set channel_pageflags = (channel_pageflags | %d) where channel_id = %d", - intval(PAGE_ADULT), - intval($channel_id) - ); - } - - if($data['user']['profile']['image_url']) { - $p = z_fetch_url($data['user']['profile']['image_url'],true); - if($p['success']) { - $rawbytes = $p['body']; - $type = guess_image_type('dummyfile',$p['header']); - import_channel_photo($rawbytes,$type,$c['channel']['channel_account_id'],$channel_id); - } - } - - $gender = escape_tags($data['user']['profile']['gender']); - $about = markdown_to_bb($data['user']['profile']['bio']); - $publish = intval($data['user']['profile']['searchable']); - if($data['user']['profile']['birthday']) - $dob = datetime_convert('UTC','UTC',$data['user']['profile']['birthday'],'Y-m-d'); - else - $dob = '0000-00-00'; - - // we're relying on the fact that this channel was just created and will only - // have the default profile currently - - $r = q("update profile set gender = '%s', about = '%s', dob = '%s', publish = %d where uid = %d", - dbesc($gender), - dbesc($about), - dbesc($dob), - dbesc($publish), - intval($channel_id) - ); - - if($data['user']['aspects']) { - foreach($data['user']['aspects'] as $aspect) { - group_add($channel_id,escape_tags($aspect['name']),intval($aspect['contacts_visible'])); - } - } - - // now add connections and send friend requests - - - if($data['user']['contacts']) { - foreach($data['user']['contacts'] as $contact) { - $result = new_contact($channel_id, $contact['person_diaspora_handle'], $c['channel']); - if($result['success']) { - if($contact['aspects']) { - foreach($contact['aspects'] as $aspect) { - group_add_member($channel_id,$aspect['name'],$result['abook']['xchan_hash']); - } - } - } - } - } - - - // Then add items - note this can't be done until Diaspora adds guids to exported - // items and comments - - - - // This will indirectly perform a refresh_all *and* update the directory - - proc_run('php', 'include/directory.php', $channel_id); - - notice( t('Import completed.') . EOL); - - change_channel($channel_id); - - goaway(z_root() . '/network' ); - -}
\ No newline at end of file diff --git a/include/Import/refimport.php b/include/Import/refimport.php index 3ef8870ac..04540a9bd 100644 --- a/include/Import/refimport.php +++ b/include/Import/refimport.php @@ -1,7 +1,6 @@ <?php require_once('include/html2bbcode.php'); -require_once('include/hubloc.php'); // Sample module for importing conversation data from Reflection CMS. Some preparation was used to // dump relevant posts, categories and comments into individual JSON files, and also JSON dump of diff --git a/include/acl_selectors.php b/include/acl_selectors.php index dcf0fe9a2..09c24f82c 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -174,6 +174,16 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti $custom = false; } + $r = q("SELECT id, profile_guid, profile_name from profile where is_default = 0 and uid = %d order by profile_name", + intval(local_channel()) + ); + if($r) { + foreach($r as $rv) { + $selected = (($single_group && 'vp.' . $rr['hash'] === $allow_gid[0]) ? ' selected = "selected" ' : ''); + $groups .= '<option id="' . 'vp' . $rv['id'] . '" value="' . 'vp.' . $rv['profile_guid'] . '"' . $selected . '>' . t('Profile','acl') . ' ' . $rv['profile_name'] . '</option>' . "\r\n"; + } + } + $r = q("SELECT id, hash, gname FROM groups WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", intval(local_channel()) ); diff --git a/include/activities.php b/include/activities.php index 3271db993..2671e668c 100644 --- a/include/activities.php +++ b/include/activities.php @@ -27,7 +27,7 @@ function profile_activity($changed, $value) { $arr['verb'] = ACTIVITY_UPDATE; $arr['obj_type'] = ACTIVITY_OBJ_PROFILE; - $arr['plink'] = z_root() . '/channel/' . $self['channel_address'] . '/?f=&mid=' . $arr['mid']; + $arr['plink'] = z_root() . '/channel/' . $self['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); $A = '[url=' . z_root() . '/channel/' . $self['channel_address'] . ']' . $self['channel_name'] . '[/url]'; diff --git a/include/api_zot.php b/include/api_zot.php index fe3a95d25..aaa9ee497 100644 --- a/include/api_zot.php +++ b/include/api_zot.php @@ -69,8 +69,13 @@ logger('api_export_basic: no user'); return false; } - - json_return_and_die(identity_basic_export(api_user(),(($_REQUEST['posts']) ? intval($_REQUEST['posts']) : 0 ))); + $sections = (($_REQUEST['sections']) ? explode(',',$_REQUEST['sections']) : ''); + if($_REQUEST['posts']) { + $sections = get_default_export_sections(); + $sections[] = 'items'; + } + + json_return_and_die(identity_basic_export(api_user(),$sections)); } @@ -350,10 +355,9 @@ if(api_user() === false) return false; logger('api_xchan'); - require_once('include/hubloc.php'); - if($_SERVER['REQUEST_METHOD'] === 'POST') { - // $r = xchan_store($_REQUEST); + if($_SERVER['REQUEST_METHOD'] === 'POST') { + $r = xchan_store($_REQUEST); } $r = xchan_fetch($_REQUEST); json_return_and_die($r); diff --git a/include/attach.php b/include/attach.php index ba2f60a90..79313ab1a 100644 --- a/include/attach.php +++ b/include/attach.php @@ -28,29 +28,31 @@ function z_mime_content_type($filename) { $mime_types = array( - 'txt' => 'text/plain', - 'htm' => 'text/html', + 'txt' => 'text/plain', + 'htm' => 'text/html', 'html' => 'text/html', - 'php' => 'text/html', - 'css' => 'text/css', - 'js' => 'application/javascript', + 'php' => 'text/html', + 'css' => 'text/css', + 'md' => 'text/markdown', + 'bb' => 'text/bbcode', + 'js' => 'application/javascript', 'json' => 'application/json', - 'xml' => 'application/xml', - 'swf' => 'application/x-shockwave-flash', - 'flv' => 'video/x-flv', + 'xml' => 'application/xml', + 'swf' => 'application/x-shockwave-flash', + 'flv' => 'video/x-flv', 'epub' => 'application/epub+zip', // images - 'png' => 'image/png', - 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'gif' => 'image/gif', - 'bmp' => 'image/bmp', - 'ico' => 'image/vnd.microsoft.icon', + 'jpg' => 'image/jpeg', + 'gif' => 'image/gif', + 'bmp' => 'image/bmp', + 'ico' => 'image/vnd.microsoft.icon', 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'svg' => 'image/svg+xml', + 'tif' => 'image/tiff', + 'svg' => 'image/svg+xml', 'svgz' => 'image/svg+xml', // archives @@ -61,27 +63,27 @@ function z_mime_content_type($filename) { 'cab' => 'application/vnd.ms-cab-compressed', // audio/video - 'mp3' => 'audio/mpeg', - 'wav' => 'audio/wav', - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - 'ogg' => 'audio/ogg', - 'ogv' => 'video/ogg', - 'ogx' => 'application/ogg', + 'mp3' => 'audio/mpeg', + 'wav' => 'audio/wav', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', 'flac' => 'audio/flac', 'opus' => 'audio/ogg', 'webm' => 'video/webm', // 'webm' => 'audio/webm', - 'mp4' => 'video/mp4', -// 'mp4' => 'audio/mp4', - 'mkv' => 'video/x-matroska', + 'mp4' => 'video/mp4', +// 'mp4' => 'audio/mp4', + 'mkv' => 'video/x-matroska', // adobe 'pdf' => 'application/pdf', 'psd' => 'image/vnd.adobe.photoshop', - 'ai' => 'application/postscript', + 'ai' => 'application/postscript', 'eps' => 'application/postscript', - 'ps' => 'application/postscript', + 'ps' => 'application/postscript', // ms office 'doc' => 'application/msword', @@ -1279,8 +1281,10 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { intval($channel_id) ); - if(! $r) + if(! $r) { + attach_drop_photo($channel_id,$resource); return; + } $cloudpath = get_parent_cloudpath($channel_id, $channel_address, $resource); $object = get_file_activity_object($channel_id, $resource, $cloudpath); @@ -1326,19 +1330,10 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { ); if($r[0]['is_photo']) { - $x = q("select id, item_hidden from item where resource_id = '%s' and resource_type = 'photo' and uid = %d", - dbesc($resource), - intval($channel_id) - ); - if($x) { - drop_item($x[0]['id'],false,(($x[0]['item_hidden']) ? DROPITEM_NORMAL : DROPITEM_PHASE1),true); - } - q("DELETE FROM photo WHERE uid = %d AND resource_id = '%s'", - intval($channel_id), - dbesc($resource) - ); + attach_drop_photo($channel_id,$resource); } + // update the parent folder's lastmodified timestamp $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", dbesc(datetime_convert()), @@ -1351,6 +1346,24 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { return; } + +function attach_drop_photo($channel_id,$resource) { + + $x = q("select id, item_hidden from item where resource_id = '%s' and resource_type = 'photo' and uid = %d", + dbesc($resource), + intval($channel_id) + ); + if($x) { + drop_item($x[0]['id'],false,(($x[0]['item_hidden']) ? DROPITEM_NORMAL : DROPITEM_PHASE1),true); + } + q("DELETE FROM photo WHERE uid = %d AND resource_id = '%s'", + intval($channel_id), + dbesc($resource) + ); + +} + + /** * @brief Returns path to file in cloud/. * @@ -1464,18 +1477,34 @@ function find_folder_hash_by_attach_hash($channel_id, $attachHash, $recurse = fa function find_folder_hash_by_path($channel_id, $path) { - $filename = end(explode('/', $path)); + if(! $path) + return ''; - $r = q("SELECT hash FROM attach WHERE uid = %d AND filename = '%s' LIMIT 1", - intval($channel_id), - dbesc($filename) - ); + $comps = explode('/',$path); + $errors = false; + $parent_hash = ''; - $hash = ''; - if($r && $r[0]['hash']) { - $hash = $r[0]['hash']; + for($x = 0; $x < count($comps); $x ++) { + $element = $comps[$x]; + $r = q("SELECT hash FROM attach WHERE uid = %d AND filename = '%s' AND folder = '%s' LIMIT 1", + intval($channel_id), + dbesc($element), + dbesc($parent_hash) + ); + if($r) { + $parent_hash = $r[0]['hash']; + } + else { + $errors ++; + break; + } } - return $hash; + + if($errors) + return ''; + + return $parent_hash; + } /** diff --git a/include/auth.php b/include/auth.php index fdcecec36..c7be69583 100644 --- a/include/auth.php +++ b/include/auth.php @@ -300,7 +300,7 @@ else { // (i.e. expire when the browser is closed), even when there's a time expiration // on the cookie - if($_POST['remember_me']) { + if(($_POST['remember_me']) || ($_POST['remember'])) { $_SESSION['remember_me'] = 1; App::$session->new_cookie(31449600); // one year } diff --git a/include/bbcode.php b/include/bbcode.php index 32354aeda..c0033f280 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -7,7 +7,7 @@ require_once('include/oembed.php'); require_once('include/event.php'); require_once('include/zot.php'); -require_once('include/hubloc.php'); + function tryoembed($match) { $url = ((count($match) == 2) ? $match[1] : $match[2]); @@ -473,11 +473,40 @@ function bb_sanitize_style($input) { return '<span style="' . $css_string_san . '">' . $input[2] . '</span>'; } +function oblanguage_callback($matches) { + if(strlen($matches[1]) == 2) { + $compare = strtolower(substr(\App::$language,0,2)); + } + else { + $compare = strtolower(\App::$language); + } + + if($compare === strtolower($matches[1])) + return $matches[2]; + + return ''; +} + +function oblanguage_necallback($matches) { + if(strlen($matches[1]) == 2) { + $compare = strtolower(substr(\App::$language,0,2)); + } + else { + $compare = strtolower(\App::$language); + } + + if($compare !== strtolower($matches[1])) + return $matches[2]; + + return ''; +} + function bb_observer($Text) { $observer = App::get_observer(); if ((strpos($Text,'[/observer]') !== false) || (strpos($Text,'[/rpost]') !== false)) { + if ($observer) { $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); @@ -511,6 +540,20 @@ function bb_code($match) { return '<code class="inline-code">' . trim($match[1]) . '</code>'; } +function bb_code_options($match) { + if(strpos($match[0], "<br />")) { + $class = ""; + } else { + $class = "inline-code"; + } + if(strpos($match[1], 'nowrap')) { + $style = "overflow-x: auto; white-space: pre;"; + } else { + $style = ""; + } + return '<code class="'. $class .'" style="'. $style .'">' . trim($match[2]) . '</code>'; +} + function bb_highlight($match) { $lang = ((in_array(strtolower($match[1]),['php','css','mysql','sql','abap','diff','html','perl','ruby', 'vbscript','avrc','dtd','java','xml','cpp','python','javascript','js','json','sh'])) @@ -530,13 +573,28 @@ function bb_fixtable_lf($match) { } function parseIdentityAwareHTML($Text) { - + + // Hide all [noparse] contained bbtags by spacefying them + if (strpos($Text,'[noparse]') !== false) { + $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy',$Text); + } + if (strpos($Text,'[nobb]') !== false) { + $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_spacefy',$Text); + } + if (strpos($Text,'[pre]') !== false) { + $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy',$Text); + } // process [observer] tags before we do anything else because we might // be stripping away stuff that then doesn't need to be worked on anymore $observer = App::get_observer(); if ((strpos($Text,'[/observer]') !== false) || (strpos($Text,'[/rpost]') !== false)) { + + $Text = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_callback', $Text); + + $Text = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_necallback', $Text); + if ($observer) { $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); @@ -568,9 +626,21 @@ function parseIdentityAwareHTML($Text) { $Text = str_replace('[observer.photo]','', $Text); } - $Text = str_replace(array('[baseurl]','[sitename]'),array(z_root(),get_config('system','sitename')),$Text); + $Text = str_replace(array('[baseurl]','[sitename]'),array(z_root(),get_config('system','sitename')),$Text); - return $Text; + + // Unhide all [noparse] contained bbtags unspacefying them + // and triming the [noparse] tag. + if (strpos($Text,'[noparse]') !== false) { + $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_unspacefy_and_trim', $Text); + } + if (strpos($Text,'[nobb]') !== false) { + $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_unspacefy_and_trim', $Text); + } + if (strpos($Text,'[pre]') !== false) { + $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_unspacefy_and_trim', $Text); + } + return $Text; } // BBcode 2 HTML was written by WAY2WEB.net @@ -607,6 +677,9 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $observer = App::get_observer(); if ((strpos($Text,'[/observer]') !== false) || (strpos($Text,'[/rpost]') !== false)) { + $Text = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_callback', $Text); + $Text = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_necallback', $Text); + if ($observer) { $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); @@ -891,8 +964,8 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) } if (strpos($Text,'[/table]') !== false) { $Text = preg_replace("/\[table\](.*?)\[\/table\]/sm", '<table>$1</table>', $Text); - $Text = preg_replace("/\[table border=1\](.*?)\[\/table\]/sm", '<table border="1" >$1</table>', $Text); - $Text = preg_replace("/\[table border=0\](.*?)\[\/table\]/sm", '<table border="0" >$1</table>', $Text); + $Text = preg_replace("/\[table border=1\](.*?)\[\/table\]/sm", '<table class="table table-responsive table-bordered" >$1</table>', $Text); + $Text = preg_replace("/\[table border=0\](.*?)\[\/table\]/sm", '<table class="table table-responsive" >$1</table>', $Text); } $Text = str_replace('</tr><br /><tr>', "</tr>\n<tr>", $Text); $Text = str_replace('[hr]', '<hr />', $Text); @@ -911,6 +984,11 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace_callback("/\[code\](.*?)\[\/code\]/ism", 'bb_code', $Text); } + // Check for [code options] text + if (strpos($Text,'[code ') !== false) { + $Text = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", 'bb_code_options', $Text); + } + // Check for [spoiler] text $endlessloop = 0; while ((strpos($Text, "[/spoiler]")!== false) and (strpos($Text, "[spoiler]") !== false) and (++$endlessloop < 20)) { @@ -1127,9 +1205,9 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("/\<(.*?)(src|href)=(.*?)\&\;(.*?)\>/ism", '<$1$2=$3&$4>', $Text); // This is subtle - it's an XSS filter. It only accepts links with a protocol scheme and where - // the scheme begins with z (zhttp), h (http(s)), f (ftp), m (mailto), and named anchors. + // the scheme begins with z (zhttp), h (http(s)), f (ftp(s)), m (mailto), t (tel) and named anchors. - $Text = preg_replace("/\<(.*?)(src|href)=\"[^zhfm#](.*?)\>/ism", '<$1$2="">', $Text); + $Text = preg_replace("/\<(.*?)(src|href)=\"[^zhfmt#](.*?)\>/ism", '<$1$2="">', $Text); $Text = bb_replace_images($Text, $saved_images); diff --git a/include/bookmarks.php b/include/bookmarks.php index c6e8e86f2..0db103054 100644 --- a/include/bookmarks.php +++ b/include/bookmarks.php @@ -23,7 +23,6 @@ function bookmark_add($channel,$sender,$taxonomy,$private,$opts = null) { $iarr['mitem_desc'] = $taxonomy['term']; $iarr['mitem_flags'] = (($ischat) ? MENU_ITEM_CHATROOM : 0); - require_once('include/hubloc.php'); $zrl = is_matrix_url($taxonomy['url']); if($zrl) diff --git a/include/channel.php b/include/channel.php index 4fc873402..71ba2476f 100644 --- a/include/channel.php +++ b/include/channel.php @@ -291,59 +291,63 @@ function create_identity($arr) { // Create a verified hub location pointing to this site. - $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_primary, - hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_network ) - values ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s' )", - dbesc($guid), - dbesc($sig), - dbesc($hash), - dbesc(channel_reddress($ret['channel'])), - intval($primary), - dbesc(z_root()), - dbesc(base64url_encode(rsa_sign(z_root(),$ret['channel']['channel_prvkey']))), - dbesc(App::get_hostname()), - dbesc(z_root() . '/post'), - dbesc(get_config('system','pubkey')), - dbesc('zot') + $r = hubloc_store_lowlevel( + [ + 'hubloc_guid' => $guid, + 'hubloc_guid_sig' => $sig, + 'hubloc_hash' => $hash, + 'hubloc_addr' => channel_reddress($ret['channel']), + 'hubloc_primary' => $primary, + 'hubloc_url' => z_root(), + 'hubloc_url_sig' => base64url_encode(rsa_sign(z_root(),$ret['channel']['channel_prvkey'])), + 'hubloc_host' => App::get_hostname(), + 'hubloc_callback' => z_root() . '/post', + 'hubloc_sitekey' => get_config('system','pubkey'), + 'hubloc_network' => 'zot', + 'hubloc_updated' => datetime_convert() + ] ); if(! $r) logger('create_identity: Unable to store hub location'); $newuid = $ret['channel']['channel_id']; - $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_l, xchan_photo_m, xchan_photo_s, xchan_addr, xchan_url, xchan_follow, xchan_connurl, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_system ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)", - dbesc($hash), - dbesc($guid), - dbesc($sig), - dbesc($key['pubkey']), - dbesc(z_root() . "/photo/profile/l/{$newuid}"), - dbesc(z_root() . "/photo/profile/m/{$newuid}"), - dbesc(z_root() . "/photo/profile/s/{$newuid}"), - dbesc(channel_reddress($ret['channel'])), - dbesc(z_root() . '/channel/' . $ret['channel']['channel_address']), - dbesc(z_root() . '/follow?f=&url=%s'), - dbesc(z_root() . '/poco/' . $ret['channel']['channel_address']), - dbesc($ret['channel']['channel_name']), - dbesc('zot'), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($system) + $r = xchan_store_lowlevel( + [ + 'xchan_hash' => $hash, + 'xchan_guid' => $guid, + 'xchan_guid_sig' => $sig, + 'xchan_pubkey' => $key['pubkey'], + 'xchan_photo_l' => z_root() . "/photo/profile/l/{$newuid}", + 'xchan_photo_m' => z_root() . "/photo/profile/m/{$newuid}", + 'xchan_photo_s' => z_root() . "/photo/profile/s/{$newuid}", + 'xchan_addr' => channel_reddress($ret['channel']), + 'xchan_url' => z_root() . '/channel/' . $ret['channel']['channel_address'], + 'xchan_follow' => z_root() . '/follow?f=&url=%s', + 'xchan_connurl' => z_root() . '/poco/' . $ret['channel']['channel_address'], + 'xchan_name' => $ret['channel']['channel_name'], + 'xchan_network' => 'zot', + 'xchan_photo_date' => datetime_convert(), + 'xchan_name_date' => datetime_convert(), + 'xchan_system' => $system + ] ); // Not checking return value. // It's ok for this to fail if it's an imported channel, and therefore the hash is a duplicate - $r = q("INSERT INTO profile ( aid, uid, profile_guid, profile_name, is_default, publish, fullname, photo, thumb) - VALUES ( %d, %d, '%s', '%s', %d, %d, '%s', '%s', '%s') ", - intval($ret['channel']['channel_account_id']), - intval($newuid), - dbesc(random_string()), - t('Default Profile'), - 1, - $publish, - dbesc($ret['channel']['channel_name']), - dbesc(z_root() . "/photo/profile/l/{$newuid}"), - dbesc(z_root() . "/photo/profile/m/{$newuid}") + $r = profile_store_lowlevel( + [ + 'aid' => intval($ret['channel']['channel_account_id']), + 'uid' => intval($newuid), + 'profile_guid' => random_string(), + 'profile_name' => t('Default Profile'), + 'is_default' => 1, + 'publish' => $publish, + 'fullname' => $ret['channel']['channel_name'], + 'photo' => z_root() . "/photo/profile/l/{$newuid}", + 'thumb' => z_root() . "/photo/profile/m/{$newuid}" + ] ); if($role_permissions) { @@ -354,15 +358,16 @@ function create_identity($arr) { $myperms = $x['perms_connect']; } - $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_closeness, abook_created, abook_updated, abook_self ) - values ( %d, %d, '%s', %d, '%s', '%s', %d ) ", - intval($ret['channel']['channel_account_id']), - intval($newuid), - dbesc($hash), - intval(0), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval(1) + $r = abook_store_lowlevel( + [ + 'abook_account' => intval($ret['channel']['channel_account_id']), + 'abook_channel' => intval($newuid), + 'abook_xchan' => $hash, + 'abook_closeness' => 0, + 'abook_created' => datetime_convert(), + 'abook_updated' => datetime_convert(), + 'abook_self' => 1 + ] ); $x = \Zotlabs\Access\Permissions::FilledPerms($myperms); @@ -387,6 +392,7 @@ function create_identity($arr) { set_pconfig($newuid,'autoperms',$k,$v); } } + // as this is a new channel, this shouldn't do anything and probaby is not needed else { $r = q("delete from pconfig where uid = %d and cat = 'autoperms'", intval($newuid) @@ -459,6 +465,7 @@ function create_identity($arr) { * if true, set this default unconditionally * if $force is false only do this if there is no existing default */ + function set_default_login_identity($account_id, $channel_id, $force = true) { $r = q("select account_default_channel from account where account_id = %d limit 1", intval($account_id) @@ -473,6 +480,16 @@ function set_default_login_identity($account_id, $channel_id, $force = true) { } } + +function get_default_export_sections() { + $sections = [ 'channel', 'connections', 'config', 'apps', 'chatrooms', 'events', 'webpages', 'mail', 'wikis' ]; + + $cb = [ 'sections' => $sections ]; + call_hooks('get_default_export_sections', $cb); + return $cb['sections']; +} + + /** * @brief Create an array representing the important channel information * which would be necessary to create a nomadic identity clone. This includes @@ -486,214 +503,280 @@ function set_default_login_identity($account_id, $channel_id, $force = true) { * @returns array * See function for details */ -function identity_basic_export($channel_id, $items = false) { + +function identity_basic_export($channel_id, $sections = null) { /* - * Red basic channel export + * basic channel export */ - $ret = array(); + if(! $sections) { + $sections = get_default_export_sections(); + } + + $ret = []; // use constants here as otherwise we will have no idea if we can import from a site // with a non-standard platform and version. - $ret['compatibility'] = array('project' => PLATFORM_NAME, 'version' => STD_VERSION, 'database' => DB_UPDATE_VERSION, 'server_role' => Zotlabs\Lib\System::get_server_role()); + + $ret['compatibility'] = [ + 'project' => PLATFORM_NAME, + 'version' => STD_VERSION, + 'database' => DB_UPDATE_VERSION, + 'server_role' => Zotlabs\Lib\System::get_server_role() + ]; + + /* + * Process channel information regardless of it is one of the sections desired + * because we need the channel relocation information in all export files/streams. + */ $r = q("select * from channel where channel_id = %d limit 1", intval($channel_id) ); if($r) { translate_channel_perms_outbound($r[0]); - $ret['channel'] = $r[0]; $ret['relocate'] = [ 'channel_address' => $r[0]['channel_address'], 'url' => z_root()]; - } - - $r = q("select * from profile where uid = %d", - intval($channel_id) - ); - if($r) - $ret['profile'] = $r; - - $xchans = array(); - $r = q("select * from abook where abook_channel = %d ", - intval($channel_id) - ); - if($r) { - $ret['abook'] = $r; - - for($x = 0; $x < count($ret['abook']); $x ++) { - $xchans[] = $ret['abook'][$x]['abook_xchan']; - $abconfig = load_abconfig($channel_id,$ret['abook'][$x]['abook_xchan']); - if($abconfig) - $ret['abook'][$x]['abconfig'] = $abconfig; - translate_abook_perms_outbound($ret['abook'][$x]); + if(in_array('channel',$sections)) { + $ret['channel'] = $r[0]; } - stringify_array_elms($xchans); } - if($xchans) { - $r = q("select * from xchan where xchan_hash in ( " . implode(',',$xchans) . " ) "); + if(in_array('channel',$sections)) { + $r = q("select * from profile where uid = %d", + intval($channel_id) + ); if($r) - $ret['xchan'] = $r; + $ret['profile'] = $r; - $r = q("select * from hubloc where hubloc_hash in ( " . implode(',',$xchans) . " ) "); - if($r) - $ret['hubloc'] = $r; - } - $r = q("select * from groups where uid = %d ", - intval($channel_id) - ); + $r = q("select mimetype, content, os_storage from photo + where imgscale = 4 and photo_usage = %d and uid = %d limit 1", + intval(PHOTO_PROFILE), + intval($channel_id) + ); - if($r) - $ret['group'] = $r; + if($r) { + $ret['photo'] = [ + 'type' => $r[0]['mimetype'], + 'data' => (($r[0]['os_storage']) + ? base64url_encode(file_get_contents($r[0]['content'])) : base64url_encode($r[0]['content'])) + ]; + } + } - $r = q("select * from group_member where uid = %d ", - intval($channel_id) - ); - if($r) - $ret['group_member'] = $r; + if(in_array('connections',$sections)) { + $xchans = array(); + $r = q("select * from abook where abook_channel = %d ", + intval($channel_id) + ); + if($r) { + $ret['abook'] = $r; + + for($x = 0; $x < count($ret['abook']); $x ++) { + $xchans[] = $ret['abook'][$x]['abook_xchan']; + $abconfig = load_abconfig($channel_id,$ret['abook'][$x]['abook_xchan']); + if($abconfig) + $ret['abook'][$x]['abconfig'] = $abconfig; + translate_abook_perms_outbound($ret['abook'][$x]); + } + stringify_array_elms($xchans); + } - $r = q("select * from pconfig where uid = %d", - intval($channel_id) - ); - if($r) - $ret['config'] = $r; + if($xchans) { + $r = q("select * from xchan where xchan_hash in ( " . implode(',',$xchans) . " ) "); + if($r) + $ret['xchan'] = $r; - $r = q("select mimetype, content, os_storage from photo where imgscale = 4 and photo_usage = %d and uid = %d limit 1", - intval(PHOTO_PROFILE), - intval($channel_id) - ); + $r = q("select * from hubloc where hubloc_hash in ( " . implode(',',$xchans) . " ) "); + if($r) + $ret['hubloc'] = $r; + } - if($r) { - $ret['photo'] = array('type' => $r[0]['mimetype'], 'data' => (($r[0]['os_storage']) ? base64url_encode(file_get_contents($r[0]['content'])) : base64url_encode($r[0]['content']))); - } + $r = q("select * from groups where uid = %d ", + intval($channel_id) + ); - // All other term types will be included in items, if requested. + if($r) + $ret['group'] = $r; - $r = q("select * from term where ttype in (%d,%d) and uid = %d", - intval(TERM_SAVEDSEARCH), - intval(TERM_THING), - intval($channel_id) - ); - if($r) - $ret['term'] = $r; + $r = q("select * from group_member where uid = %d ", + intval($channel_id) + ); + if($r) + $ret['group_member'] = $r; - // add psuedo-column obj_baseurl to aid in relocations + } - $r = q("select obj.*, '%s' as obj_baseurl from obj where obj_channel = %d", - dbesc(z_root()), - intval($channel_id) - ); + if(in_array('config',$sections)) { + $r = q("select * from pconfig where uid = %d", + intval($channel_id) + ); + if($r) + $ret['config'] = $r; + + // All other term types will be included in items, if requested. + + $r = q("select * from term where ttype in (%d,%d) and uid = %d", + intval(TERM_SAVEDSEARCH), + intval(TERM_THING), + intval($channel_id) + ); + if($r) + $ret['term'] = $r; - if($r) - $ret['obj'] = $r; + // add psuedo-column obj_baseurl to aid in relocations - $r = q("select * from app where app_channel = %d and app_system = 0", - intval($channel_id) - ); - if($r) { - for($x = 0; $x < count($r); $x ++) { - $r[$x]['term'] = q("select * from term where otype = %d and oid = %d", - intval(TERM_OBJ_APP), - intval($r[$x]['id']) - ); - } - $ret['app'] = $r; - } + $r = q("select obj.*, '%s' as obj_baseurl from obj where obj_channel = %d", + dbesc(z_root()), + intval($channel_id) + ); - $r = q("select * from chatroom where cr_uid = %d", - intval($channel_id) - ); - if($r) - $ret['chatroom'] = $r; + if($r) + $ret['obj'] = $r; - $r = q("select * from event where uid = %d", - intval($channel_id) - ); - if($r) - $ret['event'] = $r; + $r = q("select * from likes where channel_id = %d", + intval($channel_id) + ); + + if($r) + $ret['likes'] = $r; - $r = q("select * from item where resource_type = 'event' and uid = %d", - intval($channel_id) - ); - if($r) { - $ret['event_item'] = array(); - xchan_query($r); - $r = fetch_post_tags($r,true); - foreach($r as $rr) - $ret['event_item'][] = encode_item($rr,true); } - $x = menu_list($channel_id); - if($x) { - $ret['menu'] = array(); - for($y = 0; $y < count($x); $y ++) { - $m = menu_fetch($x[$y]['menu_name'],$channel_id,$ret['channel']['channel_hash']); - if($m) - $ret['menu'][] = menu_element($ret['channel'],$m); + if(in_array('apps',$sections)) { + $r = q("select * from app where app_channel = %d and app_system = 0", + intval($channel_id) + ); + if($r) { + for($x = 0; $x < count($r); $x ++) { + $r[$x]['term'] = q("select * from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($r[$x]['id']) + ); + } + $ret['app'] = $r; } } - $addon = array('channel_id' => $channel_id,'data' => $ret); - call_hooks('identity_basic_export',$addon); - $ret = $addon['data']; + if(in_array('chatrooms',$sections)) { + $r = q("select * from chatroom where cr_uid = %d", + intval($channel_id) + ); + if($r) + $ret['chatroom'] = $r; + } - if(! $items) - return $ret; - $r = q("select * from likes where channel_id = %d", - intval($channel_id) - ); + if(in_array('events',$sections)) { + $r = q("select * from event where uid = %d", + intval($channel_id) + ); + if($r) + $ret['event'] = $r; - if($r) - $ret['likes'] = $r; + $r = q("select * from item where resource_type = 'event' and uid = %d", + intval($channel_id) + ); + if($r) { + $ret['event_item'] = array(); + xchan_query($r); + $r = fetch_post_tags($r,true); + foreach($r as $rr) + $ret['event_item'][] = encode_item($rr,true); + } + } + if(in_array('webpages',$sections)) { + $x = menu_list($channel_id); + if($x) { + $ret['menu'] = array(); + for($y = 0; $y < count($x); $y ++) { + $m = menu_fetch($x[$y]['menu_name'],$channel_id,$ret['channel']['channel_hash']); + if($m) + $ret['menu'][] = menu_element($ret['channel'],$m); + } + } + $r = q("select * from item where item_type in ( " + . ITEM_TYPE_BLOCK . "," . ITEM_TYPE_PDL . "," . ITEM_TYPE_WEBPAGE . " ) and uid = %d", + intval($channel_id) + ); + if($r) { + $ret['webpages'] = array(); + xchan_query($r); + $r = fetch_post_tags($r,true); + foreach($r as $rr) + $ret['webpages'][] = encode_item($rr,true); - $r = q("select * from conv where uid = %d", - intval($channel_id) - ); - if($r) { - for($x = 0; $x < count($r); $x ++) { - $r[$x]['subject'] = base64url_decode(str_rot47($r[$x]['subject'])); } - $ret['conv'] = $r; } - $r = q("select * from mail where mail.uid = %d", - intval($channel_id) - ); - if($r) { - $m = array(); - foreach($r as $rr) { - xchan_mail_query($rr); - $m[] = mail_encode($rr,true); + if(in_array('mail',$sections)) { + $r = q("select * from conv where uid = %d", + intval($channel_id) + ); + if($r) { + for($x = 0; $x < count($r); $x ++) { + $r[$x]['subject'] = base64url_decode(str_rot47($r[$x]['subject'])); + } + $ret['conv'] = $r; } - $ret['mail'] = $m; - } + $r = q("select * from mail where mail.uid = %d", + intval($channel_id) + ); + if($r) { + $m = array(); + foreach($r as $rr) { + xchan_mail_query($rr); + $m[] = mail_encode($rr,true); + } + $ret['mail'] = $m; + } + } - /** @warning this may run into memory limits on smaller systems */ + if(in_array('wikis',$sections)) { + $r = q("select * from item where resource_type like 'nwiki%%' and uid = %d order by created", + intval($channel_id) + ); + if($r) { + $ret['wiki'] = array(); + xchan_query($r); + $r = fetch_post_tags($r,true); + foreach($r as $rv) { + $ret['wiki'][] = encode_item($rv,true); + } + } + } + if(in_array('items',$sections)) { + /** @warning this may run into memory limits on smaller systems */ - /** export three months of posts. If you want to export and import all posts you have to start with - * the first year and export/import them in ascending order. - * - * Don't export linked resource items. we'll have to pull those out separately. - */ + /** export three months of posts. If you want to export and import all posts you have to start with + * the first year and export/import them in ascending order. + * + * Don't export linked resource items. we'll have to pull those out separately. + */ - $r = q("select * from item where item_wall = 1 and item_deleted = 0 and uid = %d and created > %s - INTERVAL %s and resource_type = '' order by created", - intval($channel_id), - db_utcnow(), - db_quoteinterval('3 MONTH') - ); - if($r) { - $ret['item'] = array(); - xchan_query($r); - $r = fetch_post_tags($r,true); - foreach($r as $rr) - $ret['item'][] = encode_item($rr,true); + $r = q("select * from item where item_wall = 1 and item_deleted = 0 and uid = %d + and created > %s - INTERVAL %s and resource_type = '' order by created", + intval($channel_id), + db_utcnow(), + db_quoteinterval('3 MONTH') + ); + if($r) { + $ret['item'] = array(); + xchan_query($r); + $r = fetch_post_tags($r,true); + foreach($r as $rr) + $ret['item'][] = encode_item($rr,true); + } } + $addon = [ 'channel_id' => $channel_id, 'sections' => $sections, 'data' => $ret]; + call_hooks('identity_basic_export',$addon); + $ret = $addon['data']; + return $ret; } @@ -768,7 +851,7 @@ function channel_export_items($channel_id, $start, $finish) { $ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()]; } - $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created < '%s' and resource_type = '' order by created", + $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created <= '%s' and resource_type = '' order by created", intval(ITEM_TYPE_POST), intval($channel_id), dbesc($start), @@ -1218,7 +1301,7 @@ function advanced_profile(&$a) { $profile['marital'] = array( t('Status:'), App::$profile['marital']); if(App::$profile['partner']) - $profile['marital']['partner'] = bbcode(App::$profile['partner']); + $profile['marital']['partner'] = zidify_links(bbcode(App::$profile['partner'])); if(strlen(App::$profile['howlong']) && App::$profile['howlong'] > NULL_DATE) { $profile['howlong'] = relative_date(App::$profile['howlong'], t('for %1$d %2$s')); @@ -1278,10 +1361,14 @@ function advanced_profile(&$a) { // logger('mod_profile: things: ' . print_r($things,true), LOGGER_DATA); + $exportlink = ((App::$profile['profile_vcard']) ? zid(z_root() . '/profile/' . App::$profile['channel_address'] . '/vcard') : ''); + return replace_macros($tpl, array( '$title' => t('Profile'), '$canlike' => (($profile['canlike'])? true : false), '$likethis' => t('Like this thing'), + '$export' => t('Export'), + '$exportlink' => $exportlink, '$profile' => $profile, '$fields' => $clean_fields, '$editmenu' => profile_edit_menu(App::$profile['profile_uid']), @@ -1705,21 +1792,26 @@ function get_zcard($channel, $observer_hash = '', $args = array()) { $maxwidth = (($args['width']) ? intval($args['width']) : 0); $maxheight = (($args['height']) ? intval($args['height']) : 0); - if(($maxwidth > 1200) || ($maxwidth < 1)) + if(($maxwidth > 1200) || ($maxwidth < 1)) { $maxwidth = 1200; + $cover_width = 1200; + } if($maxwidth <= 425) { $width = 425; + $cover_width = 425; $size = 'hz_small'; $cover_size = PHOTO_RES_COVER_425; $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']); } elseif($maxwidth <= 900) { $width = 900; + $cover_width = 850; $size = 'hz_medium'; $cover_size = PHOTO_RES_COVER_850; $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']); } elseif($maxwidth <= 1200) { $width = 1200; + $cover_width = 1200; $size = 'hz_large'; $cover_size = PHOTO_RES_COVER_1200; $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']); @@ -1741,7 +1833,8 @@ function get_zcard($channel, $observer_hash = '', $args = array()) { $cover = $r[0]; $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; } else { - $cover = $pphoto; + $default_cover = get_config('system','default_cover_photo','pexels-94622'); + $cover = [ 'href' => z_root() . '/images/default_cover_photos/' . $default_cover . '/' . $cover_width . '.jpg' ]; } $o .= replace_macros(get_markup_template('zcard.tpl'), array( @@ -1765,23 +1858,28 @@ function get_zcard_embed($channel, $observer_hash = '', $args = array()) { $maxwidth = (($args['width']) ? intval($args['width']) : 0); $maxheight = (($args['height']) ? intval($args['height']) : 0); - if(($maxwidth > 1200) || ($maxwidth < 1)) + if(($maxwidth > 1200) || ($maxwidth < 1)) { $maxwidth = 1200; + $cover_width = 1200; + } if($maxwidth <= 425) { $width = 425; + $cover_width = 425; $size = 'hz_small'; $cover_size = PHOTO_RES_COVER_425; $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']); } elseif($maxwidth <= 900) { $width = 900; + $cover_width = 850; $size = 'hz_medium'; $cover_size = PHOTO_RES_COVER_850; $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']); } elseif($maxwidth <= 1200) { $width = 1200; + $cover_width = 1200; $size = 'hz_large'; $cover_size = PHOTO_RES_COVER_1200; $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']); @@ -1799,8 +1897,10 @@ function get_zcard_embed($channel, $observer_hash = '', $args = array()) { if($r) { $cover = $r[0]; $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; - } else { - $cover = $pphoto; + } + else { + $default_cover = get_config('system','default_cover_photo','pexels-94622'); + $cover = [ 'href' => z_root() . '/images/default_cover_photos/' . $default_cover . '/' . $cover_width . '.jpg' ]; } $o .= replace_macros(get_markup_template('zcard_embed.tpl'),array( @@ -1876,8 +1976,74 @@ function channel_manual_conv_update($channel_id) { $x = get_pconfig($channel_id, 'system','manual_conversation_update'); if($x === false) - $x = get_config('system','manual_conversation_update'); + $x = get_config('system','manual_conversation_update', 1); return intval($x); +} + + +function remote_login() { + + $o = replace_macros(get_markup_template('remote_login.tpl'),array( + '$title' => t('Remote Authentication'), + '$desc' => t('Enter your channel address (e.g. channel@example.com)'), + '$submit' => t('Authenticate') + )); + return $o; + +} + + + +function profile_store_lowlevel($arr) { + + $store = [ + 'profile_guid' => ((array_key_exists('profile_guid',$arr)) ? $arr['profile_guid'] : ''), + 'aid' => ((array_key_exists('aid',$arr)) ? $arr['aid'] : 0), + 'uid' => ((array_key_exists('uid',$arr)) ? $arr['uid'] : 0), + 'profile_name' => ((array_key_exists('profile_name',$arr)) ? $arr['profile_name'] : ''), + 'is_default' => ((array_key_exists('is_default',$arr)) ? $arr['is_default'] : 0), + 'hide_friends' => ((array_key_exists('hide_friends',$arr)) ? $arr['hide_friends'] : 0), + 'fullname' => ((array_key_exists('fullname',$arr)) ? $arr['fullname'] : ''), + 'pdesc' => ((array_key_exists('pdesc',$arr)) ? $arr['pdesc'] : ''), + 'chandesc' => ((array_key_exists('chandesc',$arr)) ? $arr['chandesc'] : ''), + 'dob' => ((array_key_exists('dob',$arr)) ? $arr['dob'] : ''), + 'dob_tz' => ((array_key_exists('dob_tz',$arr)) ? $arr['dob_tz'] : ''), + 'address' => ((array_key_exists('address',$arr)) ? $arr['address'] : ''), + 'locality' => ((array_key_exists('locality',$arr)) ? $arr['locality'] : ''), + 'region' => ((array_key_exists('region',$arr)) ? $arr['region'] : ''), + 'postal_code' => ((array_key_exists('postal_code',$arr)) ? $arr['postal_code'] : ''), + 'country_name' => ((array_key_exists('country_name',$arr)) ? $arr['country_name'] : ''), + 'hometown' => ((array_key_exists('hometown',$arr)) ? $arr['hometown'] : ''), + 'gender' => ((array_key_exists('gender',$arr)) ? $arr['gender'] : ''), + 'marital' => ((array_key_exists('marital',$arr)) ? $arr['marital'] : ''), + 'partner' => ((array_key_exists('partner',$arr)) ? $arr['partner'] : ''), + 'howlong' => ((array_key_exists('howlong',$arr)) ? $arr['howlong'] : NULL_DATE), + 'sexual' => ((array_key_exists('sexual',$arr)) ? $arr['sexual'] : ''), + 'politic' => ((array_key_exists('politic',$arr)) ? $arr['politic'] : ''), + 'religion' => ((array_key_exists('religion',$arr)) ? $arr['religion'] : ''), + 'keywords' => ((array_key_exists('keywords',$arr)) ? $arr['keywords'] : ''), + 'likes' => ((array_key_exists('likes',$arr)) ? $arr['likes'] : ''), + 'dislikes' => ((array_key_exists('dislikes',$arr)) ? $arr['dislikes'] : ''), + 'about' => ((array_key_exists('about',$arr)) ? $arr['about'] : ''), + 'summary' => ((array_key_exists('summary',$arr)) ? $arr['summary'] : ''), + 'music' => ((array_key_exists('music',$arr)) ? $arr['music'] : ''), + 'book' => ((array_key_exists('book',$arr)) ? $arr['book'] : ''), + 'tv' => ((array_key_exists('tv',$arr)) ? $arr['tv'] : ''), + 'film' => ((array_key_exists('film',$arr)) ? $arr['film'] : ''), + 'interest' => ((array_key_exists('interest',$arr)) ? $arr['interest'] : ''), + 'romance' => ((array_key_exists('romance',$arr)) ? $arr['romance'] : ''), + 'employment' => ((array_key_exists('employment',$arr)) ? $arr['employment'] : ''), + 'education' => ((array_key_exists('education',$arr)) ? $arr['education'] : ''), + 'contact' => ((array_key_exists('contact',$arr)) ? $arr['contact'] : ''), + 'channels' => ((array_key_exists('channels',$arr)) ? $arr['channels'] : ''), + 'homepage' => ((array_key_exists('homepage',$arr)) ? $arr['homepage'] : ''), + 'photo' => ((array_key_exists('photo',$arr)) ? $arr['photo'] : ''), + 'thumb' => ((array_key_exists('thumb',$arr)) ? $arr['thumb'] : ''), + 'publish' => ((array_key_exists('publish',$arr)) ? $arr['publish'] : 0), + 'profile_vcard' => ((array_key_exists('profile_vcard',$arr)) ? $arr['profile_vcard'] : '') + ]; + + return create_table_from_array('profile',$store); }
\ No newline at end of file diff --git a/include/connections.php b/include/connections.php index b08d046b3..e26943b68 100644 --- a/include/connections.php +++ b/include/connections.php @@ -1,6 +1,38 @@ <?php /** @file */ +function abook_store_lowlevel($arr) { + + $store = [ + 'abook_account' => ((array_key_exists('abook_account',$arr)) ? $arr['abook_account'] : 0), + 'abook_channel' => ((array_key_exists('abook_channel',$arr)) ? $arr['abook_channel'] : 0), + 'abook_xchan' => ((array_key_exists('abook_xchan',$arr)) ? $arr['abook_xchan'] : ''), + 'abook_my_perms' => ((array_key_exists('abook_my_perms',$arr)) ? $arr['abook_my_perms'] : 0), + 'abook_their_perms' => ((array_key_exists('abook_their_perms',$arr)) ? $arr['abook_their_perms'] : 0), + 'abook_closeness' => ((array_key_exists('abook_closeness',$arr)) ? $arr['abook_closeness'] : 99), + 'abook_created' => ((array_key_exists('abook_created',$arr)) ? $arr['abook_created'] : NULL_DATE), + 'abook_updated' => ((array_key_exists('abook_updated',$arr)) ? $arr['abook_updated'] : NULL_DATE), + 'abook_connected' => ((array_key_exists('abook_connected',$arr)) ? $arr['abook_connected'] : NULL_DATE), + 'abook_dob' => ((array_key_exists('abook_dob',$arr)) ? $arr['abook_dob'] : NULL_DATE), + 'abook_flags' => ((array_key_exists('abook_flags',$arr)) ? $arr['abook_flags'] : 0), + 'abook_blocked' => ((array_key_exists('abook_blocked',$arr)) ? $arr['abook_blocked'] : 0), + 'abook_ignored' => ((array_key_exists('abook_ignored',$arr)) ? $arr['abook_ignored'] : 0), + 'abook_hidden' => ((array_key_exists('abook_hidden',$arr)) ? $arr['abook_hidden'] : 0), + 'abook_archived' => ((array_key_exists('abook_archived',$arr)) ? $arr['abook_archived'] : 0), + 'abook_pending' => ((array_key_exists('abook_pending',$arr)) ? $arr['abook_pending'] : 0), + 'abook_unconnected' => ((array_key_exists('abook_unconnected',$arr)) ? $arr['abook_unconnected'] : 0), + 'abook_self' => ((array_key_exists('abook_self',$arr)) ? $arr['abook_self'] : 0), + 'abook_feed' => ((array_key_exists('abook_feed',$arr)) ? $arr['abook_feed'] : 0), + 'abook_profile' => ((array_key_exists('abook_profile',$arr)) ? $arr['abook_profile'] : ''), + 'abook_incl' => ((array_key_exists('abook_incl',$arr)) ? $arr['abook_incl'] : ''), + 'abook_excl' => ((array_key_exists('abook_excl',$arr)) ? $arr['abook_excl'] : ''), + 'abook_instance' => ((array_key_exists('abook_instance',$arr)) ? $arr['abook_instance'] : '') + ]; + + return create_table_from_array('abook',$store); + +} + function rconnect_url($channel_id,$xchan) { @@ -630,3 +662,279 @@ function random_profile() { return ''; } +function update_vcard($arr,$vcard = null) { + + + // logger('update_vcard: ' . print_r($arr,true)); + + $fn = $arr['fn']; + + + // This isn't strictly correct and could be a cause for concern. + // 'N' => array_reverse(explode(' ', $fn)) + + + // What we really want is + // 'N' => Adams;John;Quincy;Reverend,Dr.;III + // which is a very difficult parsing problem especially if you allow + // the surname to contain spaces. The only way to be sure to get it + // right is to provide a form to input all the various fields and not + // try to extract it from the FN. + + if(! $vcard) { + $vcard = new \Sabre\VObject\Component\VCard([ + 'FN' => $fn, + 'N' => array_reverse(explode(' ', $fn)) + ]); + } + else { + $vcard->FN = $fn; + $vcard->N = array_reverse(explode(' ', $fn)); + } + + $org = $arr['org']; + if($org) { + $vcard->ORG = $org; + } + + $title = $arr['title']; + if($title) { + $vcard->TITLE = $title; + } + + $tel = $arr['tel']; + $tel_type = $arr['tel_type']; + if($tel) { + $i = 0; + foreach($tel as $item) { + if($item) { + $vcard->add('TEL', $item, ['type' => $tel_type[$i]]); + } + $i++; + } + } + + $email = $arr['email']; + $email_type = $arr['email_type']; + if($email) { + $i = 0; + foreach($email as $item) { + if($item) { + $vcard->add('EMAIL', $item, ['type' => $email_type[$i]]); + } + $i++; + } + } + + $impp = $arr['impp']; + $impp_type = $arr['impp_type']; + if($impp) { + $i = 0; + foreach($impp as $item) { + if($item) { + $vcard->add('IMPP', $item, ['type' => $impp_type[$i]]); + } + $i++; + } + } + + $url = $arr['url']; + $url_type = $arr['url_type']; + if($url) { + $i = 0; + foreach($url as $item) { + if($item) { + $vcard->add('URL', $item, ['type' => $url_type[$i]]); + } + $i++; + } + } + + $adr = $arr['adr']; + $adr_type = $arr['adr_type']; + + if($adr) { + $i = 0; + foreach($adr as $item) { + if($item) { + $vcard->add('ADR', $item, ['type' => $adr_type[$i]]); + } + $i++; + } + } + + $note = $arr['note']; + if($note) { + $vcard->NOTE = $note; + } + + return $vcard->serialize(); + +} + +function get_vcard_array($vc,$id) { + + $photo = ''; + if($vc->PHOTO) { + $photo_value = strtolower($vc->PHOTO->getValueType()); // binary or uri + if($photo_value === 'binary') { + $photo_type = strtolower($vc->PHOTO['TYPE']); // mime jpeg, png or gif + $photo = 'data:image/' . $photo_type . ';base64,' . base64_encode((string)$vc->PHOTO); + } + else { + $url = parse_url((string)$vc->PHOTO); + $photo = 'data:' . $url['path']; + } + } + + $fn = ''; + if($vc->FN) { + $fn = (string) escape_tags($vc->FN); + } + + $org = ''; + if($vc->ORG) { + $org = (string) escape_tags($vc->ORG); + } + + $title = ''; + if($vc->TITLE) { + $title = (string) escape_tags($vc->TITLE); + } + + $tels = []; + if($vc->TEL) { + foreach($vc->TEL as $tel) { + $type = (($tel['TYPE']) ? vcard_translate_type((string)$tel['TYPE']) : ''); + $tels[] = [ + 'type' => $type, + 'nr' => (string) escape_tags($tel) + ]; + } + } + $emails = []; + if($vc->EMAIL) { + foreach($vc->EMAIL as $email) { + $type = (($email['TYPE']) ? vcard_translate_type((string)$email['TYPE']) : ''); + $emails[] = [ + 'type' => $type, + 'address' => (string) escape_tags($email) + ]; + } + } + + $impps = []; + if($vc->IMPP) { + foreach($vc->IMPP as $impp) { + $type = (($impp['TYPE']) ? vcard_translate_type((string)$impp['TYPE']) : ''); + $impps[] = [ + 'type' => $type, + 'address' => (string) escape_tags($impp) + ]; + } + } + + $urls = []; + if($vc->URL) { + foreach($vc->URL as $url) { + $type = (($url['TYPE']) ? vcard_translate_type((string)$url['TYPE']) : ''); + $urls[] = [ + 'type' => $type, + 'address' => (string) escape_tags($url) + ]; + } + } + + $adrs = []; + if($vc->ADR) { + foreach($vc->ADR as $adr) { + $type = (($adr['TYPE']) ? vcard_translate_type((string)$adr['TYPE']) : ''); + $adrs[] = [ + 'type' => $type, + 'address' => $adr->getParts() + ]; + $last_entry = end($adrs); + if($adrs[$last_entry]['address']) + array_walk($adrs[$last_entry]['address'],'array_escape_tags'); + } + } + + $note = ''; + if($vc->NOTE) { + $note = (string) escape_tags($vc->NOTE); + } + + $card = [ + 'id' => $id, + 'photo' => $photo, + 'fn' => $fn, + 'org' => $org, + 'title' => $title, + 'tels' => $tels, + 'emails' => $emails, + 'impps' => $impps, + 'urls' => $urls, + 'adrs' => $adrs, + 'note' => $note + ]; + + return $card; + +} + + +function vcard_translate_type($type) { + + if(!$type) + return; + + $type = strtoupper($type); + + $map = [ + 'CELL' => t('Mobile'), + 'HOME' => t('Home'), + 'HOME,VOICE' => t('Home, Voice'), + 'HOME,FAX' => t('Home, Fax'), + 'WORK' => t('Work'), + 'WORK,VOICE' => t('Work, Voice'), + 'WORK,FAX' => t('Work, Fax'), + 'OTHER' => t('Other') + ]; + + if (array_key_exists($type, $map)) { + return [$type, $map[$type]]; + } + else { + return [$type, t('Other') . ' (' . $type . ')']; + } +} + + +function vcard_query(&$r) { + + $arr = []; + + if($r && is_array($r) && count($r)) { + $uid = $r[0]['abook_channel']; + foreach($r as $rv) { + if($rv['abook_xchan'] && (! in_array("'" . dbesc($rv['abook_xchan']) . "'",$arr))) + $arr[] = "'" . dbesc($rv['abook_xchan']) . "'"; + } + } + + if($arr) { + $a = q("select * from abconfig where chan = %d and xchan in (" . protect_sprintf(implode(',', $arr)) . ") and cat = 'system' and k = 'vcard'", + intval($uid) + ); + if($a) { + foreach($a as $av) { + for($x = 0; $x < count($r); $x ++) { + if($r[$x]['abook_xchan'] == $av['xchan']) { + $vctmp = \Sabre\VObject\Reader::read($av['v']); + $r[$x]['vcard'] = (($vctmp) ? get_vcard_array($vctmp,$r[$x]['abook_id']) : [] ); + } + } + } + } + } +}
\ No newline at end of file diff --git a/include/conversation.php b/include/conversation.php index d367c27a6..a1acc456a 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -689,7 +689,8 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ 'id' => (($preview) ? 'P0' : $item['item_id']), 'linktitle' => sprintf( t('View %s\'s profile @ %s'), $profile_name, $profile_url), 'profile_url' => $profile_link, - 'item_photo_menu' => item_photo_menu($item), + 'thread_action_menu' => thread_action_menu($item,$mode), + 'thread_author_menu' => thread_author_menu($item,$mode), 'name' => $profile_name, 'sparkle' => $sparkle, 'lock' => $lock, @@ -732,7 +733,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ 'like' => '', 'dislike' => '', 'comment' => '', - 'conv' => (($preview) ? '' : array('href'=> z_root() . '/display/' . $item['mid'], 'title'=> t('View in context'))), + 'conv' => (($preview) ? '' : array('href'=> z_root() . '/display/' . gen_link_id($item['mid']), 'title'=> t('View in context'))), 'previewing' => $previewing, 'wait' => t('Please wait'), 'thread_level' => 1, @@ -960,6 +961,169 @@ function item_photo_menu($item){ return $o; } + +function thread_action_menu($item,$mode = '') { + + $menu = []; + + if((local_channel()) && local_channel() == $item['uid']) { + $menu[] = [ + 'menu' => 'view_source', + 'title' => t('View Source'), + 'icon' => 'eye', + 'action' => 'viewsrc(' . $item['id'] . '); return false;', + 'href' => '#' + ]; + + if(! in_array($mode, [ 'network-new', 'search', 'community'])) { + if($item['parent'] == $item['id'] && (get_observer_hash() != $item['author_xchan'])) { + $menu[] = [ + 'menu' => 'follow_thread', + 'title' => t('Follow Thread'), + 'icon' => 'plus', + 'action' => 'dosubthread(' . $item['id'] . '); return false;', + 'href' => '#' + ]; + } + + $menu[] = [ + 'menu' => 'unfollow_thread', + 'title' => t('Unfollow Thread'), + 'icon' => 'minus', + 'action' => 'dounsubthread(' . $item['id'] . '); return false;', + 'href' => '#' + ]; + } + + } + + + + + $args = [ 'item' => $item, 'mode' => $mode, 'menu' => $menu ]; + call_hooks('thread_action_menu', $args); + + return $args['menu']; + +} + +function thread_author_menu($item, $mode = '') { + + $menu = []; + + $local_channel = local_channel(); + + if($local_channel) { + if(! count(App::$contacts)) + load_contact_links($local_channel); + $channel = App::get_channel(); + $channel_hash = (($channel) ? $channel['channel_hash'] : ''); + } + + $profile_link = chanlink_hash($item['author_xchan']); + if($item['uid'] > 0) + $pm_url = z_root() . '/mail/new/?f=&hash=' . $item['author_xchan']; + + if(App::$contacts && array_key_exists($item['author_xchan'],App::$contacts)) + $contact = App::$contacts[$item['author_xchan']]; + else + if($local_channel && $item['author']['xchan_addr']) + $follow_url = z_root() . '/follow/?f=&url=' . $item['author']['xchan_addr']; + + if($contact) { + $poke_link = z_root() . '/poke/?f=&c=' . $contact['abook_id']; + if (! intval($contact['abook_self'])) + $contact_url = z_root() . '/connedit/' . $contact['abook_id']; + $posts_link = z_root() . '/network/?cid=' . $contact['abook_id']; + + $clean_url = normalise_link($item['author-link']); + } + + $rating_enabled = get_config('system','rating_enabled'); + + $ratings_url = (($rating_enabled) ? z_root() . '/ratings/' . urlencode($item['author_xchan']) : ''); + + if($profile_link) { + $menu[] = [ + 'menu' => 'view_profile', + 'title' => t('View Profile'), + 'icon' => 'fw', + 'action' => '', + 'href' => $profile_link + ]; + } + + if($posts_link) { + $menu[] = [ + 'menu' => 'view_posts', + 'title' => t('Activity/Posts'), + 'icon' => 'fw', + 'action' => '', + 'href' => $posts_link + ]; + } + + if($follow_url) { + $menu[] = [ + 'menu' => 'follow', + 'title' => t('Connect'), + 'icon' => 'fw', + 'action' => '', + 'href' => $follow_url + ]; + } + + if($contact_url) { + $menu[] = [ + 'menu' => 'connedit', + 'title' => t('Edit Connection'), + 'icon' => 'fw', + 'action' => '', + 'href' => $contact_url + ]; + } + + if($pm_url) { + $menu[] = [ + 'menu' => 'prv_message', + 'title' => t('Message'), + 'icon' => 'fw', + 'action' => '', + 'href' => $pm_url + ]; + } + + if($ratings_url) { + $menu[] = [ + 'menu' => 'ratings', + 'title' => t('Ratings'), + 'icon' => 'fw', + 'action' => '', + 'href' => $ratings_url + ]; + } + + if($poke_link) { + $menu[] = [ + 'menu' => 'poke', + 'title' => t('Poke'), + 'icon' => 'fw', + 'action' => '', + 'href' => $poke_link + ]; + } + + $args = [ 'item' => $item, 'mode' => $mode, 'menu' => $menu ]; + call_hooks('thread_author_menu', $args); + + return $args['menu']; + +} + + + + + /** * @brief Checks item to see if it is one of the builtin activities (like/dislike, event attendance, consensus items, etc.) * @@ -1011,8 +1175,8 @@ function builtin_activity_puller($item, &$conv_responses) { if((activity_match($item['verb'], $verb)) && ($item['id'] != $item['parent'])) { $name = (($item['author']['xchan_name']) ? $item['author']['xchan_name'] : t('Unknown')); - $url = (($item['author']['xchan_url'] && $item['author']['xchan_photo_s']) - ? '<a href="' . chanlink_url($item['author']['xchan_url']) . '">' . '<img class="dropdown-menu-img-xs" src="' . zid($item['author']['xchan_photo_s']) . '" alt="' . urlencode($name) . '" />' . $name . '</a>' + $url = (($item['author_xchan'] && $item['author']['xchan_photo_s']) + ? '<a href="' . chanlink_hash($item['author_xchan']) . '">' . '<img class="dropdown-menu-img-xs" src="' . zid($item['author']['xchan_photo_s']) . '" alt="' . urlencode($name) . '" />' . $name . '</a>' : '<a href="#" class="disabled">' . $name . '</a>' ); @@ -1232,11 +1396,11 @@ function status_editor($a, $x, $popup = false) { '$setloc' => $setloc, '$voting' => t('Toggle voting'), '$feature_voting' => $feature_voting, - '$consensus' => 0, + '$consensus' => ((array_key_exists('item',$x)) ? $x['item']['item_consensus'] : 0), '$nocommenttitle' => t('Disable comments'), '$nocommenttitlesub' => t('Toggle comments'), '$feature_nocomment' => $feature_nocomment, - '$nocomment' => 0, + '$nocomment' => ((array_key_exists('item',$x)) ? $x['item']['item_nocomment'] : 0), '$clearloc' => $clearloc, '$title' => ((x($x, 'title')) ? htmlspecialchars($x['title'], ENT_COMPAT,'UTF-8') : ''), '$placeholdertitle' => ((x($x, 'placeholdertitle')) ? $x['placeholdertitle'] : t('Title (optional)')), @@ -1400,7 +1564,7 @@ function format_location($item) { if(strpos($item['location'],'#') === 0) { $location = substr($item['location'],1); - $location = ((strpos($location,'[') !== false) ? bbcode($location) : $location); + $location = ((strpos($location,'[') !== false) ? zidify_links(bbcode($location)) : $location); } else { $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => ''); @@ -1452,7 +1616,7 @@ function prepare_page($item) { '$author' => (($naked) ? '' : $item['author']['xchan_name']), '$auth_url' => (($naked) ? '' : zid($item['author']['xchan_url'])), '$date' => (($naked) ? '' : datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'Y-m-d H:i')), - '$title' => smilies(bbcode($item['title'])), + '$title' => zidify_links(smilies(bbcode($item['title']))), '$body' => $body['html'], '$preview' => $preview, '$link' => $link, @@ -1622,6 +1786,20 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ $cal_link = '/cal/' . $nickname; } + require_once('include/security.php'); + $sql_options = item_permissions_sql($uid); + + $r = q("select item.* from item left join iconfig on item.id = iconfig.iid + where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' + and item.item_delayed = 0 and item.item_deleted = 0 + and ( iconfig.k = 'WEBPAGE' and item_type = %d ) + $sql_options limit 1", + intval($uid), + dbesc('home'), + intval(ITEM_TYPE_WEBPAGE) + ); + + $has_webpages = (($r) ? true : false); if (get_pconfig($uid, 'system', 'noprofiletabs')) return; @@ -1706,15 +1884,16 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ ); } - if ($p['write_pages'] && feature_enabled($uid,'webpages')) { + if($has_webpages && feature_enabled($uid,'webpages')) { $tabs[] = array( 'label' => t('Webpages'), - 'url' => z_root() . '/webpages/' . $nickname, + 'url' => z_root() . '/page/' . $nickname . '/home', 'sel' => ((argv(0) == 'webpages') ? 'active' : ''), - 'title' => t('Manage Webpages'), + 'title' => t('View Webpages'), 'id' => 'webpages-tab', ); - } + } + if(feature_enabled($uid,'wiki') && (get_account_techlevel($account_id) > 3)) { $tabs[] = array( diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php index 81a3bd590..e47f97387 100755 --- a/include/dba/dba_driver.php +++ b/include/dba/dba_driver.php @@ -337,7 +337,7 @@ function db_concat($fld, $sep) { function q($sql) { $args = func_get_args(); - unset($args[0]); + array_shift($args); if(\DBA::$dba && \DBA::$dba->connected) { $stmt = vsprintf($sql, $args); diff --git a/include/event.php b/include/event.php index cbee2b759..cf1cc331d 100644 --- a/include/event.php +++ b/include/event.php @@ -25,7 +25,7 @@ function format_event_html($ev) { $o = '<div class="vevent">' . "\r\n"; - $o .= '<div class="event-title"><h3><i class="fa fa-calendar"></i> ' . bbcode($ev['summary']) . '</h3></div>' . "\r\n"; + $o .= '<div class="event-title"><h3><i class="fa fa-calendar"></i> ' . zidify_links(smilies(bbcode($ev['summary']))) . '</h3></div>' . "\r\n"; $o .= '<div class="event-start"><span class="event-label">' . t('Starts:') . '</span> <span class="dtstart" title="' . datetime_convert('UTC', 'UTC', $ev['dtstart'], (($ev['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )) @@ -46,11 +46,11 @@ function format_event_html($ev) { $ev['dtend'] , $bd_format ))) . '</span></div>' . "\r\n"; - $o .= '<div class="event-description">' . bbcode($ev['description']) . '</div>' . "\r\n"; + $o .= '<div class="event-description">' . zidify_links(smilies(bbcode($ev['description']))) . '</div>' . "\r\n"; if(strlen($ev['location'])) $o .= '<div class="event-location"><span class="event-label"> ' . t('Location:') . '</span> <span class="location">' - . bbcode($ev['location']) + . zidify_links(smilies(bbcode($ev['location']))) . '</span></div>' . "\r\n"; $o .= '</div>' . "\r\n"; @@ -69,7 +69,7 @@ function format_event_obj($jobject) { $bd_format = t('l F d, Y \@ g:i A'); // Friday January 18, 2011 @ 8:01 AM $event['header'] = replace_macros(get_markup_template('event_item_header.tpl'),array( - '$title' => bbcode($object['title']), + '$title' => zidify_links(smilies(bbcode($object['title']))), '$dtstart_label' => t('Starts:'), '$dtstart_title' => datetime_convert('UTC', 'UTC', $object['dtstart'], (($object['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )), '$dtstart_dt' => (($object['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $object['dtstart'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', $object['dtstart'] , $bd_format))), @@ -80,9 +80,9 @@ function format_event_obj($jobject) { )); $event['content'] = replace_macros(get_markup_template('event_item_content.tpl'),array( - '$description' => bbcode($object['description']), + '$description' => zidify_links(smilies(bbcode($object['description']))), '$location_label' => t('Location:'), - '$location' => bbcode($object['location']) + '$location' => zidify_links(smilies(bbcode($object['location']))) )); } @@ -127,12 +127,18 @@ function format_event_ical($ev) { $o .= "\r\nDTSTART:" . datetime_convert('UTC','UTC', $ev['dtstart'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); if($ev['dtend'] && ! $ev['nofinish']) $o .= "\r\nDTEND:" . datetime_convert('UTC','UTC', $ev['dtend'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); - if($ev['summary']) + if($ev['summary']) { $o .= "\r\nSUMMARY:" . format_ical_text($ev['summary']); - if($ev['location']) + $o .= "\r\nX-ZOT-SUMMARY:" . format_ical_sourcetext($ev['summary']); + } + if($ev['location']) { $o .= "\r\nLOCATION:" . format_ical_text($ev['location']); - if($ev['description']) + $o .= "\r\nX-ZOT-LOCATION:" . format_ical_sourcetext($ev['location']); + } + if($ev['description']) { $o .= "\r\nDESCRIPTION:" . format_ical_text($ev['description']); + $o .= "\r\nX-ZOT-DESCRIPTION:" . format_ical_sourcetext($ev['description']); + } if($ev['event_priority']) $o .= "\r\nPRIORITY:" . intval($ev['event_priority']); $o .= "\r\nUID:" . $ev['event_hash'] ; @@ -154,8 +160,10 @@ function format_todo_ical($ev) { $o .= "\r\nDTSTART:" . datetime_convert('UTC','UTC', $ev['dtstart'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); if($ev['dtend'] && ! $ev['nofinish']) $o .= "\r\nDUE:" . datetime_convert('UTC','UTC', $ev['dtend'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); - if($ev['summary']) + if($ev['summary']) { $o .= "\r\nSUMMARY:" . format_ical_text($ev['summary']); + $o .= "\r\nX-ZOT-SUMMARY:" . format_ical_sourcetext($ev['summary']); + } if($ev['event_status']) { $o .= "\r\nSTATUS:" . $ev['event_status']; if($ev['event_status'] === 'COMPLETED') @@ -165,10 +173,14 @@ function format_todo_ical($ev) { $o .= "\r\nPERCENT-COMPLETE:" . $ev['event_percent']; if(intval($ev['event_sequence'])) $o .= "\r\nSEQUENCE:" . $ev['event_sequence']; - if($ev['location']) + if($ev['location']) { $o .= "\r\nLOCATION:" . format_ical_text($ev['location']); - if($ev['description']) + $o .= "\r\nX-ZOT-LOCATION:" . format_ical_sourcetext($ev['location']); + } + if($ev['description']) { $o .= "\r\nDESCRIPTION:" . format_ical_text($ev['description']); + $o .= "\r\nX-ZOT-DESCRIPTION:" . format_ical_sourcetext($ev['description']); + } $o .= "\r\nUID:" . $ev['event_hash'] ; if($ev['event_priority']) $o .= "\r\nPRIORITY:" . intval($ev['event_priority']); @@ -178,7 +190,6 @@ function format_todo_ical($ev) { } - function format_ical_text($s) { require_once('include/bbcode.php'); require_once('include/html2plain.php'); @@ -186,6 +197,12 @@ function format_ical_text($s) { $s = html2plain(bbcode($s)); $s = str_replace(["\r\n","\n"],["",""],$s); return(wordwrap(str_replace(['\\',',',';'],['\\\\','\\,','\\;'],$s),72,"\r\n ",true)); + +} + +function format_ical_sourcetext($s) { + $s = base64_encode($s); + return(wordwrap(str_replace(['\\',',',';'],['\\\\','\\,','\\;'],$s),72,"\r\n ",true)); } @@ -623,12 +640,21 @@ function event_import_ical($ical, $uid) { $ev['edited'] = datetime_convert('UTC','UTC',$edited->format(\DateTime::W3C)); } - if(isset($ical->LOCATION)) + if(isset($ical->{'X-ZOT-LOCATION'})) + $ev['location'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-LOCATION'}); + elseif(isset($ical->LOCATION)) $ev['location'] = (string) $ical->LOCATION; - if(isset($ical->DESCRIPTION)) + + if(isset($ical->{'X-ZOT-DESCRIPTION'})) + $ev['description'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-DESCRIPTION'}); + elseif(isset($ical->DESCRIPTION)) $ev['description'] = (string) $ical->DESCRIPTION; - if(isset($ical->SUMMARY)) + + if(isset($ical->{'X-ZOT-SUMMARY'})) + $ev['summary'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-SUMMARY'}); + elseif(isset($ical->SUMMARY)) $ev['summary'] = (string) $ical->SUMMARY; + if(isset($ical->PRIORITY)) $ev['event_priority'] = intval((string) $ical->PRIORITY); @@ -663,6 +689,10 @@ function event_import_ical($ical, $uid) { } +function event_ical_get_sourcetext($s) { + return base64_decode($s); +} + function event_import_ical_task($ical, $uid) { $c = q("select * from channel where channel_id = %d limit 1", @@ -718,12 +748,21 @@ function event_import_ical_task($ical, $uid) { $ev['edited'] = datetime_convert('UTC','UTC',$edited->format(\DateTime::W3C)); } - if(isset($ical->LOCATION)) + if(isset($ical->{'X-ZOT-LOCATION'})) + $ev['location'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-LOCATION'}); + elseif(isset($ical->LOCATION)) $ev['location'] = (string) $ical->LOCATION; - if(isset($ical->DESCRIPTION)) + + if(isset($ical->{'X-ZOT-DESCRIPTION'})) + $ev['description'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-DESCRIPTION'}); + elseif(isset($ical->DESCRIPTION)) $ev['description'] = (string) $ical->DESCRIPTION; - if(isset($ical->SUMMARY)) + + if(isset($ical->{'X-ZOT-SUMMARY'})) + $ev['summary'] = event_ical_get_sourcetext( (string) $ical->{'X-ZOT-SUMMARY'}); + elseif(isset($ical->SUMMARY)) $ev['summary'] = (string) $ical->SUMMARY; + if(isset($ical->PRIORITY)) $ev['event_priority'] = intval((string) $ical->PRIORITY); @@ -977,9 +1016,9 @@ function event_store_item($arr, $event) { // otherwise we'll fallback to /display/$message_id if($wall) - $item_arr['plink'] = z_root() . '/channel/' . $z[0]['channel_address'] . '/?f=&mid=' . $item_arr['mid']; + $item_arr['plink'] = z_root() . '/channel/' . $z[0]['channel_address'] . '/?f=&mid=' . urlencode($item_arr['mid']); else - $item_arr['plink'] = z_root() . '/display/' . $item_arr['mid']; + $item_arr['plink'] = z_root() . '/display/' . gen_link_id($item_arr['mid']); $x = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($arr['event_xchan']) diff --git a/include/features.php b/include/features.php index 1ccdbf015..0fc6fbc1d 100644 --- a/include/features.php +++ b/include/features.php @@ -149,6 +149,15 @@ function get_features($filtered = true) { ], [ + 'permcats', + t('Permission Groups'), + t('Provide alternate connection permission roles.'), + false, + get_config('feature_lock','permcats'), + feature_level('permcats',2), + ], + + [ 'smart_birthdays', t('Smart Birthdays'), t('Make birthday events timezone aware in case your friends are scattered across the planet.'), @@ -157,6 +166,15 @@ function get_features($filtered = true) { feature_level('smart_birthdays',2), ], + [ + 'event_tz_select', + t('Event Timezone Selection'), + t('Allow event creation in timezones other than your own.'), + false, + get_config('feature_lock','event_tz_select'), + feature_level('event_tz_select',2), + ], + [ 'advanced_dirsearch', t('Advanced Directory Search'), diff --git a/include/feedutils.php b/include/feedutils.php index 1d58ec317..b122a8e4b 100644 --- a/include/feedutils.php +++ b/include/feedutils.php @@ -236,7 +236,7 @@ function get_atom_elements($feed, $item, &$author) { if(substr($author['author_link'],-1,1) == '/') $author['author_link'] = substr($author['author_link'],0,-1); - $res['mid'] = base64url_encode(unxmlify($item->get_id())); + $res['mid'] = unxmlify($item->get_id()); $res['title'] = unxmlify($item->get_title()); $res['body'] = unxmlify($item->get_content()); $res['plink'] = unxmlify($item->get_link(0)); @@ -331,6 +331,8 @@ function get_atom_elements($feed, $item, &$author) { } } + $ostatus_protocol = (($item->get_item_tags(NAMESPACE_OSTATUS,'conversation')) ? true : false); + $apps = $item->get_item_tags(NAMESPACE_STATUSNET,'notice_info'); if($apps && $apps[0]['attribs']['']['source']) { $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source'])); @@ -343,6 +345,8 @@ function get_atom_elements($feed, $item, &$author) { $have_real_body = false; $rawenv = $item->get_item_tags(NAMESPACE_DFRN, 'env'); + if(! $rawenv) + $rawenv = $item->get_item_tags(NAMESPACE_ZOT,'source'); if($rawenv) { $have_real_body = true; $res['body'] = $rawenv[0]['data']; @@ -388,7 +392,15 @@ function get_atom_elements($feed, $item, &$author) { $res['body'] = escape_tags($res['body']); } - if($res['plink'] && $res['title']) { + + // strip title and don't apply "title-in-body" if the feed involved + // uses the OStatus stack. We need a more generalised way for the calling + // function to specify this behaviour or for plugins to alter it. + + if($ostatus_protocol) { + $res['title'] = ''; + } + elseif($res['plink'] && $res['title']) { $res['body'] = '#^[url=' . $res['plink'] . ']' . $res['title'] . '[/url]' . "\n\n" . $res['body']; $terms = array(); $terms[] = array( @@ -623,14 +635,16 @@ function get_atom_elements($feed, $item, &$author) { $res['target'] = $obj; } - $arr = array('feed' => $feed, 'item' => $item, 'result' => $res); + + $arr = array('feed' => $feed, 'item' => $item, 'author' => $author, 'result' => $res); call_hooks('parse_atom', $arr); - logger('get_atom_elements: author: ' . print_r($author,true),LOGGER_DATA); - logger('get_atom_elements: ' . print_r($res,true),LOGGER_DATA); + logger('get_atom_elements: author: ' . print_r($arr['author'],true),LOGGER_DATA); + + logger('get_atom_elements: ' . print_r($arr['result'],true),LOGGER_DATA); - return $res; + return $arr['result']; } function encode_rel_links($links) { @@ -718,7 +732,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { foreach($del_entries as $dentry) { $deleted = false; if(isset($dentry['attribs']['']['ref'])) { - $mid = $dentry['attribs']['']['ref']; + $mid = normalise_id($dentry['attribs']['']['ref']); $deleted = true; if(isset($dentry['attribs']['']['when'])) { $when = $dentry['attribs']['']['when']; @@ -730,7 +744,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { if($deleted && is_array($contact)) { $r = q("SELECT * from item where mid = '%s' and author_xchan = '%s' and uid = %d limit 1", - dbesc(base64url_encode($mid)), + dbesc($mid), dbesc($contact['xchan_hash']), intval($importer['channel_id']) ); @@ -739,7 +753,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $item = $r[0]; if(! intval($item['item_deleted'])) { - logger('consume_feed: deleting item ' . $item['id'] . ' mid=' . base64url_decode($item['mid']), LOGGER_DEBUG); + logger('consume_feed: deleting item ' . $item['id'] . ' mid=' . $item['mid'], LOGGER_DEBUG); drop_item($item['id'],false); } } @@ -758,14 +772,14 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { foreach($items as $item) { $is_reply = false; - $item_id = base64url_encode($item->get_id()); + $item_id = normalise_id($item->get_id()); - logger('consume_feed: processing ' . $item_id, LOGGER_DEBUG); + logger('consume_feed: processing ' . $raw_item_id, LOGGER_DEBUG); $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); if(isset($rawthread[0]['attribs']['']['ref'])) { $is_reply = true; - $parent_mid = base64url_encode($rawthread[0]['attribs']['']['ref']); + $parent_mid = normalise_id($rawthread[0]['attribs']['']['ref']); } if($is_reply) { @@ -775,7 +789,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // Have we seen it? If not, import it. - $item_id = base64url_encode($item->get_id()); + $item_id = normalise_id($item->get_id()); $author = array(); $datarray = get_atom_elements($feed,$item,$author); @@ -824,7 +838,16 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { continue; } + $x = q("select mid from item where mid = '%s' and uid = %d limit 1", + dbesc($parent_mid), + intval($importer['channel_id']) + ); + if($x) + $parent_mid = $x[0]['mid']; + $datarray['parent_mid'] = $parent_mid; + + $datarray['aid'] = $importer['channel_account_id']; $datarray['uid'] = $importer['channel_id']; @@ -838,7 +861,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // Head post of a conversation. Have we seen it? If not, import it. - $item_id = base64url_encode($item->get_id()); + $item_id = normalise_id($item->get_id()); $author = array(); $datarray = get_atom_elements($feed,$item,$author); @@ -943,6 +966,11 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { } +function normalise_id($id) { + return str_replace('X-ZOT:','',$id); +} + + /** * @brief Process atom feed and return the first post and structure * @@ -983,14 +1011,14 @@ function process_salmon_feed($xml, $importer) { foreach($items as $item) { - $item_id = base64url_encode($item->get_id()); + $item_id = normalise_id($item->get_id()); logger('processing ' . $item_id, LOGGER_DEBUG); $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); if(isset($rawthread[0]['attribs']['']['ref'])) { $is_reply = true; - $parent_mid = base64url_encode($rawthread[0]['attribs']['']['ref']); + $parent_mid = normalise_id($rawthread[0]['attribs']['']['ref']); } if($is_reply) @@ -1159,7 +1187,8 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { if(($item['parent'] != $item['id']) || ($item['parent_mid'] !== $item['mid']) || (($item['thr_parent'] !== '') && ($item['thr_parent'] !== $item['mid']))) { $parent_item = (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']); - $o .= '<thr:in-reply-to ref="' . z_root() . '/display/' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; + $o .= '<thr:in-reply-to ref="' . 'X-ZOT:' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; + } if(activity_match($item['obj_type'],ACTIVITY_OBJ_EVENT) && activity_match($item['verb'],ACTIVITY_POST)) { @@ -1177,7 +1206,7 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { $o .= '<content type="' . $type . '" >' . xmlify(prepare_text($body,$item['mimetype'])) . '</content>' . "\r\n"; } - $o .= '<id>' . z_root() . '/display/' . xmlify($item['mid']) . '</id>' . "\r\n"; + $o .= '<id>' . 'X-ZOT:' . xmlify($item['mid']) . '</id>' . "\r\n"; $o .= '<published>' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '</published>' . "\r\n"; $o .= '<updated>' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '</updated>' . "\r\n"; @@ -1206,26 +1235,68 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { if(strlen($actarg)) $o .= $actarg; - // FIXME -// $tags = item_getfeedtags($item); -// if(count($tags)) { -// foreach($tags as $t) { -// $o .= '<category scheme="X-DFRN:' . xmlify($t[0]) . ':' . xmlify($t[1]) . '" term="' . xmlify($t[2]) . '" />' . "\r\n"; -// } -// } -// FIXME -// $o .= item_getfeedattach($item); + if($item['attach']) { + $enclosures = json_decode($item['attach'],true); + if($enclosures) { + foreach($enclosures as $enc) { + $o .= '<link rel="enclosure" ' + . (($enc['href']) ? 'href="' . $enc['href'] . '" ' : '') + . (($enc['length']) ? 'length="' . $enc['length'] . '" ' : '') + . (($enc['type']) ? 'type="' . $enc['type'] . '" ' : '') + . ' />'; + } + } + } -// $mentioned = get_mentions($item,$tags); -// if($mentioned) -// $o .= $mentioned; + if($item['term']) { + foreach($item['term'] as $term) { + $scheme = ''; + $label = ''; + switch($term['ttype']) { + case TERM_UNKNOWN: + $scheme = NAMESPACE_ZOT . '/term/unknown'; + $label = $term['term']; + break; + case TERM_HASHTAG: + case TERM_COMMUNITYTAG: + $scheme = NAMESPACE_ZOT . '/term/hashtag'; + $label = '#' . $term['term']; + break; + case TERM_MENTION: + $scheme = NAMESPACE_ZOT . '/term/mention'; + $label = '@' . $term['term']; + break; + case TERM_CATEGORY: + $scheme = NAMESPACE_ZOT . '/term/category'; + $label = $term['term']; + break; + default: + break; + } + if(! $scheme) + continue; - call_hooks('atom_entry', $o); + $o .= '<category scheme="' . $scheme . '" term="' . $term['term'] . '" label="' . $label . '" />' . "\r\n"; + } + } $o .= '</entry>' . "\r\n"; - return $o; + $x = [ + 'item' => $item, + 'type' => $type, + 'author' => $author, + 'owner' => $owner, + 'comment' => $comment, + 'abook_id' => $cid, + 'entry' => $o + ]; + + + call_hooks('atom_entry', $x); + + return $x['entry']; } diff --git a/include/follow.php b/include/follow.php index fa198e402..751d86db1 100644 --- a/include/follow.php +++ b/include/follow.php @@ -13,12 +13,11 @@ require_once('include/zot.php'); function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) { + $result = [ 'success' => false, 'message' => '' ]; - - $result = array('success' => false,'message' => ''); - - $is_red = false; - $is_http = ((strpos($url,'://') !== false) ? true : false); + $my_perms = false; + $is_zot = false; + $is_http = ((strpos($url,'://') !== false) ? true : false); if($is_http && substr($url,-1,1) === '/') $url = substr($url,0,-1); @@ -58,20 +57,14 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $ret = Zotlabs\Zot\Finger::run($url,$channel); if($ret && is_array($ret) && $ret['success']) { - $is_red = true; + $is_zot = true; $j = $ret; } - $my_perms = get_channel_default_perms($uid); + $p = \Zotlabs\Access\Permissions::connect_perms($uid); + $my_perms = $p['perms']; - $role = get_pconfig($uid,'system','permissions_role'); - if($role) { - $x = \Zotlabs\Access\PermissionRoles::role_perms($role); - if($x['perms_connect']) - $my_perms = $x['perms_connect']; - } - - if($is_red && $j) { + if($is_zot && $j) { logger('follow: ' . $url . ' ' . print_r($j,true), LOGGER_DEBUG); @@ -166,14 +159,13 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) } } - if(! $xchan_hash) { $result['message'] = t('Channel discovery failed.'); logger('follow: ' . $result['message']); return $result; } - $allowed = (($is_red || $r[0]['xchan_network'] === 'rss') ? 1 : 0); + $allowed = (($is_zot || $r[0]['xchan_network'] === 'rss') ? 1 : 0); $x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => $allowed, 'singleton' => 0); @@ -211,7 +203,8 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) return $result; } - $r = q("select abook_id, abook_xchan, abook_pending, abook_instance from abook where abook_xchan = '%s' and abook_channel = %d limit 1", + $r = q("select abook_id, abook_xchan, abook_pending, abook_instance from abook + where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid) ); @@ -226,6 +219,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) } if($r) { + $abook_instance = $r[0]['abook_instance']; if(($singleton) && strpos($abook_instance,z_root()) === false) { @@ -240,21 +234,6 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) ); if(intval($r[0]['abook_pending'])) { - - $abook_my_perms = get_channel_default_perms($uid); - $role = get_pconfig($uid,'system','permissions_role'); - if($role) { - $x = \Zotlabs\Access\PermissionRoles::role_perms($role); - if($x['perms_connect']) { - $abook_my_perms = $x['perms_connect']; - } - } - - $filled_perms = \Zotlabs\Access\Permissions::FilledPerms($abook_my_perms); - foreach($filled_perms as $k => $v) { - set_abconfig($uid,$r[0]['abook_xchan'],'my_perms',$k,$v); - } - $x = q("update abook set abook_pending = 0 where abook_id = %d", intval($r[0]['abook_id']) ); @@ -265,29 +244,26 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) if($closeness === false) $closeness = 80; - $r = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_feed, abook_created, abook_updated, abook_instance ) - values( %d, %d, %d, '%s', %d, '%s', '%s', '%s' ) ", - intval($aid), - intval($uid), - intval($closeness), - dbesc($xchan_hash), - intval(($is_http) ? 1 : 0), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(($singleton) ? z_root() : '') + $r = abook_store_lowlevel( + [ + 'abook_account' => intval($aid), + 'abook_channel' => intval($uid), + 'abook_closeness' => intval($closeness), + 'abook_xchan' => $xchan_hash, + 'abook_feed' => intval(($is_http) ? 1 : 0), + 'abook_created' => datetime_convert(), + 'abook_updated' => datetime_convert(), + 'abook_instance' => (($singleton) ? z_root() : '') + ] ); } if(! $r) logger('mod_follow: abook creation failed'); - $all_perms = \Zotlabs\Access\Permissions::Perms(); - if($all_perms) { - foreach($all_perms as $k => $v) { - if(in_array($k,$my_perms)) - set_abconfig($uid,$xchan_hash,'my_perms',$k,1); - else - set_abconfig($uid,$xchan_hash,'my_perms',$k,0); + if($my_perms) { + foreach($my_perms as $k => $v) { + set_abconfig($uid,$xchan_hash,'my_perms',$k,$v); } } diff --git a/include/group.php b/include/group.php index 38d9d190f..3b208ef95 100644 --- a/include/group.php +++ b/include/group.php @@ -227,6 +227,26 @@ function group_get_members_xchan($gid) { return $ret; } +function group_get_profile_members_xchan($uid,$gid) { + $ret = []; + + if(intval($gid)) { + $r = q("SELECT abook_xchan as xchan from abook left join profile on abook_profile = profile_guid where profile.id = %d and profile.uid = %d", + intval($gid), + intval($uid) + ); + if($r) { + foreach($r as $rr) { + $ret[] = $rr['xchan']; + } + } + } + return $ret; +} + + + + function mini_group_select($uid,$group = '') { $grps = array(); @@ -320,20 +340,46 @@ function group_side($every="connections",$each="group",$edit = false, $group_id return $o; } -function expand_groups($a) { - if(! (is_array($a) && count($a))) +function expand_groups($g) { + if(! (is_array($g) && count($g))) return array(); - $x = $a; - stringify_array_elms($x,true); - $groups = implode(',', $x); - if($groups) - $r = q("SELECT xchan FROM group_member WHERE gid IN ( select id from groups where hash in ( $groups ))"); - $ret = array(); + $ret = []; + $x = []; - if($r) - foreach($r as $rr) - $ret[] = $rr['xchan']; + // private profile linked virtual groups + + foreach($g as $gv) { + if(substr($gv,0,3) === 'vp.') { + $profile_hash = substr($gv,3); + if($profile_hash) { + $r = q("select abook_xchan from abook where abook_profile = '%s'", + dbesc($profile_hash) + ); + if($r) { + foreach($r as $rv) { + $ret[] = $rv['abook_xchan']; + } + } + } + } + else { + $x[] = $gv; + } + } + + if($x) { + stringify_array_elms($x,true); + $groups = implode(',', $x); + if($groups) { + $r = q("SELECT xchan FROM group_member WHERE gid IN ( select id from groups where hash in ( $groups ))"); + if($r) { + foreach($r as $rr) { + $ret[] = $rr['xchan']; + } + } + } + } return $ret; } diff --git a/include/help.php b/include/help.php index 03d01d1a1..6e779f000 100644 --- a/include/help.php +++ b/include/help.php @@ -79,7 +79,7 @@ function get_help_content($tocpath = false) { } if($doctype === 'bbcode') { require_once('include/bbcode.php'); - $content = bbcode($text); + $content = zidify_links(bbcode($text)); // bbcode retargets external content to new windows. This content is internal. $content = str_replace(' target="_blank"', '', $content); } @@ -95,7 +95,7 @@ function preg_callback_help_include($matches) { $include = str_replace($matches[0],load_doc_file($matches[1]),$matches[0]); if(preg_match('/\.bb$/', $matches[1]) || preg_match('/\.txt$/', $matches[1])) { require_once('include/bbcode.php'); - $include = bbcode($include); + $include = zidify_links(bbcode($include)); $include = str_replace(' target="_blank"','',$include); } elseif(preg_match('/\.md$/', $matches[1])) { diff --git a/include/html2plain.php b/include/html2plain.php index 2f5be7f69..979354079 100644 --- a/include/html2plain.php +++ b/include/html2plain.php @@ -113,7 +113,7 @@ function html2plain($html, $wraplength = 75, $compact = false) $xpath = new DomXPath($doc); $list = $xpath->query("//pre"); foreach ($list as $node) { - $node->nodeValue = str_replace("\n", "\r", $node->nodeValue); + $node->nodeValue = str_replace("\n", "\r", htmlspecialchars($node->nodeValue)); } $message = $doc->saveHTML(); diff --git a/include/hubloc.php b/include/hubloc.php index 17f921f67..e17be028c 100644 --- a/include/hubloc.php +++ b/include/hubloc.php @@ -1,5 +1,36 @@ <?php /** @file */ + +function hubloc_store_lowlevel($arr) { + + $store = [ + 'hubloc_guid' => ((array_key_exists('hubloc_guid',$arr)) ? $arr['hubloc_guid'] : ''), + 'hubloc_guid_sig' => ((array_key_exists('hubloc_guid_sig',$arr)) ? $arr['hubloc_guid_sig'] : ''), + 'hubloc_hash' => ((array_key_exists('hubloc_hash',$arr)) ? $arr['hubloc_hash'] : ''), + 'hubloc_addr' => ((array_key_exists('hubloc_addr',$arr)) ? $arr['hubloc_addr'] : ''), + 'hubloc_network' => ((array_key_exists('hubloc_network',$arr)) ? $arr['hubloc_network'] : ''), + 'hubloc_flags' => ((array_key_exists('hubloc_flags',$arr)) ? $arr['hubloc_flags'] : 0), + 'hubloc_status' => ((array_key_exists('hubloc_status',$arr)) ? $arr['hubloc_status'] : 0), + 'hubloc_url' => ((array_key_exists('hubloc_url',$arr)) ? $arr['hubloc_url'] : ''), + 'hubloc_url_sig' => ((array_key_exists('hubloc_url_sig',$arr)) ? $arr['hubloc_url_sig'] : ''), + 'hubloc_host' => ((array_key_exists('hubloc_host',$arr)) ? $arr['hubloc_host'] : ''), + 'hubloc_callback' => ((array_key_exists('hubloc_callback',$arr)) ? $arr['hubloc_callback'] : ''), + 'hubloc_connect' => ((array_key_exists('hubloc_connect',$arr)) ? $arr['hubloc_connect'] : ''), + 'hubloc_sitekey' => ((array_key_exists('hubloc_sitekey',$arr)) ? $arr['hubloc_sitekey'] : ''), + 'hubloc_updated' => ((array_key_exists('hubloc_updated',$arr)) ? $arr['hubloc_updated'] : NULL_DATE), + 'hubloc_connected' => ((array_key_exists('hubloc_connected',$arr)) ? $arr['hubloc_connected'] : NULL_DATE), + 'hubloc_primary' => ((array_key_exists('hubloc_primary',$arr)) ? $arr['hubloc_primary'] : 0), + 'hubloc_orphancheck' => ((array_key_exists('hubloc_orphancheck',$arr)) ? $arr['hubloc_orphancheck'] : 0), + 'hubloc_error' => ((array_key_exists('hubloc_error',$arr)) ? $arr['hubloc_error'] : 0), + 'hubloc_deleted' => ((array_key_exists('hubloc_deleted',$arr)) ? $arr['hubloc_deleted'] : 0) + ]; + + return create_table_from_array('hubloc',$store); + +} + + + function prune_hub_reinstalls() { $r = q("select site_url from site where site_type = %d", @@ -176,102 +207,6 @@ function hubloc_mark_as_down($posturl) { } -function xchan_store($arr) { - - logger('xchan_store: ' . print_r($arr,true)); - - if(! $arr['hash']) - $arr['hash'] = $arr['guid']; - if(! $arr['hash']) - return false; - - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($arr['hash']) - ); - if($r) - return true; - - if(! $arr['network']) - $arr['network'] = 'unknown'; - if(! $arr['name']) - $arr['name'] = 'unknown'; - if(! $arr['url']) - $arr['url'] = z_root(); - if(! $arr['photo']) - $arr['photo'] = z_root() . '/' . get_default_profile_photo(); - - $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_addr, xchan_url, xchan_connurl, xchan_follow, xchan_connpage, xchan_name, xchan_network, xchan_instance_url, xchan_hidden, xchan_orphan, xchan_censored, xchan_selfcensored, xchan_system, xchan_pubforum, xchan_deleted, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s','%s','%s','%s',%d, %d, %d, %d, %d, %d, %d, '%s') ", - dbesc($arr['hash']), - dbesc($arr['guid']), - dbesc($arr['guid_sig']), - dbesc($arr['pubkey']), - dbesc($arr['address']), - dbesc($arr['url']), - dbesc($arr['connurl']), - dbesc($arr['follow']), - dbesc($arr['connpage']), - dbesc($arr['name']), - dbesc($arr['network']), - dbesc($arr['instance_url']), - intval($arr['hidden']), - intval($arr['orphan']), - intval($arr['censored']), - intval($arr['selfcensored']), - intval($arr['system']), - intval($arr['pubforum']), - intval($arr['deleted']), - dbesc(datetime_convert()) - ); - if(! $r) - return $r; - - $photos = import_xchan_photo($arr['photo'],$arr['hash']); - $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", - dbesc(datetime_convert()), - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($arr['hash']) - ); - return $r; - -} - - -function xchan_fetch($arr) { - - $key = ''; - if($arr['hash']) { - $key = 'xchan_hash'; - $v = $arr['hash']; - } - elseif($arr['guid']) { - $key = 'xchan_guid'; - $v = $arr['guid']; - } - elseif($arr['address']) { - $key = 'xchan_addr'; - $v = $arr['address']; - } - - if(! $key) - return false; - - $r = q("select * from xchan where $key = '$v' limit 1"); - if(! $r) - return false; - - $ret = array(); - foreach($r[0] as $k => $v) { - if($k === 'xchan_addr') - $ret['address'] = $v; - else - $ret[str_replace('xchan_','',$k)] = $v; - } - return $ret; -} - function ping_site($url) { diff --git a/include/import.php b/include/import.php index 61134000a..e1880fe6e 100644 --- a/include/import.php +++ b/include/import.php @@ -156,7 +156,7 @@ function import_profiles($channel,$profiles) { } -function import_hublocs($channel,$hublocs,$seize) { +function import_hublocs($channel,$hublocs,$seize,$moving = false) { if($channel && $hublocs) { foreach($hublocs as $hubloc) { @@ -174,19 +174,32 @@ function import_hublocs($channel,$hublocs,$seize) { $hubloc['hubloc_deleted'] = (($hubloc['hubloc_flags'] & 0x1000) ? 1 : 0); } + if($moving && $hubloc['hubloc_hash'] === $channel['channel_hash'] && $hubloc['hubloc_url'] !== z_root()) { + $hubloc['hubloc_deleted'] = 1; + } + $arr = array( 'guid' => $hubloc['hubloc_guid'], 'guid_sig' => $hubloc['hubloc_guid_sig'], 'url' => $hubloc['hubloc_url'], - 'url_sig' => $hubloc['hubloc_url_sig'] + 'url_sig' => $hubloc['hubloc_url_sig'], + 'sitekey' => ((array_key_exists('hubloc_sitekey',$hubloc)) ? $hubloc['hubloc_sitekey'] : '') ); if(($hubloc['hubloc_hash'] === $channel['channel_hash']) && intval($hubloc['hubloc_primary']) && ($seize)) $hubloc['hubloc_primary'] = 0; - if(! zot_gethub($arr)) { + if(($x = zot_gethub($arr,false)) === false) { unset($hubloc['hubloc_id']); create_table_from_array('hubloc',$hubloc); } + else { + q("UPDATE hubloc set hubloc_primary = %d, hubloc_deleted = %d where hubloc_id = %d", + intval($hubloc['hubloc_primary']), + intval($hubloc['hubloc_deleted']), + intval($x['hubloc_id']) + ); + + } } } } @@ -1175,413 +1188,424 @@ function convert_oldfields(&$arr,$old,$new) { } function scan_webpage_elements($path, $type, $cloud = false) { - $channel = \App::get_channel(); - $dirtoscan = $path; - switch ($type) { - case 'page': - $dirtoscan .= '/pages/'; - $json_filename = 'page.json'; - break; - case 'layout': - $dirtoscan .= '/layouts/'; - $json_filename = 'layout.json'; - break; - case 'block': - $dirtoscan .= '/blocks/'; - $json_filename = 'block.json'; - break; - default : - return array(); - } - if($cloud) { - $dirtoscan = get_dirpath_by_cloudpath($channel, $dirtoscan); - } - $elements = []; - if (is_dir($dirtoscan)) { - $dirlist = scandir($dirtoscan); - if ($dirlist) { - foreach ($dirlist as $element) { - if ($element === '.' || $element === '..') { - continue; + $channel = \App::get_channel(); + $dirtoscan = $path; + switch ($type) { + case 'page': + $dirtoscan .= '/pages/'; + $json_filename = 'page.json'; + break; + case 'layout': + $dirtoscan .= '/layouts/'; + $json_filename = 'layout.json'; + break; + case 'block': + $dirtoscan .= '/blocks/'; + $json_filename = 'block.json'; + break; + default : + return array(); + } + if($cloud) { + $dirtoscan = get_dirpath_by_cloudpath($channel, $dirtoscan); + } + $elements = []; + if(is_dir($dirtoscan)) { + $dirlist = scandir($dirtoscan); + if($dirlist) { + foreach($dirlist as $element) { + if($element === '.' || $element === '..') { + continue; + } + $folder = $dirtoscan . '/' . $element; + if(is_dir($folder)) { + if($cloud) { + $jsonfilepath = $folder . '/' . get_filename_by_cloudname($json_filename, $channel, $folder); } - $folder = $dirtoscan . '/' . $element; - if (is_dir($folder)) { + else { + $jsonfilepath = $folder . '/' . $json_filename; + } + if(is_file($jsonfilepath)) { + $metadata = json_decode(file_get_contents($jsonfilepath), true); if($cloud) { - $jsonfilepath = $folder . '/' . get_filename_by_cloudname($json_filename, $channel, $folder); - } else { - $jsonfilepath = $folder . '/' . $json_filename; + $contentfilename = get_filename_by_cloudname($metadata['contentfile'], $channel, $folder); + $metadata['path'] = $folder . '/' . $contentfilename; + } + else { + $contentfilename = $metadata['contentfile']; + $metadata['path'] = $folder . '/' . $contentfilename; + } + if($metadata['contentfile'] === '') { + logger('Invalid ' . $type . ' content file'); + return false; } - if (is_file($jsonfilepath)) { - $metadata = json_decode(file_get_contents($jsonfilepath), true); - if($cloud) { - $contentfilename = get_filename_by_cloudname($metadata['contentfile'], $channel, $folder); - $metadata['path'] = $folder . '/' . $contentfilename; - } else { - $contentfilename = $metadata['contentfile']; - $metadata['path'] = $folder . '/' . $contentfilename; + $content = file_get_contents($folder . '/' . $contentfilename); + if(!$content) { + if(is_readable($folder . '/' . $contentfilename)) { + $content = ''; } - if ($metadata['contentfile'] === '') { - logger('Invalid ' . $type . ' content file'); + else { + logger('Failed to get file content for ' . $metadata['contentfile']); return false; } - $content = file_get_contents($folder . '/' . $contentfilename); - if (!$content) { - if(is_readable($folder . '/' . $contentfilename)) { - $content = ''; - } else { - logger('Failed to get file content for ' . $metadata['contentfile']); - return false; - } - } - $elements[] = $metadata; } + $elements[] = $metadata; } } } } - return $elements; } + return $elements; +} - function import_webpage_element($element, $channel, $type) { +function import_webpage_element($element, $channel, $type) { - $arr = array(); // construct information for the webpage element item table record + $arr = array(); // construct information for the webpage element item table record - switch ($type) { - // - // PAGES - // - case 'page': - $arr['item_type'] = ITEM_TYPE_WEBPAGE; - $namespace = 'WEBPAGE'; - $name = $element['pagelink']; - if($name) { - require_once('library/urlify/URLify.php'); - $name = strtolower(\URLify::transliterate($name)); - } - $arr['title'] = $element['title']; - $arr['term'] = $element['term']; - $arr['layout_mid'] = ''; // by default there is no layout associated with the page - // If a layout was specified, find it in the database and get its info. If - // it does not exist, leave layout_mid empty - if($element['layout'] !== '') { - $liid = q("select iid from iconfig where k = 'PDL' and v = '%s' and cat = 'system'", - dbesc($element['layout']) - ); - if($liid) { - $linfo = q("select mid from item where id = %d", - intval($liid[0]['iid']) - ); - $arr['layout_mid'] = $linfo[0]['mid']; - } - } - break; - // - // LAYOUTS - // - case 'layout': - $arr['item_type'] = ITEM_TYPE_PDL; - $namespace = 'PDL'; - $name = $element['name']; - $arr['title'] = $element['description']; - $arr['term'] = $element['term']; - break; - // - // BLOCKS - // - case 'block': - $arr['item_type'] = ITEM_TYPE_BLOCK; - $namespace = 'BUILDBLOCK'; - $name = $element['name']; - $arr['title'] = $element['title']; + switch($type) { + // + // PAGES + // + case 'page': + $arr['item_type'] = ITEM_TYPE_WEBPAGE; + $namespace = 'WEBPAGE'; + $name = $element['pagelink']; + if($name) { + require_once('library/urlify/URLify.php'); + $name = strtolower(\URLify::transliterate($name)); + } + $arr['title'] = $element['title']; + $arr['term'] = $element['term']; + $arr['layout_mid'] = ''; // by default there is no layout associated with the page + // If a layout was specified, find it in the database and get its info. If + // it does not exist, leave layout_mid empty + if($element['layout'] !== '') { + $liid = q("select iid from iconfig where k = 'PDL' and v = '%s' and cat = 'system'", + dbesc($element['layout']) + ); + if($liid) { + $linfo = q("select mid from item where id = %d", + intval($liid[0]['iid']) + ); + $arr['layout_mid'] = $linfo[0]['mid']; + } + } + break; + // + // LAYOUTS + // + case 'layout': + $arr['item_type'] = ITEM_TYPE_PDL; + $namespace = 'PDL'; + $name = $element['name']; + $arr['title'] = $element['description']; + $arr['term'] = $element['term']; + break; + // + // BLOCKS + // + case 'block': + $arr['item_type'] = ITEM_TYPE_BLOCK; + $namespace = 'BUILDBLOCK'; + $name = $element['name']; + $arr['title'] = $element['title']; - break; - default : - return null; // return null if invalid element type - } - - $arr['uid'] = $channel['channel_id']; - $arr['aid'] = $channel['channel_account_id']; + break; + default : + return null; // return null if invalid element type + } - // Check if an item already exists based on the name - $iid = q("select iid from iconfig where k = '" . $namespace . "' and v = '%s' and cat = 'system'", - dbesc($name) - ); - if($iid) { // If the item does exist, get the item metadata - $iteminfo = q("select mid,created,edited from item where id = %d", - intval($iid[0]['iid']) - ); - $arr['mid'] = $arr['parent_mid'] = $iteminfo[0]['mid']; - $arr['created'] = $iteminfo[0]['created']; - } else { // otherwise, generate the creation times and unique id - $arr['created'] = datetime_convert('UTC', 'UTC'); - $arr['mid'] = $arr['parent_mid'] = item_message_id(); - } - // Update the edited time whether or not the element already exists - $arr['edited'] = datetime_convert('UTC', 'UTC'); - // Import the actual element content - $arr['body'] = file_get_contents($element['path']); - // The element owner is the channel importing the elements - $arr['owner_xchan'] = get_observer_hash(); - // The author is either the owner or whomever was specified - $arr['author_xchan'] = (($element['author_xchan']) ? $element['author_xchan'] : get_observer_hash()); - // Import mimetype if it is a valid mimetype for the element - $mimetypes = [ 'text/bbcode', - 'text/html', - 'text/markdown', - 'text/plain', - 'application/x-pdl', - 'application/x-php' - ]; - // Blocks and pages can have any of the valid mimetypes, but layouts must be text/bbcode - if((in_array($element['mimetype'], $mimetypes)) && ($type === 'page' || $type === 'block') ) { - $arr['mimetype'] = $element['mimetype']; - } else { - $arr['mimetype'] = 'text/bbcode'; - } - - // Verify ability to use html or php!!! - $execflag = false; - if ($arr['mimetype'] === 'application/x-php' || $arr['mimetype'] === 'text/html') { - $z = q("select account_id, account_roles, channel_pageflags from account " - . "left join channel on channel_account_id = account_id where channel_id = %d limit 1", - intval(local_channel()) - ); - - if ($z && (($z[0]['account_roles'] & ACCOUNT_ROLE_ALLOWCODE) || ($z[0]['channel_pageflags'] & PAGE_ALLOWCODE))) { - $execflag = true; - } else { - logger('Unable to import element "' . $name .'" because AllowCode permission is denied.'); - notice( t('Unable to import element "' . $name .'" because AllowCode permission is denied.') . EOL); - $element['import_success'] = 0; - return $element; - } - } + $arr['uid'] = local_channel(); + $arr['aid'] = $channel['channel_account_id']; - $z = q("select * from iconfig where v = '%s' and k = '%s' and cat = 'system' limit 1", - dbesc($name), - dbesc($namespace) + // Check if an item already exists based on the name + $iid = q("select iid from iconfig where k = '" . $namespace . "' and v = '%s' and cat = 'system'", + dbesc($name) + ); + if($iid) { // If the item does exist, get the item metadata + $iteminfo = q("select mid,created,edited from item where id = %d", + intval($iid[0]['iid']) ); + $arr['mid'] = $arr['parent_mid'] = $iteminfo[0]['mid']; + $arr['created'] = $iteminfo[0]['created']; + } + else { // otherwise, generate the creation times and unique id + $arr['created'] = datetime_convert('UTC', 'UTC'); + $arr['mid'] = $arr['parent_mid'] = item_message_id(); + } + // Update the edited time whether or not the element already exists + $arr['edited'] = datetime_convert('UTC', 'UTC'); + // Import the actual element content + $arr['body'] = file_get_contents($element['path']); + // The element owner is the channel importing the elements + $arr['owner_xchan'] = get_observer_hash(); + // The author is either the owner or whomever was specified + $arr['author_xchan'] = (($element['author_xchan']) ? $element['author_xchan'] : get_observer_hash()); + // Import mimetype if it is a valid mimetype for the element + $mimetypes = [ 'text/bbcode', + 'text/html', + 'text/markdown', + 'text/plain', + 'application/x-pdl', + 'application/x-php' + ]; + // Blocks and pages can have any of the valid mimetypes, but layouts must be text/bbcode + if((in_array($element['mimetype'], $mimetypes)) && ($type === 'page' || $type === 'block') ) { + $arr['mimetype'] = $element['mimetype']; + } + else { + $arr['mimetype'] = 'text/bbcode'; + } - $i = q("select id, edited, item_deleted from item where mid = '%s' and uid = %d limit 1", - dbesc($arr['mid']), + // Verify ability to use html or php!!! + $execflag = false; + if($arr['mimetype'] === 'application/x-php' || $arr['mimetype'] === 'text/html') { + $z = q("select account_id, account_roles, channel_pageflags from account " + . "left join channel on channel_account_id = account_id where channel_id = %d limit 1", intval(local_channel()) ); - $remote_id = 0; - if ($z && $i) { - $remote_id = $z[0]['id']; - $arr['id'] = $i[0]['id']; - // don't update if it has the same timestamp as the original - if ($arr['edited'] > $i[0]['edited']) - $x = item_store_update($arr, $execflag); - } else { - if (($i) && (intval($i[0]['item_deleted']))) { - // was partially deleted already, finish it off - q("delete from item where mid = '%s' and uid = %d", - dbesc($arr['mid']), - intval(local_channel()) - ); - } - $x = item_store($arr, $execflag); + + if($z && (($z[0]['account_roles'] & ACCOUNT_ROLE_ALLOWCODE) || ($z[0]['channel_pageflags'] & PAGE_ALLOWCODE))) { + $execflag = true; } - if ($x['success']) { - $item_id = $x['item_id']; - update_remote_id($channel, $item_id, $arr['item_type'], $name, $namespace, $remote_id, $arr['mid']); - $element['import_success'] = 1; - } else { - $element['import_success'] = 0; + else { + logger('Unable to import element "' . $name .'" because AllowCode permission is denied.'); + notice( t('Unable to import element "' . $name .'" because AllowCode permission is denied.') . EOL); + $element['import_success'] = 0; + return $element; + } + } + +// $z = q("select * from iconfig where v = '%s' and k = '%s' and cat = 'system' limit 1", +// dbesc($name), +// dbesc($namespace) +// ); + + $i = q("select id, edited, item_deleted from item where mid = '%s' and uid = %d limit 1", + dbesc($arr['mid']), + intval(local_channel()) + ); + + \Zotlabs\Lib\IConfig::Set($arr,'system',$namespace,(($name) ? $name : substr($arr['mid'],0,16)),true); + + + if($i) { + $arr['id'] = $i[0]['id']; + // don't update if it has the same timestamp as the original + if($arr['edited'] > $i[0]['edited']) + $x = item_store_update($arr,$execflag); + } + else { + if(($i) && (intval($i[0]['item_deleted']))) { + // was partially deleted already, finish it off + q("delete from item where mid = '%s' and uid = %d", + dbesc($arr['mid']), + intval(local_channel()) + ); } + else + $x = item_store($arr,$execflag); + } + + if($x && $x['success']) { + $item_id = $x['item_id']; + //update_remote_id($channel, $item_id, $arr['item_type'], $name, $namespace, $remote_id, $arr['mid']); + $element['import_success'] = 1; + } + else { + $element['import_success'] = 0; + } - return $element; - + return $element; } function get_webpage_elements($channel, $type = 'all') { - $elements = array(); - if(!$channel['channel_id']) { - return null; - } - switch ($type) { - case 'all': - // If all, execute all the pages, layouts, blocks case statements - case 'pages': - $elements['pages'] = null; - $owner = $channel['channel_id']; + $elements = array(); + if(!$channel['channel_id']) { + return null; + } + switch($type) { + case 'all': + // If all, execute all the pages, layouts, blocks case statements + case 'pages': + $elements['pages'] = null; + $owner = $channel['channel_id']; - $sql_extra = item_permissions_sql($owner); + $sql_extra = item_permissions_sql($owner); - $r = q("select * from iconfig left join item on iconfig.iid = item.id - where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item_type = %d - $sql_extra order by item.created desc", - intval($owner), - intval(ITEM_TYPE_WEBPAGE) - ); + $r = q("select * from iconfig left join item on iconfig.iid = item.id + where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item_type = %d + $sql_extra order by item.created desc", + intval($owner), + intval(ITEM_TYPE_WEBPAGE) + ); - $pages = null; + $pages = null; - if($r) { - $elements['pages'] = array(); - $pages = array(); - foreach($r as $rr) { - unobscure($rr); - - //$lockstate = (($rr['allow_cid'] || $rr['allow_gid'] || $rr['deny_cid'] || $rr['deny_gid']) ? 'lock' : 'unlock'); - - $element_arr = array( - 'type' => 'webpage', - 'title' => $rr['title'], - 'body' => $rr['body'], - 'created' => $rr['created'], - 'edited' => $rr['edited'], - 'mimetype' => $rr['mimetype'], - 'pagetitle' => $rr['v'], - 'mid' => $rr['mid'], - 'layout_mid' => $rr['layout_mid'] - ); - $pages[$rr['iid']][] = array( - 'url' => $rr['iid'], - 'pagetitle' => $rr['v'], - 'title' => $rr['title'], - 'created' => datetime_convert('UTC',date_default_timezone_get(),$rr['created']), - 'edited' => datetime_convert('UTC',date_default_timezone_get(),$rr['edited']), - 'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]', - //'lockstate' => $lockstate - ); - $elements['pages'][] = $element_arr; - } + if($r) { + $elements['pages'] = array(); + $pages = array(); + foreach($r as $rr) { + unobscure($rr); + + //$lockstate = (($rr['allow_cid'] || $rr['allow_gid'] || $rr['deny_cid'] || $rr['deny_gid']) ? 'lock' : 'unlock'); + + $element_arr = array( + 'type' => 'webpage', + 'title' => $rr['title'], + 'body' => $rr['body'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'mimetype' => $rr['mimetype'], + 'pagetitle' => $rr['v'], + 'mid' => $rr['mid'], + 'layout_mid' => $rr['layout_mid'] + ); + $pages[$rr['iid']][] = array( + 'url' => $rr['iid'], + 'pagetitle' => $rr['v'], + 'title' => $rr['title'], + 'created' => datetime_convert('UTC',date_default_timezone_get(),$rr['created']), + 'edited' => datetime_convert('UTC',date_default_timezone_get(),$rr['edited']), + 'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]', + //'lockstate' => $lockstate + ); + $elements['pages'][] = $element_arr; + } - } - if($type !== 'all') { - break; - } + } + if($type !== 'all') { + break; + } - case 'layouts': - $elements['layouts'] = null; - $owner = $channel['channel_id']; + case 'layouts': + $elements['layouts'] = null; + $owner = $channel['channel_id']; - $sql_extra = item_permissions_sql($owner); + $sql_extra = item_permissions_sql($owner); - $r = q("select * from iconfig left join item on iconfig.iid = item.id - where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' and item_type = %d - $sql_extra order by item.created desc", - intval($owner), - intval(ITEM_TYPE_PDL) - ); + $r = q("select * from iconfig left join item on iconfig.iid = item.id + where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' and item_type = %d + $sql_extra order by item.created desc", + intval($owner), + intval(ITEM_TYPE_PDL) + ); - $layouts = null; + $layouts = null; - if($r) { - $elements['layouts'] = array(); - $layouts = array(); - foreach($r as $rr) { - unobscure($rr); - - $elements['layouts'][] = array( - 'type' => 'layout', - 'description' => $rr['title'], // description of the layout - 'body' => $rr['body'], - 'created' => $rr['created'], - 'edited' => $rr['edited'], - 'mimetype' => $rr['mimetype'], - 'name' => $rr['v'], // name of reference for the layout - 'mid' => $rr['mid'], - ); - } - - } + if($r) { + $elements['layouts'] = array(); + $layouts = array(); + foreach($r as $rr) { + unobscure($rr); + + $elements['layouts'][] = array( + 'type' => 'layout', + 'description' => $rr['title'], // description of the layout + 'body' => $rr['body'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'mimetype' => $rr['mimetype'], + 'name' => $rr['v'], // name of reference for the layout + 'mid' => $rr['mid'], + ); + } + } - if($type !== 'all') { - break; - } + if($type !== 'all') { + break; + } - case 'blocks': - $elements['blocks'] = null; - $owner = $channel['channel_id']; + case 'blocks': + $elements['blocks'] = null; + $owner = $channel['channel_id']; - $sql_extra = item_permissions_sql($owner); + $sql_extra = item_permissions_sql($owner); - $r = q("select iconfig.iid, iconfig.k, iconfig.v, mid, title, body, mimetype, created, edited from iconfig - left join item on iconfig.iid = item.id - where uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK' - and item_type = %d order by item.created desc", - intval($owner), - intval(ITEM_TYPE_BLOCK) - ); + $r = q("select iconfig.iid, iconfig.k, iconfig.v, mid, title, body, mimetype, created, edited from iconfig + left join item on iconfig.iid = item.id + where uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK' + and item_type = %d order by item.created desc", + intval($owner), + intval(ITEM_TYPE_BLOCK) + ); - $blocks = null; + $blocks = null; - if($r) { - $elements['blocks'] = array(); - $blocks = array(); - foreach($r as $rr) { - unobscure($rr); - - $elements['blocks'][] = array( - 'type' => 'block', - 'title' => $rr['title'], - 'body' => $rr['body'], - 'created' => $rr['created'], - 'edited' => $rr['edited'], - 'mimetype' => $rr['mimetype'], - 'name' => $rr['v'], - 'mid' => $rr['mid'] - ); - } + if($r) { + $elements['blocks'] = array(); + $blocks = array(); + foreach($r as $rr) { + unobscure($rr); + + $elements['blocks'][] = array( + 'type' => 'block', + 'title' => $rr['title'], + 'body' => $rr['body'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'mimetype' => $rr['mimetype'], + 'name' => $rr['v'], + 'mid' => $rr['mid'] + ); + } - } + } - if($type !== 'all') { - break; - } + if($type !== 'all') { + break; + } - default: - break; - } - return $elements; + default: + break; + } + return $elements; } /* creates a compressed zip file */ function create_zip_file($files = array(), $destination = '', $overwrite = false) { - //if the zip file already exists and overwrite is false, return false - if (file_exists($destination) && !$overwrite) { - return false; + // if the zip file already exists and overwrite is false, return false + if(file_exists($destination) && !$overwrite) { + return false; + } + //vars + $valid_files = array(); + // if files were passed in... + if(is_array($files)) { + // cycle through each file + foreach($files as $file) { + // make sure the file exists + if(file_exists($file)) { + $valid_files[] = $file; + } } - //vars - $valid_files = array(); - //if files were passed in... - if (is_array($files)) { - //cycle through each file - foreach ($files as $file) { - //make sure the file exists - if (file_exists($file)) { - $valid_files[] = $file; - } - } - } - - //if we have good files... - if (count($valid_files)) { - //create the archive - $zip = new ZipArchive(); - if ($zip->open($destination, $overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) { - return false; - } - //add the files - foreach ($valid_files as $file) { - $zip->addFile($file, $file); - } - //debug - //echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status; - //close the zip -- done! - $zip->close(); - - //check to make sure the file exists - return file_exists($destination); - } else { - return false; + } + + // if we have good files... + if(count($valid_files)) { + //create the archive + $zip = new ZipArchive(); + if($zip->open($destination, $overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) { + return false; } + // add the files + foreach($valid_files as $file) { + $zip->addFile($file, $file); + } + //debug + //echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status; + //close the zip -- done! + $zip->close(); + + // check to make sure the file exists + return file_exists($destination); + } + else { + return false; + } } diff --git a/include/items.php b/include/items.php index 82cde9911..36445a46b 100755 --- a/include/items.php +++ b/include/items.php @@ -302,7 +302,7 @@ function add_source_route($iid, $hash) { * * \e boolean \b success true or false * * \e array \b activity the resulting activity if successful */ -function post_activity_item($arr) { +function post_activity_item($arr,$allow_code = false,$deliver = true) { $ret = array('success' => false); @@ -328,7 +328,8 @@ function post_activity_item($arr) { return $ret; } - $arr['public_policy'] = ((x($_REQUEST,'public_policy')) ? escape_tags($_REQUEST['public_policy']) : map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_stream'),true)); + $arr['public_policy'] = ((array_key_exists('public_policy',$arr)) ? escape_tags($arr['public_policy']) : map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_stream'),true)); + if($arr['public_policy']) $arr['item_private'] = 1; @@ -359,15 +360,18 @@ function post_activity_item($arr) { if(($is_comment) && ($arr['obj_type'] === ACTIVITY_OBJ_NOTE)) $arr['obj_type'] = ACTIVITY_OBJ_COMMENT; - $arr['allow_cid'] = ((x($arr,'allow_cid')) ? $arr['allow_cid'] : $channel['channel_allow_cid']); - $arr['allow_gid'] = ((x($arr,'allow_gid')) ? $arr['allow_gid'] : $channel['channel_allow_gid']); - $arr['deny_cid'] = ((x($arr,'deny_cid')) ? $arr['deny_cid'] : $channel['channel_deny_cid']); - $arr['deny_gid'] = ((x($arr,'deny_gid')) ? $arr['deny_gid'] : $channel['channel_deny_gid']); + if(! ( array_key_exists('allow_cid',$arr) || array_key_exists('allow_gid',$arr) + || array_key_exists('deny_cid',$arr) || array_key_exists('deny_gid',$arr))) { + $arr['allow_cid'] = $channel['channel_allow_cid']; + $arr['allow_gid'] = $channel['channel_allow_gid']; + $arr['deny_cid'] = $channel['channel_deny_cid']; + $arr['deny_gid'] = $channel['channel_deny_gid']; + } $arr['comment_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'post_comments')); if ((! $arr['plink']) && (intval($arr['item_thread_top']))) { - $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $arr['mid']; + $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); } @@ -382,18 +386,22 @@ function post_activity_item($arr) { return $ret; } - $post = item_store($arr); - if($post['success']) - $post_id = $post['item_id']; + $post = item_store($arr,$allow_code,$deliver); - if($post_id) { - $arr['id'] = $post_id; - call_hooks('post_local_end', $arr); - Zotlabs\Daemon\Master::Summon(array('Notifier','activity',$post_id)); + if($post['success']) { + $post_id = $post['item_id']; $ret['success'] = true; + $ret['item_id'] = $post_id; $ret['activity'] = $post['item']; + call_hooks('post_local_end', $ret['activity']); } + if($post_id && $deliver) { + Zotlabs\Daemon\Master::Summon(array('Notifier','activity',$post_id)); + } + + + $ret['success'] = true; return $ret; } @@ -420,7 +428,7 @@ function validate_item_elements($message,$arr) { /** - * @brief Limit lenght on imported system messages. + * @brief Limit length on imported system messages. * * The purpose of this function is to apply system message length limits to * imported messages without including any embedded photos in the length. @@ -829,13 +837,15 @@ function import_author_rss($x) { } $name = trim($x['name']); - $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_url, xchan_name, xchan_network ) - values ( '%s', '%s', '%s', '%s', '%s' )", - dbesc($x['guid']), - dbesc($x['guid']), - dbesc($x['url']), - dbesc(($name) ? $name : t('(Unknown)')), - dbesc('rss') + $r = xchan_store_lowlevel( + [ + 'xchan_hash' => $x['guid'], + 'xchan_guid' => $x['guid'], + 'xchan_url' => $x['url'], + 'xchan_name' => (($name) ? $name : t('(Unknown)')), + 'xchan_name_date' => datetime_convert(), + 'xchan_network' => 'rss' + ] ); if($r && $x['photo']) { @@ -874,14 +884,17 @@ function import_author_unknown($x) { $name = trim($x['name']); - $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_url, xchan_name, xchan_network ) - values ( '%s', '%s', '%s', '%s', '%s' )", - dbesc($x['url']), - dbesc($x['url']), - dbesc($x['url']), - dbesc(($name) ? $name : t('(Unknown)')), - dbesc('unknown') + $r = xchan_store_lowlevel( + [ + 'xchan_hash' => $x['url'], + 'xchan_guid' => $x['url'], + 'xchan_url' => $x['url'], + 'xchan_name' => (($name) ? $name : t('(Unknown)')), + 'xchan_name_date' => datetime_convert(), + 'xchan_network' => 'unknown' + ] ); + if($r && $x['photo']) { $photos = import_xchan_photo($x['photo']['src'],$x['url']); @@ -1512,6 +1525,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $arr['deny_cid'] = ((x($arr,'deny_cid')) ? trim($arr['deny_cid']) : ''); $arr['deny_gid'] = ((x($arr,'deny_gid')) ? trim($arr['deny_gid']) : ''); $arr['postopts'] = ((x($arr,'postopts')) ? trim($arr['postopts']) : ''); + $arr['route'] = ((x($arr,'route')) ? trim($arr['route']) : ''); $arr['item_private'] = ((x($arr,'item_private')) ? intval($arr['item_private']) : 0 ); $arr['item_wall'] = ((x($arr,'item_wall')) ? intval($arr['item_wall']) : 0 ); $arr['item_type'] = ((x($arr,'item_type')) ? intval($arr['item_type']) : 0 ); @@ -1528,7 +1542,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { // apply the input filter here - if it is obscured it has been filtered already $arr['body'] = trim(z_input_filter($arr['uid'],$arr['body'],$arr['mimetype'])); - if(local_channel() && (! $arr['sig'])) { + if(local_channel() && (local_channel() == $arr['uid']) && (! $arr['sig'])) { $channel = App::get_channel(); if($channel['channel_hash'] === $arr['author_xchan']) { $arr['sig'] = base64url_encode(rsa_sign($arr['body'],$channel['channel_prvkey'])); @@ -1565,8 +1579,11 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $arr['attach'] = json_encode($arr['attach']); } - $arr['aid'] = ((x($arr,'aid')) ? intval($arr['aid']) : 0); - $arr['mid'] = ((x($arr,'mid')) ? notags(trim($arr['mid'])) : random_string()); + $arr['aid'] = ((x($arr,'aid')) ? intval($arr['aid']) : 0); + $arr['mid'] = ((x($arr,'mid')) ? notags(trim($arr['mid'])) : random_string()); + $arr['revision'] = ((x($arr,'revision') && intval($arr['revision']) > 0) ? intval($arr['revision']) : 0); +logger('revision: ' . $arr['revision']); + $arr['author_xchan'] = ((x($arr,'author_xchan')) ? notags(trim($arr['author_xchan'])) : ''); $arr['owner_xchan'] = ((x($arr,'owner_xchan')) ? notags(trim($arr['owner_xchan'])) : ''); $arr['created'] = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert()); @@ -1622,7 +1639,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { if($d2 > $d1) $arr['item_delayed'] = 1; - $arr['llink'] = z_root() . '/display/' . $arr['mid']; + $arr['llink'] = z_root() . '/display/' . gen_link_id($arr['mid']); if(! $arr['plink']) $arr['plink'] = $arr['llink']; @@ -1727,9 +1744,10 @@ function item_store($arr, $allow_exec = false, $deliver = true) { if($parent_deleted) $arr['item_deleted'] = 1; - $r = q("SELECT id FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", + $r = q("SELECT id FROM item WHERE mid = '%s' AND uid = %d and revision = %d LIMIT 1", dbesc($arr['mid']), - intval($arr['uid']) + intval($arr['uid']), + intval($arr['revision']) ); if($r) { logger('item_store: duplicate item ignored. ' . print_r($arr,true)); @@ -1784,9 +1802,10 @@ function item_store($arr, $allow_exec = false, $deliver = true) { // find the item we just created - $r = q("SELECT * FROM item WHERE mid = '%s' AND uid = %d ORDER BY id ASC ", + $r = q("SELECT * FROM item WHERE mid = '%s' AND uid = %d and revision = %d ORDER BY id ASC ", $arr['mid'], // already dbesc'd - intval($arr['uid']) + intval($arr['uid']), + intval($arr['revision']) ); if($r && count($r)) { @@ -1945,7 +1964,7 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { // apply the input filter here - if it is obscured it has been filtered already $arr['body'] = trim(z_input_filter($arr['uid'],$arr['body'],$arr['mimetype'])); - if(local_channel() && (! $arr['sig'])) { + if(local_channel() && (local_channel() == $arr['uid']) && (! $arr['sig'])) { $channel = App::get_channel(); if($channel['channel_hash'] === $arr['author_xchan']) { $arr['sig'] = base64url_encode(rsa_sign($arr['body'],$channel['channel_prvkey'])); @@ -1997,6 +2016,8 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { $arr['edited'] = ((x($arr,'edited') !== false) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert()); $arr['expires'] = ((x($arr,'expires') !== false) ? datetime_convert('UTC','UTC',$arr['expires']) : $orig[0]['expires']); + $arr['revision'] = ((x($arr,'revision') && $arr['revision'] > 0) ? intval($arr['revision']) : 0); + if(array_key_exists('comments_closed',$arr) && $arr['comments_closed'] > NULL_DATE) $arr['comments_closed'] = datetime_convert('UTC','UTC',$arr['comments_closed']); else @@ -2174,7 +2195,7 @@ function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id, // since Diaspora doesn't handle edits we can only do this for the original text and not update it. - require_once('include/bb2diaspora.php'); + require_once('include/markdown.php'); $signed_body = bb2diaspora_itembody($datarray,$walltowall); if($walltowall) { @@ -2273,7 +2294,7 @@ function send_status_notifications($post_id,$item) { if($unfollowed) return; - $link = z_root() . '/display/' . $item['mid']; + $link = z_root() . '/display/' . gen_link_id($item['mid']); $y = q("select id from notify where link = '%s' and uid = %d limit 1", dbesc($link), @@ -3300,7 +3321,7 @@ function retain_item($id) { ); } -function drop_items($items) { +function drop_items($items,$interactive = false,$stage = DROPITEM_NORMAL,$force = false) { $uid = 0; if(! local_channel() && ! remote_channel()) @@ -3308,7 +3329,7 @@ function drop_items($items) { if(count($items)) { foreach($items as $item) { - $owner = drop_item($item,false); + $owner = drop_item($item,$interactive,$stage,$force); if($owner && ! $uid) $uid = $owner; } @@ -3332,6 +3353,11 @@ function drop_items($items) { function drop_item($id,$interactive = true,$stage = DROPITEM_NORMAL,$force = false) { + // These resource types have linked items that should only be removed at the same time + // as the linked resource; if we encounter one set it to item_hidden rather than item_deleted. + + $linked_resource_types = [ 'photo' ]; + // locate item to be deleted $r = q("SELECT * FROM item WHERE id = %d LIMIT 1", @@ -3347,7 +3373,7 @@ function drop_item($id,$interactive = true,$stage = DROPITEM_NORMAL,$force = fal $item = $r[0]; - $linked_item = (($item['resource_id']) ? true : false); + $linked_item = (($item['resource_id'] && $item['resource_type'] && in_array($item['resource_type'], $linked_resource_types)) ? true : false); $ok_to_delete = false; @@ -4464,9 +4490,9 @@ function item_create_edit_activity($post) { $new_item['verb'] = ACTIVITY_UPDATE; $new_item['item_thread_top'] = 0; $new_item['created'] = $new_item['edited'] = datetime_convert(); - + $new_item['obj_type'] = (($update_item['item_thread_top']) ? ACTIVITY_OBJ_NOTE : ACTIVITY_OBJ_COMMENT); $new_item['obj'] = json_encode(array( - 'type' => (($update_item['item_thread_top']) ? ACTIVITY_OBJ_NOTE : ACTIVITY_OBJ_COMMENT), + 'type' => $new_item['obj_type'], 'id' => $update_item['mid'], 'parent' => $update_item['parent_mid'], 'link' => array(array('rel' => 'alternate','type' => 'text/html', 'href' => $update_item['plink'])), diff --git a/include/bb2diaspora.php b/include/markdown.php index 87a8551ae..7afdc6c54 100644 --- a/include/bb2diaspora.php +++ b/include/markdown.php @@ -1,6 +1,6 @@ <?php /** - * @file include/bb2diaspora.php + * @file include/markdown.php * @brief Some functions for BB conversions for Diaspora protocol. */ @@ -110,6 +110,39 @@ function diaspora_mention_callback($matches) { } +function diaspora_mention_callback2($matches) { + + $webbie = $matches[1] . '@' . $matches[2]; + $link = ''; + if($webbie) { + $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_addr = '%s' limit 1", + dbesc($webbie) + ); + if(! $r) { + $x = discover_by_webbie($webbie); + if($x) { + $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_addr = '%s' limit 1", + dbesc($webbie) + ); + } + } + if($r) + $link = $r[0]['xchan_url']; + } + + $name = (($r) ? $r[0]['xchan_name'] : $matches[1]); + + if(! $link) + $link = 'https://' . $matches[2] . '/u/' . $matches[1]; + + if($r && $r[0]['hubloc_network'] === 'zot') + return '@[zrl=' . $link . ']' . trim($name) . ((substr($matches[0],-1,1) === '+') ? '+' : '') . '[/zrl]' ; + else + return '@[url=' . $link . ']' . trim($name) . ((substr($matches[0],-1,1) === '+') ? '+' : '') . '[/url]' ; + +} + + /** * @brief @@ -128,6 +161,11 @@ function markdown_to_bb($s, $use_zrl = false) { $s = str_replace("
","\r",$s); $s = str_replace("
\n>","",$s); + if(is_array($s)) { + btlogger('markdown_to_bb called with array. ' . print_r($s,true), LOGGER_NORMAL, LOG_WARNING); + return ''; + } + $s = html_entity_decode($s,ENT_COMPAT,'UTF-8'); // if empty link text replace with the url @@ -136,9 +174,11 @@ function markdown_to_bb($s, $use_zrl = false) { // first try plustags $s = preg_replace_callback('/\@\{(.+?)\; (.+?)\@(.+?)\}\+/','diaspora_mention_callback',$s); - $s = preg_replace_callback('/\@\{(.+?)\; (.+?)\@(.+?)\}/','diaspora_mention_callback',$s); + $s = preg_replace_callback('/\@\{(.+?)\@(.+?)\}\+/','diaspora_mention_callback2',$s); + $s = preg_replace_callback('/\@\{(.+?)\@(.+?)\}/','diaspora_mention_callback2',$s); + // Escaping the hash tags - doesn't always seem to work // $s = preg_replace('/\#([^\s\#])/','\\#$1',$s); // This seems to work diff --git a/include/menu.php b/include/menu.php index b54ff7f9e..4add78c39 100644 --- a/include/menu.php +++ b/include/menu.php @@ -102,7 +102,7 @@ function menu_render($menu, $class='', $edit = false, $var = array()) { if($menu['items'][$x]['mitem_flags'] & MENU_ITEM_NEWWIN) $menu['items'][$x]['newwin'] = '1'; - $menu['items'][$x]['mitem_desc'] = bbcode($menu['items'][$x]['mitem_desc']); + $menu['items'][$x]['mitem_desc'] = zidify_links(smilies(bbcode($menu['items'][$x]['mitem_desc']))); } $wrap = (($var['wrap'] === 'none') ? false : true); diff --git a/include/message.php b/include/message.php index 7cbea3c6b..bde07afd8 100644 --- a/include/message.php +++ b/include/message.php @@ -8,7 +8,7 @@ require_once('include/attach.php'); // send a private message -function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='',$expires = ''){ +function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $replyto = '', $expires = NULL_DATE) { $ret = array('success' => false); $is_reply = false; @@ -16,19 +16,6 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' $a = get_app(); $observer_hash = get_observer_hash(); - if(! $recipient) { - $ret['message'] = t('No recipient provided.'); - return $ret; - } - - if(! strlen($subject)) - $subject = t('[no subject]'); - -// if(! $expires) -// $expires = NULL_DATE; -// else -// $expires = datetime_convert(date_default_timezone_get(),'UTC',$expires); - if($uid) { $r = q("select * from channel where channel_id = %d limit 1", @@ -47,6 +34,46 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' } + $body = cleanup_bbcode($body); + $results = linkify_tags($a, $body, $uid); + + + if(preg_match_all("/\[attachment\](.*?)\[\/attachment\]/",((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$match)) + $attaches = $match[1]; + + $attachments = ''; + + if(preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/',$body,$match)) { + $attachments = array(); + foreach($match[2] as $mtch) { + $hash = substr($mtch,0,strpos($mtch,',')); + $rev = intval(substr($mtch,strpos($mtch,','))); + $r = attach_by_hash_nodata($hash,get_observer_hash(),$rev); + if($r['success']) { + $attachments[] = array( + 'href' => z_root() . '/attach/' . $r['data']['hash'], + 'length' => $r['data']['filesize'], + 'type' => $r['data']['filetype'], + 'title' => urlencode($r['data']['filename']), + 'revision' => $r['data']['revision'] + ); + } + $body = trim(str_replace($match[1],'',$body)); + } + } + + $jattach = (($attachments) ? json_encode($attachments) : ''); + + + if(! $recipient) { + $ret['message'] = t('No recipient provided.'); + return $ret; + } + + if(! strlen($subject)) + $subject = t('[no subject]'); + + // look for any existing conversation structure $conv_guid = ''; @@ -156,31 +183,6 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' $match = false; - if(preg_match_all("/\[attachment\](.*?)\[\/attachment\]/",((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$match)) - $attaches = $match[1]; - - $attachments = ''; - - if(preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/',$body,$match)) { - $attachments = array(); - foreach($match[2] as $mtch) { - $hash = substr($mtch,0,strpos($mtch,',')); - $rev = intval(substr($mtch,strpos($mtch,','))); - $r = attach_by_hash_nodata($hash,get_observer_hash(),$rev); - if($r['success']) { - $attachments[] = array( - 'href' => z_root() . '/attach/' . $r['data']['hash'], - 'length' => $r['data']['filesize'], - 'type' => $r['data']['filetype'], - 'title' => urlencode($r['data']['filename']), - 'revision' => $r['data']['revision'] - ); - } - $body = trim(str_replace($match[1],'',$body)); - } - } - - $jattach = (($attachments) ? json_encode($attachments) : ''); if($subject) $subject = str_rot47(base64url_encode($subject)); diff --git a/include/nav.php b/include/nav.php index 2762e2a14..144509cd0 100644 --- a/include/nav.php +++ b/include/nav.php @@ -1,6 +1,8 @@ <?php /** @file */ -function nav(&$a) { +use \Zotlabs\Lib as Zlib; + +function nav() { /** * @@ -12,8 +14,8 @@ function nav(&$a) { App::$page['nav'] = ''; $base = z_root(); - App::$page['htmlhead'] .= <<< EOT + App::$page['htmlhead'] .= <<< EOT <script>$(document).ready(function() { $("#nav-search-text").search_autocomplete('$base/acl'); }); @@ -21,8 +23,6 @@ function nav(&$a) { </script> EOT; - - if(local_channel()) { $channel = App::get_channel(); $observer = App::get_observer(); @@ -76,6 +76,20 @@ EOT; $userinfo = null; $nav['loginmenu']=array(); + if($observer) { + $userinfo = array( + 'icon' => $observer['xchan_photo_m'], + 'name' => $observer['xchan_addr'], + ); + } + + elseif(! $_SESSION['authenticated']) { + $nav['remote_login'] = remote_login(); + $nav['loginmenu'][] = Array('rmagic',t('Remote authentication'),'',t('Click to authenticate to your home hub'),'rmagic_nav_btn'); + } + + + if(local_channel()) { @@ -85,34 +99,42 @@ EOT; $nav['logout'] = Array('logout',t('Logout'), "", t('End this session'),'logout_nav_btn'); // user menu - $nav['usermenu'][] = Array('channel/' . $channel['channel_address'], t('Home'), "", t('Your posts and conversations'),'channel_nav_btn'); + //$nav['usermenu'][] = Array('channel/' . $channel['channel_address'], t('Home'), "", t('Your posts and conversations'),'channel_nav_btn'); $nav['usermenu'][] = Array('profile/' . $channel['channel_address'], t('View Profile'), "", t('Your profile page'),'profile_nav_btn'); if(feature_enabled(local_channel(),'multi_profiles') && (! $basic)) $nav['usermenu'][] = Array('profiles', t('Edit Profiles'),"", t('Manage/Edit profiles'),'profiles_nav_btn'); else $nav['usermenu'][] = Array('profiles/' . $prof[0]['id'], t('Edit Profile'),"", t('Edit your profile'),'profiles_nav_btn'); - $nav['usermenu'][] = Array('photos/' . $channel['channel_address'], t('Photos'), "", t('Your photos'),'photos_nav_btn'); - $nav['usermenu'][] = Array('cloud/' . $channel['channel_address'],t('Files'),"",t('Your files'),'cloud_nav_btn'); + //$nav['usermenu'][] = Array('photos/' . $channel['channel_address'], t('Photos'), "", t('Your photos'),'photos_nav_btn'); + //$nav['usermenu'][] = Array('cloud/' . $channel['channel_address'],t('Files'),"",t('Your files'),'cloud_nav_btn'); - if((! $basic) && feature_enabled(local_channel(),'ajaxchat')) - $nav['usermenu'][] = Array('chat/' . $channel['channel_address'], t('Chat'),"",t('Your chatrooms'),'chat_nav_btn'); + //if((! $basic) && feature_enabled(local_channel(),'ajaxchat')) + // $nav['usermenu'][] = Array('chat/' . $channel['channel_address'], t('Chat'),"",t('Your chatrooms'),'chat_nav_btn'); - require_once('include/menu.php'); - $has_bookmarks = menu_list_count(local_channel(),'',MENU_BOOKMARK) + menu_list_count(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK); - if(($has_bookmarks) && (! $basic)) { - $nav['usermenu'][] = Array('bookmarks', t('Bookmarks'), "", t('Your bookmarks'),'bookmarks_nav_btn'); - } + //require_once('include/menu.php'); + //$has_bookmarks = menu_list_count(local_channel(),'',MENU_BOOKMARK) + menu_list_count(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK); + //if(($has_bookmarks) && (! $basic)) { + // $nav['usermenu'][] = Array('bookmarks', t('Bookmarks'), "", t('Your bookmarks'),'bookmarks_nav_btn'); + //} - if(feature_enabled($channel['channel_id'],'webpages') && (! $basic)) - $nav['usermenu'][] = Array('webpages/' . $channel['channel_address'],t('Webpages'),"",t('Your webpages'),'webpages_nav_btn'); - if(feature_enabled($channel['channel_id'],'wiki') && (! $basic)) - $nav['usermenu'][] = Array('wiki/' . $channel['channel_address'],t('Wikis'),"",t('Your wikis'),'wiki_nav_btn'); + //if(feature_enabled($channel['channel_id'],'webpages') && (! $basic)) + // $nav['usermenu'][] = Array('webpages/' . $channel['channel_address'],t('Webpages'),"",t('Your webpages'),'webpages_nav_btn'); + //if(feature_enabled($channel['channel_id'],'wiki') && (! $basic)) + // $nav['usermenu'][] = Array('wiki/' . $channel['channel_address'],t('Wikis'),"",t('Your wikis'),'wiki_nav_btn'); } else { if(! get_account_id()) { + $nav['login'] = login(true,'main-login',false,false); $nav['loginmenu'][] = Array('login',t('Login'),'',t('Sign in'),'login_nav_btn'); + App::$page['content'] .= replace_macros(get_markup_template('nav_login.tpl'), + [ + '$nav' => $nav, + 'userinfo' => $userinfo + ] + ); + } else $nav['alogout'] = Array('logout',t('Logout'), "", t('End this session'),'logout_nav_btn'); @@ -120,16 +142,6 @@ EOT; } - if($observer) { - $userinfo = array( - 'icon' => $observer['xchan_photo_m'], - 'name' => $observer['xchan_addr'], - ); - } - - elseif(! $_SESSION['authenticated']) { - $nav['loginmenu'][] = Array('rmagic',t('Remote authentication'),'',t('Click to authenticate to your home hub'),'rmagic_nav_btn'); - } $homelink = get_my_url(); if(! $homelink) { @@ -180,9 +192,11 @@ EOT; $nav['network'] = array('network', t('Grid'), "", t('Your grid'),'network_nav_btn'); + $nav['network']['all'] = [ 'network', t('View your network/grid'), '','' ]; $nav['network']['mark'] = array('', t('Mark all grid notifications seen'), '',''); $nav['home'] = array('channel/' . $channel['channel_address'], t('Channel Home'), "", t('Channel home'),'home_nav_btn'); + $nav['home']['all'] = [ 'channel/' . $channel['channel_address'], t('View your channel home'), '' , '' ]; $nav['home']['mark'] = array('', t('Mark all channel notifications seen'), '',''); @@ -190,11 +204,11 @@ EOT; $nav['notifications'] = array('notifications/system', t('Notices'), "", t('Notifications'),'notifications_nav_btn'); - $nav['notifications']['all']=array('notifications/system', t('See all notifications'), "", ""); + $nav['notifications']['all']=array('notifications/system', t('View all notifications'), "", ""); $nav['notifications']['mark'] = array('', t('Mark all system notifications seen'), '',''); $nav['messages'] = array('mail/combined', t('Mail'), "", t('Private mail'),'mail_nav_btn'); - $nav['messages']['all']=array('mail/combined', t('See all private messages'), "", ""); + $nav['messages']['all']=array('mail/combined', t('View your private messages'), "", ""); $nav['messages']['mark'] = array('', t('Mark all private messages seen'), '',''); $nav['messages']['inbox'] = array('mail/inbox', t('Inbox'), "", t('Inbox')); $nav['messages']['outbox']= array('mail/outbox', t('Outbox'), "", t('Outbox')); @@ -202,7 +216,7 @@ EOT; $nav['all_events'] = array('events', t('Events'), "", t('Event Calendar'),'events_nav_btn'); - $nav['all_events']['all']=array('events', t('See all events'), "", ""); + $nav['all_events']['all']=array('events', t('View events'), "", ""); $nav['all_events']['mark'] = array('', t('Mark all events seen'), '',''); if(! $basic) @@ -234,16 +248,44 @@ EOT; $x = array('nav' => $nav, 'usermenu' => $userinfo ); call_hooks('nav', $x); -// Not sure the best place to put this on the page. So I'm implementing it but leaving it -// turned off until somebody discovers this and figures out a good location for it. -$powered_by = ''; + // Not sure the best place to put this on the page. So I'm implementing it but leaving it + // turned off until somebody discovers this and figures out a good location for it. + $powered_by = ''; + + // $powered_by = '<strong>red<img class="smiley" src="' . z_root() . '/images/rm-16.png" alt="r#" />matrix</strong>'; + + + //app bin + if(local_channel()) { + if(get_pconfig(local_channel(), 'system','initial_import_system_apps') === false) { + Zlib\Apps::import_system_apps(); + set_pconfig(local_channel(), 'system','initial_import_system_apps', 1); + } + + $syslist = array(); + $list = Zlib\Apps::app_list(local_channel(), false, 'nav_featured_app'); + if($list) { + foreach($list as $li) { + $syslist[] = Zlib\Apps::app_encode($li); + } + } + Zlib\Apps::translate_system_apps($syslist); + } + else { + $syslist = Zlib\Apps::get_system_apps(true); + } -// $powered_by = '<strong>red<img class="smiley" src="' . z_root() . '/images/rm-16.png" alt="r#" />matrix</strong>'; + usort($syslist,'Zotlabs\\Lib\\Apps::app_name_compare'); + + foreach($syslist as $app) { + $navapps[] = Zlib\Apps::app_render($app,'nav'); + } $tpl = get_markup_template('nav.tpl'); App::$page['nav'] .= replace_macros($tpl, array( '$baseurl' => z_root(), + '$fulldocs' => t('Help'), '$sitelocation' => $sitelocation, '$nav' => $x['nav'], '$banner' => $banner, @@ -253,10 +295,11 @@ $powered_by = ''; '$sel' => App::$nav_sel, '$powered_by' => $powered_by, '$help' => t('@name, #tag, ?doc, content'), - '$pleasewait' => t('Please wait...') + '$pleasewait' => t('Please wait...'), + '$navapps' => $navapps, + '$addapps' => t('Add Apps') )); - if(x($_SESSION, 'reload_avatar') && $observer) { // The avatar has been changed on the server but the browser doesn't know that, // force the browser to reload the image from the server instead of its cache. diff --git a/include/network.php b/include/network.php index 451ce12a1..66716ef9e 100644 --- a/include/network.php +++ b/include/network.php @@ -51,7 +51,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_CAINFO, get_capath()); @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); @curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); - @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; Red)"); + @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; zot)"); $ciphers = @get_config('system','curl_ssl_ciphers'); if($ciphers) @@ -84,7 +84,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $opts['custom']); if(x($opts,'timeout') && intval($opts['timeout'])) { - @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); + @curl_setopt($ch, CURLOPT_TIMEOUT, intval($opts['timeout'])); } else { $curl_time = intval(@get_config('system','curl_timeout')); @@ -218,7 +218,7 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); @curl_setopt($ch, CURLOPT_POST,1); @curl_setopt($ch, CURLOPT_POSTFIELDS,$params); - @curl_setopt($ch, CURLOPT_USERAGENT, "Red"); + @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; zot)"); $ciphers = @get_config('system','curl_ssl_ciphers'); if($ciphers) @@ -714,6 +714,10 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) if($i['success']) { $ph = photo_factory($i['body'], $type); + + if(! is_object($ph)) + continue; + if($ph->is_valid()) { $orig_width = $ph->getWidth(); $orig_height = $ph->getHeight(); @@ -1117,16 +1121,17 @@ function discover_by_url($url,$arr = null) { if(! $photo) $photo = z_root() . '/images/rss_icon.png'; - $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_instance_url, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", - dbesc($guid), - dbesc($guid), - dbesc($pubkey), - dbesc($addr), - dbesc($profile), - dbesc($name), - dbesc($network), - dbesc(z_root()), - dbesc(datetime_convert()) + $r = xchan_store_lowlevel( + [ + 'xchan_hash' => $guid, + 'xchan_guid' => $guid, + 'xchan_pubkey' => $pubkey, + 'xchan_addr' => $addr, + 'xchan_url' => $profile, + 'xchan_name' => $name, + 'xchan_name_date' => datetime_convert(), + 'xchan_network' => $network + ] ); $photos = import_xchan_photo($photo,$guid); @@ -1445,15 +1450,17 @@ function discover_by_webbie($webbie) { ); } else { - $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", - dbesc($address), - dbesc(($diaspora_guid) ? $diaspora_guid : $location), - dbesc($pubkey), - dbesc($address), - dbesc($location), - dbesc($fullname), - dbesc($network), - dbescdate(datetime_convert()) + $r = xchan_store_lowlevel( + [ + 'xchan_hash' => $address, + 'xchan_guid' => (($diaspora_guid) ? $diaspora_guid : $location), + 'xchan_pubkey' => $pubkey, + 'xchan_addr' => $address, + 'xchan_url' => $location, + 'xchan_name' => $fullname, + 'xchan_name_date' => datetime_convert(), + 'xchan_network' => $network + ] ); } @@ -1462,15 +1469,18 @@ function discover_by_webbie($webbie) { ); if(! $r) { - $r = q("insert into hubloc ( hubloc_guid, hubloc_hash, hubloc_addr, hubloc_network, hubloc_url, hubloc_host, hubloc_callback, hubloc_updated, hubloc_primary ) values ('%s','%s','%s','%s','%s','%s','%s','%s', 1)", - dbesc(($diaspora_guid) ? $diaspora_guid : $location), - dbesc($address), - dbesc($address), - dbesc($network), - dbesc($base), - dbesc($host), - dbesc($notify), - dbescdate(datetime_convert()) + $r = hubloc_store_lowlevel( + [ + 'hubloc_guid' => (($diaspora_guid) ? $diaspora_guid : $location), + 'hubloc_hash' => $address, + 'hubloc_addr' => $address, + 'hubloc_network' => $network, + 'hubloc_url' => $base, + 'hubloc_host' => $host, + 'hubloc_callback' => $notify, + 'hubloc_updated' => datetime_convert(), + 'hubloc_primary' => 1 + ] ); } $photos = import_xchan_photo($avatar,$address); diff --git a/include/oembed.php b/include/oembed.php index 8c78e2807..aac7d15b4 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -2,8 +2,6 @@ use Zotlabs\Lib as Zlib; -require_once('include/hubloc.php'); - function oembed_replacecb($matches){ @@ -107,7 +105,7 @@ function oembed_action($embedurl) { function oembed_process($url) { $j = oembed_fetch_url($url); - logger('oembed_process: ' . print_r($j,true)); + logger('oembed_process: ' . print_r($j,true), LOGGER_DATA, LOG_DEBUG); if($j && $j['type'] !== 'error') return '[embed]' . $url . '[/embed]'; return false; @@ -138,19 +136,15 @@ function oembed_fetch_url($embedurl){ // we should try to cache this and avoid a lookup on each render $zrl = is_matrix_url($embedurl); + $furl = ((local_channel() && $zrl) ? zid($embedurl) : $embedurl); + if($action !== 'block') { - $txt = Zlib\Cache::get('[' . App::$videowidth . '] ' . $embedurl); + $txt = Zlib\Cache::get('[' . App::$videowidth . '] ' . $furl); } if(is_null($txt)) { $txt = ""; - $furl = $embedurl; - - logger('local_channel: ' . local_channel()); - - if(local_channel() && $zrl) - $furl = zid($furl); if ($action !== 'block') { // try oembed autodiscovery @@ -209,11 +203,10 @@ function oembed_fetch_url($embedurl){ //save in cache if(! get_config('system','oembed_cache_disable')) - Zlib\Cache::set('[' . App::$videowidth . '] ' . $embedurl,$txt); + Zlib\Cache::set('[' . App::$videowidth . '] ' . $furl, $txt); } - $j = json_decode($txt,true); if(! $j) diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index af4fd0a30..c8b3c3782 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -11,8 +11,10 @@ function photo_factory($data, $type = null) { 'image/svg+xml' ); - if($type && in_array(strtolower($type),$unsupported_types)) + if($type && in_array(strtolower($type),$unsupported_types)) { + logger('photo_factory: unsupported image type'); return null; + } $ignore_imagick = get_config('system', 'ignore_imagick'); diff --git a/include/photos.php b/include/photos.php index 5e4d755e3..c0f7dc8c4 100644 --- a/include/photos.php +++ b/include/photos.php @@ -402,7 +402,7 @@ function photo_upload($channel, $observer, $args) { $arr['item_origin'] = 1; $arr['item_thread_top'] = 1; $arr['item_private'] = intval($acl->is_private()); - $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $arr['mid']; + $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); $arr['body'] = $summary; @@ -464,10 +464,15 @@ function photos_albums_list($channel, $observer, $sort_key = 'album', $direction $sort_key = dbesc($sort_key); $direction = dbesc($direction); - $albums = q("SELECT count( distinct resource_id ) as total, album from photo where uid = %d and photo_usage IN ( %d, %d ) $sql_extra group by album order by $sort_key $direction", - intval($channel_id), - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE) + //$albums = q("SELECT count( distinct resource_id ) as total, album from photo where uid = %d and photo_usage IN ( %d, %d ) $sql_extra group by album order by $sort_key $direction", + // intval($channel_id), + // intval(PHOTO_NORMAL), + // intval(PHOTO_PROFILE) + //); + + // this query provides the same results but might perform better + $albums = q("SELECT count( distinct resource_id ) as total, album from photo where uid = %d and os_storage = 1 $sql_extra group by album order by $sort_key $direction", + intval($channel_id) ); // add various encodings to the array so we can just loop through and pick them out in a template @@ -480,6 +485,7 @@ function photos_albums_list($channel, $observer, $sort_key = 'album', $direction foreach($albums as $k => $album) { $entry = array( 'text' => (($album['album']) ? $album['album'] : '/'), + 'jstext' => (($album['album']) ? addslashes($album['album']) : '/'), 'total' => $album['total'], 'url' => z_root() . '/photos/' . $channel['channel_address'] . '/album/' . bin2hex($album['album']), 'urlencode' => urlencode($album['album']), @@ -489,6 +495,8 @@ function photos_albums_list($channel, $observer, $sort_key = 'album', $direction } } + App::$data['albums'] = $ret; + return $ret; } @@ -663,7 +671,7 @@ function photos_create_item($channel, $creator_hash, $photo, $visible = false) { $arr['deny_cid'] = $photo['deny_cid']; $arr['deny_gid'] = $photo['deny_gid']; - $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $arr['mid']; + $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); $arr['body'] = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-' . $photo['imgscale'] . '[/zmg]' @@ -710,10 +718,59 @@ function gps2Num($coordPart) { return floatval($parts[0]) / floatval($parts[1]); } + +function photo_profile_setperms($channel_id,$resource_id,$profile_id) { + + if(! $profile_id) + return; + + $r = q("select profile_guid, is_default from profile where id = %d and uid = %d limit 1", + dbesc($profile_id), + intval($channel_id) + ); + + if(! $r) + return; + + $is_default = $r[0]['is_default']; + $profile_guid = $r[0]['profile_guid']; + + if($is_default) { + $r = q("update photo set allow_cid = '', allow_gid = '', deny_cid = '', deny_gid = '' + where resource_id = '%s' and uid = %d", + dbesc($resource_id), + intval($channel_id) + ); + $r = q("update attach set allow_cid = '', allow_gid = '', deny_cid = '', deny_gid = '' + where hash = '%s' and uid = %d", + dbesc($resource_id), + intval($channel_id) + ); + } + else { + $r = q("update photo set allow_cid = '', allow_gid = '%s', deny_cid = '', deny_gid = '' + where resource_id = '%s' and uid = %d", + dbesc('<vp.' . $profile_guid . '>'), + dbesc($resource_id), + intval($channel_id) + ); + + $r = q("update attach set allow_cid = '', allow_gid = '%s', deny_cid = '', deny_gid = '' + where hash = '%s' and uid = %d", + dbesc('<vp.' . $profile_guid . '>'), + dbesc($resource_id), + intval($channel_id) + ); + } +} + function profile_photo_set_profile_perms($uid, $profileid = 0) { - $allowcid = ''; - if($profileid) { + $allowcid = ''; + + + if($profileid) { + $r = q("SELECT photo, profile_guid, id, is_default, uid FROM profile WHERE uid = %d and ( profile.id = %d OR profile.profile_guid = '%s') LIMIT 1", intval($uid), diff --git a/include/plugin.php b/include/plugin.php index 80c303b42..29474735e 100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -471,6 +471,8 @@ function get_theme_info($theme){ 'description' => '', 'author' => array(), 'version' => '', + 'minversion' => '', + 'maxversion' => '', 'compat' => '', 'credits' => '', 'maintainer' => array(), @@ -556,32 +558,65 @@ function head_add_css($src, $media = 'screen') { function head_remove_css($src, $media = 'screen') { $index = array_search(array($src, $media), App::$css_sources); - if ($index !== false) + if($index !== false) unset(App::$css_sources[$index]); } function head_get_css() { $str = ''; $sources = App::$css_sources; - if (count($sources)) { - foreach ($sources as $source) + if(count($sources)) { + foreach($sources as $source) $str .= format_css_if_exists($source); } return $str; } +function head_add_link($arr) { + if($arr) { + App::$linkrel[] = $arr; + } +} + +function head_get_links() { + $str = ''; + $sources = App::$linkrel; + if(count($sources)) { + foreach($sources as $source) { + if(is_array($source) && count($source)) { + $str .= '<link'; + foreach($source as $k => $v) { + $str .= ' ' . $k . '="' . $v . '"'; + } + $str .= ' />' . "\r\n"; + + } + } + } + + return $str; +} + + function format_css_if_exists($source) { - $path_prefix = script_path() . '/'; - if (strpos($source[0], '/') !== false) { - // The source is a URL - $path = $source[0]; + // script_path() returns https://yoursite.tld + + $path_prefix = script_path(); + + $script = $source[0]; + + if(strpos($script, '/') !== false) { + // The script is a path relative to the server root + $path = $script; // If the url starts with // then it's an absolute URL - if($source[0][0] === '/' && $source[0][1] === '/') $path_prefix = ''; + if(substr($script,0,2) === '//') { + $path_prefix = ''; + } } else { // It's a file from the theme - $path = theme_include($source[0]); + $path = '/' . theme_include($script); } if($path) { @@ -651,7 +686,7 @@ function head_get_js() { foreach(App::$js_sources as $sources) { if(count($sources)) { foreach($sources as $source) { - if($src === 'main.js') + if($source === 'main.js') continue; $str .= format_js_if_exists($source); } @@ -671,16 +706,19 @@ function head_get_main_js() { } function format_js_if_exists($source) { - $path_prefix = script_path() . '/'; + $path_prefix = script_path(); if(strpos($source,'/') !== false) { - // The source is a URL + // The source is a known path on the system $path = $source; // If the url starts with // then it's an absolute URL - if($source[0] === '/' && $source[1] === '/') $path_prefix = ''; - } else { + if(substr($source,0,2) === '//') { + $path_prefix = ''; + } + } + else { // It's a file from the theme - $path = theme_include($source); + $path = '/' . theme_include($source); } if($path) { $qstring = ((parse_url($path, PHP_URL_QUERY)) ? '&' : '?') . 'v=' . STD_VERSION; diff --git a/include/queue_fn.php b/include/queue_fn.php index 0950faf85..ede6c8f11 100644 --- a/include/queue_fn.php +++ b/include/queue_fn.php @@ -2,9 +2,57 @@ function update_queue_item($id, $add_priority = 0) { logger('queue: requeue item ' . $id,LOGGER_DEBUG); - q("UPDATE outq SET outq_updated = '%s', outq_priority = outq_priority + %d WHERE outq_hash = '%s'", + $x = q("select outq_created, outq_posturl from outq where outq_hash = '%s' limit 1", + dbesc($id) + ); + if(! $x) + return; + + + $y = q("select min(outq_created) as earliest from outq where outq_posturl = '%s'", + dbesc($x[0]['outq_posturl']) + ); + + // look for the oldest queue entry with this destination URL. If it's older than a couple of days, + // the destination is considered to be down and only scheduled once an hour, regardless of the + // age of the current queue item. + + $might_be_down = false; + + if($y) + $might_be_down = ((datetime_convert('UTC','UTC',$y[0]['earliest']) < datetime_convert('UTC','UTC','now - 2 days')) ? true : false); + + + // Set all other records for this destination way into the future. + // The queue delivers by destination. We'll keep one queue item for + // this destination (this one) with a shorter delivery. If we succeed + // once, we'll try to deliver everything for that destination. + // The delivery will be set to at most once per hour, and if the + // queue item is less than 12 hours old, we'll schedule for fifteen + // minutes. + + $r = q("UPDATE outq SET outq_scheduled = '%s' WHERE outq_posturl = '%s'", + dbesc(datetime_convert('UTC','UTC','now + 5 days')), + dbesc($x[0]['outq_posturl']) + ); + + $since = datetime_convert('UTC','UTC',$x[0]['outq_created']); + + if(($might_be_down) || ($since < datetime_convert('UTC','UTC','now - 12 hour'))) { + $next = datetime_convert('UTC','UTC','now + 1 hour'); + } + else { + $next = datetime_convert('UTC','UTC','now + 15 minutes'); + } + + q("UPDATE outq SET outq_updated = '%s', + outq_priority = outq_priority + %d, + outq_scheduled = '%s' + WHERE outq_hash = '%s'", + dbesc(datetime_convert()), intval($add_priority), + dbesc($next), dbesc($id) ); } @@ -33,8 +81,12 @@ function queue_set_delivered($id,$channel = 0) { logger('queue: set delivered ' . $id,LOGGER_DEBUG); $sql_extra = (($channel_id) ? " and outq_channel = " . intval($channel_id) . " " : ''); - q("update outq set outq_delivered = 1, outq_updated = '%s' where outq_hash = '%s' $sql_extra ", + // Set the next scheduled run date so far in the future that it will be expired + // long before it ever makes it back into the delivery chain. + + q("update outq set outq_delivered = 1, outq_updated = '%s', outq_scheduled = '%s' where outq_hash = '%s' $sql_extra ", dbesc(datetime_convert()), + dbesc(datetime_convert('UTC','UTC','now + 5 days')), dbesc($id) ); } @@ -44,8 +96,8 @@ function queue_set_delivered($id,$channel = 0) { function queue_insert($arr) { $x = q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_priority, - outq_created, outq_updated, outq_notify, outq_msg ) - values ( '%s', %d, %d, '%s', '%s', %d, %d, '%s', '%s', '%s', '%s' )", + outq_created, outq_updated, outq_scheduled, outq_notify, outq_msg ) + values ( '%s', %d, %d, '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s' )", dbesc($arr['hash']), intval($arr['account_id']), intval($arr['channel_id']), @@ -55,6 +107,7 @@ function queue_insert($arr) { intval(($arr['priority']) ? $arr['priority'] : 0), dbesc(datetime_convert()), dbesc(datetime_convert()), + dbesc(datetime_convert()), dbesc($arr['notify']), dbesc(($arr['msg']) ? $arr['msg'] : '') ); diff --git a/include/security.php b/include/security.php index 2f5de54c7..b49ceec0d 100644 --- a/include/security.php +++ b/include/security.php @@ -171,19 +171,16 @@ function atoken_create_xchan($xchan) { if($r) return; - $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_photo_mimetype, xchan_photo_l, xchan_photo_m, xchan_photo_s ) - values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", - dbesc($xchan['xchan_hash']), - dbesc($xchan['xchan_hash']), - dbesc($xchan['xchan_addr']), - dbesc($xchan['xchan_url']), - dbesc($xchan['xchan_name']), - dbesc($xchan['xchan_network']), - dbesc($xchan['xchan_photo_mimetype']), - dbesc($xchan['xchan_photo_l']), - dbesc($xchan['xchan_photo_m']), - dbesc($xchan['xchan_photo_s']) - ); + $xchan['xchan_guid'] = $xchan['xchan_hash']; + + $store = []; + foreach($xchan as $k => $v) { + if(strpos($k,'xchan_') === 0) { + $store[$k] = $v; + } + } + + $r = xchan_store_lowlevel($store); return true; } @@ -556,7 +553,21 @@ function check_form_security_token_ForbiddenOnErr($typename = '', $formname = 'f // var $contact_id = xchan_hash of connection function init_groups_visitor($contact_id) { - $groups = array(); + $groups = []; + + // private profiles are treated as a virtual group + + $r = q("SELECT abook_profile from abook where abook_xchan = '%s' and abook_profile != '' ", + dbesc($contact_id) + ); + if($r) { + foreach($r as $rv) { + $groups[] = 'vp.' . $rv['abook_profile']; + } + } + + // physical groups this channel is a member of + $r = q("SELECT hash FROM groups left join group_member on groups.id = group_member.gid WHERE xchan = '%s' ", dbesc($contact_id) ); diff --git a/include/text.php b/include/text.php index b25eb8e46..eb8147f9a 100644 --- a/include/text.php +++ b/include/text.php @@ -4,7 +4,7 @@ */ require_once("include/bbcode.php"); -require_once('include/hubloc.php'); + // random string, there are 86 characters max in text mode, 128 for hex // output is urlsafe @@ -586,8 +586,10 @@ function photo_new_resource() { * @return boolean true if found */ function attribute_contains($attr, $s) { + // remove quotes + $attr = str_replace([ '"',"'" ],['',''],$attr); $a = explode(' ', $attr); - if(count($a) && in_array($s, $a)) + if($a && in_array($s, $a)) return true; return false; @@ -655,12 +657,28 @@ function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { */ function btlogger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { + if(! defined('BTLOGGER_DEBUG_FILE')) + define('BTLOGGER_DEBUG_FILE','btlogger.out'); + logger($msg, $level, $priority); + + if(file_exists(BTLOGGER_DEBUG_FILE) && is_writable(BTLOGGER_DEBUG_FILE)) { + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; + $s = datetime_convert() . ':' . log_priority_str($priority) . ':' . session_id() . ':' . $where . $msg . PHP_EOL; + @file_put_contents(BTLOGGER_DEBUG_FILE, $s, FILE_APPEND); + } + if(version_compare(PHP_VERSION, '5.4.0') >= 0) { $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); if($stack) { for($x = 1; $x < count($stack); $x ++) { - logger('stack: ' . basename($stack[$x]['file']) . ':' . $stack[$x]['line'] . ':' . $stack[$x]['function'] . '()',$level, $priority); + $s = 'stack: ' . basename($stack[$x]['file']) . ':' . $stack[$x]['line'] . ':' . $stack[$x]['function'] . '()'; + logger($s,$level, $priority); + + if(file_exists(BTLOGGER_DEBUG_FILE) && is_writable(BTLOGGER_DEBUG_FILE)) { + @file_put_contents(BTLOGGER_DEBUG_FILE, $s . PHP_EOL, FILE_APPEND); + } } } } @@ -1565,11 +1583,6 @@ function prepare_body(&$item,$attach = false) { $photo = $prep_arr['photo']; $event = $prep_arr['event']; -// q("update item set html = '%s' where id = %d", -// dbesc($s), -// intval($item['id']) -// ); - if(! $attach) { return $s; } @@ -3061,4 +3074,67 @@ function create_table_from_array($table, $arr) { } return $r; -}
\ No newline at end of file +} + + + +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. + * First protect any url inside certain bbcode tags so we don't double link it. + */ + + $body = preg_replace_callback('/\[code(.*?)\[\/(code)\]/ism','\red_escape_codeblock',$body); + $body = preg_replace_callback('/\[url(.*?)\[\/(url)\]/ism','\red_escape_codeblock',$body); + $body = preg_replace_callback('/\[zrl(.*?)\[\/(zrl)\]/ism','\red_escape_codeblock',$body); + + + $body = preg_replace_callback("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\\ ++\,\(\)]+)/ism", '\nakedoembed', $body); + $body = preg_replace_callback("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\\ ++\,\(\)]+)/ism", '\red_zrl_callback', $body); + + $body = preg_replace_callback('/\[\$b64zrl(.*?)\[\/(zrl)\]/ism','\red_unescape_codeblock',$body); + $body = preg_replace_callback('/\[\$b64url(.*?)\[\/(url)\]/ism','\red_unescape_codeblock',$body); + $body = preg_replace_callback('/\[\$b64code(.*?)\[\/(code)\]/ism','\red_unescape_codeblock',$body); + + // fix any img tags that should be zmg + + $body = preg_replace_callback('/\[img(.*?)\](.*?)\[\/img\]/ism','\red_zrlify_img_callback',$body); + + + $body = bb_translate_video($body); + + /** + * Fold multi-line [code] sequences + */ + + $body = preg_replace('/\[\/code\]\s*\[code\]/ism',"\n",$body); + + $body = scale_external_images($body,false); + + + return $body; + +} + +function gen_link_id($mid) { + if(strpbrk($mid,':/&?<>"\'') !== false) + return 'b64.' . base64url_encode($mid); + return $mid; +} + + +// callback for array_walk + +function array_trim(&$v,$k) { + $v = trim($v); +} + +function array_escape_tags(&$v,$k) { + $v = escape_tags($v); +} + diff --git a/include/widgets.php b/include/widgets.php index dde8e7cd0..04cf0d30b 100644 --- a/include/widgets.php +++ b/include/widgets.php @@ -96,19 +96,6 @@ function widget_collections($args) { return group_side($every, $each, $edit, $current, $abook_id, $wmode); } - -function widget_appselect($arr) { - return replace_macros(get_markup_template('app_select.tpl'),array( - '$title' => t('Apps'), - '$system' => t('System'), - '$authed' => ((local_channel()) ? true : false), - '$personal' => t('Personal'), - '$new' => t('New App'), - '$edit' => t('Edit App') - )); -} - - function widget_suggestions($arr) { if((! local_channel()) || (! feature_enabled(local_channel(),'suggest'))) @@ -441,11 +428,13 @@ function widget_appcategories($arr) { if(! local_channel()) return ''; - $cat = ((x($_REQUEST,'cat')) ? htmlspecialchars($_REQUEST['cat'],ENT_COMPAT,'UTF-8') : ''); - $srchurl = App::$query_string; + $selected = ((x($_REQUEST,'cat')) ? htmlspecialchars($_REQUEST['cat'],ENT_COMPAT,'UTF-8') : ''); + $srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is','',$srchurl),'&'); $srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl); + $srchurl = z_root() . '/apps'; + $terms = array(); $r = q("select distinct(term.term) @@ -453,6 +442,7 @@ function widget_appcategories($arr) { where app_channel = %d and term.uid = app_channel and term.otype = %d + and term.term != 'nav_featured_app' order by term.term asc", intval(local_channel()), intval(TERM_OBJ_APP) @@ -518,9 +508,16 @@ function widget_affinity($arr) { if(! local_channel()) return ''; - - $cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : 0); - $cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : 99); + + // Get default cmin value from pconfig, but allow GET parameter to override + $cmin = intval(get_pconfig(local_channel(),'affinity','cmin')); + $cmin = (($cmin) ? $cmin : 0); + $cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : $cmin); + + // Get default cmax value from pconfig, but allow GET parameter to override + $cmax = intval(get_pconfig(local_channel(),'affinity','cmax')); + $cmax = (($cmax) ? $cmax : 99); + $cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : $cmax); if(feature_enabled(local_channel(),'affinity')) { @@ -650,6 +647,14 @@ function widget_settings_menu($arr) { ); } + if(feature_enabled(local_channel(),'permcats')) { + $tabs[] = array( + 'label' => t('Permission Groups'), + 'url' => z_root() . '/settings/permcats', + 'selected' => ((argv(1) === 'permcats') ? 'active' : ''), + ); + } + if($role === false || $role === 'custom') { $tabs[] = array( @@ -898,9 +903,11 @@ function widget_chatroom_members() { } function widget_wiki_list($arr) { - require_once("include/wiki.php"); + $channel = channelx_by_n(App::$profile_uid); - $wikis = wiki_list($channel, get_observer_hash()); + + $wikis = Zotlabs\Lib\NativeWiki::listwikis($channel,get_observer_hash()); + if($wikis) { return replace_macros(get_markup_template('wikilist_widget.tpl'), array( '$header' => t('Wiki List'), @@ -913,8 +920,9 @@ function widget_wiki_list($arr) { function widget_wiki_pages($arr) { - require_once("include/wiki.php"); $channelname = ((array_key_exists('channel',$arr)) ? $arr['channel'] : ''); + $c = channelx_by_nick($channelname); + $wikiname = ''; if (array_key_exists('refresh', $arr)) { $not_refresh = (($arr['refresh']=== true) ? false : true); @@ -922,11 +930,12 @@ function widget_wiki_pages($arr) { $not_refresh = true; } $pages = array(); - if (!array_key_exists('resource_id', $arr)) { + if (! array_key_exists('resource_id', $arr)) { $hide = true; } else { - $p = wiki_page_list($arr['resource_id']); - if ($p['pages']) { + $p = Zotlabs\Lib\NativeWikiPage::page_list($c['channel_id'],get_observer_hash(),$arr['resource_id']); + + if($p['pages']) { $pages = $p['pages']; $w = $p['wiki']; // Wiki item record is $w['wiki'] @@ -936,31 +945,38 @@ function widget_wiki_pages($arr) { } } } - $can_create = perm_is_allowed(\App::$profile['uid'],get_observer_hash(),'write_pages'); + $can_create = perm_is_allowed(\App::$profile['uid'],get_observer_hash(),'write_wiki'); + + $can_delete = ((local_channel() && (local_channel() == \App::$profile['uid'])) ? true : false); return replace_macros(get_markup_template('wiki_page_list.tpl'), array( '$hide' => $hide, + '$resource_id' => $arr['resource_id'], '$not_refresh' => $not_refresh, '$header' => t('Wiki Pages'), '$channel' => $channelname, '$wikiname' => $wikiname, '$pages' => $pages, '$canadd' => $can_create, + '$candel' => $can_delete, '$addnew' => t('Add new page'), '$pageName' => array('pageName', t('Page name')), )); } function widget_wiki_page_history($arr) { - require_once("include/wiki.php"); + $pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : ''); $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); - $pageHistory = wiki_page_history(array('resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); - return replace_macros(get_markup_template('wiki_page_history.tpl'), array( - '$pageHistory' => $pageHistory['history'], - '$permsWrite' => $arr['permsWrite'] + $pageHistory = Zotlabs\Lib\NativeWikiPage::page_history(array('channel_id' => App::$profile_uid, 'observer_hash' => get_observer_hash(), 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); + return replace_macros(get_markup_template('nwiki_page_history.tpl'), array( + '$pageHistory' => $pageHistory['history'], + '$permsWrite' => $arr['permsWrite'], + '$name_lbl' => t('Name'), + '$msg_label' => t('Message','wiki_history') )); + } function widget_bookmarkedchats($arr) { diff --git a/include/wiki.php b/include/wiki.php deleted file mode 100644 index 922be6924..000000000 --- a/include/wiki.php +++ /dev/null @@ -1,664 +0,0 @@ -<?php -/** - * @file include/wiki.php - * @brief Wiki related functions. - */ - -use \Zotlabs\Storage\GitRepo as GitRepo; -define ( 'WIKI_ITEM_RESOURCE_TYPE', 'wiki' ); - -function wiki_list($channel, $observer_hash) { - $sql_extra = item_permissions_sql($channel['channel_id'], $observer_hash); - $wikis = q("SELECT * FROM item WHERE resource_type = '%s' AND mid = parent_mid AND uid = %d AND item_deleted = 0 $sql_extra", - dbesc(WIKI_ITEM_RESOURCE_TYPE), - intval($channel['channel_id']) - ); - if($wikis) { - foreach($wikis as &$w) { - $w['rawName'] = get_iconfig($w, 'wiki', 'rawName'); - $w['htmlName'] = get_iconfig($w, 'wiki', 'htmlName'); - $w['urlName'] = get_iconfig($w, 'wiki', 'urlName'); - $w['path'] = get_iconfig($w, 'wiki', 'path'); - $w['mimeType'] = get_iconfig($w, 'wiki', 'mimeType'); - $w['lock'] = (($w['allow_cid'] || $w['allow_gid'] || $w['deny_cid'] || $w['deny_gid']) ? true : false); - } - } - // TODO: query db for wikis the observer can access. Return with two lists, for read and write access - return array('wikis' => $wikis); -} - -function wiki_page_list($resource_id) { - // TODO: Create item table records for pages so that metadata like title can be applied - $w = wiki_get_wiki($resource_id); - if (!$w['path']) { - return array('pages' => null, 'wiki' => null); - } - - $pages[] = [ - 'resource_id' => '', - 'title' => 'Home', - 'url' => 'Home', - 'link_id' => 'id_wiki_home_0' - ]; - - if (is_dir($w['path']) === true) { - $files = array_diff(scandir($w['path']), array('.', '..', '.git')); - // TODO: Check that the files are all text files - $i = 1; - foreach($files as $file) { - // strip the file extension and unwrap URL encoding to leave HTML encoded name - $title = substr($file, 0, strrpos($file,'.')); - if(urldecode($title) !== 'Home') { - $pages[] = [ - 'resource_id' => $resource_id, - 'title' => urldecode($title), - 'url' => $title, - 'link_id' => 'id_' . substr($resource_id, 0, 10) . '_' . $i - ]; - $i++; - } - } - } - - return array('pages' => $pages, 'wiki' => $w); -} - -function wiki_init_wiki($channel, $wiki) { - // Store the path as a relative path, but pass absolute path to mkdir - $path = 'store/[data]/git/'.$channel['channel_address'].'/wiki/'.$wiki['urlName']; - if (!os_mkdir(__DIR__ . '/../' . $path, 0770, true)) { - logger('Error creating wiki path: ' . $path); - return null; - } - // Create GitRepo object - $git = new GitRepo($channel['channel_address'], null, false, $wiki['urlName'], __DIR__ . '/../' . $path); - if(!$git->initRepo()) { - logger('Error creating new git repo in ' . $git->path); - return null; - } - - return array('path' => $path); -} - -function wiki_create_wiki($channel, $observer_hash, $wiki, $acl) { - $wikiinit = wiki_init_wiki($channel, $wiki); - if (!$wikiinit['path']) { - notice('Error creating wiki'); - return array('item' => null, 'success' => false); - } - $path = $wikiinit['path']; - // Generate unique resource_id using the same method as item_message_id() - do { - $dups = false; - $resource_id = random_string(); - $r = q("SELECT mid FROM item WHERE resource_id = '%s' AND resource_type = '%s' AND uid = %d LIMIT 1", - dbesc($resource_id), - dbesc(WIKI_ITEM_RESOURCE_TYPE), - intval($channel['channel_id']) - ); - if (count($r)) - $dups = true; - } while ($dups == true); - $ac = $acl->get(); - $mid = item_message_id(); - $arr = array(); // Initialize the array of parameters for the post - $item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0); - $wiki_url = z_root() . '/wiki/' . $channel['channel_address'] . '/' . $wiki['urlName']; - $arr['aid'] = $channel['channel_account_id']; - $arr['uid'] = $channel['channel_id']; - $arr['mid'] = $mid; - $arr['parent_mid'] = $mid; - $arr['item_hidden'] = $item_hidden; - $arr['resource_type'] = WIKI_ITEM_RESOURCE_TYPE; - $arr['resource_id'] = $resource_id; - $arr['owner_xchan'] = $channel['channel_hash']; - $arr['author_xchan'] = $observer_hash; - $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $arr['mid']; - $arr['llink'] = $arr['plink']; - $arr['title'] = $wiki['htmlName']; // name of new wiki; - $arr['allow_cid'] = $ac['allow_cid']; - $arr['allow_gid'] = $ac['allow_gid']; - $arr['deny_cid'] = $ac['deny_cid']; - $arr['deny_gid'] = $ac['deny_gid']; - $arr['item_wall'] = 1; - $arr['item_origin'] = 1; - $arr['item_thread_top'] = 1; - $arr['item_private'] = intval($acl->is_private()); - $arr['verb'] = ACTIVITY_CREATE; - $arr['obj_type'] = ACTIVITY_OBJ_WIKI; - $arr['body'] = '[table][tr][td][h1]New Wiki[/h1][/td][/tr][tr][td][zrl=' . $wiki_url . ']' . $wiki['htmlName'] . '[/zrl][/td][/tr][/table]'; - // Save the path using iconfig. The file path should not be shared with other hubs - if (!set_iconfig($arr, 'wiki', 'path', $path, false)) { - return array('item' => null, 'success' => false); - } - // Save the wiki name information using iconfig. This is shareable. - if (!set_iconfig($arr, 'wiki', 'rawName', $wiki['rawName'], true)) { - return array('item' => null, 'success' => false); - } - if (!set_iconfig($arr, 'wiki', 'htmlName', $wiki['htmlName'], true)) { - return array('item' => null, 'success' => false); - } - if (!set_iconfig($arr, 'wiki', 'urlName', $wiki['urlName'], true)) { - return array('item' => null, 'success' => false); - } - if (!set_iconfig($arr, 'wiki', 'mimeType', $wiki['mimeType'], true)) { - return array('item' => null, 'success' => false); - } - $post = item_store($arr); - $item_id = $post['item_id']; - - if ($item_id) { - \Zotlabs\Daemon\Master::Summon(array('Notifier', 'activity', $item_id)); - return array('item' => $post['item'], 'success' => true); - } else { - return array('item' => null, 'success' => false); - } -} - -function wiki_delete_wiki($resource_id) { - - $w = wiki_get_wiki($resource_id); - $item = $w['wiki']; - if (!$item || !$w['path']) { - return array('item' => null, 'success' => false); - } else { - $drop = drop_item($item['id'], false, DROPITEM_NORMAL, true); - $pathdel = rrmdir($w['path']); - if ($pathdel) { - info('Wiki files deleted successfully'); - } - return array('item' => $item, 'success' => (($drop === 1 && $pathdel) ? true : false)); - } -} - -function wiki_get_wiki($resource_id) { - $item = q("SELECT * FROM item WHERE resource_type = '%s' AND resource_id = '%s' AND item_deleted = 0 limit 1", - dbesc(WIKI_ITEM_RESOURCE_TYPE), - dbesc($resource_id) - ); - if (!$item) { - return array('wiki' => null, 'path' => null); - } else { - $w = $item[0]; // wiki item table record - // Get wiki metadata - $rawName = get_iconfig($w, 'wiki', 'rawName'); - $htmlName = get_iconfig($w, 'wiki', 'htmlName'); - $urlName = get_iconfig($w, 'wiki', 'urlName'); - $mimeType = get_iconfig($w, 'wiki', 'mimeType'); - - $path = get_iconfig($w, 'wiki', 'path'); - if (!realpath(__DIR__ . '/../' . $path)) { - return array('wiki' => null, 'path' => null); - } - // Path to wiki exists - $abs_path = realpath(__DIR__ . '/../' . $path); - return array( - 'wiki' => $w, - 'path' => $abs_path, - 'rawName' => $rawName, - 'htmlName' => $htmlName, - 'urlName' => $urlName, - 'mimeType' => $mimeType - ); - } -} - -function wiki_exists_by_name($uid, $urlName) { - $item = q("SELECT id,resource_id FROM item WHERE resource_type = '%s' AND title = '%s' AND uid = '%s' AND item_deleted = 0 limit 1", - dbesc(WIKI_ITEM_RESOURCE_TYPE), - dbesc(escape_tags(urldecode($urlName))), - dbesc($uid) - ); - if (!$item) { - return array('id' => null, 'resource_id' => null); - } else { - return array('id' => $item[0]['id'], 'resource_id' => $item[0]['resource_id']); - } -} - -function wiki_get_permissions($resource_id, $owner_id, $observer_hash) { - // TODO: For now, only the owner can edit - $sql_extra = item_permissions_sql($owner_id, $observer_hash); - - if(local_channel() && local_channel == $owner_id) { - return [ 'read' => true, 'write' => true, 'success' => true ]; - } - - $r = q("SELECT * FROM item WHERE uid = %d and resource_type = '%s' AND resource_id = '%s' $sql_extra LIMIT 1", - intval($owner_id), - dbesc(WIKI_ITEM_RESOURCE_TYPE), - dbesc($resource_id) - ); - - if (!$r) { - return array('read' => false, 'write' => false, 'success' => true); - } else { - // TODO: Create a new permission setting for wiki analogous to webpages. Until - // then, use webpage permissions - $write = perm_is_allowed($owner_id, $observer_hash,'write_pages'); - return array('read' => true, 'write' => $write, 'success' => true); - } -} - -function wiki_create_page($name, $resource_id) { - $w = wiki_get_wiki($resource_id); - if (!$w['path']) { - return array('page' => null, 'wiki' => null, 'message' => 'Wiki not found.', 'success' => false); - } - - $page = array('rawName' => $name, 'htmlName' => escape_tags($name), 'urlName' => urlencode(escape_tags($name)), 'fileName' => urlencode(escape_tags($name)) . wiki_get_file_ext($w)); - $page_path = $w['path'] . '/' . $page['fileName']; - if (is_file($page_path)) { - return array('page' => null, 'wiki' => null, 'message' => 'Page already exists.', 'success' => false); - } - // Create the page file in the wiki repo - if(!touch($page_path)) { - return array('page' => null, 'wiki' => null, 'message' => 'Page file cannot be created.', 'success' => false); - } else { - return array('page' => $page, 'wiki' => $w, 'message' => '', 'success' => true); - } - -} - -function wiki_rename_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $pageNewName = ((array_key_exists('pageNewName',$arr)) ? $arr['pageNewName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $w = wiki_get_wiki($resource_id); - if (!$w['path']) { - return array('message' => 'Wiki not found.', 'success' => false); - } - $page_path_old = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); - if (!is_readable($page_path_old) === true) { - return array('message' => 'Cannot read wiki page: ' . $page_path_old, 'success' => false); - } - $page = array('rawName' => $pageNewName, 'htmlName' => escape_tags($pageNewName), 'urlName' => urlencode(escape_tags($pageNewName)), 'fileName' => urlencode(escape_tags($pageNewName)) . wiki_get_file_ext($w)); - $page_path_new = $w['path'] . '/' . $page['fileName'] ; - if (is_file($page_path_new)) { - return array('message' => 'Page already exists.', 'success' => false); - } - // Rename the page file in the wiki repo - if(!rename($page_path_old, $page_path_new)) { - return array('message' => 'Error renaming page file.', 'success' => false); - } else { - return array('page' => $page, 'message' => '', 'success' => true); - } - -} - -function wiki_get_page_content($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $w = wiki_get_wiki($resource_id); - if (!$w['path']) { - return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); - } - $page_path = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); - if (is_readable($page_path) === true) { - if(filesize($page_path) === 0) { - $content = ''; - } else { - $content = file_get_contents($page_path); - if(!$content) { - return array('content' => null, 'message' => 'Error reading page content', 'success' => false); - } - } - // TODO: Check that the files are all text files - return array('content' => json_encode($content), 'mimeType' => $w['mimeType'], 'message' => '', 'success' => true); - } -} - -function wiki_page_history($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $w = wiki_get_wiki($resource_id); - if (!$w['path']) { - return array('history' => null, 'message' => 'Error reading wiki', 'success' => false); - } - $page_path = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); - if (!is_readable($page_path) === true) { - return array('history' => null, 'message' => 'Cannot read wiki page: ' . $page_path, 'success' => false); - } - $reponame = ((array_key_exists('title', $w['wiki'])) ? $w['wiki']['title'] : 'repo'); - if($reponame === '') { - $reponame = 'repo'; - } - $git = new GitRepo('', null, false, $w['wiki']['title'], $w['path']); - try { - $gitlog = $git->git->log('', $page_path , array('limit' => 500)); - return array('history' => $gitlog, 'message' => '', 'success' => true); - } catch (\PHPGit\Exception\GitException $e) { - return array('history' => null, 'message' => 'GitRepo error thrown', 'success' => false); - } -} - -function wiki_prepare_content($s) { - - $text = preg_replace_callback('{ - (?:\n\n|\A\n?) - ( # $1 = the code block -- one or more lines, starting with a space/tab - (?> - [ ]{'.'4'.'} # Lines must start with a tab or a tab-width of spaces - .*\n+ - )+ - ) - ((?=^[ ]{0,'.'4'.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc - }xm', - 'wiki_prepare_content_callback', $s); - - return $text; -} - -function wiki_prepare_content_callback($matches) { - $codeblock = $matches[1]; - - $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES, UTF8, false); - return "\n\n" . $codeblock ; -} - - - -function wiki_save_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $content = ((array_key_exists('content',$arr)) ? purify_html(wiki_prepare_content($arr['content'])) : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $w = wiki_get_wiki($resource_id); - if (!$w['path']) { - return array('message' => 'Error reading wiki', 'success' => false); - } - - $fileName = $pageUrlName . wiki_get_file_ext($w); - $page_path = $w['path'] . '/' . $fileName; - if (is_writable($page_path) === true) { - if(!file_put_contents($page_path, $content)) { - return array('message' => 'Error writing to page file', 'success' => false); - } - return array('message' => '', 'filename' => $filename, 'success' => true); - } else { - return array('message' => 'Page file not writable', 'success' => false); - } -} - -function wiki_delete_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $w = wiki_get_wiki($resource_id); - if (!$w['path']) { - return array('message' => 'Error reading wiki', 'success' => false); - } - $page_path = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); - if (is_writable($page_path) === true) { - if(!unlink($page_path)) { - return array('message' => 'Error deleting page file', 'success' => false); - } - return array('message' => '', 'success' => true); - } else { - return array('message' => 'Page file not writable', 'success' => false); - } -} - -function wiki_revert_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $commitHash = ((array_key_exists('commitHash',$arr)) ? $arr['commitHash'] : null); - if (! $commitHash) { - return array('content' => $content, 'message' => 'No commit was provided', 'success' => false); - } - $w = wiki_get_wiki($resource_id); - if (!$w['path']) { - return array('content' => $content, 'message' => 'Error reading wiki', 'success' => false); - } - $page_path = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); - if (is_writable($page_path) === true) { - - $reponame = ((array_key_exists('title', $w['wiki'])) ? urlencode($w['wiki']['title']) : 'repo'); - if($reponame === '') { - $reponame = 'repo'; - } - $git = new GitRepo($observer['xchan_addr'], null, false, $w['wiki']['title'], $w['path']); - $content = null; - try { - $git->setIdentity($observer['xchan_name'], $observer['xchan_addr']); - foreach ($git->git->tree($commitHash) as $object) { - if ($object['type'] == 'blob' && $object['file'] === $pageUrlName . wiki_get_file_ext($w)) { - $content = $git->git->cat->blob($object['hash']); - } - } - } catch (\PHPGit\Exception\GitException $e) { - return array('content' => $content, 'message' => 'GitRepo error thrown', 'success' => false); - } - return array('content' => $content, 'message' => '', 'success' => true); - } else { - return array('content' => $content, 'message' => 'Page file not writable', 'success' => false); - } -} - -function wiki_compare_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $currentCommit = ((array_key_exists('currentCommit',$arr)) ? $arr['currentCommit'] : 'HEAD'); - $compareCommit = ((array_key_exists('compareCommit',$arr)) ? $arr['compareCommit'] : null); - if (! $compareCommit) { - return array('message' => 'No compare commit was provided', 'success' => false); - } - $w = wiki_get_wiki($resource_id); - if (!$w['path']) { - return array('message' => 'Error reading wiki', 'success' => false); - } - $page_path = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); - if (is_readable($page_path) === true) { - $reponame = ((array_key_exists('title', $w['wiki'])) ? urlencode($w['wiki']['title']) : 'repo'); - if($reponame === '') { - $reponame = 'repo'; - } - $git = new GitRepo('', null, false, $w['wiki']['title'], $w['path']); - $compareContent = $currentContent = ''; - try { - foreach ($git->git->tree($currentCommit) as $object) { - if ($object['type'] == 'blob' && $object['file'] === $pageUrlName . wiki_get_file_ext($w)) { - $currentContent = $git->git->cat->blob($object['hash']); - } - } - foreach ($git->git->tree($compareCommit) as $object) { - if ($object['type'] == 'blob' && $object['file'] === $pageUrlName . wiki_get_file_ext($w)) { - $compareContent = $git->git->cat->blob($object['hash']); - } - } - require_once('library/class.Diff.php'); - $diff = Diff::toTable(Diff::compare($currentContent, $compareContent)); - } catch (\PHPGit\Exception\GitException $e) { - return array('message' => 'GitRepo error thrown', 'success' => false); - } - return array('diff' => $diff, 'message' => '', 'success' => true); - } else { - return array('message' => 'Page file not writable', 'success' => false); - } -} - -function wiki_git_commit($arr) { - $files = ((array_key_exists('files', $arr)) ? $arr['files'] : null); - $all = ((array_key_exists('all', $arr)) ? $arr['all'] : false); - $commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : 'Repo updated'); - if(array_key_exists('resource_id', $arr)) { - $resource_id = $arr['resource_id']; - } else { - return array('message' => 'Wiki resource_id required for git commit', 'success' => false); - } - if(array_key_exists('observer', $arr)) { - $observer = $arr['observer']; - } else { - return array('message' => 'Observer required for git commit', 'success' => false); - } - $w = wiki_get_wiki($resource_id); - if (!$w['path']) { - return array('message' => 'Error reading wiki', 'success' => false); - } - $reponame = ((array_key_exists('title', $w['wiki'])) ? urlencode($w['wiki']['title']) : 'repo'); - if($reponame === '') { - $reponame = 'repo'; - } - $git = new GitRepo($observer['xchan_addr'], null, false, $w['wiki']['title'], $w['path']); - try { - $git->setIdentity($observer['xchan_name'], $observer['xchan_addr']); - if ($files === null) { - $options = array('all' => true); // git commit option to include all changes - } else { - $options = array('all' => $all); // git commit options\ - foreach ($files as $file) { - if (!$git->git->add($file)) { // add specified files to the git repo stage - if (!$git->git->reset->hard()) { - return array('message' => 'Error adding file to git stage: ' . $file . '. Error resetting git repo.', 'success' => false); - } - return array('message' => 'Error adding file to git stage: ' . $file, 'success' => false); - } - } - } - if ($git->commit($commit_msg, $options)) { - return array('message' => 'Wiki repo commit succeeded', 'success' => true); - } else { - return array('message' => 'Wiki repo commit failed', 'success' => false); - } - } catch (\PHPGit\Exception\GitException $e) { - return array('message' => 'GitRepo error thrown', 'success' => false); - } -} - -function wiki_convert_links($s, $wikiURL) { - - if (strpos($s,'[[') !== false) { - preg_match_all("/\[\[(.*?)\]\]/", $s, $match); - $pages = $pageURLs = array(); - foreach ($match[1] as $m) { - // TODO: Why do we need to double urlencode for this to work? - $pageURLs[] = urlencode(urlencode(escape_tags($m))); - $pages[] = $m; - } - $idx = 0; - while(strpos($s,'[[') !== false) { - $replace = '<a href="'.$wikiURL.'/'.$pageURLs[$idx].'">'.$pages[$idx].'</a>'; - $s = preg_replace("/\[\[(.*?)\]\]/", $replace, $s, 1); - $idx++; - } - } - return $s; -} - -/** - * Replace the instances of the string [toc] with a list element that will be populated by - * a table of contents by the JavaScript library - * @param string $s - * @return string - */ -function wiki_generate_toc($s) { - if (strpos($s,'[toc]') !== false) { - //$toc_md = wiki_toc($s); // Generate Markdown-formatted list prior to HTML render - $toc_md = '<ul id="wiki-toc"></ul>'; // use the available jQuery plugin http://ndabas.github.io/toc/ - $s = preg_replace("/\[toc\]/", $toc_md, $s, -1); - } - return $s; -} - -/** - * Converts a select set of bbcode tags. Much of the code is copied from include/bbcode.php - * @param string $s - * @return string - */ -function wiki_bbcode($s) { - - $s = str_replace(array('[baseurl]', '[sitename]'), array(z_root(), get_config('system', 'sitename')), $s); - - $observer = App::get_observer(); - if ($observer) { - $s1 = '<span class="bb_observer" title="' . t('Different viewers will see this text differently') . '">'; - $s2 = '</span>'; - $obsBaseURL = $observer['xchan_connurl']; - $obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL); - $s = str_replace('[observer.baseurl]', $obsBaseURL, $s); - $s = str_replace('[observer.url]', $observer['xchan_url'], $s); - $s = str_replace('[observer.name]', $s1 . $observer['xchan_name'] . $s2, $s); - $s = str_replace('[observer.address]', $s1 . $observer['xchan_addr'] . $s2, $s); - $s = str_replace('[observer.webname]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $s); - $s = str_replace('[observer.photo]', '', $s); - } else { - $s = str_replace('[observer.baseurl]', '', $s); - $s = str_replace('[observer.url]', '', $s); - $s = str_replace('[observer.name]', '', $s); - $s = str_replace('[observer.address]', '', $s); - $s = str_replace('[observer.webname]', '', $s); - $s = str_replace('[observer.photo]', '', $s); - } - - return $s; -} - -function wiki_get_file_ext($arr) { - if($arr['mimeType'] == 'text/bbcode') - return '.bb'; - else - return '.md'; -} - -// This function is derived from -// http://stackoverflow.com/questions/32068537/generate-table-of-contents-from-markdown-in-php -function wiki_toc($content) { - // ensure using only "\n" as line-break - $source = str_replace(["\r\n", "\r"], "\n", $content); - - // look for markdown TOC items - preg_match_all( - '/^(?:=|-|#).*$/m', - $source, - $matches, - PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE - ); - - // preprocess: iterate matched lines to create an array of items - // where each item is an array(level, text) - $file_size = strlen($source); - foreach ($matches[0] as $item) { - $found_mark = substr($item[0], 0, 1); - if ($found_mark == '#') { - // text is the found item - $item_text = $item[0]; - $item_level = strrpos($item_text, '#') + 1; - $item_text = substr($item_text, $item_level); - } else { - // text is the previous line (empty if <hr>) - $item_offset = $item[1]; - $prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2)); - $item_text = - substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1); - $item_text = trim($item_text); - $item_level = $found_mark == '=' ? 1 : 2; - } - if (!trim($item_text) OR strpos($item_text, '|') !== FALSE) { - // item is an horizontal separator or a table header, don't mind - continue; - } - $raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)]; - } - $o = ''; - foreach($raw_toc as $t) { - $level = intval($t['level']); - $text = $t['text']; - switch ($level) { - case 1: - $li = '* '; - break; - case 2: - $li = ' * '; - break; - case 3: - $li = ' * '; - break; - case 4: - $li = ' * '; - break; - default: - $li = '* '; - break; - } - $o .= $li . $text . "\n"; - } - return $o; -} diff --git a/include/xchan.php b/include/xchan.php new file mode 100644 index 000000000..12eb674fa --- /dev/null +++ b/include/xchan.php @@ -0,0 +1,139 @@ +<?php + + +function xchan_store_lowlevel($arr) { + + $store = [ + 'xchan_hash' => ((array_key_exists('xchan_hash',$arr)) ? $arr['xchan_hash'] : ''), + 'xchan_guid' => ((array_key_exists('xchan_guid',$arr)) ? $arr['xchan_guid'] : ''), + 'xchan_guid_sig' => ((array_key_exists('xchan_guid_sig',$arr)) ? $arr['xchan_guid_sig'] : ''), + 'xchan_pubkey' => ((array_key_exists('xchan_pubkey',$arr)) ? $arr['xchan_pubkey'] : ''), + 'xchan_photo_mimetype' => ((array_key_exists('xchan_photo_mimetype',$arr)) ? $arr['xchan_photo_mimetype'] : ''), + 'xchan_photo_l' => ((array_key_exists('xchan_photo_l',$arr)) ? $arr['xchan_photo_l'] : ''), + 'xchan_photo_m' => ((array_key_exists('xchan_photo_m',$arr)) ? $arr['xchan_photo_m'] : ''), + 'xchan_photo_s' => ((array_key_exists('xchan_photo_s',$arr)) ? $arr['xchan_photo_s'] : ''), + 'xchan_addr' => ((array_key_exists('xchan_addr',$arr)) ? $arr['xchan_addr'] : ''), + 'xchan_url' => ((array_key_exists('xchan_url',$arr)) ? $arr['xchan_url'] : ''), + 'xchan_connurl' => ((array_key_exists('xchan_connurl',$arr)) ? $arr['xchan_connurl'] : ''), + 'xchan_follow' => ((array_key_exists('xchan_follow',$arr)) ? $arr['xchan_follow'] : ''), + 'xchan_connpage' => ((array_key_exists('xchan_connpage',$arr)) ? $arr['xchan_connpage'] : ''), + 'xchan_name' => ((array_key_exists('xchan_name',$arr)) ? $arr['xchan_name'] : ''), + 'xchan_network' => ((array_key_exists('xchan_network',$arr)) ? $arr['xchan_network'] : ''), + 'xchan_instance_url' => ((array_key_exists('xchan_instance_url',$arr)) ? $arr['xchan_instance_url'] : ''), + 'xchan_flags' => ((array_key_exists('xchan_flags',$arr)) ? intval($arr['xchan_flags']) : 0), + 'xchan_photo_date' => ((array_key_exists('xchan_photo_date',$arr)) ? datetime_convert('UTC','UTC',$arr['xchan_photo_date']) : NULL_DATE), + 'xchan_name_date' => ((array_key_exists('xchan_name_date',$arr)) ? datetime_convert('UTC','UTC',$arr['xchan_name_date']) : NULL_DATE), + 'xchan_hidden' => ((array_key_exists('xchan_hidden',$arr)) ? intval($arr['xchan_hidden']) : 0), + 'xchan_orphan' => ((array_key_exists('xchan_orphan',$arr)) ? intval($arr['xchan_orphan']) : 0), + 'xchan_censored' => ((array_key_exists('xchan_censored',$arr)) ? intval($arr['xchan_censored']) : 0), + 'xchan_selfcensored' => ((array_key_exists('xchan_selfcensored',$arr)) ? intval($arr['xchan_selfcensored']) : 0), + 'xchan_system' => ((array_key_exists('xchan_system',$arr)) ? intval($arr['xchan_system']) : 0), + 'xchan_pubforum' => ((array_key_exists('xchan_pubforum',$arr)) ? intval($arr['xchan_pubforum']) : 0), + 'xchan_deleted' => ((array_key_exists('xchan_deleted',$arr)) ? intval($arr['xchan_deleted']) : 0) + ]; + + return create_table_from_array('xchan',$store); +} + + + +function xchan_store($arr) { + + logger('xchan_store: ' . print_r($arr,true)); + + if(! $arr['hash']) + $arr['hash'] = $arr['guid']; + if(! $arr['hash']) + return false; + + $r = q("select * from xchan where xchan_hash = '%s' limit 1", + dbesc($arr['hash']) + ); + if($r) + return true; + + if(! $arr['network']) + $arr['network'] = 'unknown'; + if(! $arr['name']) + $arr['name'] = 'unknown'; + if(! $arr['url']) + $arr['url'] = z_root(); + if(! $arr['photo']) + $arr['photo'] = z_root() . '/' . get_default_profile_photo(); + + + if($arr['network'] === 'zot') { + if((! $arr['key']) || (! rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$arr['key']))) { + logger('Unable to verify signature for ' . $arr['hash']); + return false; + } + } + + $x = []; + foreach($arr as $k => $v) { + if($k === 'key') { + $x['xchan_pubkey'] = $v; + continue; + } + if($k === 'photo') { + continue; + } + + $x['xchan_' . $k] = $v; + } + + $x['xchan_name_date'] = datetime_convert(); + + $r = xchan_store_lowlevel($x); + + if(! $r) + return $r; + + $photos = import_xchan_photo($arr['photo'],$arr['hash']); + $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", + dbesc(datetime_convert()), + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($arr['hash']) + ); + return $r; + +} + + +function xchan_fetch($arr) { + + $key = ''; + if($arr['hash']) { + $key = 'xchan_hash'; + $v = $arr['hash']; + } + elseif($arr['guid']) { + $key = 'xchan_guid'; + $v = $arr['guid']; + } + elseif($arr['address']) { + $key = 'xchan_addr'; + $v = $arr['address']; + } + + if(! $key) + return false; + + $r = q("select * from xchan where $key = '$v' limit 1"); + if(! $r) + return false; + + $ret = array(); + foreach($r[0] as $k => $v) { + if($k === 'xchan_addr') + $ret['address'] = $v; + else + $ret[str_replace('xchan_','',$k)] = $v; + } + return $ret; +} + + diff --git a/include/zid.php b/include/zid.php index ac6433e4c..f5df1c611 100644 --- a/include/zid.php +++ b/include/zid.php @@ -170,7 +170,7 @@ function zidify_text($s) { * @return string */ function red_zrl_callback($matches) { - require_once('include/hubloc.php'); + $zrl = is_matrix_url($matches[2]); $t = strip_zids($matches[2]); @@ -215,7 +215,7 @@ function red_unescape_codeblock($m) { function red_zrlify_img_callback($matches) { - require_once('include/hubloc.php'); + $zrl = is_matrix_url($matches[2]); $t = strip_zids($matches[2]); diff --git a/include/zot.php b/include/zot.php index d4e75fe91..9fa2f98a8 100644 --- a/include/zot.php +++ b/include/zot.php @@ -10,7 +10,6 @@ require_once('include/crypto.php'); require_once('include/items.php'); -require_once('include/hubloc.php'); require_once('include/queue_fn.php'); require_once('include/perm_upgrade.php'); @@ -169,6 +168,11 @@ function zot_best_algorithm($methods) { if(\Zotlabs\Lib\System::get_server_role() !== 'pro') return 'aes256cbc'; + $x = [ 'methods' => $methods, 'result' => '' ]; + call_hooks('zot_best_algorithm',$x); + if($x['result']) + return $x['result']; + if($methods) { $x = explode(',',$methods); if($x) { @@ -413,28 +417,12 @@ function zot_refresh($them, $channel = null, $force = false) { } else { - // new connection - - $my_perms = null; - $automatic = false; + $p = \Zotlabs\Access\Permissions::connect_perms($channel['channel_id']); - $role = get_pconfig($channel['channel_id'],'system','permissions_role'); - if($role) { - $xx = \Zotlabs\Access\PermissionRoles::role_perms($role); - if($xx['perms_auto']) { - $automatic = true; - $default_perms = $xx['perms_connect']; - $my_perms = \Zotlabs\Access\Permissions::FilledPerms($default_perms); - } - } + $my_perms = $p['perms']; + $automatic = $p['automatic']; - if(! $my_perms) { - $m = \Zotlabs\Access\Permissions::FilledAutoperms($channel['channel_id']); - if($m) { - $automatic = true; - $my_perms = $m; - } - } + // new connection if($my_perms) { foreach($my_perms as $k => $v) { @@ -446,15 +434,17 @@ function zot_refresh($them, $channel = null, $force = false) { if($closeness === false) $closeness = 80; - $y = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_created, abook_updated, abook_dob, abook_pending ) values ( %d, %d, %d, '%s', '%s', '%s', '%s', %d )", - intval($channel['channel_account_id']), - intval($channel['channel_id']), - intval($closeness), - dbesc($x['hash']), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc($next_birthday), - intval(($automatic) ? 0 : 1) + $y = abook_store_lowlevel( + [ + 'abook_account' => intval($channel['channel_account_id']), + 'abook_channel' => intval($channel['channel_id']), + 'abook_closeness' => intval($closeness), + 'abook_xchan' => $x['hash'], + 'abook_created' => datetime_convert(), + 'abook_updated' => datetime_convert(), + 'abook_dob' => $next_birthday, + 'abook_pending' => intval(($automatic) ? 0 : 1) + ] ); if($y) { @@ -563,7 +553,7 @@ function zot_gethub($arr, $multiple = false) { } logger('zot_gethub: not found: ' . print_r($arr,true), LOGGER_DEBUG); - return null; + return false; } /** @@ -754,28 +744,28 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { && ($arr['site']['url'] != z_root())) $arr['searchable'] = false; - $x = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_mimetype, - xchan_photo_l, xchan_addr, xchan_url, xchan_connurl, xchan_follow, xchan_connpage, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_hidden, xchan_selfcensored, xchan_deleted, xchan_pubforum ) - values ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d) ", - dbesc($xchan_hash), - dbesc($arr['guid']), - dbesc($arr['guid_sig']), - dbesc($arr['key']), - dbesc($arr['photo_mimetype']), - dbesc($arr['photo']), - dbesc($arr['address']), - dbesc($arr['url']), - dbesc($arr['connections_url']), - dbesc($arr['follow_url']), - dbesc($arr['connect_url']), - dbesc(($arr['name']) ? $arr['name'] : '-'), - dbesc('zot'), - dbescdate($arr['photo_updated']), - dbescdate($arr['name_updated']), - intval(1 - intval($arr['searchable'])), - intval($arr['adult_content']), - intval($arr['deleted']), - intval($arr['public_forum']) + $x = xchan_store_lowlevel( + [ + 'xchan_hash' => $xchan_hash, + 'xchan_guid' => $arr['guid'], + 'xchan_guid_sig' => $arr['guid_sig'], + 'xchan_pubkey' => $arr['key'], + 'xchan_photo_mimetype' => $arr['photo_mimetype'], + 'xchan_photo_l' => $arr['photo'], + 'xchan_addr' => $arr['address'], + 'xchan_url' => $arr['url'], + 'xchan_connurl' => $arr['connections_url'], + 'xchan_follow' => $arr['follow_url'], + 'xchan_connpage' => $arr['connect_url'], + 'xchan_name' => (($arr['name']) ? $arr['name'] : '-'), + 'xchan_network' => 'zot', + 'xchan_photo_date' => $arr['photo_updated'], + 'xchan_name_date' => $arr['name_updated'], + 'xchan_hidden' => intval(1 - intval($arr['searchable'])), + 'xchan_selfcensored' => $arr['adult_content'], + 'xchan_deleted' => $arr['deleted'], + 'xchan_pubforum' => $arr['public_forum'] + ] ); $what .= 'new_xchan'; @@ -1946,6 +1936,7 @@ function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) { return; } + $x = item_store_update($item); // If we're updating an event that we've saved locally, we store the item info first @@ -2463,22 +2454,25 @@ function sync_locations($sender, $arr, $absolute = false) { ); } logger('sync_locations: new hub: ' . $location['url']); - $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_network, hubloc_primary, hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_updated, hubloc_connected) - values ( '%s','%s','%s','%s', '%s', %d ,'%s','%s','%s','%s','%s','%s','%s')", - dbesc($sender['guid']), - dbesc($sender['guid_sig']), - dbesc($sender['hash']), - dbesc($location['address']), - dbesc('zot'), - intval($location['primary']), - dbesc($location['url']), - dbesc($location['url_sig']), - dbesc($location['host']), - dbesc($location['callback']), - dbesc($location['sitekey']), - dbesc(datetime_convert()), - dbesc(datetime_convert()) + + $r = hubloc_store_lowlevel( + [ + 'hubloc_guid' => $sender['guid'], + 'hubloc_guid_sig' => $sender['guid_sig'], + 'hubloc_hash' => $sender['hash'], + 'hubloc_addr' => $location['address'], + 'hubloc_network' => 'zot', + 'hubloc_primary' => $location['primary'], + 'hubloc_url' => $location['url'], + 'hubloc_url_sig' => $location['url_sig'], + 'hubloc_host' => $location['host'], + 'hubloc_callback' => $location['callback'], + 'hubloc_sitekey' => $location['sitekey'], + 'hubloc_updated' => datetime_convert(), + 'hubloc_connected' => datetime_convert() + ] ); + $what .= 'newhub '; $changed = true; @@ -3168,8 +3162,11 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { if(array_key_exists('menu',$arr) && $arr['menu']) sync_menus($channel,$arr['menu']); - if(array_key_exists('file',$arr) && $arr['file']) - sync_files($channel,$arr['file']); + if(array_key_exists('menu',$arr) && $arr['menu']) + sync_menus($channel,$arr['menu']); + + if(array_key_exists('wiki',$arr) && $arr['wiki']) + sync_items($channel,$arr['wiki'],((array_key_exists('relocate',$arr)) ? $arr['relocate'] : null)); if(array_key_exists('channel',$arr) && is_array($arr['channel']) && count($arr['channel'])) { @@ -3312,10 +3309,12 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { logger('process_channel_sync_delivery: total_feeds service class limit exceeded'); continue; } - q("insert into abook ( abook_xchan, abook_account, abook_channel ) values ('%s', %d, %d ) ", - dbesc($clean['abook_xchan']), - intval($channel['channel_account_id']), - intval($channel['channel_id']) + abook_store_lowlevel( + [ + 'abook_xchan' => $clean['abook_xchan'], + 'abook_account' => $channel['channel_account_id'], + 'abook_channel' => $channel['channel_id'] + ] ); $total_friends ++; if(intval($clean['abook_feed'])) @@ -4048,21 +4047,24 @@ function check_zotinfo($channel,$locations,&$ret) { q("delete from hubloc where hubloc_hash = '%s'", dbesc($channel['channel_hash']) ); - $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_primary, - hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_network ) - values ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s' )", - dbesc($channel['channel_guid']), - dbesc($channel['channel_guid_sig']), - dbesc($channel['channel_hash']), - dbesc(channel_reddress($channel)), - intval(1), - dbesc(z_root()), - dbesc(base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey']))), - dbesc(App::get_hostname()), - dbesc(z_root() . '/post'), - dbesc(get_config('system','pubkey')), - dbesc('zot') + + $r = hubloc_store_lowlevel( + [ + 'hubloc_guid' => $channel['channel_guid'], + 'hubloc_guid_sig' => $channel['channel_guid_sig'], + 'hubloc_hash' => $channel['channel_hash'], + 'hubloc_addr' => channel_reddress($channel), + 'hubloc_network' => 'zot', + 'hubloc_primary' => 1, + 'hubloc_url' => z_root(), + 'hubloc_url_sig' => base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'])), + 'hubloc_host' => App::get_hostname(), + 'hubloc_callback' => z_root() . '/post', + 'hubloc_sitekey' => get_config('system','pubkey'), + 'hubloc_updated' => datetime_convert(), + ] ); + if($r) { $x = zot_encode_locations($channel); if($x) { |