diff options
author | mrjive <mrjive@mrjive.it> | 2018-04-05 10:52:39 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-05 10:52:39 +0200 |
commit | a7ff2cc5ea11afd7b832bef24866abfb0220d022 (patch) | |
tree | e7dcd63bbfdaa6ae564c60d59ef60edf0e6ba7a7 | |
parent | 20ac91703d54679d2e5080ba2d4985e93986a515 (diff) | |
parent | dcacdd23c87061dc15bd12ed2e959bcbe020c5df (diff) | |
download | volse-hubzilla-a7ff2cc5ea11afd7b832bef24866abfb0220d022.tar.gz volse-hubzilla-a7ff2cc5ea11afd7b832bef24866abfb0220d022.tar.bz2 volse-hubzilla-a7ff2cc5ea11afd7b832bef24866abfb0220d022.zip |
Merge pull request #3 from redmatrix/dev
Dev
48 files changed, 1068 insertions, 222 deletions
diff --git a/Zotlabs/Lib/Chatroom.php b/Zotlabs/Lib/Chatroom.php index e762620ae..882c846cd 100644 --- a/Zotlabs/Lib/Chatroom.php +++ b/Zotlabs/Lib/Chatroom.php @@ -266,7 +266,7 @@ class Chatroom { intval($room_id), dbesc($xchan), dbesc(datetime_convert()), - dbesc($arr['chat_text']) + dbesc(str_rot47(base64url_encode($arr['chat_text']))) ); $ret['success'] = true; diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php index 245b0a9b7..ef901aef1 100644 --- a/Zotlabs/Module/Acl.php +++ b/Zotlabs/Module/Acl.php @@ -95,7 +95,7 @@ class Acl extends \Zotlabs\Web\Controller { . "' IN xchan_name) else position('" . protect_sprintf(dbesc(punify($search))) . "' IN xchan_addr) end, "; $col = ((strpos($search,'@') !== false) ? 'xchan_addr' : 'xchan_name' ); - $sql_extra3 = "AND $col like " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " "; + $sql_extra3 = "AND $col like " . protect_sprintf( "'%" . dbesc(($col === 'xchan_addr') ? punify($search) : $search) . "%'" ) . " "; } else { diff --git a/Zotlabs/Module/Authorize.php b/Zotlabs/Module/Authorize.php index 254700b4e..bfb76150f 100644 --- a/Zotlabs/Module/Authorize.php +++ b/Zotlabs/Module/Authorize.php @@ -4,60 +4,89 @@ namespace Zotlabs\Module; use Zotlabs\Identity\OAuth2Storage; - class Authorize extends \Zotlabs\Web\Controller { - function init() { - - // workaround for HTTP-auth in CGI mode - if (x($_SERVER, 'REDIRECT_REMOTE_USER')) { - $userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6)) ; - if(strlen($userpass)) { - list($name, $password) = explode(':', $userpass); - $_SERVER['PHP_AUTH_USER'] = $name; - $_SERVER['PHP_AUTH_PW'] = $password; - } + function get() { + if (!local_channel()) { + return login(); + } else { + // TODO: Fully implement the dynamic client registration protocol: + // OpenID Connect Dynamic Client Registration 1.0 Client Metadata + // http://openid.net/specs/openid-connect-registration-1_0.html + $app = array( + 'name' => (x($_REQUEST, 'client_name') ? urldecode($_REQUEST['client_name']) : t('Unknown App')), + 'icon' => (x($_REQUEST, 'logo_uri') ? urldecode($_REQUEST['logo_uri']) : z_root() . '/images/icons/plugin.png'), + 'url' => (x($_REQUEST, 'client_uri') ? urldecode($_REQUEST['client_uri']) : ''), + ); + $o .= replace_macros(get_markup_template('oauth_authorize.tpl'), array( + '$title' => t('Authorize'), + '$authorize' => sprintf( t('Do you authorize the app %s to access your channel data?'), '<a style="float: none;" href="' . $app['url'] . '">' . $app['name'] . '</a> '), + '$app' => $app, + '$yes' => t('Allow'), + '$no' => t('Deny'), + '$client_id' => (x($_REQUEST, 'client_id') ? $_REQUEST['client_id'] : ''), + '$redirect_uri' => (x($_REQUEST, 'redirect_uri') ? $_REQUEST['redirect_uri'] : ''), + '$state' => (x($_REQUEST, 'state') ? $_REQUEST['state'] : ''), + )); + return $o; } + } - if (x($_SERVER, 'HTTP_AUTHORIZATION')) { - $userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6)) ; - if(strlen($userpass)) { - list($name, $password) = explode(':', $userpass); - $_SERVER['PHP_AUTH_USER'] = $name; - $_SERVER['PHP_AUTH_PW'] = $password; - } + function post() { + if (! local_channel()) { + return; } - $s = new \Zotlabs\Identity\OAuth2Server(new OAuth2Storage(\DBA::$dba->db)); + $storage = new OAuth2Storage(\DBA::$dba->db); + $s = new \Zotlabs\Identity\OAuth2Server($storage); + + // TODO: The automatic client registration protocol below should adhere more + // closely to "OAuth 2.0 Dynamic Client Registration Protocol" defined + // at https://tools.ietf.org/html/rfc7591 + + // If no client_id was provided, generate a new one. + if (x($_POST, 'client_id')) { + $client_id = $_POST['client_id']; + } else { + $client_id = $_POST['client_id'] = random_string(16); + } + // If no redirect_uri was provided, generate a fake one. + if (x($_POST, 'redirect_uri')) { + $redirect_uri = $_POST['redirect_uri']; + } else { + $redirect_uri = $_POST['redirect_uri'] = 'https://fake.example.com/oauth'; + } $request = \OAuth2\Request::createFromGlobals(); $response = new \OAuth2\Response(); - // validate the authorize request - if (! $s->validateAuthorizeRequest($request, $response)) { + // If the client is not registered, add to the database + if (!$client = $storage->getClientDetails($client_id)) { + $client_secret = random_string(16); + // Client apps are registered per channel + $user_id = local_channel(); + $storage->setClientDetails($client_id, $client_secret, $redirect_uri, 'authorization_code', null, $user_id); + + } + if (!$client = $storage->getClientDetails($client_id)) { + // There was an error registering the client. $response->send(); killme(); } + $response->setParameter('client_secret', $client['client_secret']); - // display an authorization form - if (empty($_POST)) { - - return ' -<form method="post"> - <label>Do You Authorize TestClient?</label><br /> - <input type="submit" name="authorized" value="yes"> - <input type="submit" name="authorized" value="no"> -</form>'; + // validate the authorize request + if (!$s->validateAuthorizeRequest($request, $response)) { + $response->send(); + killme(); } // print the authorization code if the user has authorized your client - $is_authorized = ($_POST['authorized'] === 'yes'); + $is_authorized = ($_POST['authorize'] === 'allow'); $s->handleAuthorizeRequest($request, $response, $is_authorized, local_channel()); if ($is_authorized) { - // this is only here so that you get to see your code in the cURL request. Otherwise, - // we'd redirect back to the client - $code = substr($response->getHttpHeader('Location'), strpos($response->getHttpHeader('Location'), 'code=')+5, 40); - echo("SUCCESS! Authorization Code: $code"); + $code = substr($response->getHttpHeader('Location'), strpos($response->getHttpHeader('Location'), 'code=') + 5, 40); + logger('Authorization Code: ' . $code); } $response->send(); diff --git a/Zotlabs/Module/Chatsvc.php b/Zotlabs/Module/Chatsvc.php index 0f79e3b4c..b4657e84d 100644 --- a/Zotlabs/Module/Chatsvc.php +++ b/Zotlabs/Module/Chatsvc.php @@ -60,7 +60,7 @@ class Chatsvc extends \Zotlabs\Web\Controller { intval(\App::$data['chat']['room_id']), dbesc(get_observer_hash()), dbesc(datetime_convert()), - dbesc($arr['chat_text']) + dbesc(str_rot47(base64url_encode($arr['chat_text']))) ); $ret['success'] = true; @@ -157,7 +157,7 @@ class Chatsvc extends \Zotlabs\Web\Controller { 'name' => $rr['xchan_name'], 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $rr['created'], 'c'), 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $rr['created'], 'r'), - 'text' => zidify_links(smilies(bbcode($rr['chat_text']))), + 'text' => zidify_links(smilies(bbcode(base64url_decode(str_rot47($rr['chat_text']))))), 'self' => ((get_observer_hash() == $rr['chat_xchan']) ? 'self' : '') ); } diff --git a/Zotlabs/Module/Connedit.php b/Zotlabs/Module/Connedit.php index f359175c1..cb9c19cf0 100644 --- a/Zotlabs/Module/Connedit.php +++ b/Zotlabs/Module/Connedit.php @@ -828,7 +828,7 @@ class Connedit extends \Zotlabs\Web\Controller { $locstr = locations_by_netid($contact['xchan_hash']); if(! $locstr) - $locstr = $contact['xchan_url']; + $locstr = unpunify($contact['xchan_url']); $clone_warn = ''; $clonable = (in_array($contact['xchan_network'],['zot','rss']) ? true : false); @@ -852,8 +852,8 @@ class Connedit extends \Zotlabs\Web\Controller { '$permcat' => [ 'permcat', t('Permission role'), '', '<span class="loading invisible">' . t('Loading') . '<span class="jumping-dots"><span class="dot-1">.</span><span class="dot-2">.</span><span class="dot-3">.</span></span></span>',$permcats ], '$permcat_new' => t('Add permission role'), '$permcat_enable' => feature_enabled(local_channel(),'permcats'), - '$addr' => $contact['xchan_addr'], - '$primeurl' => $contact['xchan_url'], + '$addr' => unpunify($contact['xchan_addr']), + '$primeurl' => unpunify($contact['xchan_url']), '$section' => $section, '$sections' => $sections, '$vcard' => $vcard, diff --git a/Zotlabs/Module/Directory.php b/Zotlabs/Module/Directory.php index 62a1670f9..87387ef56 100644 --- a/Zotlabs/Module/Directory.php +++ b/Zotlabs/Module/Directory.php @@ -299,9 +299,9 @@ class Directory extends \Zotlabs\Web\Controller { if(strlen($out)) $out .= ', '; if($marr && in_arrayi($k,$marr)) - $out .= '<strong>' . $k . '</strong>'; + $out .= '<a href="' . z_root() . '/directory/f=&keywords=' . urlencode($k) .'"><strong>' . $k . '</strong></a>'; else - $out .= $k; + $out .= '<a href="' . z_root() . '/directory/f=&keywords=' . urlencode($k) .'">' . $k . '</a>'; } } diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php index d3047bc59..30f2a7f5f 100644 --- a/Zotlabs/Module/Display.php +++ b/Zotlabs/Module/Display.php @@ -114,7 +114,8 @@ class Display extends \Zotlabs\Web\Controller { dbesc($target_item['author_xchan']) ); if($x) { - \App::$poi = $x[0]; +// not yet ready for prime time +// \App::$poi = $x[0]; } //if the item is to be moderated redirect to /moderate diff --git a/Zotlabs/Module/Hashtags.php b/Zotlabs/Module/Hashtags.php index edb631871..300485196 100644 --- a/Zotlabs/Module/Hashtags.php +++ b/Zotlabs/Module/Hashtags.php @@ -18,7 +18,7 @@ class Hashtags extends \Zotlabs\Web\Controller { ); if($r) { foreach($r as $rv) { - $result[] = [ 'text' => strtolower($rv['term']) ]; + $result[] = [ 'text' => $rv['term'] ]; } } diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index fba2ef7a4..ad72d9ccd 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -542,7 +542,7 @@ class Item extends \Zotlabs\Web\Controller { // Look for tags and linkify them $results = linkify_tags($a, $body, ($uid) ? $uid : $profile_uid); - + if($results) { // Set permissions based on tag replacements diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index 6d9fde17c..12de86e72 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -296,10 +296,11 @@ class Like extends \Zotlabs\Web\Controller { notice( t('Permission denied') . EOL); killme(); } - + $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($item['owner_xchan']) ); + if($r) $thread_owner = $r[0]; else diff --git a/Zotlabs/Module/Mail.php b/Zotlabs/Module/Mail.php index b58b169d0..ca183f644 100644 --- a/Zotlabs/Module/Mail.php +++ b/Zotlabs/Module/Mail.php @@ -67,14 +67,14 @@ class Mail extends \Zotlabs\Web\Controller { if(! $recipient) { $channel = \App::get_channel(); - $j = \Zotlabs\Zot\Finger::run($rstr,$channel); + $j = \Zotlabs\Zot\Finger::run(punify($rstr),$channel); if(! $j['success']) { notice( t('Unable to lookup recipient.') . EOL); return; } - logger('message_post: lookup: ' . $url . ' ' . print_r($j,true)); + logger('message_post: lookup: ' . $rstr . ' ' . print_r($j,true)); if(! $j['guid']) { notice( t('Unable to communicate with requested channel.')); diff --git a/Zotlabs/Module/Oauth2testvehicle.php b/Zotlabs/Module/Oauth2testvehicle.php new file mode 100644 index 000000000..5ae278e8c --- /dev/null +++ b/Zotlabs/Module/Oauth2testvehicle.php @@ -0,0 +1,151 @@ +<?php + +namespace Zotlabs\Module; + +/** + * The OAuth2TestVehicle class is a way to test the registration of an OAuth2 + * client app. It allows you to walk through the steps of registering a client, + * requesting an authorization code for that client, and then requesting an + * access token for use in authentication against the Hubzilla API endpoints. + */ +class OAuth2TestVehicle extends \Zotlabs\Web\Controller { + + function init() { + + killme(); + + // If there is a 'code' and 'state' parameter then this is a client app + // callback issued after the authorization code request + // TODO: Check state value and compare to original sent value + // "You should first compare this state value to ensure it matches the + // one you started with. You can typically store the state value in a + // cookie, and compare it when the user comes back. This ensures your + // redirection endpoint isn't able to be tricked into attempting to + // exchange arbitrary authorization codes." + $_SESSION['redirect_uri'] = z_root() . '/oauth2testvehicle'; + $_SESSION['authorization_code'] = (x($_REQUEST, 'code') ? $_REQUEST['code'] : $_SESSION['authorization_code']); + $_SESSION['state'] = (x($_REQUEST, 'state') ? $_REQUEST['state'] : $_SESSION['state'] ); + $_SESSION['client_id'] = (x($_REQUEST, 'client_id') ? $_REQUEST['client_id'] : $_SESSION['client_id'] ); + $_SESSION['client_secret'] = (x($_REQUEST, 'client_secret') ? $_REQUEST['client_secret'] : $_SESSION['client_secret']); + $_SESSION['access_token'] = (x($_REQUEST, 'access_token') ? $_REQUEST['access_token'] : $_SESSION['access_token'] ); + $_SESSION['api_response'] = (x($_SESSION, 'api_response') ? $_SESSION['api_response'] : ''); + } + function get() { + + $o .= replace_macros(get_markup_template('oauth2testvehicle.tpl'), array( + '$baseurl' => z_root(), + '$api_response' => $_SESSION['api_response'], + /* + endpoints => array( + array( + 'path_to_endpoint', + array( + array('field_name_1', 'value'), + array('field_name_2', 'value'), + ... + ), + 'submit_button_name', + 'Description of API action' + ) + ) + */ + '$endpoints' => array( + array( + 'authorize', + array( + array('response_type', 'code'), + array('client_id', (x($_REQUEST, 'client_id') ? $_REQUEST['client_id'] : 'oauth2_test_app')), + array('redirect_uri', $_SESSION['redirect_uri']), + array('state', 'xyz'), + // OpenID Connect Dynamic Client Registration 1.0 Client Metadata + // http://openid.net/specs/openid-connect-registration-1_0.html + array('client_name', 'OAuth2 Test App'), + array('logo_uri', urlencode(z_root() . '/images/icons/plugin.png')), + array('client_uri', urlencode('https://client.example.com/website')), + array('application_type', 'web'), // would be 'native' for mobile app + ), + 'oauth_authorize', + 'Authorize a test client app', + 'GET', + (($_REQUEST['code'] && $_REQUEST['state']) ? true : false), + ), + array( + 'oauth2testvehicle', + array( + array('action', 'request_token'), + array('grant_type', 'authorization_code'), + array('code', $_SESSION['authorization_code']), + array('redirect_uri', $_SESSION['redirect_uri']), + array('client_id', ($_SESSION['client_id'] ? $_SESSION['client_id'] : 'oauth2_test_app')), + array('client_secret', $_SESSION['client_secret']), + ), + 'oauth_token_request', + 'Request a token', + 'POST', + ($_SESSION['success'] === 'request_token'), + ), + array( + 'oauth2testvehicle', + array( + array('action', 'api_files'), + array('access_token', $_SESSION['access_token']), + ), + 'oauth_api_files', + 'API: Get channel files', + 'POST', + ($_SESSION['success'] === 'api_files'), + ) + ) + )); + $_SESSION['success'] = ''; + return $o; + } + + function post() { + + switch ($_POST['action']) { + case 'api_files': + $access_token = $_SESSION['access_token']; + $url = z_root() . '/api/z/1.0/files/'; + $headers = []; + $headers[] = 'Authorization: Bearer ' . $access_token; + $post = z_fetch_url($url, false, 0, array( + 'custom' => 'GET', + 'headers' => $headers, + )); + logger(json_encode($post, JSON_PRETTY_PRINT), LOGGER_DEBUG); + $response = json_decode($post['body'], true); + $_SESSION['api_response'] = json_encode($response, JSON_PRETTY_PRINT); + break; + case 'request_token': + $grant_type = (x($_POST, 'grant_type') ? $_POST['grant_type'] : ''); + $redirect_uri = (x($_POST, 'redirect_uri') ? $_POST['redirect_uri'] : ''); + $client_id = (x($_POST, 'client_id') ? $_POST['client_id'] : ''); + $code = (x($_POST, 'code') ? $_POST['code'] : ''); + $client_secret = (x($_POST, 'client_secret') ? $_POST['client_secret'] : ''); + $url = z_root() . '/token/'; + $params = http_build_query(array( + 'grant_type' => $grant_type, + 'redirect_uri' => urlencode($redirect_uri), + 'client_id' => $client_id, + 'code' => $code, + )); + $post = z_post_url($url, $params, 0, array( + 'http_auth' => $client_id . ':' . $client_secret, + )); + logger(json_encode($post, JSON_PRETTY_PRINT), LOGGER_DEBUG); + $response = json_decode($post['body'], true); + logger(json_encode($response, JSON_PRETTY_PRINT), LOGGER_DEBUG); + if($response['access_token']) { + info('Access token received: ' . $response['access_token'] . EOL); + $_SESSION['success'] = 'request_token'; + $_SESSION['access_token'] = $response['access_token']; + } + break; + + default: + break; + } + } + +} diff --git a/Zotlabs/Module/Settings/Channel.php b/Zotlabs/Module/Settings/Channel.php index 139e5f966..a7dfdd790 100644 --- a/Zotlabs/Module/Settings/Channel.php +++ b/Zotlabs/Module/Settings/Channel.php @@ -412,12 +412,16 @@ class Channel { )); $subdir = ((strlen(\App::get_path())) ? '<br />' . t('or') . ' ' . z_root() . '/channel/' . $nickname : ''); + + $webbie = $nickname . '@' . \App::get_hostname(); + $intl_nickname = unpunify($nickname) . '@' . unpunify(\App::get_hostname()); + $tpl_addr = get_markup_template("settings_nick_set.tpl"); $prof_addr = replace_macros($tpl_addr,array( '$desc' => t('Your channel address is'), - '$nickname' => $nickname, + '$nickname' => (($intl_nickname === $webbie) ? $webbie : $intl_nickname . ' (' . $webbie . ')'), '$subdir' => $subdir, '$davdesc' => t('Your files/photos are accessible via WebDAV at'), '$davpath' => ((get_account_techlevel() > 3) ? z_root() . '/dav/' . $nickname : ''), diff --git a/Zotlabs/Module/Tagger.php b/Zotlabs/Module/Tagger.php index 603a95f2b..24adf1bde 100644 --- a/Zotlabs/Module/Tagger.php +++ b/Zotlabs/Module/Tagger.php @@ -80,6 +80,8 @@ class Tagger extends \Zotlabs\Web\Controller { break; } + + $clean_term = trim($term,'"\' '); $links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => z_root() . '/display/' . gen_link_id($item['mid']))); @@ -103,15 +105,15 @@ class Tagger extends \Zotlabs\Web\Controller { ), )); - $tagid = z_root() . '/search?tag=' . $term; + $tagid = z_root() . '/search?tag=' . $clean_term; $objtype = ACTIVITY_OBJ_TAGTERM; $obj = json_encode(array( 'type' => $objtype, 'id' => $tagid, 'link' => array(array('rel' => 'alternate','type' => 'text/html', 'href' => $tagid)), - 'title' => $term, - 'content' => $term + 'title' => $clean_term, + 'content' => $clean_term )); $bodyverb = t('%1$s tagged %2$s\'s %3$s with %4$s'); @@ -119,7 +121,7 @@ class Tagger extends \Zotlabs\Web\Controller { // saving here for reference // also check out x22d5 and x2317 and x0d6b and x0db8 and x24d0 and xff20 !!! - $termlink = html_entity_decode('⋕') . '[zrl=' . z_root() . '/search?tag=' . urlencode($term) . ']'. $term . '[/zrl]'; + $termlink = html_entity_decode('⋕') . '[zrl=' . z_root() . '/search?tag=' . urlencode($clean_term) . ']'. $clean_term . '[/zrl]'; $channel = \App::get_channel(); @@ -143,8 +145,7 @@ class Tagger extends \Zotlabs\Web\Controller { $arr['obj_type'] = $objtype; $arr['obj'] = $obj; $arr['parent_mid'] = $item['mid']; - - store_item_tag($item['uid'],$item['id'],TERM_OBJ_POST,TERM_COMMUNITYTAG,$term,$tagid); + store_item_tag($item['uid'],$item['id'],TERM_OBJ_POST,TERM_COMMUNITYTAG,$clean_term,$tagid); $ret = post_activity_item($arr); if($ret['success']) { diff --git a/Zotlabs/Module/Token.php b/Zotlabs/Module/Token.php index f7c074233..32cf95c61 100644 --- a/Zotlabs/Module/Token.php +++ b/Zotlabs/Module/Token.php @@ -29,7 +29,8 @@ class Token extends \Zotlabs\Web\Controller { } $s = new \Zotlabs\Identity\OAuth2Server(new OAuth2Storage(\DBA::$dba->db)); - $s->handleTokenRequest(\OAuth2\Request::createFromGlobals())->send(); + $request = \OAuth2\Request::createFromGlobals(); + $s->handleTokenRequest($request)->send(); killme(); } diff --git a/Zotlabs/Module/Wiki.php b/Zotlabs/Module/Wiki.php index ae543eb98..7dc8eb1bc 100644 --- a/Zotlabs/Module/Wiki.php +++ b/Zotlabs/Module/Wiki.php @@ -284,6 +284,8 @@ class Wiki extends \Zotlabs\Web\Controller { $wikiheaderPage = urldecode($pageUrlName); $renamePage = (($wikiheaderPage === 'Home') ? '' : t('Rename page')); + $sharePage = t('Share'); + $p = []; if(! $ignore_language) { @@ -354,6 +356,8 @@ class Wiki extends \Zotlabs\Web\Controller { '$wikiheaderName' => $wikiheaderName, '$wikiheaderPage' => $wikiheaderPage, '$renamePage' => $renamePage, + '$sharePage' => $sharePage, + '$shareLink' => urlencode('#^[zrl=' . z_root() . '/wiki/' . argv(1) . '/' . $wikiUrlName . '/' . $pageUrlName . ']' . '[ ' . $owner['channel_name'] . ' ] ' . $wikiheaderName . ' - ' . $wikiheaderPage . '[/zrl]'), '$showPageControls' => $showPageControls, '$editOrSourceLabel' => (($showPageControls) ? t('Edit') : t('Source')), '$tools_label' => 'Page Tools', diff --git a/Zotlabs/Storage/ZotOauth2Pdo.php b/Zotlabs/Storage/ZotOauth2Pdo.php new file mode 100644 index 000000000..b2c3ce228 --- /dev/null +++ b/Zotlabs/Storage/ZotOauth2Pdo.php @@ -0,0 +1,10 @@ +<?php + +namespace Zotlabs\Storage; + +class ZotOauth2Pdo extends \OAuth2\Storage\Pdo { + public function getConfig() + { + return $this->config; + } +} diff --git a/Zotlabs/Update/_1210.php b/Zotlabs/Update/_1210.php new file mode 100644 index 000000000..813e3fe82 --- /dev/null +++ b/Zotlabs/Update/_1210.php @@ -0,0 +1,78 @@ +<?php + +namespace Zotlabs\Update; + +class _1210 { + + function run() { + + $sql = "CREATE TABLE oauth_clients ( + client_id VARCHAR(80) NOT NULL, + client_secret VARCHAR(80), + redirect_uri VARCHAR(2000), + grant_types VARCHAR(80), + scope VARCHAR(4000), + user_id VARCHAR(80), + PRIMARY KEY (client_id) +); + +CREATE TABLE oauth_access_tokens ( + access_token VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(255), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + PRIMARY KEY (access_token) +); + +CREATE TABLE oauth_authorization_codes ( + authorization_code VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(255), + redirect_uri VARCHAR(2000), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + id_token VARCHAR(1000), + PRIMARY KEY (authorization_code) +); + +CREATE TABLE oauth_refresh_tokens ( + refresh_token VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(255), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + PRIMARY KEY (refresh_token) +); + +CREATE TABLE oauth_scopes ( + scope VARCHAR(191) NOT NULL, + is_default SMALLINT, + PRIMARY KEY (scope) +); + +CREATE TABLE oauth_jwt ( + client_id VARCHAR(80) NOT NULL, + subject VARCHAR(80), + public_key VARCHAR(2000) NOT NULL +); +"; + + $arr = explode(';', $sql); + $errors = 0; + foreach($arr as $a) { + if(strlen(trim($a))) { + $r = dbq(trim($a)); + if(! $r) { + $errors ++; + } + } + } + + if(! $errors) + return UPDATE_SUCCESS; + return UPDATE_FAILED; + + } + +} @@ -54,7 +54,7 @@ define ( 'STD_VERSION', '3.3.3' ); define ( 'ZOT_REVISION', '6.0a' ); -define ( 'DB_UPDATE_VERSION', 1209 ); +define ( 'DB_UPDATE_VERSION', 1210 ); define ( 'PROJECT_BASE', __DIR__ ); @@ -954,7 +954,10 @@ class App { && is_array(self::$config['system']) && array_key_exists('baseurl',self::$config['system']) && strlen(self::$config['system']['baseurl'])) { - $url = punify(self::$config['system']['baseurl']); + // get_baseurl() is a heavily used function. + // Do not use punify() here until we find a library that performs better than what we have now. + //$url = punify(self::$config['system']['baseurl']); + $url = self::$config['system']['baseurl']; $url = trim($url,'\\/'); return $url; } diff --git a/doc/feature/additional/access.md b/doc/feature/additional/access.md new file mode 100644 index 000000000..3581ca11d --- /dev/null +++ b/doc/feature/additional/access.md @@ -0,0 +1,41 @@ +## Access Control and Permissions + + +### Privacy Groups + +Enable management and selection of privacy groups. +<!-- TODO: full description for Privacy Groups --> + +Minimum required technical skill level to see this feature: 0 + + +### Multiple Profiles + +Ability to create multiple profiles. +<!-- TODO: full description for Multiple Profiles --> + +Minimum required technical skill level to see this feature: 3 + + +### Permission Groups + +Provide alternate connection permission roles. +<!-- TODO: full description for Permission Groups --> + +Minimum required technical skill level to see this feature: 2 + + +### OAuth Clients + +Manage authenticatication tokens for mobile and remote apps. +<!-- TODO: full description for OAuth Clients --> + +Minimum required technical skill level to see this feature: 1 + + +### Access Tokens + +Create access tokens so that non-members can access private content. +<!-- TODO: full description for Access Tokens --> + +Minimum required technical skill level to see this feature: 2 diff --git a/doc/feature/additional/composition.md b/doc/feature/additional/composition.md new file mode 100644 index 000000000..1256f7501 --- /dev/null +++ b/doc/feature/additional/composition.md @@ -0,0 +1,67 @@ +## Post Composition Features + + +### Large Photos + +Include large (1024px) photo thumbnails in posts. +If not enabled, use small (640px) photo thumbnails +<!-- TODO: full description for Large Photos --> + +Minimum required technical skill level to see this feature: 1 + + +### Channel Sources + +Automatically import channel content from other channels or feeds +<!-- TODO: full description for Channel Sources --> + +Minimum required technical skill level to see this feature: 3 + + +### Even More Encryption + +Allow optional encryption of content end-to-end with a shared secret key +<!-- TODO: full description for Even More Encryption --> + +Minimum required technical skill level to see this feature: 3 + + +### Enable Voting Tools + +Provide a class of post which others can vote on +<!-- TODO: full description for Enable Voting Tools --> + +Minimum required technical skill level to see this feature: 3 + + +### Disable Comments + +Provide the option to disable comments for a post +<!-- TODO: full description for Disable Comments --> + +Minimum required technical skill level to see this feature: 2 + + +### Delayed Posting + +Allow posts to be published at a later date +<!-- TODO: full description for Delayed Posting --> + +Minimum required technical skill level to see this feature: 2 + + +### Content Expiration + +Remove posts/comments and/or private messages at a future time +<!-- TODO: full description for Content Expiration --> + +Minimum required technical skill level to see this feature: 1 + + +### Suppress Duplicate Posts/Comments + +Prevent posts with identical content to be published +with less than two minutes in between submissions. +<!-- TODO: full description for Suppress Duplicate Posts/Comments --> + +Minimum required technical skill level to see this feature: 1 diff --git a/doc/feature/additional/filtering.md b/doc/feature/additional/filtering.md new file mode 100644 index 000000000..ba8e1e29f --- /dev/null +++ b/doc/feature/additional/filtering.md @@ -0,0 +1,57 @@ +## Network and Stream Filtering + + +### Search by Date + +Ability to select posts by date ranges +<!-- TODO: full description for Search by Date --> + +Minimum required technical skill level to see this feature: 1 + + +### Saved Searches + +Save search terms for re-use +<!-- TODO: full description for Saved Searches --> + +Minimum required technical skill level to see this feature: 2 + + +### Network Personal Tab + +Enable tab to display only Network posts that you've interacted on +<!-- TODO: full description for Network Personal Tab --> + +Minimum required technical skill level to see this feature: 1 + + +### Network New Tab + +Enable tab to display all new Network activity +<!-- TODO: full description for Network New Tab --> + +Minimum required technical skill level to see this feature: 2 + + +### Affinity Tool + +Filter stream activity by depth of relationships +<!-- TODO: full description for Affinity Tool --> + +Minimum required technical skill level to see this feature: 1 + + +### Suggest Channels + +Show friend and connection suggestions +<!-- TODO: full description for Suggest Channels --> + +Minimum required technical skill level to see this feature: 1 + + +### Connection Filtering + +Filter incoming posts from connections based on keywords/content +<!-- TODO: full description for Connection Filtering --> + +Minimum required technical skill level to see this feature: 3 diff --git a/doc/feature/additional/general.md b/doc/feature/additional/general.md new file mode 100644 index 000000000..a1b712b12 --- /dev/null +++ b/doc/feature/additional/general.md @@ -0,0 +1,130 @@ +## General Features + + +### New Member Links + +Display new member quick links menu. +<!-- TODO: full description for New Member Links --> + +Minimum required technical skill level to see this feature: 1 + + +### Advanced Profiles + +Additional profile sections and selections +<!-- TODO: full description for Advanced Profiles --> + +Minimum required technical skill level to see this feature: 1 + + +### Profile Import/Export + +Save and load profile details across sites/channels +<!-- TODO: full description for Profile Import/Export --> + +Minimum required technical skill level to see this feature: 3 + + +### Web Pages + +Provide managed web pages on your channel +<!-- TODO: full description for Web Pages --> + +Minimum required technical skill level to see this feature: 3 + + +### Wiki + +Provide a wiki for your channel +<!-- TODO: full description for Wiki --> + +Minimum required technical skill level to see this feature: 2 + + +### Private Notes + +Enables a tool to store notes and reminders (note: not encrypted) +<!-- TODO: full description for Private Notes --> + +Minimum required technical skill level to see this feature: 1 + + +### Cards + +Create personal planning cards +<!-- TODO: full description for Cards --> + +Minimum required technical skill level to see this feature: 1 + + +### Articles + +Create interactive articles +<!-- TODO: full description for Articles --> + +Minimum required technical skill level to see this feature: 1 + + +### Navigation Channel Select + +Change channels directly from within the navigation dropdown menu +<!-- TODO: full description for Navigation Channel Select --> + +Minimum required technical skill level to see this feature: 3 + + +### Photo Location + +If location data is available on uploaded photos, link this to a map. +<!-- TODO: full description for Photo Location --> + +Minimum required technical skill level to see this feature: 2 + + +### Access Controlled Chatrooms + +Provide chatrooms and chat services with access control. +<!-- TODO: full description for Access Controlled Chatrooms --> + +Minimum required technical skill level to see this feature: 1 + + +### Smart Birthdays + +Make birthday events timezone aware in case your friends are scattered across the planet. +<!-- TODO: full description for Smart Birthdays --> + +Minimum required technical skill level to see this feature: 2 + + +### Event Timezone Selection + +Allow event creation in timezones other than your own. +<!-- TODO: full description for Event Timezone Selection --> + +Minimum required technical skill level to see this feature: 2 + + +### Premium Channel + +Allows you to set restrictions and terms +on those that connect with your channel +<!-- TODO: full description for Premium Channel --> + +Minimum required technical skill level to see this feature: 4 + + +### Advanced Directory Search + +Allows creation of complex directory search queries +<!-- TODO: full description for Advanced Directory Search --> + +Minimum required technical skill level to see this feature: 4 + + +### Advanced Theme and Layout Settings + +Allows fine tuning of themes and page layouts +<!-- TODO: full description for Advanced Theme and Layout Settings --> + +Minimum required technical skill level to see this feature: 4 diff --git a/doc/feature/additional/overview.md b/doc/feature/additional/overview.md new file mode 100644 index 000000000..b51f60bcd --- /dev/null +++ b/doc/feature/additional/overview.md @@ -0,0 +1,33 @@ +[chset]: /settings "Channel Settings" +[ftset]: /settings/features "Additional Features Settings" +[ftgen]: /help/feature/additional/general "General Features" +[ftacc]: /help/feature/additional/access "Access Control and Permissions" +[ftcom]: /help/feature/additional/composition "Post Composition Features" +[ftfil]: /help/feature/additional/filtering "Network and Stream Filtering" +[ftpos]: /help/feature/additional/posts "Post/Comment Tools" + + +# Additional Features + +<!-- TODO: Introduction to additional features --> + +<!-- TODO: Short info and crosslink on techlevels --> + +You can switch the features on and off from the +[Additional Features][ftset] link in the [Channel Settings][chset]. + +<!-- TODO: Infos about feature visibility and causes/dependencies --> + +The following pages decribe all the available features +grouped in the same way as they are with the accordion tabs on the +[Additional Features][ftset] settings page: + +[General Features][ftgen] + +[Access Control and Permissions][ftacc] + +[Post Composition Features][ftcom] + +[Network and Stream Filtering][ftfil] + +[Post/Comment Tools][ftpos] diff --git a/doc/feature/additional/posts.md b/doc/feature/additional/posts.md new file mode 100644 index 000000000..d3f6b37db --- /dev/null +++ b/doc/feature/additional/posts.md @@ -0,0 +1,57 @@ +## Post/Comment Tools + + +### Community Tagging + +Ability to tag existing posts +<!-- TODO: full description for Community Tagging --> + +Minimum required technical skill level to see this feature: 1 + + +### Post Categories + +Add categories to your posts +<!-- TODO: full description for Post Categories --> + +Minimum required technical skill level to see this feature: 1 + + +### Emoji Reactions + +Add emoji reaction ability to posts +<!-- TODO: full description for Emoji Reactions --> + +Minimum required technical skill level to see this feature: 1 + + +### Saved Folders + +Ability to file posts under folders +<!-- TODO: full description for Saved Folders --> + +Minimum required technical skill level to see this feature: 2 + + +### Dislike Posts + +Ability to dislike posts/comments +<!-- TODO: full description for Dislike Posts --> + +Minimum required technical skill level to see this feature: 1 + + +### Star Posts + +Ability to mark special posts with a star indicator +<!-- TODO: full description for Star Posts --> + +Minimum required technical skill level to see this feature: 1 + + +### Tag Cloud + +Provide a personal tag cloud on your channel page +<!-- TODO: full description for Tag Cloud --> + +Minimum required technical skill level to see this feature: 2 diff --git a/doc/toc.html b/doc/toc.html index f6ade61c7..9c3d22ab8 100644 --- a/doc/toc.html +++ b/doc/toc.html @@ -21,7 +21,8 @@ <div class="flex-column"> <a class="nav-link" href="/help/member/member_guide">Guide</a> <a class="nav-link" href="/help/member/bbcode">BBcode Reference</a> - <a class="nav-link" href="/help/bugs">Reporting Bugs</a> + <a class="nav-link" href="/help/feature/additional/overview">Additional Features</a> + <a class="nav-link" href="/help/bugs">Reporting Bugs</a> <a class="nav-link" href="/help/member/member_faq">FAQ</a> </div> </div> diff --git a/include/api.php b/include/api.php index c91590070..6a05a40a5 100644 --- a/include/api.php +++ b/include/api.php @@ -193,26 +193,18 @@ require_once('include/api_zot.php'); $redirect = trim($_REQUEST['redirect_uris'][0]); else $redirect = trim($_REQUEST['redirect_uris']); + $grant_types = trim($_REQUEST['grant_types']); + $scope = trim($_REQUEST['scope']); $icon = trim($_REQUEST['logo_uri']); - if($oauth2) { - $r = q("INSERT INTO oauth_clients (client_id, client_secret, redirect_uri, grant_types, scope, user_id) - VALUES ( '%s', '%s', '%s', null, null, null ) ", - dbesc($key), - dbesc($secret), - dbesc($redirect) - ); - } - else { - $r = q("INSERT INTO clients (client_id, pw, clname, redirect_uri, icon, uid) - VALUES ('%s','%s','%s','%s','%s',%d)", - dbesc($key), - dbesc($secret), - dbesc($name), - dbesc($redirect), - dbesc($icon), - intval(0) - ); - } + $r = q("INSERT INTO oauth_clients (client_id, client_secret, redirect_uri, grant_types, scope, user_id) + VALUES ( '%s', '%s', '%s', '%s', '%s', '%s' ) ", + dbesc($key), + dbesc($secret), + dbesc($redirect), + dbesc($grant_types), + dbesc($scope), + dbesc((string) api_user()) + ); $ret['client_id'] = $key; $ret['client_secret'] = $secret; diff --git a/include/api_auth.php b/include/api_auth.php index 5c0bcb317..e2f7ab155 100644 --- a/include/api_auth.php +++ b/include/api_auth.php @@ -14,25 +14,58 @@ function api_login(&$a){ // login with oauth try { - $oauth = new ZotOAuth1(); - $req = OAuth1Request::from_request(); + // OAuth 2.0 + $storage = new \Zotlabs\Identity\OAuth2Storage(\DBA::$dba->db); + $server = new \Zotlabs\Identity\OAuth2Server($storage); + $request = \OAuth2\Request::createFromGlobals(); + if ($server->verifyResourceRequest($request)) { + $token = $server->getAccessTokenData($request); + $uid = $token['user_id']; + $r = q("SELECT * FROM channel WHERE channel_id = %d LIMIT 1", + intval($uid) + ); + if (count($r)) { + $record = $r[0]; + } else { + header('HTTP/1.0 401 Unauthorized'); + echo('This api requires login'); + killme(); + } + + $_SESSION['uid'] = $record['channel_id']; + $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; + + $x = q("select * from account where account_id = %d LIMIT 1", + intval($record['channel_account_id']) + ); + if ($x) { + require_once('include/security.php'); + authenticate_success($x[0], null, true, false, true, true); + $_SESSION['allow_api'] = true; + call_hooks('logged_in', App::$user); + return; + } + } else { + // OAuth 1.0 + $oauth = new ZotOAuth1(); + $req = OAuth1Request::from_request(); - list($consumer,$token) = $oauth->verify_request($req); + list($consumer, $token) = $oauth->verify_request($req); - if (!is_null($token)){ - $oauth->loginUser($token->uid); + if (!is_null($token)) { + $oauth->loginUser($token->uid); - App::set_oauth_key($consumer->key); + App::set_oauth_key($consumer->key); - call_hooks('logged_in', App::$user); - return; + call_hooks('logged_in', App::$user); + return; + } + killme(); } - killme(); - } - catch(Exception $e) { + } catch (Exception $e) { logger($e->getMessage()); } - + // workarounds for HTTP-auth in CGI mode foreach([ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $head) { diff --git a/include/bbcode.php b/include/bbcode.php index 67f40dd23..152e4888f 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -780,10 +780,16 @@ function parseIdentityAwareHTML($Text) { function bbcode($Text, $options = []) { + if(! is_array($options)) { + $options = []; + } + $preserve_nl = ((array_key_exists('preserve_nl',$options)) ? $options['preserve_nl'] : false); $tryoembed = ((array_key_exists('tryoembed',$options)) ? $options['tryoembed'] : true); $cache = ((array_key_exists('cache',$options)) ? $options['cache'] : false); + $newwin = ((array_key_exists('newwin',$options)) ? $options['newwin'] : true); + $target = (($newwin) ? ' target="_blank" ' : ''); call_hooks('bbcode_filter', $Text); @@ -934,7 +940,7 @@ function bbcode($Text, $options = []) { if($tryoembed) { $Text = preg_replace_callback("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/$urlchars+)/ismu", 'tryoembed', $Text); } - $Text = preg_replace("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/$urlchars+)/ismu", '$1<a href="$2" target="_blank" rel="nofollow noopener">$2</a>', $Text); + $Text = preg_replace("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/$urlchars+)/ismu", '$1<a href="$2" ' . $target . ' rel="nofollow noopener">$2</a>', $Text); } if (strpos($Text,'[/share]') !== false) { @@ -946,16 +952,16 @@ function bbcode($Text, $options = []) { } } if (strpos($Text,'[/url]') !== false) { - $Text = preg_replace("/\#\^\[url\]([$URLSearchString]*)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); - $Text = preg_replace("/\#\^\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" target="_blank" rel="nofollow noopener" >$2</a>', $Text); - $Text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); - $Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" target="_blank" rel="nofollow noopener" >$2</a>', $Text); + $Text = preg_replace("/\#\^\[url\]([$URLSearchString]*)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\#\^\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $Text); + $Text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $Text); } if (strpos($Text,'[/zrl]') !== false) { - $Text = preg_replace("/\#\^\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); - $Text = preg_replace("/\#\^\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" target="_blank" rel="nofollow noopener" >$2</a>', $Text); - $Text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<a class="zrl" href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); - $Text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a class="zrl" href="$1" target="_blank" rel="nofollow noopener" >$2</a>', $Text); + $Text = preg_replace("/\#\^\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\#\^\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $Text); + $Text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<a class="zrl" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a class="zrl" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $Text); } if (get_account_techlevel() < 2) @@ -963,8 +969,8 @@ function bbcode($Text, $options = []) { // Perform MAIL Search if (strpos($Text,'[/mail]') !== false) { - $Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); - $Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1" target="_blank" rel="nofollow noopener" >$2</a>', $Text); + $Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $Text); } @@ -1286,28 +1292,18 @@ function bbcode($Text, $options = []) { // if video couldn't be embedded, link to it instead. if (strpos($Text,'[/video]') !== false) { - $Text = preg_replace("/\[video\](.*?)\[\/video\]/", '<a href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\[video\](.*?)\[\/video\]/", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); } if (strpos($Text,'[/audio]') !== false) { - $Text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '<a href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); } if (strpos($Text,'[/zvideo]') !== false) { - $Text = preg_replace("/\[zvideo\](.*?)\[\/zvideo\]/", '<a class="zid" href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\[zvideo\](.*?)\[\/zvideo\]/", '<a class="zid" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); } if (strpos($Text,'[/zaudio]') !== false) { - $Text = preg_replace("/\[zaudio\](.*?)\[\/zaudio\]/", '<a class="zid" href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); - } - -// if ($tryoembed){ -// if (strpos($Text,'[/iframe]') !== false) { -// $Text = preg_replace_callback("/\[iframe\](.*?)\[\/iframe\]/ism", 'bb_iframe', $Text); -// } -// } else { -// if (strpos($Text,'[/iframe]') !== false) { -// $Text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<a href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); -// } -// } + $Text = preg_replace("/\[zaudio\](.*?)\[\/zaudio\]/", '<a class="zid" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); + } // oembed tag $Text = oembed_bbcode2html($Text); diff --git a/include/channel.php b/include/channel.php index c94f5c657..4a87ef602 100644 --- a/include/channel.php +++ b/include/channel.php @@ -780,7 +780,7 @@ function identity_basic_export($channel_id, $sections = null) { } } - if(in_array('channel',$sections)) { + if(in_array('channel',$sections) || in_array('profile',$sections)) { $r = q("select * from profile where uid = %d", intval($channel_id) ); @@ -1573,14 +1573,25 @@ function advanced_profile() { $profile['howlong'] = relative_date(App::$profile['howlong'], t('for %1$d %2$s')); } + if(App::$profile['keywords']) { + $keywords = str_replace(',',' ', App::$profile['keywords']); + $keywords = str_replace(' ',' ', $keywords); + $karr = explode(' ', $keywords); + if($karr) { + for($cnt = 0; $cnt < count($karr); $cnt ++) { + $karr[$cnt] = '<a href="' . z_root() . '/directory/f=&keywords=' . trim($karr[$cnt]) . '">' . $karr[$cnt] . '</a>'; + } + } + $profile['keywords'] = array( t('Tags:'), implode(' ', $karr)); + } + + if(App::$profile['sexual']) $profile['sexual'] = array( t('Sexual Preference:'), App::$profile['sexual'] ); if(App::$profile['homepage']) $profile['homepage'] = array( t('Homepage:'), linkify(App::$profile['homepage']) ); if(App::$profile['hometown']) $profile['hometown'] = array( t('Hometown:'), linkify(App::$profile['hometown']) ); - if(App::$profile['keywords']) $profile['keywords'] = array( t('Tags:'), App::$profile['keywords']); - if(App::$profile['politic']) $profile['politic'] = array( t('Political Views:'), App::$profile['politic']); if(App::$profile['religion']) $profile['religion'] = array( t('Religion:'), App::$profile['religion']); diff --git a/include/conversation.php b/include/conversation.php index 64beb1b0e..ce0467770 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1607,7 +1607,7 @@ function prepare_page($item) { // prepare_body calls unobscure() as a side effect. Do it here so that // the template will get passed an unobscured title. - $body = prepare_body($item, true); + $body = prepare_body($item, [ 'newwin' => false ]); if(App::$page['template'] == 'none') { $tpl = 'page_display_empty.tpl'; diff --git a/include/datetime.php b/include/datetime.php index 766c90d16..3a07f1ccf 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -125,10 +125,16 @@ function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d */ function dob($dob) { + $y = substr($dob,0,4); + if((! ctype_digit($y)) || ($y < 1900)) + $ignore_year = true; + else + $ignore_year = false; + if ($dob === '0000-00-00' || $dob === '') $value = ''; else - $value = (($year) ? datetime_convert('UTC','UTC',$dob,'Y-m-d') : datetime_convert('UTC','UTC',$dob,'m-d')); + $value = (($ignore_year) ? datetime_convert('UTC','UTC',$dob,'m-d') : datetime_convert('UTC','UTC',$dob,'Y-m-d')); $o = replace_macros(get_markup_template("field_input.tpl"), [ '$field' => [ 'dob', t('Birthday'), $value, ((intval($value)) ? t('Age: ') . age($value,App::$user['timezone'],App::$user['timezone']) : ''), '', 'placeholder="' . t('YYYY-MM-DD or MM-DD') .'"' ] diff --git a/include/help.php b/include/help.php index 0dc37e517..ce389b4db 100644 --- a/include/help.php +++ b/include/help.php @@ -306,7 +306,7 @@ function store_doc_file($s) { require_once('include/html2plain.php'); - $item['body'] = html2plain(prepare_text(file_get_contents($s),$mimetype, true)); + $item['body'] = html2plain(prepare_text(file_get_contents($s),$mimetype, [ 'cache' => true ])); $item['mimetype'] = 'text/plain'; $item['plink'] = z_root() . '/' . str_replace('doc','help',$s); diff --git a/include/hubloc.php b/include/hubloc.php index 0d1a2e560..33d5dcbb2 100644 --- a/include/hubloc.php +++ b/include/hubloc.php @@ -270,7 +270,8 @@ function locations_by_netid($netid) { dbesc($netid) ); - return array_elm_to_str($locs,'location',', '); + + return array_elm_to_str($locs,'location',', ','trim_and_unpunify'); } diff --git a/include/items.php b/include/items.php index d1625e944..6ddab9bf8 100755 --- a/include/items.php +++ b/include/items.php @@ -2532,43 +2532,7 @@ function tag_deliver($uid, $item_id) { */ if($item['obj_type'] === ACTIVITY_OBJ_TAGTERM) { - - // We received a community tag activity for a post. - // See if we are the owner of the parent item and have given permission to tag our posts. - // If so tag the parent post. - - logger('tag_deliver: community tag activity received'); - - if(($item['owner_xchan'] === $u[0]['channel_hash']) && (! get_pconfig($u[0]['channel_id'],'system','blocktags'))) { - logger('tag_deliver: community tag recipient: ' . $u[0]['channel_name']); - $j_tgt = json_decode($item['target'],true); - if($j_tgt && $j_tgt['id']) { - $p = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc($j_tgt['id']), - intval($u[0]['channel_id']) - ); - if($p) { - $j_obj = json_decode($item['obj'],true); - logger('tag_deliver: tag object: ' . print_r($j_obj,true), LOGGER_DATA); - if($j_obj && $j_obj['id'] && $j_obj['title']) { - if(is_array($j_obj['link'])) - $taglink = get_rel_link($j_obj['link'],'alternate'); - - store_item_tag($u[0]['channel_id'],$p[0]['id'],TERM_OBJ_POST,TERM_COMMUNITYTAG,$j_obj['title'],$j_obj['id']); - $x = q("update item set edited = '%s', received = '%s', changed = '%s' where mid = '%s' and uid = %d", - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc($j_tgt['id']), - intval($u[0]['channel_id']) - ); - Zotlabs\Daemon\Master::Summon(array('Notifier','edit_post',$p[0]['id'])); - } - } - } - } - else - logger('Tag permission denied for ' . $u[0]['channel_address']); + item_community_tag($u[0],$item); } /* @@ -2763,6 +2727,61 @@ function tag_deliver($uid, $item_id) { } + +function item_community_tag($channel,$item) { + + + // We received a community tag activity for a post. + // See if we are the owner of the parent item and have given permission to tag our posts. + // If so tag the parent post. + + logger('tag_deliver: community tag activity received: channel: ' . $channel['channel_name']); + + $tag_the_post = false; + $p = null; + + $j_obj = json_decode($item['obj'],true); + $j_tgt = json_decode($item['target'],true); + if($j_tgt && $j_tgt['id']) { + $p = q("select * from item where mid = '%s' and uid = %d limit 1", + dbesc($j_tgt['id']), + intval($channel['channel_id']) + ); + } + if($p) { + xchan_query($p); + $items = fetch_post_tags($p,true); + $pitem = $items[0]; + $auth = get_iconfig($item,'system','communitytagauth'); + if($auth) { + if(rsa_verify('tagauth.' . $item['mid'],base64url_decode($auth),$pitem['owner']['xchan_pubkey']) || rsa_verify('tagauth.' . $item['mid'],base64url_decode($auth),$pitem['author']['xchan_pubkey'])) { + logger('tag_deliver: tagging the post: ' . $channel['channel_name']); + $tag_the_post = true; + } + } + else { + if(($pitem['owner_xchan'] === $channel['channel_hash']) && (! intval(get_pconfig($channel['channel_id'],'system','blocktags')))) { + logger('tag_deliver: community tag recipient: ' . $channel['channel_name']); + $tag_the_post = true; + $sig = rsa_sign('tagauth.' . $item['mid'],$channel['channel_prvkey']); + logger('tag_deliver: setting iconfig for ' . $item['id']); + set_iconfig($item['id'],'system','communitytagauth',base64url_encode($sig),1); + } + } + + if($tag_the_post) { + store_item_tag($channel['channel_id'],$pitem['id'],TERM_OBJ_POST,TERM_COMMUNITYTAG,$j_obj['title'],$j_obj['id']); + } + else { + logger('Tag permission denied for ' . $channel['channel_address']); + } + } + +} + + + + /** * @brief This function is called pre-deliver to see if a post matches the criteria to be tag delivered. * diff --git a/include/network.php b/include/network.php index f8cb68613..a49e5920d 100644 --- a/include/network.php +++ b/include/network.php @@ -1873,7 +1873,7 @@ function probe_api_path($host) { foreach($paths as $path) { $curpath = $scheme . '://' . $host . $path; $x = z_fetch_url($curpath); - if($x['success'] && ! strlen($x['body'], 'not implemented')) + if($x['success'] && ! strpos($x['body'], 'not implemented')) return str_replace('version', '', $curpath); } } diff --git a/include/statistics_fns.php b/include/statistics_fns.php index d213485bf..98b0efd41 100644 --- a/include/statistics_fns.php +++ b/include/statistics_fns.php @@ -17,23 +17,10 @@ function update_channels_active_halfyear_stat() { db_utcnow(), db_quoteinterval('6 MONTH') ); if($r) { - $s = ''; - foreach($r as $rr) { - if($s) - $s .= ','; - $s .= intval($rr['channel_id']); - } - $x = q("select uid from item where uid in ( $s ) and item_wall = 1 and created > %s - INTERVAL %s group by uid", - db_utcnow(), db_quoteinterval('6 MONTH') - ); - if($x) { - $channels_active_halfyear_stat = count($x); - set_config('system','channels_active_halfyear_stat',$channels_active_halfyear_stat); - } else { - set_config('system','channels_active_halfyear_stat',0); - } - } else { - set_config('system','channels_active_halfyear_stat',0); + set_config('system','channels_active_halfyear_stat',count($r)); + } + else { + set_config('system','channels_active_halfyear_stat','0'); } } @@ -43,28 +30,15 @@ function update_channels_active_monthly_stat() { db_utcnow(), db_quoteinterval('1 MONTH') ); if($r) { - $s = ''; - foreach($r as $rr) { - if($s) - $s .= ','; - $s .= intval($rr['channel_id']); - } - $x = q("select uid from item where uid in ( $s ) and item_wall = 1 and created > %s - INTERVAL %s group by uid", - db_utcnow(), db_quoteinterval('1 MONTH') - ); - if($x) { - $channels_active_monthly_stat = count($x); - set_config('system','channels_active_monthly_stat',$channels_active_monthly_stat); - } else { - set_config('system','channels_active_monthly_stat',0); - } - } else { - set_config('system','channels_active_monthly_stat',0); + set_config('system','channels_active_monthly_stat',count($r)); + } + else { + set_config('system','channels_active_monthly_stat','0'); } } function update_local_posts_stat() { - $posts = q("SELECT COUNT(*) AS local_posts FROM item WHERE item_wall = 1 "); + $posts = q("SELECT COUNT(*) AS local_posts FROM item WHERE item_wall = 1 and id = parent"); if (is_array($posts)) { $local_posts_stat = intval($posts[0]["local_posts"]); set_config('system','local_posts_stat',$local_posts_stat); diff --git a/include/text.php b/include/text.php index f9cefd020..255d02c7c 100644 --- a/include/text.php +++ b/include/text.php @@ -1588,7 +1588,7 @@ function generate_named_map($location) { } -function prepare_body(&$item,$attach = false) { +function prepare_body(&$item,$attach = false,$opts = false) { call_hooks('prepare_body_init', $item); @@ -1616,7 +1616,7 @@ function prepare_body(&$item,$attach = false) { $s .= prepare_binary($item); } else { - $s .= prepare_text($item['body'],$item['mimetype'], false); + $s .= prepare_text($item['body'],$item['mimetype'], $opts); } $event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event_obj($item['obj']) : false); @@ -1698,7 +1698,8 @@ function prepare_binary($item) { * * @return string */ -function prepare_text($text, $content_type = 'text/bbcode', $cache = false) { +function prepare_text($text, $content_type = 'text/bbcode', $opts = false) { + switch($content_type) { case 'text/plain': @@ -1742,7 +1743,7 @@ function prepare_text($text, $content_type = 'text/bbcode', $cache = false) { if(stristr($text,'[nosmile]')) $s = bbcode($text, [ 'cache' => $cache ]); else - $s = smilies(bbcode($text, [ 'cache' => $cache ])); + $s = smilies(bbcode($text, ((is_array($opts)) ? $opts : [] ))); $s = zidify_links($s); @@ -2189,13 +2190,13 @@ function ids_to_querystr($arr,$idx = 'id',$quote = false) { * @returns string */ -function array_elm_to_str($arr,$elm,$delim = ',') { +function array_elm_to_str($arr,$elm,$delim = ',',$each = 'trim') { $tmp = []; if($arr && is_array($arr)) { foreach($arr as $x) { if(is_array($x) && array_key_exists($elm,$x)) { - $z = trim($x[$elm]); + $z = $each($x[$elm]); if(($z) && (! in_array($z,$tmp))) { $tmp[] = $z; } @@ -2205,7 +2206,9 @@ function array_elm_to_str($arr,$elm,$delim = ',') { return implode($delim,$tmp); } - +function trim_and_unpunify($s) { + return unpunify(trim($s)); +} /** @@ -2574,6 +2577,9 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d // The @! tag will alter permissions $exclusive = (((! $grouptag) && (strpos($tag,'!') === 1) && (! $diaspora)) ? true : false); + if(($grouptag) && (strpos($tag,'!!') === 0)) { + $exclusive = true; + } //is it already replaced? if(strpos($tag,'[zrl=')) @@ -2746,8 +2752,8 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d $profile = str_replace(',','%2c',$profile); $url = $profile; if($grouptag) { - $newtag = '!' . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; - $body = str_replace('!' . $name, $newtag, $body); + $newtag = '!' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; + $body = str_replace('!' . (($exclusive) ? '!' : '') . $name, $newtag, $body); } else { $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . (($forum && ! $trailing_plus_name) ? '+' : '') . '[/zrl]'; @@ -2797,6 +2803,7 @@ function linkify_tags($a, &$body, $uid, $diaspora = false) { continue; $success = handle_tag($a, $body, $access_tag, $str_tags, ($uid) ? $uid : App::$profile_uid , $tag, $diaspora); + $results[] = array('success' => $success, 'access_tag' => $access_tag); if($success['replaced']) $tagged[] = $tag; } diff --git a/install/schema_mysql.sql b/install/schema_mysql.sql index 0d098d661..bfb49d195 100644 --- a/install/schema_mysql.sql +++ b/install/schema_mysql.sql @@ -1597,3 +1597,55 @@ CREATE TABLE if not exists calendarinstances ( UNIQUE(calendarid, principaluri), UNIQUE(calendarid, share_href) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + +CREATE TABLE if not exists oauth_clients ( + client_id VARCHAR(80) NOT NULL, + client_secret VARCHAR(80), + redirect_uri VARCHAR(2000), + grant_types VARCHAR(80), + scope VARCHAR(4000), + user_id VARCHAR(80), + PRIMARY KEY (client_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE if not exists oauth_access_tokens ( + access_token VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(255), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + PRIMARY KEY (access_token) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE if not exists oauth_authorization_codes ( + authorization_code VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(255), + redirect_uri VARCHAR(2000), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + id_token VARCHAR(1000), + PRIMARY KEY (authorization_code) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE if not exists oauth_refresh_tokens ( + refresh_token VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(255), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + PRIMARY KEY (refresh_token) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE if not exists oauth_scopes ( + scope VARCHAR(191) NOT NULL, + is_default TINYINT(1), + PRIMARY KEY (scope) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE if not exists oauth_jwt ( + client_id VARCHAR(80) NOT NULL, + subject VARCHAR(80), + public_key VARCHAR(2000) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/install/schema_postgres.sql b/install/schema_postgres.sql index a4f6e9253..df94712fe 100644 --- a/install/schema_postgres.sql +++ b/install/schema_postgres.sql @@ -1610,3 +1610,57 @@ ALTER TABLE ONLY users CREATE UNIQUE INDEX users_ukey ON users USING btree (username); + + +CREATE TABLE oauth_clients ( + client_id VARCHAR(80) NOT NULL, + client_secret VARCHAR(80), + redirect_uri VARCHAR(2000), + grant_types VARCHAR(80), + scope VARCHAR(4000), + user_id VARCHAR(80), + PRIMARY KEY (client_id) +); + +CREATE TABLE oauth_access_tokens ( + access_token VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(255), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + PRIMARY KEY (access_token) +); + +CREATE TABLE oauth_authorization_codes ( + authorization_code VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(255), + redirect_uri VARCHAR(2000), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + id_token VARCHAR(1000), + PRIMARY KEY (authorization_code) +); + +CREATE TABLE oauth_refresh_tokens ( + refresh_token VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(255), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + PRIMARY KEY (refresh_token) +); + +CREATE TABLE oauth_scopes ( + scope VARCHAR(191) NOT NULL, + is_default SMALLINT, + PRIMARY KEY (scope) +); + +CREATE TABLE oauth_jwt ( + client_id VARCHAR(80) NOT NULL, + subject VARCHAR(80), + public_key VARCHAR(2000) NOT NULL +); + + diff --git a/view/css/mod_oauth2testvehicle.css b/view/css/mod_oauth2testvehicle.css new file mode 100644 index 000000000..0ef2896b5 --- /dev/null +++ b/view/css/mod_oauth2testvehicle.css @@ -0,0 +1,4 @@ +.oath2test-form-box { + border: #ccc thin solid; + padding: 1em; +}
\ No newline at end of file diff --git a/view/js/autocomplete.js b/view/js/autocomplete.js index f7570523e..07b9bc449 100644 --- a/view/js/autocomplete.js +++ b/view/js/autocomplete.js @@ -198,7 +198,7 @@ function string2bb(element) { // Autocomplete forums forums = { - match: /(^|\s)(\!)([^ \n]+)$/, + match: /(^|\s)(\!\!*)([^ \n]+)$/, index: 3, search: function(term, callback) { contact_search(term, callback, backend_url, 'f', extra_channels, spinelement=false); }, replace: editor_replace, @@ -210,7 +210,7 @@ function string2bb(element) { tags = { match: /(^|\s)(\#)([^ \n]{2,})$/, index: 3, - search: function(term, callback) { $.getJSON('/hashtags/' + '$f=&t=' + term).done(function(data) { callback($.map(data, function(entry) { return entry.text.indexOf(term) === 0 ? entry : null; })); }); }, + search: function(term, callback) { $.getJSON('/hashtags/' + '$f=&t=' + term).done(function(data) { callback($.map(data, function(entry) { return entry.text.toLowerCase().indexOf(term.toLowerCase()) === 0 ? entry : null; })); }); }, replace: function(item) { return "$1$2" + item.text + ' '; }, context: function(text) { return text.toLowerCase(); }, template: tag_format diff --git a/view/tpl/navbar_default.tpl b/view/tpl/navbar_default.tpl index 92b067bcb..532e10f22 100755 --- a/view/tpl/navbar_default.tpl +++ b/view/tpl/navbar_default.tpl @@ -91,7 +91,7 @@ <div class="collapse navbar-collapse" id="navbar-collapse-1"> <ul class="navbar-nav mr-auto"> {{if $nav.login && !$userinfo}} - <li class="nav-item d-none d-xl-flex"> + <li class="nav-item d-lg-flex"> {{if $nav.loginmenu.1.4}} <a class="nav-link" href="#" title="{{$nav.loginmenu.1.3}}" id="{{$nav.loginmenu.1.4}}" data-toggle="modal" data-target="#nav-login"> {{$nav.loginmenu.1.1}} @@ -104,12 +104,12 @@ </li> {{/if}} {{if $nav.register}} - <li class="nav-item {{$nav.register.2}} d-none d-xl-flex"> + <li class="nav-item {{$nav.register.2}} d-lg-flex"> <a class="nav-link" href="{{$nav.register.0}}" title="{{$nav.register.3}}" id="{{$nav.register.4}}">{{$nav.register.1}}</a> </li> {{/if}} {{if $nav.alogout}} - <li class="nav-item {{$nav.alogout.2}} d-none d-xl-flex"> + <li class="nav-item {{$nav.alogout.2}} d-lg-flex"> <a class="nav-link" href="{{$nav.alogout.0}}" title="{{$nav.alogout.3}}" id="{{$nav.alogout.4}}">{{$nav.alogout.1}}</a> </li> {{/if}} diff --git a/view/tpl/navbar_tucson.tpl b/view/tpl/navbar_tucson.tpl index 92b067bcb..800f863dd 100755 --- a/view/tpl/navbar_tucson.tpl +++ b/view/tpl/navbar_tucson.tpl @@ -91,7 +91,7 @@ <div class="collapse navbar-collapse" id="navbar-collapse-1"> <ul class="navbar-nav mr-auto"> {{if $nav.login && !$userinfo}} - <li class="nav-item d-none d-xl-flex"> + <li class="nav-item d-lg-flex"> {{if $nav.loginmenu.1.4}} <a class="nav-link" href="#" title="{{$nav.loginmenu.1.3}}" id="{{$nav.loginmenu.1.4}}" data-toggle="modal" data-target="#nav-login"> {{$nav.loginmenu.1.1}} @@ -104,12 +104,12 @@ </li> {{/if}} {{if $nav.register}} - <li class="nav-item {{$nav.register.2}} d-none d-xl-flex"> + <li class="nav-item {{$nav.register.2}} d-lg-flex"> <a class="nav-link" href="{{$nav.register.0}}" title="{{$nav.register.3}}" id="{{$nav.register.4}}">{{$nav.register.1}}</a> </li> {{/if}} {{if $nav.alogout}} - <li class="nav-item {{$nav.alogout.2}} d-none d-xl-flex"> + <li class="nav-item {{$nav.alogout.2}} d-none d-lg-flex"> <a class="nav-link" href="{{$nav.alogout.0}}" title="{{$nav.alogout.3}}" id="{{$nav.alogout.4}}">{{$nav.alogout.1}}</a> </li> {{/if}} diff --git a/view/tpl/oauth2testvehicle.tpl b/view/tpl/oauth2testvehicle.tpl new file mode 100644 index 000000000..ce46b58c0 --- /dev/null +++ b/view/tpl/oauth2testvehicle.tpl @@ -0,0 +1,21 @@ +<h1>OAuth 2.0 Test Vehicle</h1> + +{{foreach $endpoints as $ept}} +<div class="oath2test-form-box"> +<form action="{{$baseurl}}/{{$ept.0}}/" method="{{$ept.4}}"> + <h3>{{$ept.3}}</h3> + {{$baseurl}}/{{$ept.0}}/?{{foreach $ept.1 as $field}}{{$field.0}}={{$field.1}}&<input type="hidden" name="{{$field.0}}" value="{{$field.1}}" />{{/foreach}} + <br> + <button type="submit" name="{{$ept.2}}_submit" value="submit" class="btn btn-med" title="">Submit</button> + <span style="display: {{if $ept.5}}inline{{else}}none{{/if}}; font-size: 2em;"> <i class="fa fa-check"></i></span> +</form> + </div> +{{/foreach}} +<div> + <h3>API response</h3> + <pre style="display: inline-block; overflow-x: auto; white-space: nowrap; width: 100%;"> + <code> + {{$api_response}} + </code> + </pre> +</div>
\ No newline at end of file diff --git a/view/tpl/oauth_authorize.tpl b/view/tpl/oauth_authorize.tpl index 2b7afa80e..72701ce52 100755 --- a/view/tpl/oauth_authorize.tpl +++ b/view/tpl/oauth_authorize.tpl @@ -2,9 +2,15 @@ <div class='oauthapp'> <img src='{{$app.icon}}'> - <h4>{{$app.name}}</h4> -</div> -<h3>{{$authorize}}</h3> -<form method="POST"> -<div class="settings-submit-wrapper"><input class="settings-submit" type="submit" name="oauth_yes" value="{{$yes}}" /></div> -</form> + <h3>{{$app.name}}</h3> + <p class="descriptive-paragraph">{{$authorize}}</p> + <form method="POST"> + <div class="settings-submit-wrapper"> + <input type="hidden" name="client_id" value="{{$client_id}}" /> + <input type="hidden" name="redirect_uri" value="{{$redirect_uri}}" /> + <input type="hidden" name="state" value="{{$state}}" /> + <button class="btn btn-lg btn-danger" name="authorize" value="deny" type="submit">{{$no}}</button> + <button class="btn btn-lg btn-success" name="authorize" value="allow" type="submit">{{$yes}}</button> + </div> + </form> +</div>
\ No newline at end of file diff --git a/view/tpl/settings_nick_set.tpl b/view/tpl/settings_nick_set.tpl index 76fe7cd8d..4dbe58d82 100755 --- a/view/tpl/settings_nick_set.tpl +++ b/view/tpl/settings_nick_set.tpl @@ -1,5 +1,5 @@ <div id="settings-nick-wrapper" class="section-content-info-wrapper"> -<div id="settings-nickname-desc">{{$desc}} <strong>'{{$nickname}}@{{$basepath}}'</strong></div> +<div id="settings-nickname-desc">{{$desc}} <strong>{{$nickname}}</strong></div> {{if $davpath}} <br> <div id="settings-dav-desc">{{$davdesc}} <strong>'{{$davpath}}'</strong></div> diff --git a/view/tpl/wiki.tpl b/view/tpl/wiki.tpl index 2be323deb..6ca4b0c77 100644 --- a/view/tpl/wiki.tpl +++ b/view/tpl/wiki.tpl @@ -15,6 +15,7 @@ </div> </div> {{/if}} + <button type="button" class="btn btn-outline-secondary btn-sm" title="{{$sharePage}}" onclick="window.location.href='rpost?f=&body={{$shareLink}}';"><i class="fa fa-fw fa-share"></i></button> <button id="fullscreen-btn" type="button" class="btn btn-outline-secondary btn-sm" onclick="makeFullScreen(); adjustFullscreenEditorHeight();"><i class="fa fa-expand"></i></button> <button id="inline-btn" type="button" class="btn btn-outline-secondary btn-sm" onclick="makeFullScreen(false); adjustInlineEditorHeight()"><i class="fa fa-compress"></i></button> </div> |