aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG11
-rw-r--r--Zotlabs/Daemon/Xchan_photo.php9
-rw-r--r--Zotlabs/Lib/Activity.php19
-rw-r--r--Zotlabs/Lib/Config.php2
-rw-r--r--Zotlabs/Lib/Libsync.php2
-rw-r--r--Zotlabs/Lib/ThreadItem.php4
-rw-r--r--Zotlabs/Module/Hq.php20
-rw-r--r--Zotlabs/Module/Item.php2
-rw-r--r--Zotlabs/Render/SmartyInterface.php2
-rw-r--r--Zotlabs/Render/Theme.php6
-rw-r--r--boot.php8
-rw-r--r--include/attach.php9
-rw-r--r--include/auth.php37
-rw-r--r--include/config.php4
-rw-r--r--include/event.php6
-rw-r--r--include/html2plain.php4
-rw-r--r--include/language.php4
-rw-r--r--include/network.php12
-rw-r--r--tests/unit/UnitTestCase.php1
-rw-r--r--tests/unit/includes/AuthTest.php81
-rw-r--r--tests/unit/includes/dba/_files/account.yml2
-rw-r--r--view/tpl/cal_event.tpl2
-rw-r--r--view/tpl/event.tpl6
23 files changed, 186 insertions, 67 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 67f6783a9..b5cd098a4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,14 @@
+Hubzilla 9.0.2 (2024-06-07)
+ - Fix buttons in event viewer
+ - Fix some PHP warnings and errors
+ - Fix issue when inReplyTo field is an array
+ - Fix possible queueworker crash
+ - Fix missing pdl file for mod home
+ - Reduced default directory result set
+ - Fix fatal error in likebanner addon
+ - Fix fatal error in hilite addon
+
+
Hubzilla 9.0.1 (2024-03-26)
- Fix an issue where after an update initiated from a modal the modal backdrop would remain
- Fix bootstrap namespace in conv list templates
diff --git a/Zotlabs/Daemon/Xchan_photo.php b/Zotlabs/Daemon/Xchan_photo.php
index f90d1d726..662fc967c 100644
--- a/Zotlabs/Daemon/Xchan_photo.php
+++ b/Zotlabs/Daemon/Xchan_photo.php
@@ -8,14 +8,15 @@ class Xchan_photo {
static public function run($argc, $argv) {
- if ($argc != 3) {
+ if ($argc < 3) {
return;
}
$url = hex2bin($argv[1]);
$xchan = hex2bin($argv[2]);
+ $force = $argv[3];
- $photos = import_xchan_photo($url, $xchan);
+ $photos = import_xchan_photo($url, $xchan, false, $force);
if ($photos) {
$result = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'",
dbescdate(datetime_convert()),
@@ -27,8 +28,10 @@ class Xchan_photo {
);
if (! $result) {
- logger("xchan update failed for $url");
+ logger("xchan photo update failed for $url");
}
}
+
+ return;
}
}
diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php
index b819ef0f7..9178dac39 100644
--- a/Zotlabs/Lib/Activity.php
+++ b/Zotlabs/Lib/Activity.php
@@ -70,7 +70,7 @@ class Activity {
}
if ($items) {
- return self::encode_item(array_shift($items), true);
+ return self::encode_item(array_shift($items));
}
return null;
@@ -762,7 +762,12 @@ class Activity {
$ptr = [$ptr];
}
foreach ($ptr as $att) {
+ if (!is_array($att)) {
+ continue;
+ }
+
$entry = [];
+
if (array_key_exists('href', $att) && $att['href']) {
$entry['href'] = $att['href'];
} elseif (array_key_exists('url', $att) && $att['url']) {
@@ -1085,12 +1090,16 @@ class Activity {
static function encode_person($p, $extended = true) {
+ $ret = (($extended) ? [] : '');
+
+ if (!is_array($p)) {
+ return $ret;
+ }
+
$c = ((array_key_exists('channel_id', $p)) ? $p : channelx_by_hash($p['xchan_hash']));
$id = (($c) ? channel_url($c) : ((filter_var($p['xchan_hash'], FILTER_VALIDATE_URL)) ? $p['xchan_hash'] : $p['xchan_url']));
- $ret = (($extended) ? [] : '');
-
if (!$id) {
return $ret;
}
@@ -1866,7 +1875,7 @@ class Activity {
}
if ($icon) {
- Master::Summon(['Xchan_photo', bin2hex($icon), bin2hex($url)]);
+ Master::Summon(['Xchan_photo', bin2hex($icon), bin2hex($url), $force]);
}
}
@@ -2893,7 +2902,7 @@ class Activity {
if (intval($parent[0]['item_private'])) {
if (!intval($item['item_private'])) {
- $item['item_private'] = intval($parent_item['item_private']);
+ $item['item_private'] = intval($parent[0]['item_private']);
$item['allow_cid'] = '<' . $channel['channel_hash'] . '>';
$item['allow_gid'] = $item['deny_cid'] = $item['deny_gid'] = '';
}
diff --git a/Zotlabs/Lib/Config.php b/Zotlabs/Lib/Config.php
index 95df8ed6f..cd8b08991 100644
--- a/Zotlabs/Lib/Config.php
+++ b/Zotlabs/Lib/Config.php
@@ -115,7 +115,7 @@ class Config {
* The category of the configuration value
* @param string $key
* The configuration key to query
- * @param string $default (optional) default false
+ * @param mixed $default (optional) default false
* @return mixed Return value or false on error or if not set
*/
public static function Get($family, $key, $default = false) {
diff --git a/Zotlabs/Lib/Libsync.php b/Zotlabs/Lib/Libsync.php
index e68fd53f1..a7e33ba6b 100644
--- a/Zotlabs/Lib/Libsync.php
+++ b/Zotlabs/Lib/Libsync.php
@@ -267,7 +267,7 @@ class Libsync {
}
if ($cat !== 'hz_delpconfig') {
- set_pconfig($channel['channel_id'],$cat,$k,$v,$pconfig_updated[$k]);
+ set_pconfig($channel['channel_id'], $cat, $k, $v, $pconfig_updated[$k]);
}
}
}
diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php
index 290a7b0c2..eb1020cd2 100644
--- a/Zotlabs/Lib/ThreadItem.php
+++ b/Zotlabs/Lib/ThreadItem.php
@@ -44,8 +44,6 @@ class ThreadItem {
$this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
$this->threaded = Config::Get('system','thread_allow');
- $observer = \App::get_observer();
-
// Prepare the children
if(isset($data['children'])) {
@@ -875,7 +873,7 @@ class ThreadItem {
'$feature_encrypt' => ((feature_enabled($conv->get_profile_owner(),'content_encrypt')) ? true : false),
'$encrypt' => t('Encrypt text'),
'$cipher' => $conv->get_cipher(),
- '$sourceapp' => \App::$sourcename,
+ '$sourceapp' => App::$sourcename,
'$observer' => get_observer_hash(),
'$anoncomments' => ((in_array($conv->get_mode(), ['channel', 'display', 'cards', 'articles']) && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false),
'$anonname' => [ 'anonname', t('Your full name (required)') ],
diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php
index ae6a016fc..c8e6efe38 100644
--- a/Zotlabs/Module/Hq.php
+++ b/Zotlabs/Module/Hq.php
@@ -4,14 +4,6 @@ namespace Zotlabs\Module;
use App;
use Zotlabs\Widget\Messages;
-
-require_once("include/bbcode.php");
-require_once('include/security.php');
-require_once('include/conversation.php');
-require_once('include/acl_selectors.php');
-require_once('include/items.php');
-
-
class Hq extends \Zotlabs\Web\Controller {
function init() {
@@ -98,7 +90,6 @@ class Hq extends \Zotlabs\Web\Controller {
'permissions' => $channel_acl,
'bang' => '',
'visitor' => true,
- 'profile_uid' => local_channel(),
'return_path' => 'hq',
'expanded' => true,
'editor_autocomplete' => true,
@@ -117,17 +108,6 @@ class Hq extends \Zotlabs\Web\Controller {
nav_set_selected('HQ');
- if($target_item) {
- // if the target item is not a post (eg a like) we want to address its thread parent
- //$mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']);
-
- // if we got a decoded hash we must encode it again before handing to javascript
- // $mid = gen_link_id($target_item['mid']);
- }
- else {
- $mid = '';
- }
-
$o .= '<div id="live-hq"></div>' . "\r\n";
$o .= "<script> var profile_uid = " . local_channel()
. "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . ";</script>\r\n";
diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php
index 0324bf662..0b46f2baf 100644
--- a/Zotlabs/Module/Item.php
+++ b/Zotlabs/Module/Item.php
@@ -1654,7 +1654,7 @@ class Item extends Controller {
$listener = Libzot::zot_record_preferred($listener);
$c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'",
- intval($profile_uid),
+ intval($item['uid']),
dbesc($listener['hubloc_hash'])
);
diff --git a/Zotlabs/Render/SmartyInterface.php b/Zotlabs/Render/SmartyInterface.php
index d80ea3f3a..64c6aa377 100644
--- a/Zotlabs/Render/SmartyInterface.php
+++ b/Zotlabs/Render/SmartyInterface.php
@@ -20,7 +20,7 @@ class SmartyInterface extends Smarty {
$template_dirs = array('theme' => "view/theme/$thname/tpl/");
if ( x(App::$theme_info,"extends") ) {
- $template_dirs = $template_dirs + array('extends' => "view/theme/" . \App::$theme_info["extends"] . "/tpl/");
+ $template_dirs = $template_dirs + array('extends' => "view/theme/" . App::$theme_info["extends"] . "/tpl/");
}
$template_dirs = $template_dirs + array('base' => 'view/tpl/');
$this->setTemplateDir($template_dirs);
diff --git a/Zotlabs/Render/Theme.php b/Zotlabs/Render/Theme.php
index 543bf7a3f..a42b65e03 100644
--- a/Zotlabs/Render/Theme.php
+++ b/Zotlabs/Render/Theme.php
@@ -24,7 +24,7 @@ class Theme {
*
* @return array
*/
- static public function current(){
+ static public function current() {
self::$system_theme = ((isset(App::$config['system']['theme']))
? App::$config['system']['theme'] : '');
@@ -37,7 +37,7 @@ class Theme {
if(App::$profile_uid) {
$r = q("select channel_theme from channel where channel_id = %d limit 1",
- intval(\App::$profile_uid)
+ intval(App::$profile_uid)
);
if($r) {
$page_theme = $r[0]['channel_theme'];
@@ -46,7 +46,7 @@ class Theme {
// Themes from Comanche layouts over-ride the channel theme
- if(array_key_exists('theme', \App::$layout) && \App::$layout['theme']) {
+ if(array_key_exists('theme', App::$layout) && App::$layout['theme']) {
$page_theme = App::$layout['theme'];
}
diff --git a/boot.php b/boot.php
index 372c26763..60e022ba2 100644
--- a/boot.php
+++ b/boot.php
@@ -62,9 +62,11 @@ require_once('include/conversation.php');
require_once('include/acl_selectors.php');
require_once('include/selectors.php');
require_once('include/activities.php');
+require_once('include/security.php');
+
define('PLATFORM_NAME', 'hubzilla');
-define('STD_VERSION', '9.1.3');
+define('STD_VERSION', '9.1.4');
define('ZOT_REVISION', '6.0');
define('DB_UPDATE_VERSION', 1263);
@@ -1646,7 +1648,7 @@ function fix_system_urls($oldurl, $newurl) {
}
}
- Zotlabs\Daemon\Master::Summon(['Notifier', 'refresh_all', $c[0]['channel_id']]);
+ Master::Summon(['Notifier', 'refresh_all', $c[0]['channel_id']]);
}
}
@@ -2723,7 +2725,7 @@ function check_cron_broken() {
$d = Config::Get('system', 'lastcron');
if ((!$d) || ($d < datetime_convert('UTC', 'UTC', 'now - 4 hours'))) {
- Zotlabs\Daemon\Master::Summon(['Cron']);
+ Master::Summon(['Cron']);
Config::Set('system', 'lastcron', datetime_convert());
}
diff --git a/include/attach.php b/include/attach.php
index 2283da1c6..449721793 100644
--- a/include/attach.php
+++ b/include/attach.php
@@ -113,7 +113,6 @@ function z_mime_content_type($filename) {
'odf' => 'application/vnd.oasis.opendocument.formula',
'odi' => 'application/vnd.oasis.opendocument.image',
'odm' => 'application/vnd.oasis.opendocument.text-master',
- 'odb' => 'application/vnd.oasis.opendocument.base',
'odb' => 'application/vnd.oasis.opendocument.database',
'ott' => 'application/vnd.oasis.opendocument.text-template',
'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
@@ -2467,8 +2466,8 @@ function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpat
* @param int $channel_id
* @param int $resource_id
* @param string $new_folder_hash
- * @param (optional) string $newname
- * @param (optional) boolean $recurse
+ * @param string (optional) $newname
+ * @param boolean (optional) $recurse
* @return array Associative array with:
* * \e boolean \b success
* * \e string \b resource_id
@@ -2668,8 +2667,8 @@ function attach_move($channel_id, $resource_id, $new_folder_hash, $newname = '',
* @param int $channel_id
* @param int $resource_id
* @param string $new_folder_hash
- * @param (optional) string $newname
- * @param (optional) boolean $recurse
+ * @param string (optional) $newname
+ * @param boolean (optional) $recurse
* @return array Associative array with:
* * \e boolean \b success
* * \e string \b resource_id of the new resource
diff --git a/include/auth.php b/include/auth.php
index 0cd48bce3..1fc2cc556 100644
--- a/include/auth.php
+++ b/include/auth.php
@@ -176,6 +176,40 @@ function log_failed_login($errormsg) {
@file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $errormsg . PHP_EOL, FILE_APPEND);
}
+
+/**
+ * Determines if checking for multifactor authentication needs to be checked.
+ *
+ * Checks that multi factor authentication is enabled for the given account_id,
+ * and whether it's already authenticated or not.
+ *
+ * Some modules needs to be excluded from the mfa checks for various reasons:
+ *
+ * - `totp_check` is used by the mfa module itself.
+ * - `dav` provides WebDAV access, and has no way of providing a mfa code.
+ * - `cdav` is accessed both via CardDAV which has the same limitations as
+ * the `dav` module, but may also be accessed via a web browser over http.
+ * We only exclude it if it's not being accessed via a web browser.
+ *
+ * @param int $account_id The id of the account we're verifying.
+ * @param string $module The requested module.
+ * @param string $arg The first arg passed to the module (or empty if none.)
+ *
+ * @return bool `true` if mfa status needs to be checked, `false` otherwise.
+ */
+function requires_mfa_check(int $account_id, string $module, string $arg): bool {
+ if (in_array($module, ['totp_check', 'dav'], true)) {
+ return false;
+ }
+
+ if ($module === 'cdav' && !in_array($arg, ['addressbook', 'calendar'], true)) {
+ return false;
+ }
+
+ $multiFactor = AConfig::Get($account_id, 'system', 'mfa_enabled');
+ return $multiFactor && empty($_SESSION['2FA_VERIFIED']);
+}
+
/**
* Inline - not a function
* look for auth parameters or re-validate an existing session
@@ -267,8 +301,7 @@ if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) &&
$login_refresh = true;
}
- $multiFactor = AConfig::Get(App::$account['account_id'], 'system', 'mfa_enabled');
- if ($multiFactor && empty($_SESSION['2FA_VERIFIED']) && App::$module !== 'totp_check') {
+ if (requires_mfa_check(App::$account['account_id'], App::$module, argv(1))) {
$o = new Totp_check;
echo $o->get();
killme();
diff --git a/include/config.php b/include/config.php
index 674d5afe4..4dd40eccf 100644
--- a/include/config.php
+++ b/include/config.php
@@ -120,8 +120,8 @@ function get_pconfig($uid, $family, $key, $default = false) {
return Zlib\PConfig::Get($uid,$family,$key,$default);
}
-function set_pconfig($uid, $family, $key, $value) {
- return Zlib\PConfig::Set($uid,$family,$key,$value);
+function set_pconfig($uid, $family, $key, $value, $updated = NULL) {
+ return Zlib\PConfig::Set($uid, $family, $key, $value, $updated);
}
function del_pconfig($uid, $family, $key, $updated = NULL) {
diff --git a/include/event.php b/include/event.php
index ef73fc9b5..f8511cbe2 100644
--- a/include/event.php
+++ b/include/event.php
@@ -272,9 +272,9 @@ function format_event_ical($ev) {
if($ev['adjust']) {
if($ev['dtstart'])
- $o .= "\r\nDTSTART$tzid:" . datetime_convert($tz,'UTC', $ev['dtstart'],'Ymd\\THis\\Z');
+ $o .= "\r\nDTSTART$tzid:" . datetime_convert('UTC', $tz, $ev['dtstart'],'Ymd\\THis');
if($ev['dtend'] && ! $ev['nofinish'])
- $o .= "\r\nDTEND$tzid:" . datetime_convert($tz,'UTC', $ev['dtend'],'Ymd\\THis\\Z');
+ $o .= "\r\nDTEND$tzid:" . datetime_convert('UTC', $tz, $ev['dtend'],'Ymd\\THis');
}
else {
if($ev['dtstart'])
@@ -797,7 +797,7 @@ function parse_event_object($event_object_json) {
$tz = $object['timezone'] ?? 'UTC';
$ev['summary'] = $object['summary'] ?? $object['name'] ?? '';
- $ev['description'] = html2bbcode($content['content']) ?? '';
+ $ev['description'] = html2bbcode($object['content']) ?? '';
$ev['dtstart'] = $object['startTime'] ? datetime_convert('UTC', 'UTC', $object['startTime']) : '';
$ev['dtend'] = $object['endTime'] ? datetime_convert('UTC', 'UTC', $object['endTime']) : $ev['dtstart'];
$ev['location'] = $object['location']['name'] ?? '';
diff --git a/include/html2plain.php b/include/html2plain.php
index 88dce577d..69fb5193a 100644
--- a/include/html2plain.php
+++ b/include/html2plain.php
@@ -196,7 +196,7 @@ function html2plain($html, $wraplength = 75, $compact = false)
// Problem: there is no reliable way to detect if it is a link to a tag or profile
//node2bbcode($doc, 'a', array('href'=>'/(.+)/'), ' $1 ', '', true);
- node2bbcode($doc, 'a', array('href'=>'/(.+)/', 'rel'=>'oembed'), ' $1 ', '', true);
+ node2bbcode($doc, 'a', array('href'=>'/(.+)/', 'rel'=>'oembed'), ' $1 ', '');
//node2bbcode($doc, 'img', array('alt'=>'/(.+)/'), '$1', '');
//node2bbcode($doc, 'img', array('title'=>'/(.+)/'), '$1', '');
//node2bbcode($doc, 'img', array(), '', '');
@@ -205,7 +205,7 @@ function html2plain($html, $wraplength = 75, $compact = false)
else
node2bbcode($doc, 'img', array('src'=>'/(.+)/'), '', '');
- node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), ' $1 ', '', true);
+ node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), ' $1 ', '');
$message = $doc->saveHTML();
diff --git a/include/language.php b/include/language.php
index 22f350aac..538f67d90 100644
--- a/include/language.php
+++ b/include/language.php
@@ -198,10 +198,10 @@ function load_translation_table($lang, $install = false) {
*
* @param string $s string that should get translated
* @param string $ctx (optional) context to appear in po file
- * @return translated string if exists, otherwise return $s
+ * @return string translated string if exists, otherwise return $s
*
*/
-function t($s, $ctx = '') {
+function t($s, $ctx = ''): string {
$cs = $ctx ? '__ctx:' . $ctx . '__ ' . $s : $s;
if (x(App::$strings, $cs)) {
diff --git a/include/network.php b/include/network.php
index a7a11ff6e..bb5bc1ce7 100644
--- a/include/network.php
+++ b/include/network.php
@@ -116,7 +116,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) {
}
else {
$curl_time = intval(@Config::Get('system','curl_timeout'));
- @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
+ @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== 0) ? $curl_time : 60));
}
if(x($opts,'connecttimeout') && intval($opts['connecttimeout'])) {
@@ -124,7 +124,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) {
}
else {
$curl_contime = intval(@Config::Get('system','curl_connecttimeout'));
- @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (($curl_contime !== false) ? $curl_contime : 30));
+ @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (($curl_contime !== 0) ? $curl_contime : 30));
}
if(x($opts,'http_auth')) {
@@ -298,7 +298,7 @@ function z_post_url($url, $params, $redirects = 0, $opts = array()) {
}
else {
$curl_time = intval(@Config::Get('system','curl_timeout'));
- @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
+ @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== 0) ? $curl_time : 60));
}
if(x($opts,'http_auth')) {
@@ -2146,9 +2146,9 @@ function get_request_string($url) {
*
* @param array $parsed_url An associative array as produced by `parse_url`.
*
- * @return The reassembled URL as a string.
+ * @return string The reassembled URL as a string.
*/
-function unparse_url($parsed_url) {
+function unparse_url(array $parsed_url): string {
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
@@ -2158,5 +2158,5 @@ function unparse_url($parsed_url) {
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
- return "$scheme$user$pass$host$port$path$query$fragment";
+ return $scheme . $user . $pass . $host . $port . $path . $query . $fragment;
}
diff --git a/tests/unit/UnitTestCase.php b/tests/unit/UnitTestCase.php
index 844746a51..afc309205 100644
--- a/tests/unit/UnitTestCase.php
+++ b/tests/unit/UnitTestCase.php
@@ -31,6 +31,7 @@ use PHPUnit\Framework\TestCase;
*/
require_once __DIR__ . '/../../boot.php';
require_once 'include/dba/dba_driver.php' ;
+require_once 'include/dba/dba_transaction.php';
/**
* Base class for our Unit Tests.
diff --git a/tests/unit/includes/AuthTest.php b/tests/unit/includes/AuthTest.php
new file mode 100644
index 000000000..fa9726fe8
--- /dev/null
+++ b/tests/unit/includes/AuthTest.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Tests for the authentication code used in Hubzilla.
+ *
+ * SPDX-FileCopyrightText: 2024 Hubzilla Community
+ * SPDX-FileContributor: Harald Eilertsen
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+namespace Zotlabs\Tests\Unit\Includes;
+
+use App;
+use Zotlabs\Lib\AConfig;
+use Zotlabs\Tests\Unit\UnitTestCase;
+use PHPUnit\Framework\Attributes\{DataProvider, RunTestsInSeparateProcesses};
+
+/**
+ * Test class containing the test for the Hubzilla authentication code.
+ *
+ * Since the main authentication code is executed in the global scope on
+ * inclusion of the `includes/auth.php` file, we need to run each test in a
+ * separate process to make sure we can excersize the code as we need.
+ */
+#[RunTestsInSeparateProcesses]
+class AuthTest extends UnitTestCase {
+
+ /**
+ * Check that mfa status is not checked for certain modules.
+ *
+ * This causes issues with things like WebDAV and CardDAV, as there's
+ * currently no way for these modules to signal that a TOTP code is needed
+ * back to the connecting client.
+ */
+ #[DataProvider('modules_excluded_from_mfa')]
+ public function test_mfa_is_not_checked_for_excluded_modules(string $module, array $args): void {
+ $account_id = $this->fixtures['account']['0']['account_id'];
+
+ $_SESSION = [
+ 'authenticated' => true,
+ 'account_id' => $account_id,
+
+ // Trick the code to not warn that $_SESSION['uid'] is not set,
+ // but also not trigger the code that tries to change to the
+ // given channel. *Remove when code is fixed!*
+ 'uid' => 0,
+ ];
+
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
+
+ App::$session = $this->create_session_stub();
+ App::$module = $module;
+ App::$argv = $args;
+ App::$argc = count($args);
+
+ // Enable multi factor authentication for this account
+ AConfig::Set($account_id, 'system', 'mfa_enabled', true);
+
+ require 'include/auth.php';
+
+ $this->assertEquals(1, $_SESSION['authenticated']);
+ }
+
+ /**
+ * Data provider for testing modules excluded from mfa
+ * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
+ */
+ public static function modules_excluded_from_mfa(): array {
+ return [
+ ['totp_check', []],
+ ['cdav', []],
+ ['cdav', ['calendar']],
+ ['cdav', ['addressbook']],
+ ['dav', []],
+ ];
+ }
+
+ private function create_session_stub(): \Zotlabs\Web\Session {
+ return $this->createStub('Zotlabs\Web\Session');
+ }
+}
diff --git a/tests/unit/includes/dba/_files/account.yml b/tests/unit/includes/dba/_files/account.yml
index 344bdb799..88e59056e 100644
--- a/tests/unit/includes/dba/_files/account.yml
+++ b/tests/unit/includes/dba/_files/account.yml
@@ -3,7 +3,9 @@ account:
account_id: 42
account_email: "hubzilla@example.com"
account_language: "no"
+ account_flags: 0
-
account_id: 43
account_email: "hubzilla@example.org"
account_language: "de"
+ account_flags: 1
diff --git a/view/tpl/cal_event.tpl b/view/tpl/cal_event.tpl
index d7662786b..0719d5262 100644
--- a/view/tpl/cal_event.tpl
+++ b/view/tpl/cal_event.tpl
@@ -6,7 +6,7 @@
</div>
{{$event.html}}
<div class="event-buttons">
- {{if $event.item.plink}}<a href="{{$event.plink.0}}" title="{{$event.plink.1}}" class="plink-event-link"><i class="fa fa-external-link btn btn-outline-secondary" ></i></a>{{/if}}
+ {{if $event.item.plink}}<a href="{{$event.plink.0}}" title="{{$event.plink.1}}" class="btn btn-sm btn-outline-secondary border-0 plink-event-link"><i class="fa fa-external-link"></i></a>{{/if}}
</div>
<div class="clear"></div>
</div>
diff --git a/view/tpl/event.tpl b/view/tpl/event.tpl
index cc0bfc1c7..91dc53421 100644
--- a/view/tpl/event.tpl
+++ b/view/tpl/event.tpl
@@ -6,9 +6,9 @@
</div>
{{$event.html}}
<div class="event-buttons">
- {{if $event.item.plink}}<a href="{{$event.plink.0}}" title="{{$event.plink.1}}" class="plink-event-link"><i class="fa fa-external-link btn btn-outline-secondary" ></i></a>{{/if}}
- {{if $event.edit}}<a href="{{$event.edit.0}}" title="{{$event.edit.1}}" class="edit-event-link"><i class="fa fa-pencil btn btn-outline-secondary"></i></a>{{/if}}
- {{if $event.drop}}<a href="{{$event.drop.0}}" title="{{$event.drop.1}}" class="drop-event-link"><i class="fa fa-trash-o btn btn-outline-secondary"></i></a>{{/if}}
+ {{if $event.item.plink}}<a href="{{$event.plink.0}}" title="{{$event.plink.1}}" class="btn btn-sm btn-outline-secondary border-0 plink-event-link"><i class="fa fa-external-link" ></i></a>{{/if}}
+ {{if $event.edit}}<a href="{{$event.edit.0}}" title="{{$event.edit.1}}" class="btn btn-sm btn-outline-secondary border-0 edit-event-link"><i class="fa fa-pencil"></i></a>{{/if}}
+ {{if $event.drop}}<a href="{{$event.drop.0}}" title="{{$event.drop.1}}" class="btn btn-sm btn-outline-secondary border-0 drop-event-link"><i class="fa fa-trash-o"></i></a>{{/if}}
</div>
<div class="clear"></div>
</div>