diff --git a/sources/CHANGELOG b/sources/CHANGELOG index 9e9156fb..d708278b 100644 --- a/sources/CHANGELOG +++ b/sources/CHANGELOG @@ -1,3 +1,36 @@ +Hubzilla 1.4 + [This list may appear brief, but encompasses a huge amount of re-writing and re-factoring + of the internal code structure to gain long-term performance and stability and provide a standard + interface to alternate protocol federation plugins which were made possible by the UNO configuration. + UNO is a configuration of hubzilla introduced in 1.3 with reduced complexity and which provides + improved protocol federation potential to other networks by virtue of removing nomadic identity + (which is not possible to model or work around using other network protocols).] + + Implement channel move operation for UNO configuration + Remove bookmark references in UNO (which has no bookmarks by default) + UI cleanup profiles/chat/manage + Refactor webfinger probes and salmon backend for GNU-social federation + SECURITY: DAV authentication exploit + Context help added + More help pages + Provide 'posts only' feed + Refactor App to remove globals + Refactor Session to remove globals + provide a fullscreen mode for selected modules and functions + Regression: some addon routes broken + fix "remember me" + Autocomplete tool extended to bbcode/comanche + Clone sync of file/photo updates + system rename (e.g. http to https or DNS name change) missing some connection photos + calendar module not blocked to public whhen block_public enabled + Use timeago.js in reshare content so that timestamps will be correct on federated reshares + Rework detection of JavaScript to avoid reload penalty under normal operation + Changed primary directory server to a hubzilla server + Plugins: + Diaspora - switch to alternate XML parser to avoid storing compound objects + GNU-Social - Huge amounts of work, federation somewhat working now, several issues remain + Friendica - Initial federation work (not yet published) + Hubzilla 1.3 Admin Security configuration page created which consolidates several previously hidden settings: Communication white/black lists diff --git a/sources/README.md b/sources/README.md index 64506594..ad7a4a9c 100644 --- a/sources/README.md +++ b/sources/README.md @@ -3,26 +3,26 @@ Hubzilla - Community Server =========================== -Help us redefine the web - using integrated and united community websites. --------------------------------------------------------------------------- +Connected and linked web communities. +-------------------------------------
-**What are Hubs?** +**What are Hubz?** -Hubs are independent general-purpose websites that not only connect with their associated members and viewers, but also connect together to exchange personal communications and other information with each other. +Hubz are independent general-purpose websites that not only connect with their associated members and viewers, but also connect together to exchange personal communications and other information with each other. This allows hub members on any hub to securely and privately share anything; with anybody, on any hub - anywhere; or share stuff publicly with anybody on the internet if desired. -**Hubzilla** is the server software which makes this possible. It is a sophisticated and unique combination of an open source content management system and a decentralised identity, communications, and permissions framework and protocol suite, built using common webserver technology (PHP/MySQL/Apache, although Mariadb or Postgres and Nginx could also be used - we're pretty easy). The end result is a level of systems integration, privacy control, and communications features that you wouldn't think are possible in either a content management system or a decentralised communications network. It also brings a new level of cooperation and privacy to the web and introduces the concept of personally owned "single sign-on" to web services across the entire internet. +**Hubzilla** is the server software which makes this possible. It is a sophisticated and unique combination of an open source content management system and a decentralised identity, communications, and permissions framework and protocol suite, built using common webserver technology (PHP/MySQL/Apache and popular variants). The end result is a level of systems integration, privacy control, and communications features that you wouldn't think are possible in either a content management system or a decentralised communications network. It also brings a new level of cooperation and privacy to the web and introduces the concept of personally owned "single sign-on" to web services across the entire internet. -Hubzilla hubs are +Hubzilla hubz are * decentralised * inherently social -* optionally inter-networked with other hubs -* privacy-enabled (privacy exclusions work across the entire internet to any registered identity on any compatible hubs) +* optionally inter-networked with other hubz +* privacy-enabled (privacy exclusions work across the entire internet to any registered identity on any compatible hubz) Possible website applications include diff --git a/sources/Zotlabs/Project/System.php b/sources/Zotlabs/Project/System.php index ca57c50a..a67742db 100644 --- a/sources/Zotlabs/Project/System.php +++ b/sources/Zotlabs/Project/System.php @@ -5,38 +5,39 @@ namespace Zotlabs\Project; class System { function get_platform_name() { - $a = get_app(); - if(is_array($a->config) && is_array($a->config['system']) && $a->config['system']['platform_name']) - return $a->config['system']['platform_name']; + if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['platform_name']) + return \App::$config['system']['platform_name']; return PLATFORM_NAME; } + function get_site_name() { + if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['sitename']) + return \App::$config['system']['sitename']; + return ''; + } + function get_project_version() { - $a = get_app(); - if(is_array($a->config) && is_array($a->config['system']) && $a->config['system']['hide_version']) + if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['hide_version']) return ''; return RED_VERSION; } function get_update_version() { - $a = get_app(); - if(is_array($a->config) && is_array($a->config['system']) && $a->config['system']['hide_version']) + if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['hide_version']) return ''; return DB_UPDATE_VERSION; } function get_notify_icon() { - $a = get_app(); - if(is_array($a->config) && is_array($a->config['system']) && $a->config['system']['email_notify_icon_url']) - return $a->config['system']['email_notify_icon_url']; + if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['email_notify_icon_url']) + return \App::$config['system']['email_notify_icon_url']; return z_root() . '/images/hz-white-32.png'; } function get_site_icon() { - $a = get_app(); - if(is_array($a->config) && is_array($a->config['system']) && $a->config['system']['site_icon_url']) - return $a->config['system']['site_icon_url']; + if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['site_icon_url']) + return \App::$config['system']['site_icon_url']; return z_root() . '/images/hz-32.png'; } diff --git a/sources/Zotlabs/Storage/Browser.php b/sources/Zotlabs/Storage/Browser.php index fde443e6..72094095 100644 --- a/sources/Zotlabs/Storage/Browser.php +++ b/sources/Zotlabs/Storage/Browser.php @@ -243,7 +243,7 @@ class Browser extends DAV\Browser\Plugin { )); $a = get_app(); - $a->page['content'] = $html; + \App::$page['content'] = $html; load_pdl($a); $theme_info_file = "view/theme/" . current_theme() . "/php/theme.php"; diff --git a/sources/Zotlabs/Storage/Directory.php b/sources/Zotlabs/Storage/Directory.php index e38d7691..edbef5a9 100644 --- a/sources/Zotlabs/Storage/Directory.php +++ b/sources/Zotlabs/Storage/Directory.php @@ -53,7 +53,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { logger('directory ' . $ext_path, LOGGER_DATA); $this->ext_path = $ext_path; // remove "/cloud" from the beginning of the path - $modulename = get_app()->module; + $modulename = \App::$module; $this->red_path = ((strpos($ext_path, '/' . $modulename) === 0) ? substr($ext_path, strlen($modulename) + 1) : $ext_path); if (! $this->red_path) { $this->red_path = '/'; @@ -114,7 +114,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { throw new DAV\Exception\Forbidden('Permission denied.'); } - $modulename = get_app()->module; + $modulename = \App::$module; if ($this->red_path === '/' && $name === $modulename) { return new Directory('/' . $modulename, $this->auth); } @@ -168,6 +168,14 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { intval($this->auth->owner_id) ); + + $ch = channelx_by_n($this->auth->owner_id); + if($ch) { + $sync = attach_export_data($ch,$this->folder_hash); + if($sync) + build_sync_packet($ch['channel_id'],array('file' => array($sync))); + } + $this->red_path = $new_path; } @@ -332,9 +340,15 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { require_once('include/photos.php'); $args = array( 'resource_id' => $hash, 'album' => $album, 'os_path' => $f, 'filename' => $name, 'getimagesize' => $x, 'directory' => $direct); - $p = photo_upload($c[0],get_app()->get_observer(),$args); + $p = photo_upload($c[0],\App::get_observer(),$args); } + $sync = attach_export_data($c[0],$hash); + + if($sync) + build_sync_packet($c[0]['channel_id'],array('file' => array($sync))); + + } /** @@ -356,7 +370,14 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { if ($r) { $result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash)); - if (! $result['success']) { + + if($result['success']) { + $sync = attach_export_data($r[0],$ret['data']['hash']); + if($sync) { + build_sync_packet($r[0]['channel_id'],array('file' => array($sync))); + } + } + else { logger('error ' . print_r($result, true), LOGGER_DEBUG); } } @@ -380,6 +401,15 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { } attach_delete($this->auth->owner_id, $this->folder_hash); + + $ch = channelx_by_n($this->auth->owner_id); + if($ch) { + $sync = attach_export_data($ch,$this->folder_hash,true); + if($sync) + build_sync_packet($ch['channel_id'],array('file' => array($sync))); + } + + } @@ -393,7 +423,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { public function childExists($name) { // On /cloud we show a list of available channels. // @todo what happens if no channels are available? - $modulename = get_app()->module; + $modulename = \App::$module; if ($this->red_path === '/' && $name === $modulename) { //logger('We are at ' $modulename . ' show a channel list', LOGGER_DEBUG); return true; @@ -417,7 +447,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { logger('GetDir: ' . $this->ext_path, LOGGER_DEBUG); $this->auth->log(); - $modulename = get_app()->module; + $modulename = \App::$module; $file = $this->ext_path; diff --git a/sources/Zotlabs/Storage/File.php b/sources/Zotlabs/Storage/File.php index 2a2a8b93..897f24ed 100644 --- a/sources/Zotlabs/Storage/File.php +++ b/sources/Zotlabs/Storage/File.php @@ -84,6 +84,20 @@ class File extends DAV\Node implements DAV\IFile { dbesc($this->data['hash']), intval($this->data['id']) ); + + if($this->data->is_photo) { + $r = q("update photo set filename = '%s' where resource_id = '%s' and uid = %d", + dbesc($newName), + dbesc($this->data['hash']), + intval($this->auth->owner_id) + ); + } + $ch = channelx_by_n($this->auth->owner_id); + if($ch) { + $sync = attach_export_data($ch,$this->data['hash']); + if($sync) + build_sync_packet($ch['channel_id'],array('file' => array($sync))); + } } /** @@ -173,7 +187,7 @@ class File extends DAV\Node implements DAV\IFile { if($is_photo) { require_once('include/photos.php'); $args = array( 'resource_id' => $this->data['hash'], 'album' => $album, 'os_path' => $f, 'filename' => $r[0]['filename'], 'getimagesize' => $gis, 'directory' => $direct ); - $p = photo_upload($c[0],get_app()->get_observer(),$args); + $p = photo_upload($c[0],\App::get_observer(),$args); } // update the folder's lastmodified timestamp @@ -205,6 +219,12 @@ class File extends DAV\Node implements DAV\IFile { return; } } + + $sync = attach_export_data($c[0],$this->data['hash']); + + if($sync) + build_sync_packet($c[0]['channel_id'],array('file' => array($sync))); + } /** @@ -318,5 +338,12 @@ class File extends DAV\Node implements DAV\IFile { } attach_delete($this->auth->owner_id, $this->data['hash']); + + $ch = channelx_by_n($this->auth->owner_id); + if($ch) { + $sync = attach_export_data($ch,$this->data['hash'],true); + if($sync) + build_sync_packet($ch['channel_id'],array('file' => array($sync))); + } } } diff --git a/sources/Zotlabs/Web/CheckJS.php b/sources/Zotlabs/Web/CheckJS.php new file mode 100644 index 00000000..3ad5fc1e --- /dev/null +++ b/sources/Zotlabs/Web/CheckJS.php @@ -0,0 +1,36 @@ +jsdisabled = 1; + if(intval($_COOKIE['jsdisabled'])) + $this->jsdisabled = 1; + + if(! $this->jsdisabled) { + $page = urlencode(\App::$query_string); + + if($test) { + \App::$page['htmlhead'] .= "\r\n" . '' . "\r\n"; + } + else { + \App::$page['htmlhead'] .= "\r\n" . '' . "\r\n"; + } + } + + } + + function disabled() { + return self::$jsdisabled; + } + + +} + + diff --git a/sources/Zotlabs/Web/Router.php b/sources/Zotlabs/Web/Router.php index af171437..29f2b520 100644 --- a/sources/Zotlabs/Web/Router.php +++ b/sources/Zotlabs/Web/Router.php @@ -9,9 +9,9 @@ class Router { /** * - * We have already parsed the server path into $a->argc and $a->argv + * We have already parsed the server path into App::$argc and App::$argv * - * $a->argv[0] is our module name. We will load the file mod/{$a->argv[0]}.php + * App::$argv[0] is our module name. We will load the file mod/{App::$argv[0]}.php * and use it for handling our URL request. * The module file contains a few functions that we call in various circumstances * and in the following order: @@ -25,7 +25,9 @@ class Router { * further processing. */ - if(strlen($a->module)) { + $module = \App::$module; + + if(strlen($module)) { /** * @@ -34,14 +36,14 @@ class Router { * */ - if(is_array($a->plugins) && in_array($a->module,$a->plugins) && file_exists("addon/{$a->module}/{$a->module}.php")) { - include_once("addon/{$a->module}/{$a->module}.php"); - if(function_exists($a->module . '_module')) - $a->module_loaded = true; + if(is_array(\App::$plugins) && in_array($module,\App::$plugins) && file_exists("addon/{$module}/{$module}.php")) { + include_once("addon/{$module}/{$module}.php"); + if(function_exists($module . '_module')) + \App::$module_loaded = true; } - if((strpos($a->module,'admin') === 0) && (! is_site_admin())) { - $a->module_loaded = false; + if((strpos($module,'admin') === 0) && (! is_site_admin())) { + \App::$module_loaded = false; notice( t('Permission denied.') . EOL); goaway(z_root()); } @@ -51,17 +53,19 @@ class Router { * Otherwise, look for the standard program module in the 'mod' directory */ - if(! $a->module_loaded) { - if(file_exists("mod/site/{$a->module}.php")) { - include_once("mod/site/{$a->module}.php"); - $a->module_loaded = true; + if(! (\App::$module_loaded)) { + if(file_exists("mod/site/{$module}.php")) { + include_once("mod/site/{$module}.php"); + \App::$module_loaded = true; } - elseif(file_exists("mod/{$a->module}.php")) { - include_once("mod/{$a->module}.php"); - $a->module_loaded = true; + elseif(file_exists("mod/{$module}.php")) { + include_once("mod/{$module}.php"); + \App::$module_loaded = true; } + else logger("mod/{$module}.php not found."); } + /** * This provides a place for plugins to register module handlers which don't otherwise exist on the system. * If the plugin sets 'installed' to true we won't throw a 404 error for the specified module even if @@ -69,10 +73,10 @@ class Router { * The plugin should catch at least one of the module hooks for this URL. */ - $x = array('module' => $a->module, 'installed' => false); + $x = array('module' => $module, 'installed' => false); call_hooks('module_loaded', $x); if($x['installed']) - $a->module_loaded = true; + \App::$module_loaded = true; /** * The URL provided does not resolve to a valid module. @@ -85,28 +89,28 @@ class Router { * Otherwise we are going to emit a 404 not found. */ - if(! $a->module_loaded) { + if(! (\App::$module_loaded)) { // Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit. if((x($_SERVER, 'QUERY_STRING')) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) { killme(); } - if((x($_SERVER, 'QUERY_STRING')) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && $a->config['system']['dreamhost_error_hack']) { + if((x($_SERVER, 'QUERY_STRING')) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && \App::$config['system']['dreamhost_error_hack']) { logger('index.php: dreamhost_error_hack invoked. Original URI =' . $_SERVER['REQUEST_URI']); - goaway($a->get_baseurl() . $_SERVER['REQUEST_URI']); + goaway(z_root() . $_SERVER['REQUEST_URI']); } logger('index.php: page not found: ' . $_SERVER['REQUEST_URI'] . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' . $_SERVER['QUERY_STRING'], LOGGER_DEBUG); header($_SERVER['SERVER_PROTOCOL'] . ' 404 ' . t('Not Found')); $tpl = get_markup_template('404.tpl'); - $a->page['content'] = replace_macros($tpl, array( + \App::$page['content'] = replace_macros($tpl, array( '$message' => t('Page not found.') )); // pretend this is a module so it will initialise the theme - $a->module = '404'; - $a->module_loaded = true; + \App::$module = '404'; + \App::$module_loaded = true; } } } @@ -118,8 +122,8 @@ class Router { * Call module functions */ - if($a->module_loaded) { - $a->page['page_title'] = $a->module; + if(\App::$module_loaded) { + \App::$page['page_title'] = \App::$module; $placeholder = ''; /** @@ -129,11 +133,11 @@ class Router { * to over-ride them. */ - if(function_exists($a->module . '_init')) { + if(function_exists(\App::$module . '_init')) { $arr = array('init' => true, 'replace' => false); - call_hooks($a->module . '_mod_init', $arr); + call_hooks(\App::$module . '_mod_init', $arr); if(! $arr['replace']) { - $func = $a->module . '_init'; + $func = \App::$module . '_init'; $func($a); } } @@ -167,32 +171,32 @@ class Router { $func = str_replace('-', '_', current_theme()) . '_init'; $func($a); } - elseif (x($a->theme_info, 'extends') && file_exists('view/theme/' . $a->theme_info['extends'] . '/php/theme.php')) { - require_once('view/theme/' . $a->theme_info['extends'] . '/php/theme.php'); - if(function_exists(str_replace('-', '_', $a->theme_info['extends']) . '_init')) { - $func = str_replace('-', '_', $a->theme_info['extends']) . '_init'; + elseif (x(\App::$theme_info, 'extends') && file_exists('view/theme/' . \App::$theme_info['extends'] . '/php/theme.php')) { + require_once('view/theme/' . \App::$theme_info['extends'] . '/php/theme.php'); + if(function_exists(str_replace('-', '_', \App::$theme_info['extends']) . '_init')) { + $func = str_replace('-', '_', \App::$theme_info['extends']) . '_init'; $func($a); } } - if(($_SERVER['REQUEST_METHOD'] === 'POST') && (! $a->error) - && (function_exists($a->module . '_post')) + if(($_SERVER['REQUEST_METHOD'] === 'POST') && (! \App::$error) + && (function_exists(\App::$module . '_post')) && (! x($_POST, 'auth-params'))) { - call_hooks($a->module . '_mod_post', $_POST); - $func = $a->module . '_post'; + call_hooks(\App::$module . '_mod_post', $_POST); + $func = \App::$module . '_post'; $func($a); } - if((! $a->error) && (function_exists($a->module . '_content'))) { - $arr = array('content' => $a->page['content'], 'replace' => false); - call_hooks($a->module . '_mod_content', $arr); - $a->page['content'] = $arr['content']; + if((! \App::$error) && (function_exists(\App::$module . '_content'))) { + $arr = array('content' => \App::$page['content'], 'replace' => false); + call_hooks(\App::$module . '_mod_content', $arr); + \App::$page['content'] = $arr['content']; if(! $arr['replace']) { - $func = $a->module . '_content'; + $func = \App::$module . '_content'; $arr = array('content' => $func($a)); } - call_hooks($a->module . '_mod_aftercontent', $arr); - $a->page['content'] .= $arr['content']; + call_hooks(\App::$module . '_mod_aftercontent', $arr); + \App::$page['content'] .= $arr['content']; } } } diff --git a/sources/Zotlabs/Web/Session.php b/sources/Zotlabs/Web/Session.php new file mode 100644 index 00000000..f998df39 --- /dev/null +++ b/sources/Zotlabs/Web/Session.php @@ -0,0 +1,160 @@ + $v) { + unset($_SESSION[$k]); + } + } + } + + function new_cookie($xtime) { + + $newxtime = (($xtime> 0) ? (time() + $xtime) : 0); + + $old_sid = session_id(); + + if(self::$handler && self::$session_started) { + session_regenerate_id(true); + + // force SessionHandler record creation with the new session_id + // which occurs as a side effect of read() + + self::$handler->read(session_id()); + } + else + logger('no session handler'); + + if (x($_COOKIE, 'jsdisabled')) { + setcookie('jsdisabled', $_COOKIE['jsdisabled'], $newxtime); + } + setcookie(session_name(),session_id(),$newxtime); + + $arr = array('expire' => $xtime); + call_hooks('new_cookie', $arr); + + } + + function extend_cookie() { + + // if there's a long-term cookie, extend it + + $xtime = (($_SESSION['remember_me']) ? (60 * 60 * 24 * 365) : 0 ); + + if($xtime) + setcookie(session_name(),session_id(),(time() + $xtime)); + $arr = array('expire' => $xtime); + call_hooks('extend_cookie', $arr); + + } + + + function return_check() { + + // check a returning visitor against IP changes. + // If the change results in being blocked from re-entry with the current cookie + // nuke the session and logout. + // Returning at all indicates the session is still valid. + + // first check if we're enforcing that sessions can't change IP address + // @todo what to do with IPv6 addresses + + if($_SESSION['addr'] && $_SESSION['addr'] != $_SERVER['REMOTE_ADDR']) { + logger('SECURITY: Session IP address changed: ' . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']); + + $partial1 = substr($_SESSION['addr'], 0, strrpos($_SESSION['addr'], '.')); + $partial2 = substr($_SERVER['REMOTE_ADDR'], 0, strrpos($_SERVER['REMOTE_ADDR'], '.')); + + $paranoia = intval(get_pconfig($_SESSION['uid'], 'system', 'paranoia')); + + if(! $paranoia) + $paranoia = intval(get_config('system', 'paranoia')); + + switch($paranoia) { + case 0: + // no IP checking + break; + case 2: + // check 2 octets + $partial1 = substr($partial1, 0, strrpos($partial1, '.')); + $partial2 = substr($partial2, 0, strrpos($partial2, '.')); + if($partial1 == $partial2) + break; + case 1: + // check 3 octets + if($partial1 == $partial2) + break; + case 3: + default: + // check any difference at all + logger('Session address changed. Paranoid setting in effect, blocking session. ' + . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']); + self::nuke(); + goaway(z_root()); + break; + } + } + return true; + } + +} diff --git a/sources/Zotlabs/Web/SessionHandler.php b/sources/Zotlabs/Web/SessionHandler.php new file mode 100644 index 00000000..6980a640 --- /dev/null +++ b/sources/Zotlabs/Web/SessionHandler.php @@ -0,0 +1,88 @@ +channel['channel_hash'] == $hubloc['xchan_hash']) { + if (\App::$channel['channel_hash'] == $hubloc['xchan_hash']) { return true; } else { @@ -242,9 +242,9 @@ class Auth { $arr = array('xchan' => $hubloc, 'url' => $this->desturl, 'session' => $_SESSION); call_hooks('magic_auth_success',$arr); - get_app()->set_observer($hubloc); + \App::set_observer($hubloc); require_once('include/security.php'); - get_app()->set_groups(init_groups_visitor($_SESSION['visitor_id'])); + \App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); info(sprintf( t('Welcome %s. Remote authentication successful.'),$hubloc['xchan_name'])); logger('mod_zot: auth success from ' . $hubloc['xchan_addr']); $this->success = true; @@ -341,5 +341,5 @@ class Auth { * Service_class can be used by cooperating sites to provide different access rights based on account rights and subscription plans. It is * a string whose contents are not defined by protocol. Example: "basic" or "gold". * - * @param[in,out] App &$a + * @param[in,out] \App &$a */ diff --git a/sources/Zotlabs/Zot/Verify.php b/sources/Zotlabs/Zot/Verify.php new file mode 100644 index 00000000..1192202d --- /dev/null +++ b/sources/Zotlabs/Zot/Verify.php @@ -0,0 +1,42 @@ +theme_info['extends'] == 'redbasic') + if(function_exists('redbasic_init') || App::$theme_info['extends'] == 'redbasic') $bookmarkicon = ''; else $bookmarkicon = '