aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Zotlabs/Module/Profiles.php5
-rw-r--r--tests/unit/CreateIdentityTest.php131
-rw-r--r--tests/unit/Lib/ZotfingerTest.php118
-rw-r--r--tests/unit/Module/MagicTest.php17
-rw-r--r--tests/unit/Module/ProfilesTest.php100
-rw-r--r--tests/unit/includes/dba/_files/hubloc.yml60
-rw-r--r--view/tpl/field_checkbox.tpl4
-rw-r--r--view/tpl/field_input.tpl2
-rw-r--r--view/tpl/field_textarea.tpl4
-rw-r--r--view/tpl/profile_edit.tpl58
10 files changed, 451 insertions, 48 deletions
diff --git a/Zotlabs/Module/Profiles.php b/Zotlabs/Module/Profiles.php
index a51a56307..4ccec5b46 100644
--- a/Zotlabs/Module/Profiles.php
+++ b/Zotlabs/Module/Profiles.php
@@ -696,10 +696,9 @@ class Profiles extends \Zotlabs\Web\Controller {
$show_presence = ['show_presence', t('Reveal my online status'), $show_presence_val, '', [t('No'), t('Yes')]];
}
+ $extra_fields = array();
$q = q("select * from profdef where true");
if($q) {
- $extra_fields = array();
-
foreach($q as $qq) {
$mine = q("select v from profext where k = '%s' and hash = '%s' and channel_id = %d limit 1",
dbesc($qq['field_name']),
@@ -833,6 +832,8 @@ class Profiles extends \Zotlabs\Web\Controller {
);
if($r) {
+ $profiles = '';
+
$tpl = get_markup_template('profile_entry.tpl');
foreach($r as $rr) {
$profiles .= replace_macros($tpl, array(
diff --git a/tests/unit/CreateIdentityTest.php b/tests/unit/CreateIdentityTest.php
index a5e0f278a..b8b556637 100644
--- a/tests/unit/CreateIdentityTest.php
+++ b/tests/unit/CreateIdentityTest.php
@@ -10,11 +10,140 @@
namespace Zotlabs\Tests\Unit;
+use phpmock\phpunit\PHPMock;
+use PHPUnit\Framework\Attributes\Before;
+
class CreateIdentityTest extends UnitTestCase {
+ use PHPMock;
+
+ //
+ // Static private key used by the openssl stubs.
+ //
+ private const PRVKEY = <<<'PRVKEY'
+ -----BEGIN PRIVATE KEY-----
+ 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
+ 0UH3yDnLoPuCvHeX2WAXdyWob6vBOA==
+ -----END PRIVATE KEY-----
+ PRVKEY;
+
+ //
+ // Static public key corresponding to self::PRVKEY, used by the
+ // openssl_stubs
+ //
+ private const PUBKEY = <<<'PUBKEY'
+ -----BEGIN PUBLIC KEY-----
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvIcMderJEy61sNlpW00h
+ 1lF3KRVKV48heeClWtJ/rJql4yHSaAtC1ce1SS9fLX66evbAXy3ClKm8iPQ9We/P
+ F6jzijyWj4w2CMB5GSX/mDq7z0D/eaAI0kcBQTcTSMypMr9vttgqJV9WWDT3uqw+
+ VwxXV7Aic8UzMduv/rl4j3tx5EP319WUiTNqnBgXZpffCm1zONo3fi3q5kpZuRL0
+ d28aYpXzyKt1kOBQe31OUVHt57imhVYZfpLVI7kp4tAxfbbRp5KDj5v0F0ElvwGy
+ N+5KX5G3KhCRiMdJ1mBLFWeXE7PeJQSvxIhCD5CTTgoSjjzuzsqd/WNUJdze9q+p
+ 9xQzzGasQio1/kCPGmJLJhqpsvHF+YiMPgIP86w3wWwqAbO1imS/QgdqTMWT1/7T
+ mLx22JMK/vIyZ5ZlOjOF/tBBi0Ls2iBwfl1uLRm1ILfXc2kwX7byWxmE5cFENLe+
+ C3gzFLL7gsU9rLx5lTMg+mNWFT6OLZZUeNe9AN4muziXWeMpI/wAabEaAc6F67Cp
+ Vv/VKhFbEB9S7rddzDC3vsSfVypa/Sqq+EBmMeTqIT5+PBMpVVA+/O2wzef44+pE
+ NFFB3eoCswxl+S22A75OtJ1MuCh7D5rCMR7ptLWCt4YQ9kgxfOPjZzbvJEkm25+Z
+ ZAjxDyWHEoxQr8oS6Fg1Lz8CAwEAAQ==
+ -----END PUBLIC KEY-----
+ PUBKEY;
+
private bool $queueworker_started = false;
- public function test_empty_args() {
+ #[Before]
+ public function setup_openssl_mocks(): void {
+ //
+ // The `create_identity` function will call `Crypto::new_keypair()`,
+ // which again will call `openssl_pkey_new`, to create a new
+ // private/public keypair for the channel. While this is safe to call
+ // from the code under test, it's also slow.
+ //
+ // Since we don't really care about the cryptography here, we can make
+ // the tests run a bit faster by stubbing the slow function call and
+ // return a sentinel value instead of actually generaing the keys.
+ //
+ $openssl_pkey_new =
+ $this->getFunctionMock('Zotlabs\Lib', 'openssl_pkey_new')
+ ->expects($this->any())
+ ->willReturn('openssl_pkey_result');
+
+ //
+ // Since we have stubbed `openssl_pkey_new` we also need to mock the
+ // call to `openssl_pkey_export` that is used to get the private key
+ // from the result of `openssl_pkey_new`.
+ //
+ // As this function returns the value in a output param, we need to
+ // replace it with a callback function to simulate it.
+ //
+ $openssl_pkey_export =
+ $this->getFunctionMock('Zotlabs\Lib', 'openssl_pkey_export')
+ ->expects($this->any())
+ ->willReturnCallback(function (string $result, string &$response): bool {
+ $this->assertEquals('openssl_pkey_result', $result);
+ $response = self::PRVKEY;
+ return true;
+ });
+
+ //
+ // Finally we also need to stub the `openssl_pkey_get_details` function
+ // used to retrevive the public key from the `openssl_pkey_new` result.
+ //
+ $openssl_pkey_get_details =
+ $this->getFunctionMock('Zotlabs\Lib', 'openssl_pkey_get_details')
+ ->expects($this->any())
+ ->with($this->identicalTo('openssl_pkey_result'))
+ ->willReturn([ 'key' => self::PUBKEY ]);
+ }
+
+ public function test_empty_args_fails_with_an_error_message() {
insert_hook('proc_run', [$this, 'proc_run_hook']);
$result = create_identity([]);
$this->assertEquals(
diff --git a/tests/unit/Lib/ZotfingerTest.php b/tests/unit/Lib/ZotfingerTest.php
new file mode 100644
index 000000000..bc70380c6
--- /dev/null
+++ b/tests/unit/Lib/ZotfingerTest.php
@@ -0,0 +1,118 @@
+<?php
+/*
+ * SPDX-FileCopyrightText: 2025 The Hubzilla Community
+ * SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+namespace Zotlabs\Tests\Unit\Lib;
+
+use PHPUnit\Framework\Attributes\TestWith;
+use Zotlabs\Lib\Zotfinger;
+use Zotlabs\Tests\Unit\UnitTestCase;
+
+use phpmock\phpunit\PHPMock;
+
+class ZotfingerTest extends UnitTestCase
+{
+ // Import PHPMock methods into this class
+ use PHPMock;
+
+ #[TestWith([null])]
+ #[TestWith([false])]
+ #[TestWith([''])]
+ public function testShouldReturnFalseOnNoResource($resource): void
+ {
+ $this->assertFalse(Zotfinger::exec($resource));
+ }
+
+ public function testShouldReturnFalseIfResourceNotFound(): void
+ {
+ //
+ // Mock z_post_url to prevent it fram connecting to an actual remote site,
+ // and so that we can control what it should return.
+ //
+ // Here we simulate a request that return 404 Not Found.
+ //
+ $mock = $this->getFunctionMock('Zotlabs\Lib', 'z_post_url')
+ ->expects($this->once())
+ ->willReturn([
+ 'return_code' => 404,
+ 'success' => false,
+ ]);
+
+ $this->assertFalse(Zotfinger::exec('https://example.test/some-resource'));
+ }
+
+ public function testShouldReturnBodyIfResourceFound(): void
+ {
+ //
+ // Mock z_post_url to prevent it fram connecting to an actual remote site,
+ // and so that we can control what it should return.
+ //
+ // Here we simulate a request returning 200 OK and some data
+ //
+ $mock = $this->getFunctionMock('Zotlabs\Lib', 'z_post_url')
+ ->expects($this->once())
+ ->willReturn([
+ 'return_code' => 200,
+ 'success' => true,
+ 'body' => '{"subject":"acct:user@example.test"}',
+ ]);
+
+ //
+ // Initialize some $_SERVER superglobal values needed by HTTPSig:
+ //
+ $_SERVER['REQUEST_METHOD'] = 'GET';
+ $_SERVER['REQUEST_URI'] = 'some_uri';
+ $_SERVER['CONTENT_TYPE'] = 'application/json';
+ $_SERVER['CONTENT_LENGTH'] = 42;
+
+ $result = Zotfinger::exec('https://example.test/some-resource');
+
+ //
+ // Verify that the json payload has been decoded
+ //
+ $this->assertIsArray($result['data']);
+ $this->assertArrayHasKey('subject', $result['data']);
+ $this->assertEquals('acct:user@example.test', $result['data']['subject']);
+ }
+
+ public function testThatRequestFromChannelIsSigned(): void
+ {
+ //
+ // Mock z_post_url to prevent it fram connecting to an actual remote site,
+ // and so that we can control what it should return.
+ //
+ // Here we simulate a request returning 200 OK and some data
+ //
+ $mock = $this->getFunctionMock('Zotlabs\Lib', 'z_post_url')
+ ->expects($this->once())
+ ->willReturn([
+ 'return_code' => 200,
+ 'success' => true,
+ 'body' => '{"subject":"acct:user@example.test"}',
+ ]);
+
+ //
+ // Initialize some $_SERVER superglobal values needed by HTTPSig:
+ //
+ $_SERVER['REQUEST_METHOD'] = 'GET';
+ $_SERVER['REQUEST_URI'] = 'some_uri';
+ $_SERVER['CONTENT_TYPE'] = 'application/json';
+ $_SERVER['CONTENT_LENGTH'] = 42;
+
+
+ $channel = $this->fixtures['channel'][0];
+
+ $testresult = Zotfinger::exec('https://example.test/some-resource', $channel);
+
+ //
+ // Verify that the json payload has been decoded
+ //
+ $this->assertIsArray($testresult['data']);
+ $this->assertArrayHasKey('subject', $testresult['data']);
+ $this->assertEquals('acct:user@example.test', $testresult['data']['subject']);
+ }
+}
diff --git a/tests/unit/Module/MagicTest.php b/tests/unit/Module/MagicTest.php
index 2c426bf76..24bcfda21 100644
--- a/tests/unit/Module/MagicTest.php
+++ b/tests/unit/Module/MagicTest.php
@@ -76,13 +76,6 @@ class MagicTest extends TestCase {
'xchan_hash' => 'foreign hash',
]);
- // Create the channel the foreign observer wants to access
- $result = create_identity([
- 'account_id' => $this->fixtures['account'][0]['account_id'],
- 'nickname' => 'testuser',
- 'name' => 'Trish Testuser',
- ]);
-
// Shortcut the permission checks, by saying this observer is allowed
// the delegate privilege over the target channel
insert_hook('perm_is_allowed', function (array &$perm) {
@@ -101,18 +94,20 @@ class MagicTest extends TestCase {
// assertions after the redirect is thrown.
$this->stub_goaway();
+ $channel = $this->fixtures['channel'][1];
+
try {
// Send a request to get delegate privileges for the `testuser` channel
// on the local hub.
$this->get('magic', [
'bdest' => bin2hex($dest_url),
- 'delegate' => 'testuser@hubzilla.test']
- );
+ 'delegate' => channel_reddress($channel)
+ ]);
} catch (RedirectException $e) {
$this->assertEquals($dest_url, $e->getMessage());
- $this->assertEquals($result['channel']['channel_id'], App::$channel['channel_id']);
+ $this->assertEquals($channel['channel_id'], App::$channel['channel_id']);
$this->assertEquals($original_session, $_SESSION['delegate_push']);
- $this->assertEquals($result['channel']['channel_id'], $_SESSION['delegate_channel']);
+ $this->assertEquals($channel['channel_id'], $_SESSION['delegate_channel']);
$this->assertEquals('foreign hash', $_SESSION['delegate']);
$this->assertEquals($this->fixtures['account'][0]['account_id'], $_SESSION['account_id']);
}
diff --git a/tests/unit/Module/ProfilesTest.php b/tests/unit/Module/ProfilesTest.php
new file mode 100644
index 000000000..2dc88f9b6
--- /dev/null
+++ b/tests/unit/Module/ProfilesTest.php
@@ -0,0 +1,100 @@
+<?php
+/*
+ * SPDX-FileCopyrightText: 2025 The Hubzilla Community
+ * SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+namespace Zotlabs\Tests\Unit\Module;
+
+use App;
+use PDOStatement;
+
+/**
+ * Tests for the Profiles module
+ */
+class ProfilesTest extends TestCase
+{
+ public function testDisplaysProfileEditFormIfMultiProfilesDisabled(): void {
+ // Set up the channel we'll be making the request for
+ $channel = $this->fixtures['channel'][0];
+ App::set_channel($channel);
+
+ // And set up the default profile
+ $this->createDefaultProfile($channel);
+
+ // Replace calls to 'local_channel' and make sure they return
+ // the id of the current channel.
+ $local_channel_mock =
+ $this->getFunctionMock('Zotlabs\Module', 'local_channel')
+ ->expects($this->any())
+ ->willReturn($channel['channel_id']);
+
+ // Replace calls to 'feature_enabled', and make it unconditionally
+ // return true. This is called by the Profiles module to check that
+ // multiple profiles are enabled.
+ $feature_enabled_mock =
+ $this->getFunctionMock('Zotlabs\Module', 'feature_enabled')
+ ->expects($this->any())
+ ->willReturn(false);
+
+ // Simulate a GET request to the module
+ $this->get('profiles');
+
+ // Check that the rendered page has the expected entry.
+ $this->assertPageContains('<form id="profile-edit-form"');
+ }
+
+ public function testListDefaultProfileWithNoArgs(): void {
+ // Set up the channel we'll be making the request for
+ $channel = $this->fixtures['channel'][0];
+ App::set_channel($channel);
+
+ // And set up the default profile
+ $this->createDefaultProfile($channel);
+
+ // Replace calls to 'local_channel' and make sure they return
+ // the id of the current channel.
+ $local_channel_mock =
+ $this->getFunctionMock('Zotlabs\Module', 'local_channel')
+ ->expects($this->any())
+ ->willReturn($channel['channel_id']);
+
+ // Replace calls to 'feature_enabled', and make it unconditionally
+ // return true. This is called by the Profiles module to check that
+ // multiple profiles are enabled.
+ $feature_enabled_mock =
+ $this->getFunctionMock('Zotlabs\Module', 'feature_enabled')
+ ->expects($this->once())
+ ->willReturn(true);
+
+ // Simulate a GET request to the module
+ $this->get('profiles');
+
+ // Check that the rendered page has the expected entry.
+ $this->assertPageContains('Default Profile');
+ }
+
+ /**
+ * Helper function to create the default profile for a channel
+ *
+ * @param array $channel An associative array containing the channel.
+ */
+ private function createDefaultProfile(array $channel): void {
+ $res = profile_store_lowlevel([
+ 'aid' => intval($channel['channel_account_id']),
+ 'uid' => intval($channel['channel_id']),
+ 'profile_guid' => random_string(),
+ 'profile_name' => t('Default Profile'),
+ 'is_default' => 1,
+ 'publish' => true,
+ 'fullname' => $channel['channel_name'],
+ 'photo' => z_root() . "/photo/profile/l/{$channel['channel_id']}",
+ 'thumb' => z_root() . "/photo/profile/m/{$channel['channel_id']}"
+ ]);
+
+ $this->assertInstanceOf(PDOStatement::class, $res);
+ $this->assertEquals('00000', $res->errorCode());
+ }
+}
diff --git a/tests/unit/includes/dba/_files/hubloc.yml b/tests/unit/includes/dba/_files/hubloc.yml
new file mode 100644
index 000000000..bd644bfe0
--- /dev/null
+++ b/tests/unit/includes/dba/_files/hubloc.yml
@@ -0,0 +1,60 @@
+---
+hubloc:
+ - # sys channel
+ hubloc_guid: uGp_IvDBQ7b_-s3HV7IehysbKFyx1w0oG4dm1Q3-trFlnw01H-rJhNRQ8QNdDRSiolJmIMizCiexogrHy3_kpg
+ hubloc_guid_sig: sha256.bawP2sNBcfnjpKx2OCMlISbLJBByejs0KkbWcKvFj_vfQ_TnKvTZ9PvfxPz0puUkSqaJVskwz7mY59d56aRqc3kTCniuuR_3L8padNElwkyqoPLgIBQlffeqoa0ax0R_kF667_MmlUj6F6RyQHuCNeElWRfy0LW4SzIsn72ogHevvbOa5gh27jDcjUqkY5C0i8wyxEHzI5Ue4zNwJ7JS6WETZVL_z2WgFemKagZ8SWNVdFQfHCmhJ-scBZZK9rM0rdLf1O9HjkxgdvzImSHH3ODT-qOfE-IKwlum0hCUJqmGwOQAHTydRHKn-pdEHX1eFE_Y15lwBPToyQW1zTso9PPzwRXKqJkrqapO6hqerRCWqI5dtgE_6Up3AFolWCU82h5to0Y5LT2iEvMids9x64laGKNw-bzdYpJtKZZqgxejchIuYoqZZJp4ivGzHeJktBveTCluP2lRXw2W1TupMOVADlfUGPFMTYj2LPPIp3CTD6RsSZobh9VvT22J1SzwwTFvHG2VoZbezOPKvWj-PHRb3iBTSYoQAgL8ecI4oWnn9HK-KHwUsoWOtIXDSKVRiySun3AXGjI8iIVI7ORk7-33aVrxuVuh76_ni7LEadFFGClbxZtzoM44QizkPDHVS4rYl1fsuYDbxr93FvG7_QCBsOzgfWgE2eN65poE7mU
+ hubloc_hash: de3-bhcFpdY5PZ-ef4vy-6PpSeGrEHB8DNQFC6uP9lG-RkPaMceUMdvIRYHvHIvRzjJ0RPII2sJ1ffz4ATQOHQ
+ hubloc_id_url: https://hubzilla.test/channel/sys
+ hubloc_addr: sys@hubzilla.test
+ hubloc_primary: 1
+ hubloc_url: https://hubzilla.test
+ hubloc_url_sig: sha256.ORRyOWqsjER4i_Vx2HN5u_4u6djHb7NDEixTssCK9L-tpfBcbDmJD32pVgQvGs4UPexqlvIj7AwE-_fju2xlc_ZaI7UfII20Re0DWYfZR5xiexPxSbxtoUu-VuHoFNLjfnAwvj3Xleg_TQ4UBc5ap1_F0B7FsctOpx6P9ot_0tD7qDmyeiyU3OCCqDhAUASRYoXbkxzAZCSMHHT0Dnjz0IDJ8avi2FvrUns-ThKypxqyXzPz1Ie-Fz9XzdTCohrXUFixcIhIkUBtrL_pUe-Al6SVjICTqtoXNITqN5DEQLDEX5ObFiVotKZztg_IJxx6bzVta6e-9_Wu3ikM4DwFM-i_Lyybxoc745BnKVtZUfk4X3HF4_z0JLFW9YobcLzKU4KJgP4fMbmjEznqk4UXB_pGm-EgW90vou18Yi0_m4pGvQJgW2xRgioEDUNrkcBo6O_ej8Y71hsDfkv6SDNxqC-eZ6YFLYN5cJ2TKS05GXYCMdOrmtvH4TMEyxbwe0lfZa5eBSZby2qp26Ler51kft-VaZeXDowtK5Qr6Gg3YNCYHjBdeJz_ks9M9JUJqV-SHsRV2mC9OnFxkMGIzV1oRhn91wgJKMDJn6tEZDCXHVCcuC-4l7GstKRT-UbpqtkV__NYWuE9L3yvwXEDT9FXBw0hljBIn5EsG2CPkvFrf0U
+ hubloc_site_id: xJFCfYhhPGPqHeDsNQKjaY9EdS0iLJmV5lvuR3rFi09LOwwt0uN6adhJND5gyzs-3EEILSG_OY7zPAUKSnMa6w
+ hubloc_host: hubzilla.test
+ hubloc_callback: https://hubzilla.test/zot
+ hubloc_sitekey: |
+ -----BEGIN PUBLIC KEY-----
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAquJ4YP2qctsfxG2nTmGU
+ /yIojix38qGkDyEarmtLUfn7Rm+sg1zT1IjW3Zvw/q4rmK8zfC9doCFR6Z9zU6/v
+ XlFDCY+EJ2vdw5U+D/OijvnDLABkOS1iKRII9225olh1ZbB2QpLq3tC1SbE04Wy6
+ fTuyehBrufrvqDXX+CyUYYbCiVtMv01v6Lr8Qc498afASlOJd2spoGcppeNlqL8u
+ 6Ipf+fMoo89oIn5JOk3aLvKu+33i1NbVzC3NrVRjWV+D7MQgCroDAYHujNA1Kkuw
+ QxYlxy+exeSnHD6YzZ1lAFHZSc+xPXQVc6wCFMuruk9KymrGNh5V4lFoU+Bm1bLw
+ 5berLIpQn8jKffZulSPeWcPw0cBQWgKMk3uEW1H6vJggepjunnjINDW7RvP7Thma
+ wVmU45EUeL3uzZR2JO+gX+Ay3oLCBTNCFrIa1U/cgrdXgTLBKT6JVfLaVPWMxqfA
+ n7f9e9DryEcB2pIq1l1CfUPMZdSYCI3pAT53io4imaUsB2HzWO39Ie1fGaHOo1Mk
+ MCSny0ySLRTGeefYJur3LCCyGkxpxGxFzqmCG2oF0UdQgw6rf8ENjojG3KyGNeYO
+ fkOd1kzv22NI8Zs+LEJQdkXWz16EeP8ZViZZ3lnG2lViFXAK3f4/U/fpuLCuMUi7
+ pDhncIFqrTPRozFWL0xbPAsCAwEAAQ==
+ -----END PUBLIC KEY-----
+ hubloc_network: zot6
+ hubloc_updated: 2025-11-19T21:27:19
+ - # test channel
+ hubloc_guid: "NTHCKiCblxvlLxdlu5SpOqoC_EWtKFqHEg6nc2Ps5X_UGJAfPBzENdOagS_IBC75RIQNtAYizZeobnpv0CEinA"
+ hubloc_guid_sig: "sha256.cLzCjVb_kgDUsXg-SM2qnn-DQ55cRcXlbPeigeWtnT_pjlCD7Pg6trcLa12s3iPup4dudG9MIZkjuwBUUBv25mVER9w97NGYCqLx1OGFbjuuAuZJzYHMZLiWSfv4PNzjvkB4sOAsCY6CBCcpg6rno55FxFhJNz10zTYkaqJQbS2zKnBnxqseXEwULKDgBbU8yapP1BX505FedcJR3MyVMYg0teiINOFywTjKQHOm_Pe6mmU_wblOy7eWLfoWXU9lq_Z9D47UTgeJd0IB89btcF0WsfyUhg3NNLE_kpfk2Ow-DBO9Joz64qoBe8Ri68pnom5LPgGTWwGWJnSud123ZfTDLzFg1h9sFMcJMiMzvJnvFm4puKKu4K0jYNkAp0DROnaqadreACU5y_vOQyCT0aiicIX3uPkKGhNbwSjcquOylzricqgaZeK6iOOb_iHRLuQd4lJgYShr9xqBk2LU31QnWO3QUmBVlVPpM26YeoulmwVsoOSxC7QcLjYmQCgYpGOUaWWxGlJSwDyvm4DKiG88hp_clzN4MDilycVmS_JvmYxsw1Do_RDeDRxq0xLnqtBZpnafEjm-LXkZGVYJ5kqvEtl3DmPai2Wzrnyb24XxGmAhOZo1NvfjrvRWtFN-uNXLHKDZEcEqogmiqMUd20XMh1YGLqH2IsvbPyYShXo"
+ hubloc_hash: "LAOCZBv-Cj6QVab_EpMFV6HIDmsUSDybKfSnH9I5cwYAkar4UvBcsj9ofsaIejYLTfgMy9BcGPWjy9-7Uk7rlQ"
+ hubloc_id_url: https://hubzilla.test/channel/test
+ hubloc_addr: test@hubzilla.test
+ hubloc_primary: 1
+ hubloc_url: https://hubzilla.test
+ hubloc_url_sig: sha256.t8AdUv_uurSc1RMM5lHuifGxceSLuX6-fT5I4KflJg16qO6u1dUuAVcH1pWA0rYIi0J9LL0rSCAfGgwa53wx5-C8Vt1TfMVoz0xyUTFJLd6FeLKRrYKQBj2Bos1VjHlXKj3Pn1455dacvDTXhg7t1WISaBMdzNlPQrTQnZPgNEy7cnPVmLkHuu5SVtYtT3mFiZXXUbaK4nFuw3lR83WVauCFIiKm8kcFSWtsLJzTMlx2NDOxTO-IMiSYXU4KwhcxmzLOqWQrT_vBDJNrny3jm79lIu5DJDCJdoVp2pwL72c3Esi5NhUKMPG7dyH4tKev5Ug3z4MUEaF_DvtDMgC0hAEtE3sE6wzqp4Fg6zLLE7V3wZI5cOpdjeL8-3TJokKJoUCDwKUwcLMyd9vbJKDN5QPtVKguLb7-QCfFZHA-xEoZlBW5gRptan9fTE3-3jRJF0PIywOM9YpwbLWaaUikQ8BHkS_pCVjAM7wiFfJzD6ocQhz5mKC-HQsABCck_jYzzjHzE4lfVziPN3arS_1tPFwDFsdk7Q04gOJ2F--DRBqY6R_iajZq0aDQgXelFk4B_BVU2N8zaom-0djQw16RPWchzMk8A1ablZ53P-D5IlWaJhL8BUEUtrKCrAqh6ecoQGYBwGitIy2FP2M0qfDiyTxkqnx31lW04Uk2tOFtJWI
+ hubloc_site_id: xJFCfYhhPGPqHeDsNQKjaY9EdS0iLJmV5lvuR3rFi09LOwwt0uN6adhJND5gyzs-3EEILSG_OY7zPAUKSnMa6w
+ hubloc_host: hubzilla.test
+ hubloc_callback: https://hubzilla.test/zot
+ hubloc_sitekey: |
+ -----BEGIN PUBLIC KEY-----
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAquJ4YP2qctsfxG2nTmGU
+ /yIojix38qGkDyEarmtLUfn7Rm+sg1zT1IjW3Zvw/q4rmK8zfC9doCFR6Z9zU6/v
+ XlFDCY+EJ2vdw5U+D/OijvnDLABkOS1iKRII9225olh1ZbB2QpLq3tC1SbE04Wy6
+ fTuyehBrufrvqDXX+CyUYYbCiVtMv01v6Lr8Qc498afASlOJd2spoGcppeNlqL8u
+ 6Ipf+fMoo89oIn5JOk3aLvKu+33i1NbVzC3NrVRjWV+D7MQgCroDAYHujNA1Kkuw
+ QxYlxy+exeSnHD6YzZ1lAFHZSc+xPXQVc6wCFMuruk9KymrGNh5V4lFoU+Bm1bLw
+ 5berLIpQn8jKffZulSPeWcPw0cBQWgKMk3uEW1H6vJggepjunnjINDW7RvP7Thma
+ wVmU45EUeL3uzZR2JO+gX+Ay3oLCBTNCFrIa1U/cgrdXgTLBKT6JVfLaVPWMxqfA
+ n7f9e9DryEcB2pIq1l1CfUPMZdSYCI3pAT53io4imaUsB2HzWO39Ie1fGaHOo1Mk
+ MCSny0ySLRTGeefYJur3LCCyGkxpxGxFzqmCG2oF0UdQgw6rf8ENjojG3KyGNeYO
+ fkOd1kzv22NI8Zs+LEJQdkXWz16EeP8ZViZZ3lnG2lViFXAK3f4/U/fpuLCuMUi7
+ pDhncIFqrTPRozFWL0xbPAsCAwEAAQ==
+ -----END PUBLIC KEY-----
+ hubloc_network: zot6
+ hubloc_updated: 2025-11-19T21:57:19
diff --git a/view/tpl/field_checkbox.tpl b/view/tpl/field_checkbox.tpl
index 670bb7e29..be9b4f7f0 100644
--- a/view/tpl/field_checkbox.tpl
+++ b/view/tpl/field_checkbox.tpl
@@ -1,6 +1,6 @@
<div id="{{$field.0}}_container" class="clearfix onoffswitch checkbox mb-3">
- <label for="id_{{$field.0}}">{{$field.1}}{{if $field.6}}<sup class="required zuiqmid"> {{$field.6}}</sup>{{/if}}</label>
- <div class="float-end"><input type="checkbox" name="{{$field.0}}" id="id_{{$field.0}}" value="1" {{if $field.2}}checked="checked"{{/if}} {{if $field.5}}{{$field.5}}{{/if}} /><label class="switchlabel" for='id_{{$field.0}}'> <span class="onoffswitch-inner" data-on='{{if $field.4}}{{$field.4.1}}{{/if}}' data-off='{{if $field.4}}{{$field.4.0}}{{/if}}'></span><span class="onoffswitch-switch"></span></label></div>
+ <label for="id_{{$field.0}}">{{$field.1}}{{if isset($field.6)}}<sup class="required zuiqmid"> {{$field.6}}</sup>{{/if}}</label>
+ <div class="float-end"><input type="checkbox" name="{{$field.0}}" id="id_{{$field.0}}" value="1" {{if $field.2}}checked="checked"{{/if}} {{$field.5|default:''}} ><label class="switchlabel" for='id_{{$field.0}}'> <span class="onoffswitch-inner" data-on='{{if $field.4}}{{$field.4.1}}{{/if}}' data-off='{{if $field.4}}{{$field.4.0}}{{/if}}'></span><span class="onoffswitch-switch"></span></label></div>
<small class="form-text text-muted">{{$field.3}}</small>
</div>
{{*
diff --git a/view/tpl/field_input.tpl b/view/tpl/field_input.tpl
index c22b6fc29..1a2fff5c7 100644
--- a/view/tpl/field_input.tpl
+++ b/view/tpl/field_input.tpl
@@ -11,7 +11,7 @@
{{if isset($field.5)}}{{$field.5}}{{/if}}
>
<small id="help_{{$field.0}}" class="form-text text-muted">
- {{$field.3}}
+ {{$field.3|default:''}}
</small>
</div>
{{*
diff --git a/view/tpl/field_textarea.tpl b/view/tpl/field_textarea.tpl
index 684043830..6239089b7 100644
--- a/view/tpl/field_textarea.tpl
+++ b/view/tpl/field_textarea.tpl
@@ -1,5 +1,5 @@
<div class="mb-3">
<label for="id_{{$field.0}}">{{$field.1}}</label>
- <textarea class="form-control" name="{{$field.0}}" id="id_{{$field.0}}" {{if $field.4}}{{$field.4}}{{/if}} >{{$field.2}}</textarea>
- <small class="form-text text-muted">{{$field.3}}</small>
+ <textarea class="form-control" name="{{$field.0}}" id="id_{{$field.0}}" {{$field.4|default:''}}>{{$field.2}}</textarea>
+ <small class="form-text text-muted">{{$field.3|default:''}}</small>
</div>
diff --git a/view/tpl/profile_edit.tpl b/view/tpl/profile_edit.tpl
index 63a92e156..033b05e65 100644
--- a/view/tpl/profile_edit.tpl
+++ b/view/tpl/profile_edit.tpl
@@ -225,7 +225,7 @@
{{/if}}
- {{if $fields.address || $fields.locality || $fields.postal_code || $fields.region || $fields.country_name || $fields.hometown}}
+ {{if isset($fields.address) || isset($fields.locality) || isset($fields.postal_code) || isset($fields.region) || isset($fields.country_name) || isset($fields.hometown)}}
<div class="panel">
<div class="section-subtitle-wrapper" role="tab" id="location">
<h3>
@@ -236,27 +236,27 @@
</div>
<div id="location-collapse" class="panel-collapse collapse" data-bs-parent="#profile-edit-wrapper" role="tabpanel" aria-labelledby="location">
<div class="section-content-tools-wrapper">
- {{if $fields.address}}
+ {{if $fields.address|default:false}}
{{include file="field_input.tpl" field=$address}}
{{/if}}
- {{if $fields.locality}}
+ {{if $fields.locality|default:false}}
{{include file="field_input.tpl" field=$locality}}
{{/if}}
- {{if $fields.postal_code}}
+ {{if $fields.postal_code|default:false}}
{{include file="field_input.tpl" field=$postal_code}}
{{/if}}
- {{if $fields.region}}
+ {{if $fields.region|default:false}}
{{include file="field_input.tpl" field=$region}}
{{/if}}
- {{if $fields.country_name}}
+ {{if $fields.country_name|default:false}}
{{include file="field_input.tpl" field=$country_name}}
{{/if}}
- {{if $fields.hometown}}
+ {{if $fields.hometown|default:false}}
{{include file="field_input.tpl" field=$hometown}}
{{/if}}
@@ -281,7 +281,7 @@
</div>
<div id="relation-collapse" class="panel-collapse collapse" data-bs-parent="#profile-edit-wrapper" role="tabpanel" aria-labelledby="relation">
<div class="section-content-tools-wrapper">
- {{if $fields.marital }}
+ {{if $fields.marital|default:false }}
<div id="profile-edit-marital-wrapper" class="mb-3 field" >
<label id="profile-edit-marital-label" for="profile-edit-marital" ><span class="heart"><i class="bi fa-heart"></i>&nbsp;</span>{{$lbl_marital}}</label>
{{if $advanced}}
@@ -292,16 +292,16 @@
</div>
<div class="clear"></div>
- {{if $fields.partner}}
+ {{if isset($fields.partner)}}
{{include file="field_input.tpl" field=$with}}
{{/if}}
- {{if $fields.howlong}}
+ {{if isset($fields.howlong)}}
{{include file="field_input.tpl" field=$howlong}}
{{/if}}
{{/if}}
- {{if $fields.sexual}}
+ {{if isset($fields.sexual)}}
<div id="profile-edit-sexual-wrapper" class="mb-3 field" >
<label id="profile-edit-sexual-label" for="sexual-select" >{{$lbl_sexual}}</label>
{{if $advanced}}
@@ -321,7 +321,7 @@
</div>
</div>
{{/if}}
- {{if $fields.keywords || $fields.politic || $fields.religion || $fields.about || $fields.contact || $fields.homepage || $fields.interest || $fields.likes || $fields.dislikes || $fields.channels || $fields.music || $fields.book || $fields.tv || $fields.film || $fields.romance || $fields.employment || $fields.education || $extra_fields}}
+ {{if isset($fields.keywords) || isset($fields.politic) || isset($fields.religion) || isset($fields.about) || isset($fields.contact) || isset($fields.homepage) || isset($fields.interest) || isset($fields.likes) || isset($fields.dislikes) || isset($fields.channels) || isset($fields.music) || isset($fields.book) || isset($fields.tv) || isset($fields.film) || isset($fields.romance) || isset($fields.employment) || isset($fields.education) || isset($extra_fields)}}
<div class="panel">
<div class="section-subtitle-wrapper" role="tab" id="miscellaneous">
<h3>
@@ -332,71 +332,71 @@
</div>
<div id="miscellaneous-collapse" class="panel-collapse collapse" data-bs-parent="#profile-edit-wrapper" role="tabpanel" aria-labelledby="miscellaneous">
<div class="section-content-tools-wrapper">
- {{if $fields.homepage}}
+ {{if isset($fields.homepage)}}
{{include file="field_input.tpl" field=$homepage}}
{{/if}}
- {{if $fields.keywords}}
+ {{if isset($fields.keywords)}}
{{include file="field_input.tpl" field=$keywords}}
{{/if}}
- {{if $fields.politic}}
+ {{if isset($fields.politic)}}
{{include file="field_input.tpl" field=$politic}}
{{/if}}
- {{if $fields.religion}}
+ {{if isset($fields.religion)}}
{{include file="field_input.tpl" field=$religion}}
{{/if}}
- {{if $fields.about}}
+ {{if isset($fields.about)}}
{{include file="field_textarea.tpl" field=$about}}
{{/if}}
- {{if $fields.contact}}
+ {{if isset($fields.contact)}}
{{include file="field_textarea.tpl" field=$contact}}
{{/if}}
- {{if $fields.interest}}
+ {{if isset($fields.interest)}}
{{include file="field_textarea.tpl" field=$interest}}
{{/if}}
- {{if $fields.likes}}
+ {{if isset($fields.likes)}}
{{include file="field_textarea.tpl" field=$likes}}
{{/if}}
- {{if $fields.dislikes}}
+ {{if isset($fields.dislikes)}}
{{include file="field_textarea.tpl" field=$dislikes}}
{{/if}}
- {{if $fields.channels}}
+ {{if isset($fields.channels)}}
{{include file="field_textarea.tpl" field=$channels}}
{{/if}}
- {{if $fields.music}}
+ {{if isset($fields.music)}}
{{include file="field_textarea.tpl" field=$music}}
{{/if}}
- {{if $fields.book}}
+ {{if isset($fields.book)}}
{{include file="field_textarea.tpl" field=$book}}
{{/if}}
- {{if $fields.tv}}
+ {{if isset($fields.tv)}}
{{include file="field_textarea.tpl" field=$tv}}
{{/if}}
- {{if $fields.film}}
+ {{if isset($fields.film)}}
{{include file="field_textarea.tpl" field=$film}}
{{/if}}
- {{if $fields.romance}}
+ {{if isset($fields.romance)}}
{{include file="field_textarea.tpl" field=$romance}}
{{/if}}
- {{if $fields.employment}}
+ {{if isset($fields.employment)}}
{{include file="field_textarea.tpl" field=$employ}}
{{/if}}
- {{if $fields.education}}
+ {{if isset($fields.education)}}
{{include file="field_textarea.tpl" field=$education}}
{{/if}}