diff options
| -rw-r--r-- | Zotlabs/Module/Profiles.php | 5 | ||||
| -rw-r--r-- | tests/unit/CreateIdentityTest.php | 131 | ||||
| -rw-r--r-- | tests/unit/Lib/ZotfingerTest.php | 118 | ||||
| -rw-r--r-- | tests/unit/Module/MagicTest.php | 17 | ||||
| -rw-r--r-- | tests/unit/Module/ProfilesTest.php | 100 | ||||
| -rw-r--r-- | tests/unit/includes/dba/_files/hubloc.yml | 60 | ||||
| -rw-r--r-- | view/tpl/field_checkbox.tpl | 4 | ||||
| -rw-r--r-- | view/tpl/field_input.tpl | 2 | ||||
| -rw-r--r-- | view/tpl/field_textarea.tpl | 4 | ||||
| -rw-r--r-- | view/tpl/profile_edit.tpl | 58 |
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> </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}} |
