diff --git a/tests/tine20/Admin/Controller/UserTest.php b/tests/tine20/Admin/Controller/UserTest.php
index 1bf5ba64f5..5c20bb3db5 100644
--- a/tests/tine20/Admin/Controller/UserTest.php
+++ b/tests/tine20/Admin/Controller/UserTest.php
@@ -321,6 +321,37 @@ public function testUpdateUserAdbContainer()
Addressbook_Controller_Contact::getInstance()->get($user->contact_id)->getIdFromProperty('container_id'));
}
+ public function testAddUserUpdateContact()
+ {
+ $userToCreate = TestCase::getTestUser();
+ $pw = Tinebase_Record_Abstract::generateUID(12);
+
+ $this->_usernamesToDelete[] = $userToCreate->accountLoginName;
+ $user = Admin_Controller_User::getInstance()->create($userToCreate, $pw, $pw);
+
+ $newContact = Addressbook_Controller_Contact::getInstance()->create(new Addressbook_Model_Contact([
+ 'n_given' => 'foo',
+ 'n_family' => 'test',
+ 'email' => 'foo@tine20.org',
+ 'tel_cell_private' => '+49TELCELLPRIVATE',
+ ]));
+
+ $oldContactId = $user->contact_id;
+ $user->contact_id = $newContact->getId();
+ $user = Admin_Controller_User::getInstance()->update($user, $pw, $pw);
+ static::assertEquals($newContact->getId(), $user->contact_id);
+
+ $oldContact = Addressbook_Controller_Contact::getInstance()->get($oldContactId);
+ static::assertEquals($oldContact->type, Addressbook_Model_Contact::CONTACTTYPE_CONTACT, 'old user contact should switch type to contact');
+
+ $newContact = Addressbook_Controller_Contact::getInstance()->get($user->contact_id);
+ static::assertEquals($newContact->type, Addressbook_Model_Contact::CONTACTTYPE_USER, 'new user contact should switch type to user');
+ static::assertEquals($newContact->email, $user->accountEmailAddress, 'new contact email should be the same as user email');
+ static::assertEquals($newContact->n_fn, $user->accountFullName);
+ static::assertEquals($newContact->n_fileas, $user->accountDisplayName);
+
+ }
+
public function testUpdateUserWithEmailButNoPassword()
{
$this->_skipWithoutEmailSystemAccountConfig();
diff --git a/tests/tine20/Admin/Frontend/Json/UserTest.php b/tests/tine20/Admin/Frontend/Json/UserTest.php
index bda0ccc247..995ffcc27a 100644
--- a/tests/tine20/Admin/Frontend/Json/UserTest.php
+++ b/tests/tine20/Admin/Frontend/Json/UserTest.php
@@ -273,6 +273,53 @@ public function testUpdateUserWithoutContainerACL()
self::assertEquals(2, $account['groups']['totalcount']);
}
+ /**
+ * testUpdateUserSendSMS
+ *
+ */
+ public function testUpdateUserSendSMS()
+ {
+ Tinebase_Config::getInstance()->{Tinebase_Config::SMS}->{Tinebase_Config::SMS_ADAPTERS} = [
+ Tinebase_Model_Sms_AdapterConfigs::FLD_ADAPTER_CONFIGS => [
+ [
+ Tinebase_Model_Sms_AdapterConfig::FLD_NAME => 'sms1',
+ Tinebase_Model_Sms_AdapterConfig::FLD_ADAPTER_CLASS => Tinebase_Model_Sms_GenericHttpAdapter::class,
+ Tinebase_Model_Sms_AdapterConfig::FLD_ADAPTER_CONFIG => [
+ Tinebase_Model_Sms_GenericHttpAdapter::FLD_URL => 'https://shoo.tld/restapi/message',
+ Tinebase_Model_Sms_GenericHttpAdapter::FLD_BODY => '{"encoding":"auto","body":"{{ message }}","originator":"{{ app.branding.title }}","recipients":["{{ cellphonenumber }}"],"route":"2345"}',
+ Tinebase_Model_Sms_GenericHttpAdapter::FLD_METHOD => 'POST',
+ Tinebase_Model_Sms_GenericHttpAdapter::FLD_HEADERS => [
+ 'Auth-Bearer' => 'unittesttokenshaaaaalalala'
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ $userArray = $this->_originalTestUser->toArray();
+
+ $userArray['send_password_via_sms'] = true;
+ $userArray['sms_phone_number'] = '777777777';
+ $userArray['accountPassword'] = 'tine20admin';
+
+ $smsConfig = Tinebase_Config::getInstance()->{Tinebase_Config::SMS}->{Tinebase_Config::SMS_ADAPTERS}
+ ?->{Tinebase_Model_Sms_AdapterConfigs::FLD_ADAPTER_CONFIGS}->getFirstRecord();
+ $smsAdapterConfig = $smsConfig ? $smsConfig->{Tinebase_Model_Sms_AdapterConfig::FLD_ADAPTER_CONFIG} : null;
+ $smsAdapterConfig->setHttpClientConfig([
+ 'adapter' => ($httpClientTestAdapter = new Tinebase_ZendHttpClientAdapter())
+ ]);
+ $httpClientTestAdapter->writeBodyCallBack = function($body) {
+ Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' sms request body: ' . $body);
+ };
+ $httpClientTestAdapter->setResponse(new Zend_Http_Response(200, []));
+
+ $result = $this->_json->saveUser($userArray);
+
+ $this->assertStringContainsString('Instance: ' . Tinebase_Config::getInstance()->get(Tinebase_Config::BRANDING_TITLE) . ' , new password: ' . $userArray['accountPassword'],
+ $httpClientTestAdapter->lastRequestBody);
+ $this->assertTrue($result['sms']['777777777']);
+ }
+
/**
* @param Tinebase_Model_FullUser $account
* @return Tinebase_Model_Container
diff --git a/tine20/Addressbook/Controller.php b/tine20/Addressbook/Controller.php
index fe2f3ee3a2..3253bb92e4 100644
--- a/tine20/Addressbook/Controller.php
+++ b/tine20/Addressbook/Controller.php
@@ -88,6 +88,16 @@ protected function _handleEvent(Tinebase_Event_Abstract $_eventObject)
case 'Admin_Event_AddAccount':
$this->createPersonalFolder($_eventObject->account);
break;
+ case Admin_Event_UpdateAccount::class:
+ /** @var Admin_Event_UpdateAccount $_eventObject */
+ if ($_eventObject->account->contact_id !== $_eventObject->oldAccount->contact_id) {
+ $contact = Addressbook_Controller_Contact::getInstance()->get($_eventObject->oldAccount->contact_id);
+ $contact->type = Addressbook_Model_Contact::CONTACTTYPE_CONTACT;
+ $contact = Addressbook_Controller_Contact::getInstance()->update($contact);
+ if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__
+ . ' switch deprecated user contact type to contact : ' . $contact->getId());
+ }
+ break;
case 'Tinebase_Event_User_DeleteAccount':
/**
* @var Tinebase_Event_User_DeleteAccount $_eventObject
diff --git a/tine20/Addressbook/Controller/Contact.php b/tine20/Addressbook/Controller/Contact.php
index 05d631c9b7..9ddc12519c 100644
--- a/tine20/Addressbook/Controller/Contact.php
+++ b/tine20/Addressbook/Controller/Contact.php
@@ -688,7 +688,7 @@ protected function _inspectBeforeUpdate($_record, $_oldRecord)
$this->_checkAndSetShortName($_record, $_oldRecord);
- if (isset($_oldRecord->type) && $_oldRecord->type == Addressbook_Model_Contact::CONTACTTYPE_USER) {
+ if (isset($_oldRecord->type) && $_oldRecord->type == Addressbook_Model_Contact::CONTACTTYPE_USER && empty($_record->type)) {
$_record->type = Addressbook_Model_Contact::CONTACTTYPE_USER;
}
diff --git a/tine20/Admin/Frontend/Json.php b/tine20/Admin/Frontend/Json.php
index 9cb71fa922..915851df3b 100644
--- a/tine20/Admin/Frontend/Json.php
+++ b/tine20/Admin/Frontend/Json.php
@@ -449,6 +449,67 @@ public function saveUser($recordData)
'totalcount' => count($userRoles)
);
+
+ if (!empty($password) && isset($recordData['sms_phone_number'])) {
+ $smsAdapterConfigs = Tinebase_Config::getInstance()->{Tinebase_Config::SMS}->{Tinebase_Config::SMS_ADAPTERS}
+ ?->{Tinebase_Model_Sms_AdapterConfigs::FLD_ADAPTER_CONFIGS};
+
+ if (!$smsAdapterConfigs || count($smsAdapterConfigs) === 0) {
+ if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
+ __METHOD__ . '::' . __LINE__ . ' sms adapter configs is not found , skip sending new password message');
+ } else {
+ $smsAdapterConfig = $smsAdapterConfigs->getFirstRecord();
+ $smsAdapterConfig = $smsAdapterConfig->{Tinebase_Model_Sms_AdapterConfig::FLD_ADAPTER_CONFIG};
+
+ if (empty($smsAdapterConfig->getHttpClientConfig())) {
+ $smsAdapterConfig->setHttpClientConfig([
+ 'adapter' => ($genericHttpAdapter = new Tinebase_ZendHttpClientAdapter())
+ ]);
+
+ $genericHttpAdapter->writeBodyCallBack = function($body) {
+ $colorGreen = "\033[43m";
+ $colorReset = "\033[0m";
+ Tinebase_Core::getLogger()->warn($colorGreen . __METHOD__ . '::' . __LINE__ . ' sms request body: ' . $body . $colorReset . PHP_EOL);
+ };
+ $genericHttpAdapter->setResponse(new Zend_Http_Response(200, []));
+ }
+
+ $template = Tinebase_Config::getInstance()->{Tinebase_Config::SMS}->{Tinebase_Config::SMS_MESSAGE_TEMPLATES}->get(Tinebase_Config::SMS_TEMPLATE_NEW_PASSWORD);
+ $locale = Tinebase_Core::getLocale();
+ if (! $locale) {
+ $locale = Tinebase_Translation::getLocale();
+ }
+ $twig = new Tinebase_Twig($locale, Tinebase_Translation::getTranslation(), [
+ Tinebase_Twig::TWIG_LOADER =>
+ new Tinebase_Twig_CallBackLoader(__METHOD__ . 'password', time() - 1, function () use ($template) {
+ return $template;
+ })
+ ]);
+ $message = $twig->load(__METHOD__ . 'password')->render([
+ 'instanceName' => Tinebase_Config::getInstance()->get(Tinebase_Config::BRANDING_TITLE),
+ 'password' => $password,
+ ]);
+
+ $smsSendConfig = new Tinebase_Model_Sms_SendConfig([
+ Tinebase_Model_Sms_SendConfig::FLD_MESSAGE => $message,
+ Tinebase_Model_Sms_SendConfig::FLD_RECIPIENT_NUMBER => $recordData['sms_phone_number'],
+ Tinebase_Model_Sms_SendConfig::FLD_ADAPTER_CLASS => Tinebase_Model_Sms_GenericHttpAdapter::class,
+ Tinebase_Model_Sms_SendConfig::FLD_ADAPTER_CONFIG => $smsAdapterConfig,
+ ]);
+
+ if (Tinebase_Sms::send($smsSendConfig)) {
+ try {
+ $result['sms'] = [
+ $recordData['sms_phone_number'] => true,
+ ];
+ } catch (Zend_Session_Exception $zse) {
+ if (Tinebase_Core::isLogLevel(Zend_Log::WARN))
+ Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' ' . $zse->getMessage()) ;
+ }
+ }
+ }
+ }
+
Tinebase_Core::setExecutionLifeTime($oldMaxExcecutionTime);
return $result;
diff --git a/tine20/Admin/js/user/EditDialog.js b/tine20/Admin/js/user/EditDialog.js
index 955430941b..5616b96c92 100644
--- a/tine20/Admin/js/user/EditDialog.js
+++ b/tine20/Admin/js/user/EditDialog.js
@@ -54,7 +54,7 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
* @private
*/
initComponent: function () {
- var accountBackend = Tine.Tinebase.registry.get('accountBackend');
+ const accountBackend = Tine.Tinebase.registry.get('accountBackend');
this.ldapBackend = (accountBackend === 'Ldap' || accountBackend === 'ActiveDirectory');
this.twingEnv = getTwingEnv();
@@ -62,6 +62,7 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
for (const [fieldName, template] of Object.entries(Tine.Tinebase.configManager.get('accountTwig'))) {
loader.setTemplate(fieldName, template);
}
+ this.hasSmsAdapters = Tine.Tinebase.registry.get('hasSmsAdapters');
Tine.Admin.UserEditDialog.superclass.initComponent.call(this);
},
@@ -134,6 +135,13 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
this.getForm().findField('personalFSQuota').setValue(xprops.personalFSQuota);
}
+ this.defaultContainer = this.record.get('container_id');
+
+ if (this.hasSmsAdapters) {
+ const contact = this.record.get('contact_id');
+ this.loadSMSContactPhoneNumbers(contact);
+ }
+
this.mustChangePasswordCheck()
Tine.Admin.UserEditDialog.superclass.onRecordLoad.call(this);
@@ -161,6 +169,17 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
this.mustChangeTriggerPlugin.setVisible(true)
},
+
+ loadSMSContactPhoneNumbers(contact) {
+ const phoneFields = _.sortBy(_.filter(Tine.Addressbook.Model.Contact.getModelConfiguration().fields, (field) => {
+ return field?.specialType === 'Addressbook_Model_ContactProperties_Phone' && contact?.[field.fieldName];
+ }), (field) => {return _.get(field, 'uiconfig.sort')});
+ const mobilePhones = phoneFields.map((phoneField) => {
+ return [phoneField.fieldName, contact?.[phoneField.fieldName], `${contact?.[phoneField.fieldName]} [${phoneField.label}]`];
+ });
+ this.phoneCombo.store.loadData(mobilePhones);
+ },
+
/**
* @private
*/
@@ -220,11 +239,21 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
this.record.set('password_must_change', this.record.get('password_must_change_actual'))
}
- var xprops = this.record.get('xprops');
+ let xprops = this.record.get('xprops');
+ this.unsetLocalizedDateTimeFields(this.record, ['accountLastLogin', 'accountLastPasswordChange']);
+
+ const sendPwdViaSMS = this.getForm().findField('send_password_via_sms').getValue();
+ const smsPhoneNumber = this.getForm().findField('sms_phone_number').getValue();
+
+ if (sendPwdViaSMS && smsPhoneNumber) {
+ this.record.set('sms_phone_number', smsPhoneNumber);
+ }
+
xprops = Ext.isObject(xprops) ? xprops : {};
xprops.personalFSQuota = this.getForm().findField('personalFSQuota').getValue();
Tine.Tinebase.common.assertComparable(xprops);
this.record.set('xprops', xprops);
+
},
/**
* need to unset localized datetime fields before saving
@@ -923,7 +952,43 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
qtip: i18n._('Password is expired in accordance with the password policy and needs to be changed'),
preserveElStyle: true
})
+
this.saveInaddressbookFields = this.getSaveInAddessbookFields(this);
+ this.saveInaddressbookFields.push({
+ hideLabel: true,
+ xtype: 'checkbox',
+ boxLabel: this.app.i18n.gettext('Send password via SMS'),
+ hidden: !this.hasSmsAdapters,
+ disabled: true,
+ ctCls: 'admin-checkbox',
+ fieldClass: 'admin-checkbox-box',
+ name: 'send_password_via_sms',
+ listeners: {
+ scope: this,
+ check: function(cb, checked) {
+ const checkbox = this.getForm().findField('sms_phone_number');
+ checkbox.setDisabled(!checked);
+ }
+ }
+ },{
+ xtype: 'combo',
+ fieldLabel: this.app.i18n.gettext('Mobile Phone'),
+ name: 'sms_phone_number',
+ hidden: !this.hasSmsAdapters,
+ disabled: true,
+ ref: '../../../../../phoneCombo',
+ store: new Ext.data.ArrayStore({
+ idIndex: 0,
+ fields: ['name', 'value', 'display_value']
+ }),
+ mode: 'local',
+ triggerAction: 'all',
+ editable: true,
+ valueField: 'value',
+ displayField: 'display_value',
+ forceSelection: false,
+ });
+
this.saveInaddressbookFields.push({
hideLabel: true,
xtype: 'checkbox',
@@ -1024,6 +1089,8 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
if (this.passwordConfirmWindow) {
field.passwordsMatch = false;
}
+ const checkbox = this.getForm().findField('send_password_via_sms');
+ if (checkbox) checkbox.setDisabled(false);
}
},
validateValue: function (value) {
@@ -1036,6 +1103,7 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
checkboxToggle: false,
columnWidth: 1,
layout: 'hfit',
+ style: 'margin-bottom: 8px',
items: [this.MFAPanel]
}], [{
vtype: 'email',
@@ -1052,7 +1120,7 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
fieldLabel: this.app.i18n.gettext('OpenID'),
emptyText: '(' + this.app.i18n.gettext('Login name') + ')',
name: 'openid',
- columnWidth: 0.5
+ columnWidth: 0.5,
}], [{
xtype: 'tinerecordpickercombobox',
fieldLabel: this.app.i18n.gettext('Primary group'),
@@ -1240,10 +1308,10 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
validateLoginName: function (value) {
return value.match(/^[a-z\d._-]+$/i) !== null;
},
-
+
getSaveInAddessbookFields(scope, hidden) {
this.app = Tine.Tinebase.appMgr.get('Admin');
-
+
return [{
xtype: 'combo',
fieldLabel: this.app.i18n.gettext('Visibility'),
@@ -1259,11 +1327,18 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
scope: scope,
select: function (combo, record) {
// disable container_id combo if hidden
- var addressbookContainerCombo = scope.getForm().findField('container_id');
- addressbookContainerCombo.setDisabled(record.data.field1 === 'hidden');
+ const hidden = record.data.field1 === 'hidden';
+ const addressbookContainerCombo = scope.getForm().findField('container_id');
+ addressbookContainerCombo.setDisabled(hidden);
if (addressbookContainerCombo.getValue() === '') {
addressbookContainerCombo.setValue(null);
}
+ // disable container_id combo if hidden
+ const addressbookContactCombo = scope.getForm().findField('contact_id');
+ addressbookContactCombo.setDisabled(hidden);
+ if (addressbookContactCombo.getValue() === '') {
+ addressbookContactCombo.setValue(null);
+ }
}
}
}, {
@@ -1279,6 +1354,18 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
hidden: hidden ?? false,
recordProxy: Tine.Admin.sharedAddressbookBackend,
listeners: {
+ beforeselect: (combo, status, index) => {
+ Ext.MessageBox.confirm(
+ scope.app.i18n._('Confirm'),
+ scope.app.i18n._('Do you want to move the contact to this addressbook?'),
+ (btn) => {
+ if (btn === 'yes') {
+ combo.setValue(status.id);
+ }
+ },
+ );
+ return false;
+ },
specialkey: function(combo, e) {
if (e.getKey() == e.TAB && ! e.shiftKey) {
// move cursor to first input field (skip display fields)
@@ -1290,7 +1377,43 @@ Tine.Admin.UserEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
},
scope: scope
}
- }];
+ }, {
+ xtype: 'addressbookcontactpicker',
+ disabled: scope.record.get('visibility') === 'hidden',
+ hidden: hidden ?? false,
+ name: 'contact_id',
+ filter: [{field: 'type', operator: 'equals', value: 'contact'}],
+ listeners: {
+ scope: scope,
+ beforeselect: (combo, status, index) => {
+ const addressbookContainerCombo = scope.getForm().findField('container_id');
+ const selectedContainer = addressbookContainerCombo.selectedRecord;
+
+ let msg = scope.app.i18n._('The selected contact will be updated') + ` :
${status.data.n_fileas}
`
+ + '
' + this.app.i18n.gettext('Saved in Addressbook') + ` : ${selectedContainer.data.name}
`;
+
+ Ext.MessageBox.confirm(
+ scope.app.i18n._('Confirm'),
+ scope.app.i18n._(msg),
+ (btn) => {
+ if (btn === 'yes') {
+ combo.setValue(status.id);
+ if (scope.hasSmsAdapters) {
+ scope.loadSMSContactPhoneNumbers(status.data);
+ }
+ }
+ },
+ );
+ return false;
+ },
+ select: (combo, status, index) => {
+ if (scope.hasSmsAdapters && status === '') {
+ scope.loadSMSContactPhoneNumbers('');
+ }
+ },
+ }
+ }
+ ];
}
});
diff --git a/tine20/Tinebase/Application.php b/tine20/Tinebase/Application.php
index 109508c599..a2ba4d49ff 100644
--- a/tine20/Tinebase/Application.php
+++ b/tine20/Tinebase/Application.php
@@ -685,6 +685,8 @@ public function removeApplicationAuxiliaryData(Tinebase_Model_Application $_appl
);
foreach ($dataToDelete as $dataType => $info) {
+ Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Deleting ' . $dataType . ' for app'
+ . $_application->name . ' ...');
switch ($dataType) {
case 'container':
$count = Tinebase_Container::getInstance()->dropContainerByApplicationId($_application->getId());
diff --git a/tine20/Tinebase/Config.php b/tine20/Tinebase/Config.php
index 4f1d281e80..9afca1562c 100644
--- a/tine20/Tinebase/Config.php
+++ b/tine20/Tinebase/Config.php
@@ -743,6 +743,8 @@ class Tinebase_Config extends Tinebase_Config_Abstract
public const SMS = 'sms';
public const SMS_ADAPTERS = 'sms_adapters';
+ public const SMS_MESSAGE_TEMPLATES = 'sms_message_templates';
+ public const SMS_TEMPLATE_NEW_PASSWORD = 'sms_template_new_password';
/**
* max username length
@@ -3606,6 +3608,27 @@ class Tinebase_Config extends Tinebase_Config_Abstract
self::CONTENT_CLASS => Tinebase_Model_Sms_AdapterConfigs::class,
self::DEFAULT_STR => [],
],
+ self::SMS_MESSAGE_TEMPLATES => [
+ self::LABEL => 'SMS Message Templates', //_('SMS Message Templates')
+ self::DESCRIPTION => 'SMS Message Templates', //_('SMS Message Templates')
+ self::TYPE => self::TYPE_OBJECT,
+ self::CLASSNAME => Tinebase_Config_Struct::class,
+ self::CONTENT => [
+ self::SMS_TEMPLATE_NEW_PASSWORD => [
+ //_('Template for SMS New Password')
+ self::LABEL => 'Template for SMS New Password',
+ //_('Template for SMS New Password with parameters: instanceName, password')
+ self::DESCRIPTION => 'Template for SMS New Password with parameters: instanceName, password',
+ self::TYPE => self::TYPE_STRING,
+ //_('Instance: {{ instanceName }} , new password: {{ password }}')
+ self::DEFAULT_STR => 'Instance: {{ instanceName }} , new password: {{ password }}',
+ self::CLIENTREGISTRYINCLUDE => true,
+ self::SETBYADMINMODULE => true,
+ self::SETBYSETUPMODULE => false,
+ ]
+ ],
+ self::DEFAULT_STR => [],
+ ],
],
self::DEFAULT_STR => [],
],
diff --git a/tine20/Tinebase/Controller.php b/tine20/Tinebase/Controller.php
index 20657bf940..8115289b8a 100644
--- a/tine20/Tinebase/Controller.php
+++ b/tine20/Tinebase/Controller.php
@@ -150,11 +150,6 @@ public function login($loginName, $password, \Zend\Http\PhpEnvironment\Request $
Tinebase_Controller::getInstance()->changeUserAccount($roleChangeUserName);
}
- $loginEvent = new Tinebase_Event_User_Login();
- $loginEvent->password = $password;
- $loginEvent->user = $user;
- Tinebase_Event::fireEvent($loginEvent);
-
return true;
}
@@ -772,7 +767,6 @@ protected function _handleEvent(Tinebase_Event_Abstract $_eventObject)
}
}
break;
-
case Tinebase_Event_User_Login::class:
if (($userCtrl = Tinebase_User::getInstance()) instanceof Tinebase_User_Interface_SyncAble
&& Tinebase_Config::getInstance()->{Tinebase_Config::USERBACKEND}->{Tinebase_Config::SYNCOPTIONS}->{Tinebase_Config::SYNC_USER_OF_GROUPS}
diff --git a/tine20/Tinebase/Core.php b/tine20/Tinebase/Core.php
index 87cd9dc5c4..10198c4978 100644
--- a/tine20/Tinebase/Core.php
+++ b/tine20/Tinebase/Core.php
@@ -2363,9 +2363,16 @@ public static function getHttpClient($uri = null, $config = null)
}
}
+ $resolvedConfig = $config;
+ if (isset($config['adapter']) && is_callable($config['adapter']->writeBodyCallBack)) {
+ $adapterConfig = clone $config['adapter'];
+ unset($adapterConfig->writeBodyCallBack);
+ $resolvedConfig['adapter'] = $adapterConfig;
+ }
+
if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
__METHOD__ . '::' . __LINE__ . ' Creating Zend_Http_Client for ' . $uri . ' with config: '
- . print_r($config, true));
+ . print_r($resolvedConfig, true));
return new Zend_Http_Client($uri, $config);
}
diff --git a/tine20/Tinebase/Frontend/Json.php b/tine20/Tinebase/Frontend/Json.php
index 91c6e639fb..0db6eb2a33 100644
--- a/tine20/Tinebase/Frontend/Json.php
+++ b/tine20/Tinebase/Frontend/Json.php
@@ -839,7 +839,10 @@ protected function _getUserRegistryData()
$smtpConfig = $manageSmtpEmailUser
? Tinebase_EmailUser::getConfig(Tinebase_Config::SMTP, true)
: [];
-
+ $smsAdapterConfig = Tinebase_Config::getInstance()->{Tinebase_Config::SMS}->{Tinebase_Config::SMS_ADAPTERS}->{Tinebase_Model_Sms_AdapterConfigs::FLD_ADAPTER_CONFIGS} ?? [];
+ if ($smsAdapterConfig instanceof Tinebase_Record_RecordSet) {
+ $smsAdapterConfig = $smsAdapterConfig->toArray();
+ }
// be license class for setting some license registry data
$license = Tinebase_License::getInstance();
@@ -866,6 +869,7 @@ protected function _getUserRegistryData()
'additionaldomains' => isset($smtpConfig['additionaldomains']) ? $smtpConfig['additionaldomains'] : '',
'allowExternalEmail' => ! $manageImapEmailUser || Tinebase_Config::getInstance()->get(Tinebase_Config::IMAP)->allowExternalEmail,
'smtpAliasesDispatchFlag' => Tinebase_EmailUser::smtpAliasesDispatchFlag(),
+ 'hasSmsAdapters' => count($smsAdapterConfig) > 0,
);
if (Tinebase_Core::get(Tinebase_Core::SESSION)->encourage_mfa) {
diff --git a/tine20/Tinebase/Model/FullUser.php b/tine20/Tinebase/Model/FullUser.php
index 015cbfde1c..cac85c4eff 100644
--- a/tine20/Tinebase/Model/FullUser.php
+++ b/tine20/Tinebase/Model/FullUser.php
@@ -147,6 +147,11 @@ class Tinebase_Model_FullUser extends Tinebase_Model_User
self::MODEL_NAME => Addressbook_Model_Contact::MODEL_NAME_PART,
],
'validators' => [Zend_Filter_Input::ALLOW_EMPTY => true],
+ self::UI_CONFIG => [
+ 'recordEditPluginConfig' => [
+ 'allowCreateNew' => true,
+ ],
+ ],
],
'accountLastLogin' => [
'type' => 'datetime',
diff --git a/tine20/Tinebase/Model/Sms/GenericHttpAdapter.php b/tine20/Tinebase/Model/Sms/GenericHttpAdapter.php
index 821e4ca620..5cd8281b3d 100644
--- a/tine20/Tinebase/Model/Sms/GenericHttpAdapter.php
+++ b/tine20/Tinebase/Model/Sms/GenericHttpAdapter.php
@@ -58,6 +58,11 @@ class Tinebase_Model_Sms_GenericHttpAdapter extends Tinebase_Record_NewAbstract
protected array $_httpClientConfig = [];
+ public function getHttpClientConfig()
+ {
+ return $this->_httpClientConfig;
+ }
+
public function setHttpClientConfig(array $_config): void
{
$this->_httpClientConfig = $_config;
diff --git a/tine20/Tinebase/Sms.php b/tine20/Tinebase/Sms.php
index 36e402c9e1..be0ffd380c 100644
--- a/tine20/Tinebase/Sms.php
+++ b/tine20/Tinebase/Sms.php
@@ -20,7 +20,7 @@ class Tinebase_Sms
public static function send(Tinebase_Model_Sms_SendConfig $config): bool
{
if (!$config->{Tinebase_Model_Sms_SendConfig::FLD_ADAPTER_CONFIG}) {
- // TODO get default sms adapter config or throw
+ throw new Tinebase_Exception_UnexpectedValue('sms send config needs to be set');
}
return $config->{Tinebase_Model_Sms_SendConfig::FLD_ADAPTER_CONFIG}->send($config);
diff --git a/tine20/Tinebase/User.php b/tine20/Tinebase/User.php
index 4f7d73c36a..959ad2c0d0 100644
--- a/tine20/Tinebase/User.php
+++ b/tine20/Tinebase/User.php
@@ -919,6 +919,7 @@ public static function user2Contact($user, $contact = null)
}
$contact->account_id = $user->getId();
+ $contact->type = Addressbook_Model_Contact::CONTACTTYPE_USER;
return $contact;
}
diff --git a/tine20/Tinebase/js/widgets/ActivitiesPanel.js b/tine20/Tinebase/js/widgets/ActivitiesPanel.js
index eb0bf0bc63..00eac8378c 100644
--- a/tine20/Tinebase/js/widgets/ActivitiesPanel.js
+++ b/tine20/Tinebase/js/widgets/ActivitiesPanel.js
@@ -129,7 +129,7 @@ Tine.widgets.activities.ActivitiesTabPanel = Ext.extend(Ext.Panel, {
},
noteRenderer: function(note) {
- var editDialog = this.findParentBy(function(c){return !!c.record}),
+ const editDialog = this.findParentBy(function(c){return !!c.record}),
record = editDialog ? editDialog.record : {},
recordClass = Tine.Tinebase.data.RecordMgr.get(this.record_model) || Tine.Tinebase.data.RecordMgr.get(this.app + '_Model_' + this.record_model),
appName = recordClass.getMeta('appName'),
@@ -142,14 +142,13 @@ Tine.widgets.activities.ActivitiesTabPanel = Ext.extend(Ext.Panel, {
if (recordClass) {
Ext.each(recordClass.getFieldDefinitions(), function(field) {
- var _ = window.lodash,
- i18nLabel = field.label ? i18n._hidden(field.label) : field.name,
- regexp = new RegExp(' (' + _.escapeRegExp(field.name) +'|' + _.escapeRegExp(i18nLabel) + `) \\((.*?) (->) ([^)]*)\\)`),
- struct = regexp.exec(note),
- label = struct && struct.length == 5 ? struct[1] : null,
- oldValue = label ? struct[2] : null,
- newValue = label ? struct[4] : null,
- renderer = Tine.widgets.grid.RendererManager.get(appName, recordClass, field.name, Tine.widgets.grid.RendererManager.CATEGORY_GRIDPANEL);
+ const i18nLabel = field.label ? i18n._hidden(field.label) : field.name;
+ const renderer = Tine.widgets.grid.RendererManager.get(appName, recordClass, field.name, Tine.widgets.grid.RendererManager.CATEGORY_GRIDPANEL);
+ let regexp = new RegExp(' (' + _.escapeRegExp(field.name) +'|' + _.escapeRegExp(i18nLabel) + `) \\((.*?) (->) ([^)]*)\\)`);
+ let struct = regexp.exec(note);
+ let label = struct && struct.length === 5 ? struct[1] : null;
+ let oldValue = label ? struct[2] : null;
+ let newValue = label ? struct[4] : null;
if (label) {
if (['record', 'records'].indexOf(_.get(field, 'fieldDefinition.type')) < 0) {
@@ -163,8 +162,8 @@ Tine.widgets.activities.ActivitiesTabPanel = Ext.extend(Ext.Panel, {
regexp = new RegExp(' (' + _.escapeRegExp(field.name) +'|' + _.escapeRegExp(i18nLabel) + ') \\((.[^)])\\)');
struct = regexp.exec(note);
- label = struct && struct.length == 3 ? struct[1] : null;
- var value = label ? struct[2] : null;
+ label = struct && struct.length === 3 ? struct[1] : null;
+ const value = label ? struct[2] : null;
note = note.replace(regexp, '
\u00A0\u2022\u00A0 ' + i18nLabel + ' (' + value + ')');
}
diff --git a/tine20/Tinebase/translations/de.po b/tine20/Tinebase/translations/de.po
index ad5f8e8814..63816e0960 100644
--- a/tine20/Tinebase/translations/de.po
+++ b/tine20/Tinebase/translations/de.po
@@ -1056,6 +1056,18 @@ msgstr ""
msgid "SMS Adapter Configs"
msgstr ""
+msgid "SMS Message Templates"
+msgstr ""
+
+msgid "Template for SMS New Password"
+msgstr "Schablone für SMS Neues Passwort"
+
+msgid "Template for SMS New Password with parameters: instanceName, password"
+msgstr "Schablone für SMS Neues Passwort mit Parameter: instanceName, password "
+
+msgid "Instance: {{ instanceName }} , new password: {{ password }}"
+msgstr "Instanz: {{ instanceName }} , neues Passwort: {{ password }}"
+
msgid "User type"
msgstr "Nutzer*innen Typ"
diff --git a/tine20/Tinebase/translations/en.po b/tine20/Tinebase/translations/en.po
index 3629ba333f..fe9bbf3dca 100644
--- a/tine20/Tinebase/translations/en.po
+++ b/tine20/Tinebase/translations/en.po
@@ -5,7 +5,6 @@ msgstr ""
"PO-Revision-Date: 2008-07-29 21:14+0100\n"
"Last-Translator: Cornelius Weiss \n"
"Language-Team: Tine 2.0 Translators\n"
-"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -1040,6 +1039,18 @@ msgstr "SMS Config"
msgid "SMS Adapter Configs"
msgstr "SMS Adapter Configs"
+msgid "SMS Message Templates"
+msgstr "SMS Message Templates"
+
+msgid "Template for SMS New Password"
+msgstr "Template for SMS New Password"
+
+msgid "Template for SMS New Password with parameters: instanceName, password"
+msgstr "Template for SMS New Password with parameters: instanceName, password"
+
+msgid "Instance: {{ instanceName }} , new password: {{ password }}"
+msgstr "Instance: {{ instanceName }} , new password: {{ password }}"
+
msgid "User type"
msgstr "User type"
diff --git a/tine20/Tinebase/translations/template.pot b/tine20/Tinebase/translations/template.pot
index d891c2d118..abfbf2e927 100644
--- a/tine20/Tinebase/translations/template.pot
+++ b/tine20/Tinebase/translations/template.pot
@@ -1039,6 +1039,18 @@ msgstr ""
msgid "SMS Adapter Configs"
msgstr ""
+msgid "SMS Message Templates"
+msgstr ""
+
+msgid "Template for SMS New Password"
+msgstr ""
+
+msgid "Template for SMS New Password with parameters: instanceName, password"
+msgstr ""
+
+msgid "Instance: {{ instanceName }} , new password: {{ password }}"
+msgstr ""
+
msgid "User type"
msgstr ""