diff options
author | Mario Vavti <mario@mariovavti.com> | 2016-10-12 12:13:44 +0200 |
---|---|---|
committer | Mario Vavti <mario@mariovavti.com> | 2016-10-12 12:13:44 +0200 |
commit | cf547be1d6900d12719a9372cbc1a5d433d31863 (patch) | |
tree | 03a525cf40905adafc21b0c4146e467bad288437 /include | |
parent | 29617737ca2c03c800ebbe4701dc21cf9f25ce22 (diff) | |
parent | 16da1a4e810889448eecbd13d68183a820bcebe2 (diff) | |
download | volse-hubzilla-cf547be1d6900d12719a9372cbc1a5d433d31863.tar.gz volse-hubzilla-cf547be1d6900d12719a9372cbc1a5d433d31863.tar.bz2 volse-hubzilla-cf547be1d6900d12719a9372cbc1a5d433d31863.zip |
Merge branch '1.14RC'
Diffstat (limited to 'include')
32 files changed, 1975 insertions, 598 deletions
diff --git a/include/account.php b/include/account.php index 5c44f13ca..b78c3e56d 100644 --- a/include/account.php +++ b/include/account.php @@ -14,6 +14,13 @@ require_once('include/crypto.php'); require_once('include/channel.php'); +function get_account_by_id($account_id) { + $r = q("select * from account where account_id = %d", + intval($account_id) + ); + return (($r) ? $r[0] : false); +} + function check_account_email($email) { $result = array('error' => false, 'message' => ''); @@ -112,6 +119,7 @@ function create_account($arr) { $flags = ((x($arr,'account_flags')) ? intval($arr['account_flags']) : ACCOUNT_OK); $roles = ((x($arr,'account_roles')) ? intval($arr['account_roles']) : 0 ); $expires = ((x($arr,'expires')) ? intval($arr['expires']) : NULL_DATE); + $techlevel = ((array_key_exists('techlevel',$arr)) ? intval($arr['techlevel']) : intval(get_config('system','techlevel'))); $default_service_class = get_config('system','default_service_class'); @@ -171,16 +179,17 @@ function create_account($arr) { $r = q("INSERT INTO account ( account_parent, account_salt, account_password, account_email, account_language, - account_created, account_flags, account_roles, account_expires, account_service_class ) - VALUES ( %d, '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s' )", + account_created, account_flags, account_roles, account_level, account_expires, account_service_class ) + VALUES ( %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', '%s' )", intval($parent), dbesc($salt), dbesc($password_encoded), dbesc($email), dbesc(get_best_language()), dbesc(datetime_convert()), - dbesc($flags), - dbesc($roles), + intval($flags), + intval($roles), + intval($techlevel), dbesc($expires), dbesc($default_service_class) ); @@ -237,20 +246,23 @@ function verify_email_address($arr) { dbesc($arr['account']['account_language']) ); +//@fixme - get correct language template + $email_msg = replace_macros(get_intltext_template('register_verify_member.tpl'), array( '$sitename' => get_config('system','sitename'), - '$siteurl' => z_root(), + '$siteurl' => z_root(), '$email' => $arr['email'], '$uid' => $arr['account']['account_id'], '$hash' => $hash, '$details' => $details )); - $res = mail($arr['email'], email_header_encode(sprintf( t('Registration confirmation for %s'), get_config('system','sitename'))), - $email_msg, - 'From: ' . 'Administrator' . '@' . App::get_hostname() . "\n" - . 'Content-type: text/plain; charset=UTF-8' . "\n" - . 'Content-transfer-encoding: 8bit' + $res = z_mail( + [ + 'toEmail' => $arr['email'], + 'messageSubject' => sprintf( t('Registration confirmation for %s'), get_config('system','sitename')), + 'textVersion' => $email_msg, + ] ); if($res) @@ -312,11 +324,12 @@ function send_reg_approval_email($arr) { '$details' => $details )); - $res = mail($admin['email'], sprintf( t('Registration request at %s'), get_config('system','sitename')), - $email_msg, - 'From: ' . t('Administrator') . '@' . App::get_hostname() . "\n" - . 'Content-type: text/plain; charset=UTF-8' . "\n" - . 'Content-transfer-encoding: 8bit' + $res = z_mail( + [ + 'toEmail' => $admin['email'], + 'messageSubject' => sprintf( t('Registration request at %s'), get_config('system','sitename')), + 'textVersion' => $email_msg, + ] ); if($res) @@ -339,12 +352,14 @@ function send_register_success_email($email,$password) { '$password' => t('your registration password'), )); - $res = mail($email, sprintf( t('Registration details for %s'), get_config('system','sitename')), - $email_msg, - 'From: ' . t('Administrator') . '@' . App::get_hostname() . "\n" - . 'Content-type: text/plain; charset=UTF-8' . "\n" - . 'Content-transfer-encoding: 8bit' + $res = z_mail( + [ + 'toEmail' => $email, + 'messageSubject' => sprintf( t('Registration details for %s'), get_config('system','sitename')), + 'textVersion' => $email_msg, + ] ); + return($res ? true : false); } @@ -390,7 +405,7 @@ function account_allow($hash) { push_lang($register[0]['lang']); $email_tpl = get_intltext_template("register_open_eml.tpl"); - $email_tpl = replace_macros($email_tpl, array( + $email_msg = replace_macros($email_tpl, array( '$sitename' => get_config('system','sitename'), '$siteurl' => z_root(), '$username' => $account[0]['account_email'], @@ -399,11 +414,13 @@ function account_allow($hash) { '$uid' => $account[0]['account_id'] )); - $res = mail($account[0]['account_email'], sprintf( t('Registration details for %s'), get_config('system','sitename')), - $email_tpl, - 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n" - . 'Content-type: text/plain; charset=UTF-8' . "\n" - . 'Content-transfer-encoding: 8bit' ); + $res = z_mail( + [ + 'toEmail' => $account[0]['account_email'], + 'messageSubject' => sprintf( t('Registration details for %s'), get_config('system','sitename')), + 'textVersion' => $email_msg, + ] + ); pop_lang(); @@ -539,8 +556,8 @@ function account_approve($hash) { */ function downgrade_accounts() { - $r = q("select * from account where not ( account_flags & %d )>0 - and account_expires != '%s' + $r = q("select * from account where not ( account_flags & %d ) > 0 + and account_expires > '%s' and account_expires < %s ", intval(ACCOUNT_EXPIRED), dbesc(NULL_DATE), @@ -751,3 +768,23 @@ function upgrade_bool_message($bbcode = false) { $x = upgrade_link($bbcode); return t('This action is not available under your subscription plan.') . (($x) ? ' ' . $x : '') ; } + + +function get_account_techlevel($account_id = 0) { + + $role = \Zotlabs\Lib\System::get_server_role(); + if($role == 'basic') + return 0; + if($role == 'standard') + return 5; + + if(! $account_id) { + $x = \App::get_account(); + } + else { + $x = get_account_by_id($account_id); + } + + return (($x) ? intval($x['account_level']) : 0); + +}
\ No newline at end of file diff --git a/include/acl_selectors.php b/include/acl_selectors.php index 148c67a6c..362776b44 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -13,7 +13,7 @@ function group_select($selname,$selclass,$preselected = false,$size = 4) { $o .= "<select name=\"{$selname}[]\" id=\"$selclass\" class=\"$selclass\" multiple=\"multiple\" size=\"$size\" >\r\n"; - $r = q("SELECT * FROM `groups` WHERE `deleted` = 0 AND `uid` = %d ORDER BY `gname` ASC", + $r = q("SELECT * FROM groups WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", intval(local_channel()) ); @@ -44,112 +44,6 @@ function group_select($selname,$selclass,$preselected = false,$size = 4) { return $o; } -/* MicMee 20130114 function contact_selector no longer in use, sql table contact does no longer exist -function contact_selector($selname, $selclass, $preselected = false, $options) { - - - $mutual = false; - $networks = null; - $single = false; - $exclude = false; - $size = 4; - - if(is_array($options)) { - if(x($options,'size')) - $size = $options['size']; - - if(x($options,'mutual_friends')) - $mutual = true; - if(x($options,'single')) - $single = true; - if(x($options,'multiple')) - $single = false; - if(x($options,'exclude')) - $exclude = $options['exclude']; - - if(x($options,'networks')) { - switch($options['networks']) { - case 'DFRN_ONLY': - $networks = array('dfrn'); - break; - case 'PRIVATE': - $networks = array('dfrn','face','mail', 'dspr'); - break; - case 'TWO_WAY': - $networks = array('dfrn','face','mail','dspr','stat'); - break; - default: - break; - } - } - } - - $x = array('options' => $options, 'size' => $size, 'single' => $single, 'mutual' => $mutual, 'exclude' => $exclude, 'networks' => $networks); - - call_hooks('contact_select_options', $x); - - $o = ''; - - $sql_extra = ''; - - if($x['mutual']) { - $sql_extra .= sprintf(" AND `rel` = %d ", intval(CONTACT_IS_FRIEND)); - } - - if(intval($x['exclude'])) - $sql_extra .= sprintf(" AND `id` != %d ", intval($x['exclude'])); - - if(is_array($x['networks']) && count($x['networks'])) { - for($y = 0; $y < count($x['networks']) ; $y ++) - $x['networks'][$y] = "'" . dbesc($x['networks'][$y]) . "'"; - $str_nets = implode(',',$x['networks']); - $sql_extra .= " AND `network` IN ( $str_nets ) "; - } - - $tabindex = (x($options, 'tabindex') ? "tabindex=\"" . $options["tabindex"] . "\"" : ""); - - if($x['single']) - $o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"" . $x['size'] . "\" $tabindex >\r\n"; - else - $o .= "<select name=\"{$selname}[]\" id=\"$selclass\" class=\"$selclass\" multiple=\"multiple\" size=\"" . $x['size'] . "$\" $tabindex >\r\n"; - - $r = q("SELECT `id`, `name`, `url`, `network` FROM `contact` - WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 AND `archive` = 0 AND `notify` != '' - $sql_extra - ORDER BY `name` ASC ", - intval(local_channel()) - ); - - - $arr = array('contact' => $r, 'entry' => $o); - - // e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow' - - call_hooks(App::$module . '_pre_' . $selname, $arr); - - if(count($r)) { - foreach($r as $rr) { - if((is_array($preselected)) && in_array($rr['id'], $preselected)) - $selected = " selected=\"selected\" "; - else - $selected = ''; - - $trimmed = mb_substr($rr['name'],0,20); - - $o .= "<option value=\"{$rr['id']}\" $selected title=\"{$rr['name']}|{$rr['url']}\" >$trimmed</option>\r\n"; - } - - } - - $o .= "</select>\r\n"; - - call_hooks(App::$module . '_post_' . $selname, $o); - - return $o; -}*/ - - - function contact_select($selname, $selclass, $preselected = false, $size = 4, $privmail = false, $celeb = false, $privatenet = false, $tabindex = null) { @@ -254,20 +148,26 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti array_walk($deny_cid,'fixacl'); array_walk($deny_gid,'fixacl'); } - - $jotnets = ''; - if($show_jotnets) { - call_hooks('jot_networks', $jotnets); + + $r = q("SELECT id, hash, gname FROM groups WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", + intval(local_channel()) + ); + + if($r) { + foreach($r as $rr) { + $groups .= '<option id="' . $rr['id'] . '" value="' . $rr['hash'] . '">' . $rr['gname'] . '</option>' . "\r\n"; + } } $tpl = get_markup_template("acl_selector.tpl"); $o = replace_macros($tpl, array( '$showall' => $showall_caption, '$onlyme' => t('Only me'), + '$groups' => $groups, '$showallOrigin' => $showall_origin, '$showallIcon' => $showall_icon, '$select_label' => t('Who can see this?'), - '$showlimited' => t('Custom selection'), + '$custom' => t('Custom selection'), '$showlimitedDesc' => t('Select "Show" to allow viewing. "Don\'t show" lets you override and limit the scope of "Show".'), '$show' => t("Show"), '$hide' => t("Don't show"), @@ -276,8 +176,6 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti '$allowgid' => json_encode($allow_gid), '$denycid' => json_encode($deny_cid), '$denygid' => json_encode($deny_gid), - '$jnetModalTitle' => t('Other networks and post services'), - '$jotnets' => $jotnets, '$aclModalTitle' => t('Permissions'), '$aclModalDesc' => $dialog_description, '$aclModalDismiss' => t('Close'), diff --git a/include/api.php b/include/api.php index 2587a72bb..692baf563 100644 --- a/include/api.php +++ b/include/api.php @@ -61,10 +61,8 @@ require_once('include/api_auth.php'); } - function api_register_func($path, $func, $auth=false){ - global $API; - $API[$path] = array('func'=>$func, - 'auth'=>$auth); + function api_register_func($path, $func, $auth=false) { + \Zotlabs\Lib\Api_router::register($path,$func,$auth); } @@ -72,76 +70,88 @@ require_once('include/api_auth.php'); * MAIN API ENTRY POINT * **************************/ - function api_call($a){ - GLOBAL $API, $called_api; + function api_call(){ - // preset - $type="json"; + $p = App::$cmd; + $type = null; - foreach ($API as $p=>$info){ - if (strpos(App::$query_string, $p)===0){ - $called_api= explode("/",$p); - //unset($_SERVER['PHP_AUTH_USER']); - if ($info['auth'] === true && api_user() === false) { - api_login($a); - } + if(strrpos($p,'.')) { + $type = substr($p,strrpos($p,'.')+1); + if(strpos($type,'/') === false) { + $p = substr($p,0,strrpos($p,'.')); + // recalculate App argc,argv since we just extracted the type from it + App::$argv = explode('/',$p); + App::$argc = count(App::$argv); + } + } + + if((! $type) || (! in_array($type, [ 'json', 'xml', 'rss', 'as', 'atom' ]))) + $type = 'json'; + + $info = \Zotlabs\Lib\Api_router::find($p); + + logger('info: ' . $p . ' type: ' . $type . ' ' . print_r($info,true)); + + if($info) { - load_contact_links(api_user()); + if ($info['auth'] === true && api_user() === false) { + api_login($a); + } - $channel = App::get_channel(); + load_contact_links(api_user()); - logger('API call for ' . $channel['channel_name'] . ': ' . App::$query_string); - logger('API parameters: ' . print_r($_REQUEST,true)); + $channel = App::get_channel(); - $type="json"; + logger('API call for ' . $channel['channel_name'] . ': ' . App::$query_string); + logger('API parameters: ' . print_r($_REQUEST,true)); - if (strpos(App::$query_string, ".xml")>0) $type="xml"; - if (strpos(App::$query_string, ".json")>0) $type="json"; - if (strpos(App::$query_string, ".rss")>0) $type="rss"; - if (strpos(App::$query_string, ".atom")>0) $type="atom"; - if (strpos(App::$query_string, ".as")>0) $type="as"; + $r = call_user_func($info['func'],$type); - $r = call_user_func($info['func'], $a, $type); - if ($r===false) return; + if($r === false) + return; - switch($type){ - case "xml": - $r = mb_convert_encoding($r, "UTF-8",mb_detect_encoding($r)); - header ("Content-Type: text/xml"); - return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; - break; - case "json": - header ("Content-Type: application/json"); - foreach($r as $rr) { - if(! $rr) - $rr = array(); - $json = json_encode($rr); + switch($type) { + case "xml": + $r = mb_convert_encoding($r, "UTF-8",mb_detect_encoding($r)); + header ("Content-Type: text/xml"); + return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; + break; + case "json": + header ("Content-Type: application/json"); + if($r) { + foreach($r as $rv) { + $json = json_encode($rv); } - if ($_GET['callback']) - $json = $_GET['callback']."(".$json.")"; - return $json; - break; - case "rss": - header ("Content-Type: application/rss+xml"); - return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; - break; - case "atom": - header ("Content-Type: application/atom+xml"); - return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; - break; - case "as": - //header ("Content-Type: application/json"); - //foreach($r as $rr) - // return json_encode($rr); - return json_encode($r); - break; + } + // Lookup JSONP to understand these lines. They provide cross-domain AJAX ability. + if ($_GET['callback']) + $json = $_GET['callback'] . '(' . $json . ')' ; + return $json; + break; + case "rss": + header ("Content-Type: application/rss+xml"); + return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; + break; + case "atom": + header ("Content-Type: application/atom+xml"); + return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; + break; + case "as": + if($r) { + foreach($r as $rv) { + $json = json_encode($rv); + } + } + return $json; + break; - } - //echo "<pre>"; var_dump($r); die(); } + } + + header("HTTP/1.1 404 Not Found"); - logger('API call not implemented: '.App::$query_string." - ".print_r($_REQUEST,true)); + logger('API call not implemented: ' . App::$query_string . ' - ' . print_r($_REQUEST,true)); $r = '<status><error>not implemented</error></status>'; switch($type){ case "xml": @@ -166,7 +176,8 @@ require_once('include/api_auth.php'); /** * RSS extra info */ - function api_rss_extra($a, $arr, $user_info){ + + function api_rss_extra( $arr, $user_info){ if (is_null($user_info)) $user_info = api_get_user($a); $arr['$user'] = $user_info; $arr['$rss'] = array( @@ -186,8 +197,8 @@ require_once('include/api_auth.php'); * Returns user info array. */ - function api_get_user($a, $contact_id = null, $contact_xchan = null){ - global $called_api; + function api_get_user( $contact_id = null, $contact_xchan = null){ + $user = null; $extra_query = ""; @@ -212,26 +223,12 @@ require_once('include/api_auth.php'); if (api_user()!==false) $extra_query .= " AND abook_channel = ".intval(api_user()); } - - if (is_null($user) && argc() > (count($called_api)-1) && (strstr(App::$cmd,'/users'))){ - $argid = count($called_api); - list($xx, $null) = explode(".",argv($argid)); - if(is_numeric($xx)){ - $user = intval($xx); - $extra_query = " AND abook_id = %d "; - } else { - $user = dbesc($xx); - $extra_query = " AND xchan_addr like '%s@%%' "; - if (api_user() !== false) - $extra_query .= " AND abook_channel = ".intval(api_user()); - } - } } if (! $user) { if (api_user() === false) { api_login($a); - return False; + return false; } else { $user = local_channel(); $extra_query = " AND abook_channel = %d AND abook_self = 1 "; @@ -239,7 +236,8 @@ require_once('include/api_auth.php'); } - logger('api_user: ' . $extra_query . ', user: ' . $user); + logger('api_user: ' . $extra_query . ', user: ' . $user, LOGGER_DATA, LOG_INFO); + // user info $uinfo = q("SELECT * from abook left join xchan on abook_xchan = xchan_hash @@ -349,14 +347,13 @@ require_once('include/api_auth.php'); $x = api_get_status($uinfo[0]['xchan_hash']); if($x) $ret['status'] = $x; - // logger('api_get_user: ' . print_r($ret,true)); return $ret; } - function api_client_register($a,$type) { + function api_client_register($type) { $ret = array(); $key = random_string(16); @@ -389,12 +386,12 @@ require_once('include/api_auth.php'); - function api_item_get_user($a, $item) { + function api_item_get_user( $item) { // The author is our direct contact, in a conversation with us. if($item['author']['abook_id']) { - return api_get_user($a,$item['author']['abook_id']); + return api_get_user($item['author']['abook_id']); } // We don't know this person directly. @@ -461,9 +458,11 @@ require_once('include/api_auth.php'); $ret = replace_macros($tpl, $data); break; case "json": + default: $ret = $data; break; } + return $ret; } @@ -473,17 +472,16 @@ require_once('include/api_auth.php'); * returns a 401 status code and an error message if not. * http://developer.twitter.com/doc/get/account/verify_credentials */ - function api_account_verify_credentials($a, $type){ + function api_account_verify_credentials( $type){ if (api_user()===false) return false; $user_info = api_get_user($a); - return api_apply_template("user", $type, array('$user' => $user_info)); } api_register_func('api/account/verify_credentials','api_account_verify_credentials', true); - function api_account_logout($a, $type){ + function api_account_logout( $type){ require_once('include/auth.php'); App::$session->nuke(); return api_apply_template("user", $type, array('$user' => null)); @@ -507,7 +505,7 @@ require_once('include/api_auth.php'); * Red basic channel export */ - function api_export_basic($a, $type) { + function api_export_basic( $type) { if(api_user() === false) { logger('api_export_basic: no user'); return false; @@ -519,9 +517,10 @@ require_once('include/api_auth.php'); } api_register_func('api/export/basic','api_export_basic', true); api_register_func('api/red/channel/export/basic','api_export_basic', true); + api_register_func('api/hz/1.0/channel/export/basic','api_export_basic', true); - function api_channel_stream($a, $type) { + function api_channel_stream( $type) { if(api_user() === false) { logger('api_channel_stream: no user'); return false; @@ -536,18 +535,20 @@ require_once('include/api_auth.php'); } } api_register_func('api/red/channel/stream','api_channel_stream', true); + api_register_func('api/hz/1.0/channel/stream','api_channel_stream', true); - function api_attach_list($a,$type) { + function api_attach_list($type) { logger('api_user: ' . api_user()); json_return_and_die(attach_list_files(api_user(),get_observer_hash(),'','','','created asc')); } api_register_func('api/red/files','api_attach_list', true); + api_register_func('api/hz/1.0/files','api_attach_list', true); - function api_file_meta($a,$type) { + function api_file_meta($type) { if (api_user()===false) return false; if(! $_REQUEST['file_id']) return false; $r = q("select * from attach where uid = %d and hash = '%s' limit 1", @@ -563,9 +564,10 @@ require_once('include/api_auth.php'); } api_register_func('api/red/filemeta', 'api_file_meta', true); + api_register_func('api/hz/1.0/filemeta', 'api_file_meta', true); - function api_file_data($a,$type) { + function api_file_data($type) { if (api_user()===false) return false; if(! $_REQUEST['file_id']) return false; $start = (($_REQUEST['start']) ? intval($_REQUEST['start']) : 0); @@ -606,10 +608,11 @@ require_once('include/api_auth.php'); } api_register_func('api/red/filedata', 'api_file_data', true); + api_register_func('api/hz/1.0/filedata', 'api_file_data', true); - function api_file_detail($a,$type) { + function api_file_detail($type) { if (api_user()===false) return false; if(! $_REQUEST['file_id']) return false; $r = q("select * from attach where uid = %d and hash = '%s' limit 1", @@ -631,20 +634,23 @@ require_once('include/api_auth.php'); } api_register_func('api/red/file', 'api_file_detail', true); + api_register_func('api/hz/1.0/file', 'api_file_detail', true); - function api_albums($a,$type) { + function api_albums($type) { json_return_and_die(photos_albums_list(App::get_channel(),App::get_observer())); } api_register_func('api/red/albums','api_albums', true); + api_register_func('api/hz/1.0/albums','api_albums', true); - function api_photos($a,$type) { + function api_photos($type) { $album = $_REQUEST['album']; json_return_and_die(photos_list_photos(App::get_channel(),App::get_observer(),$album)); } api_register_func('api/red/photos','api_photos', true); + api_register_func('api/hz/1.0/photos','api_photos', true); - function api_photo_detail($a,$type) { + function api_photo_detail($type) { if (api_user()===false) return false; if(! $_REQUEST['photo_id']) return false; $scale = ((array_key_exists('scale',$_REQUEST)) ? intval($_REQUEST['scale']) : 0); @@ -684,9 +690,10 @@ require_once('include/api_auth.php'); } api_register_func('api/red/photo', 'api_photo_detail', true); + api_register_func('api/hz/1.0/photo', 'api_photo_detail', true); - function api_group_members($a,$type) { + function api_group_members($type) { if(api_user() === false) return false; @@ -706,11 +713,12 @@ require_once('include/api_auth.php'); } api_register_func('api/red/group_members','api_group_members', true); + api_register_func('api/hz/1.0/group_members','api_group_members', true); - function api_group($a,$type) { + function api_group($type) { if(api_user() === false) return false; @@ -720,9 +728,10 @@ require_once('include/api_auth.php'); json_return_and_die($r); } api_register_func('api/red/group','api_group', true); + api_register_func('api/hz/1.0/group','api_group', true); - function api_red_xchan($a,$type) { + function api_red_xchan($type) { logger('api_xchan'); if(api_user() === false) @@ -738,9 +747,10 @@ require_once('include/api_auth.php'); }; api_register_func('api/red/xchan','api_red_xchan',true); + api_register_func('api/hz/1.0/xchan','api_red_xchan',true); - function api_statuses_mediap($a, $type) { + function api_statuses_mediap( $type) { if (api_user() === false) { logger('api_statuses_update: no user'); return false; @@ -782,11 +792,11 @@ require_once('include/api_auth.php'); $mod->post(); // this should output the last post (the one we just posted). - return api_status_show($a,$type); + return api_status_show($type); } api_register_func('api/statuses/mediap','api_statuses_mediap', true); - function api_statuses_update($a, $type) { + function api_statuses_update( $type) { if (api_user() === false) { logger('api_statuses_update: no user'); return false; @@ -901,13 +911,13 @@ require_once('include/api_auth.php'); $mod->post(); // this should output the last post (the one we just posted). - return api_status_show($a,$type); + return api_status_show($type); } api_register_func('api/statuses/update_with_media','api_statuses_update', true); api_register_func('api/statuses/update','api_statuses_update', true); - function red_item_new($a, $type) { + function red_item_new( $type) { if (api_user() === false) { logger('api_red_item_new: no user'); @@ -939,9 +949,10 @@ require_once('include/api_auth.php'); } api_register_func('api/red/item/new','red_item_new', true); + api_register_func('api/hz/1.0/item/new','red_item_new', true); - function red_item($a, $type) { + function red_item( $type) { if (api_user() === false) { logger('api_red_item_full: no user'); @@ -977,6 +988,7 @@ require_once('include/api_auth.php'); } api_register_func('api/red/item/full','red_item', true); + api_register_func('api/hz/1.0/item/full','red_item', true); @@ -1042,7 +1054,7 @@ require_once('include/api_auth.php'); return $status_info; } - function api_status_show($a, $type){ + function api_status_show( $type){ $user_info = api_get_user($a); // get last public message @@ -1120,7 +1132,7 @@ require_once('include/api_auth.php'); // FIXME - this is essentially the same as api_status_show except for the template formatting at the end. Consolidate. - function api_users_show($a, $type){ + function api_users_show( $type){ $user_info = api_get_user($a); require_once('include/security.php'); @@ -1192,7 +1204,7 @@ require_once('include/api_auth.php'); * TODO: Add reply info */ - function api_statuses_home_timeline($a, $type){ + function api_statuses_home_timeline( $type){ if (api_user() === false) return false; @@ -1259,10 +1271,10 @@ require_once('include/api_auth.php'); switch($type){ case "atom": case "rss": - $data = api_rss_extra($a, $data, $user_info); + $data = api_rss_extra( $data, $user_info); break; case "as": - $as = api_format_as($a, $ret, $user_info); + $as = api_format_as( $ret, $user_info); $as['title'] = App::$config['sitename']." Home Timeline"; $as['link']['url'] = z_root()."/".$user_info["screen_name"]."/all"; return($as); @@ -1274,7 +1286,7 @@ require_once('include/api_auth.php'); api_register_func('api/statuses/home_timeline','api_statuses_home_timeline', true); api_register_func('api/statuses/friends_timeline','api_statuses_home_timeline', true); - function api_statuses_public_timeline($a, $type){ + function api_statuses_public_timeline( $type){ if (api_user()===false) return false; $user_info = api_get_user($a); @@ -1320,10 +1332,10 @@ require_once('include/api_auth.php'); switch($type){ case "atom": case "rss": - $data = api_rss_extra($a, $data, $user_info); + $data = api_rss_extra( $data, $user_info); break; case "as": - $as = api_format_as($a, $ret, $user_info); + $as = api_format_as( $ret, $user_info); $as['title'] = App::$config['sitename']. " " . t('Public Timeline'); $as['link']['url'] = z_root()."/"; return($as); @@ -1338,7 +1350,7 @@ require_once('include/api_auth.php'); * */ - function api_statuses_show($a, $type){ + function api_statuses_show( $type){ if (api_user()===false) return false; $user_info = api_get_user($a); @@ -1377,7 +1389,7 @@ require_once('include/api_auth.php'); /*switch($type){ case "atom": case "rss": - $data = api_rss_extra($a, $data, $user_info); + $data = api_rss_extra( $data, $user_info); }*/ return api_apply_template("status", $type, $data); } @@ -1388,7 +1400,7 @@ require_once('include/api_auth.php'); /** * */ - function api_statuses_repeat($a, $type){ + function api_statuses_repeat( $type){ if (api_user()===false) return false; $user_info = api_get_user($a); @@ -1434,7 +1446,7 @@ require_once('include/api_auth.php'); * */ - function api_statuses_destroy($a, $type){ + function api_statuses_destroy( $type){ if (api_user()===false) return false; $user_info = api_get_user($a); @@ -1498,7 +1510,7 @@ require_once('include/api_auth.php'); */ - function api_statuses_mentions($a, $type){ + function api_statuses_mentions( $type){ if (api_user()===false) return false; $user_info = api_get_user($a); @@ -1548,10 +1560,10 @@ require_once('include/api_auth.php'); switch($type){ case "atom": case "rss": - $data = api_rss_extra($a, $data, $user_info); + $data = api_rss_extra( $data, $user_info); break; case "as": - $as = api_format_as($a, $ret, $user_info); + $as = api_format_as( $ret, $user_info); $as["title"] = App::$config['sitename']." Mentions"; $as['link']['url'] = z_root()."/"; return($as); @@ -1565,7 +1577,7 @@ require_once('include/api_auth.php'); api_register_func('api/statuses/replies','api_statuses_mentions', true); - function api_statuses_user_timeline($a, $type){ + function api_statuses_user_timeline( $type){ if (api_user()===false) return false; $user_info = api_get_user($a); @@ -1633,7 +1645,7 @@ require_once('include/api_auth.php'); switch($type){ case "atom": case "rss": - $data = api_rss_extra($a, $data, $user_info); + $data = api_rss_extra( $data, $user_info); } return api_apply_template("timeline", $type, $data); @@ -1649,7 +1661,7 @@ require_once('include/api_auth.php'); * * api v1 : https://web.archive.org/web/20131019055350/https://dev.twitter.com/docs/api/1/post/favorites/create/%3Aid */ - function api_favorites_create_destroy($a, $type){ + function api_favorites_create_destroy( $type){ logger('favorites_create_destroy'); @@ -1706,7 +1718,7 @@ require_once('include/api_auth.php'); switch($type){ case "atom": case "rss": - $data = api_rss_extra($a, $data, $user_info); + $data = api_rss_extra( $data, $user_info); } return api_apply_template("status", $type, $data); @@ -1717,7 +1729,7 @@ require_once('include/api_auth.php'); - function api_favorites($a, $type){ + function api_favorites( $type){ if (api_user()===false) return false; @@ -1770,10 +1782,10 @@ require_once('include/api_auth.php'); switch($type){ case "atom": case "rss": - $data = api_rss_extra($a, $data, $user_info); + $data = api_rss_extra( $data, $user_info); break; case "as": - $as = api_format_as($a, $ret, $user_info); + $as = api_format_as( $ret, $user_info); $as['title'] = App::$config['sitename']." Home Timeline"; $as['link']['url'] = z_root()."/".$user_info["screen_name"]."/all"; return($as); @@ -1789,7 +1801,7 @@ require_once('include/api_auth.php'); - function api_format_as($a, $ret, $user_info) { + function api_format_as( $ret, $user_info) { $as = array(); $as['title'] = App::$config['sitename']." Public Timeline"; @@ -1907,7 +1919,7 @@ require_once('include/api_auth.php'); localize_item($item); - $status_user = (($item['author_xchan']==$user_info['guid'])?$user_info: api_item_get_user($a,$item)); + $status_user = (($item['author_xchan']==$user_info['guid'])?$user_info: api_item_get_user($item)); if(array_key_exists('status',$status_user)) unset($status_user['status']); @@ -1986,7 +1998,7 @@ require_once('include/api_auth.php'); } - function api_account_rate_limit_status($a,$type) { + function api_account_rate_limit_status($type) { $hash = array( 'reset_time_in_seconds' => strtotime('now + 1 hour'), @@ -2002,7 +2014,7 @@ require_once('include/api_auth.php'); } api_register_func('api/account/rate_limit_status','api_account_rate_limit_status',true); - function api_help_test($a,$type) { + function api_help_test($type) { if ($type == 'xml') $ok = "true"; @@ -2019,7 +2031,7 @@ require_once('include/api_auth.php'); * This function is deprecated by Twitter * returns: json, xml **/ - function api_statuses_f($a, $type, $qtype) { + function api_statuses_f( $type, $qtype) { if (api_user()===false) return false; $user_info = api_get_user($a); @@ -2054,20 +2066,20 @@ require_once('include/api_auth.php'); $ret = array(); foreach($r as $cid){ - $ret[] = api_get_user($a, $cid['abook_id']); + $ret[] = api_get_user( $cid['abook_id']); } return array('$users' => $ret); } - function api_statuses_friends($a, $type){ - $data = api_statuses_f($a,$type,"friends"); + function api_statuses_friends( $type){ + $data = api_statuses_f($type,"friends"); if ($data===false) return false; return api_apply_template("friends", $type, $data); } - function api_statuses_followers($a, $type){ - $data = api_statuses_f($a,$type,"followers"); + function api_statuses_followers( $type){ + $data = api_statuses_f($type,"followers"); if ($data===false) return false; return api_apply_template("friends", $type, $data); } @@ -2079,7 +2091,7 @@ require_once('include/api_auth.php'); - function api_statusnet_config($a,$type) { + function api_statusnet_config($type) { load_config('system'); @@ -2115,8 +2127,9 @@ require_once('include/api_auth.php'); api_register_func('api/statusnet/config','api_statusnet_config',false); api_register_func('api/friendica/config','api_statusnet_config',false); api_register_func('api/red/config','api_statusnet_config',false); + api_register_func('api/hz/1.0/config','api_statusnet_config',false); - function api_statusnet_version($a,$type) { + function api_statusnet_version($type) { // liar @@ -2134,7 +2147,7 @@ require_once('include/api_auth.php'); api_register_func('api/statusnet/version','api_statusnet_version',false); - function api_friendica_version($a,$type) { + function api_friendica_version($type) { if($type === 'xml') { header("Content-type: application/xml"); @@ -2149,9 +2162,10 @@ require_once('include/api_auth.php'); } api_register_func('api/friendica/version','api_friendica_version',false); api_register_func('api/red/version','api_friendica_version',false); + api_register_func('api/hz/1.0/version','api_friendica_version',false); - function api_ff_ids($a,$type,$qtype) { + function api_ff_ids($type,$qtype) { if(! api_user()) return false; @@ -2187,17 +2201,17 @@ require_once('include/api_auth.php'); } } - function api_friends_ids($a,$type) { - api_ff_ids($a,$type,'friends'); + function api_friends_ids($type) { + api_ff_ids($type,'friends'); } - function api_followers_ids($a,$type) { - api_ff_ids($a,$type,'followers'); + function api_followers_ids($type) { + api_ff_ids($type,'followers'); } api_register_func('api/friends/ids','api_friends_ids',true); api_register_func('api/followers/ids','api_followers_ids',true); - function api_direct_messages_new($a, $type) { + function api_direct_messages_new( $type) { if (api_user()===false) return false; if (!x($_POST, "text") || !x($_POST,"screen_name")) return; @@ -2213,7 +2227,7 @@ require_once('include/api_auth.php'); dbesc($_POST['screen_name'] . '@%') ); - $recipient = api_get_user($a, $r[0]['abook_id']); + $recipient = api_get_user( $r[0]['abook_id']); $replyto = ''; $sub = ''; if (x($_REQUEST,'replyto')) { @@ -2247,7 +2261,7 @@ require_once('include/api_auth.php'); switch($type){ case "atom": case "rss": - $data = api_rss_extra($a, $data, $user_info); + $data = api_rss_extra( $data, $user_info); } return api_apply_template("direct_messages", $type, $data); @@ -2255,7 +2269,7 @@ require_once('include/api_auth.php'); } api_register_func('api/direct_messages/new','api_direct_messages_new',true); - function api_direct_messages_box($a, $type, $box) { + function api_direct_messages_box( $type, $box) { if (api_user()===false) return false; $user_info = api_get_user($a); @@ -2292,10 +2306,10 @@ require_once('include/api_auth.php'); foreach($r as $item) { if ($item['from_xchan'] == $channel['channel_hash']) { $sender = $user_info; - $recipient = api_get_user($a, null, $item['to_xchan']); + $recipient = api_get_user( null, $item['to_xchan']); } else { - $sender = api_get_user($a, null, $item['from_xchan']); + $sender = api_get_user( null, $item['from_xchan']); $recipient = $user_info; } @@ -2308,24 +2322,24 @@ require_once('include/api_auth.php'); switch($type){ case "atom": case "rss": - $data = api_rss_extra($a, $data, $user_info); + $data = api_rss_extra( $data, $user_info); } return api_apply_template("direct_messages", $type, $data); } - function api_direct_messages_sentbox($a, $type){ - return api_direct_messages_box($a, $type, "sentbox"); + function api_direct_messages_sentbox( $type){ + return api_direct_messages_box( $type, "sentbox"); } - function api_direct_messages_inbox($a, $type){ - return api_direct_messages_box($a, $type, "inbox"); + function api_direct_messages_inbox( $type){ + return api_direct_messages_box( $type, "inbox"); } - function api_direct_messages_all($a, $type){ - return api_direct_messages_box($a, $type, "all"); + function api_direct_messages_all( $type){ + return api_direct_messages_box( $type, "all"); } - function api_direct_messages_conversation($a, $type){ - return api_direct_messages_box($a, $type, "conversation"); + function api_direct_messages_conversation( $type){ + return api_direct_messages_box( $type, "conversation"); } api_register_func('api/direct_messages/conversation','api_direct_messages_conversation',true); api_register_func('api/direct_messages/all','api_direct_messages_all',true); @@ -2333,7 +2347,7 @@ require_once('include/api_auth.php'); api_register_func('api/direct_messages','api_direct_messages_inbox',true); - function api_oauth_request_token($a, $type){ + function api_oauth_request_token( $type){ try{ $oauth = new ZotOAuth1(); $req = OAuth1Request::from_request(); @@ -2348,7 +2362,7 @@ require_once('include/api_auth.php'); killme(); } - function api_oauth_access_token($a, $type){ + function api_oauth_access_token( $type){ try{ $oauth = new ZotOAuth1(); $req = OAuth1Request::from_request(); diff --git a/include/attach.php b/include/attach.php index 172840b96..b5c334d3e 100644 --- a/include/attach.php +++ b/include/attach.php @@ -229,7 +229,7 @@ function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $ * @param int $rev Revision * @return array */ -function attach_by_hash($hash, $rev = 0) { +function attach_by_hash($hash, $observer_hash, $rev = 0) { $ret = array('success' => false); @@ -249,12 +249,12 @@ function attach_by_hash($hash, $rev = 0) { return $ret; } - if(! perm_is_allowed($r[0]['uid'], get_observer_hash(), 'view_storage')) { + if(! perm_is_allowed($r[0]['uid'], $observer_hash, 'view_storage')) { $ret['message'] = t('Permission denied.'); return $ret; } - $sql_extra = permissions_sql($r[0]['uid']); + $sql_extra = permissions_sql($r[0]['uid'],$observer_hash); // Now we'll see if we can access the attachment @@ -269,7 +269,7 @@ function attach_by_hash($hash, $rev = 0) { } if($r[0]['folder']) { - $x = attach_can_view_folder($r[0]['uid'],get_observer_hash(),$r[0]['folder']); + $x = attach_can_view_folder($r[0]['uid'],$observer_hash,$r[0]['folder']); if(! $x) { $ret['message'] = t('Permission denied.'); return $ret; @@ -315,7 +315,7 @@ function attach_can_view_folder($uid,$ob_hash,$folder_hash) { * * \e string \b message (optional) only when success is false * * \e array \b data array of attach DB entry without data component */ -function attach_by_hash_nodata($hash, $rev = 0) { +function attach_by_hash_nodata($hash, $observer_hash, $rev = 0) { $ret = array('success' => false); @@ -335,12 +335,12 @@ function attach_by_hash_nodata($hash, $rev = 0) { return $ret; } - if(! perm_is_allowed($r[0]['uid'],get_observer_hash(),'view_storage')) { + if(! perm_is_allowed($r[0]['uid'],$observer_hash,'view_storage')) { $ret['message'] = t('Permission denied.'); return $ret; } - $sql_extra = permissions_sql($r[0]['uid']); + $sql_extra = permissions_sql($r[0]['uid'],$observer_hash); // Now we'll see if we can access the attachment @@ -355,7 +355,7 @@ function attach_by_hash_nodata($hash, $rev = 0) { } if($r[0]['folder']) { - $x = attach_can_view_folder($r[0]['uid'],get_observer_hash(),$r[0]['folder']); + $x = attach_can_view_folder($r[0]['uid'],$observer_hash,$r[0]['folder']); if(! $x) { $ret['message'] = t('Permission denied.'); return $ret; @@ -709,6 +709,9 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $os_relpath .= $hash; + // not yet used + $os_path = ''; + if($src) @file_put_contents($os_basepath . $os_relpath,@file_get_contents($src)); @@ -723,7 +726,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $edited = $created; if($options === 'replace') { - $r = q("update attach set filename = '%s', filetype = '%s', folder = '%s', filesize = %d, os_storage = %d, is_photo = %d, content = '%s', edited = '%s' where id = %d and uid = %d", + $r = q("update attach set filename = '%s', filetype = '%s', folder = '%s', filesize = %d, os_storage = %d, is_photo = %d, content = '%s', edited = '%s', os_path = '%s' where id = %d and uid = %d", dbesc($filename), dbesc($mimetype), dbesc($folder_hash), @@ -732,13 +735,14 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { intval($is_photo), dbesc($os_basepath . $os_relpath), dbesc($created), + dbesc($os_path), intval($existing_id), intval($channel_id) ); } elseif($options === 'revise') { - $r = q("insert into attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, allow_cid, allow_gid, deny_cid, deny_gid ) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", + $r = q("insert into attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, os_path, allow_cid, allow_gid, deny_cid, deny_gid ) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", intval($x[0]['aid']), intval($channel_id), dbesc($x[0]['hash']), @@ -753,6 +757,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { dbesc($os_basepath . $os_relpath), dbesc($created), dbesc($created), + dbesc($os_path), dbesc($x[0]['allow_cid']), dbesc($x[0]['allow_gid']), dbesc($x[0]['deny_cid']), @@ -760,7 +765,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { ); } elseif($options === 'update') { - $r = q("update attach set filename = '%s', filetype = '%s', folder = '%s', edited = '%s', os_storage = %d, is_photo = %d, + $r = q("update attach set filename = '%s', filetype = '%s', folder = '%s', edited = '%s', os_storage = %d, is_photo = %d, os_path = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where id = %d and uid = %d", dbesc((array_key_exists('filename',$arr)) ? $arr['filename'] : $x[0]['filename']), dbesc((array_key_exists('filetype',$arr)) ? $arr['filetype'] : $x[0]['filetype']), @@ -768,6 +773,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { dbesc($created), dbesc((array_key_exists('os_storage',$arr)) ? $arr['os_storage'] : $x[0]['os_storage']), dbesc((array_key_exists('is_photo',$arr)) ? $arr['is_photo'] : $x[0]['is_photo']), + dbesc((array_key_exists('os_path',$arr)) ? $arr['os_path'] : $x[0]['os_path']), dbesc((array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : $x[0]['allow_cid']), dbesc((array_key_exists('allow_gid',$arr)) ? $arr['allow_gid'] : $x[0]['allow_gid']), dbesc((array_key_exists('deny_cid',$arr)) ? $arr['deny_cid'] : $x[0]['deny_cid']), @@ -778,8 +784,8 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { } else { - $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, allow_cid, allow_gid,deny_cid, deny_gid ) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", + $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, os_path, allow_cid, allow_gid,deny_cid, deny_gid ) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", intval($channel['channel_account_id']), intval($channel_id), dbesc($hash), @@ -794,6 +800,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { dbesc($os_basepath . $os_relpath), dbesc($created), dbesc($created), + dbesc($os_path), dbesc(($arr && array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : $str_contact_allow), dbesc(($arr && array_key_exists('allow_gid',$arr)) ? $arr['allow_gid'] : $str_group_allow), dbesc(($arr && array_key_exists('deny_cid',$arr)) ? $arr['deny_cid'] : $str_contact_deny), @@ -1191,7 +1198,11 @@ function attach_mkdirp($channel, $observer_hash, $arr = null) { * @param string $deny_gid * @param boolean $recurse (optional) default false */ -function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $recurse = false) { +function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $recurse = false, $sync = false) { + + $channel = channelx_by_n($channel_id); + if(! $channel) + return; $r = q("select hash, flags, is_dir, is_photo from attach where hash = '%s' and uid = %d limit 1", dbesc($resource), @@ -1209,7 +1220,7 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi ); if($r) { foreach($r as $rr) { - attach_change_permissions($channel_id, $rr['hash'], $allow_cid, $allow_gid, $deny_cid, $deny_gid, $recurse); + attach_change_permissions($channel_id, $rr['hash'], $allow_cid, $allow_gid, $deny_cid, $deny_gid, $recurse, $sync); } } } @@ -1233,6 +1244,13 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi intval($channel_id) ); } + + if($sync) { + $data = attach_export_data($channel,$resource); + + if($data) + build_sync_packet($channel['channel_id'],array('file' => array($data))); + } } /** @@ -1994,3 +2012,222 @@ function get_filename_by_cloudname($cloudname, $channel, $storepath) { } return null; } + + +// recursively copy a directory into cloud files +function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpath) +{ + if (!is_dir($srcpath) || !is_readable($srcpath)) { + logger('Error reading source path: ' . $srcpath, LOGGER_NORMAL); + return false; + } + $nodes = array_diff(scandir($srcpath), array('.', '..')); + foreach ($nodes as $node) { + $clouddir = $cloudpath . '/' . $node; // Sub-folder in cloud files destination + $nodepath = $srcpath . '/' . $node; // Sub-folder in source path + if(is_dir($nodepath)) { + $x = attach_mkdirp($channel, $observer_hash, array('pathname' => $clouddir)); + if(!$x['success']) { + logger('Error creating cloud path: ' . $clouddir, LOGGER_NORMAL); + return false; + } + // Recursively call this function where the source and destination are the subfolders + $success = copy_folder_to_cloudfiles($channel, $observer_hash, $nodepath, $clouddir); + if(!$success) { + logger('Error copying contents of folder: ' . $nodepath, LOGGER_NORMAL); + return false; + } + } elseif (is_file($nodepath) && is_readable($nodepath)) { + $x = attach_store($channel, $observer_hash, 'import', + array( + 'directory' => $cloudpath, + 'src' => $nodepath, + 'filename' => $node, + 'filesize' => @filesize($nodepath), + 'preserve_original' => true) + ); + if(!$x['success']) { + logger('Error copying file: ' . $nodepath , LOGGER_NORMAL); + logger('Return value: ' . json_encode($x), LOGGER_NORMAL); + return false; + } + } else { + logger('Error scanning source path', LOGGER_NORMAL); + return false; + } + } + + return true; +} +/** + * attach_move() + * This function performs an in place directory-to-directory move of a stored attachment or photo. + * The data is physically moved in the store/nickname storage location and the paths adjusted + * in the attach structure (and if applicable the photo table). The new 'album name' is recorded + * for photos and will show up immediately there. + * This takes a channel_id, attach.hash of the file to move (this is the same as a photo resource_id), and + * the attach.hash of the new parent folder, which must already exist. If $new_folder_hash is blank or empty, + * the file is relocated to the root of the channel's storage area. + * + * @fixme: this operation is currently not synced to clones !! + */ + +function attach_move($channel_id,$resource_id,$new_folder_hash) { + + $c = channelx_by_n($channel_id); + if(! $c) + return false; + + $r = q("select * from attach where hash = '%s' and uid = %d limit 1", + dbesc($resource_id), + intval($channel_id) + ); + if(! $r) + return false; + + $oldstorepath = $r[0]['content']; + + if($new_folder_hash) { + $n = q("select * from attach where hash = '%s' and uid = %d limit 1", + dbesc($new_folder_hash), + intval($channel_id) + ); + if(! $n) + return; + $newdirname = $n[0]['filename']; + $newstorepath = $n[0]['content'] . '/' . $resource_id; + } + else { + $newstorepath = 'store/' . $c['channel_address'] . '/' . $resource_id; + } + + rename($oldstorepath,$newstorepath); + + // duplicate detection. If 'overwrite' is specified, return false because we can't yet do that. + + $filename = $r[0]['filename']; + + $s = q("select filename, id, hash, filesize from attach where filename = '%s' and folder = '%s' ", + dbesc($filename), + dbesc($new_folder_hash) + ); + + if($s) { + $overwrite = get_pconfig($channel_id,'system','overwrite_dup_files'); + if($overwrite) { + // @fixme + return; + } + else { + if(strpos($filename,'.') !== false) { + $basename = substr($filename,0,strrpos($filename,'.')); + $ext = substr($filename,strrpos($filename,'.')); + } + else { + $basename = $filename; + $ext = ''; + } + + $v = q("select filename from attach where ( filename = '%s' OR filename like '%s' ) and folder = '%s' ", + dbesc($basename . $ext), + dbesc($basename . '(%)' . $ext), + dbesc($new_folder_hash) + ); + + if($v) { + $x = 1; + + do { + $found = false; + foreach($v as $vv) { + if($vv['filename'] === $basename . '(' . $x . ')' . $ext) { + $found = true; + break; + } + } + if($found) + $x++; + } + while($found); + $filename = $basename . '(' . $x . ')' . $ext; + } + else + $filename = $basename . $ext; + } + } + + $t = q("update attach set content = '%s', folder = '%s', filename = '%s' where id = %d", + dbesc($newstorepath), + dbesc($new_folder_hash), + dbesc($filename), + intval($r[0]['id']) + ); + + if($r[0]['is_photo']) { + $t = q("update photo set album = '%s', filename = '%s' where resource_id = '%s' and uid = %d", + dbesc($newdirname), + dbesc($filename), + dbesc($resource_id), + intval($channel_id) + ); + + $t = q("update photo set content = '%s' where resource_id = '%s' and uid = %d and imgscale = 0", + dbesc($newstorepath), + dbesc($resource_id), + intval($channel_id) + ); + } + + return true; + +} + + +function attach_folder_select_list($channel_id) { + + $r = q("select * from attach where is_dir = 1 and uid = %d", + intval($channel_id) + ); + + $out = []; + $out[''] = '/'; + + if($r) { + foreach($r as $rv) { + $x = attach_folder_rpaths($r,$rv); + if($x) + $out[$x[0]] = $x[1]; + } + } + return $out; +} + +function attach_folder_rpaths($all_folders,$that_folder) { + + $path = '/' . $that_folder['filename']; + $current_hash = $that_folder['hash']; + $parent_hash = $that_folder['folder']; + $error = false; + $found = false; + + if($parent_hash) { + do { + foreach($all_folders as $selected) { + if(! $selected['is_dir']) + continue; + if($selected['hash'] == $parent_hash) { + $path = '/' . $selected['filename'] . $path; + $current_hash = $selected['hash']; + $parent_hash = $selected['folder']; + $found = true; + break; + } + } + if(! $found) + $error = true; + } + while((! $found) && (! $error) && ($parent_hash != '')); + } + return (($error) ? false : [ $current_hash , $path ]); + +}
\ No newline at end of file diff --git a/include/bb2diaspora.php b/include/bb2diaspora.php index 16f67dc4a..d3f88e17c 100644 --- a/include/bb2diaspora.php +++ b/include/bb2diaspora.php @@ -268,7 +268,7 @@ function bb2dmention_callback($match) { } -function bb2diaspora_itemwallwall(&$item) { +function bb2diaspora_itemwallwall(&$item,$uplink = false) { // We will provide wallwall (embedded author on the Diaspora side) if // 1. It is a wall-to-wall post @@ -302,13 +302,19 @@ function bb2diaspora_itemwallwall(&$item) { } } + if($uplink) + $wallwall = true; + if(($wallwall) && (is_array($item['author'])) && $item['author']['xchan_url'] && $item['author']['xchan_name'] && $item['author']['xchan_photo_s']) { logger('bb2diaspora_itemwallwall: wall to wall post',LOGGER_DEBUG); // post will come across with the owner's identity. Throw a preamble onto the post to indicate the true author. $item['body'] = "\n\n" + . '[quote]' . '[img]' . $item['author']['xchan_photo_s'] . '[/img]' - . '[url=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/url]' . "\n\n" - . $item['body']; + . ' ' + . '[url=' . $item['author']['xchan_url'] . '][b]' . $item['author']['xchan_name'] . '[/b][/url]' . "\n\n" + . $item['body'] + . '[/quote]'; } // $item['author'] might cause a surprise further down the line if it wasn't expected to be here. @@ -318,7 +324,7 @@ function bb2diaspora_itemwallwall(&$item) { } -function bb2diaspora_itembody($item, $force_update = false, $have_channel = false) { +function bb2diaspora_itembody($item, $force_update = false, $have_channel = false, $uplink) { if(! get_iconfig($item,'diaspora','fields')) { @@ -362,7 +368,7 @@ function bb2diaspora_itembody($item, $force_update = false, $have_channel = fals } if(! $have_channel) - bb2diaspora_itemwallwall($newitem); + bb2diaspora_itemwallwall($newitem,$uplink); $title = $newitem['title']; $body = preg_replace('/\#\^http/i', 'http', $newitem['body']); diff --git a/include/bbcode.php b/include/bbcode.php index db3a1011b..a82b658b1 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -12,16 +12,27 @@ require_once('include/hubloc.php'); function tryoembed($match) { $url = ((count($match) == 2) ? $match[1] : $match[2]); - $o = oembed_fetch_url($url); - if ($o->type == 'error') + if ($o['type'] == 'error') return $match[0]; $html = oembed_format_object($o); return $html; } + +function nakedoembed($match) { + $url = ((count($match) == 2) ? $match[1] : $match[2]); + + $o = oembed_fetch_url($url); + + if ($o['type'] == 'error') + return $match[0]; + + return '[embed]' . $url . '[/embed]'; +} + function tryzrlaudio($match) { $link = $match[1]; $zrl = is_matrix_url($link); @@ -516,6 +527,9 @@ function bb_fixtable_lf($match) { function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) { + + call_hooks('bbcode_filter', $Text); + // Hide all [noparse] contained bbtags by spacefying them if (strpos($Text,'[noparse]') !== false) { $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy',$Text); @@ -642,6 +656,9 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $urlchars = '[a-zA-Z0-9\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\+\,\@]'; if (strpos($Text,'http') !== false) { + if($tryoembed) { + $Text = preg_replace_callback("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/$urlchars+)/ism", 'tryoembed', $Text); + } $Text = preg_replace("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/$urlchars+)/ism", '$1<a href="$2" target="_blank" >$2</a>', $Text); } @@ -666,8 +683,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a class="zrl" href="$1" target="_blank" >$2</a>', $Text); } - // Remove bookmarks from UNO - if (get_config('system','server_role') === 'basic') + if (get_account_techlevel() < 2) $Text = str_replace('<span class="bookmark-identifier">#^</span>', '', $Text); // Perform MAIL Search @@ -769,11 +785,14 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) } // Check for list text $Text = str_replace("[*]", "<li>", $Text); + $Text = str_replace("[]", "<li><input type=\"checkbox\" disabled=\"disabled\">", $Text); + $Text = str_replace("[x]", "<li><input type=\"checkbox\" checked=\"checked\" disabled=\"disabled\">", $Text); // handle nested lists $endlessloop = 0; while ((((strpos($Text, "[/list]") !== false) && (strpos($Text, "[list") !== false)) || + ((strpos($Text, "[/checklist]") !== false) && (strpos($Text, "[checklist]") !== false)) || ((strpos($Text, "[/ol]") !== false) && (strpos($Text, "[ol]") !== false)) || ((strpos($Text, "[/ul]") !== false) && (strpos($Text, "[ul]") !== false)) || ((strpos($Text, "[/dl]") !== false) && (strpos($Text, "[dl") !== false)) || @@ -785,6 +804,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("/\[list=((?-i)I)\](.*?)\[\/list\]/ism", '<ul class="listupperroman" style="list-style-type: upper-roman;">$2</ul>', $Text); $Text = preg_replace("/\[list=((?-i)a)\](.*?)\[\/list\]/ism", '<ul class="listloweralpha" style="list-style-type: lower-alpha;">$2</ul>', $Text); $Text = preg_replace("/\[list=((?-i)A)\](.*?)\[\/list\]/ism", '<ul class="listupperalpha" style="list-style-type: upper-alpha;">$2</ul>', $Text); + $Text = preg_replace("/\[checklist\](.*?)\[\/checklist\]/ism", '<ul class="checklist" style="list-style-type: none;">$1</ul>', $Text); $Text = preg_replace("/\[ul\](.*?)\[\/ul\]/ism", '<ul class="listbullet" style="list-style-type: circle;">$1</ul>', $Text); $Text = preg_replace("/\[ol\](.*?)\[\/ol\]/ism", '<ul class="listdecimal" style="list-style-type: decimal;">$1</ul>', $Text); $Text = preg_replace("/\[li\](.*?)\[\/li\]/ism", '<li>$1</li>', $Text); @@ -942,13 +962,13 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace_callback("/\[video\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mpeg|mpg))\[\/video\]/ism", 'tryzrlvideo', $Text); } if (strpos($Text,'[/audio]') !== false) { - $Text = preg_replace_callback("/\[audio\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mp3|opus))\[\/audio\]/ism", 'tryzrlaudio', $Text); + $Text = preg_replace_callback("/\[audio\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mp3|opus|m4a))\[\/audio\]/ism", 'tryzrlaudio', $Text); } if (strpos($Text,'[/zvideo]') !== false) { $Text = preg_replace_callback("/\[zvideo\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mpeg|mpg))\[\/zvideo\]/ism", 'tryzrlvideo', $Text); } if (strpos($Text,'[/zaudio]') !== false) { - $Text = preg_replace_callback("/\[zaudio\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mp3|opus))\[\/zaudio\]/ism", 'tryzrlaudio', $Text); + $Text = preg_replace_callback("/\[zaudio\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mp3|opus|m4a))\[\/zaudio\]/ism", 'tryzrlaudio', $Text); } // Try to Oembed diff --git a/include/channel.php b/include/channel.php index bfd300a0d..c86cea6f1 100644 --- a/include/channel.php +++ b/include/channel.php @@ -297,7 +297,7 @@ function create_identity($arr) { dbesc($guid), dbesc($sig), dbesc($hash), - dbesc($ret['channel']['channel_address'] . '@' . App::get_hostname()), + dbesc(channel_reddress($ret['channel'])), intval($primary), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(),$ret['channel']['channel_prvkey']))), @@ -319,7 +319,7 @@ function create_identity($arr) { dbesc(z_root() . "/photo/profile/l/{$newuid}"), dbesc(z_root() . "/photo/profile/m/{$newuid}"), dbesc(z_root() . "/photo/profile/s/{$newuid}"), - dbesc($ret['channel']['channel_address'] . '@' . App::get_hostname()), + 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']), @@ -918,7 +918,7 @@ function profile_load($nickname, $profile = '') { App::$profile = $p[0]; App::$profile_uid = $p[0]['profile_uid']; - App::$page['title'] = App::$profile['channel_name'] . " - " . App::$profile['channel_address'] . "@" . App::get_hostname(); + App::$page['title'] = App::$profile['channel_name'] . " - " . channel_reddress(App::$profile); App::$profile['permission_to_view'] = $can_view_profile; @@ -936,7 +936,7 @@ function profile_load($nickname, $profile = '') { * load/reload current theme info */ - $_SESSION['theme'] = $p[0]['channel_theme']; +// $_SESSION['theme'] = $p[0]['channel_theme']; } @@ -1033,7 +1033,7 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa $connect_url = rconnect_url($profile['uid'],get_observer_hash()); $connect = (($connect_url) ? t('Connect') : ''); if($connect_url) - $connect_url = sprintf($connect_url,urlencode($profile['channel_address'] . '@' . App::get_hostname())); + $connect_url = sprintf($connect_url,urlencode(channel_reddress($profile))); // premium channel - over-ride @@ -1103,8 +1103,8 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa require_once('include/widgets.php'); - if(! feature_enabled($profile['uid'],'hide_rating')) - $z = widget_rating(array('target' => $profile['channel_hash'])); +// if(! feature_enabled($profile['uid'],'hide_rating')) + $z = widget_rating(array('target' => $profile['channel_hash'])); $o .= replace_macros($tpl, array( '$zcard' => $zcard, @@ -1212,7 +1212,7 @@ function advanced_profile(&$a) { if(App::$profile['partner']) $profile['marital']['partner'] = bbcode(App::$profile['partner']); - if(strlen(App::$profile['howlong']) && App::$profile['howlong'] !== NULL_DATE) { + if(strlen(App::$profile['howlong']) && App::$profile['howlong'] > NULL_DATE) { $profile['howlong'] = relative_date(App::$profile['howlong'], t('for %1$d %2$s')); } @@ -1381,6 +1381,11 @@ function zid($s,$address = '') { if (! strlen($s) || strpos($s,'zid=')) return $s; + $m = parse_url($s); + $fragment = ((array_key_exists('fragment',$m) && $m['fragment']) ? $m['fragment'] : false); + if($fragment !== false) + $s = str_replace('#' . $fragment,'',$s); + $has_params = ((strpos($s,'?')) ? true : false); $num_slashes = substr_count($s, '/'); if (! $has_params) @@ -1401,6 +1406,11 @@ function zid($s,$address = '') { else $zurl = $s; + // put fragment at the end + + if($fragment) + $zurl .= '#' . $fragment; + $arr = array('url' => $s, 'zid' => urlencode($myaddr), 'result' => $zurl); call_hooks('zid', $arr); @@ -1769,7 +1779,7 @@ function get_zcard($channel,$observer_hash = '',$args = array()) { // $translate = intval(($scale / 1.0) * 100); - $channel['channel_addr'] = $channel['channel_address'] . '@' . App::get_hostname(); + $channel['channel_addr'] = channel_reddress($channel); $zcard = array('chan' => $channel); $r = q("select height, width, resource_id, imgscale, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", @@ -1831,7 +1841,7 @@ function get_zcard_embed($channel,$observer_hash = '',$args = array()) { $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']); } - $channel['channel_addr'] = $channel['channel_address'] . '@' . App::get_hostname(); + $channel['channel_addr'] = channel_reddress($channel); $zcard = array('chan' => $channel); $r = q("select height, width, resource_id, imgscale, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", @@ -1884,3 +1894,8 @@ function channelx_by_n($id) { return(($r) ? $r[0] : false); } +function channel_reddress($channel) { + if(! ($channel && array_key_exists('channel_address',$channel))) + return ''; + return strtolower($channel['channel_address'] . '@' . App::get_hostname()); +}
\ No newline at end of file diff --git a/include/connections.php b/include/connections.php index 4f685388c..017117dda 100644 --- a/include/connections.php +++ b/include/connections.php @@ -608,7 +608,7 @@ function random_profile() { for($i = 0; $i < $retryrandom; $i++) { - $r = q("select xchan_url from xchan left join hubloc on hubloc_hash = xchan_hash where xchan_addr not like '%s' and hubloc_connected > %s - interval %s order by $randfunc limit 1", + $r = q("select xchan_url from xchan left join hubloc on hubloc_hash = xchan_hash where xchan_addr not like '%s' and xchan_hidden = 0 and hubloc_connected > %s - interval %s order by $randfunc limit 1", dbesc('sys@%'), db_utcnow(), db_quoteinterval('30 day') ); diff --git a/include/contact_widgets.php b/include/contact_widgets.php index 85c46b0d1..8f76bb4bc 100644 --- a/include/contact_widgets.php +++ b/include/contact_widgets.php @@ -13,7 +13,7 @@ function findpeople_widget() { } } - $advanced_search = ((local_channel() && get_pconfig(local_channel(),'feature','expert')) ? t('Advanced') : false); + $advanced_search = ((local_channel() && feature_enabled(local_channel(),'advanced_dirsearch')) ? t('Advanced') : false); return replace_macros(get_markup_template('peoplefind.tpl'),array( '$findpeople' => t('Find Channels'), diff --git a/include/conversation.php b/include/conversation.php index b53498d20..287dd4983 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -712,10 +712,8 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ 'forged' => $forged, 'txt_cats' => t('Categories:'), 'txt_folders' => t('Filed under:'), - 'has_cats' => ((count($categories)) ? 'true' : ''), - 'has_folders' => ((count($folders)) ? 'true' : ''), - 'categories' => $categories, - 'folders' => $folders, + 'has_cats' => ((count($body['categories'])) ? 'true' : ''), + 'has_folders' => ((count($body['folders'])) ? 'true' : ''), 'text' => strip_tags($body['html']), 'ago' => relative_date($item['created']), 'app' => $item['app'], @@ -723,7 +721,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'), 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), 'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''), - 'expiretime' => (($item['expires'] !== NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''), + 'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''), 'location' => $location, 'indent' => '', 'owner_name' => $owner_name, @@ -941,12 +939,9 @@ function item_photo_menu($item){ $clean_url = normalise_link($item['author-link']); } - $poco_rating = get_config('system','poco_rating_enable'); - // if unset default to enabled - if($poco_rating === false) - $poco_rating = true; + $rating_enabled = get_config('system','rating_enabled'); - $ratings_url = (($poco_rating) ? z_root() . '/ratings/' . urlencode($item['author_xchan']) : ''); + $ratings_url = (($rating_enabled) ? z_root() . '/ratings/' . urlencode($item['author_xchan']) : ''); $post_menu = Array( t("View Source") => $vsrc_link, @@ -1056,6 +1051,9 @@ function builtin_activity_puller($item, &$conv_responses) { $conv_responses[$mode][$item['thr_parent']] ++; $conv_responses[$mode][$item['thr_parent'] . '-l'][] = $url; + if(get_observer_hash() && get_observer_hash() === $item['author_xchan']) { + $conv_responses[$mode][$item['thr_parent'] . '-m'] = true; + } // there can only be one activity verb per item so if we found anything, we can stop looking return; @@ -1121,6 +1119,10 @@ function status_editor($a, $x, $popup = false) { $feature_voting = feature_enabled($x['profile_uid'], 'consensus_tools'); if(x($x, 'hide_voting')) $feature_voting = false; + + $feature_nocomment = feature_enabled($x['profile_uid'], 'disable_comments'); + if(x($x, 'disable_comments')) + $feature_nocomment = false; $feature_expire = ((feature_enabled($x['profile_uid'], 'content_expire') && (! $webpage)) ? true : false); if(x($x, 'hide_expire')) @@ -1190,12 +1192,12 @@ function status_editor($a, $x, $popup = false) { '$modalerrorlist' => t('Error getting album list'), '$modalerrorlink' => t('Error getting photo link'), '$modalerroralbum' => t('Error getting album'), + '$nocomment_enabled' => t('Comments enabled'), + '$nocomment_disabled' => t('Comments disabled'), )); $tpl = get_markup_template('jot.tpl'); - $jotplugins = ''; - $preview = t('Preview'); if(x($x, 'hide_preview')) $preview = ''; @@ -1212,8 +1214,18 @@ function status_editor($a, $x, $popup = false) { if(! $cipher) $cipher = 'aes256'; + // avoid illegal offset errors + if(! array_key_exists('permissions',$x)) + $x['permissions'] = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; + + $jotplugins = ''; call_hooks('jot_tool', $jotplugins); + $jotnets = ''; + if(x($x,'jotnets')) { + call_hooks('jot_networks', $jotnets); + } + $o .= replace_macros($tpl, array( '$return_path' => ((x($x, 'return_path')) ? $x['return_path'] : App::$query_string), '$action' => z_root() . '/item', @@ -1239,6 +1251,10 @@ function status_editor($a, $x, $popup = false) { '$voting' => t('Toggle voting'), '$feature_voting' => $feature_voting, '$consensus' => 0, + '$nocommenttitle' => t('Disable comments'), + '$nocommenttitlesub' => t('Toggle comments'), + '$feature_nocomment' => $feature_nocomment, + '$nocomment' => 0, '$clearloc' => $clearloc, '$title' => ((x($x, 'title')) ? htmlspecialchars($x['title'], ENT_COMPAT,'UTF-8') : ''), '$placeholdertitle' => ((x($x, 'placeholdertitle')) ? $x['placeholdertitle'] : t('Title (optional)')), @@ -1266,6 +1282,8 @@ function status_editor($a, $x, $popup = false) { '$preview' => $preview, '$source' => ((x($x, 'source')) ? $x['source'] : ''), '$jotplugins' => $jotplugins, + '$jotnets' => $jotnets, + '$jotnets_label' => t('Other networks and post services'), '$defexpire' => $defexpire, '$feature_expire' => $feature_expire, '$expires' => t('Set expiration date'), @@ -1608,6 +1626,8 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ $uid = ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : local_channel()); + $account_id = ((App::$profile['profile_uid']) ? App::$profile['channel_account_id'] : App::$channel['channel_account_id']); + if($uid == local_channel()) { $cal_link = ''; @@ -1710,7 +1730,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ ); } - if(feature_enabled($uid,'wiki') && (get_config('system','server_role') !== 'basic')) { + if(feature_enabled($uid,'wiki') && (get_account_techlevel($account_id) > 3)) { $tabs[] = array( 'label' => t('Wiki'), 'url' => z_root() . '/wiki/' . $nickname, diff --git a/include/datetime.php b/include/datetime.php index 76bd6b8d6..cd08ab367 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -24,6 +24,13 @@ function timezone_cmp($a, $b) { return ( t($a) < t($b)) ? -1 : 1; } +function is_null_date($s) { + if($s === '0000-00-00 00:00:00' || $s === '0001-01-01 00:00:00') + return true; + return false; +} + + /** * @brief Return timezones grouped (primarily) by continent. * @@ -78,15 +85,20 @@ function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d if( ($s === '') || (! is_string($s)) ) $s = 'now'; + if(is_null_date($s)) { + $d = new DateTime('0001-01-01 00:00:00', new DateTimeZone('UTC')); + return $d->format($fmt); + } + // Slight hackish adjustment so that 'zero' datetime actually returns what is intended // otherwise we end up with -0001-11-30 ... // add 32 days so that we at least get year 00, and then hack around the fact that // months and days always start with 1. - if(substr($s,0,10) == '0000-00-00') { - $d = new DateTime($s . ' + 32 days', new DateTimeZone('UTC')); - return str_replace('1', '0', $d->format($fmt)); - } +// if(substr($s,0,10) == '0000-00-00') { +// $d = new DateTime($s . ' + 32 days', new DateTimeZone('UTC')); +// return str_replace('1', '0', $d->format($fmt)); +// } try { $from_obj = new DateTimeZone($from); @@ -268,7 +280,7 @@ function relative_date($posted_date, $format = null) { $abs = strtotime($localtime); - if (is_null($posted_date) || $posted_date === NULL_DATE || $abs === false) { + if (is_null($posted_date) || is_null_date($posted_date) || $abs === false) { return t('never'); } diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php index 1905241a7..852dc16af 100755 --- a/include/dba/dba_driver.php +++ b/include/dba/dba_driver.php @@ -62,6 +62,8 @@ class DBA { if(is_object(self::$dba) && self::$dba->connected) { + if($server === 'localhost') + $port = $set_port; $dns = ((self::$dbtype == DBTYPE_POSTGRES) ? 'postgres' : 'mysql') . ':host=' . $server . (is_null($port) ? '' : ';port=' . $port) . ';dbname=' . $db; @@ -84,7 +86,7 @@ class DBA { abstract class dba_driver { // legacy behavior const INSTALL_SCRIPT='install/schema_mysql.sql'; - const NULL_DATE = '0000-00-00 00:00:00'; + const NULL_DATE = '0001-01-01 00:00:00'; const UTC_NOW = 'UTC_TIMESTAMP()'; protected $db; @@ -260,11 +262,8 @@ function dbg($state) { */ function dbesc($str) { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES && $str === '0000-00-00 00:00:00') { + if(is_null_date($str)) $str = NULL_DATE; - } else if(ACTIVE_DBTYPE != DBTYPE_POSTGRES && $str === '0001-01-01 00:00:00') { - $str = NULL_DATE; - } if(\DBA::$dba && \DBA::$dba->connected) return(\DBA::$dba->escape($str)); @@ -280,12 +279,9 @@ function dbunescbin($str) { } function dbescdate($date) { - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES && $date === '0000-00-00 00:00:00') { - $date = NULL_DATE; - } else if(ACTIVE_DBTYPE != DBTYPE_POSTGRES && $date === '0001-01-01 00:00:00') { - $date = NULL_DATE; - } - return $date; + if(is_null_date($date)) + return \DBA::$dba->escape(NULL_DATE); + return \DBA::$dba->escape($date); } function db_quoteinterval($txt) { @@ -380,11 +376,8 @@ function dbq($sql) { function dbesc_array_cb(&$item, $key) { if(is_string($item)) { - if($item == '0000-00-00 00:00:00' && ACTIVE_DBTYPE == DBTYPE_POSTGRES) - $item = '0001-01-01 00:00:00'; - else if($item == '0001-01-01 00:00:00' && ACTIVE_DBTYPE == DBTYPE_MYSQL) - $item = '0000-00-00 00:00:00'; - + if(is_null_date($item)) + $item = NULL_DATE; $item = dbesc($item); } } @@ -439,4 +432,4 @@ function db_logger($s,$level = LOGGER_NORMAL,$syslog = LOG_INFO) { logger($s,$level,$syslog); \DBA::$logging = false; \DBA::$dba->debug = $saved; -}
\ No newline at end of file +} diff --git a/include/dir_fns.php b/include/dir_fns.php index 9f1be1a42..03cc2706a 100644 --- a/include/dir_fns.php +++ b/include/dir_fns.php @@ -227,7 +227,7 @@ function sync_directories($dirmode) { $token = get_config('system','realm_token'); - $syncdate = (($rr['site_sync'] === NULL_DATE) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']); + $syncdate = (($rr['site_sync'] <= NULL_DATE) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']); $x = z_fetch_url($rr['site_directory'] . '?f=&sync=' . urlencode($syncdate) . (($token) ? '&t=' . $token : '')); if (! $x['success']) @@ -423,7 +423,7 @@ function local_dir_update($uid, $force) { $arr = array('channel_id' => $uid, 'hash' => $hash, 'profile' => $profile); call_hooks('local_dir_update', $arr); - $address = $p[0]['channel_address'] . '@' . App::get_hostname(); + $address = channel_reddress($p[0]); if (perm_is_allowed($uid, '', 'view_profile')) { import_directory_profile($hash, $arr['profile'], $address, 0); @@ -439,5 +439,5 @@ function local_dir_update($uid, $force) { } $ud_hash = random_string() . '@' . App::get_hostname(); - update_modtime($hash, $ud_hash, $p[0]['channel_address'] . '@' . App::get_hostname(),(($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED)); + update_modtime($hash, $ud_hash, channel_reddress($p[0]),(($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED)); } diff --git a/include/event.php b/include/event.php index 3d650cd14..153654120 100644 --- a/include/event.php +++ b/include/event.php @@ -343,6 +343,13 @@ function event_store_event($arr) { } } + $hook_info = [ 'event' => $arr, 'existing_event' => $existing_event, 'cancel' => false ]; + call_hooks('event_store_event',$hook_info); + if($hook_info['cancel']) + return false; + + $arr = $hook_info['event']; + $existing_event = $hook_info['existing_event']; if($existing_event) { @@ -371,6 +378,7 @@ function event_store_event($arr) { `event_repeat` = '%s', `event_sequence` = %d, `event_priority` = %d, + `event_vdata` = '%s', `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', @@ -392,6 +400,7 @@ function event_store_event($arr) { dbesc($arr['event_repeat']), intval($arr['event_sequence']), intval($arr['event_priority']), + dbesc($arr['event_vdata']), dbesc($arr['allow_cid']), dbesc($arr['allow_gid']), dbesc($arr['deny_cid']), @@ -412,8 +421,8 @@ function event_store_event($arr) { $hash = random_string() . '@' . App::get_hostname(); $r = q("INSERT INTO event ( uid,aid,event_xchan,event_hash,created,edited,dtstart,dtend,summary,description,location,etype, - adjust,nofinish, event_status, event_status_date, event_percent, event_repeat, event_sequence, event_priority, allow_cid,allow_gid,deny_cid,deny_gid) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', %d, '%s', %d, %d, '%s', '%s', '%s', '%s' ) ", + adjust,nofinish, event_status, event_status_date, event_percent, event_repeat, event_sequence, event_priority, event_vdata, allow_cid,allow_gid,deny_cid,deny_gid) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s' ) ", intval($arr['uid']), intval($arr['account']), dbesc($arr['event_xchan']), @@ -434,6 +443,7 @@ function event_store_event($arr) { dbesc($arr['event_repeat']), intval($arr['event_sequence']), intval($arr['event_priority']), + dbesc($arr['event_vdata']), dbesc($arr['allow_cid']), dbesc($arr['allow_gid']), dbesc($arr['deny_cid']), diff --git a/include/features.php b/include/features.php index 041c028c6..1ccdbf015 100644 --- a/include/features.php +++ b/include/features.php @@ -36,96 +36,419 @@ function get_feature_default($feature) { } +function feature_level($feature,$def) { + $x = get_config('feature_level',$feature); + if($x !== false) + return intval($x); + return $def; +} + function get_features($filtered = true) { - $server_role = get_config('system','server_role'); + $server_role = \Zotlabs\Lib\System::get_server_role(); if($server_role === 'basic' && $filtered) return array(); - $arr = array( + $arr = [ // General - 'general' => array( + 'general' => [ + t('General Features'), - // This is per post, and different from fixed expiration 'expire' which isn't working yet - array('content_expire', t('Content Expiration'), t('Remove posts/comments and/or private messages at a future time'), false, get_config('feature_lock','content_expire')), - array('multi_profiles', t('Multiple Profiles'), t('Ability to create multiple profiles'), false, get_config('feature_lock','multi_profiles')), - array('advanced_profiles', t('Advanced Profiles'), t('Additional profile sections and selections'),false,get_config('feature_lock','advanced_profiles')), - array('profile_export', t('Profile Import/Export'), t('Save and load profile details across sites/channels'),false,get_config('feature_lock','profile_export')), - array('webpages', t('Web Pages'), t('Provide managed web pages on your channel'),false,get_config('feature_lock','webpages')), - array('wiki', t('Wiki'), t('Provide a wiki for your channel'),(($server_role === 'basic') ? false : true),get_config('feature_lock','wiki')), - array('hide_rating', t('Hide Rating'), t('Hide the rating buttons on your channel and profile pages. Note: People can still rate you somewhere else.'),false,get_config('feature_lock','hide_rating')), - array('private_notes', t('Private Notes'), t('Enables a tool to store notes and reminders (note: not encrypted)'),false,get_config('feature_lock','private_notes')), - array('nav_channel_select', t('Navigation Channel Select'), t('Change channels directly from within the navigation dropdown menu'),false,get_config('feature_lock','nav_channel_select')), - array('photo_location', t('Photo Location'), t('If location data is available on uploaded photos, link this to a map.'),false,get_config('feature_lock','photo_location')), - array('ajaxchat', t('Access Controlled Chatrooms'), t('Provide chatrooms and chat services with access control.'),true,get_config('feature_lock','ajaxchat')), - array('smart_birthdays', t('Smart Birthdays'), t('Make birthday events timezone aware in case your friends are scattered across the planet.'),true,get_config('feature_lock','smart_birthdays')), - array('expert', t('Expert Mode'), t('Enable Expert Mode to provide advanced configuration options'),false,get_config('feature_lock','expert')), - array('premium_channel', t('Premium Channel'), t('Allows you to set restrictions and terms on those that connect with your channel'),false,get_config('feature_lock','premium_channel')), - ), + + + [ + 'multi_profiles', + t('Multiple Profiles'), + t('Ability to create multiple profiles'), + false, + get_config('feature_lock','multi_profiles'), + feature_level('multi_profiles',3), + ], + + [ + 'advanced_profiles', + t('Advanced Profiles'), + t('Additional profile sections and selections'), + false, + get_config('feature_lock','advanced_profiles'), + feature_level('advanced_profiles',1), + ], + + [ + 'profile_export', + t('Profile Import/Export'), + t('Save and load profile details across sites/channels'), + false, + get_config('feature_lock','profile_export'), + feature_level('profile_export',3), + ], + + [ + 'webpages', + t('Web Pages'), + t('Provide managed web pages on your channel'), + false, + get_config('feature_lock','webpages'), + feature_level('webpages',3), + ], + + [ + 'wiki', + t('Wiki'), + t('Provide a wiki for your channel'), + false, + get_config('feature_lock','wiki'), + feature_level('wiki',2), + ], +/* + [ + 'hide_rating', + t('Hide Rating'), + t('Hide the rating buttons on your channel and profile pages. Note: People can still rate you somewhere else.'), + false, + get_config('feature_lock','hide_rating'), + feature_level('hide_rating',3), + ], +*/ + [ + 'private_notes', + t('Private Notes'), + t('Enables a tool to store notes and reminders (note: not encrypted)'), + false, + get_config('feature_lock','private_notes'), + feature_level('private_notes',1), + ], + + [ + 'nav_channel_select', + t('Navigation Channel Select'), + t('Change channels directly from within the navigation dropdown menu'), + false, + get_config('feature_lock','nav_channel_select'), + feature_level('nav_channel_select',3), + ], + + [ + 'photo_location', + t('Photo Location'), + t('If location data is available on uploaded photos, link this to a map.'), + false, + get_config('feature_lock','photo_location'), + feature_level('photo_location',2), + ], + + [ + 'ajaxchat', + t('Access Controlled Chatrooms'), + t('Provide chatrooms and chat services with access control.'), + true, + get_config('feature_lock','ajaxchat'), + feature_level('ajaxchat',1), + ], + + [ + 'smart_birthdays', + t('Smart Birthdays'), + t('Make birthday events timezone aware in case your friends are scattered across the planet.'), + true, + get_config('feature_lock','smart_birthdays'), + feature_level('smart_birthdays',2), + ], + + [ + 'advanced_dirsearch', + t('Advanced Directory Search'), + t('Allows creation of complex directory search queries'), + false, + get_config('feature_lock','advanced_dirsearch'), + feature_level('advanced_dirsearch',4), + ], + + [ + 'advanced_theming', + t('Advanced Theme and Layout Settings'), + t('Allows fine tuning of themes and page layouts'), + false, + get_config('feature_lock','advanced_theming'), + feature_level('advanced_theming',4), + ], + ], // Post composition - 'composition' => array( + 'composition' => [ + t('Post Composition Features'), -// array('richtext', t('Richtext Editor'), t('Enable richtext editor'),falseget_config('feature_lock','richtext')), -// array('markdown', t('Use Markdown'), t('Allow use of "Markdown" to format posts'),false,get_config('feature_lock','markdown')), - array('large_photos', t('Large Photos'), t('Include large (1024px) photo thumbnails in posts. If not enabled, use small (640px) photo thumbnails'),false,get_config('feature_lock','large_photos')), - array('channel_sources', t('Channel Sources'), t('Automatically import channel content from other channels or feeds'),false,get_config('feature_lock','channel_sources')), - array('content_encrypt', t('Even More Encryption'), t('Allow optional encryption of content end-to-end with a shared secret key'),false,get_config('feature_lock','content_encrypt')), - array('consensus_tools', t('Enable Voting Tools'), t('Provide a class of post which others can vote on'),false,get_config('feature_lock','consensus_tools')), - array('delayed_posting', t('Delayed Posting'), t('Allow posts to be published at a later date'),false,get_config('feature_lock','delayed_posting')), - array('suppress_duplicates', t('Suppress Duplicate Posts/Comments'), t('Prevent posts with identical content to be published with less than two minutes in between submissions.'),true,get_config('feature_lock','suppress_duplicates')), - ), + [ + 'large_photos', + t('Large Photos'), + t('Include large (1024px) photo thumbnails in posts. If not enabled, use small (640px) photo thumbnails'), + false, + get_config('feature_lock','large_photos'), + feature_level('large_photos',1), + ], + + [ + 'channel_sources', + t('Channel Sources'), + t('Automatically import channel content from other channels or feeds'), + false, + get_config('feature_lock','channel_sources'), + feature_level('channel_sources',3), + ], + + [ + 'content_encrypt', + t('Even More Encryption'), + t('Allow optional encryption of content end-to-end with a shared secret key'), + false, + get_config('feature_lock','content_encrypt'), + feature_level('content_encrypt',3), + ], + + [ + 'consensus_tools', + t('Enable Voting Tools'), + t('Provide a class of post which others can vote on'), + false, + get_config('feature_lock','consensus_tools'), + feature_level('consensus_tools',3), + ], + + [ + 'disable_comments', + t('Disable Comments'), + t('Provide the option to disable comments for a post'), + false, + get_config('feature_lock','disable_comments'), + feature_level('disable_comments',2), + ], + + [ + 'delayed_posting', + t('Delayed Posting'), + t('Allow posts to be published at a later date'), + false, + get_config('feature_lock','delayed_posting'), + feature_level('delayed_posting',2), + ], + + [ + 'content_expire', + t('Content Expiration'), + t('Remove posts/comments and/or private messages at a future time'), + false, + get_config('feature_lock','content_expire'), + feature_level('content_expire',1), + ], + + [ + 'suppress_duplicates', + t('Suppress Duplicate Posts/Comments'), + t('Prevent posts with identical content to be published with less than two minutes in between submissions.'), + true, + get_config('feature_lock','suppress_duplicates'), + feature_level('suppress_duplicates',1), + ], + + ], // Network Tools - 'net_module' => array( + 'net_module' => [ + t('Network and Stream Filtering'), - array('archives', t('Search by Date'), t('Ability to select posts by date ranges'),false,get_config('feature_lock','archives')), - array('groups', t('Privacy Groups'), t('Enable management and selection of privacy groups'),true,get_config('feature_lock','groups')), - array('savedsearch', t('Saved Searches'), t('Save search terms for re-use'),false,get_config('feature_lock','savedsearch')), - array('personal_tab', t('Network Personal Tab'), t('Enable tab to display only Network posts that you\'ve interacted on'),false,get_config('feature_lock','personal_tab')), - array('new_tab', t('Network New Tab'), t('Enable tab to display all new Network activity'),false,get_config('feature_lock','new_tab')), - array('affinity', t('Affinity Tool'), t('Filter stream activity by depth of relationships'),false,get_config('feature_lock','affinity')), - array('connfilter', t('Connection Filtering'), t('Filter incoming posts from connections based on keywords/content'),false,get_config('feature_lock','connfilter')), - array('suggest', t('Suggest Channels'), t('Show channel suggestions'),false,get_config('feature_lock','suggest')), - ), + + [ + 'archives', + t('Search by Date'), + t('Ability to select posts by date ranges'), + false, + get_config('feature_lock','archives'), + feature_level('archives',1), + ], + + [ + 'groups', + t('Privacy Groups'), + t('Enable management and selection of privacy groups'), + true, + get_config('feature_lock','groups'), + feature_level('groups',0), + ], + + [ + 'savedsearch', + t('Saved Searches'), + t('Save search terms for re-use'), + false, + get_config('feature_lock','savedsearch'), + feature_level('savedsearch',2), + ], + + [ + 'personal_tab', + t('Network Personal Tab'), + t('Enable tab to display only Network posts that you\'ve interacted on'), + false, + get_config('feature_lock','personal_tab'), + feature_level('personal_tab',1), + ], + + [ + 'new_tab', + t('Network New Tab'), + t('Enable tab to display all new Network activity'), + false, + get_config('feature_lock','new_tab'), + feature_level('new_tab',2), + ], + + [ + 'affinity', + t('Affinity Tool'), + t('Filter stream activity by depth of relationships'), + false, + get_config('feature_lock','affinity'), + feature_level('affinity',1), + ], + + [ + 'suggest', + t('Suggest Channels'), + t('Show friend and connection suggestions'), + false, + get_config('feature_lock','suggest'), + feature_level('suggest',1), + ], + + [ + 'connfilter', + t('Connection Filtering'), + t('Filter incoming posts from connections based on keywords/content'), + false, + get_config('feature_lock','connfilter'), + feature_level('connfilter',3), + ], + + + ], // Item tools - 'tools' => array( + 'tools' => [ + t('Post/Comment Tools'), - array('commtag', t('Community Tagging'), t('Ability to tag existing posts'),false,get_config('feature_lock','commtag')), - array('categories', t('Post Categories'), t('Add categories to your posts'),false,get_config('feature_lock','categories')), - array('emojis', t('Emoji Reactions'), t('Add emoji reaction ability to posts'),true,get_config('feature_lock','emojis')), - array('filing', t('Saved Folders'), t('Ability to file posts under folders'),false,get_config('feature_lock','filing')), - array('dislike', t('Dislike Posts'), t('Ability to dislike posts/comments'),false,get_config('feature_lock','dislike')), - array('star_posts', t('Star Posts'), t('Ability to mark special posts with a star indicator'),false,get_config('feature_lock','star_posts')), - array('tagadelic', t('Tag Cloud'), t('Provide a personal tag cloud on your channel page'),false,get_config('feature_lock','tagedelic')), - ), - ); + + [ + 'commtag', + t('Community Tagging'), + t('Ability to tag existing posts'), + false, + get_config('feature_lock','commtag'), + feature_level('commtag',1), + ], + + [ + 'categories', + t('Post Categories'), + t('Add categories to your posts'), + false, + get_config('feature_lock','categories'), + feature_level('categories',1), + ], + + [ + 'emojis', + t('Emoji Reactions'), + t('Add emoji reaction ability to posts'), + true, + get_config('feature_lock','emojis'), + feature_level('emojis',1), + ], + + [ + 'filing', + t('Saved Folders'), + t('Ability to file posts under folders'), + false, + get_config('feature_lock','filing'), + feature_level('filing',2), + ], + + [ + 'dislike', + t('Dislike Posts'), + t('Ability to dislike posts/comments'), + false, + get_config('feature_lock','dislike'), + feature_level('dislike',1), + ], + + [ + 'star_posts', + t('Star Posts'), + t('Ability to mark special posts with a star indicator'), + false, + get_config('feature_lock','star_posts'), + feature_level('star_posts',1), + ], + + [ + 'tagadelic', + t('Tag Cloud'), + t('Provide a personal tag cloud on your channel page'), + false, + get_config('feature_lock','tagadelic'), + feature_level('tagadelic',2), + ], + ], + ]; + + + if($server_role === 'pro') { + $arr['general'][] = [ + 'premium_channel', + t('Premium Channel'), + t('Allows you to set restrictions and terms on those that connect with your channel'), + false, + get_config('feature_lock','premium_channel'), + feature_level('premium_channel',4), + ]; + } + + $techlevel = get_account_techlevel(); // removed any locked features and remove the entire category if this makes it empty if($filtered) { + $narr = []; foreach($arr as $k => $x) { + $narr[$k] = [ $arr[$k][0] ]; $has_items = false; - for($y = 0; $y < count($arr[$k]); $y ++) { + for($y = 0; $y < count($arr[$k]); $y ++) { + $disabled = false; if(is_array($arr[$k][$y])) { - if($arr[$k][$y][4] === false) { - $has_items = true; + if($arr[$k][$y][5] > $techlevel) { + $disabled = true; + } + if($arr[$k][$y][4] !== false) { + $disabled = true; } - else { - unset($arr[$k][$y]); + if(! $disabled) { + $has_items = true; + $narr[$k][$y] = $arr[$k][$y]; } } } if(! $has_items) { - unset($arr[$k]); + unset($narr[$k]); } } } - - call_hooks('get_features',$arr); - return $arr; + else { + $narr = $arr; + } + call_hooks('get_features',$narr); + return $narr; } diff --git a/include/feedutils.php b/include/feedutils.php index 01ec0687e..1d58ec317 100644 --- a/include/feedutils.php +++ b/include/feedutils.php @@ -33,7 +33,7 @@ function get_public_feed($channel, $params) { // put a sane lower limit on feed requests if not specified -// if($params['begin'] === NULL_DATE) +// if($params['begin'] <= NULL_DATE) // $params['begin'] = datetime_convert('UTC','UTC','now - 1 month'); switch($params['type']) { @@ -884,7 +884,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $datarray['owner_xchan'] = $contact['xchan_hash']; - if(array_key_exists('created',$datarray) && $datarray['created'] != NULL_DATE && $expire_days) { + if(array_key_exists('created',$datarray) && $datarray['created'] > NULL_DATE && $expire_days) { $t1 = $datarray['created']; $t2 = datetime_convert('UTC','UTC','now - ' . $expire_days . 'days'); if($t1 < $t2) { diff --git a/include/help.php b/include/help.php index 7f57f3334..3081ae41f 100644 --- a/include/help.php +++ b/include/help.php @@ -1,5 +1,107 @@ <?php +function get_help_content($tocpath = false) { + + global $lang; + + $doctype = 'markdown'; + + $text = ''; + + $path = (($tocpath !== false) ? $tocpath : ''); + + if($tocpath === false && argc() > 1) { + $path = ''; + for($x = 1; $x < argc(); $x ++) { + if(strlen($path)) + $path .= '/'; + $path .= argv($x); + } + } + + if($path) { + $title = basename($path); + if(! $tocpath) + \App::$page['title'] = t('Help:') . ' ' . ucwords(str_replace('-',' ',notags($title))); + + $text = load_doc_file('doc/' . $path . '.md'); + + if(! $text) { + $text = load_doc_file('doc/' . $path . '.bb'); + if($text) + $doctype = 'bbcode'; + } + if(! $text) { + $text = load_doc_file('doc/' . $path . '.html'); + if($text) + $doctype = 'html'; + } + } + + if(($tocpath) && (! $text)) + return ''; + + if($tocpath === false) { + if(! $text) { + $text = load_doc_file('doc/Site.md'); + \App::$page['title'] = t('Help'); + } + if(! $text) { + $doctype = 'bbcode'; + $text = load_doc_file('doc/main.bb'); + \App::$page['title'] = t('Help'); + } + + if(! $text) { + header($_SERVER["SERVER_PROTOCOL"] . ' 404 ' . t('Not Found')); + $tpl = get_markup_template("404.tpl"); + return replace_macros($tpl, array( + '$message' => t('Page not found.' ) + )); + } + } + + if($doctype === 'html') + $content = $text; + if($doctype === 'markdown') { + require_once('library/markdown.php'); + # escape #include tags + $text = preg_replace('/#include/ism', '%%include', $text); + $content = Markdown($text); + $content = preg_replace('/%%include/ism', '#include', $content); + } + if($doctype === 'bbcode') { + require_once('include/bbcode.php'); + $content = bbcode($text); + // bbcode retargets external content to new windows. This content is internal. + $content = str_replace(' target="_blank"','',$content); + } + + $content = preg_replace_callback("/#include (.*?)\;/ism", 'preg_callback_help_include', $content); + return translate_projectname($content); + +} + +function preg_callback_help_include($matches) { + + if($matches[1]) { + $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 = str_replace(' target="_blank"','',$include); + } + elseif(preg_match('/\.md$/', $matches[1])) { + require_once('library/markdown.php'); + $include = Markdown($include); + } + return $include; + } + +} + + + function load_doc_file($s) { $lang = \App::$language; if(! isset($lang)) @@ -17,8 +119,9 @@ function load_doc_file($s) { } function find_doc_file($s) { - if(file_exists($s)) + if(file_exists($s)) { return file_get_contents($s); + } return ''; } @@ -40,8 +143,13 @@ function search_doc_files($s) { $r = fetch_post_tags($r,true); for($x = 0; $x < count($r); $x ++) { - - $r[$x]['text'] = $r[$x]['body']; + $position = stripos($r[$x]['body'], $s); + $dislen = 300; + $start = $position-floor($dislen/2); + if ( $start < 0) { + $start = 0; + } + $r[$x]['text'] = substr($r[$x]['body'], $start, $dislen); $r[$x]['rank'] = 0; if($r[$x]['term']) { diff --git a/include/html2bbcode.php b/include/html2bbcode.php index 9ffc85a82..29880a627 100644 --- a/include/html2bbcode.php +++ b/include/html2bbcode.php @@ -2,7 +2,7 @@ /* html2bbcode.php Converter for HTML to BBCode -Made by: ike@piratenpartei.de +Made by: Mike@piratenpartei.de Originally made for the syncom project: http://wiki.piratenpartei.de/Syncom https://github.com/annando/Syncom */ diff --git a/include/import.php b/include/import.php index 5bbed828f..479e45cc2 100644 --- a/include/import.php +++ b/include/import.php @@ -422,9 +422,9 @@ function sync_apps($channel,$apps) { ); } - if(! $app['app_created'] || $app['app_created'] === NULL_DATE) + if((! $app['app_created']) || ($app['app_created'] <= NULL_DATE)) $app['app_created'] = datetime_convert(); - if(! $app['app_edited'] || $app['app_edited'] === NULL_DATE) + if((! $app['app_edited']) || ($app['app_edited'] <= NULL_DATE)) $app['app_edited'] = datetime_convert(); $app['app_channel'] = $channel['channel_id']; @@ -536,9 +536,9 @@ function sync_chatrooms($channel,$chatrooms) { unset($chatroom['cr_aid']); unset($chatroom['cr_uid']); - if(! $chatroom['cr_created'] || $chatroom['cr_created'] === NULL_DATE) + if((! $chatroom['cr_created']) || ($chatroom['cr_created'] <= NULL_DATE)) $chatroom['cr_created'] = datetime_convert(); - if(! $chatroom['cr_edited'] || $chatroom['cr_edited'] === NULL_DATE) + if((! $chatroom['cr_edited']) || ($chatroom['cr_edited'] <= NULL_DATE)) $chatroom['cr_edited'] = datetime_convert(); $chatroom['cr_aid'] = $channel['channel_account_id']; @@ -629,6 +629,10 @@ function import_items($channel,$items,$sync = false,$relocate = null) { $item_result = item_store($item,$allow_code,$deliver); } + fix_attached_photo_permissions($channel['channel_id'],$item['author_xchan'],$item['body'],$item['allow_cid'],$item['allow_gid'],$item['deny_cid'],$item['deny_gid']); + + fix_attached_file_permissions($channel,$item['author_xchan'],$item['body'],$item['allow_cid'],$item['allow_gid'],$item['deny_cid'],$item['deny_gid']); + if($sync && $item['item_wall']) { // deliver singletons if we have any if($item_result && $item_result['success']) { @@ -1032,7 +1036,7 @@ function sync_files($channel,$files) { } $attach_exists = false; - $x = attach_by_hash($att['hash']); + $x = attach_by_hash($att['hash'],$channel['channel_hash']); logger('sync_files duplicate check: attach_exists=' . $attach_exists, LOGGER_DEBUG); logger('sync_files duplicate check: att=' . print_r($att,true), LOGGER_DEBUG); logger('sync_files duplicate check: attach_by_hash() returned ' . print_r($x,true), LOGGER_DEBUG); @@ -1102,8 +1106,11 @@ function sync_files($channel,$files) { // If the hash ever contains any escapable chars this could cause // problems. Currently it does not. - dbesc_array($att); + // @TODO implement os_path + if(!isset($att['os_path'])) + $att['os_path'] = ''; + dbesc_array($att); if($attach_exists) { logger('sync_files attach exists: ' . print_r($att,true), LOGGER_DEBUG); @@ -1213,6 +1220,9 @@ function sync_files($channel,$files) { $p['content'] = base64_decode($p['content']); + if(!isset($p['display_path'])) + $p['display_path'] = ''; + $exists = q("select * from photo where resource_id = '%s' and imgscale = %d and uid = %d limit 1", dbesc($p['resource_id']), intval($p['imgscale']), @@ -1308,8 +1318,12 @@ function scan_webpage_elements($path, $type, $cloud = false) { } $content = file_get_contents($folder . '/' . $contentfilename); if (!$content) { - logger('Failed to get file content for ' . $metadata['contentfile']); - return false; + if(is_readable($folder . '/' . $contentfilename)) { + $content = ''; + } else { + logger('Failed to get file content for ' . $metadata['contentfile']); + return false; + } } $elements[] = $metadata; } @@ -1391,12 +1405,12 @@ function scan_webpage_elements($path, $type, $cloud = false) { ); $arr['mid'] = $arr['parent_mid'] = $iteminfo[0]['mid']; $arr['created'] = $iteminfo[0]['created']; - $arr['edited'] = (($element['edited']) ? datetime_convert('UTC', 'UTC', $element['edited']) : datetime_convert()); } else { // otherwise, generate the creation times and unique id - $arr['created'] = (($element['created']) ? datetime_convert('UTC', 'UTC', $element['created']) : datetime_convert()); - $arr['edited'] = datetime_convert('UTC', 'UTC', '0000-00-00 00:00:00'); + $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 @@ -1411,7 +1425,7 @@ function scan_webpage_elements($path, $type, $cloud = false) { 'application/x-pdl', 'application/x-php' ]; - // Blocks and pages can have any mimetype, but layouts must be text/bbcode + // 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 { @@ -1420,7 +1434,7 @@ function scan_webpage_elements($path, $type, $cloud = false) { // Verify ability to use html or php!!! $execflag = false; - if ($arr['mimetype'] === 'application/x-php') { + 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()) @@ -1428,10 +1442,15 @@ function scan_webpage_elements($path, $type, $cloud = false) { 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; } } - $z = q("select * from iconfig where v = '%s' and k = '%s' and cat = 'service' limit 1", + $z = q("select * from iconfig where v = '%s' and k = '%s' and cat = 'system' limit 1", dbesc($name), dbesc($namespace) ); @@ -1468,3 +1487,193 @@ function scan_webpage_elements($path, $type, $cloud = false) { 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']; + + $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) + ); + + $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($type !== 'all') { + break; + } + + case 'layouts': + $elements['layouts'] = null; + $owner = $channel['channel_id']; + + $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) + ); + + $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($type !== 'all') { + break; + } + + case 'blocks': + $elements['blocks'] = null; + $owner = $channel['channel_id']; + + $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) + ); + + $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($type !== 'all') { + break; + } + + 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; + } + //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; + } +} diff --git a/include/items.php b/include/items.php index 5bd0b0968..c62d53c3e 100755 --- a/include/items.php +++ b/include/items.php @@ -113,6 +113,26 @@ function collect_recipients($item, &$private_envelope) { // if($policy === 'pub') // $recipients[] = $sys['xchan_hash']; } + + // Add the authors of any posts in this thread, if they are known to us. + // This is specifically designed to forward wall-to-wall posts to the original author, + // in case they aren't a connection but have permission to write on our wall. + // This is important for issue tracker channels. It should be a no-op for most channels. + // Whether or not they will accept the delivery is not determined here, but should + // be taken into account by zot:process_delivery() + + $r = q("select author_xchan from item where parent = %d", + intval($item['parent']) + ); + if($r) { + foreach($r as $rv) { + if(! in_array($rv['author_xchan'],$recipients)) { + $recipients[] = $rv['author_xchan']; + } + } + } + + } // This is a somewhat expensive operation but important. @@ -143,7 +163,7 @@ function collect_recipients($item, &$private_envelope) { } function comments_are_now_closed($item) { - if($item['comments_closed'] !== NULL_DATE) { + if($item['comments_closed'] > NULL_DATE) { $d = datetime_convert(); if($d > $item['comments_closed']) return true; @@ -214,11 +234,10 @@ function can_comment_on_post($observer_xchan, $item) { return true; break; case 'public': - // We don't allow public comments yet, until a policy - // for dealing with anonymous comments is in place with - // a means to moderate comments. Until that time, return - // false. - return false; + // We don't really allow or support public comments yet, but anonymous + // folks won't ever reach this point (as $observer_xchan will be empty). + // This means the viewer has an xchan and we can identify them. + return true; break; case 'any connections': case 'contacts': @@ -695,8 +714,9 @@ function get_item_elements($x,$allow_code = false) { // hub and verify that they are legit - or else we're going to toss the post. We only need to do this // once, and after that your hub knows them. Sure some info is in the post, but it's only a transit identifier // and not enough info to be able to look you up from your hash - which is the only thing stored with the post. - - if(($xchan_hash = import_author_xchan($x['author'])) !== false) + + $xchan_hash = import_author_xchan($x['author']); + if($xchan_hash) $arr['author_xchan'] = $xchan_hash; else return array(); @@ -705,7 +725,8 @@ function get_item_elements($x,$allow_code = false) { if($arr['author_xchan'] === make_xchan_hash($x['owner']['guid'],$x['owner']['guid_sig'])) $arr['owner_xchan'] = $arr['author_xchan']; else { - if(($xchan_hash = import_author_xchan($x['owner'])) !== false) + $xchan_hash = import_author_xchan($x['owner']); + if($xchan_hash) $arr['owner_xchan'] = $xchan_hash; else return array(); @@ -1069,7 +1090,7 @@ function encode_item($item,$mirror = false) { if($y = encode_item_flags($item)) $x['flags'] = $y; - if($item['comments_closed'] !== NULL_DATE) + if($item['comments_closed'] > NULL_DATE) $x['comments_closed'] = $item['comments_closed']; $x['public_scope'] = $scope; @@ -1166,7 +1187,7 @@ function encode_item_xchan($xchan) { $ret['name'] = $xchan['xchan_name']; $ret['address'] = $xchan['xchan_addr']; - $ret['url'] = (($xchan['hubloc_url']) ? $xchan['hubloc_url'] : $xchan['xchan_url']); + $ret['url'] = $xchan['xchan_url']; $ret['network'] = $xchan['xchan_network']; $ret['photo'] = array('mimetype' => $xchan['xchan_photo_mimetype'], 'src' => $xchan['xchan_photo_m']); $ret['guid'] = $xchan['xchan_guid']; @@ -1418,7 +1439,7 @@ function get_mail_elements($x) { $arr['conv_guid'] = (($x['conv_guid'])? htmlspecialchars($x['conv_guid'],ENT_COMPAT,'UTF-8',false) : ''); $arr['created'] = datetime_convert('UTC','UTC',$x['created']); - if((! array_key_exists('expires',$x)) || ($x['expires'] === NULL_DATE)) + if((! array_key_exists('expires',$x)) || ($x['expires'] <= NULL_DATE)) $arr['expires'] = NULL_DATE; else $arr['expires'] = datetime_convert('UTC','UTC',$x['expires']); @@ -1569,6 +1590,9 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $arr['item_wall'] = ((x($arr,'item_wall')) ? intval($arr['item_wall']) : 0 ); $arr['item_type'] = ((x($arr,'item_type')) ? intval($arr['item_type']) : 0 ); + // obsolete, but needed so as not to throw not-null constraints on some database driveres + $arr['item_flags'] = ((x($arr,'item_flags')) ? intval($arr['item_flags']) : 0 ); + // only detect language if we have text content, and if the post is private but not yet // obscured, make it so. @@ -1624,9 +1648,23 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $arr['expires'] = ((x($arr,'expires') !== false) ? datetime_convert('UTC','UTC',$arr['expires']) : NULL_DATE); $arr['commented'] = ((x($arr,'commented') !== false) ? datetime_convert('UTC','UTC',$arr['commented']) : datetime_convert()); $arr['comments_closed'] = ((x($arr,'comments_closed') !== false) ? datetime_convert('UTC','UTC',$arr['comments_closed']) : NULL_DATE); + $arr['html'] = ((array_key_exists('html',$arr)) ? $arr['html'] : ''); + + if($deliver) { + $arr['received'] = datetime_convert(); + $arr['changed'] = datetime_convert(); + } + else { + + // When deliver flag is false, we are *probably* performing an import or bulk migration. + // If one updates the changed timestamp it will be made available to zotfeed and delivery + // will still take place through backdoor methods. Since these fields are rarely used + // otherwise, just preserve the original timestamp. + + $arr['received'] = ((x($arr,'received') !== false) ? datetime_convert('UTC','UTC',$arr['received']) : datetime_convert()); + $arr['changed'] = ((x($arr,'changed') !== false) ? datetime_convert('UTC','UTC',$arr['changed']) : datetime_convert()); + } - $arr['received'] = datetime_convert(); - $arr['changed'] = datetime_convert(); $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : ''); $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : ''); $arr['parent_mid'] = ((x($arr,'parent_mid')) ? notags(trim($arr['parent_mid'])) : ''); @@ -2027,14 +2065,28 @@ 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']); - if(array_key_exists('comments_closed',$arr) && $arr['comments_closed'] != NULL_DATE) + if(array_key_exists('comments_closed',$arr) && $arr['comments_closed'] > NULL_DATE) $arr['comments_closed'] = datetime_convert('UTC','UTC',$arr['comments_closed']); else $arr['comments_closed'] = $orig[0]['comments_closed']; $arr['commented'] = $orig[0]['commented']; - $arr['received'] = datetime_convert(); - $arr['changed'] = datetime_convert(); + + if($deliver) { + $arr['received'] = datetime_convert(); + $arr['changed'] = datetime_convert(); + } + else { + + // When deliver flag is false, we are *probably* performing an import or bulk migration. + // If one updates the changed timestamp it will be made available to zotfeed and delivery + // will still take place through backdoor methods. Since these fields are rarely used + // otherwise, just preserve the original timestamp. + + $arr['received'] = $orig[0]['received']; + $arr['changed'] = $orig[0]['changed']; + } + $arr['route'] = ((array_key_exists('route',$arr)) ? trim($arr['route']) : $orig[0]['route']); $arr['diaspora_meta'] = ((x($arr,'diaspora_meta')) ? $arr['diaspora_meta'] : $orig[0]['diaspora_meta']); $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : $orig[0]['location']); @@ -2213,7 +2265,7 @@ function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id, logger('storing diaspora comment signature',LOGGER_DEBUG); - $diaspora_handle = $channel['channel_address'] . '@' . App::get_hostname(); + $diaspora_handle = channel_reddress($channel); $signed_text = $datarray['mid'] . ';' . $parent_item['mid'] . ';' . $signed_body . ';' . $diaspora_handle; @@ -2243,6 +2295,11 @@ function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id, function send_status_notifications($post_id,$item) { + // only send notifications for comments + + if($item['mid'] == $item['parent_mid']) + return; + $notify = false; $unfollowed = false; @@ -2258,6 +2315,7 @@ function send_status_notifications($post_id,$item) { if($item['author_xchan'] === $r[0]['channel_hash']) return; + // I'm the owner - notify me if($item['owner_hash'] === $r[0]['channel_hash']) @@ -3774,7 +3832,7 @@ function zot_feed($uid,$observer_hash,$arr) { $limit = " LIMIT 100 "; - if($mindate != NULL_DATE) { + if($mindate > NULL_DATE) { $sql_extra .= " and ( created > '$mindate' or changed > '$mindate' ) "; } @@ -4327,3 +4385,137 @@ function sync_an_item($channel_id,$item_id) { build_sync_packet($channel_d,array('item' => array(encode_item($sync_item[0],true)),'item_id' => $rid)); } } + + +function fix_attached_photo_permissions($uid,$xchan_hash,$body, + $str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny) { + + if(get_pconfig($uid,'system','force_public_uploads')) { + $str_contact_allow = $str_group_allow = $str_contact_deny = $str_group_deny = ''; + } + + $match = null; + // match img and zmg image links + if(preg_match_all("/\[[zi]mg(.*?)\](.*?)\[\/[zi]mg\]/",$body,$match)) { + $images = $match[2]; + if($images) { + foreach($images as $image) { + if(! stristr($image,z_root() . '/photo/')) + continue; + $image_uri = substr($image,strrpos($image,'/') + 1); + if(strpos($image_uri,'-') !== false) + $image_uri = substr($image_uri,0, strpos($image_uri,'-')); + if(strpos($image_uri,'.') !== false) + $image_uri = substr($image_uri,0, strpos($image_uri,'.')); + if(! strlen($image_uri)) + continue; + $srch = '<' . $xchan_hash . '>'; + + $r = q("select folder from attach where hash = '%s' and uid = %d limit 1", + dbesc($image_uri), + intval($uid) + ); + if($r && $r[0]['folder']) { + $f = q("select * from attach where hash = '%s' and is_dir = 1 and uid = %d limit 1", + dbesc($r[0]['folder']), + intval($uid) + ); + if(($f) && (($f[0]['allow_cid']) || ($f[0]['allow_gid']) || ($f[0]['deny_cid']) || ($f[0]['deny_gid']))) { + $str_contact_allow = $f[0]['allow_cid']; + $str_group_allow = $f[0]['allow_gid']; + $str_contact_deny = $f[0]['deny_cid']; + $str_group_deny = $f[0]['deny_gid']; + } + } + + $r = q("SELECT id FROM photo + WHERE allow_cid = '%s' AND allow_gid = '' AND deny_cid = '' AND deny_gid = '' + AND resource_id = '%s' AND uid = %d LIMIT 1", + dbesc($srch), + dbesc($image_uri), + intval($uid) + ); + + if($r) { + $r = q("UPDATE photo SET allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' + WHERE resource_id = '%s' AND uid = %d ", + dbesc($str_contact_allow), + dbesc($str_group_allow), + dbesc($str_contact_deny), + dbesc($str_group_deny), + dbesc($image_uri), + intval($uid) + ); + + // also update the linked item (which is probably invisible) + + $r = q("select id from item + WHERE allow_cid = '%s' AND allow_gid = '' AND deny_cid = '' AND deny_gid = '' + AND resource_id = '%s' and resource_type = 'photo' AND uid = %d LIMIT 1", + dbesc($srch), + dbesc($image_uri), + intval($uid) + ); + if($r) { + $private = (($str_contact_allow || $str_group_allow || $str_contact_deny || $str_group_deny) ? true : false); + + $r = q("UPDATE item SET allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d + WHERE id = %d AND uid = %d", + dbesc($str_contact_allow), + dbesc($str_group_allow), + dbesc($str_contact_deny), + dbesc($str_group_deny), + intval($private), + intval($r[0]['id']), + intval($uid) + ); + } + $r = q("select id from attach where hash = '%s' and uid = %d limit 1", + dbesc($image_uri), + intval($uid) + ); + if($r) { + q("update attach SET allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' + WHERE id = %d AND uid = %d", + dbesc($str_contact_allow), + dbesc($str_group_allow), + dbesc($str_contact_deny), + dbesc($str_group_deny), + intval($r[0]['id']), + intval($uid) + ); + } + } + } + } + } +} + + +function fix_attached_file_permissions($channel,$observer_hash,$body, + $str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny) { + + if(get_pconfig($channel['channel_id'],'system','force_public_uploads')) { + $str_contact_allow = $str_group_allow = $str_contact_deny = $str_group_deny = ''; + } + + $match = false; + + if(preg_match_all("/\[attachment\](.*?)\[\/attachment\]/",$body,$match)) { + $attaches = $match[1]; + if($attaches) { + foreach($attaches as $attach) { + $hash = substr($attach,0,strpos($attach,',')); + $rev = intval(substr($attach,strpos($attach,','))); + attach_store($channel,$observer_hash,$options = 'update', array( + 'hash' => $hash, + 'revision' => $rev, + 'allow_cid' => $str_contact_allow, + 'allow_gid' => $str_group_allow, + 'deny_cid' => $str_contact_deny, + 'deny_gid' => $str_group_deny + )); + } + } + } +} diff --git a/include/message.php b/include/message.php index d3d8181ae..748689206 100644 --- a/include/message.php +++ b/include/message.php @@ -75,7 +75,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' if($recip) $recip_handle = $recip[0]['xchan_addr']; - $sender_handle = $channel['channel_address'] . '@' . App::get_hostname(); + $sender_handle = channel_reddress($channel); $handles = $recip_handle . ';' . $sender_handle; @@ -166,7 +166,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' foreach($match[2] as $mtch) { $hash = substr($mtch,0,strpos($mtch,',')); $rev = intval(substr($mtch,strpos($mtch,','))); - $r = attach_by_hash_nodata($hash,$rev); + $r = attach_by_hash_nodata($hash,get_observer_hash(),$rev); if($r['success']) { $attachments[] = array( 'href' => z_root() . '/attach/' . $r['data']['hash'], @@ -299,14 +299,30 @@ function private_messages_list($uid, $mailbox = '', $start = 0, $numitems = 0) { break; case 'combined': - $sql = "SELECT * FROM ( SELECT * FROM mail WHERE channel_id = $local_channel ORDER BY created DESC $limit ) AS temp_table GROUP BY parent_mid ORDER BY created DESC"; + $parents = q("SELECT parent_mid FROM mail WHERE mid = parent_mid AND channel_id = %d ORDER BY created DESC", + dbesc($local_channel) + ); + //FIXME: We need the latest mail of a thread here. This query throws errors in postgres. We now look for the latest in php until somebody can fix this... + //$sql = "SELECT * FROM ( SELECT * FROM mail WHERE channel_id = $local_channel ORDER BY created DESC $limit ) AS temp_table GROUP BY parent_mid ORDER BY created DESC"; break; } } - $r = q($sql); + if($parents) { + foreach($parents as $parent) { + $all[] = q("SELECT * FROM mail WHERE parent_mid = '%s' AND channel_id = %d ORDER BY created DESC", + dbesc($parent['parent_mid']), + dbesc($local_channel) + ); + } + foreach($all as $single) + $r[] = $single[0]; + } + else { + $r = q($sql); + } if(! $r) { return array(); diff --git a/include/nav.php b/include/nav.php index 025da71b3..c2a058457 100644 --- a/include/nav.php +++ b/include/nav.php @@ -63,6 +63,7 @@ EOT; $server_role = get_config('system','server_role'); $basic = (($server_role === 'basic') ? true : false); + $techlevel = get_account_techlevel(); // nav links: array of array('href', 'text', 'extra css classes', 'title') $nav = Array(); @@ -144,10 +145,10 @@ EOT; $homelink = (($observer) ? $observer['xchan_url'] : ''); } - if((App::$module != 'home') && (! (local_channel()))) + if(! local_channel()) $nav['home'] = array($homelink, t('Home'), "", t('Home Page'),'home_nav_btn'); - if((App::$config['system']['register_policy'] == REGISTER_OPEN) && (! $_SESSION['authenticated'])) + if(((get_config('system','register_policy') == REGISTER_OPEN) || (get_config('system','register_policy') == REGISTER_APPROVE)) && (! $_SESSION['authenticated'])) $nav['register'] = array('register',t('Register'), "", t('Create an account'),'register_nav_btn'); if(! get_config('system','hide_help')) { diff --git a/include/network.php b/include/network.php index fe001b362..7851f8976 100644 --- a/include/network.php +++ b/include/network.php @@ -1148,8 +1148,10 @@ function discover_by_webbie($webbie) { if($link['rel'] === PROTOCOL_ZOT) { logger('discover_by_webbie: zot found for ' . $webbie, LOGGER_DEBUG); - if(array_key_exists('zot',$x) && $x['zot']['success']) + if(array_key_exists('zot',$x) && $x['zot']['success']) { $i = import_xchan($x['zot']); + return true; + } else { $z = z_fetch_url($link['href']); if($z['success']) { @@ -1937,7 +1939,9 @@ function format_and_send_email($sender,$xchan,$item) { $hostname = App::get_hostname(); if(strpos($hostname,':')) $hostname = substr($hostname,0,strpos($hostname,':')); - $sender_email = 'noreply' . '@' . $hostname; + $sender_email = get_config('system','reply_address'); + if(! $sender_email) + $sender_email = 'noreply' . '@' . $hostname; // use the EmailNotification library to send the message @@ -2008,11 +2012,11 @@ function get_site_info() { $admin = array(); foreach($r as $rr) { if($rr['channel_pageflags'] & PAGE_HUBADMIN) - $admin[] = array( 'name' => $rr['channel_name'], 'address' => $rr['channel_address'] . '@' . App::get_hostname(), 'channel' => z_root() . '/channel/' . $rr['channel_address']); + $admin[] = array( 'name' => $rr['channel_name'], 'address' => channel_reddress($rr), 'channel' => z_root() . '/channel/' . $rr['channel_address']); } if(! $admin) { foreach($r as $rr) { - $admin[] = array( 'name' => $rr['channel_name'], 'address' => $rr['channel_address'] . '@' . App::get_hostname(), 'channel' => z_root() . '/channel/' . $rr['channel_address']); + $admin[] = array( 'name' => $rr['channel_name'], 'address' => channel_reddress($rr), 'channel' => z_root() . '/channel/' . $rr['channel_address']); } } } @@ -2222,3 +2226,65 @@ function network_to_name($s) { return str_replace($search,$replace,$s); } + + +function z_mail($params) { + + /** + * @brief Send a text email message + * + * @param array $params an assoziative array with: + * * \e string \b fromName name of the sender + * * \e string \b fromEmail email of the sender + * * \e string \b replyTo replyTo address to direct responses + * * \e string \b toEmail destination email address + * * \e string \b messageSubject subject of the message + * * \e string \b htmlVersion html version of the message + * * \e string \b textVersion text only version of the message + * * \e string \b additionalMailHeader additions to the smtp mail header + */ + + if(! $params['fromEmail']) { + $params['fromEmail'] = get_config('system','from_email'); + if(! $params['fromEmail']) + $params['fromEmail'] = 'Administrator' . '@' . App::get_hostname(); + } + if(! $params['fromName']) { + $params['fromName'] = get_config('system','from_email_name'); + if(! $params['fromName']) + $params['fromName'] = Zotlabs\Lib\System::get_site_name(); + } + if(! $params['replyTo']) { + $params['replyTo'] = get_config('system','reply_address'); + if(! $params['replyTo']) + $params['replyTo'] = 'noreply' . '@' . App::get_hostname(); + } + + $params['sent'] = false; + $params['result'] = false; + + call_hooks('email_send', $params); + + if($params['sent']) { + logger('notification: z_mail returns ' . $params['result'], LOGGER_DEBUG); + return $params['result']; + } + + $fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8'); + $messageSubject = email_header_encode(html_entity_decode($params['messageSubject'],ENT_QUOTES,'UTF-8'),'UTF-8'); + + $messageHeader = + $params['additionalMailHeader'] . + "From: $fromName <{$params['fromEmail']}>\n" . + "Reply-To: $fromName <{$params['replyTo']}>"; + + // send the message + $res = mail( + $params['toEmail'], // send to address + $messageSubject, // subject + $params['textVersion'], + $messageHeader // message headers + ); + logger('notification: z_mail returns ' . $res, LOGGER_DEBUG); + return $res; +} diff --git a/include/oembed.php b/include/oembed.php index fe6f10d71..1780e7046 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -105,7 +105,7 @@ function oembed_action($embedurl) { function oembed_process($url) { $j = oembed_fetch_url($url); logger('oembed_process: ' . print_r($j,true)); - if($j && $j->type !== 'error') + if($j && $j['type'] !== 'error') return '[embed]' . $url . '[/embed]'; return false; } @@ -117,7 +117,7 @@ function oembed_fetch_url($embedurl){ // These media files should now be caught in bbcode.php // left here as a fallback in case this is called from another source - $noexts = array(".mp3",".mp4",".ogg",".ogv",".oga",".ogm",".webm",".opus"); + $noexts = [ '.mp3', '.mp4', '.ogg', '.ogv', '.oga', '.ogm', '.webm', '.opus', '.m4a' ]; $result = oembed_action($embedurl); @@ -156,9 +156,12 @@ function oembed_fetch_url($embedurl){ if ($action !== 'block') { // try oembed autodiscovery $redirects = 0; - $result = z_fetch_url($furl, false, $redirects, array('timeout' => 15, 'accept_content' => "text/*", 'novalidate' => true )); + $result = z_fetch_url($furl, false, $redirects, array('timeout' => 30, 'accept_content' => "text/*", 'novalidate' => true )); + if($result['success']) $html_text = $result['body']; + else + logger('fetch failure: ' . $furl); if($html_text) { $dom = @DOMDocument::loadHTML($html_text); @@ -171,7 +174,10 @@ function oembed_fetch_url($embedurl){ foreach($entries as $e){ $href = $e->getAttributeNode("href")->nodeValue; $x = z_fetch_url($href . '&maxwidth=' . App::$videowidth); - $txt = $x['body']; + if($x['success']) + $txt = $x['body']; + else + logger('fetch failed: ' . $href); break; } // soundcloud is now using text/json+oembed instead of application/json+oembed, @@ -180,7 +186,10 @@ function oembed_fetch_url($embedurl){ foreach($entries as $e){ $href = $e->getAttributeNode("href")->nodeValue; $x = z_fetch_url($href . '&maxwidth=' . App::$videowidth); - $txt = $x['body']; + if($x['success']) + $txt = $x['body']; + else + logger('json fetch failed: ' . $href); break; } } @@ -206,26 +215,29 @@ function oembed_fetch_url($embedurl){ } - $j = json_decode($txt); + $j = json_decode($txt,true); + + if(! $j) + $j = []; if($action === 'filter') { - if($j->html) { - $orig = $j->html; + if($j['html']) { + $orig = $j['html']; $allow_position = (($zrl) ? true : false); - $j->html = purify_html($j->html,$allow_position); - if($j->html != $orig) { - logger('oembed html was purified. original: ' . $orig . ' purified: ' . $j->html, LOGGER_DEBUG, LOG_INFO); + $j['html'] = purify_html($j['html'],$allow_position); + if($j['html'] != $orig) { + logger('oembed html was purified. original: ' . $orig . ' purified: ' . $j['html'], LOGGER_DEBUG, LOG_INFO); } $orig_len = mb_strlen(preg_replace('/\s+/','',$orig)); - $new_len = mb_strlen(preg_replace('/\s+/','',$j->html)); + $new_len = mb_strlen(preg_replace('/\s+/','',$j['html'])); if(stripos($orig,'<script') || (! $new_len)) - $j->type = 'error'; + $j['type'] = 'error'; elseif($orig_len) { $ratio = $new_len / $orig_len; if($ratio < 0.5) { - $j->type = 'error'; + $j['type'] = 'error'; logger('oembed html truncated: ' . $ratio, LOGGER_DEBUG, LOG_INFO); } } @@ -233,7 +245,7 @@ function oembed_fetch_url($embedurl){ } } - $j->embedurl = $embedurl; + $j['embedurl'] = $embedurl; // logger('fetch return: ' . print_r($j,true)); @@ -244,27 +256,27 @@ function oembed_fetch_url($embedurl){ function oembed_format_object($j){ - $embedurl = $j->embedurl; + $embedurl = $j['embedurl']; // logger('format: ' . print_r($j,true)); - $jhtml = oembed_iframe($j->embedurl,(isset($j->width) ? $j->width : null), (isset($j->height) ? $j->height : null)); + $jhtml = oembed_iframe($j['embedurl'],(isset($j['width']) ? $j['width'] : null), (isset($j['height']) ? $j['height'] : null)); - $ret="<span class='oembed ".$j->type."'>"; - switch ($j->type) { + $ret="<span class='oembed " . $j['type'] . "'>"; + switch ($j['type']) { case "video": { - if (isset($j->thumbnail_url)) { - $tw = (isset($j->thumbnail_width)) ? $j->thumbnail_width:200; - $th = (isset($j->thumbnail_height)) ? $j->thumbnail_height:180; + if (isset($j['thumbnail_url'])) { + $tw = (isset($j['thumbnail_width'])) ? $j['thumbnail_width'] : 200; + $th = (isset($j['thumbnail_height'])) ? $j['thumbnail_height'] : 180; $tr = $tw/$th; $th=120; $tw = $th*$tr; $tpl=get_markup_template('oembed_video.tpl'); if(strstr($embedurl,'youtu') && strstr(z_root(),'https:')) { $embedurl = str_replace('http:','https:',$embedurl); - $j->thumbnail_url = str_replace('http:','https:', $j->thumbnail_url); + $j['thumbnail_url'] = str_replace('http:','https:', $j['thumbnail_url']); $jhtml = str_replace('http:','https:', $jhtml); - $j->html = str_replace('http:','https:', $j->html); + $j['html'] = str_replace('http:','https:', $j['html']); } $ret.=replace_macros($tpl, array( @@ -273,7 +285,7 @@ function oembed_format_object($j){ '$escapedhtml'=>base64_encode($jhtml), '$tw'=>$tw, '$th'=>$th, - '$turl'=>$j->thumbnail_url, + '$turl'=> $j['thumbnail_url'], )); } else { @@ -282,19 +294,19 @@ function oembed_format_object($j){ $ret.="<br>"; }; break; case "photo": { - $ret.= "<img width='".$j->width."' src='".$j->url."'>"; + $ret.= "<img width='".$j['width']."' src='".$j['url']."'>"; $ret.="<br>"; }; break; case "link": { - if($j->thumbnail_url) { + if($j['thumbnail_url']) { if(is_matrix_url($embedurl)) { $embedurl = zid($embedurl); - $j->thumbnail_url = zid($j->thumbnail_url); + $j['thumbnail_url'] = zid($j['thumbnail_url']); } - $ret = '<a href="' . $embedurl . '" ><img src="' . $j->thumbnail_url . '" alt="thumbnail" /></a><br /><br />'; + $ret = '<a href="' . $embedurl . '" ><img src="' . $j['thumbnail_url'] . '" alt="thumbnail" /></a><br /><br />'; } - //$ret = "<a href='".$embedurl."'>".$j->title."</a>"; + //$ret = "<a href='".$embedurl."'>".$j['title']."</a>"; }; break; case "rich": { // not so safe.. @@ -303,12 +315,12 @@ function oembed_format_object($j){ } // add link to source if not present in "rich" type - if ( $j->type!='rich' || !strpos($j->html,$embedurl) ){ - $embedlink = (isset($j->title))?$j->title:$embedurl; + if ( $j['type'] != 'rich' || !strpos($j['html'],$embedurl) ){ + $embedlink = (isset($j['title']))?$j['title'] : $embedurl; $ret .= '<br />' . "<a href='$embedurl' rel='oembed'>$embedlink</a>"; $ret .= "<br />"; - if (isset($j->author_name)) $ret.=" by ".$j->author_name; - if (isset($j->provider_name)) $ret.=" on ".$j->provider_name; + if (isset($j['author_name'])) $ret .= t(' by ') . $j['author_name']; + if (isset($j['provider_name'])) $ret .= t(' on ') . $j['provider_name']; } else { // add <a> for html2bbcode conversion $ret .= "<br /><a href='$embedurl' rel='oembed'>$embedurl</a>"; diff --git a/include/permissions.php b/include/permissions.php index 637193973..d21b45550 100644 --- a/include/permissions.php +++ b/include/permissions.php @@ -6,8 +6,14 @@ require_once('include/security.php'); * @file include/permissions.php * * This file conntains functions to check and work with permissions. + * + * Most of this file is obsolete and has been superceded by extensible permissions in v1.12; it is left here + * for reference and because we haven't yet checked that all functions have been replaced and are available + * elsewhere (typically Zotlabs/Access/*). */ + + /** * @brief Return an array with all available permissions. * diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index 6de75d497..9b6d38cc1 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -328,6 +328,7 @@ abstract class photo_driver { $p['photo_usage'] = intval($arr['photo_usage']); $p['os_storage'] = intval($arr['os_storage']); $p['os_path'] = $arr['os_path']; + $p['display_path'] = (($arr['display_path']) ? $arr['display_path'] : ''); if(! intval($p['imgscale'])) logger('save: ' . print_r($arr,true), LOGGER_DATA); @@ -358,6 +359,8 @@ abstract class photo_driver { `photo_usage` = %d, `title` = '%s', `description` = '%s', + `os_path` = '%s', + `display_path` = '%s', `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', @@ -382,6 +385,8 @@ abstract class photo_driver { intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), + dbesc($p['os_path']), + dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), @@ -391,8 +396,8 @@ abstract class photo_driver { } else { $r = q("INSERT INTO `photo` - ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, mimetype, `album`, `height`, `width`, `content`, `os_storage`, `filesize`, `imgscale`, `photo_usage`, `title`, `description`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s' )", + ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, mimetype, `album`, `height`, `width`, `content`, `os_storage`, `filesize`, `imgscale`, `photo_usage`, `title`, `description`, `os_path`, `display_path`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )", intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), @@ -411,6 +416,8 @@ abstract class photo_driver { intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), + dbesc($p['os_path']), + dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), @@ -420,6 +427,9 @@ abstract class photo_driver { return $r; } + + // should be obsolete now + public function store($aid, $uid, $xchan, $rid, $filename, $album, $scale, $usage = PHOTO_NORMAL, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '') { $x = q("select id from photo where `resource_id` = '%s' and uid = %d and `xchan` = '%s' and `imgscale` = %d limit 1", diff --git a/include/photos.php b/include/photos.php index bd25fe8b7..a3018816c 100644 --- a/include/photos.php +++ b/include/photos.php @@ -588,6 +588,8 @@ function photos_album_rename($channel_id, $oldname, $newname) { ); } + + /** * @brief * diff --git a/include/plugin.php b/include/plugin.php index cb206d944..663d17959 100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -404,6 +404,18 @@ function check_plugin_versions($info) { return false; } } + if(array_key_exists('serverroles',$info)) { + $role = \Zotlabs\Lib\System::get_server_role(); + if(! ( + stristr($info['serverroles'],'*') + || stristr($info['serverroles'],'any') + || stristr($info['serverroles'],$role))) { + logger('serverrole limit: ' . $info['name'],LOGGER_NORMAL,LOG_WARNING); + return false; + + } + } + if(array_key_exists('requires',$info)) { $arr = explode(',',$info['requires']); diff --git a/include/security.php b/include/security.php index 83bf51bc0..9b508d339 100644 --- a/include/security.php +++ b/include/security.php @@ -265,7 +265,7 @@ function change_channel($change_channel) { ); if($x) { $_SESSION['my_url'] = $x[0]['xchan_url']; - $_SESSION['my_address'] = $r[0]['channel_address'] . '@' . App::get_hostname(); + $_SESSION['my_address'] = channel_reddress($r[0]); App::set_observer($x[0]); App::set_perms(get_all_perms(local_channel(), $hash)); diff --git a/include/text.php b/include/text.php index ac210b336..9c4a4a5c5 100644 --- a/include/text.php +++ b/include/text.php @@ -138,31 +138,74 @@ function purify_html($s, $allow_position = false) { $def = $config->getHTMLDefinition(true); //data- attributes used by the foundation library - $def->info_global_attr['data-options'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-magellan-expedition'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-magellan-destination'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-magellan-arrival'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-offcanvas'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-topbar'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-orbit'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-orbit-slide-number'] = new HTMLPurifier_AttrDef_Text; + + // f6 navigation + + //dropdown menu + $def->info_global_attr['data-dropdown-menu'] = new HTMLPurifier_AttrDef_Text; + //drilldown menu + $def->info_global_attr['data-drilldown'] = new HTMLPurifier_AttrDef_Text; + //accordion menu + $def->info_global_attr['data-accordion-menu'] = new HTMLPurifier_AttrDef_Text; + //responsive navigation + $def->info_global_attr['data-responsive-menu'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-responsive-toggle'] = new HTMLPurifier_AttrDef_Text; + //magellan + $def->info_global_attr['data-magellan'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-magellan-target'] = new HTMLPurifier_AttrDef_Text; + + // f6 containers + + //accordion + $def->info_global_attr['data-accordion'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-accordion-item'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-tab-content'] = new HTMLPurifier_AttrDef_Text; + //dropdown $def->info_global_attr['data-dropdown'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-dropdown-content'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-reveal-id'] = new HTMLPurifier_AttrDef_Text; + //off-canvas + $def->info_global_attr['data-off-canvas-wrapper'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-off-canvas'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-off-canvas-content'] = new HTMLPurifier_AttrDef_Text; + //reveal $def->info_global_attr['data-reveal'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-alert'] = new HTMLPurifier_AttrDef_Text; + //tabs + $def->info_global_attr['data-tabs'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-tabs-content'] = new HTMLPurifier_AttrDef_Text; + + // f6 media + + //orbit + $def->info_global_attr['data-orbit'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-slide'] = new HTMLPurifier_AttrDef_Text; + //tooltip $def->info_global_attr['data-tooltip'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-joyride'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-id'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-text'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-class'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-prev-tex'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-button'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-accordion'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-tab'] = new HTMLPurifier_AttrDef_Text; + + // f6 plugins + + //abide - the use is pointless since we can't do anything with forms + + //equalizer $def->info_global_attr['data-equalizer'] = new HTMLPurifier_AttrDef_Text; $def->info_global_attr['data-equalizer-watch'] = new HTMLPurifier_AttrDef_Text; + //interchange - potentially dangerous since it can load content + + //toggler + $def->info_global_attr['data-toggler'] = new HTMLPurifier_AttrDef_Text; + + //sticky + $def->info_global_attr['data-sticky'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-sticky-container'] = new HTMLPurifier_AttrDef_Text; + + // f6 common + + $def->info_global_attr['data-options'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-toggle'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-close'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-open'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-position'] = new HTMLPurifier_AttrDef_Text; + + //data- attributes used by the bootstrap library $def->info_global_attr['data-dismiss'] = new HTMLPurifier_AttrDef_Text; $def->info_global_attr['data-target'] = new HTMLPurifier_AttrDef_Text; @@ -191,12 +234,15 @@ function purify_html($s, $allow_position = false) { $def->info_global_attr['data-offset-bottom'] = new HTMLPurifier_AttrDef_Text; //some html5 elements + //Block $def->addElement('section', 'Block', 'Flow', 'Common'); $def->addElement('nav', 'Block', 'Flow', 'Common'); $def->addElement('article', 'Block', 'Flow', 'Common'); $def->addElement('aside', 'Block', 'Flow', 'Common'); $def->addElement('header', 'Block', 'Flow', 'Common'); $def->addElement('footer', 'Block', 'Flow', 'Common'); + //Inline + $def->addElement('button', 'Inline', 'Inline', 'Common'); if($allow_position) { @@ -936,7 +982,7 @@ function searchbox($s,$id='search-box',$url='/search',$save = false) { '$action_url' => z_root() . '/' . $url, '$search_label' => t('Search'), '$save_label' => t('Save'), - '$savedsearch' => feature_enabled(local_channel(),'savedsearch') + '$savedsearch' => ($save && feature_enabled(local_channel(),'savedsearch')) )); } @@ -2267,11 +2313,11 @@ function design_tools() { } /** - * @brief Creates website import tools menu + * @brief Creates website portation tools menu * * @return string */ -function website_import_tools() { +function website_portation_tools() { $channel = App::get_channel(); $sys = false; @@ -2282,7 +2328,7 @@ function website_import_tools() { $sys = true; } - return replace_macros(get_markup_template('website_import_tools.tpl'), array( + return replace_macros(get_markup_template('website_portation_tools.tpl'), array( '$title' => t('Import'), '$import_label' => t('Import website...'), '$import_placeholder' => t('Select folder to import'), @@ -2290,7 +2336,15 @@ function website_import_tools() { '$file_import_text' => t('Import from cloud files:'), '$desc' => t('/cloud/channel/path/to/folder'), '$hint' => t('Enter path to website files'), - '$select' => t('Select folder'), + '$select' => t('Select folder'), + '$export_label' => t('Export website...'), + '$file_download_text' => t('Export to a zip file'), + '$filename_desc' => t('website.zip'), + '$filename_hint' => t('Enter a name for the zip file.'), + '$cloud_export_text' => t('Export to cloud files'), + '$cloud_export_desc' => t('/path/to/export/folder'), + '$cloud_export_hint' => t('Enter a path to a cloud files destination.'), + '$cloud_export_select' => t('Specify folder'), )); } @@ -2975,3 +3029,38 @@ function text_highlight($s,$lang) { return('<code>' . $o . '</code>'); } +// function to convert multi-dimensional array to xml +// create new instance of simplexml + +// $xml = new SimpleXMLElement('<root/>'); + +// function callback +// array2XML($xml, $my_array); + +// save as xml file +// echo (($xml->asXML('data.xml')) ? 'Your XML file has been generated successfully!' : 'Error generating XML file!'); + +function arrtoxml($root_elem,$arr) { + $xml = new SimpleXMLElement('<' . $root_elem . '/>'); + array2XML($xml,$arr); + return $xml->asXML(); +} + +function array2XML($obj, $array) +{ + foreach ($array as $key => $value) + { + if(is_numeric($key)) + $key = 'item' . $key; + + if (is_array($value)) + { + $node = $obj->addChild($key); + array2XML($node, $value); + } + else + { + $obj->addChild($key, htmlspecialchars($value)); + } + } +} diff --git a/include/widgets.php b/include/widgets.php index 68db74703..2e1e58717 100644 --- a/include/widgets.php +++ b/include/widgets.php @@ -209,7 +209,9 @@ function widget_savedsearch($arr) { if((! local_channel()) || (! feature_enabled(local_channel(),'savedsearch'))) return ''; - $search = ((x($_GET,'search')) ? $_GET['search'] : ''); + $search = ((x($_GET,'netsearch')) ? $_GET['netsearch'] : ''); + if(! $search) + $search = ((x($_GET,'search')) ? $_GET['search'] : ''); if(x($_GET,'searchsave') && $search) { $r = q("select * from `term` where `uid` = %d and `ttype` = %d and `term` = '%s' limit 1", @@ -287,6 +289,40 @@ function widget_savedsearch($arr) { return $o; } +function widget_sitesearch($arr) { + + $search = ((x($_GET,'search')) ? $_GET['search'] : ''); + + $srchurl = App::$query_string; + + $srchurl = rtrim(preg_replace('/search\=[^\&].*?(\&|$)/is','',$srchurl),'&'); + $srchurl = rtrim(preg_replace('/submit\=[^\&].*?(\&|$)/is','',$srchurl),'&'); + $srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl); + + + $hasq = ((strpos($srchurl,'?') !== false) ? true : false); + $hasamp = ((strpos($srchurl,'&') !== false) ? true : false); + + if(($hasamp) && (! $hasq)) + $srchurl = substr($srchurl,0,strpos($srchurl,'&')) . '?f=&' . substr($srchurl,strpos($srchurl,'&')+1); + + $o = ''; + + $saved = array(); + + $tpl = get_markup_template("sitesearch.tpl"); + $o = replace_macros($tpl, array( + '$title' => t('Search'), + '$searchbox' => searchbox($search, 'netsearch-box', $srchurl . (($hasq) ? '' : '?f='), false), + '$saved' => $saved, + )); + + return $o; +} + + + + function widget_filer($arr) { if(! local_channel()) @@ -566,7 +602,7 @@ function widget_settings_menu($arr) { ); - if(get_features()) { + if(get_account_techlevel() > 0 && get_features()) { $tabs[] = array( 'label' => t('Additional features'), 'url' => z_root().'/settings/features', @@ -594,14 +630,11 @@ function widget_settings_menu($arr) { ); } - // IF can go away when UNO export and import is fully functional - if(get_config('system','server_role') !== 'basic') { - $tabs[] = array( - 'label' => t('Export channel'), - 'url' => z_root() . '/uexport', - 'selected' => '' - ); - } + $tabs[] = array( + 'label' => t('Export channel'), + 'url' => z_root() . '/uexport', + 'selected' => '' + ); $tabs[] = array( 'label' => t('Connected apps'), @@ -609,7 +642,7 @@ function widget_settings_menu($arr) { 'selected' => ((argv(1) === 'oauth') ? 'active' : ''), ); - if(get_config('system','server_role') !== 'basic') { + if(get_account_techlevel() > 2) { $tabs[] = array( 'label' => t('Guest Access Tokens'), 'url' => z_root() . '/settings/tokens', @@ -779,7 +812,7 @@ function widget_design_tools($arr) { return design_tools(); } -function widget_website_import_tools($arr) { +function widget_website_portation_tools($arr) { // mod menu doesn't load a profile. For any modules which load a profile, check it. // otherwise local_channel() is sufficient for permissions. @@ -791,7 +824,7 @@ function widget_website_import_tools($arr) { if(! local_channel()) return ''; - return website_import_tools(); + return website_portation_tools(); } function widget_findpeople($arr) { @@ -963,6 +996,14 @@ function widget_suggestedchats($arr) { if(! feature_enabled(App::$profile['profile_uid'],'ajaxchat')) return ''; + // There are reports that this tool does not ever remove chatrooms on dead sites, + // and also will happily link to private chats which you cannot enter. + // For those reasons, it will be disabled until somebody decides it's worth + // fixing and comes up with a plan for doing so. + + return ''; + + // probably should restrict this to your friends, but then the widget will only work // if you are logged in locally. @@ -1286,8 +1327,8 @@ function widget_random_block($arr) { function widget_rating($arr) { - $poco_rating = get_config('system','poco_rating_enable'); - if((! $poco_rating) && ($poco_rating !== false)) { + $rating_enabled = get_config('system','rating_enabled'); + if(! $rating_enabled) { return; } @@ -1459,13 +1500,42 @@ function widget_tasklist($arr) { function widget_helpindex($arr) { - $o .= '<div class="widget">' . '<h3>' . t('Documentation') . '</h3>'; - $o .= '<ul class="nav nav-pills nav-stacked">'; - $o .= '<li><a href="help/general">' . t('Project/Site Information') . '</a></li>'; - $o .= '<li><a href="help/members">' . t('For Members') . '</a></li>'; - $o .= '<li><a href="help/admins">' . t('For Administrators') . '</a></li>'; - $o .= '<li><a href="help/develop">' . t('For Developers') . '</a></li>'; - $o .= '</ul></div>'; + + $o .= '<div class="widget">'; + $o .= '<h3>' . t('Documentation') . '</h3>'; + + $level_0 = get_help_content('sitetoc'); + if(! $level_0) + $level_0 = get_help_content('toc'); + + $level_0 = preg_replace('/\<ul(.*?)\>/','<ul class="nav nav-pills nav-stacked">',$level_0); + + $levels = array(); + + + if(argc() > 2) { + $path = ''; + for($x = 1; $x < argc(); $x ++) { + $path .= argv($x) . '/'; + $y = get_help_content($path . 'sitetoc'); + if(! $y) + $y = get_help_content($path . 'toc'); + if($y) + $levels[] = preg_replace('/\<ul(.*?)\>/','<ul class="nav nav-pills nav-stacked">',$y); + } + } + + if($level_0) + $o .= $level_0; + if($levels) { + foreach($levels as $l) { + $o .= '<br /><br />'; + $o .= $l; + } + } + + $o .= '</div>'; + return $o; } diff --git a/include/zot.php b/include/zot.php index c3c924113..1df600abd 100644 --- a/include/zot.php +++ b/include/zot.php @@ -1593,7 +1593,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ } $channel = $r[0]; - $DR->addto_recipient($channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . App::get_hostname() . '>'); + $DR->addto_recipient($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); /* blacklisted channels get a permission denied, no special message to tip them off */ @@ -2082,7 +2082,7 @@ function process_mail_delivery($sender, $arr, $deliveries) { } $channel = $r[0]; - $DR->addto_recipient($channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . App::get_hostname() . '>'); + $DR->addto_recipient($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); /* blacklisted channels get a permission denied, no special message to tip them off */ @@ -2844,6 +2844,7 @@ function import_site($arr, $pubkey) { $site_location = htmlspecialchars($arr['location'],ENT_COMPAT,'UTF-8',false); $site_realm = htmlspecialchars($arr['realm'],ENT_COMPAT,'UTF-8',false); $site_project = htmlspecialchars($arr['project'],ENT_COMPAT,'UTF-8',false); + $site_version = ((array_key_exists('version',$arr)) ? htmlspecialchars($arr['version'],ENT_COMPAT,'UTF-8',false) : ''); // You can have one and only one primary directory per realm. // Downgrade any others claiming to be primary. As they have @@ -2863,14 +2864,16 @@ function import_site($arr, $pubkey) { || ($siterecord['site_location'] != $site_location) || ($siterecord['site_register'] != $register_policy) || ($siterecord['site_project'] != $site_project) - || ($siterecord['site_realm'] != $site_realm)) { + || ($siterecord['site_realm'] != $site_realm) + || ($siterecord['site_version'] != $site_version) ) { + $update = true; // logger('import_site: input: ' . print_r($arr,true)); // logger('import_site: stored: ' . print_r($siterecord,true)); - $r = q("update site set site_dead = 0, site_location = '%s', site_flags = %d, site_access = %d, site_directory = '%s', site_register = %d, site_update = '%s', site_sellpage = '%s', site_realm = '%s', site_type = %d, site_project = '%s' + $r = q("update site set site_dead = 0, site_location = '%s', site_flags = %d, site_access = %d, site_directory = '%s', site_register = %d, site_update = '%s', site_sellpage = '%s', site_realm = '%s', site_type = %d, site_project = '%s', site_version = '%s' where site_url = '%s'", dbesc($site_location), intval($site_directory), @@ -2882,6 +2885,7 @@ function import_site($arr, $pubkey) { dbesc($site_realm), intval(SITE_TYPE_ZOT), dbesc($site_project), + dbesc($site_version), dbesc($url) ); if(! $r) { @@ -2899,8 +2903,8 @@ function import_site($arr, $pubkey) { else { $update = true; - $r = q("insert into site ( site_location, site_url, site_access, site_flags, site_update, site_directory, site_register, site_sellpage, site_realm, site_type, site_project ) - values ( '%s', '%s', %d, %d, '%s', '%s', %d, '%s', '%s', %d, '%s' )", + $r = q("insert into site ( site_location, site_url, site_access, site_flags, site_update, site_directory, site_register, site_sellpage, site_realm, site_type, site_project, site_version ) + values ( '%s', '%s', %d, %d, '%s', '%s', %d, '%s', '%s', %d, '%s', '%s' )", dbesc($site_location), dbesc($url), intval($access_policy), @@ -2911,7 +2915,8 @@ function import_site($arr, $pubkey) { dbesc($sellpage), dbesc($site_realm), intval(SITE_TYPE_ZOT), - dbesc($site_project) + dbesc($site_project), + dbesc($site_version) ); if(! $r) { logger('import_site: record create failed. ' . print_r($arr,true)); @@ -3159,7 +3164,10 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { if(array_key_exists('channel',$arr) && is_array($arr['channel']) && count($arr['channel'])) { - translate_channel_perms_inbound($arr['channel']); + $remote_channel = $arr['channel']; + $remote_channel['channel_id'] = $channel['channel_id']; + translate_channel_perms_inbound($remote_channel); + if(array_key_exists('channel_pageflags',$arr['channel']) && intval($arr['channel']['channel_pageflags'])) { // These flags cannot be sync'd. @@ -3527,7 +3535,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { if(array_key_exists('item',$arr) && is_array($arr['item'][0])) { $DR = new Zotlabs\Zot\DReport(z_root(),$d['hash'],$d['hash'],$arr['item'][0]['message_id'],'channel sync processed'); - $DR->addto_recipient($channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . App::get_hostname() . '>'); + $DR->addto_recipient($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); } else $DR = new Zotlabs\Zot\DReport(z_root(),$d['hash'],$d['hash'],'sync packet','channel sync delivered'); @@ -3638,8 +3646,7 @@ function zot_reply_message_request($data) { if ($messages) { $env_recips = null; - $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_error = 0 and hubloc_deleted = 0 - group by hubloc_sitekey", + $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_error = 0 and hubloc_deleted = 0", dbesc($sender_hash) ); if (! $r) { @@ -3787,18 +3794,12 @@ function zotinfo($arr) { } elseif($ztarget_hash) { // check if it has characteristics of a public forum based on custom permissions. - $t = q("select * from abconfig where abconfig.cat = 'my_perms' and abconfig.chan = %d and abconfig.xchan = '%s' and abconfig.k in ('tag_deliver', 'send_stream') ", - intval($e['channel_id']), - dbesc($ztarget_hash) - ); - - $ch = 0; - - if($t) { - foreach($t as $tt) { - if($tt['k'] == 'tag_deliver' && $tt['v'] == 1) + $m = \Zotlabs\Access\Permissions::FilledAutoperms($e['channel_id']); + if($m) { + foreach($m as $k => $v) { + if($k == 'tag_deliver' && intval($v) == 1) $ch ++; - if($tt['k'] == 'send_stream' && $tt['v'] == 0) + if($k == 'send_stream' && intval($v) == 0) $ch ++; } if($ch == 2) @@ -3964,9 +3965,6 @@ function zotinfo($arr) { require_once('include/channel.php'); $ret['site']['channels'] = channel_total(); - - $ret['site']['version'] = Zotlabs\Lib\System::get_platform_name() . ' ' . STD_VERSION . '[' . DB_UPDATE_VERSION . ']'; - $ret['site']['admin'] = get_config('system','admin_email'); $visible_plugins = array(); @@ -3984,6 +3982,7 @@ function zotinfo($arr) { $ret['site']['location'] = get_config('system','site_location'); $ret['site']['realm'] = get_directory_realm(); $ret['site']['project'] = Zotlabs\Lib\System::get_platform_name() . ' ' . Zotlabs\Lib\System::get_server_role(); + $ret['site']['version'] = Zotlabs\Lib\System::get_project_version(); } @@ -4035,7 +4034,7 @@ function check_zotinfo($channel,$locations,&$ret) { dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_hash']), - dbesc($channel['channel_address'] . '@' . App::get_hostname()), + dbesc(channel_reddress($channel)), intval(1), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey']))), |