From 2d0a93100ce6911146cd593b36ece27589c041c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Philipp=20Sch=C3=BCle?=
Date: Wed, 19 Feb 2025 12:50:15 +0100
Subject: [PATCH 1/2] feature(MatrixSynapseIntegrator/UserEditDialog): add
matrix ID and active fields
---
tine20/Admin/js/user/EditDialog.js | 6 +-
.../Admin/userEditDialogRecordFormPlugin.js | 93 +++++++++++++++++++
.../js/elementIntegrator.js | 9 ++
.../MatrixSynapseIntegrator/js/package.json | 17 ++++
4 files changed, 124 insertions(+), 1 deletion(-)
create mode 100644 tine20/MatrixSynapseIntegrator/js/Admin/userEditDialogRecordFormPlugin.js
create mode 100644 tine20/MatrixSynapseIntegrator/js/elementIntegrator.js
create mode 100644 tine20/MatrixSynapseIntegrator/js/package.json
diff --git a/tine20/Admin/js/user/EditDialog.js b/tine20/Admin/js/user/EditDialog.js
index 4082b9dabc0..b9091faa571 100644
--- a/tine20/Admin/js/user/EditDialog.js
+++ b/tine20/Admin/js/user/EditDialog.js
@@ -972,7 +972,7 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
name: 'password_must_change',
plugins: [this.mustChangeTriggerPlugin]
});
-
+
var config = {
xtype: 'tabpanel',
deferredRender: false,
@@ -985,6 +985,10 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
border: false,
frame: true,
layout: 'hfit',
+ plugins: [{
+ ptype: 'ux.itemregistry',
+ key: 'Admin-UserEditDialog-RecordForm'
+ }],
items: [{
xtype: 'columnform',
labelAlign: 'top',
diff --git a/tine20/MatrixSynapseIntegrator/js/Admin/userEditDialogRecordFormPlugin.js b/tine20/MatrixSynapseIntegrator/js/Admin/userEditDialogRecordFormPlugin.js
new file mode 100644
index 00000000000..204c1bace70
--- /dev/null
+++ b/tine20/MatrixSynapseIntegrator/js/Admin/userEditDialogRecordFormPlugin.js
@@ -0,0 +1,93 @@
+Promise.all([Tine.Tinebase.appMgr.isInitialised('MatrixSynapseIntegrator'),
+ Tine.Tinebase.ApplicationStarter.isInitialised()]).then(() => {
+
+ if (!Tine.Tinebase.configManager.get('matrixDomain', 'MatrixSynapseIntegrator')) {
+ Tine.log.debug('MatrixSynapseIntegrator: matrixDomain not configured - skipping admin user edit dialog hook');
+ return;
+ }
+
+ // add panel with matrixid field
+ const mySubPanel = Ext.extend(Ext.form.FieldSet, {
+ layout: 'fit',
+
+ initComponent: function() {
+ this.app = Tine.Tinebase.appMgr.get('MatrixSynapseIntegrator');
+ this.title = this.app.i18n._('Matrix Integration');
+ this.matrixIdField = new Ext.form.TextField({
+ fieldLabel: this.app.i18n.gettext('Matrix ID'),
+ name: 'matrixId',
+ });
+ this.matrixActiveField = new Ext.form.Checkbox({
+ fieldLabel: this.app.i18n.gettext('Matrix Account Active'),
+ name: 'matrixActive',
+ listeners: {
+ 'check': function(checkbox, value) {
+ if (value) {
+ if (!this.matrixIdField.getValue()) {
+ this.setDefaultMatrixId(this.editDialog.record);
+ }
+ this.matrixIdField.enable();
+ } else {
+ this.matrixIdField.disable();
+ }
+ },
+ scope: this
+ }
+ });
+
+ this.items = [{
+ xtype: 'columnform',
+ labelAlign: 'top',
+ items: [[this.matrixActiveField, this.matrixIdField]]
+ }];
+
+ this.supr().initComponent.apply(this, arguments);
+ },
+
+ setDefaultMatrixId: function(record) {
+ const matrixDomain = Tine.Tinebase.configManager.get('matrixDomain', 'MatrixSynapseIntegrator');
+ const userId = record.phantom ? '@{user.id}' : record.id;
+ this.matrixIdField.setValue(`${userId}:${matrixDomain}`);
+ },
+
+ onRecordLoad: async function(editDialog, record) {
+ if (record.phantom) {
+ // new record
+ this.setDefaultMatrixId(record);
+ this.matrixActiveField.setValue(true);
+ } else {
+ // get value from xprops
+ let xprops = record.get('xprops');
+ xprops = Ext.isObject(xprops) ? xprops : {};
+ if (xprops.matrixId) {
+ this.matrixIdField.setValue(xprops.matrixId);
+ }
+ if (xprops.matrixActive) {
+ this.matrixActiveField.setValue(xprops.matrixActive);
+ } else {
+ this.matrixIdField.disable();
+ }
+ }
+ },
+
+ onRecordUpdate: function(editDialog, record) {
+ let xprops = record.get('xprops');
+ xprops = Ext.isObject(xprops) ? xprops : {};
+ xprops.matrixId = this.matrixIdField.getValue();
+ xprops.matrixActive = this.matrixActiveField.getValue();
+ },
+
+ onRender: function() {
+ this.supr().onRender.apply(this, arguments);
+
+ if (!this.editDialog) {
+ this.editDialog = this.findParentBy(function (c) {
+ return c instanceof Tine.widgets.dialog.EditDialog
+ });
+ }
+ this.editDialog.on('load', this.onRecordLoad, this);
+ this.editDialog.on('recordUpdate', this.onRecordUpdate, this);
+ }
+ })
+ Ext.ux.ItemRegistry.registerItem('Admin-UserEditDialog-RecordForm', mySubPanel, 5);
+})
diff --git a/tine20/MatrixSynapseIntegrator/js/elementIntegrator.js b/tine20/MatrixSynapseIntegrator/js/elementIntegrator.js
new file mode 100644
index 00000000000..5b2de70fedd
--- /dev/null
+++ b/tine20/MatrixSynapseIntegrator/js/elementIntegrator.js
@@ -0,0 +1,9 @@
+/*
+ * Tine 2.0
+ *
+ * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author Cornelius Weiß
+ * @copyright Copyright (c) 2024 Metaways Infosystems GmbH (http://www.metaways.de)
+ */
+
+import './Admin/userEditDialogRecordFormPlugin'
diff --git a/tine20/MatrixSynapseIntegrator/js/package.json b/tine20/MatrixSynapseIntegrator/js/package.json
new file mode 100644
index 00000000000..a8a649a1803
--- /dev/null
+++ b/tine20/MatrixSynapseIntegrator/js/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "MatrixSynapseIntegrator",
+ "version": "0.0.1",
+ "description": "Tine MatrixSynapseIntegrator extensions",
+ "main": "elementIntegrator.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [
+ "element",
+ "synapse",
+ "matrix",
+ "extension"
+ ],
+ "author": "Cornelius Weiß",
+ "license": "AGPL-3.0"
+}
From eb738eedf8d06da25d1203d597ffc8fae8f34b29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Philipp=20Sch=C3=BCle?=
Date: Thu, 20 Feb 2025 11:49:59 +0100
Subject: [PATCH 2/2] feature(MatrixSynapseIntegrator/Corporal): manage user
lifecycle via corporal
see https://github.com/devture/matrix-corporal
---
.../MatrixSynapseIntegrator/AllTests.php | 5 +-
.../Backend/CorporalMock.php | 26 ++++
.../Controller/UserTests.php | 82 ++++++++++++
.../ControllerTests.php | 4 +-
.../MatrixSynapseIntegrator | 1 -
tine20/Felamimail/Controller.php | 5 +-
.../Backend/Corporal.php | 118 ++++++++++++++++++
tine20/MatrixSynapseIntegrator/Config.php | 34 ++++-
tine20/MatrixSynapseIntegrator/Controller.php | 24 +++-
.../Controller/User.php | 97 ++++++++++++++
10 files changed, 384 insertions(+), 12 deletions(-)
create mode 100644 tests/tine20/MatrixSynapseIntegrator/Backend/CorporalMock.php
create mode 100644 tests/tine20/MatrixSynapseIntegrator/Controller/UserTests.php
delete mode 120000 tests/tine20/MatrixSynapseIntegrator/MatrixSynapseIntegrator
create mode 100644 tine20/MatrixSynapseIntegrator/Backend/Corporal.php
create mode 100644 tine20/MatrixSynapseIntegrator/Controller/User.php
diff --git a/tests/tine20/MatrixSynapseIntegrator/AllTests.php b/tests/tine20/MatrixSynapseIntegrator/AllTests.php
index 179d8325ccc..b3222efda90 100644
--- a/tests/tine20/MatrixSynapseIntegrator/AllTests.php
+++ b/tests/tine20/MatrixSynapseIntegrator/AllTests.php
@@ -4,7 +4,7 @@
*
* @package MatrixSynapseIntegrator
* @license http://www.gnu.org/licenses/agpl.html
- * @copyright Copyright (c) 2020 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright Copyright (c) 2020-2025 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Paul Mehrer
*/
@@ -15,13 +15,12 @@
*/
class MatrixSynapseIntegrator_AllTests
{
-
-
public static function suite ()
{
$suite = new PHPUnit\Framework\TestSuite('All MatrixSynapseIntegrator tests');
$suite->addTestSuite(MatrixSynapseIntegrator_ControllerTests::class);
+ $suite->addTestSuite(MatrixSynapseIntegrator_Controller_UserTests::class);
return $suite;
}
diff --git a/tests/tine20/MatrixSynapseIntegrator/Backend/CorporalMock.php b/tests/tine20/MatrixSynapseIntegrator/Backend/CorporalMock.php
new file mode 100644
index 00000000000..0a7bd00fc28
--- /dev/null
+++ b/tests/tine20/MatrixSynapseIntegrator/Backend/CorporalMock.php
@@ -0,0 +1,26 @@
+//www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @copyright Copyright (c) 2025 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author Philipp Schüle
+ */
+
+/**
+ * MatrixSynapseIntegrator Backend Mock
+ *
+ * @package MatrixSynapseIntegrator
+ * @subpackage Backend
+ */
+class MatrixSynapseIntegrator_Backend_CorporalMock extends MatrixSynapseIntegrator_Backend_Corporal
+{
+ public function push(Tinebase_Model_FullUser $user): bool
+ {
+ $this->_policy = $this->_getPolicy($user);
+ return true;
+ }
+}
diff --git a/tests/tine20/MatrixSynapseIntegrator/Controller/UserTests.php b/tests/tine20/MatrixSynapseIntegrator/Controller/UserTests.php
new file mode 100644
index 00000000000..0ff6115eb13
--- /dev/null
+++ b/tests/tine20/MatrixSynapseIntegrator/Controller/UserTests.php
@@ -0,0 +1,82 @@
+
+ */
+
+class MatrixSynapseIntegrator_Controller_UserTests extends TestCase
+{
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ MatrixSynapseIntegrator_Controller_User::getInstance()->setBackend(new MatrixSynapseIntegrator_Backend_CorporalMock());
+ }
+
+ public function tearDown(): void
+ {
+ parent::tearDown();
+
+ MatrixSynapseIntegrator_Controller_User::destroyInstance();
+ }
+
+ public function testCreateUser(): Tinebase_Model_FullUser
+ {
+ $user = $this->_createTestUser([
+ 'xprops' => [
+ MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID => '@{user.id}:matrix.domain',
+ MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE => true,
+ ]
+ ]);
+
+ self::assertTrue($user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE]);
+ self::assertEquals('@' . $user->getId() . ':matrix.domain',
+ $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID]);
+
+ // assert corporal policy json
+ $backend = MatrixSynapseIntegrator_Controller_User::getInstance()->getBackend();
+ $policy = $backend->getPushedPolicy();
+ self::assertArrayHasKey('users', $policy);
+ self::assertCount(1, $policy['users']);
+ self::assertArrayHasKey('authType', $policy['users'][0]);
+ self::assertEquals('sha1', $policy['users'][0]['authType']);
+ $userData = $policy['users'][0];
+ self::assertEquals($user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID], $userData['id']);
+ self::assertEquals($user->accountDisplayName, $userData['displayName']);
+ return $user;
+ }
+
+ public function testUpdateUser()
+ {
+ $user = $this->testCreateUser();
+ $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE] = false;
+ $updatedUser = Admin_Controller_User::getInstance()->update($user);
+ self::assertFalse($updatedUser->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE]);
+ $this->_assertInactiveUserInPolicy();
+ }
+
+ protected function _assertInactiveUserInPolicy()
+ {
+ $backend = MatrixSynapseIntegrator_Controller_User::getInstance()->getBackend();
+ $policy = $backend->getPushedPolicy();
+ self::assertArrayHasKey('users', $policy);
+ self::assertCount(1, $policy['users']);
+ self::assertArrayHasKey('active', $policy['users'][0]);
+ self::assertFalse($policy['users'][0]['active']);
+ }
+
+ public function testDeleteUser()
+ {
+ $user = $this->testCreateUser();
+ // user deletion need the confirmation header
+ Admin_Controller_User::getInstance()->setRequestContext(['confirm' => true]);
+ Admin_Controller_User::getInstance()->delete([$user->getId()]);
+ $this->_assertInactiveUserInPolicy();
+ }
+}
diff --git a/tests/tine20/MatrixSynapseIntegrator/ControllerTests.php b/tests/tine20/MatrixSynapseIntegrator/ControllerTests.php
index 2d0ec0cd8cb..d42593bb38f 100644
--- a/tests/tine20/MatrixSynapseIntegrator/ControllerTests.php
+++ b/tests/tine20/MatrixSynapseIntegrator/ControllerTests.php
@@ -31,7 +31,7 @@ class MatrixSynapseIntegrator_ControllerTests extends TestCase
public function setUp(): void
-{
+ {
parent::setUp();
$this->_oldRequest = Tinebase_Core::getContainer()->get(RequestInterface::class);
@@ -41,7 +41,7 @@ public function setUp(): void
}
public function tearDown(): void
-{
+ {
Tinebase_Core::getContainer()->set(RequestInterface::class, $this->_oldRequest);
if ($this->_originalTestUser) {
diff --git a/tests/tine20/MatrixSynapseIntegrator/MatrixSynapseIntegrator b/tests/tine20/MatrixSynapseIntegrator/MatrixSynapseIntegrator
deleted file mode 120000
index cc31fc3839a..00000000000
--- a/tests/tine20/MatrixSynapseIntegrator/MatrixSynapseIntegrator
+++ /dev/null
@@ -1 +0,0 @@
-./../../tine20/vendor/metaways/tine20-matrixsynapseintegrator/tests/MatrixSynapseIntegrator
\ No newline at end of file
diff --git a/tine20/Felamimail/Controller.php b/tine20/Felamimail/Controller.php
index 4f5bf8b1174..a8279009b80 100644
--- a/tine20/Felamimail/Controller.php
+++ b/tine20/Felamimail/Controller.php
@@ -41,9 +41,10 @@ class Felamimail_Controller extends Tinebase_Controller_Event
private static $_instance = NULL;
/**
- * constructor (get current user)
+ * constructor
*/
- private function __construct() {
+ private function __construct()
+ {
}
/**
diff --git a/tine20/MatrixSynapseIntegrator/Backend/Corporal.php b/tine20/MatrixSynapseIntegrator/Backend/Corporal.php
new file mode 100644
index 00000000000..f0e9df7f407
--- /dev/null
+++ b/tine20/MatrixSynapseIntegrator/Backend/Corporal.php
@@ -0,0 +1,118 @@
+//www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @copyright Copyright (c) 2025 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author Philipp Schüle
+ */
+
+/**
+ * MatrixSynapseIntegrator Backend
+ *
+ * @package MatrixSynapseIntegrator
+ * @subpackage Backend
+ */
+class MatrixSynapseIntegrator_Backend_Corporal
+{
+ protected array $_policy = [];
+
+ protected const CORPORAL_ENDPOINT = '_matrix/corporal/policy';
+
+ public function push(Tinebase_Model_FullUser $user): bool
+ {
+ $this->_policy = $this->_getPolicy($user);
+ $this->pushPolicyToCorporal($this->_policy);
+
+ return true;
+ }
+
+ protected function _getHttpClient(): Zend_Http_Client
+ {
+ $matrixHomeServer = MatrixSynapseIntegrator_Config::getInstance()->get(
+ MatrixSynapseIntegrator_Config::HOME_SERVER_URL);
+ $corporalUrl = $matrixHomeServer . '/' . self::CORPORAL_ENDPOINT;
+
+ return Tinebase_Core::getHttpClient($corporalUrl);
+ }
+
+ /**
+ * @see https://github.com/devture/matrix-corporal/blob/master/docs/http-api.md#policy-submission-endpoint
+ *
+ * @param array $policy
+ * @return bool
+ * @throws Zend_Http_Client_Exception
+ */
+ protected function pushPolicyToCorporal(array $policy): bool
+ {
+ $client = $this->_getHttpClient();
+ $client->setHeaders([
+ 'Authorization' => 'Bearer ' . MatrixSynapseIntegrator_Config::getInstance()->get(
+ MatrixSynapseIntegrator_Config::CORPORAL_SHARED_AUTH_TOKEN),
+ 'Content-Type' => 'application/json',
+ ]);
+ $client->setRawData(json_encode($policy));
+
+ if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
+ Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
+ . ' Pushing policy to ' . $client->getUri());
+ }
+
+ $client->request(Zend_Http_Client::PUT);
+
+ $response = $client->getLastResponse();
+ if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
+ Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
+ . ' Response status: ' . $response->getStatus());
+ }
+
+ return $response->isSuccessful();
+ }
+
+ protected function _getPolicy(Tinebase_Model_FullUser $user): array
+ {
+ // TODO allow to configure defaults/flags
+
+ return [
+ "schemaVersion" => 2,
+ "flags" => [
+ "allowCustomUserDisplayNames" => true,
+ "allowCustomUserAvatars" => true,
+ "allowCustomPassthroughUserPasswords" => true,
+ "forbidRoomCreation" => false,
+ "forbidEncryptedRoomCreation" => false,
+ "forbidUnencryptedRoomCreation" => false
+ ],
+ "users" => [
+ $this->_getUserPolicy($user)
+ ],
+ ];
+ }
+
+ protected function _getUserPolicy(Tinebase_Model_FullUser $user): array
+ {
+
+ return [
+ "id" => $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID],
+ "active" => $user->is_deleted === 0 && $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE],
+ "displayName" => $user->accountDisplayName,
+ "forbidRoomCreation" => false,
+ "authType" => "sha1",
+ "authCredential" => Tinebase_User::getInstance()->getPasswordHashByLoginname($user->accountLoginName),
+// "authType" => "plain",
+// "avatarUri" => "https://example.com/john.jpg",
+// "joinedRooms" => [
+// {"roomId": "!roomA:example.com", "powerLevel": 0},
+// {"roomId": "!roomB:example.com", "powerLevel": 50}
+// ],
+ ];
+ }
+
+ public function getPushedPolicy(): array
+ {
+ return $this->_policy;
+ }
+}
diff --git a/tine20/MatrixSynapseIntegrator/Config.php b/tine20/MatrixSynapseIntegrator/Config.php
index 30c00e3f31a..fb394f5a1e4 100644
--- a/tine20/MatrixSynapseIntegrator/Config.php
+++ b/tine20/MatrixSynapseIntegrator/Config.php
@@ -1,4 +1,5 @@
- * @copyright Copyright (c) 2020 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright Copyright (c) 2020-2025 Metaways Infosystems GmbH (http://www.metaways.de)
*/
/**
@@ -18,9 +19,14 @@
*/
class MatrixSynapseIntegrator_Config extends Tinebase_Config_Abstract
{
- const APP_NAME = 'MatrixSynapseIntegrator';
+ public const APP_NAME = 'MatrixSynapseIntegrator';
+
+ public const MATRIX_DOMAIN = 'matrixDomain';
+ public const HOME_SERVER_URL = 'homeServerUrl';
+ public const CORPORAL_SHARED_AUTH_TOKEN = 'corporalSharedAuthToken';
- const MATRIX_DOMAIN = 'matrixDomain';
+ public const USER_XPROP_MATRIX_ID = 'matrixId';
+ public const USER_XPROP_MATRIX_ACTIVE = 'matrixActive';
/**
* (non-PHPdoc)
@@ -43,6 +49,28 @@ class MatrixSynapseIntegrator_Config extends Tinebase_Config_Abstract
self::SETBYADMINMODULE => true,
self::SETBYSETUPMODULE => true,
],
+ self::HOME_SERVER_URL => [
+ //_('Home Server URL')
+ self::LABEL => 'Home Server URL',
+ //_('Home Server URL')
+ self::DESCRIPTION => 'Home Server URL',
+ self::TYPE => self::TYPE_STRING,
+ self::CLIENTREGISTRYINCLUDE => true,
+ self::SETBYADMINMODULE => true,
+ self::SETBYSETUPMODULE => true,
+ self::DEFAULT_STR => 'https://matrix.mydomain',
+ ],
+ self::CORPORAL_SHARED_AUTH_TOKEN => [
+ //_('Corporal Shared Auth Token')
+ self::LABEL => 'Corporal Shared Auth Token',
+ //_('Corporal Shared Auth Token')
+ self::DESCRIPTION => 'Corporal Shared Auth Token',
+ self::TYPE => self::TYPE_STRING,
+ self::CLIENTREGISTRYINCLUDE => false,
+ self::SETBYADMINMODULE => false,
+ self::SETBYSETUPMODULE => false,
+ self::DEFAULT_STR => 'abc',
+ ],
];
/**
diff --git a/tine20/MatrixSynapseIntegrator/Controller.php b/tine20/MatrixSynapseIntegrator/Controller.php
index aa6bc636012..7493346335f 100644
--- a/tine20/MatrixSynapseIntegrator/Controller.php
+++ b/tine20/MatrixSynapseIntegrator/Controller.php
@@ -137,7 +137,9 @@ public function identity()
}
}
- if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' response: ' . print_r($result, true));
+ if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
+ Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' response: ' . print_r($result, true));
+ }
$response = (new \Zend\Diactoros\Response())->withHeader('Content-Type', 'application/json');
$response->getBody()->write(json_encode(isset($bodyMsg['lookup']['medium']) ?
@@ -265,4 +267,24 @@ public function getMatrixDomain()
return MatrixSynapseIntegrator_Config::getInstance()->{MatrixSynapseIntegrator_Config::MATRIX_DOMAIN} ?:
Tinebase_Core::getUrl(Tinebase_Core::GET_URL_HOST);
}
+
+ /**
+ * implement logic for each controller in this function
+ *
+ * @param Tinebase_Event_Abstract $_eventObject
+ */
+ protected function _handleEvent(Tinebase_Event_Abstract $_eventObject)
+ {
+ switch (get_class($_eventObject)) {
+ case Admin_Event_AddAccount::class:
+ MatrixSynapseIntegrator_Controller_User::getInstance()->create($_eventObject->account);
+ break;
+ case Admin_Event_UpdateAccount::class:
+ MatrixSynapseIntegrator_Controller_User::getInstance()->update($_eventObject->account, $_eventObject->oldAccount);
+ break;
+ case Tinebase_Event_User_DeleteAccount::class:
+ MatrixSynapseIntegrator_Controller_User::getInstance()->delete($_eventObject->account);
+ break;
+ }
+ }
}
diff --git a/tine20/MatrixSynapseIntegrator/Controller/User.php b/tine20/MatrixSynapseIntegrator/Controller/User.php
new file mode 100644
index 00000000000..c9c05eaa6b2
--- /dev/null
+++ b/tine20/MatrixSynapseIntegrator/Controller/User.php
@@ -0,0 +1,97 @@
+
+ */
+
+/**
+ * MatrixSynapseIntegrator Controller
+ *
+ * @package MatrixSynapseIntegrator
+ * @subpackage Controller
+ */
+class MatrixSynapseIntegrator_Controller_User extends Tinebase_Controller_Abstract
+{
+ use Tinebase_Controller_SingletonTrait;
+
+ protected ?MatrixSynapseIntegrator_Backend_Corporal $_backend = null;
+
+ public function setBackend(?MatrixSynapseIntegrator_Backend_Corporal $backend = null): MatrixSynapseIntegrator_Backend_Corporal
+ {
+ return $this->_backend = $backend ?: new MatrixSynapseIntegrator_Backend_Corporal();
+ }
+
+ public function getBackend(): MatrixSynapseIntegrator_Backend_Corporal
+ {
+ return $this->_backend ?: $this->setBackend();
+ }
+
+ public function create(Tinebase_Model_FullUser $user): bool
+ {
+ if ($this->_inactiveMatrixUser($user)) {
+ return true;
+ }
+
+ $this->_replaceUserIdXprop($user);
+ $this->getBackend()->push($user);
+
+ return true;
+ }
+
+ protected function _inactiveMatrixUser(Tinebase_Model_FullUser $user): bool
+ {
+ $matrixId = $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID] ?? null;
+ $active = $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE] ?? false;
+ return (! $active || empty($matrixId));
+ }
+
+ public function update(Tinebase_Model_FullUser $user, Tinebase_Model_FullUser $olduser): bool
+ {
+ $matrixId = $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID] ?? null;
+ $active = $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE] ?? false;
+ $oldMatrixId = $olduser->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID] ?? null;
+ $oldActive = $olduser->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE] ?? false;
+
+ if ($active === $oldActive && $matrixId == $oldMatrixId) {
+ return true;
+ }
+ $this->getBackend()->push($user);
+ return true;
+ }
+
+ public function delete(Tinebase_Model_FullUser $user): bool
+ {
+ if ($this->_inactiveMatrixUser($user)) {
+ return true;
+ }
+
+ $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE] = false;
+ $this->getBackend()->push($user);
+ return true;
+ }
+
+ /**
+ * update xprop - replace user id
+ *
+ * @param Tinebase_Model_FullUser $user
+ * @return void
+ * @throws Tinebase_Exception_InvalidArgument
+ * @throws Tinebase_Exception_Record_Validation
+ * @throws Tinebase_Exception_SystemGeneric
+ */
+ protected function _replaceUserIdXprop(Tinebase_Model_FullUser $user): void
+ {
+ $matrixId = $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID];
+ if (str_contains($matrixId, '{user.id}')) {
+ $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID] = str_replace('{user.id}',
+ $user->getId(), $matrixId);
+ Tinebase_User::getInstance()->updateUserInSqlBackend($user);
+ }
+ }
+}