mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Added: Plugin: Contacts suggestion from ldap
Added: Custom favicon setting + Small fixes
This commit is contained in:
parent
d6d19c99bf
commit
7312b2854b
25 changed files with 526 additions and 25 deletions
|
|
@ -78,6 +78,16 @@
|
||||||
<param name="plugin-name" value="hmailserver-change-password"/>
|
<param name="plugin-name" value="hmailserver-change-password"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
</target>
|
</target>
|
||||||
|
<target name="ldap-change-password">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="ldap-change-password"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="ldap-contacts-suggestions">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="ldap-contacts-suggestions"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
<target name="snowfall-on-login-screen">
|
<target name="snowfall-on-login-screen">
|
||||||
<antcall target="_build_plugin_">
|
<antcall target="_build_plugin_">
|
||||||
<param name="plugin-name" value="snowfall-on-login-screen"/>
|
<param name="plugin-name" value="snowfall-on-login-screen"/>
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,9 @@
|
||||||
this.loadingDesc = ko.observable(Settings.settingsGet('LoadingDescription'));
|
this.loadingDesc = ko.observable(Settings.settingsGet('LoadingDescription'));
|
||||||
this.loadingDesc.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
|
this.loadingDesc.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
|
||||||
|
|
||||||
|
this.faviconUrl = ko.observable(Settings.settingsGet('FaviconUrl'));
|
||||||
|
this.faviconUrl.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
|
||||||
|
|
||||||
this.loginLogo = ko.observable(Settings.settingsGet('LoginLogo') || '');
|
this.loginLogo = ko.observable(Settings.settingsGet('LoginLogo') || '');
|
||||||
this.loginLogo.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
|
this.loginLogo.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
|
||||||
|
|
||||||
|
|
@ -88,7 +91,8 @@
|
||||||
|
|
||||||
var
|
var
|
||||||
f1 = Utils.settingsSaveHelperSimpleFunction(self.title.trigger, self),
|
f1 = Utils.settingsSaveHelperSimpleFunction(self.title.trigger, self),
|
||||||
f2 = Utils.settingsSaveHelperSimpleFunction(self.loadingDesc.trigger, self)
|
f2 = Utils.settingsSaveHelperSimpleFunction(self.loadingDesc.trigger, self),
|
||||||
|
f3 = Utils.settingsSaveHelperSimpleFunction(self.faviconUrl.trigger, self)
|
||||||
;
|
;
|
||||||
|
|
||||||
self.title.subscribe(function (sValue) {
|
self.title.subscribe(function (sValue) {
|
||||||
|
|
@ -103,6 +107,12 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.faviconUrl.subscribe(function (sValue) {
|
||||||
|
Remote.saveAdminConfig(f3, {
|
||||||
|
'FaviconUrl': Utils.trim(sValue)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
}, 50);
|
}, 50);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "RainLoop",
|
"name": "RainLoop",
|
||||||
"title": "RainLoop Webmail",
|
"title": "RainLoop Webmail",
|
||||||
"version": "1.9.2",
|
"version": "1.9.2",
|
||||||
"release": "357",
|
"release": "361",
|
||||||
"description": "Simple, modern & fast web-based email client",
|
"description": "Simple, modern & fast web-based email client",
|
||||||
"homepage": "http://rainloop.net",
|
"homepage": "http://rainloop.net",
|
||||||
"main": "gulpfile.js",
|
"main": "gulpfile.js",
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
"plugins"
|
"plugins"
|
||||||
],
|
],
|
||||||
"readmeFilename": "README.md",
|
"readmeFilename": "README.md",
|
||||||
"ownCloudPackageVersion": "4.6",
|
"ownCloudPackageVersion": "4.7",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.10.0"
|
"node": ">= 0.10.0"
|
||||||
},
|
},
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
"rimraf": "*",
|
"rimraf": "*",
|
||||||
"jshint-summary": "*",
|
"jshint-summary": "*",
|
||||||
"webpack": "*",
|
"webpack": "*",
|
||||||
"gulp": "*",
|
"gulp": "~3.9.0",
|
||||||
"gulp-util": "*",
|
"gulp-util": "*",
|
||||||
"gulp-uglify": "*",
|
"gulp-uglify": "*",
|
||||||
"gulp-rimraf": "*",
|
"gulp-rimraf": "*",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,11 @@ class ChangePasswordLdapDriver implements \RainLoop\Providers\ChangePassword\Cha
|
||||||
*/
|
*/
|
||||||
private $sHostName = '127.0.0.1';
|
private $sHostName = '127.0.0.1';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $iHostPort = 389;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
|
|
@ -34,15 +39,17 @@ class ChangePasswordLdapDriver implements \RainLoop\Providers\ChangePassword\Cha
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sHostName
|
* @param string $sHostName
|
||||||
|
* @param int $iHostPort
|
||||||
* @param string $sUserDnFormat
|
* @param string $sUserDnFormat
|
||||||
* @param string $sPasswordField
|
* @param string $sPasswordField
|
||||||
* @param string $sPasswordEncType
|
* @param string $sPasswordEncType
|
||||||
*
|
*
|
||||||
* @return \ChangePasswordLdapDriver
|
* @return \ChangePasswordLdapDriver
|
||||||
*/
|
*/
|
||||||
public function SetConfig($sHostName, $sUserDnFormat, $sPasswordField, $sPasswordEncType)
|
public function SetConfig($sHostName, $iHostPort, $sUserDnFormat, $sPasswordField, $sPasswordEncType)
|
||||||
{
|
{
|
||||||
$this->sHostName = $sHostName;
|
$this->sHostName = $sHostName;
|
||||||
|
$this->iHostPort = $iHostPort;
|
||||||
$this->sUserDnFormat = $sUserDnFormat;
|
$this->sUserDnFormat = $sUserDnFormat;
|
||||||
$this->sPasswordField = $sPasswordField;
|
$this->sPasswordField = $sPasswordField;
|
||||||
$this->sPasswordEncType = $sPasswordEncType;
|
$this->sPasswordEncType = $sPasswordEncType;
|
||||||
|
|
@ -114,7 +121,7 @@ class ChangePasswordLdapDriver implements \RainLoop\Providers\ChangePassword\Cha
|
||||||
'{imap:port}' => $oAccount->DomainIncPort()
|
'{imap:port}' => $oAccount->DomainIncPort()
|
||||||
));
|
));
|
||||||
|
|
||||||
$oCon = @\ldap_connect($this->sHostName);
|
$oCon = @\ldap_connect($this->sHostName, $this->iHostPort);
|
||||||
if ($oCon)
|
if ($oCon)
|
||||||
{
|
{
|
||||||
@\ldap_set_option($oCon, LDAP_OPT_PROTOCOL_VERSION, 3);
|
@\ldap_set_option($oCon, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||||
|
|
@ -133,22 +140,31 @@ class ChangePasswordLdapDriver implements \RainLoop\Providers\ChangePassword\Cha
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sSshaSalt = '';
|
||||||
|
$sShaPrefix = '{SHA}';
|
||||||
$sEncodedNewPassword = $sNewPassword;
|
$sEncodedNewPassword = $sNewPassword;
|
||||||
switch (\strtolower($this->sPasswordEncType))
|
switch (\strtolower($this->sPasswordEncType))
|
||||||
{
|
{
|
||||||
|
case 'ssha':
|
||||||
|
$sSshaSalt = $this->getSalt(4);
|
||||||
|
$sShaPrefix = '{SSHA}';
|
||||||
case 'sha':
|
case 'sha':
|
||||||
switch (true)
|
switch (true)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case \function_exists('sha1'):
|
case \function_exists('sha1'):
|
||||||
$sEncodedNewPassword = '{SHA}'.\base64_encode(\pack('H*', \sha1($sNewPassword)));
|
$sEncodedNewPassword = $sShaPrefix.\base64_encode(\sha1($sNewPassword.$sSshaSalt, true).$sSshaSalt);
|
||||||
break;
|
break;
|
||||||
case \function_exists('hash'):
|
case \function_exists('hash'):
|
||||||
$sEncodedNewPassword = '{SHA}'.\base64_encode(\hash('sha1', $sNewPassword, true));
|
$sEncodedNewPassword = $sShaPrefix.\base64_encode(\hash('sha1', $sNewPassword, true).$sSshaSalt);
|
||||||
break;
|
break;
|
||||||
case \function_exists('mhash') && defined('MHASH_SHA1'):
|
case \function_exists('mhash') && defined('MHASH_SHA1'):
|
||||||
$sEncodedNewPassword = '{SHA}'.\base64_encode(\mhash(MHASH_SHA1, $sNewPassword));
|
$sEncodedNewPassword = $sShaPrefix.\base64_encode(\mhash(MHASH_SHA1, $sNewPassword).$sSshaSalt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1.0
|
1.1
|
||||||
|
|
@ -31,18 +31,19 @@ class LdapChangePasswordPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||||
case 'change-password':
|
case 'change-password':
|
||||||
|
|
||||||
$sHostName = \trim($this->Config()->Get('plugin', 'hostname', ''));
|
$sHostName = \trim($this->Config()->Get('plugin', 'hostname', ''));
|
||||||
|
$iHostPort = (int) $this->Config()->Get('plugin', 'port', 389);
|
||||||
$sUserDnFormat = \trim($this->Config()->Get('plugin', 'user_dn_format', ''));
|
$sUserDnFormat = \trim($this->Config()->Get('plugin', 'user_dn_format', ''));
|
||||||
$sPasswordField = \trim($this->Config()->Get('plugin', 'password_field', ''));
|
$sPasswordField = \trim($this->Config()->Get('plugin', 'password_field', ''));
|
||||||
$sPasswordEncType = \trim($this->Config()->Get('plugin', 'password_enc_type', ''));
|
$sPasswordEncType = \trim($this->Config()->Get('plugin', 'password_enc_type', ''));
|
||||||
|
|
||||||
if (!empty($sHostName) && !empty($sUserDnFormat) && !empty($sPasswordField) && !empty($sPasswordEncType))
|
if (!empty($sHostName) && 0 < $iHostPort && !empty($sUserDnFormat) && !empty($sPasswordField) && !empty($sPasswordEncType))
|
||||||
{
|
{
|
||||||
include_once __DIR__.'/ChangePasswordLdapDriver.php';
|
include_once __DIR__.'/ChangePasswordLdapDriver.php';
|
||||||
|
|
||||||
$oProvider = new \ChangePasswordLdapDriver();
|
$oProvider = new \ChangePasswordLdapDriver();
|
||||||
|
|
||||||
$oProvider
|
$oProvider
|
||||||
->SetConfig($sHostName, $sUserDnFormat, $sPasswordField, $sPasswordEncType)
|
->SetConfig($sHostName, $iHostPort, $sUserDnFormat, $sPasswordField, $sPasswordEncType)
|
||||||
->SetAllowedEmails(\strtolower(\trim($this->Config()->Get('plugin', 'allowed_emails', ''))))
|
->SetAllowedEmails(\strtolower(\trim($this->Config()->Get('plugin', 'allowed_emails', ''))))
|
||||||
->SetLogger($this->Manager()->Actions()->Logger())
|
->SetLogger($this->Manager()->Actions()->Logger())
|
||||||
;
|
;
|
||||||
|
|
@ -59,6 +60,9 @@ class LdapChangePasswordPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||||
return array(
|
return array(
|
||||||
\RainLoop\Plugins\Property::NewInstance('hostname')->SetLabel('LDAP hostname')
|
\RainLoop\Plugins\Property::NewInstance('hostname')->SetLabel('LDAP hostname')
|
||||||
->SetDefaultValue('127.0.0.1'),
|
->SetDefaultValue('127.0.0.1'),
|
||||||
|
\RainLoop\Plugins\Property::NewInstance('port')->SetLabel('LDAP port')
|
||||||
|
->SetType(\RainLoop\Enumerations\PluginPropertyType::INT)
|
||||||
|
->SetDefaultValue(389),
|
||||||
\RainLoop\Plugins\Property::NewInstance('user_dn_format')->SetLabel('User DN format')
|
\RainLoop\Plugins\Property::NewInstance('user_dn_format')->SetLabel('User DN format')
|
||||||
->SetDescription('LDAP user dn format. Supported tokens: {email}, {login}, {domain}, {domain:dc}, {imap:login}, {imap:host}, {imap:port}')
|
->SetDescription('LDAP user dn format. Supported tokens: {email}, {login}, {domain}, {domain:dc}, {imap:login}, {imap:host}, {imap:port}')
|
||||||
->SetDefaultValue('uid={imap:login},ou=Users,{domain:dc}'),
|
->SetDefaultValue('uid={imap:login},ou=Users,{domain:dc}'),
|
||||||
|
|
@ -66,7 +70,7 @@ class LdapChangePasswordPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||||
->SetDefaultValue('userPassword'),
|
->SetDefaultValue('userPassword'),
|
||||||
\RainLoop\Plugins\Property::NewInstance('password_enc_type')->SetLabel('Encryption type')
|
\RainLoop\Plugins\Property::NewInstance('password_enc_type')->SetLabel('Encryption type')
|
||||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::SELECTION)
|
->SetType(\RainLoop\Enumerations\PluginPropertyType::SELECTION)
|
||||||
->SetDefaultValue(array('SHA', 'MD5', 'Crypt', 'Clear')),
|
->SetDefaultValue(array('SHA', 'SSHA', 'MD5', 'Crypt', 'Clear')),
|
||||||
\RainLoop\Plugins\Property::NewInstance('allowed_emails')->SetLabel('Allowed emails')
|
\RainLoop\Plugins\Property::NewInstance('allowed_emails')->SetLabel('Allowed emails')
|
||||||
->SetDescription('Allowed emails, space as delimiter, wildcard supported. Example: user1@domain1.net user2@domain1.net *@domain2.net')
|
->SetDescription('Allowed emails, space as delimiter, wildcard supported. Example: user1@domain1.net user2@domain1.net *@domain2.net')
|
||||||
->SetDefaultValue('*')
|
->SetDefaultValue('*')
|
||||||
|
|
|
||||||
20
plugins/ldap-contacts-suggestions/LICENSE
Normal file
20
plugins/ldap-contacts-suggestions/LICENSE
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 RainLoop Team
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
317
plugins/ldap-contacts-suggestions/LdapContactsSuggestions.php
Normal file
317
plugins/ldap-contacts-suggestions/LdapContactsSuggestions.php
Normal file
|
|
@ -0,0 +1,317 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class LdapContactsSuggestions implements \RainLoop\Providers\Suggestions\ISuggestions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $sHostName = '127.0.0.1';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $iHostPort = 389;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $sAccessDn = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $sAccessPassword = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $sUsersDn = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $sObjectClass = 'inetOrgPerson';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $sNameField = 'givenname';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $sEmailField = 'mail';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \MailSo\Log\Logger
|
||||||
|
*/
|
||||||
|
private $oLogger = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $sAllowedEmails = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sHostName
|
||||||
|
* @param int $iHostPort
|
||||||
|
* @param string $sAccessDn
|
||||||
|
* @param string $sAccessPassword
|
||||||
|
* @param string $sUsersDn
|
||||||
|
* @param string $sObjectClass
|
||||||
|
* @param string $sNameField
|
||||||
|
* @param string $sEmailField
|
||||||
|
*
|
||||||
|
* @return \LdapContactsSuggestions
|
||||||
|
*/
|
||||||
|
public function SetConfig($sHostName, $iHostPort, $sAccessDn, $sAccessPassword, $sUsersDn, $sObjectClass, $sNameField, $sEmailField)
|
||||||
|
{
|
||||||
|
$this->sHostName = $sHostName;
|
||||||
|
$this->iHostPort = $iHostPort;
|
||||||
|
$this->sAccessDn = $sAccessDn;
|
||||||
|
$this->sAccessPassword = $sAccessPassword;
|
||||||
|
$this->sUsersDn = $sUsersDn;
|
||||||
|
$this->sObjectClass = $sObjectClass;
|
||||||
|
$this->sNameField = $sNameField;
|
||||||
|
$this->sEmailField = $sEmailField;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sAllowedEmails
|
||||||
|
*
|
||||||
|
* @return \LdapContactsSuggestions
|
||||||
|
*/
|
||||||
|
public function SetAllowedEmails($sAllowedEmails)
|
||||||
|
{
|
||||||
|
$this->sAllowedEmails = $sAllowedEmails;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \RainLoop\Model\Account $oAccount
|
||||||
|
* @param string $sQuery
|
||||||
|
* @param int $iLimit = 20
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function Process($oAccount, $sQuery, $iLimit = 20)
|
||||||
|
{
|
||||||
|
$sQuery = \trim($sQuery);
|
||||||
|
|
||||||
|
if (2 > \strlen($sQuery))
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
else if (!$oAccount || !\RainLoop\Plugins\Helper::ValidateWildcardValues($oAccount->Email(), $this->sAllowedEmails))
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$aResult = $this->ldapSearch($oAccount, $sQuery);
|
||||||
|
|
||||||
|
$aResult = \RainLoop\Utils::RemoveSuggestionDuplicates($aResult);
|
||||||
|
if ($iLimit < \count($aResult))
|
||||||
|
{
|
||||||
|
$aResult = \array_slice($aResult, 0, $iLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $aLdapItem
|
||||||
|
* @param array $aEmailFields
|
||||||
|
* @param array $aNameFields
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function findNameAndEmail($aLdapItem, $aEmailFields, $aNameFields)
|
||||||
|
{
|
||||||
|
$sEmail = $sName = '';
|
||||||
|
if ($aLdapItem)
|
||||||
|
{
|
||||||
|
foreach ($aEmailFields as $sField)
|
||||||
|
{
|
||||||
|
if (!empty($aLdapItem[$sField][0]))
|
||||||
|
{
|
||||||
|
$sEmail = \trim($aLdapItem[$sField][0]);
|
||||||
|
if (!empty($sEmail))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($aNameFields as $sField)
|
||||||
|
{
|
||||||
|
if (!empty($aLdapItem[$sField][0]))
|
||||||
|
{
|
||||||
|
$sName = \trim($aLdapItem[$sField][0]);
|
||||||
|
if (!empty($sName))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array($sEmail, $sName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \RainLoop\Model\Account $oAccount
|
||||||
|
* @param string $sQuery
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function ldapSearch($oAccount, $sQuery)
|
||||||
|
{
|
||||||
|
$sSearchEscaped = $this->escape($sQuery);
|
||||||
|
|
||||||
|
$aResult = array();
|
||||||
|
$oCon = @\ldap_connect($this->sHostName, $this->iHostPort);
|
||||||
|
if ($oCon)
|
||||||
|
{
|
||||||
|
$this->oLogger->Write('ldap_connect: connected', \MailSo\Log\Enumerations\Type::INFO, 'LDAP');
|
||||||
|
|
||||||
|
@\ldap_set_option($oCon, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||||
|
|
||||||
|
if (!@\ldap_bind($oCon, $this->sAccessDn, $this->sAccessPassword))
|
||||||
|
{
|
||||||
|
$this->logLdapError($oCon, 'ldap_bind');
|
||||||
|
return $aResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oAccount->Email());
|
||||||
|
$sSearchDn = \strtr($this->sUsersDn, array(
|
||||||
|
'{domain}' => $sDomain,
|
||||||
|
'{domain:dc}' => 'dc='.\strtr($sDomain, array('.' => ',dc=')),
|
||||||
|
'{email}' => $oAccount->Email(),
|
||||||
|
'{email:user}' => \MailSo\Base\Utils::GetAccountNameFromEmail($oAccount->Email()),
|
||||||
|
'{email:domain}' => $sDomain,
|
||||||
|
'{login}' => $oAccount->Login(),
|
||||||
|
'{imap:login}' => $oAccount->Login(),
|
||||||
|
'{imap:host}' => $oAccount->DomainIncHost(),
|
||||||
|
'{imap:port}' => $oAccount->DomainIncPort()
|
||||||
|
));
|
||||||
|
|
||||||
|
$aEmails = empty($this->sEmailField) ? array() : \explode(',', $this->sEmailField);
|
||||||
|
$aNames = empty($this->sNameField) ? array() : \explode(',', $this->sNameField);
|
||||||
|
|
||||||
|
$aEmails = \array_map('trim', $aEmails);
|
||||||
|
$aNames = \array_map('trim', $aNames);
|
||||||
|
|
||||||
|
$aFields = \array_merge($aEmails, $aNames);
|
||||||
|
|
||||||
|
$aItems = array();
|
||||||
|
$sSubFilter = '';
|
||||||
|
foreach ($aFields as $sItem)
|
||||||
|
{
|
||||||
|
if (!empty($sItem))
|
||||||
|
{
|
||||||
|
$aItems[] = $sItem;
|
||||||
|
$sSubFilter .= '('.$sItem.'=*'.$sSearchEscaped.'*)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sFilter = '(&(objectclass='.$this->sObjectClass.')';
|
||||||
|
$sFilter .= (1 < count($aItems) ? '(|' : '').$sSubFilter.(1 < count($aItems) ? ')' : '');
|
||||||
|
$sFilter .= ')';
|
||||||
|
|
||||||
|
$this->oLogger->Write('ldap_search: start: '.$sSearchDn.' / '.$sFilter, \MailSo\Log\Enumerations\Type::INFO, 'LDAP');
|
||||||
|
$oS = @\ldap_search($oCon, $sSearchDn, $sFilter, $aItems, 0, 30, 30);
|
||||||
|
if ($oS)
|
||||||
|
{
|
||||||
|
$aEntries = @\ldap_get_entries($oCon, $oS);
|
||||||
|
if (is_array($aEntries))
|
||||||
|
{
|
||||||
|
if (isset($aEntries['count']))
|
||||||
|
{
|
||||||
|
unset($aEntries['count']);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($aEntries as $aItem)
|
||||||
|
{
|
||||||
|
if ($aItem)
|
||||||
|
{
|
||||||
|
$sName = $sEmail = '';
|
||||||
|
list ($sEmail, $sName) = $this->findNameAndEmail($aItem, $aEmails, $aNames);
|
||||||
|
if (!empty($sEmail))
|
||||||
|
{
|
||||||
|
$aResult[] = array($sEmail, $sName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->logLdapError($oCon, 'ldap_get_entries');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->logLdapError($oCon, 'ldap_search');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return $aResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sStr
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function escape($sStr)
|
||||||
|
{
|
||||||
|
$aNewChars = array();
|
||||||
|
$aChars = array('\\', '*', '(', ')', \chr(0));
|
||||||
|
|
||||||
|
foreach ($aChars as $iIndex => $sValue)
|
||||||
|
{
|
||||||
|
$aNewChars[$iIndex] = '\\'.\str_pad(\dechex(\ord($sValue)), 2, '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
return \str_replace($aChars, $aNewChars, $sStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $oCon
|
||||||
|
* @param string $sCmd
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function logLdapError($oCon, $sCmd)
|
||||||
|
{
|
||||||
|
if ($this->oLogger)
|
||||||
|
{
|
||||||
|
$sError = $oCon ? @\ldap_error($oCon) : '';
|
||||||
|
$iErrno = $oCon ? @\ldap_errno($oCon) : 0;
|
||||||
|
|
||||||
|
$this->oLogger->Write($sCmd.' error: '.$sError.' ('.$iErrno.')',
|
||||||
|
\MailSo\Log\Enumerations\Type::WARNING, 'LDAP');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \MailSo\Log\Logger $oLogger
|
||||||
|
*
|
||||||
|
* @return \LdapContactsSuggestions
|
||||||
|
*/
|
||||||
|
public function SetLogger($oLogger)
|
||||||
|
{
|
||||||
|
if ($oLogger instanceof \MailSo\Log\Logger)
|
||||||
|
{
|
||||||
|
$this->oLogger = $oLogger;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
plugins/ldap-contacts-suggestions/VERSION
Normal file
1
plugins/ldap-contacts-suggestions/VERSION
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
1.0
|
||||||
92
plugins/ldap-contacts-suggestions/index.php
Normal file
92
plugins/ldap-contacts-suggestions/index.php
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class LdapContactsSuggestionsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||||
|
{
|
||||||
|
public function Init()
|
||||||
|
{
|
||||||
|
$this->addHook('main.fabrica', 'MainFabrica');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function Supported()
|
||||||
|
{
|
||||||
|
if (!\function_exists('ldap_connect'))
|
||||||
|
{
|
||||||
|
return 'The LDAP PHP exention must be installed to use this plugin';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sName
|
||||||
|
* @param mixed $mResult
|
||||||
|
*/
|
||||||
|
public function MainFabrica($sName, &$mResult)
|
||||||
|
{
|
||||||
|
switch ($sName)
|
||||||
|
{
|
||||||
|
case 'suggestions':
|
||||||
|
|
||||||
|
if (!\is_array($mResult))
|
||||||
|
{
|
||||||
|
$mResult = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$sHostName = \trim($this->Config()->Get('plugin', 'hostname', ''));
|
||||||
|
$iHostPort = (int) $this->Config()->Get('plugin', 'port', 389);
|
||||||
|
$sAccessDn = \trim($this->Config()->Get('plugin', 'access_dn', ''));
|
||||||
|
$sAccessPassword = \trim($this->Config()->Get('plugin', 'access_password', ''));
|
||||||
|
$sUsersDn = \trim($this->Config()->Get('plugin', 'users_dn_format', ''));
|
||||||
|
$sObjectClass = \trim($this->Config()->Get('plugin', 'object_class', ''));
|
||||||
|
$sNameField = \trim($this->Config()->Get('plugin', 'name_field', ''));
|
||||||
|
$sEmailField = \trim($this->Config()->Get('plugin', 'mail_field', ''));
|
||||||
|
|
||||||
|
if (0 < \strlen($sAccessDn) && 0 < \strlen($sAccessPassword) && 0 < \strlen($sUsersDn) &&
|
||||||
|
0 < \strlen($sObjectClass) && 0 < \strlen($sEmailField))
|
||||||
|
{
|
||||||
|
include_once __DIR__.'/LdapContactsSuggestions.php';
|
||||||
|
|
||||||
|
$oProvider = new LdapContactsSuggestions();
|
||||||
|
$oProvider->SetConfig($sHostName, $iHostPort, $sAccessDn, $sAccessPassword, $sUsersDn, $sObjectClass, $sNameField, $sEmailField);
|
||||||
|
|
||||||
|
$mResult[] = $oProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function configMapping()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
\RainLoop\Plugins\Property::NewInstance('hostname')->SetLabel('LDAP hostname')
|
||||||
|
->SetDefaultValue('127.0.0.1'),
|
||||||
|
\RainLoop\Plugins\Property::NewInstance('port')->SetLabel('LDAP port')
|
||||||
|
->SetType(\RainLoop\Enumerations\PluginPropertyType::INT)
|
||||||
|
->SetDefaultValue(389),
|
||||||
|
\RainLoop\Plugins\Property::NewInstance('access_dn')->SetLabel('Access dn (login)')
|
||||||
|
->SetDefaultValue(''),
|
||||||
|
\RainLoop\Plugins\Property::NewInstance('access_password')->SetLabel('Access password')
|
||||||
|
->SetType(\RainLoop\Enumerations\PluginPropertyType::PASSWORD)
|
||||||
|
->SetDefaultValue(''),
|
||||||
|
\RainLoop\Plugins\Property::NewInstance('users_dn_format')->SetLabel('Users DN format')
|
||||||
|
->SetDescription('LDAP users dn format. Supported tokens: {email}, {login}, {domain}, {domain:dc}, {imap:login}, {imap:host}, {imap:port}')
|
||||||
|
->SetDefaultValue('ou=People,dc=domain,dc=com'),
|
||||||
|
\RainLoop\Plugins\Property::NewInstance('object_class')->SetLabel('objectClass value')
|
||||||
|
->SetDefaultValue('inetOrgPerson'),
|
||||||
|
\RainLoop\Plugins\Property::NewInstance('name_field')->SetLabel('Name field')
|
||||||
|
->SetDefaultValue('givenname'),
|
||||||
|
\RainLoop\Plugins\Property::NewInstance('mail_field')->SetLabel('Mail field')
|
||||||
|
->SetDefaultValue('mail'),
|
||||||
|
\RainLoop\Plugins\Property::NewInstance('allowed_emails')->SetLabel('Allowed emails')
|
||||||
|
->SetDescription('Allowed emails, space as delimiter, wildcard supported. Example: user1@domain1.net user2@domain1.net *@domain2.net')
|
||||||
|
->SetDefaultValue('*')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -175,6 +175,11 @@ END;
|
||||||
public static function NormalizeCharset($sEncoding, $bAsciAsUtf8 = false)
|
public static function NormalizeCharset($sEncoding, $bAsciAsUtf8 = false)
|
||||||
{
|
{
|
||||||
$sEncoding = \strtolower($sEncoding);
|
$sEncoding = \strtolower($sEncoding);
|
||||||
|
|
||||||
|
$sEncoding = \preg_replace('/^iso8/', 'iso-8', $sEncoding);
|
||||||
|
$sEncoding = \preg_replace('/^cp-([\d])/', 'cp$1', $sEncoding);
|
||||||
|
$sEncoding = \preg_replace('/^windows?12/', 'windows-12', $sEncoding);
|
||||||
|
|
||||||
switch ($sEncoding)
|
switch ($sEncoding)
|
||||||
{
|
{
|
||||||
case 'asci':
|
case 'asci':
|
||||||
|
|
@ -185,6 +190,8 @@ END;
|
||||||
\MailSo\Base\Enumerations\Charset::ISO_8859_1;
|
\MailSo\Base\Enumerations\Charset::ISO_8859_1;
|
||||||
break;
|
break;
|
||||||
case 'unicode-1-1-utf-7':
|
case 'unicode-1-1-utf-7':
|
||||||
|
case 'unicode-1-utf-7':
|
||||||
|
case 'unicode-utf-7':
|
||||||
$sEncoding = \MailSo\Base\Enumerations\Charset::UTF_7;
|
$sEncoding = \MailSo\Base\Enumerations\Charset::UTF_7;
|
||||||
break;
|
break;
|
||||||
case 'utf8':
|
case 'utf8':
|
||||||
|
|
|
||||||
|
|
@ -1378,6 +1378,7 @@ class Actions
|
||||||
'Title' => 'RainLoop Webmail',
|
'Title' => 'RainLoop Webmail',
|
||||||
'LoadingDescription' => 'RainLoop',
|
'LoadingDescription' => 'RainLoop',
|
||||||
'LoadingDescriptionEsc' => 'RainLoop',
|
'LoadingDescriptionEsc' => 'RainLoop',
|
||||||
|
'FaviconUrl' => '',
|
||||||
'LoginDescription' => '',
|
'LoginDescription' => '',
|
||||||
'LoginPowered' => true,
|
'LoginPowered' => true,
|
||||||
'LoginLogo' => '',
|
'LoginLogo' => '',
|
||||||
|
|
@ -1472,6 +1473,7 @@ class Actions
|
||||||
|
|
||||||
$aResult['Title'] = $oConfig->Get('webmail', 'title', '');
|
$aResult['Title'] = $oConfig->Get('webmail', 'title', '');
|
||||||
$aResult['LoadingDescription'] = $oConfig->Get('webmail', 'loading_description', '');
|
$aResult['LoadingDescription'] = $oConfig->Get('webmail', 'loading_description', '');
|
||||||
|
$aResult['FaviconUrl'] = $oConfig->Get('webmail', 'favicon_url', '');
|
||||||
|
|
||||||
if ($oPremProvider)
|
if ($oPremProvider)
|
||||||
{
|
{
|
||||||
|
|
@ -3686,6 +3688,7 @@ class Actions
|
||||||
|
|
||||||
$this->setConfigFromParams($oConfig, 'Title', 'webmail', 'title', 'string');
|
$this->setConfigFromParams($oConfig, 'Title', 'webmail', 'title', 'string');
|
||||||
$this->setConfigFromParams($oConfig, 'LoadingDescription', 'webmail', 'loading_description', 'string');
|
$this->setConfigFromParams($oConfig, 'LoadingDescription', 'webmail', 'loading_description', 'string');
|
||||||
|
$this->setConfigFromParams($oConfig, 'FaviconUrl', 'webmail', 'favicon_url', 'string');
|
||||||
|
|
||||||
$this->setConfigFromParams($oConfig, 'TokenProtection', 'security', 'csrf_protection', 'bool');
|
$this->setConfigFromParams($oConfig, 'TokenProtection', 'security', 'csrf_protection', 'bool');
|
||||||
$this->setConfigFromParams($oConfig, 'EnabledPlugins', 'plugins', 'enable', 'bool');
|
$this->setConfigFromParams($oConfig, 'EnabledPlugins', 'plugins', 'enable', 'bool');
|
||||||
|
|
@ -7030,7 +7033,7 @@ class Actions
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$aResult = \RainLoop\Utils::RemoveSuggestionsdDuplicates($aResult);
|
$aResult = \RainLoop\Utils::RemoveSuggestionDuplicates($aResult);
|
||||||
if ($iLimit < \count($aResult))
|
if ($iLimit < \count($aResult))
|
||||||
{
|
{
|
||||||
$aResult = \array_slice($aResult, 0, $iLimit);
|
$aResult = \array_slice($aResult, 0, $iLimit);
|
||||||
|
|
@ -7038,7 +7041,7 @@ class Actions
|
||||||
|
|
||||||
$this->Plugins()->RunHook('ajax.suggestions-post', array(&$aResult, $sQuery, $oAccount, $iLimit));
|
$this->Plugins()->RunHook('ajax.suggestions-post', array(&$aResult, $sQuery, $oAccount, $iLimit));
|
||||||
|
|
||||||
$aResult = \RainLoop\Utils::RemoveSuggestionsdDuplicates($aResult);
|
$aResult = \RainLoop\Utils::RemoveSuggestionDuplicates($aResult);
|
||||||
if ($iLimit < \count($aResult))
|
if ($iLimit < \count($aResult))
|
||||||
{
|
{
|
||||||
$aResult = \array_slice($aResult, 0, $iLimit);
|
$aResult = \array_slice($aResult, 0, $iLimit);
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ class Application extends \RainLoop\Config\AbstractConfig
|
||||||
|
|
||||||
'title' => array('RainLoop Webmail', 'Text displayed as page title'),
|
'title' => array('RainLoop Webmail', 'Text displayed as page title'),
|
||||||
'loading_description' => array('RainLoop', 'Text displayed on startup'),
|
'loading_description' => array('RainLoop', 'Text displayed on startup'),
|
||||||
|
'favicon_url' => array('', ''),
|
||||||
|
|
||||||
'theme' => array('Default', 'Theme used by default'),
|
'theme' => array('Default', 'Theme used by default'),
|
||||||
'allow_themes' => array(true, 'Allow theme selection on settings screen'),
|
'allow_themes' => array(true, 'Allow theme selection on settings screen'),
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ class Suggestions extends \RainLoop\Providers\AbstractProvider
|
||||||
*/
|
*/
|
||||||
public function Process($oAccount, $sQuery, $iLimit = 20)
|
public function Process($oAccount, $sQuery, $iLimit = 20)
|
||||||
{
|
{
|
||||||
|
|
||||||
$aSuggestions = array();
|
$aSuggestions = array();
|
||||||
if ($oAccount instanceof \RainLoop\Model\Account &&
|
if ($oAccount instanceof \RainLoop\Model\Account &&
|
||||||
$this->IsActive() && \is_array($this->aDrivers) && 0 < \strlen($sQuery))
|
$this->IsActive() && \is_array($this->aDrivers) && 0 < \strlen($sQuery))
|
||||||
|
|
@ -54,7 +53,7 @@ class Suggestions extends \RainLoop\Providers\AbstractProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$aResult = \RainLoop\Utils::RemoveSuggestionsdDuplicates($aSuggestions);
|
$aResult = \RainLoop\Utils::RemoveSuggestionDuplicates($aSuggestions);
|
||||||
|
|
||||||
if ($iLimit < \count($aResult))
|
if ($iLimit < \count($aResult))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -235,14 +235,15 @@ class Service
|
||||||
$bAppJsDebug = !!$this->oActions->Config()->Get('labs', 'use_app_debug_js', false);
|
$bAppJsDebug = !!$this->oActions->Config()->Get('labs', 'use_app_debug_js', false);
|
||||||
$bAppCssDebug = !!$this->oActions->Config()->Get('labs', 'use_app_debug_css', false);
|
$bAppCssDebug = !!$this->oActions->Config()->Get('labs', 'use_app_debug_css', false);
|
||||||
|
|
||||||
|
$sFaviconUrl = (string) $this->oActions->Config()->Get('webmail', 'favicon_url', '');
|
||||||
|
|
||||||
$sStaticPrefix = \RainLoop\Utils::WebStaticPath();
|
$sStaticPrefix = \RainLoop\Utils::WebStaticPath();
|
||||||
|
|
||||||
$aData = array(
|
$aData = array(
|
||||||
'Language' => $sLanguage,
|
'Language' => $sLanguage,
|
||||||
'Theme' => $sTheme,
|
'Theme' => $sTheme,
|
||||||
'FaviconIcoLink' => $sStaticPrefix.'favicon.ico',
|
'FaviconPngLink' => $sFaviconUrl ? $sFaviconUrl : $sStaticPrefix.'favicon.png',
|
||||||
'FaviconPngLink' => $sStaticPrefix.'favicon.png',
|
'AppleTouchLink' => $sFaviconUrl ? '' : $sStaticPrefix.'apple-touch-icon.png',
|
||||||
'AppleTouchLink' => $sStaticPrefix.'apple-touch-icon.png',
|
|
||||||
'AppCssLink' => $sStaticPrefix.'css/app'.($bAppCssDebug ? '' : '.min').'.css',
|
'AppCssLink' => $sStaticPrefix.'css/app'.($bAppCssDebug ? '' : '.min').'.css',
|
||||||
'BootJsLink' => $sStaticPrefix.'js/min/boot.js',
|
'BootJsLink' => $sStaticPrefix.'js/min/boot.js',
|
||||||
'ComponentsJsLink' => $sStaticPrefix.'js/'.($bAppJsDebug ? '' : 'min/').'components.js',
|
'ComponentsJsLink' => $sStaticPrefix.'js/'.($bAppJsDebug ? '' : 'min/').'components.js',
|
||||||
|
|
@ -255,8 +256,8 @@ class Service
|
||||||
|
|
||||||
$aTemplateParameters = array(
|
$aTemplateParameters = array(
|
||||||
'{{BaseAppDataScriptLink}}' => ($bAdmin ? './?/AdminAppData/' : './?/AppData/'),
|
'{{BaseAppDataScriptLink}}' => ($bAdmin ? './?/AdminAppData/' : './?/AppData/'),
|
||||||
'{{BaseAppFaviconIcoFile}}' => $aData['FaviconIcoLink'],
|
'{{BaseAppFaviconPngLinkTag}}' => $aData['FaviconPngLink'] ? '<link rel="shortcut icon" href="'.$aData['FaviconPngLink'].'" type="image/png" />' : '',
|
||||||
'{{BaseAppFaviconPngFile}}' => $aData['FaviconPngLink'],
|
'{{BaseAppFaviconTouchLinkTag}}' => $aData['AppleTouchLink'] ? '<link rel="apple-touch-icon" href="'.$aData['AppleTouchLink'].'" type="image/png" />' : '',
|
||||||
'{{BaseAppAppleTouchFile}}' => $aData['AppleTouchLink'],
|
'{{BaseAppAppleTouchFile}}' => $aData['AppleTouchLink'],
|
||||||
'{{BaseAppMainCssLink}}' => $aData['AppCssLink'],
|
'{{BaseAppMainCssLink}}' => $aData['AppCssLink'],
|
||||||
'{{BaseAppBootScriptLink}}' => $aData['BootJsLink'],
|
'{{BaseAppBootScriptLink}}' => $aData['BootJsLink'],
|
||||||
|
|
|
||||||
|
|
@ -613,7 +613,7 @@ class Utils
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function RemoveSuggestionsdDuplicates($aSuggestions)
|
public static function RemoveSuggestionDuplicates($aSuggestions)
|
||||||
{
|
{
|
||||||
$aResult = array();
|
$aResult = array();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,7 @@
|
||||||
<meta name="app-version" content="{{BaseVersion}}" id="rlAppVersion" />
|
<meta name="app-version" content="{{BaseVersion}}" id="rlAppVersion" />
|
||||||
<title></title>
|
<title></title>
|
||||||
<style>#rl-content{display:none;}</style>
|
<style>#rl-content{display:none;}</style>
|
||||||
<link rel="shortcut icon" href="{{BaseAppFaviconPngFile}}" type="image/png" />
|
{{BaseAppFaviconPngLinkTag}}{{BaseAppFaviconTouchLinkTag}}
|
||||||
<link rel="apple-touch-icon" href="{{BaseAppAppleTouchFile}}" type="image/png" />
|
|
||||||
<link type="text/css" rel="stylesheet" href="{{BaseAppMainCssLink}}" />
|
<link type="text/css" rel="stylesheet" href="{{BaseAppMainCssLink}}" />
|
||||||
<link type="text/css" rel="stylesheet" id="rlThemeLink" />
|
<link type="text/css" rel="stylesheet" id="rlThemeLink" />
|
||||||
<script data-cfasync="false" src="{{BaseAppBootScriptLink}}"></script>
|
<script data-cfasync="false" src="{{BaseAppBootScriptLink}}"></script>
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,20 @@
|
||||||
}"></div>
|
}"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label" data-i18n="TAB_BRANDING/LABEL_FAVICON_URL"></label>
|
||||||
|
<div class="controls">
|
||||||
|
<div data-bind="component: {
|
||||||
|
name: 'Input',
|
||||||
|
params: {
|
||||||
|
value: faviconUrl,
|
||||||
|
trigger: faviconUrl.trigger,
|
||||||
|
placeholder: 'https://',
|
||||||
|
size: 5
|
||||||
|
}
|
||||||
|
}"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<ul class="nav nav-tabs" data-bind="visible: !community">
|
<ul class="nav nav-tabs" data-bind="visible: !community">
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ LABEL_DETERMINE_USER_LANGUAGE = "Versuche Sprache des Benutzers zu ermitteln"
|
||||||
LEGEND_BRANDING = "Branding"
|
LEGEND_BRANDING = "Branding"
|
||||||
LABEL_PAGE_TITLE = "Seitentitel"
|
LABEL_PAGE_TITLE = "Seitentitel"
|
||||||
LABEL_LOADING_DESCRIPTION = "Ladevorgang-Beschreibung"
|
LABEL_LOADING_DESCRIPTION = "Ladevorgang-Beschreibung"
|
||||||
|
LABEL_FAVICON_URL = "Favicon"
|
||||||
LEGEND_LOGIN = "Anmeldung"
|
LEGEND_LOGIN = "Anmeldung"
|
||||||
LABEL_LOGIN_LOGO = "Logo"
|
LABEL_LOGIN_LOGO = "Logo"
|
||||||
LABEL_LOGIN_DESCRIPTION = "Beschreibung"
|
LABEL_LOGIN_DESCRIPTION = "Beschreibung"
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ LABEL_DETERMINE_USER_LANGUAGE = "Try to determine user language"
|
||||||
LEGEND_BRANDING = "Branding"
|
LEGEND_BRANDING = "Branding"
|
||||||
LABEL_PAGE_TITLE = "Page Title"
|
LABEL_PAGE_TITLE = "Page Title"
|
||||||
LABEL_LOADING_DESCRIPTION = "Loading Description"
|
LABEL_LOADING_DESCRIPTION = "Loading Description"
|
||||||
|
LABEL_FAVICON_URL = "Favicon"
|
||||||
LEGEND_LOGIN = "Login"
|
LEGEND_LOGIN = "Login"
|
||||||
LABEL_LOGIN_LOGO = "Logo"
|
LABEL_LOGIN_LOGO = "Logo"
|
||||||
LABEL_LOGIN_DESCRIPTION = "Description"
|
LABEL_LOGIN_DESCRIPTION = "Description"
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ LABEL_DETERMINE_USER_LANGUAGE = "Prova a determinare la lingua dell'utente"
|
||||||
LEGEND_BRANDING = "Personalizzazione"
|
LEGEND_BRANDING = "Personalizzazione"
|
||||||
LABEL_PAGE_TITLE = "Titolo della pagina"
|
LABEL_PAGE_TITLE = "Titolo della pagina"
|
||||||
LABEL_LOADING_DESCRIPTION = "Messaggio di caricamento"
|
LABEL_LOADING_DESCRIPTION = "Messaggio di caricamento"
|
||||||
|
LABEL_FAVICON_URL = "Favicon"
|
||||||
LEGEND_LOGIN = "Pagina di accesso"
|
LEGEND_LOGIN = "Pagina di accesso"
|
||||||
LABEL_LOGIN_LOGO = "Logo"
|
LABEL_LOGIN_LOGO = "Logo"
|
||||||
LABEL_LOGIN_DESCRIPTION = "Descrizione"
|
LABEL_LOGIN_DESCRIPTION = "Descrizione"
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ LABEL_DETERMINE_USER_LANGUAGE = "Probeer gebruiker taal te bepalen"
|
||||||
LEGEND_BRANDING = "Huisstijl"
|
LEGEND_BRANDING = "Huisstijl"
|
||||||
LABEL_PAGE_TITLE = "Pagina titel"
|
LABEL_PAGE_TITLE = "Pagina titel"
|
||||||
LABEL_LOADING_DESCRIPTION = "Beschrijving tijdens laden"
|
LABEL_LOADING_DESCRIPTION = "Beschrijving tijdens laden"
|
||||||
|
LABEL_FAVICON_URL = "Favicon"
|
||||||
LEGEND_LOGIN = "Login scherm"
|
LEGEND_LOGIN = "Login scherm"
|
||||||
LABEL_LOGIN_LOGO = "Logo"
|
LABEL_LOGIN_LOGO = "Logo"
|
||||||
LABEL_LOGIN_DESCRIPTION = "Beschrijving"
|
LABEL_LOGIN_DESCRIPTION = "Beschrijving"
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ LABEL_DETERMINE_USER_LANGUAGE = "Spróbuj określić język użytkownika"
|
||||||
LEGEND_BRANDING = "Personalizacja"
|
LEGEND_BRANDING = "Personalizacja"
|
||||||
LABEL_PAGE_TITLE = "Tytuł strony:"
|
LABEL_PAGE_TITLE = "Tytuł strony:"
|
||||||
LABEL_LOADING_DESCRIPTION = "Komunikat ład. strony:"
|
LABEL_LOADING_DESCRIPTION = "Komunikat ład. strony:"
|
||||||
|
LABEL_FAVICON_URL = "Favicon:"
|
||||||
LEGEND_LOGIN = "Login"
|
LEGEND_LOGIN = "Login"
|
||||||
LABEL_LOGIN_LOGO = "Logo:"
|
LABEL_LOGIN_LOGO = "Logo:"
|
||||||
LABEL_LOGIN_DESCRIPTION = "Opis:"
|
LABEL_LOGIN_DESCRIPTION = "Opis:"
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ LABEL_DETERMINE_USER_LANGUAGE = "Tentar determinar o idioma do usuário"
|
||||||
LEGEND_BRANDING = "Personalização"
|
LEGEND_BRANDING = "Personalização"
|
||||||
LABEL_PAGE_TITLE = "Título da página"
|
LABEL_PAGE_TITLE = "Título da página"
|
||||||
LABEL_LOADING_DESCRIPTION = "Descrição do 'carregando'"
|
LABEL_LOADING_DESCRIPTION = "Descrição do 'carregando'"
|
||||||
|
LABEL_FAVICON_URL = "Favicon"
|
||||||
LEGEND_LOGIN = "Tela de Entrada"
|
LEGEND_LOGIN = "Tela de Entrada"
|
||||||
LABEL_LOGIN_LOGO = "Logo"
|
LABEL_LOGIN_LOGO = "Logo"
|
||||||
LABEL_LOGIN_DESCRIPTION = "Descrição"
|
LABEL_LOGIN_DESCRIPTION = "Descrição"
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ LABEL_DETERMINE_USER_LANGUAGE = "Пытаться определить язык
|
||||||
LEGEND_BRANDING = "Брендинг"
|
LEGEND_BRANDING = "Брендинг"
|
||||||
LABEL_PAGE_TITLE = "Название страницы"
|
LABEL_PAGE_TITLE = "Название страницы"
|
||||||
LABEL_LOADING_DESCRIPTION = "Описание при загрузке"
|
LABEL_LOADING_DESCRIPTION = "Описание при загрузке"
|
||||||
|
LABEL_FAVICON_URL = "Иконка (favicon)"
|
||||||
LEGEND_LOGIN = "Экран входа"
|
LEGEND_LOGIN = "Экран входа"
|
||||||
LABEL_LOGIN_LOGO = "Логотип"
|
LABEL_LOGIN_LOGO = "Логотип"
|
||||||
LABEL_LOGIN_DESCRIPTION = "Описание"
|
LABEL_LOGIN_DESCRIPTION = "Описание"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue