diff options
Diffstat (limited to 'include')
39 files changed, 990 insertions, 952 deletions
diff --git a/include/PermissionDescription.php b/include/PermissionDescription.php deleted file mode 100644 index 1f7799406..000000000 --- a/include/PermissionDescription.php +++ /dev/null @@ -1,170 +0,0 @@ -<?php - -if(class_exists('PermissionDescription')) return; - -require_once("include/permissions.php"); -require_once("include/language.php"); -require_once("include/text.php"); - - -/** - * Encapsulates information the ACL dialog requires to describe - * permission settings for an item with an empty ACL. - * i.e the caption, icon, and tooltip for the no-ACL option in the ACL dialog. - */ -class PermissionDescription { - - private $global_perm; - private $channel_perm; - private $fallback_description; - - /** - * Constructor is private. - * Use static methods fromGlobalPermission(), fromStandalonePermission(), or fromDescription() - * to create instances. - */ - private function __construct($global_perm, $channel_perm, $description = '') { - - $this->global_perm = $global_perm; - $this->channel_perm = $channel_perm; - - $this->fallback_description = ($description == '') ? t('Visible to your default audience') : $description; - } - - /** - * If the interpretation of an empty ACL can't be summarised with a global default permission - * or a specific permission setting then use this method and describe what it means instead. - * Remember to localize the description first. - * - * @param string $description - the localized caption for the no-ACL option in the ACL dialog. - * @return a new instance of PermissionDescription - */ - public static function fromDescription($description) { - return new PermissionDescription('', 0x80000, $description); - } - - - /** - * Use this method only if the interpretation of an empty ACL doesn't fall back to a global - * default permission. You should pass one of the constants from boot.php - PERMS_PUBLIC, - * PERMS_NETWORK etc. - * - * @param integer $perm - a single enumerated constant permission - PERMS_PUBLIC, PERMS_NETWORK etc. - * @return a new instance of PermissionDescription - */ - public static function fromStandalonePermission($perm) { - - $result = new PermissionDescription('', $perm); - - $checkPerm = $this->get_permission_description(); - if ($checkPerm == $this->fallback_description) { - $result = null; - logger('null PermissionDescription from unknown standalone permission: ' . $perm ,LOGGER_DEBUG, LOG_ERROR); - } - - return $result; - } - - /** - * This is the preferred way to create a PermissionDescription, as it provides the most details. - * Use this method if you know an empty ACL will result in one of the global default permissions - * being used, such as channel_r_stream (for which you would pass 'view_stream'). - * - * @param string $permname - a key for the global perms array from get_perms() in permissions.php, - * e.g. 'view_stream', 'view_profile', etc. - * @return a new instance of PermissionDescription - */ - public static function fromGlobalPermission($permname) { - - $result = null; - - $global_perms = get_perms(); - - if (array_key_exists($permname, $global_perms)) { - - $permDetails = $global_perms[$permname]; - - // It should be OK to always just read the permissions from App::$channel - // - // App::$profile is a union of channel and profile fields. - // The distinction is basically that App::$profile is pointing to the resource - // being observed. App::$channel is referring to the current logged-in channel - // member (if this is a local channel) e.g. the observer. We only show the ACL - // widget to the page owner (observer and observed are the same) so in that case - // I believe either may be safely used here. - $channelPerm = \App::$channel[$permDetails[0]]; - $result = new PermissionDescription($permDetails[1], $channelPerm); - } else { - // The acl dialog can handle null arguments, but it shouldn't happen - logger('null PermissionDescription from unknown global permission: ' . $permname ,LOGGER_DEBUG, LOG_ERROR); - } - return $result; - } - - - /** - * Gets a localized description of the permission, or a generic message if the permission - * is unknown. - * - * @return string description - */ - public function get_permission_description() { - - switch($this->channel_perm) { - - case 0: return t('Only me'); - case PERMS_PUBLIC: return t('Public'); - case PERMS_NETWORK: return t('Anybody in the $Projectname network'); - case PERMS_SITE: return sprintf(t('Any account on %s'), \App::get_hostname()); - case PERMS_CONTACTS: return t('Any of my connections'); - case PERMS_SPECIFIC: return t('Only connections I specifically allow'); - case PERMS_AUTHED: return t('Anybody authenticated (could include visitors from other networks)'); - case PERMS_PENDING: return t('Any connections including those who haven\'t yet been approved'); - default: return $this->fallback_description; - } - } - - /** - * Returns an icon css class name if an appropriate one is available, e.g. "fa-globe" for Public, - * otherwise returns empty string. - * - * @return string icon css class name (often FontAwesome) - */ - public function get_permission_icon() { - - switch($this->channel_perm) { - - case 0:/* only me */ return 'fa-eye-slash'; - case PERMS_PUBLIC: return 'fa-globe'; - case PERMS_NETWORK: return 'fa-share-alt-square'; // fa-share-alt-square is very similiar to the hubzilla logo, but we should create our own logo class to use - case PERMS_SITE: return 'fa-sitemap'; - case PERMS_CONTACTS: return 'fa-group'; - case PERMS_SPECIFIC: return 'fa-list'; - case PERMS_AUTHED: return ''; - case PERMS_PENDING: return ''; - default: return ''; - } - } - - - /** - * Returns a localized description of where the permission came from, if this is known. - * If it's not know, or if the permission is standalone and didn't come from a default - * permission setting, then empty string is returned. - * - * @return string description or empty string - */ - public function get_permission_origin_description() { - - switch($this->global_perm) { - - case PERMS_R_STREAM: return t('This is your default setting for the audience of your normal stream, and posts.'); - case PERMS_R_PROFILE: return t('This is your default setting for who can view your default channel profile'); - case PERMS_R_ABOOK: return t('This is your default setting for who can view your connections'); - case PERMS_R_STORAGE: return t('This is your default setting for who can view your file storage and photos'); - case PERMS_R_PAGES: return t('This is your default setting for the audience of your webpages'); - default: return ''; - } - } - -} diff --git a/include/account.php b/include/account.php index caf12878e..142ad1bea 100644 --- a/include/account.php +++ b/include/account.php @@ -499,11 +499,27 @@ function account_approve($hash) { intval($register[0]['uid']) ); + // get a fresh copy after we've modified it. + + $account = q("SELECT * FROM account WHERE account_id = %d LIMIT 1", + intval($register[0]['uid']) + ); + + if(! $account) + return $ret; + + + if(get_config('system','auto_channel_create') || UNO) auto_channel_create($register[0]['uid']); + else { + $_SESSION['login_return_url'] = 'new_channel'; + authenticate_success($account[0],null,true,true,false,true); + } + - info( t('Account verified. Please login.') . EOL ); + // info( t('Account verified. Please login.') . EOL ); return true; } diff --git a/include/acl_selectors.php b/include/acl_selectors.php index 89d054e3b..148c67a6c 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -7,8 +7,6 @@ * @package acl_selectors */ -require_once("include/PermissionDescription.php"); - function group_select($selname,$selclass,$preselected = false,$size = 4) { $o = ''; @@ -231,7 +229,7 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti if(! $emptyACL_description) { $showall_caption = t('Visible to your default audience'); - } else if (is_a($emptyACL_description, 'PermissionDescription')) { + } else if (is_a($emptyACL_description, '\\Zotlabs\\Lib\\PermissionDescription')) { $showall_caption = $emptyACL_description->get_permission_description(); $showall_origin = (($role === 'custom') ? $emptyACL_description->get_permission_origin_description() : ''); $showall_icon = $emptyACL_description->get_permission_icon(); diff --git a/include/api.php b/include/api.php index be525f7e9..df6aba957 100644 --- a/include/api.php +++ b/include/api.php @@ -839,7 +839,7 @@ require_once('include/api_auth.php'); $_REQUEST['parent_mid'] = $parent; if($_REQUEST['namespace'] && $parent) { - $x = q("select iid from item_id where service = '%s' and sid = '%s' limit 1", + $x = q("select iid from iconfig where cat = 'system' and k = '%s' and v = '%s' limit 1", dbesc($_REQUEST['namespace']), dbesc($parent) ); @@ -967,20 +967,10 @@ require_once('include/api_auth.php'); $ret = array(); $tmp = array(); - $str = ''; foreach($i as $ii) { $tmp[] = encode_item($ii,true); - if($str) - $str .= ','; - $str .= $ii['id']; } $ret['item'] = $tmp; - if($str) { - $r = q("select item_id.*, item.mid from item_id left join item on item_id.iid = item.id where item.id in ( $str ) "); - - if($r) - $ret['item_id'] = $r; - } json_return_and_die($ret); } @@ -1462,7 +1452,8 @@ require_once('include/api_auth.php'); } else { if($_REQUEST['namespace'] && $_REQUEST['remote_id']) { - $r = q("select * from item_id where service = '%s' and sid = '%s' and uid = %d limit 1", + $r = q("select * from iconfig left join item on iconfig.iid = item.id + where cat = 'system' and k = '%s' and v = '%s' and item.uid = %d limit 1", dbesc($_REQUEST['namespace']), dbesc($_REQUEST['remote_id']), intval($user_info['uid']) @@ -1472,7 +1463,7 @@ require_once('include/api_auth.php'); $id = $r[0]['iid']; } if($_REQUEST['namespace'] && $_REQUEST['comment_id']) { - $r = q("select * from item_id left join item on item.id = item_id.iid where service = '%s' and sid = '%s' and uid = %d and item.id != item.parent limit 1", + $r = q("select * from iconfig left join item on item.id = iconfig.iid where cat = 'system' and k = '%s' and v = '%s' and uid = %d and item.id != item.parent limit 1", dbesc($_REQUEST['namespace']), dbesc($_REQUEST['comment_id']), intval($user_info['uid']) diff --git a/include/api_auth.php b/include/api_auth.php index dc8492b20..89882f821 100644 --- a/include/api_auth.php +++ b/include/api_auth.php @@ -59,21 +59,13 @@ function api_login(&$a){ if(isset($_SERVER['PHP_AUTH_USER'])) { $channel_login = 0; $record = account_verify_password($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']); - if(! $record) { - $r = q("select * from channel left join account on account.account_id = channel.channel_account_id - where channel.channel_address = '%s' limit 1", - dbesc($_SERVER['PHP_AUTH_USER']) - ); - if ($r) { - $record = account_verify_password($r[0]['account_email'],$_SERVER['PHP_AUTH_PW']); - if($record) - $channel_login = $r[0]['channel_id']; - } + if($record && $record['channel']) { + $channel_login = $record['channel']['channel_id']; } } - if($record) { - authenticate_success($record); + if($record['account']) { + authenticate_success($record['account']); if($channel_login) change_channel($channel_login); diff --git a/include/attach.php b/include/attach.php index 78efde51f..b3ddfee88 100644 --- a/include/attach.php +++ b/include/attach.php @@ -423,6 +423,8 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $observer = array(); + $dosync = ((array_key_exists('nosync',$arr) && $arr['nosync']) ? 0 : 1); + if($observer_hash) { $x = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($observer_hash) @@ -616,7 +618,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { ); if($r) { $overwrite = get_pconfig($channel_id,'system','overwrite_dup_files'); - if($overwrite) { + if(($overwrite) || ($options === 'import')) { $options = 'replace'; $existing_id = $x[0]['id']; $existing_size = intval($x[0]['filesize']); @@ -800,7 +802,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { if($is_photo) { - $args = array( 'source' => $source, 'visible' => $visible, 'resource_id' => $hash, 'album' => basename($pathname), 'os_path' => $os_basepath . $os_relpath, 'filename' => $filename, 'getimagesize' => $gis, 'directory' => $direct); + $args = array( 'source' => $source, 'visible' => $visible, 'resource_id' => $hash, 'album' => basename($pathname), 'os_path' => $os_basepath . $os_relpath, 'filename' => $filename, 'getimagesize' => $gis, 'directory' => $direct, 'options' => $options ); if($arr['contact_allow']) $args['contact_allow'] = $arr['contact_allow']; if($arr['group_allow']) @@ -829,6 +831,8 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { if($arr['description']) $args['description'] = $arr['description']; + $args['deliver'] = $dosync; + $p = photo_upload($channel,$observer,$args); if($p['success']) { $ret['body'] = $p['body']; @@ -865,10 +869,12 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { call_hooks('photo_upload_end',$ret); } - $sync = attach_export_data($channel,$hash); + if($dosync) { + $sync = attach_export_data($channel,$hash); - if($sync) - build_sync_packet($channel['channel_id'],array('file' => array($sync))); + if($sync) + build_sync_packet($channel['channel_id'],array('file' => array($sync))); + } return $ret; } @@ -1462,7 +1468,7 @@ function find_filename_by_hash($channel_id, $attachHash) { function pipe_streams($in, $out) { $size = 0; while (!feof($in)) - $size += fwrite($out, fread($in, 8192)); + $size += fwrite($out, fread($in, 16384)); return $size; } @@ -1903,4 +1909,4 @@ function get_attach_binname($s) { $p = substr($p,strpos($p,'/')+1); } return $p; -}
\ No newline at end of file +} diff --git a/include/auth.php b/include/auth.php index 01fcf0094..f3592cee3 100644 --- a/include/auth.php +++ b/include/auth.php @@ -16,55 +16,97 @@ require_once('include/security.php'); /** * @brief Verify login credentials. * - * If system <i>authlog</i> is set a log entry will be added for failed login + * If system.authlog is set a log entry will be added for failed login * attempts. * - * @param string $email - * The email address to verify. + * @param string $login + * The login to verify (channel address, account email or guest login token). * @param string $pass * The provided password to verify. * @return array|null * Returns account record on success, null on failure. + * The return array is dependent on the login mechanism. + * $ret['account'] will be set if either an email or channel address validation was successful (local login). + * $ret['channel'] will be set if a channel address validation was successful. + * $ret['xchan'] will be set if a guest access token validation was successful. + * Keys will exist for invalid return arrays but will be set to null. + * This function does not perform a login. It merely validates systems passwords and tokens. + * */ -function account_verify_password($email, $pass) { + +function account_verify_password($login, $pass) { + + $ret = [ 'account' => null, 'channel' => null, 'xchan' => null ]; $email_verify = get_config('system', 'verify_email'); $register_policy = get_config('system', 'register_policy'); + if(! $login) + return null; + + $account = null; + $channel = null; + $xchan = null; + + if(! strpos($login,'@')) { + $channel = channelx_by_nick($login); + if(! $channel) { + $x = q("select * from atoken where atoken_name = '%s' and atoken_token = '%s' limit 1", + dbesc($login), + dbesc($pass) + ); + if($x) { + $ret['xchan'] = atoken_xchan($x[0]); + return $ret; + } + } + } + if($channel) { + $where = " where account_id = " . intval($channel['channel_account_id']) . " "; + } + else { + $where = " where account_email = '" . dbesc($login) . "' "; + } + + $a = q("select * from account $where"); + if(! $a) { + return null; + } + + $account = $a[0]; + // Currently we only verify email address if there is an open registration policy. // This isn't because of any policy - it's because the workflow gets too complicated if // you have to verify the email and then go through the account approval workflow before // letting them login. - if(($email_verify) && ($register_policy == REGISTER_OPEN) && ($record['account_flags'] & ACCOUNT_UNVERIFIED)) - return null; - - $r = q("select * from account where account_email = '%s'", - dbesc($email) - ); - if(! ($r && count($r))) + if(($email_verify) && ($register_policy == REGISTER_OPEN) && ($account['account_flags'] & ACCOUNT_UNVERIFIED)) { + logger('email verification required for ' . $login); return null; + } - foreach($r as $record) { - if(($record['account_flags'] == ACCOUNT_OK) - && (hash('whirlpool', $record['account_salt'] . $pass) === $record['account_password'])) { - logger('password verified for ' . $email); - return $record; - } + if(($account['account_flags'] == ACCOUNT_OK) + && (hash('whirlpool',$account['account_salt'] . $pass) === $account['account_password'])) { + logger('password verified for ' . $login); + $ret['account'] = $account; + if($channel) + $ret['channel'] = $channel; + return $ret; } - $error = 'password failed for ' . $email; + + $error = 'password failed for ' . $login; logger($error); - if($record['account_flags'] & ACCOUNT_UNVERIFIED) - logger('Account is unverified. account_flags = ' . $record['account_flags']); - if($record['account_flags'] & ACCOUNT_BLOCKED) - logger('Account is blocked. account_flags = ' . $record['account_flags']); - if($record['account_flags'] & ACCOUNT_EXPIRED) - logger('Account is expired. account_flags = ' . $record['account_flags']); - if($record['account_flags'] & ACCOUNT_REMOVED) - logger('Account is removed. account_flags = ' . $record['account_flags']); - if($record['account_flags'] & ACCOUNT_PENDING) - logger('Account is pending. account_flags = ' . $record['account_flags']); + if($account['account_flags'] & ACCOUNT_UNVERIFIED) + logger('Account is unverified. account_flags = ' . $account['account_flags']); + if($account['account_flags'] & ACCOUNT_BLOCKED) + logger('Account is blocked. account_flags = ' . $account['account_flags']); + if($account['account_flags'] & ACCOUNT_EXPIRED) + logger('Account is expired. account_flags = ' . $account['account_flags']); + if($account['account_flags'] & ACCOUNT_REMOVED) + logger('Account is removed. account_flags = ' . $account['account_flags']); + if($account['account_flags'] & ACCOUNT_PENDING) + logger('Account is pending. account_flags = ' . $account['account_flags']); log_failed_login($error); @@ -120,13 +162,21 @@ if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) && App::$session->new_cookie(60 * 60 * 24); // one day $_SESSION['last_login_date'] = datetime_convert(); unset($_SESSION['visitor_id']); // no longer a visitor - authenticate_success($x[0], true, true); + authenticate_success($x[0], null, true, true); } } - - $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s' limit 1", - dbesc($_SESSION['visitor_id']) - ); + if(array_key_exists('atoken',$_SESSION)) { + $y = q("select * from atoken where atoken_id = %d limit 1", + intval($_SESSION['atoken']) + ); + if($y) + $r = array(atoken_xchan($y[0])); + } + else { + $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s' limit 1", + dbesc($_SESSION['visitor_id']) + ); + } if($r) { App::set_observer($r[0]); } @@ -158,7 +208,8 @@ if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) && App::$session->extend_cookie(); $login_refresh = true; } - authenticate_success($r[0], false, false, false, $login_refresh); + $ch = (($_SESSION['uid']) ? channelx_by_n($_SESSION['uid']) : null); + authenticate_success($r[0], null, $ch, false, false, $login_refresh); } else { $_SESSION['account_id'] = 0; @@ -199,30 +250,38 @@ else { call_hooks('authenticate', $addon_auth); + $atoken = null; + $account = null; + if(($addon_auth['authenticated']) && (count($addon_auth['user_record']))) { - $record = $addon_auth['user_record']; + $account = $addon_auth['user_record']; } else { - $record = App::$account = account_verify_password($_POST['username'], $_POST['password']); + $verify = account_verify_password($_POST['username'], $_POST['password']); + if($verify) { + $atoken = $verify['xchan']; + $channel = $verify['channel']; + $account = App::$account = $verify['account']; + } if(App::$account) { $_SESSION['account_id'] = App::$account['account_id']; } + elseif($atoken) { + atoken_login($atoken); + } else { notice( t('Failed authentication') . EOL); } - - logger('authenticate: ' . print_r(App::$account, true), LOGGER_ALL); } - if((! $record) || (! count($record))) { + if(! ($account || $atoken)) { $error = 'authenticate: failed login attempt: ' . notags(trim($_POST['username'])) . ' from IP ' . $_SERVER['REMOTE_ADDR']; logger($error); // Also log failed logins to a separate auth log to reduce overhead for server side intrusion prevention $authlog = get_config('system', 'authlog'); if ($authlog) @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $error . "\n", FILE_APPEND); - notice( t('Login failed.') . EOL ); goaway(z_root() . '/login'); } @@ -252,7 +311,8 @@ else { // if we haven't failed up this point, log them in. $_SESSION['last_login_date'] = datetime_convert(); - authenticate_success($record, true, true); + if(! $atoken) + authenticate_success($account,$channel,true, true); } } @@ -270,6 +330,7 @@ else { * @return int|bool * Return channel_id from pconfig or false. */ + function match_openid($authid) { // Query the uid/channel_id from pconfig for a given value. $r = q("SELECT uid FROM pconfig WHERE cat = 'system' AND k = 'openid' AND v = '%s' LIMIT 1", diff --git a/include/bb2diaspora.php b/include/bb2diaspora.php index c7d0e56b1..16f67dc4a 100644 --- a/include/bb2diaspora.php +++ b/include/bb2diaspora.php @@ -302,11 +302,11 @@ function bb2diaspora_itemwallwall(&$item) { } } - if(($wallwall) && (is_array($item['author'])) && $item['author']['xchan_url'] && $item['author']['xchan_name'] && $item['author']['xchan_photo_m']) { + 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" - . '[img]' . $item['author']['xchan_photo_m'] . '[/img]' + . '[img]' . $item['author']['xchan_photo_s'] . '[/img]' . '[url=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/url]' . "\n\n" . $item['body']; } diff --git a/include/bbcode.php b/include/bbcode.php index 42741b392..7f7be4300 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -242,6 +242,13 @@ function bb_ShareAttributes($match) { if ($matches[1] != "") $message_id = $matches[1]; + if(! $message_id) { + preg_match("/guid='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $message_id = $matches[1]; + } + + $reldate = '<span class="autotime" title="' . datetime_convert('UTC', date_default_timezone_get(), $posted, 'c') . '" >' . datetime_convert('UTC', date_default_timezone_get(), $posted, 'r') . '</span>'; $headline = '<div class="shared_container"> <div class="shared_header">'; @@ -484,6 +491,24 @@ function bb_code($match) { return '<code class="inline-code">' . trim($match[1]) . '</code>'; } +function bb_highlight($match) { + if(in_array(strtolower($match[1]),['php','css','mysql','sql','abap','diff','html','perl','ruby', + 'vbscript','avrc','dtd','java','xml','cpp','python','javascript','js','json','sh'])) + return text_highlight($match[2],strtolower($match[1])); + return $match[0]; +} + +function bb_fixtable_lf($match) { + + // remove extraneous whitespace between table element tags since newlines will all + // be converted to '<br />' and turn your neatly crafted tables into a whole lot of + // empty space. + + $x = preg_replace("/\]\s+\[/",'][',$match[1]); + return '[table]' . $x . '[/table]'; + +} + // BBcode 2 HTML was written by WAY2WEB.net @@ -559,6 +584,15 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = str_replace(">", ">", $Text); + // Check for [code] text here, before the linefeeds are messed with. + // The highlighter will unescape and re-escape the content. + + if (strpos($Text,'[code=') !== false) { + $Text = preg_replace_callback("/\[code=(.*?)\](.*?)\[\/code\]/ism", 'bb_highlight', $Text); + } + + $Text = preg_replace_callback("/\[table\](.*?)\[\/table\]/ism",'bb_fixtable_lf',$Text); + // Convert new line chars to html <br /> tags // nlbr seems to be hopelessly messed up diff --git a/include/cache.php b/include/cache.php deleted file mode 100644 index 4a3f453e1..000000000 --- a/include/cache.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php /** @file */ - - /** - * cache api - */ - - class Cache { - public static function get($key){ - $r = q("SELECT v FROM cache WHERE k = '%s' limit 1", - dbesc($key) - ); - - if ($r) - return $r[0]['v']; - return null; - } - - public static function set($key,$value) { - - $r = q("SELECT * FROM cache WHERE k = '%s' limit 1", - dbesc($key) - ); - if($r) { - q("UPDATE cache SET v = '%s', updated = '%s' WHERE k = '%s'", - dbesc($value), - dbesc(datetime_convert()), - dbesc($key)); - } - else { - q("INSERT INTO cache ( k, v, updated) VALUES ('%s','%s','%s')", - dbesc($key), - dbesc($value), - dbesc(datetime_convert())); - } - } - - - public static function clear(){ - q("DELETE FROM cache WHERE updated < '%s'", - dbesc(datetime_convert('UTC','UTC',"now - 30 days"))); - } - - } - diff --git a/include/channel.php b/include/channel.php index 087bd4162..8e0747f25 100644 --- a/include/channel.php +++ b/include/channel.php @@ -16,7 +16,7 @@ require_once('include/menu.php'); * @param int $account_id * Account_id used for this request * - * @returns assoziative array with: + * @returns associative array with: * * \e boolean \b success boolean true if creating a new channel is allowed for this account * * \e string \b message (optional) if success is false, optional error text * * \e int \b total_identities @@ -496,8 +496,10 @@ function identity_basic_export($channel_id, $items = false) { $r = q("select * from channel where channel_id = %d limit 1", intval($channel_id) ); - if($r) + if($r) { $ret['channel'] = $r[0]; + $ret['relocate'] = [ 'channel_address' => $r[0]['channel_address'], 'url' => z_root()]; + } $r = q("select * from profile where uid = %d", intval($channel_id) @@ -514,7 +516,7 @@ function identity_basic_export($channel_id, $items = false) { for($x = 0; $x < count($ret['abook']); $x ++) { $xchans[] = $ret['abook'][$x]['abook_chan']; - $abconfig = load_abconfig($ret['channel']['channel_hash'],$ret['abook'][$x]['abook_xchan']); + $abconfig = load_abconfig($channel_id,$ret['abook'][$x]['abook_xchan']); if($abconfig) $ret['abook'][$x]['abconfig'] = $abconfig; } @@ -676,14 +678,6 @@ function identity_basic_export($channel_id, $items = false) { $ret['mail'] = $m; } - $r = q("select item_id.*, item.mid from item_id left join item on item_id.iid = item.id where item_id.uid = %d", - intval($channel_id) - ); - - if($r) - $ret['item_id'] = $r; - - //$key = get_config('system','prvkey'); /** @warning this may run into memory limits on smaller systems */ @@ -725,6 +719,10 @@ function identity_export_year($channel_id,$year,$month = 0) { $ret = array(); + $ch = channelx_by_n($channel_id); + if($ch) { + $ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()]; + } $mindate = datetime_convert('UTC','UTC',$year . '-' . $target_month . '-01 00:00:00'); if($month && $month < 12) $maxdate = datetime_convert('UTC','UTC',$year . '-' . $target_month_plus . '-01 00:00:00'); @@ -746,16 +744,43 @@ function identity_export_year($channel_id,$year,$month = 0) { $ret['item'][] = encode_item($rr,true); } - $r = q("select item_id.*, item.mid from item_id left join item on item_id.iid = item.id where item_id.uid = %d - and item.created >= '%s' and item.created < '%s' order by created ", + return $ret; +} + +// export items within an arbitrary date range. Date/time is in UTC. + +function channel_export_items($channel_id,$start,$finish) { + + if(! $start) + return array(); + else + $start = datetime_convert('UTC','UTC',$start); + + $finish = datetime_convert('UTC','UTC',(($finish) ? $finish : 'now')); + if($finish < $start) + return array(); + + $ret = array(); + + $ch = channelx_by_n($channel_id); + if($ch) { + $ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()]; + } + + $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created < '%s' and resource_type = '' order by created", + intval(ITEM_TYPE_POST), intval($channel_id), - dbesc($mindate), - dbesc($maxdate) + dbesc($start), + dbesc($finish) ); - if($r) - $ret['item_id'] = $r; - + if($r) { + $ret['item'] = array(); + xchan_query($r); + $r = fetch_post_tags($r,true); + foreach($r as $rr) + $ret['item'][] = encode_item($rr,true); + } return $ret; } @@ -774,11 +799,10 @@ function identity_export_year($channel_id,$year,$month = 0) { * * The channel default theme is also selected for use, unless over-riden elsewhere. * - * @param[in,out] App &$a * @param string $nickname * @param string $profile */ -function profile_load(&$a, $nickname, $profile = '') { +function profile_load($nickname, $profile = '') { // logger('profile_load: ' . $nickname . (($profile) ? ' profile: ' . $profile : '')); @@ -875,7 +899,7 @@ function profile_load(&$a, $nickname, $profile = '') { ); if($z) { $p[0]['picdate'] = $z[0]['xchan_photo_date']; - $p[0]['reddress'] = str_replace('@','@',$z[0]['xchan_addr']); + $p[0]['reddress'] = str_replace('@','@',$z[0]['xchan_addr']); } // fetch user tags if this isn't the default profile @@ -1046,6 +1070,7 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa $diaspora = array( 'podloc' => z_root(), + 'guid' => $profile['channel_guid'] . str_replace('.','',App::get_hostname()), 'searchable' => (($block) ? 'false' : 'true'), 'nickname' => $profile['channel_address'], 'fullname' => $profile['channel_name'], @@ -1243,6 +1268,7 @@ function advanced_profile(&$a) { $things = get_things(App::$profile['profile_guid'],App::$profile['profile_uid']); + // logger('mod_profile: things: ' . print_r($things,true), LOGGER_DATA); return replace_macros($tpl, array( @@ -1284,13 +1310,12 @@ function get_my_address() { * If somebody arrives at our site using a zid, add their xchan to our DB if we don't have it already. * And if they aren't already authenticated here, attempt reverse magic auth. * - * @param App &$a * * @hooks 'zid_init' * string 'zid' - their zid * string 'url' - the destination url */ -function zid_init(&$a) { +function zid_init() { $tmp_str = get_my_address(); if(validate_email($tmp_str)) { Zotlabs\Daemon\Master::Summon(array('Gprobe',bin2hex($tmp_str))); @@ -1317,6 +1342,29 @@ function zid_init(&$a) { } /** + * @brief + * + * If somebody arrives at our site using a zat, authenticate them + * + */ + +function zat_init() { + if(local_channel() || remote_channel()) + return; + + $r = q("select * from atoken where atoken_token = '%s' limit 1", + dbesc($_REQUEST['zat']) + ); + if($r) { + $xchan = atoken_xchan($r[0]); + atoken_login($xchan); + } + +} + + + +/** * @brief Adds a zid parameter to a url. * * @param string $s diff --git a/include/config.php b/include/config.php index 65199283d..08810e298 100644 --- a/include/config.php +++ b/include/config.php @@ -98,20 +98,20 @@ function del_aconfig($account_id, $family, $key) { } -function load_abconfig($chash,$xhash) { - Zlib\AbConfig::Load($chash,$xhash); +function load_abconfig($chan, $xhash, $family = '') { + return Zlib\AbConfig::Load($chan,$xhash,$family); } -function get_abconfig($chash,$xhash,$family,$key) { - return Zlib\AbConfig::Get($chash,$xhash,$family,$key); +function get_abconfig($chan,$xhash,$family,$key) { + return Zlib\AbConfig::Get($chan,$xhash,$family,$key); } -function set_abconfig($chash,$xhash,$family,$key,$value) { - return Zlib\AbConfig::Set($chash,$xhash,$family,$key,$value); +function set_abconfig($chan,$xhash,$family,$key,$value) { + return Zlib\AbConfig::Set($chan,$xhash,$family,$key,$value); } -function del_abconfig($chash,$xhash,$family,$key) { - return Zlib\AbConfig::Delete($chash,$xhash,$family,$key); +function del_abconfig($chan,$xhash,$family,$key) { + return Zlib\AbConfig::Delete($chan,$xhash,$family,$key); } function load_iconfig(&$item) { diff --git a/include/connections.php b/include/connections.php index 2d10b8354..ed4526a09 100644 --- a/include/connections.php +++ b/include/connections.php @@ -283,18 +283,30 @@ function channel_remove($channel_id, $local = true, $unset_session=false) { Zotlabs\Daemon\Master::Summon(array('Notifier','purge_all',$channel_id)); } + + $r = q("select * from iconfig left join item on item.id = iconfig.iid + where item.uid = %d", + intval($channel_id) + ); + if($r) { + foreach($r as $rr) { + q("delete from iconfig where iid = %d", + intval($rr['iid']) + ); + } + } + + q("DELETE FROM `groups` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `group_member` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `event` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `item` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `item_id` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `mail` WHERE `channel_id` = %d", intval($channel_id)); q("DELETE FROM `notify` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `photo` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `attach` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `profile` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `pconfig` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `spam` WHERE `uid` = %d", intval($channel_id)); // @FIXME At this stage we need to remove the file resources located under /store/$nickname diff --git a/include/conversation.php b/include/conversation.php index 518193b08..957dbf8e9 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -403,9 +403,12 @@ function count_descendants($item) { * @return boolean */ function visible_activity($item) { - $hidden_activities = array(ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_AGREE, ACTIVITY_DISAGREE, ACTIVITY_ABSTAIN, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE); + $hidden_activities = [ ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_AGREE, ACTIVITY_DISAGREE, ACTIVITY_ABSTAIN, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE ]; - $post_types = array(ACTIVITY_OBJ_NOTE,ACTIVITY_OBJ_COMMENT,basename(ACTIVITY_OBJ_NOTE),basename(ACTIVITY_OBJ_COMMENT)); + $post_types = [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT, basename(ACTIVITY_OBJ_NOTE), basename(ACTIVITY_OBJ_COMMENT)]; + + if(intval($item['item_notshown'])) + return false; foreach ($hidden_activities as $act) { if ((activity_match($item['verb'], $act)) && ($item['mid'] != $item['parent_mid'])) { @@ -1143,6 +1146,8 @@ function status_editor($a, $x, $popup = false) { $weblink = (($mimetype === 'text/bbcode') ? t('Insert web link') : false); if(x($x, 'hide_weblink')) $weblink = false; + + $embedPhotos = t('Embed image from photo albums'); $writefiles = (($mimetype === 'text/bbcode') ? perm_is_allowed($x['profile_uid'], get_observer_hash(), 'write_storage') : false); if(x($x, 'hide_attach')) @@ -1178,6 +1183,12 @@ function status_editor($a, $x, $popup = false) { '$whereareu' => t('Where are you right now?'), '$editor_autocomplete'=> ((x($x,'editor_autocomplete')) ? $x['editor_autocomplete'] : ''), '$bbco_autocomplete'=> ((x($x,'bbco_autocomplete')) ? $x['bbco_autocomplete'] : ''), + '$modalchooseimages' => t('Choose images to embed'), + '$modalchoosealbum' => t('Choose an album'), + '$modaldiffalbum' => t('Choose a different album...'), + '$modalerrorlist' => t('Error getting album list'), + '$modalerrorlink' => t('Error getting photo link'), + '$modalerroralbum' => t('Error getting album'), )); $tpl = get_markup_template('jot.tpl'); @@ -1219,6 +1230,10 @@ function status_editor($a, $x, $popup = false) { '$code' => t('Code'), '$attach' => t('Attach file'), '$weblink' => $weblink, + '$embedPhotos' => $embedPhotos, + '$embedPhotosModalTitle' => t('Embed an image from your albums'), + '$embedPhotosModalCancel' => t('Cancel'), + '$embedPhotosModalOK' => t('OK'), '$setloc' => $setloc, '$voting' => t('Toggle voting'), '$feature_voting' => $feature_voting, @@ -1688,13 +1703,19 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ 'title' => t('Manage Webpages'), 'id' => 'webpages-tab', ); - } else { - /** - * @FIXME we probably need a listing of events that were created by - * this channel and are visible to the observer - */ + } + + if(feature_enabled($uid,'wiki') && (! UNO)) { + $tabs[] = array( + 'label' => t('Wiki'), + 'url' => z_root() . '/wiki/' . $nickname, + 'sel' => ((argv(0) == 'wiki') ? 'active' : ''), + 'title' => t('Wiki'), + 'id' => 'wiki-tab', + ); } + $arr = array('is_owner' => $is_owner, 'nickname' => $nickname, 'tab' => (($tab) ? $tab : false), 'tabs' => $tabs); call_hooks('profile_tabs', $arr); diff --git a/include/datetime.php b/include/datetime.php index 600ad6ec4..76bd6b8d6 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -556,13 +556,13 @@ function update_birthdays() { $ev['uid'] = $rr['abook_channel']; $ev['account'] = $rr['abook_account']; $ev['event_xchan'] = $rr['xchan_hash']; - $ev['start'] = datetime_convert('UTC', 'UTC', $rr['abook_dob']); - $ev['finish'] = datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day '); + $ev['dtstart'] = datetime_convert('UTC', 'UTC', $rr['abook_dob']); + $ev['dtend'] = datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day '); $ev['adjust'] = intval(feature_enabled($rr['abook_channel'],'smart_birthdays')); $ev['summary'] = sprintf( t('%1$s\'s birthday'), $rr['xchan_name']); $ev['description'] = sprintf( t('Happy Birthday %1$s'), '[zrl=' . $rr['xchan_url'] . ']' . $rr['xchan_name'] . '[/zrl]') ; - $ev['type'] = 'birthday'; + $ev['etype'] = 'birthday'; $z = event_store_event($ev); if ($z) { diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php index df072ed76..f6091f6e1 100755 --- a/include/dba/dba_driver.php +++ b/include/dba/dba_driver.php @@ -90,7 +90,7 @@ abstract class dba_driver { protected $db; protected $pdo = array(); - public $debug = 0; + public $debug = 0; public $connected = false; public $error = false; @@ -332,6 +332,9 @@ function q($sql) { else db_logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true),LOGGER_NORMAL,LOG_CRIT); } + if(\DBA::$dba->debug) + db_logger('Sql: ' . $stmt, LOGGER_DEBUG, LOG_INFO); + return \DBA::$dba->q($stmt); } diff --git a/include/event.php b/include/event.php index a4118ec78..3d650cd14 100644 --- a/include/event.php +++ b/include/event.php @@ -183,7 +183,9 @@ function format_ical_text($s) { require_once('include/bbcode.php'); require_once('include/html2plain.php'); - return(wordwrap(str_replace(array(',',';','\\'),array('\\,','\\;','\\\\'),html2plain(bbcode($s))),72,"\r\n ",true)); + $s = html2plain(bbcode($s)); + $s = str_replace(["\r\n","\n"],["",""],$s); + return(wordwrap(str_replace(['\\',',',';'],['\\\\','\\,','\\;'],$s),72,"\r\n ",true)); } diff --git a/include/features.php b/include/features.php index 6d38bcfb4..2d71aa9be 100644 --- a/include/features.php +++ b/include/features.php @@ -52,6 +52,7 @@ function get_features($filtered = true) { 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'),((UNO) ? 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')), diff --git a/include/feedutils.php b/include/feedutils.php index 685b2f982..01ec0687e 100644 --- a/include/feedutils.php +++ b/include/feedutils.php @@ -393,7 +393,7 @@ function get_atom_elements($feed, $item, &$author) { $terms = array(); $terms[] = array( 'otype' => TERM_OBJ_POST, - 'type' => TERM_BOOKMARK, + 'ttype' => TERM_BOOKMARK, 'url' => $res['plink'], 'term' => $res['title'], ); @@ -403,7 +403,7 @@ function get_atom_elements($feed, $item, &$author) { $terms = array(); $terms[] = array( 'otype' => TERM_OBJ_POST, - 'type' => TERM_BOOKMARK, + 'ttype' => TERM_BOOKMARK, 'url' => $res['plink'], 'term' => $res['plink'], ); diff --git a/include/group.php b/include/group.php index a4938b848..10853ff6b 100644 --- a/include/group.php +++ b/include/group.php @@ -17,7 +17,7 @@ function group_add($uid,$name,$public = 0) { $z = q("SELECT * FROM `groups` WHERE `id` = %d LIMIT 1", intval($r) ); - if(count($z) && $z[0]['deleted']) { + if(($z) && $z[0]['deleted']) { /*$r = q("UPDATE `groups` SET `deleted` = 0 WHERE `uid` = %d AND `gname` = '%s' LIMIT 1", intval($uid), dbesc($name) @@ -129,7 +129,7 @@ function group_byname($uid,$name) { intval($uid), dbesc($name) ); - if(count($r)) + if($r) return $r[0]['id']; return false; } @@ -178,11 +178,11 @@ function group_add_member($uid,$name,$member,$gid = 0) { intval($gid), dbesc($member) ); - if(count($r)) + if($r) return true; // You might question this, but // we indicate success because the group member was in fact created // -- It was just created at another time - if(! count($r)) + if(! $r) $r = q("INSERT INTO `group_member` (`uid`, `gid`, `xchan`) VALUES( %d, %d, '%s' ) ", intval($uid), @@ -205,7 +205,7 @@ function group_get_members($gid) { intval(local_channel()), intval(local_channel()) ); - if(count($r)) + if($r) $ret = $r; } return $ret; @@ -218,7 +218,7 @@ function group_get_members_xchan($gid) { intval($gid), intval(local_channel()) ); - if(count($r)) { + if($r) { foreach($r as $rr) { $ret[] = $rr['xchan']; } @@ -236,7 +236,7 @@ function mini_group_select($uid,$group = '') { intval($uid) ); $grps[] = array('name' => '', 'hash' => '0', 'selected' => ''); - if(count($r)) { + if($r) { foreach($r as $rr) { $grps[] = array('name' => $rr['gname'], 'id' => $rr['hash'], 'selected' => (($group == $rr['hash']) ? 'true' : '')); } @@ -279,7 +279,7 @@ function group_side($every="connections",$each="group",$edit = false, $group_id $member_of = groups_containing(local_channel(),$cid); } - if(count($r)) { + if($r) { foreach($r as $rr) { $selected = (($group_id == $rr['id']) ? ' group-selected' : ''); @@ -356,7 +356,7 @@ function groups_containing($uid,$c) { ); $ret = array(); - if(count($r)) { + if($r) { foreach($r as $rr) $ret[] = $rr['gid']; } diff --git a/include/help.php b/include/help.php index 5518eeb70..7f57f3334 100644 --- a/include/help.php +++ b/include/help.php @@ -30,7 +30,8 @@ function search_doc_files($s) { $regexop = db_getfunc('REGEXP'); - $r = q("select item_id.sid, item.* from item left join item_id on item.id = item_id.iid where service = 'docfile' and + $r = q("select iconfig.v, item.* from item left join iconfig on item.id = iconfig.iid + where iconfig.cat = 'system' and iconfig.k = 'docfile' and body $regexop '%s' and item_type = %d $pager_sql", dbesc($s), intval(ITEM_TYPE_DOC) @@ -50,7 +51,7 @@ function search_doc_files($s) { } } } - if(stristr($r[$x]['sid'],$s)) + if(stristr($r[$x]['v'],$s)) $r[$x]['rank'] ++; $r[$x]['rank'] += substr_count(strtolower($r[$x]['text']),strtolower($s)); // bias the results to the observer's native language @@ -123,12 +124,15 @@ function store_doc_file($s) { $item['owner_xchan'] = $item['author_xchan'] = $sys['channel_hash']; $item['item_type'] = ITEM_TYPE_DOC; - $r = q("select item.* from item left join item_id on item.id = item_id.iid where service = 'docfile' and - sid = '%s' and item_type = %d limit 1", + $r = q("select item.* from item left join iconfig on item.id = iconfig.iid + where iconfig.cat = 'system' and iconfig.k = 'docfile' and + iconfig.v = '%s' and item_type = %d limit 1", dbesc($s), intval(ITEM_TYPE_DOC) ); + \Zotlabs\Lib\IConfig::Set($item,'system','docfile',$s); + if($r) { $item['id'] = $r[0]['id']; $item['mid'] = $item['parent_mid'] = $r[0]['mid']; @@ -139,10 +143,7 @@ function store_doc_file($s) { $x = item_store($item); } - if($x['success']) { - update_remote_id($sys,$x['item_id'],ITEM_TYPE_DOC,$s,'docfile',0,$item['mid']); - } - + return $x; } diff --git a/include/import.php b/include/import.php index be456bfa9..889b50eb1 100644 --- a/include/import.php +++ b/include/import.php @@ -553,7 +553,7 @@ function sync_chatrooms($channel,$chatrooms) { -function import_items($channel,$items,$sync = false) { +function import_items($channel,$items,$sync = false,$relocate = null) { if($channel && $items) { $allow_code = false; @@ -575,22 +575,23 @@ function import_items($channel,$items,$sync = false) { if(! $item) continue; + if($relocate && $item['mid'] === $item['parent_mid']) { + item_url_replace($channel,$item,$relocate['url'],z_root(),$relocate['channel_address']); + } + $r = q("select id, edited from item where mid = '%s' and uid = %d limit 1", dbesc($item['mid']), intval($channel['channel_id']) ); if($r) { - if($item['edited'] > $r[0]['edited']) { + + // flags may have changed and we are probably relocating the post, + // so force an update even if we have the same timestamp + + if($item['edited'] >= $r[0]['edited']) { $item['id'] = $r[0]['id']; $item['uid'] = $channel['channel_id']; $item_result = item_store_update($item,$allow_code,$deliver); - if($sync && $item['item_wall']) { - // deliver singletons if we have any - if($item_result && $item_result['success']) { - Zotlabs\Daemon\Master::Summon(array('Notifier','single_activity',$item_result['item_id'])); - } - } - continue; } } else { @@ -598,10 +599,11 @@ function import_items($channel,$items,$sync = false) { $item['uid'] = $channel['channel_id']; $item_result = item_store($item,$allow_code,$deliver); } + if($sync && $item['item_wall']) { // deliver singletons if we have any if($item_result && $item_result['success']) { - Zotlabs\Daemon\Master::Summon(array('Notifier','single_activity',$item_result['item_id'])); + Zotlabs\Daemon\Master::Summon( [ 'Notifier','single_activity',$item_result['item_id'] ]); } } } @@ -609,8 +611,8 @@ function import_items($channel,$items,$sync = false) { } -function sync_items($channel,$items) { - import_items($channel,$items,true); +function sync_items($channel,$items,$relocate = null) { + import_items($channel,$items,true,$relocate); } @@ -624,19 +626,14 @@ function import_item_ids($channel,$itemids) { ); if(! $r) continue; - $z = q("select * from item_id where service = '%s' and sid = '%s' and iid = %d and uid = %d limit 1", + $z = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = '%s' + and iconfig.v = '%s' and iid = %d limit 1", dbesc($i['service']), dbesc($i['sid']), - intval($r[0]['id']), - intval($channel['channel_id']) + intval($r[0]['id']) ); if(! $z) { - q("insert into item_id (iid,uid,sid,service) values(%d,%d,'%s','%s')", - intval($r[0]['id']), - intval($channel['channel_id']), - dbesc($i['sid']), - dbesc($i['service']) - ); + \Zotlabs\Lib\IConfig::Set($r[0]['id'],'system',$i['service'],$i['sid'],true); } } } @@ -1007,7 +1004,7 @@ function sync_files($channel,$files) { $attach_id = $x[0]['id']; } - $newfname = 'store/' . $channel['channel_address'] . '/' . get_attach_binname($att['data']); + $newfname = 'store/' . $channel['channel_address'] . '/' . get_attach_binname($att['content']); unset($att['id']); $att['aid'] = $channel['channel_account_id']; @@ -1205,31 +1202,9 @@ function sync_files($channel,$files) { } } if($f['item']) { - sync_items($channel,$f['item']); - foreach($f['item'] as $i) { - if($i['message_id'] !== $i['message_parent']) - continue; - $r = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc($i['message_id']), - intval($channel['channel_id']) - ); - if($r) { - $item = $r[0]; - item_url_replace($channel,$item,$oldbase,z_root(),$original_channel); - - dbesc_array($item); - $item_id = $item['id']; - unset($item['id']); - $str = ''; - foreach($item as $k => $v) { - if($str) - $str .= ","; - $str .= " `" . $k . "` = '" . $v . "' "; - } - - $r = dbq("update `item` set " . $str . " where id = " . $item_id ); - } - } + sync_items($channel,$f['item'], + ['channel_address' => $original_channel,'url' => $oldbase] + ); } } } diff --git a/include/items.php b/include/items.php index 93385c6e6..373090d41 100755 --- a/include/items.php +++ b/include/items.php @@ -449,11 +449,7 @@ function post_activity_item($arr) { call_hooks('post_local_end', $arr); Zotlabs\Daemon\Master::Summon(array('Notifier','activity',$post_id)); $ret['success'] = true; - $r = q("select * from item where id = %d limit 1", - intval($post_id) - ); - if($r) - $ret['activity'] = $r[0]; + $ret['activity'] = $post['item']; } return $ret; @@ -677,13 +673,23 @@ function get_item_elements($x,$allow_code = false) { $arr['item_flags'] = 0; - if(array_key_exists('flags',$x) && in_array('consensus',$x['flags'])) - $arr['item_consensus'] = 1; + if(array_key_exists('flags',$x)) { - if(array_key_exists('flags',$x) && in_array('deleted',$x['flags'])) - $arr['item_deleted'] = 1; - if(array_key_exists('flags',$x) && in_array('hidden',$x['flags'])) - $arr['item_hidden'] = 1; + if(in_array('consensus',$x['flags'])) + $arr['item_consensus'] = 1; + + if(in_array('deleted',$x['flags'])) + $arr['item_deleted'] = 1; + + if(in_array('notshown',$x['flags'])) + $arr['item_notshown'] = 1; + + // hidden item are no longer propagated - notshown may be a suitable alternative + + if(in_array('hidden',$x['flags'])) + $arr['item_hidden'] = 1; + + } // Here's the deal - the site might be down or whatever but if there's a new person you've never // seen before sending stuff to your stream, we MUST be able to look them up and import their data from their @@ -1339,6 +1345,8 @@ function encode_item_flags($item) { $ret[] = 'deleted'; if(intval($item['item_hidden'])) $ret[] = 'hidden'; + if(intval($item['item_notshown'])) + $ret[] = 'notshown'; if(intval($item['item_thread_top'])) $ret[] = 'thread_parent'; if(intval($item['item_nsfw'])) @@ -1877,6 +1885,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { } + $ret['item'] = $arr; call_hooks('post_remote_end',$arr); @@ -2127,6 +2136,15 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { return $ret; } + // fetch an unescaped complete copy of the stored item + + $r = q("select * from item where id = %d", + intval($orig_post_id) + ); + if($r) + $arr = $r[0]; + + $r = q("delete from term where oid = %d and otype = %d", intval($orig_post_id), intval(TERM_OBJ_POST) @@ -2158,6 +2176,8 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { $arr['iconfig'] = $meta; } + $ret['item'] = $arr; + call_hooks('post_remote_update_end',$arr); if($deliver) { @@ -3266,15 +3286,17 @@ function item_expire($uid,$days) { $item_normal = item_normal(); - $r = q("SELECT * FROM `item` - WHERE `uid` = %d - AND `created` < %s - INTERVAL %s - AND `id` = `parent` - $sql_extra + $r = q("SELECT id FROM item + WHERE uid = %d + AND created < %s - INTERVAL %s AND item_retained = 0 - $item_normal LIMIT $expire_limit ", + AND item_thread_top = 1 + AND resource_type = '' + AND item_starred = 0 + $sql_extra $item_normal LIMIT $expire_limit ", intval($uid), - db_utcnow(), db_quoteinterval(intval($days).' DAY') + db_utcnow(), + db_quoteinterval(intval($days).' DAY') ); if(! $r) @@ -3292,17 +3314,6 @@ function item_expire($uid,$days) { continue; } - // Only expire posts, not photos and photo comments - - if($item['resource_type'] === 'photo') { - retain_item($item['id']); - continue; - } - if(intval($item['item_starred'])) { - retain_item($item['id']); - continue; - } - drop_item($item['id'],false); } @@ -3537,9 +3548,8 @@ function delete_item_lowlevel($item, $stage = DROPITEM_NORMAL, $force = false) { intval($item['id']) ); - q("delete from item_id where iid = %d and uid = %d", - intval($item['id']), - intval($item['uid']) + q("delete from iconfig where iid = %d", + intval($item['id']) ); q("delete from term where oid = %d and otype = %d", @@ -4105,6 +4115,23 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C return $items; } +function webpage_to_namespace($webpage) { + + if($webpage == ITEM_TYPE_WEBPAGE) + $page_type = 'WEBPAGE'; + elseif($webpage == ITEM_TYPE_BLOCK) + $page_type = 'BUILDBLOCK'; + elseif($webpage == ITEM_TYPE_PDL) + $page_type = 'PDL'; + elseif($webpage == ITEM_TYPE_DOC) + $page_type = 'docfile'; + else + $page_type = 'unknown'; + return $page_type; + +} + + function update_remote_id($channel,$post_id,$webpage,$pagetitle,$namespace,$remote_id,$mid) { @@ -4127,32 +4154,19 @@ function update_remote_id($channel,$post_id,$webpage,$pagetitle,$namespace,$remo } if($page_type) { - // store page info as an alternate message_id so we can access it via // https://sitename/page/$channelname/$pagetitle // if no pagetitle was given or it couldn't be transliterated into a url, use the first // sixteen bytes of the mid - which makes the link portable and not quite as daunting // as the entire mid. If it were the post_id the link would be less portable. - $r = q("select * from item_id where iid = %d and uid = %d and service = '%s' limit 1", + \Zotlabs\Lib\IConfig::Set( intval($post_id), - intval($channel['channel_id']), - dbesc($page_type) + 'system', + $page_type, + ($pagetitle) ? $pagetitle : substr($mid,0,16), + false ); - if($r) { - q("update item_id set sid = '%s' where id = %d", - dbesc(($pagetitle) ? $pagetitle : substr($mid,0,16)), - intval($r[0]['id']) - ); - } - else { - q("insert into item_id ( iid, uid, sid, service ) values ( %d, %d, '%s','%s' )", - intval($post_id), - intval($channel['channel_id']), - dbesc(($pagetitle) ? $pagetitle : substr($mid,0,16)), - dbesc($page_type) - ); - } } } diff --git a/include/js_strings.php b/include/js_strings.php index b1817f373..1b4668061 100644 --- a/include/js_strings.php +++ b/include/js_strings.php @@ -4,10 +4,10 @@ function js_strings() { return replace_macros(get_markup_template('js_strings.tpl'), array( '$delitem' => t('Delete this item?'), '$comment' => t('Comment'), - '$showmore' => t('[+] show all'), - '$showfewer' => t('[-] show less'), - '$divgrowmore' => t('[+] expand'), - '$divgrowless' => t('[-] collapse'), + '$showmore' => sprintf( t('%s show all'), '<i class=\'fa fa-chevron-down\'></i>'), + '$showfewer' => sprintf( t('%s show less'), '<i class=\'fa fa-chevron-up\'></i>'), + '$divgrowmore' => sprintf( t('%s expand'), '<i class=\'fa fa-chevron-down\'></i>'), + '$divgrowless' => sprintf( t('%s collapse'),'<i class=\'fa fa-chevron-up\'></i>'), '$pwshort' => t("Password too short"), '$pwnomatch' => t("Passwords do not match"), '$everybody' => t('everybody'), diff --git a/include/nav.php b/include/nav.php index 70faec598..1fb0e98dc 100644 --- a/include/nav.php +++ b/include/nav.php @@ -104,6 +104,8 @@ EOT; if(feature_enabled($channel['channel_id'],'webpages') && (! UNO)) $nav['usermenu'][] = Array('webpages/' . $channel['channel_address'],t('Webpages'),"",t('Your webpages'),'webpages_nav_btn'); + if(feature_enabled($channel['channel_id'],'wiki') && (! UNO)) + $nav['usermenu'][] = Array('wiki/' . $channel['channel_address'],t('Wiki'),"",t('Your wiki'),'wiki_nav_btn'); } else { if(! get_account_id()) { @@ -126,7 +128,7 @@ EOT; $nav['lock'] = array('logout','','lock', sprintf( t('%s - click to logout'), $observer['xchan_addr'])); } - else { + elseif(! $_SESSION['authenticated']) { $nav['loginmenu'][] = Array('rmagic',t('Remote authentication'),'',t('Click to authenticate to your home hub'),'rmagic_nav_btn'); } @@ -143,7 +145,7 @@ EOT; if((App::$module != 'home') && (! (local_channel()))) $nav['home'] = array($homelink, t('Home'), "", t('Home Page'),'home_nav_btn'); - if((App::$config['system']['register_policy'] == REGISTER_OPEN) && (! local_channel()) && (! remote_channel())) + if((App::$config['system']['register_policy'] == REGISTER_OPEN) && (! $_SESSION['authenticated'])) $nav['register'] = array('register',t('Register'), "", t('Create an account'),'register_nav_btn'); if(! get_config('system','hide_help')) { @@ -253,6 +255,19 @@ $powered_by = ''; '$pleasewait' => t('Please wait...') )); + + if(x($_SESSION, 'reload_avatar') && $observer) { + // The avatar has been changed on the server but the browser doesn't know that, + // force the browser to reload the image from the server instead of its cache. + $tpl = get_markup_template('force_image_reload.tpl'); + + App::$page['nav'] .= replace_macros($tpl, array( + '$imgUrl' => $observer['xchan_photo_m'] + )); + unset($_SESSION['reload_avatar']); + } + + call_hooks('page_header', App::$page['nav']); } diff --git a/include/network.php b/include/network.php index 0dd10e29b..47863b680 100644 --- a/include/network.php +++ b/include/network.php @@ -21,15 +21,18 @@ function get_capath() { * TRUE if asked to return binary results (file download) * @param int $redirects default 0 * internal use, recursion counter - * @param array $opts (optional parameters) assoziative array with: + * @param array $opts (optional parameters) associative array with: * * \b accept_content => supply Accept: header with 'accept_content' as the value * * \b timeout => int seconds, default system config value or 60 seconds * * \b http_auth => username:password * * \b novalidate => do not validate SSL certs, default is to validate using our CA list * * \b nobody => only return the header * * \b filep => stream resource to write body to. header and body are not returned when using this option. + * * \b custom => custom request method: e.g. 'PUT', 'DELETE' + * * \b cookiejar => cookie file (write) + * * \B cookiefile => cookie file (read) * - * @return array an assoziative array with: + * @return array an associative array with: * * \e int \b return_code => HTTP return code or 0 if timeout or failure * * \e boolean \b success => boolean true (if HTTP 2xx result) or false * * \e string \b header => HTTP headers @@ -59,12 +62,27 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_HEADER, $false); } + if(x($opts,'upload')) + @curl_setopt($ch, CURLOPT_UPLOAD, $opts['upload']); + + if(x($opts,'infile')) + @curl_setopt($ch, CURLOPT_INFILE, $opts['infile']); + + if(x($opts,'infilesize')) + @curl_setopt($ch, CURLOPT_INFILESIZE, $opts['infilesize']); + + if(x($opts,'readfunc')) + @curl_setopt($ch, CURLOPT_READFUNCTION, $opts['readfunc']); + if(x($opts,'headers')) @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']); if(x($opts,'nobody')) @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); + if(x($opts,'custom')) + @curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $opts['custom']); + if(x($opts,'timeout') && intval($opts['timeout'])) { @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); } @@ -78,6 +96,14 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']); } + if(x($opts,'cookiejar')) + @curl_setopt($ch, CURLOPT_COOKIEJAR, $opts['cookiejar']); + if(x($opts,'cookiefile')) + @curl_setopt($ch, CURLOPT_COOKIEFILE, $opts['cookiefile']); + + if(x($opts,'cookie')) + @curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']); + @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, ((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true)); @@ -165,7 +191,9 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { * 'http_auth' => username:password * 'novalidate' => do not validate SSL certs, default is to validate using our CA list * 'filep' => stream resource to write body to. header and body are not returned when using this option. - * @return array an assoziative array with: + * 'custom' => custom request method: e.g. 'PUT', 'DELETE' + * + * @return array an associative array with: * * \e int \b return_code => HTTP return code or 0 if timeout or failure * * \e boolean \b success => boolean true (if HTTP 2xx result) or false * * \e string \b header => HTTP headers @@ -174,6 +202,10 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { */ function z_post_url($url,$params, $redirects = 0, $opts = array()) { +// logger('url: ' . $url); +// logger('params: ' . print_r($params,true)); +// logger('opts: ' . print_r($opts,true)); + $ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => ""); $ch = curl_init($url); @@ -199,12 +231,17 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { if(x($opts,'headers')) { @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']); -logger('headers: ' . print_r($opts['headers'],true) . 'redir: ' . $redirects); } if(x($opts,'nobody')) @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); + if(x($opts,'custom')) { + @curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $opts['custom']); + @curl_setopt($ch, CURLOPT_POST,0); + } + + if(x($opts,'timeout') && intval($opts['timeout'])) { @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); } @@ -218,6 +255,16 @@ logger('headers: ' . print_r($opts['headers'],true) . 'redir: ' . $redirects); @curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']); } + + if(x($opts,'cookiejar')) + @curl_setopt($ch, CURLOPT_COOKIEJAR, $opts['cookiejar']); + if(x($opts,'cookiefile')) + @curl_setopt($ch, CURLOPT_COOKIEFILE, $opts['cookiefile']); + + + if(x($opts,'cookie')) + @curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']); + @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, ((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true)); @@ -1296,8 +1343,20 @@ function discover_by_webbie($webbie) { $fullname = $vcard['fn']; if($vcard['photo'] && (strpos($vcard['photo'],'http') !== 0)) $vcard['photo'] = $diaspora_base . '/' . $vcard['photo']; + if(($vcard['key']) && (! $pubkey)) + $pubkey = $vcard['key']; if(! $avatar) $avatar = $vcard['photo']; + if($diaspora) { + if(($vcard['guid']) && (! $diaspora_guid)) + $diaspora_guid = $vcard['guid']; + if(($vcard['url']) && (! $diaspora_base)) + $diaspora_base = $vcard['url']; + + + + + } } } @@ -1962,14 +2021,7 @@ function get_site_info() { else $service_class = false; - $visible_plugins = array(); - if(is_array(App::$plugins) && count(App::$plugins)) { - $r = q("select * from addon where hidden = 0"); - if(count($r)) - foreach($r as $rr) - $visible_plugins[] = $rr['aname']; - } - sort($visible_plugins); + $visible_plugins = visible_plugin_list(); if(@is_dir('.git') && function_exists('shell_exec')) $commit = trim(@shell_exec('git log -1 --format="%h"')); diff --git a/include/oauth.php b/include/oauth.php index 984e0e6c6..a3c52bf27 100644 --- a/include/oauth.php +++ b/include/oauth.php @@ -170,7 +170,7 @@ class ZotOAuth1 extends OAuth1Server { ); if($x) { require_once('include/security.php'); - authenticate_success($x[0],true,false,true,true); + authenticate_success($x[0],null,true,false,true,true); $_SESSION['allow_api'] = true; } } diff --git a/include/oembed.php b/include/oembed.php index e968a8f65..fe068278e 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -1,6 +1,8 @@ <?php /** @file */ +use Zotlabs\Lib as Zlib; + function oembed_replacecb($matches){ $embedurl=$matches[1]; @@ -129,7 +131,7 @@ function oembed_fetch_url($embedurl){ $txt = null; if($action !== 'block') { - $txt = Cache::get(App::$videowidth . $embedurl); + $txt = Zlib\Cache::get('[' . App::$videowidth . '] ' . $embedurl); if(strstr($txt,'youtu') && strstr(z_root(),'https:')) { $txt = str_replace('http:','https:',$txt); @@ -198,7 +200,7 @@ function oembed_fetch_url($embedurl){ //save in cache if(! get_config('system','oembed_cache_disable')) - Cache::set(App::$videowidth . $embedurl,$txt); + Zlib\Cache::set('[' . App::$videowidth . '] ' . $embedurl,$txt); } diff --git a/include/page_widgets.php b/include/page_widgets.php index 49d1439be..3270de4a3 100644 --- a/include/page_widgets.php +++ b/include/page_widgets.php @@ -1,7 +1,8 @@ <?php // A basic toolbar for observers with write_pages permissions -function writepages_widget ($who,$which){ + +function writepages_widget ($who,$which) { return replace_macros(get_markup_template('write_pages.tpl'), array( '$new' => t('New Page'), '$newurl' => "webpages/$who", @@ -13,9 +14,11 @@ function writepages_widget ($who,$which){ // Chan is channel_id, $which is channel_address - we'll need to pass observer later too. -function pagelist_widget ($owner,$which){ - $r = q("select * from item_id left join item on item_id.iid = item.id where item_id.uid = %d and service = 'WEBPAGE' order by item.created desc", +function pagelist_widget ($owner,$which) { + + $r = q("select * from iconfig left join item on iconfig.iid = item.id where item_id.uid = %d + and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' order by item.created desc", intval($owner) ); @@ -24,7 +27,7 @@ function pagelist_widget ($owner,$which){ if($r) { $pages = array(); foreach($r as $rr) { - $pages[$rr['iid']][] = array('url' => $rr['iid'],'pagetitle' => $rr['sid'],'title' => $rr['title'],'created' => datetime_convert('UTC',date_default_timezone_get(),$rr['created']),'edited' => datetime_convert('UTC',date_default_timezone_get(),$rr['edited'])); + $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'])); } } diff --git a/include/photos.php b/include/photos.php index c64d662ea..c70478146 100644 --- a/include/photos.php +++ b/include/photos.php @@ -41,6 +41,10 @@ function photo_upload($channel, $observer, $args) { else $visible = 0; + $deliver = true; + if(array_key_exists('deliver',$args)) + $deliver = intval($args['deliver']); + // Set to default channel permissions. If the parent directory (album) has permissions set, // use those instead. If we have specific permissions supplied, they take precedence over // all other settings. 'allow_cid' being passed from an external source takes priority over channel settings. @@ -330,7 +334,7 @@ function photo_upload($channel, $observer, $args) { if($item['mid'] === $item['parent_mid']) { - $item['body'] = $args['body']; + $item['body'] = $summary; $item['obj_type'] = ACTIVITY_OBJ_PHOTO; $item['obj'] = json_encode($object); @@ -355,14 +359,14 @@ function photo_upload($channel, $observer, $args) { if(($item['edited'] > $r[0]['edited']) || $force) { $item['id'] = $r[0]['id']; $item['uid'] = $channel['channel_id']; - item_store_update($item); + item_store_update($item,false,$deliver); continue; } } else { $item['aid'] = $channel['channel_account_id']; $item['uid'] = $channel['channel_id']; - $item_result = item_store($item); + $item_result = item_store($item,false,$deliver); } } } @@ -414,10 +418,10 @@ function photo_upload($channel, $observer, $args) { - $result = item_store($arr); + $result = item_store($arr,false,$deliver); $item_id = $result['item_id']; - if($visible) + if($visible && $deliver) Zotlabs\Daemon\Master::Summon(array('Notifier', 'wall-new', $item_id)); } @@ -703,40 +707,65 @@ function gps2Num($coordPart) { return floatval($parts[0]) / floatval($parts[1]); } -function profile_photo_set_profile_perms($profileid = '') { +function profile_photo_set_profile_perms($uid, $profileid = 0) { $allowcid = ''; - if (x($profileid)) { - - $r = q("SELECT photo, profile_guid, id, is_default, uid FROM profile WHERE profile.id = %d OR profile.profile_guid = '%s' LIMIT 1", intval($profileid), dbesc($profileid)); - - } else { - + if($profileid) { + $r = q("SELECT photo, profile_guid, id, is_default, uid + FROM profile WHERE uid = %d and ( profile.id = %d OR profile.profile_guid = '%s') LIMIT 1", + intval($profileid), + dbesc($profileid) + ); + } + else { logger('Resetting permissions on default-profile-photo for user'.local_channel()); - $r = q("SELECT photo, profile_guid, id, is_default, uid FROM profile WHERE profile.uid = %d AND is_default = 1 LIMIT 1", intval(local_channel()) ); //If no profile is given, we update the default profile + + $r = q("SELECT photo, profile_guid, id, is_default, uid FROM profile + WHERE profile.uid = %d AND is_default = 1 LIMIT 1", + intval($uid) + ); //If no profile is given, we update the default profile } + if(! $r) + return; $profile = $r[0]; - if(x($profile['id']) && x($profile['photo'])) { - preg_match("@\w*(?=-\d*$)@i", $profile['photo'], $resource_id); - $resource_id = $resource_id[0]; + + if($profile['id'] && $profile['photo']) { + preg_match("@\w*(?=-\d*$)@i", $profile['photo'], $resource_id); + $resource_id = $resource_id[0]; - if (intval($profile['is_default']) != 1) { - $r0 = q("SELECT channel_hash FROM channel WHERE channel_id = %d LIMIT 1", intval(local_channel()) ); - $r1 = q("SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%d' ", intval($profile['id'])); //Should not be needed in future. Catches old int-profile-ids. - $r2 = q("SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%s'", dbesc($profile['profile_guid'])); + if (! intval($profile['is_default'])) { + $r0 = q("SELECT channel_hash FROM channel WHERE channel_id = %d LIMIT 1", + intval($uid) + ); + //Should not be needed in future. Catches old int-profile-ids. + $r1 = q("SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%d' ", + intval($profile['id']) + ); + $r2 = q("SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%s'", + dbesc($profile['profile_guid']) + ); $allowcid = "<" . $r0[0]['channel_hash'] . ">"; foreach ($r1 as $entry) { $allowcid .= "<" . $entry['abook_xchan'] . ">"; } foreach ($r2 as $entry) { - $allowcid .= "<" . $entry['abook_xchan'] . ">"; - } + $allowcid .= "<" . $entry['abook_xchan'] . ">"; + } - q("UPDATE `photo` SET allow_cid = '%s' WHERE resource_id = '%s' AND uid = %d",dbesc($allowcid),dbesc($resource_id),intval($profile['uid'])); + q("UPDATE photo SET allow_cid = '%s' WHERE resource_id = '%s' AND uid = %d", + dbesc($allowcid), + dbesc($resource_id), + intval($uid) + ); - } else { - q("UPDATE `photo` SET allow_cid = '' WHERE profile = 1 AND uid = %d",intval($profile['uid'])); //Reset permissions on default profile picture to public + } + else { + //Reset permissions on default profile picture to public + q("UPDATE photo SET allow_cid = '' WHERE photo_usage = %d AND uid = %d", + intval(PHOTO_PROFILE), + intval($uid) + ); } } diff --git a/include/plugin.php b/include/plugin.php index be4e92f03..cb206d944 100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -167,6 +167,12 @@ function reload_plugins() { } } +function visible_plugin_list() { + $r = q("select * from addon where hidden = 0 order by aname asc"); + return(($r) ? ids_to_array($r,'aname') : array()); +} + + /** * @brief registers a hook. @@ -545,15 +551,21 @@ function head_get_css() { } function format_css_if_exists($source) { - if (strpos($source[0], '/') !== false) + $path_prefix = script_path() . '/'; + + if (strpos($source[0], '/') !== false) { + // The source is a URL $path = $source[0]; - else + // If the url starts with // then it's an absolute URL + if($source[0][0] === '/' && $source[0][1] === '/') $path_prefix = ''; + } else { + // It's a file from the theme $path = theme_include($source[0]); + } if($path) { - $path = script_path() . '/' . $path; $qstring = ((parse_url($path, PHP_URL_QUERY)) ? '&' : '?') . 'v=' . STD_VERSION; - return '<link rel="stylesheet" href="' . $path . $qstring . '" type="text/css" media="' . $source[1] . '">' . "\r\n"; + return '<link rel="stylesheet" href="' . $path_prefix . $path . $qstring . '" type="text/css" media="' . $source[1] . '">' . "\r\n"; } } @@ -593,26 +605,38 @@ function script_path() { return $scheme . '://' . $hostname; } -function head_add_js($src) { - App::$js_sources[] = $src; +function head_add_js($src, $priority = 0) { + if(! is_array(App::$js_sources[$priority])) + App::$js_sources[$priority] = array(); + App::$js_sources[$priority][] = $src; } -function head_remove_js($src) { +function head_remove_js($src, $priority = 0) { - $index = array_search($src, App::$js_sources); + $index = array_search($src, App::$js_sources[$priority]); if($index !== false) - unset(App::$js_sources[$index]); + unset(App::$js_sources[$priority][$index]); } +// We should probably try to register main.js with a high priority, but currently we handle it +// separately and put it at the end of the html head block in case any other javascript is +// added outside the head_add_js construct. + function head_get_js() { + $str = ''; - $sources = App::$js_sources; - if(count($sources)) - foreach($sources as $source) { - if($source === 'main.js') - continue; - $str .= format_js_if_exists($source); + if(App::$js_sources) { + ksort(App::$js_sources,SORT_NUMERIC); + foreach(App::$js_sources as $sources) { + if(count($sources)) { + foreach($sources as $source) { + if($src === 'main.js') + continue; + $str .= format_js_if_exists($source); + } + } } + } return $str; } @@ -626,14 +650,20 @@ function head_get_main_js() { } function format_js_if_exists($source) { - if(strpos($source,'/') !== false) + $path_prefix = script_path() . '/'; + + if(strpos($source,'/') !== false) { + // The source is a URL $path = $source; - else + // If the url starts with // then it's an absolute URL + if($source[0] === '/' && $source[1] === '/') $path_prefix = ''; + } else { + // It's a file from the theme $path = theme_include($source); + } if($path) { - $path = script_path() . '/' . $path; $qstring = ((parse_url($path, PHP_URL_QUERY)) ? '&' : '?') . 'v=' . STD_VERSION; - return '<script src="' . $path . $qstring . '" ></script>' . "\r\n" ; + return '<script src="' . $path_prefix . $path . $qstring . '" ></script>' . "\r\n" ; } } diff --git a/include/reddav.php b/include/reddav.php deleted file mode 100644 index abf21b86d..000000000 --- a/include/reddav.php +++ /dev/null @@ -1,299 +0,0 @@ -<?php -/** - * @file include/reddav.php - * @brief some DAV related functions for Hubzilla. - * - * This file contains some functions which did not fit into one of the RedDAV - * classes. - * - * The extended SabreDAV classes you will find in the RedDAV namespace under - * @ref includes/RedDAV/. - * The original SabreDAV classes you can find under @ref vendor/sabre/dav/. - * We need to use SabreDAV 1.8.x for PHP5.3 compatibility. SabreDAV >= 2.0 - * requires PHP >= 5.4. - * - * @todo split up the classes into own files. - * - * @link http://github.com/friendica/red - * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) - */ - -use Sabre\DAV; -use Zotlabs\Storage; - -require_once('vendor/autoload.php'); -require_once('include/attach.php'); - -/** - * @brief Returns an array with viewable channels. - * - * Get a list of RedDirectory objects with all the channels where the visitor - * has <b>view_storage</b> perms. - * - * @todo Is there any reason why this is not inside RedDirectory class? - * @fixme function name looks like a class name, should we rename it? - * - * @param RedBasicAuth &$auth - * @return array RedDirectory[] - */ -function RedChannelList(&$auth) { - $ret = array(); - - $r = q("SELECT channel_id, channel_address FROM channel WHERE channel_removed = 0 AND channel_system = 0 AND NOT (channel_pageflags & %d)>0", - intval(PAGE_HIDDEN) - ); - - if ($r) { - foreach ($r as $rr) { - if (perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage')) { - logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DATA); - // @todo can't we drop '/cloud'? It gets stripped off anyway in RedDirectory - $ret[] = new Zotlabs\Storage\Directory('/cloud/' . $rr['channel_address'], $auth); - } - } - } - return $ret; -} - - -/** - * @brief TODO what exactly does this function? - * - * Array with all RedDirectory and RedFile DAV\Node items for the given path. - * - * @todo Is there any reason why this is not inside RedDirectory class? Seems - * only to be used there and we could simplify it a bit there. - * @fixme function name looks like a class name, should we rename it? - * - * @param string $file path to a directory - * @param RedBasicAuth &$auth - * @returns null|array \Sabre\DAV\INode[] - * @throw \Sabre\DAV\Exception\Forbidden - * @throw \Sabre\DAV\Exception\NotFound - */ -function RedCollectionData($file, &$auth) { - $ret = array(); - - $x = strpos($file, '/cloud'); - if ($x === 0) { - $file = substr($file, 6); - } - - // return a list of channel if we are not inside a channel - if ((! $file) || ($file === '/')) { - return RedChannelList($auth); - } - - $file = trim($file, '/'); - $path_arr = explode('/', $file); - - if (! $path_arr) - return null; - - $channel_name = $path_arr[0]; - - $r = q("SELECT channel_id FROM channel WHERE channel_address = '%s' LIMIT 1", - dbesc($channel_name) - ); - - if (! $r) - return null; - - $channel_id = $r[0]['channel_id']; - $perms = permissions_sql($channel_id); - - $auth->owner_id = $channel_id; - - $path = '/' . $channel_name; - - $folder = ''; - $errors = false; - $permission_error = false; - - for ($x = 1; $x < count($path_arr); $x++) { - $r = q("SELECT id, hash, filename, flags, is_dir FROM attach WHERE folder = '%s' AND filename = '%s' AND uid = %d AND is_dir != 0 $perms LIMIT 1", - dbesc($folder), - dbesc($path_arr[$x]), - intval($channel_id) - ); - if (! $r) { - // path wasn't found. Try without permissions to see if it was the result of permissions. - $errors = true; - $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0 limit 1", - dbesc($folder), - basename($path_arr[$x]), - intval($channel_id) - ); - if ($r) { - $permission_error = true; - } - break; - } - - if ($r && intval($r[0]['is_dir'])) { - $folder = $r[0]['hash']; - $path = $path . '/' . $r[0]['filename']; - } - } - - if ($errors) { - if ($permission_error) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } else { - throw new DAV\Exception\NotFound('A component of the request file path could not be found.'); - } - } - - // This should no longer be needed since we just returned errors for paths not found - if ($path !== '/' . $file) { - logger("Path mismatch: $path !== /$file"); - return NULL; - } - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $prefix = 'DISTINCT ON (filename)'; - $suffix = 'ORDER BY filename'; - } else { - $prefix = ''; - $suffix = 'GROUP BY filename'; - } - $r = q("select $prefix id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, created, edited from attach where folder = '%s' and uid = %d $perms $suffix", - dbesc($folder), - intval($channel_id) - ); - - foreach ($r as $rr) { - //logger('filename: ' . $rr['filename'], LOGGER_DEBUG); - if (intval($rr['is_dir'])) { - $ret[] = new Zotlabs\Storage\Directory($path . '/' . $rr['filename'], $auth); - } else { - $ret[] = new Zotlabs\Storage\File($path . '/' . $rr['filename'], $rr, $auth); - } - } - - return $ret; -} - - -/** - * @brief TODO What exactly is this function for? - * - * @fixme function name looks like a class name, should we rename it? - * - * @param string $file - * path to file or directory - * @param RedBasicAuth &$auth - * @param boolean $test (optional) enable test mode - * @return RedFile|RedDirectory|boolean|null - * @throw \Sabre\DAV\Exception\Forbidden - */ -function RedFileData($file, &$auth, $test = false) { - logger($file . (($test) ? ' (test mode) ' : ''), LOGGER_DATA); - - $x = strpos($file, '/cloud'); - if ($x === 0) { - $file = substr($file, 6); - } - else { - $x = strpos($file,'/dav'); - if($x === 0) - $file = substr($file,4); - } - - - if ((! $file) || ($file === '/')) { - return new Zotlabs\Storage\Directory('/', $auth); - } - - $file = trim($file, '/'); - - $path_arr = explode('/', $file); - - if (! $path_arr) - return null; - - $channel_name = $path_arr[0]; - - $r = q("select channel_id from channel where channel_address = '%s' limit 1", - dbesc($channel_name) - ); - - if (! $r) - return null; - - $channel_id = $r[0]['channel_id']; - - $path = '/' . $channel_name; - - $auth->owner_id = $channel_id; - - $permission_error = false; - - $folder = ''; - - require_once('include/security.php'); - $perms = permissions_sql($channel_id); - - $errors = false; - - for ($x = 1; $x < count($path_arr); $x++) { - $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0 $perms", - dbesc($folder), - dbesc($path_arr[$x]), - intval($channel_id) - ); - - if ($r && intval($r[0]['is_dir'])) { - $folder = $r[0]['hash']; - $path = $path . '/' . $r[0]['filename']; - } - if (! $r) { - $r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach - where folder = '%s' and filename = '%s' and uid = %d $perms order by filename limit 1", - dbesc($folder), - dbesc(basename($file)), - intval($channel_id) - ); - } - if (! $r) { - $errors = true; - $r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach - where folder = '%s' and filename = '%s' and uid = %d order by filename limit 1", - dbesc($folder), - dbesc(basename($file)), - intval($channel_id) - ); - if ($r) - $permission_error = true; - } - } - - if ($path === '/' . $file) { - if ($test) - return true; - // final component was a directory. - return new Zotlabs\Storage\Directory($file, $auth); - } - - if ($errors) { - logger('not found ' . $file); - if ($test) - return false; - if ($permission_error) { - logger('permission error ' . $file); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - return; - } - - if ($r) { - if ($test) - return true; - - if (intval($r[0]['is_dir'])) { - return new Zotlabs\Storage\Directory($path . '/' . $r[0]['filename'], $auth); - } else { - return new Zotlabs\Storage\File($path . '/' . $r[0]['filename'], $r[0], $auth); - } - } - return false; -}
\ No newline at end of file diff --git a/include/security.php b/include/security.php index 38045c8a9..2107ed819 100644 --- a/include/security.php +++ b/include/security.php @@ -12,7 +12,7 @@ * @param bool $return * @param bool $update_lastlog */ -function authenticate_success($user_record, $login_initial = false, $interactive = false, $return = false, $update_lastlog = false) { +function authenticate_success($user_record, $channel = null, $login_initial = false, $interactive = false, $return = false, $update_lastlog = false) { $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; @@ -23,11 +23,15 @@ function authenticate_success($user_record, $login_initial = false, $interactive $_SESSION['account_id'] = $user_record['account_id']; $_SESSION['authenticated'] = 1; + if($channel) + $uid_to_load = $channel['channel_id']; - $uid_to_load = (((x($_SESSION,'uid')) && (intval($_SESSION['uid']))) - ? intval($_SESSION['uid']) - : intval(App::$account['account_default_channel']) - ); + if(! $uid_to_load) { + $uid_to_load = (((x($_SESSION,'uid')) && (intval($_SESSION['uid']))) + ? intval($_SESSION['uid']) + : intval(App::$account['account_default_channel']) + ); + } if($uid_to_load) { change_channel($uid_to_load); @@ -82,6 +86,41 @@ function authenticate_success($user_record, $login_initial = false, $interactive /* else just return */ } +function atoken_login($atoken) { + if(! $atoken) + return false; + $_SESSION['authenticated'] = 1; + $_SESSION['visitor_id'] = $atoken['xchan_hash']; + $_SESSION['atoken'] = $atoken['atoken_id']; + + \App::set_observer($atoken); + return true; +} + + +function atoken_xchan($atoken) { + + $c = channelx_by_n($atoken['atoken_uid']); + if($c) { + return [ + 'atoken_id' => $atoken['atoken_id'], + 'xchan_hash' => substr($c['channel_hash'],0,16) . '.' . $atoken['atoken_name'], + 'xchan_name' => $atoken['atoken_name'], + 'xchan_addr' => t('guest:') . $atoken['atoken_name'] . '@' . \App::get_hostname(), + 'xchan_network' => 'unknown', + 'xchan_hidden' => 1, + 'xchan_photo_mimetype' => 'image/jpeg', + 'xchan_photo_l' => get_default_profile_photo(300), + 'xchan_photo_m' => get_default_profile_photo(80), + 'xchan_photo_s' => get_default_profile_photo(48) + + ]; + } + return null; +} + + + /** * @brief Change to another channel with current logged-in account. * @@ -125,13 +164,17 @@ function change_channel($change_channel) { ); if($x) { $_SESSION['my_url'] = $x[0]['xchan_url']; - $_SESSION['my_address'] = $r[0]['channel_address'] . '@' . substr(z_root(), strpos(z_root(), '://') + 3); + $_SESSION['my_address'] = $r[0]['channel_address'] . '@' . App::get_hostname(); App::set_observer($x[0]); App::set_perms(get_all_perms(local_channel(), $hash)); } if(! is_dir('store/' . $r[0]['channel_address'])) @os_mkdir('store/' . $r[0]['channel_address'], STORAGE_DEFAULT_PERMISSIONS,true); + + $arr = [ 'channel_id' => $change_channel, 'chanx' => $ret ]; + call_hooks('change_channel', $arr); + } return $ret; diff --git a/include/spam.php b/include/spam.php deleted file mode 100644 index 8b158b7ae..000000000 --- a/include/spam.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php /** @file */ - - -function string_splitter($s) { - - if(! $s) - return array(); - - $s = preg_replace('/\pP+/','',$s); - - $x = mb_split("\[|\]|\s",$s); - - $ret = array(); - if($x) { - foreach($x as $y) { - if(mb_strlen($y) > 2) - $ret[] = substr($y,0,64); - } - } - return $ret; -} - - - -function get_words($uid,$list) { - - stringify($list,true); - - $r = q("select * from spam where term in ( " . $list . ") and uid = %d", - intval($uid) - ); - - return $r; -} - diff --git a/include/taxonomy.php b/include/taxonomy.php index 177215fe8..067bd3246 100644 --- a/include/taxonomy.php +++ b/include/taxonomy.php @@ -400,7 +400,7 @@ function get_things($profile_hash,$uid) { if(! $things[$rr['obj_verb']]) $things[$rr['obj_verb']] = array(); - $things[$rr['obj_verb']][] = array('term' => $rr['obj_term'],'url' => $rr['obj_url'],'img' => $rr['obj_imgurl'], 'profile' => $rr['profile_name'],'term_hash' => $rr['obj_obj'], 'likes' => $l,'like_count' => count($l),'like_label' => tt('Like','Likes',count($l),'noun')); + $things[$rr['obj_verb']][] = array('term' => $rr['obj_term'],'url' => $rr['obj_url'],'img' => $rr['obj_imgurl'], 'editurl' => z_root() . '/thing/' . $rr['obj_obj'], 'profile' => $rr['profile_name'],'term_hash' => $rr['obj_obj'], 'likes' => $l,'like_count' => count($l),'like_label' => tt('Like','Likes',count($l),'noun')); } $sorted_things = array(); if($things) { diff --git a/include/text.php b/include/text.php index 1bc19da34..d4d151f2e 100644 --- a/include/text.php +++ b/include/text.php @@ -376,30 +376,6 @@ function unxmlify($s) { return $ret; } -/** - * Convenience wrapper, reverse the operation "bin2hex" - * This is a built-in function in php >= 5.4 - * - * @FIXME We already have php >= 5.4 requirements, so can we remove this? - */ -if(! function_exists('hex2bin')) { -function hex2bin($s) { - if(! (is_string($s) && strlen($s))) - return ''; - - if(strlen($s) & 1) { - logger('hex2bin: illegal hex string: ' . $s); - return $s; - } - - if(! ctype_xdigit($s)) { - return($s); - } - - return(pack("H*",$s)); -}} - - // Automatic pagination. // To use, get the count of total items. // Then call App::set_pager_total($number_items); @@ -711,7 +687,7 @@ function get_tags($s) { // ignore anything in a code block - $s = preg_replace('/\[code\](.*?)\[\/code\]/sm','',$s); + $s = preg_replace('/\[code(.*?)\](.*?)\[\/code\]/sm','',$s); // ignore anything in [style= ] $s = preg_replace('/\[style=(.*?)\]/sm','',$s); @@ -798,6 +774,10 @@ function strip_zids($s) { return preg_replace('/[\?&]zid=(.*?)(&|$)/ism','$2',$s); } +function strip_zats($s) { + return preg_replace('/[\?&]zat=(.*?)(&|$)/ism','$2',$s); +} + // quick and dirty quoted_printable encoding @@ -1283,7 +1263,7 @@ function normalise_link($url) { * is https and the other isn't, or if one is www.something and the other * isn't - and also ignore case differences. * - * @see normalis_link() + * @see normalise_link() * * @param string $a * @param string $b @@ -1635,7 +1615,7 @@ function prepare_text($text, $content_type = 'text/bbcode', $cache = false) { function create_export_photo_body(&$item) { if(($item['verb'] === ACTIVITY_POST) && ($item['obj_type'] === ACTIVITY_OBJ_PHOTO)) { - $j = json_decode($item['object'],true); + $j = json_decode($item['obj'],true); if($j) { $item['body'] .= "\n\n" . (($j['body']) ? $j['body'] : $j['bbcode']); $item['sig'] = ''; @@ -1743,7 +1723,8 @@ function unamp($s) { } function layout_select($channel_id, $current = '') { - $r = q("select mid,sid from item left join item_id on iid = item.id where service = 'PDL' and item.uid = item_id.uid and item_id.uid = %d and item_type = %d ", + $r = q("select mid, v from item left join iconfig on iconfig.iid = item.id + where iconfig.cat = 'system' and iconfig.k = 'PDL' and item.uid = %d and item_type = %d ", intval($channel_id), intval(ITEM_TYPE_PDL) ); @@ -1753,7 +1734,7 @@ function layout_select($channel_id, $current = '') { $options .= '<option value="" ' . $empty_selected . '>' . t('default') . '</option>'; foreach($r as $rr) { $selected = (($rr['mid'] == $current) ? ' selected="selected" ' : ''); - $options .= '<option value="' . $rr['mid'] . '"' . $selected . '>' . $rr['sid'] . '</option>'; + $options .= '<option value="' . $rr['mid'] . '"' . $selected . '>' . $rr['v'] . '</option>'; } } @@ -2049,7 +2030,7 @@ function ids_to_array($arr,$idx = 'id') { $t = array(); if($arr) { foreach($arr as $x) { - if(! in_array($x[$idx],$t)) { + if(array_key_exists($idx,$x) && strlen($x[$idx]) && (! in_array($x[$idx],$t))) { $t[] = $x[$idx]; } } @@ -2088,9 +2069,9 @@ function xchan_query(&$items,$abook = true,$effective_uid = 0) { } foreach($items as $item) { - if($item['owner_xchan'] && (! in_array($item['owner_xchan'],$arr))) + if($item['owner_xchan'] && (! in_array("'" . dbesc($item['owner_xchan']) . "'",$arr))) $arr[] = "'" . dbesc($item['owner_xchan']) . "'"; - if($item['author_xchan'] && (! in_array($item['author_xchan'],$arr))) + if($item['author_xchan'] && (! in_array("'" . dbesc($item['author_xchan']) . "'",$arr))) $arr[] = "'" . dbesc($item['author_xchan']) . "'"; } } @@ -2123,9 +2104,9 @@ function xchan_mail_query(&$item) { $arr = array(); $chans = null; if($item) { - if($item['from_xchan'] && (! in_array($item['from_xchan'],$arr))) + if($item['from_xchan'] && (! in_array("'" . dbesc($item['from_xchan']) . "'",$arr))) $arr[] = "'" . dbesc($item['from_xchan']) . "'"; - if($item['to_xchan'] && (! in_array($item['to_xchan'],$arr))) + if($item['to_xchan'] && (! in_array("'" . dbesc($item['to_xchan']) . "'",$arr))) $arr[] = "'" . dbesc($item['to_xchan']) . "'"; } @@ -2377,7 +2358,13 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d $str_tags .= $newtag; } - return array('replaced' => $replaced, 'termtype' => $termtype, 'term' => $basetag, 'url' => $url, 'contact' => $r[0]); + return [ + 'replaced' => $replaced, + 'termtype' => $termtype, + 'term' => $basetag, + 'url' => $url, + 'contact' => $r[0] + ]; } //is it a person tag? @@ -2568,7 +2555,13 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d } } - return array('replaced' => $replaced, 'termtype' => $termtype, 'term' => $newname, 'url' => $url, 'contact' => $r[0]); + return [ + 'replaced' => $replaced, + 'termtype' => $termtype, + 'term' => $newname, + 'url' => $url, + 'contact' => $r[0] + ]; } function linkify_tags($a, &$body, $uid, $diaspora = false) { @@ -2821,13 +2814,16 @@ function expand_acl($s) { // If it has a pdl we'll load it as we know the mid and pass the body through comanche_parser() which will generate the // page layout from the given description +// @FIXME - there is apparently a very similar function called layout_select; this one should probably take precedence +// and the other should be checked for compatibility and removed function pdl_selector($uid, $current="") { $o = ''; $sql_extra = item_permissions_sql($uid); - $r = q("select item_id.*, mid from item_id left join item on iid = item.id where item_id.uid = %d and item_id.uid = item.uid and service = 'PDL' $sql_extra order by sid asc", + $r = q("select iconfig.*, mid from item_id left join item on iconfig.iid = item.id + where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' $sql_extra order by v asc", intval($uid) ); @@ -2841,7 +2837,7 @@ function pdl_selector($uid, $current="") { $entries[] = array('title' => t('Default'), 'mid' => ''); foreach($entries as $selection) { $selected = (($selection == $current) ? ' selected="selected" ' : ''); - $o .= "<option value=\"{$selection['mid']}\" $selected >{$selection['sid']}</option>"; + $o .= "<option value=\"{$selection['mid']}\" $selected >{$selection['v']}</option>"; } $o .= '</select>'; @@ -2877,3 +2873,54 @@ function flatten_array_recursive($arr) { } return($ret); } + +function text_highlight($s,$lang) { + + if($lang === 'js') + $lang = 'javascript'; + + if($lang === 'json') { + $lang = 'javascript'; + if(! strpos(trim($s),"\n")) + $s = jindent($s); + } + + if(! strpos('Text_Highlighter',get_include_path())) { + set_include_path(get_include_path() . PATH_SEPARATOR . 'library/Text_Highlighter'); + } + require_once('library/Text_Highlighter/Text/Highlighter.php'); + require_once('library/Text_Highlighter/Text/Highlighter/Renderer/Html.php'); + $options = array( + 'numbers' => HL_NUMBERS_LI, + 'tabsize' => 4, + ); + $tag_added = false; + $s = trim(html_entity_decode($s,ENT_COMPAT)); + $s = str_replace(" ","\t",$s); + + // The highlighter library insists on an opening php tag for php code blocks. If + // it isn't present, nothing is highlighted. So we're going to see if it's present. + // If not, we'll add it, and then quietly remove it after we get the processed output back. + + if($lang === 'php') { + if(strpos('<?php',$s) !== 0) { + $s = '<?php' . "\n" . $s; + $tag_added = true; + } + + } + $renderer = new Text_Highlighter_Renderer_HTML($options); + $hl = Text_Highlighter::factory($lang); + $hl->setRenderer($renderer); + $o = $hl->highlight($s); + $o = str_replace([" ","\n"],[" ",''],$o); + + if($tag_added) { + $b = substr($o,0,strpos($o,'<li>')); + $e = substr($o,strpos($o,'</li>')); + $o = $b . $e; + } + + return('<code>' . $o . '</code>'); +} + diff --git a/include/widgets.php b/include/widgets.php index 3ca189af0..da73657f5 100644 --- a/include/widgets.php +++ b/include/widgets.php @@ -296,7 +296,7 @@ function widget_filer($arr) { $selected = ((x($_REQUEST,'file')) ? $_REQUEST['file'] : ''); $terms = array(); - $r = q("select distinct(term) from term where uid = %d and ttype = %d order by term asc", + $r = q("select distinct term from term where uid = %d and ttype = %d order by term asc", intval(local_channel()), intval(TERM_FILE) ); @@ -609,6 +609,15 @@ function widget_settings_menu($arr) { 'selected' => ((argv(1) === 'oauth') ? 'active' : ''), ); + if(! UNO) { + $tabs[] = array( + 'label' => t('Guest Access Tokens'), + 'url' => z_root() . '/settings/tokens', + 'selected' => ((argv(1) === 'tokens') ? 'active' : ''), + ); + } + + if($role === false || $role === 'custom') { $tabs[] = array( 'label' => t('Connection Default Permissions'), @@ -743,21 +752,6 @@ function widget_conversations($arr) { return $o; } -function widget_eventsmenu($arr) { - if (! local_channel()) - return; - - return replace_macros(get_markup_template('events_menu_side.tpl'), array( - '$title' => t('Events Menu'), - '$day' => t('Day View'), - '$week' => t('Week View'), - '$month' => t('Month View'), - '$export' => t('Export'), - '$upload' => t('Import'), - '$submit' => t('Submit') - )); -} - function widget_eventstools($arr) { if (! local_channel()) return; @@ -994,8 +988,9 @@ function widget_item($arr) { $sql_extra = item_permissions_sql($channel_id); if($arr['title']) { - $r = q("select item.* from item left join item_id on item.id = item_id.iid - where item.uid = %d and sid = '%s' and service = 'WEBPAGE' and item_type = %d $sql_options $revision limit 1", + $r = q("select item.* from item left join iconfig on item.id = iconfig.iid + where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' + and iconfig.k = 'WEBPAGE' and item_type = %d $sql_options $revision limit 1", intval($channel_id), dbesc($arr['title']), intval(ITEM_TYPE_WEBPAGE) @@ -1157,7 +1152,7 @@ function widget_cover_photo($arr) { if(array_key_exists('subtitle', $arr) && isset($arr['subtitle'])) $subtitle = $arr['subtitle']; else - $subtitle = $channel['xchan_addr']; + $subtitle = str_replace('@','@',$channel['xchan_addr']); $c = get_cover_photo($channel_id,'html'); @@ -1253,8 +1248,8 @@ function widget_random_block($arr) { $randfunc = db_getfunc('RAND'); - $r = q("select item.* from item left join item_id on item.id = item_id.iid - where item.uid = %d and sid like '%s' and service = 'BUILDBLOCK' and + $r = q("select item.* from item left join iconfig on item.id = iconfig.iid + where item.uid = %d and iconfig.cat = 'system' and iconfig.v like '%s' and iconfig.k = 'BUILDBLOCK' and item_type = %d $sql_options order by $randfunc limit 1", intval($channel_id), dbesc('%' . $contains . '%'), @@ -1361,7 +1356,7 @@ function widget_forums($arr) { $perms_sql = item_permissions_sql(local_channel()) . item_normal(); - $r1 = q("select * from abook left join xchan on abook_xchan = xchan_hash where ( xchan_pubforum = 1 or ((abook_their_perms & %d ) != 0 and (abook_their_perms & %d ) = 0) ) and xchan_deleted = 0 and abook_channel = %d order by xchan_name $limit ", + $r1 = q("select abook_id, xchan_hash, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where ( xchan_pubforum = 1 or ((abook_their_perms & %d ) != 0 and (abook_their_perms & %d ) = 0) ) and xchan_deleted = 0 and abook_channel = %d order by xchan_name $limit ", intval(PERMS_W_TAGWALL), intval(PERMS_W_STREAM), intval(local_channel()) @@ -1375,12 +1370,34 @@ function widget_forums($arr) { // There also should be a way to update this via ajax. for($x = 0; $x < count($r1); $x ++) { - $r = q("select sum(item_unseen) as unseen from item where owner_xchan = '%s' and uid = %d $perms_sql ", + $r = q("select sum(item_unseen) as unseen from item where owner_xchan = '%s' and uid = %d and item_unseen = 1 $perms_sql ", dbesc($r1[$x]['xchan_hash']), intval(local_channel()) ); if($r) $r1[$x]['unseen'] = $r[0]['unseen']; + +/** + * @FIXME + * This SQL makes the counts correct when you get forum posts arriving from different routes/sources + * (like personal channels). However the network query for these posts doesn't yet include this + * correction and it makes the SQL for that query pretty hairy so this is left as a future exercise. + * It may make more sense in that query to look for the mention in the body rather than another join, + * but that makes it very inefficient. + * + $r = q("select sum(item_unseen) as unseen from item left join term on oid = id where otype = %d and owner_xchan != '%s' and item.uid = %d and url = '%s' and ttype = %d $perms_sql ", + intval(TERM_OBJ_POST), + dbesc($r1[$x]['xchan_hash']), + intval(local_channel()), + dbesc($r1[$x]['xchan_url']), + intval(TERM_MENTION) + ); + if($r) + $r1[$x]['unseen'] = ((array_key_exists('unseen',$r1[$x])) ? $r1[$x]['unseen'] + $r[0]['unseen'] : $r[0]['unseen']); + * + * end @FIXME + */ + } if($r1) { diff --git a/include/wiki.php b/include/wiki.php index 4aa3fc1b4..424b2d9a0 100644 --- a/include/wiki.php +++ b/include/wiki.php @@ -51,7 +51,7 @@ function wiki_init_wiki($channel, $wiki) { return null; } // Create GitRepo object - $git = new GitRepo($channel['channel_address'], null, false, $name, __DIR__ . '/../' . $path); + $git = new GitRepo($channel['channel_address'], null, false, $wiki['urlName'], __DIR__ . '/../' . $path); if(!$git->initRepo()) { logger('Error creating new git repo in ' . $git->path); return null; @@ -82,7 +82,7 @@ function wiki_create_wiki($channel, $observer_hash, $wiki, $acl) { $ac = $acl->get(); $mid = item_message_id(); $arr = array(); // Initialize the array of parameters for the post - $item_hidden = 0; // TODO: Allow form creator to send post to ACL about new game automatically + $item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0); $wiki_url = z_root() . '/wiki/' . $channel['channel_address'] . '/' . $wiki['urlName']; $arr['aid'] = $channel['channel_account_id']; $arr['uid'] = $channel['channel_id']; @@ -231,6 +231,32 @@ function wiki_create_page($name, $resource_id) { } +function wiki_rename_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $pageNewName = ((array_key_exists('pageNewName',$arr)) ? $arr['pageNewName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('message' => 'Wiki not found.', 'success' => false); + } + $page_path_old = $w['path'].'/'.$pageUrlName.'.md'; + if (!is_readable($page_path_old) === true) { + return array('message' => 'Cannot read wiki page: ' . $page_path_old, 'success' => false); + } + $page = array('rawName' => $pageNewName, 'htmlName' => escape_tags($pageNewName), 'urlName' => urlencode(escape_tags($pageNewName)), 'fileName' => urlencode(escape_tags($pageNewName)).'.md'); + $page_path_new = $w['path'] . '/' . $page['fileName'] ; + if (is_file($page_path_new)) { + return array('message' => 'Page already exists.', 'success' => false); + } + // Rename the page file in the wiki repo + if(!rename($page_path_old, $page_path_new)) { + return array('message' => 'Error renaming page file.', 'success' => false); + } else { + return array('page' => $page, 'message' => '', 'success' => true); + } + +} + function wiki_get_page_content($arr) { $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); @@ -319,7 +345,7 @@ function wiki_revert_page($arr) { $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); $commitHash = ((array_key_exists('commitHash',$arr)) ? $arr['commitHash'] : null); if (! $commitHash) { - return array('content' => $content, 'message' => 'No commit has provided', 'success' => false); + return array('content' => $content, 'message' => 'No commit was provided', 'success' => false); } $w = wiki_get_wiki($resource_id); if (!$w['path']) { @@ -342,7 +368,7 @@ function wiki_revert_page($arr) { } } } catch (\PHPGit\Exception\GitException $e) { - json_return_and_die(array('content' => $content, 'message' => 'GitRepo error thrown', 'success' => false)); + return array('content' => $content, 'message' => 'GitRepo error thrown', 'success' => false); } return array('content' => $content, 'message' => '', 'success' => true); } else { @@ -350,11 +376,62 @@ function wiki_revert_page($arr) { } } +function wiki_compare_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $currentCommit = ((array_key_exists('currentCommit',$arr)) ? $arr['currentCommit'] : 'HEAD'); + $compareCommit = ((array_key_exists('compareCommit',$arr)) ? $arr['compareCommit'] : null); + if (! $compareCommit) { + return array('message' => 'No compare commit was provided', 'success' => false); + } + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('message' => 'Error reading wiki', 'success' => false); + } + $page_path = $w['path'].'/'.$pageUrlName.'.md'; + if (is_readable($page_path) === true) { + $reponame = ((array_key_exists('title', $w['wiki'])) ? urlencode($w['wiki']['title']) : 'repo'); + if($reponame === '') { + $reponame = 'repo'; + } + $git = new GitRepo('', null, false, $w['wiki']['title'], $w['path']); + $compareContent = $currentContent = ''; + try { + foreach ($git->git->tree($currentCommit) as $object) { + if ($object['type'] == 'blob' && $object['file'] === $pageUrlName.'.md' ) { + $currentContent = $git->git->cat->blob($object['hash']); + } + } + foreach ($git->git->tree($compareCommit) as $object) { + if ($object['type'] == 'blob' && $object['file'] === $pageUrlName.'.md' ) { + $compareContent = $git->git->cat->blob($object['hash']); + } + } + require_once('library/class.Diff.php'); + $diff = Diff::toTable(Diff::compare($currentContent, $compareContent)); + } catch (\PHPGit\Exception\GitException $e) { + return array('message' => 'GitRepo error thrown', 'success' => false); + } + return array('diff' => $diff, 'message' => '', 'success' => true); + } else { + return array('message' => 'Page file not writable', 'success' => false); + } +} + function wiki_git_commit($arr) { $files = ((array_key_exists('files', $arr)) ? $arr['files'] : null); + $all = ((array_key_exists('all', $arr)) ? $arr['all'] : false); $commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : 'Repo updated'); - $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : json_return_and_die(array('message' => 'Wiki resource_id required for git commit', 'success' => false))); - $observer = ((array_key_exists('observer', $arr)) ? $arr['observer'] : json_return_and_die(array('message' => 'Observer required for git commit', 'success' => false))); + if(array_key_exists('resource_id', $arr)) { + $resource_id = $arr['resource_id']; + } else { + return array('message' => 'Wiki resource_id required for git commit', 'success' => false); + } + if(array_key_exists('observer', $arr)) { + $observer = $arr['observer']; + } else { + return array('message' => 'Observer required for git commit', 'success' => false); + } $w = wiki_get_wiki($resource_id); if (!$w['path']) { return array('message' => 'Error reading wiki', 'success' => false); @@ -369,23 +446,23 @@ function wiki_git_commit($arr) { if ($files === null) { $options = array('all' => true); // git commit option to include all changes } else { - $options = array(); // git commit options + $options = array('all' => $all); // git commit options\ foreach ($files as $file) { if (!$git->git->add($file)) { // add specified files to the git repo stage if (!$git->git->reset->hard()) { - json_return_and_die(array('message' => 'Error adding file to git stage: ' . $file . '. Error resetting git repo.', 'success' => false)); + return array('message' => 'Error adding file to git stage: ' . $file . '. Error resetting git repo.', 'success' => false); } - json_return_and_die(array('message' => 'Error adding file to git stage: ' . $file, 'success' => false)); + return array('message' => 'Error adding file to git stage: ' . $file, 'success' => false); } } } if ($git->commit($commit_msg, $options)) { - json_return_and_die(array('message' => 'Wiki repo commit succeeded', 'success' => true)); + return array('message' => 'Wiki repo commit succeeded', 'success' => true); } else { - json_return_and_die(array('message' => 'Wiki repo commit failed', 'success' => false)); + return array('message' => 'Wiki repo commit failed', 'success' => false); } } catch (\PHPGit\Exception\GitException $e) { - json_return_and_die(array('message' => 'GitRepo error thrown', 'success' => false)); + return array('message' => 'GitRepo error thrown', 'success' => false); } } @@ -396,4 +473,99 @@ function wiki_generate_page_filename($name) { } else { return $file . '.md'; } -}
\ No newline at end of file +} + +function wiki_convert_links($s, $wikiURL) { + + if (strpos($s,'[[') !== false) { + preg_match_all("/\[\[(.*?)\]\]/", $s, $match); + $pages = $pageURLs = array(); + foreach ($match[1] as $m) { + // TODO: Why do we need to double urlencode for this to work? + $pageURLs[] = urlencode(urlencode(escape_tags($m))); + $pages[] = $m; + } + $idx = 0; + while(strpos($s,'[[') !== false) { + $replace = '<a href="'.$wikiURL.'/'.$pageURLs[$idx].'">'.$pages[$idx].'</a>'; + $s = preg_replace("/\[\[(.*?)\]\]/", $replace, $s, 1); + $idx++; + } + } + return $s; +} + +function wiki_generate_toc($s) { + + if (strpos($s,'[toc]') !== false) { + //$toc_md = wiki_toc($s); // Generate Markdown-formatted list prior to HTML render + $toc_md = '<ul id="wiki-toc"></ul>'; // use the available jQuery plugin http://ndabas.github.io/toc/ + $s = preg_replace("/\[toc\]/", $toc_md, $s, -1); + } + return $s; +} + +// This function is derived from +// http://stackoverflow.com/questions/32068537/generate-table-of-contents-from-markdown-in-php +function wiki_toc($content) { + // ensure using only "\n" as line-break + $source = str_replace(["\r\n", "\r"], "\n", $content); + + // look for markdown TOC items + preg_match_all( + '/^(?:=|-|#).*$/m', + $source, + $matches, + PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE + ); + + // preprocess: iterate matched lines to create an array of items + // where each item is an array(level, text) + $file_size = strlen($source); + foreach ($matches[0] as $item) { + $found_mark = substr($item[0], 0, 1); + if ($found_mark == '#') { + // text is the found item + $item_text = $item[0]; + $item_level = strrpos($item_text, '#') + 1; + $item_text = substr($item_text, $item_level); + } else { + // text is the previous line (empty if <hr>) + $item_offset = $item[1]; + $prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2)); + $item_text = + substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1); + $item_text = trim($item_text); + $item_level = $found_mark == '=' ? 1 : 2; + } + if (!trim($item_text) OR strpos($item_text, '|') !== FALSE) { + // item is an horizontal separator or a table header, don't mind + continue; + } + $raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)]; + } + $o = ''; + foreach($raw_toc as $t) { + $level = intval($t['level']); + $text = $t['text']; + switch ($level) { + case 1: + $li = '* '; + break; + case 2: + $li = ' * '; + break; + case 3: + $li = ' * '; + break; + case 4: + $li = ' * '; + break; + default: + $li = '* '; + break; + } + $o .= $li . $text . "\n"; + } + return $o; +} diff --git a/include/zot.php b/include/zot.php index 043139e2f..45347ef22 100644 --- a/include/zot.php +++ b/include/zot.php @@ -552,7 +552,7 @@ function zot_refresh($them, $channel = null, $force = false) { unset($new_connection[0]['abook_account']); unset($new_connection[0]['abook_channel']); - $abconfig = load_abconfig($channel['channel_hash'],$new_connection['abook_xchan']); + $abconfig = load_abconfig($channel['channel_id'],$new_connection['abook_xchan']); if($abconfig) $new_connection['abconfig'] = $abconfig; @@ -3014,7 +3014,12 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { if($x['hubloc_host'] == App::get_hostname()) continue; - $synchubs[] = $x; + $y = q("select site_dead from site where site_url = '%s' limit 1", + dbesc($x['hubloc_url']) + ); + + if((! $y) || ($y[0]['site_dead'] == 0)) + $synchubs[] = $x; } if(! $synchubs) @@ -3031,7 +3036,8 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { $info = (($packet) ? $packet : array()); $info['type'] = 'channel_sync'; - $info['encoding'] = 'red'; // note: not zot, this packet is very red specific + $info['encoding'] = 'red'; // note: not zot, this packet is very platform specific + $info['relocate'] = ['channel_address' => $channel['channel_address'], 'url' => z_root() ]; if(array_key_exists($uid,App::$config) && array_key_exists('transient',App::$config[$uid])) { $settings = App::$config[$uid]['transient']; @@ -3169,10 +3175,13 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { sync_events($channel,$arr['event']); if(array_key_exists('event_item',$arr) && $arr['event_item']) - sync_items($channel,$arr['event_item']); + sync_items($channel,$arr['event_item'],((array_key_exists('relocate',$arr)) ? $arr['relocate'] : null)); if(array_key_exists('item',$arr) && $arr['item']) - sync_items($channel,$arr['item']); + sync_items($channel,$arr['item'],((array_key_exists('relocate',$arr)) ? $arr['relocate'] : null)); + + // deprecated, maintaining for a few months for upward compatibility + // this should sync webpages, but the logic is a bit subtle if(array_key_exists('item_id',$arr) && $arr['item_id']) sync_items($channel,$arr['item_id']); @@ -3331,8 +3340,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { if($abconfig) { // @fixme does not handle sync of del_abconfig foreach($abconfig as $abc) { - if($abc['chan'] === $channel['channel_hash']) - set_abconfig($abc['chan'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']); + set_abconfig($channel['channel_id'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']); } } } @@ -3529,13 +3537,6 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { } } - - if(array_key_exists('item',$arr) && $arr['item']) - sync_items($channel,$arr['item']); - - if(array_key_exists('item_id',$arr) && $arr['item_id']) - sync_items($channel,$arr['item_id']); - $addon = array('channel' => $channel,'data' => $arr); call_hooks('process_channel_sync_delivery',$addon); |