Sieve filters (alpha / beta version coming soon)

This commit is contained in:
RainLoop Team 2015-01-24 02:35:42 +04:00
parent 02ba68868f
commit 94789632f0
33 changed files with 544 additions and 81 deletions

75
build/langs.php Normal file
View file

@ -0,0 +1,75 @@
<?php
define('LANGS_PATH', __DIR__.'/../rainloop/v/0.0.0/langs');
function getLangStructure($sLangFile)
{
$sEngLang = \file_get_contents(LANGS_PATH.'/'.$sLangFile);
return $sEngLang ? \parse_ini_string($sEngLang, true) : null;
}
function mergeLangStructure($aFromLang, $aEngLang, &$iCount = 0)
{
$iCount = 0;
foreach ($aEngLang as $sSectionKey => $aSectionValue)
{
foreach (\array_keys($aSectionValue) as $sParamKey)
{
if (isset($aFromLang[$sSectionKey][$sParamKey]))
{
$aEngLang[$sSectionKey][$sParamKey] = $aFromLang[$sSectionKey][$sParamKey];
}
else
{
// echo $sSectionKey.'/'.$sParamKey.','."\n";
$iCount++;
}
}
}
return $aEngLang;
}
function saveLangStructure($sLangFile, $aLang)
{
$aResultLines = array();
$aResultLines[] = '; '.$sLangFile;
foreach ($aLang as $sSectionKey => $aSectionValue)
{
$aResultLines[] = '';
$aResultLines[] = '['.$sSectionKey.']';
foreach ($aSectionValue as $sParamKey => $sParamValue)
{
$aResultLines[] = $sParamKey.' = "'.
\str_replace(array('\\', '"'), array('\\\\', '\\"'), \trim($sParamValue)).'"';
}
}
\file_put_contents(LANGS_PATH.'/'.$sLangFile, implode("\n", $aResultLines));
}
$sNL = "\n";
$aEngLang = \getLangStructure('en.ini');
$aFiles = \glob(LANGS_PATH.'/*.ini');
foreach ($aFiles as $sFile)
{
$iCount = 0;
$sFileName = \basename($sFile);
$aNextLang = \getLangStructure($sFileName);
$aNewLang = \mergeLangStructure($aNextLang, $aEngLang, $iCount);
if (\json_encode($aNextLang) === \json_encode($aNewLang))
{
echo $sFileName.': ok'.$sNL;
}
else
{
echo $sFileName.': changed ('.$iCount.')'.$sNL;
}
// \saveLangStructure($sFileName, $aNewLang);
}

View file

@ -307,6 +307,7 @@
'MoveTo': 'MoveTo',
'Discard': 'Discard',
'Vacation': 'Vacation',
'Reject': 'Reject',
'Forward': 'Forward'
};

View file

@ -45,7 +45,7 @@
this.actionSkipOthers = ko.observable(false);
this.keepForward = ko.observable(true);
this.actionKeep = ko.observable(true);
this.actionType = ko.observable(Enums.FiltersAction.MoveTo);
@ -79,6 +79,9 @@
case Enums.FiltersAction.Vacation:
sResult = 'Vacation message @i18n';
break;
case Enums.FiltersAction.Reject:
sResult = 'Reject @i18n';
break;
case Enums.FiltersAction.Discard:
sResult = 'Discard @i18n';
break;
@ -103,6 +106,9 @@
case Enums.FiltersAction.Vacation:
sTemplate = 'SettingsFiltersActionVacation';
break;
case Enums.FiltersAction.Reject:
sTemplate = 'SettingsFiltersActionReject';
break;
case Enums.FiltersAction.None:
sTemplate = 'SettingsFiltersActionNone';
break;
@ -161,6 +167,7 @@
if (-1 < Utils.inArray(this.actionType(), [
Enums.FiltersAction.MoveTo,
Enums.FiltersAction.Forward,
Enums.FiltersAction.Reject,
Enums.FiltersAction.Vacation
]))
{
@ -197,8 +204,8 @@
'ActionValueSecond': this.actionValueSecond(),
'ActionType': this.actionType(),
'Keep': this.actionKeep() ? '1' : '0',
'MarkAsRead': this.actionMarkAsRead() ? '1' : '0',
'KeepForward': this.keepForward() ? '1' : '0',
'SkipOthers': this.actionSkipOthers() ? '1' : '0'
};
};
@ -241,8 +248,8 @@
this.actionValue(Utils.pString(oItem['ActionValue']));
this.actionValueSecond(Utils.pString(oItem['ActionValueSecond']));
this.actionKeep(!!oItem['Keep']);
this.actionMarkAsRead(!!oItem['MarkAsRead']);
this.keepForward(!!oItem['KeepForward']);
this.actionSkipOthers(!!oItem['SkipOthers']);
bResult = true;
@ -274,7 +281,7 @@
oClone.actionValueSecond(this.actionValueSecond());
oClone.keepForward(this.keepForward());
oClone.actionKeep(this.actionKeep());
oClone.conditions(_.map(this.conditions(), function (oCondition) {
return oCondition.cloneSelf();

View file

@ -10,6 +10,8 @@
Enums = require('Common/Enums'),
Utils = require('Common/Utils'),
Data = require('Storage/User/Data'),
Remote = require('Storage/User/Remote')
;
@ -25,12 +27,19 @@
this.processText = ko.observable('');
this.visibility = ko.observable(false);
this.modules = Data.filterModules;
this.filters = ko.observableArray([]);
this.filters.loading = ko.observable(false).extend({'throttle': 200});
this.filters.saving = ko.observable(false).extend({'throttle': 200});
this.filters.subscribe(Utils.windowResizeCallback);
this.filterRaw = ko.observable('');
this.filterRaw.capa = ko.observable('');
this.filterRaw.active = ko.observable(false);
this.filterRaw.allow = ko.observable(false);
this.processText = ko.computed(function () {
return this.filters.loading() ? Utils.i18n('SETTINGS_FILTERS/LOADING_PROCESS') : '';
}, this);
@ -58,7 +67,7 @@
self.updateList();
}
}, this.filters());
}, this.filters(), this.filterRaw(), this.filterRaw.active());
}
return true;
@ -70,6 +79,14 @@
this.filters.subscribe(function () {
this.haveChanges(true);
}, this);
this.filterRaw.subscribe(function () {
this.haveChanges(true);
}, this);
this.filterRaw.active.subscribe(function () {
this.haveChanges(true);
}, this);
}
FiltersUserSettings.prototype.scrollableOptions = function ()
@ -93,18 +110,28 @@
self.filters.loading(false);
if (Enums.StorageResultType.Success === sResult && oData &&
oData.Result && Utils.isArray(oData.Result))
oData.Result && Utils.isArray(oData.Result.Filters))
{
var aResult = _.compact(_.map(oData.Result, function (aItem) {
var aResult = _.compact(_.map(oData.Result.Filters, function (aItem) {
var oNew = new FilterModel();
return (oNew && oNew.parse(aItem)) ? oNew : null;
}));
self.filters(aResult);
self.modules(oData.Result.Modules ? oData.Result.Modules : {});
self.filterRaw(oData.Result.Raw || '');
self.filterRaw.capa(Utils.isArray(oData.Result.Capa) ? oData.Result.Capa.join(' ') : '');
self.filterRaw.active(!!oData.Result.RawIsActive);
self.filterRaw.allow(!!oData.Result.RawIsAllow);
}
else
{
self.filters([]);
self.modules({});
self.filterRaw('');
self.filterRaw.capa({});
}
self.haveChanges(false);
@ -130,6 +157,7 @@
require('View/Popup/Filter'), [oNew, function () {
self.filters.push(oNew);
self.haveChanges(true);
self.filterRaw.active(false);
}, false]);
};

View file

@ -212,7 +212,7 @@
RemoteAdminStorage.prototype.createOrUpdateDomain = function (fCallback,
bCreate, sName,
sIncHost, iIncPort, sIncSecure, bIncShortLogin,
bUseSieve, sSieveHost, iSievePort, sSieveSecure,
bUseSieve, sSieveAllowRaw, sSieveHost, iSievePort, sSieveSecure,
sOutHost, iOutPort, sOutSecure, bOutShortLogin, bOutAuth, bOutPhpMail,
sWhiteList)
{
@ -226,6 +226,7 @@
'IncShortLogin': bIncShortLogin ? '1' : '0',
'UseSieve': bUseSieve ? '1' : '0',
'SieveAllowRaw': sSieveAllowRaw ? '1' : '0',
'SieveHost': sSieveHost,
'SievePort': iSievePort,
'SieveSecure': sSieveSecure,

View file

@ -445,6 +445,7 @@
}, this);
// other
this.filterModules = ko.observable({});
this.composeInEdit = ko.observable(false);
this.capaOpenPGP = ko.observable(false);
this.openpgpkeys = ko.observableArray([]);

View file

@ -233,9 +233,12 @@
/**
* @param {?Function} fCallback
*/
RemoteUserStorage.prototype.filtersSave = function (fCallback, aFilters)
RemoteUserStorage.prototype.filtersSave = function (fCallback,
aFilters, sRaw, bRawIsActive)
{
this.defaultRequest(fCallback, 'FiltersSave', {
'Raw': sRaw,
'RawIsActive': bRawIsActive ? '1' : '0',
'Filters': _.map(aFilters, function (oItem) {
return oItem.toJson();
})

View file

@ -70,6 +70,7 @@
this.imapSecure = ko.observable(Enums.ServerSecure.None);
this.imapShortLogin = ko.observable(false);
this.useSieve = ko.observable(false);
this.sieveAllowRaw = ko.observable(false);
this.sieveServer = ko.observable('');
this.sievePort = ko.observable('' + Consts.Values.SieveDefaulPort);
this.sieveSecure = ko.observable(Enums.ServerSecure.None);
@ -126,6 +127,7 @@
this.imapShortLogin(),
this.useSieve(),
this.sieveAllowRaw(),
this.sieveServer(),
Utils.pInt(this.sievePort()),
this.sieveSecure(),
@ -353,6 +355,7 @@
this.imapSecure(Utils.trim(oDomain.IncSecure));
this.imapShortLogin(!!oDomain.IncShortLogin);
this.useSieve(!!oDomain.UseSieve);
this.sieveAllowRaw(!!oDomain.SieveAllowRaw);
this.sieveServer(Utils.trim(oDomain.SieveHost));
this.sievePort('' + Utils.pInt(oDomain.SievePort));
this.sieveSecure(Utils.trim(oDomain.SieveSecure));
@ -396,6 +399,7 @@
this.imapShortLogin(false);
this.useSieve(false);
this.sieveAllowRaw(false);
this.sieveServer('');
this.sievePort('' + Consts.Values.SieveDefaulPort);
this.sieveSecure(Enums.ServerSecure.None);

View file

@ -29,6 +29,9 @@
this.fTrueCallback = null;
this.filter = ko.observable(null);
this.modules = Data.filterModules;
this.allowMarkAsRead = ko.observable(false);
this.defautOptionsAfterRender = Utils.defautOptionsAfterRender;
this.folderSelectList = Data.folderMenuForFilters;
this.selectedFolderValue = ko.observable('');
@ -68,13 +71,55 @@
return true;
});
this.actionTypeOptions = [
// {'id': Enums.FiltersAction.None, 'name': 'None @i18n'},
{'id': Enums.FiltersAction.MoveTo, 'name': ' Move to @i18n'},
{'id': Enums.FiltersAction.Forward, 'name': 'Forward to @i18n'},
{'id': Enums.FiltersAction.Vacation, 'name': 'Vacation message @i18n'},
{'id': Enums.FiltersAction.Discard, 'name': 'Discard @i18n'}
];
this.actionTypeOptions = [];
this.fieldOptions = [];
this.typeOptions = [];
this.populateOptions();
this.modules.subscribe(this.populateOptions, this)
kn.constructorEnd(this);
}
kn.extendAsViewModel(['View/Popup/Filter', 'PopupsFilterViewModel'], FilterPopupView);
_.extend(FilterPopupView.prototype, AbstractView.prototype);
FilterPopupView.prototype.populateOptions = function ()
{
this.actionTypeOptions = []
// this.actionTypeOptions.push({'id': Enums.FiltersAction.None, 'name': 'None @i18n'});
var oModules = this.modules();
if (oModules)
{
if (oModules.markasread)
{
this.allowMarkAsRead(true);
}
if (oModules.moveto)
{
this.actionTypeOptions.push({'id': Enums.FiltersAction.MoveTo, 'name': 'Move to @i18n'});
}
if (oModules.redirect)
{
this.actionTypeOptions.push({'id': Enums.FiltersAction.Forward, 'name': 'Forward to @i18n'});
}
if (oModules.reject)
{
this.actionTypeOptions.push({'id': Enums.FiltersAction.Reject, 'name': 'Reject @i18n'});
}
if (oModules.vacation)
{
this.actionTypeOptions.push({'id': Enums.FiltersAction.Vacation, 'name': 'Vacation message @i18n'});
}
}
this.actionTypeOptions.push({'id': Enums.FiltersAction.Discard, 'name': 'Discard @i18n'});
this.fieldOptions = [
{'id': Enums.FilterConditionField.From, 'name': 'From @i18n'},
@ -88,12 +133,8 @@
{'id': Enums.FilterConditionType.EqualTo, 'name': 'Equal To @i18n'},
{'id': Enums.FilterConditionType.NotEqualTo, 'name': 'Not Equal To @i18n'}
];
};
kn.constructorEnd(this);
}
kn.extendAsViewModel(['View/Popup/Filter', 'PopupsFilterViewModel'], FilterPopupView);
_.extend(FilterPopupView.prototype, AbstractView.prototype);
FilterPopupView.prototype.removeCondition = function (oConditionToDelete)
{

View file

@ -70,7 +70,15 @@ class ManageSieveClient extends \MailSo\Net\NetClient
*/
public function IsModuleSupported($sModule)
{
return $this->IsSupported('SIEVE') && \in_array(\strtoupper($sModule), $this->aModules);
return $this->IsSupported('SIEVE') && \in_array(\strtolower(\trim($sModule)), $this->aModules);
}
/**
* @return array
*/
public function Modules()
{
return $this->aModules;
}
/**
@ -487,7 +495,7 @@ class ManageSieveClient extends \MailSo\Net\NetClient
$this->aAuth = \explode(' ', \strtoupper($aTokens[1]));
break;
case 'SIEVE':
$this->aModules = \explode(' ', \strtoupper($aTokens[1]));
$this->aModules = \explode(' ', \strtolower($aTokens[1]));
break;
}
}
@ -513,7 +521,6 @@ class ManageSieveClient extends \MailSo\Net\NetClient
$this->IsConnected(true);
$sRequest = \trim($sRequest);
$this->sendRaw($sRequest);
}
@ -617,7 +624,7 @@ class ManageSieveClient extends \MailSo\Net\NetClient
*/
protected function getLogName()
{
return 'MANAGE-SIEVE';
return 'SIEVE';
}
/**

View file

@ -251,8 +251,10 @@ class Actions
case 'filters':
// \RainLoop\Providers\Filters\FiltersInterface
$oResult = new \RainLoop\Providers\Filters\SieveStorage(
!!$this->Config()->Get('labs', 'sieve_utf8_folder_name', true)
$this->Plugins(), $this->Config()
);
$oResult->SetLogger($this->Logger());
break;
case 'address-book':
// \RainLoop\Providers\AddressBook\AddressBookInterface
@ -2053,7 +2055,9 @@ class Actions
*/
public function DoFilters()
{
return $this->DefaultResponse(__FUNCTION__, $this->FiltersProvider()->Load());
$oAccount = $this->getAccountFromToken();
return $this->DefaultResponse(__FUNCTION__,
$this->FiltersProvider()->Load($oAccount, $oAccount->DomainSieveAllowRaw()));
}
/**
@ -2063,8 +2067,13 @@ class Actions
*/
public function DoFiltersSave()
{
$oAccount = $this->getAccountFromToken();
$aIncFilters = $this->GetActionParam('Filters', array());
$sRaw = $this->GetActionParam('Raw', '');
$bRawIsActive = '1' === (string) $this->GetActionParam('RawIsActive', '0');
$aFilters = array();
foreach ($aIncFilters as $aFilter)
{
@ -2078,8 +2087,8 @@ class Actions
}
}
return $this->DefaultResponse(__FUNCTION__, $this->FiltersProvider()->Save($aFilters));
// return $this->TrueResponse(__FUNCTION__);
return $this->DefaultResponse(__FUNCTION__, $this->FiltersProvider()->Save($oAccount,
$aFilters, $sRaw, $bRawIsActive));
}
/**

View file

@ -116,7 +116,7 @@ class Application extends \RainLoop\Config\AbstractConfig
'admin_password' => array('12345'),
'allow_admin_panel' => array(true, 'Access settings'),
'allow_two_factor_auth' => array(false),
'allow_universal_login' => array(true),
'allow_universal_login' => array(false),
'admin_panel_host' => array(''),
'core_install_access_domain' => array('')
),

View file

@ -305,6 +305,38 @@ class Account extends \RainLoop\Account // for backward compatibility
return $this->Domain()->OutAuth();
}
/**
* @return string
*/
public function DomainSieveHost()
{
return $this->Domain()->SieveHost();
}
/**
* @return int
*/
public function DomainSievePort()
{
return $this->Domain()->SievePort();
}
/**
* @return int
*/
public function DomainSieveSecure()
{
return $this->Domain()->SieveSecure();
}
/**
* @return bool
*/
public function DomainSieveAllowRaw()
{
return $this->Domain()->SieveAllowRaw();
}
/**
* @return string
*/
@ -443,4 +475,52 @@ class Account extends \RainLoop\Account // for backward compatibility
return $bLogin;
}
/**
* @param \RainLoop\Plugins\Manager $oPlugins
* @param \MailSo\Sieve\ManageSieveClient $oSieveClient
* @param \RainLoop\Application $oConfig
*/
public function SieveConnectAndLoginHelper($oPlugins, $oSieveClient, $oConfig)
{
$bLogin = false;
$aSieveCredentials = array(
'UseConnect' => true,
'UseAuth' => true,
'Host' => $this->DomainSieveHost(),
'Port' => $this->DomainSievePort(),
'Secure' => $this->DomainSieveSecure(),
'Login' => $this->IncLogin(),
'Password' => $this->Password(),
'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false),
'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true)
);
$oPlugins->RunHook('filter.sieve-credentials', array($this, &$aSieveCredentials));
$oPlugins->RunHook('event.sieve-pre-connect', array($this, $aSieveCredentials['UseConnect'], $aSieveCredentials));
if ($aSieveCredentials['UseConnect'] && $oSieveClient)
{
$oSieveClient->Connect($aSieveCredentials['Host'], $aSieveCredentials['Port'],
$aSieveCredentials['Secure'], $aSieveCredentials['VerifySsl'], $aSieveCredentials['AllowSelfSigned']
);
}
$oPlugins->RunHook('event.sieve-post-connect', array($this, $aSieveCredentials['UseConnect'], $aSieveCredentials));
$oPlugins->RunHook('event.sieve-pre-login', array($this, $aSieveCredentials['UseAuth'], $aSieveCredentials));
if ($aSieveCredentials['UseAuth'])
{
$oSieveClient->Login($aSieveCredentials['Login'], $aSieveCredentials['Password']);
$bLogin = true;
}
$oPlugins->RunHook('event.sieve-post-login', array($this, $aSieveCredentials['UseAuth'], $bLogin, $aSieveCredentials));
return $bLogin;
}
}

View file

@ -83,6 +83,11 @@ class Domain
*/
private $iSieveSecure;
/**
* @var bool
*/
private $bSieveAllowRaw;
/**
* @var string
*/
@ -130,6 +135,8 @@ class Domain
$this->iSievePort = $iSievePort;
$this->iSieveSecure = $iSieveSecure;
$this->bSieveAllowRaw = false;
$this->sWhiteList = \trim($sWhiteList);
}
@ -184,6 +191,7 @@ class Domain
!empty($aDomain['imap_secure']) ? $aDomain['imap_secure'] : '');
$bUseSieve = isset($aDomain['sieve_use']) ? (bool) $aDomain['sieve_use'] : false;
$bSieveAllowRaw = isset($aDomain['sieve_allow_raw']) ? (bool) $aDomain['sieve_allow_raw'] : false;
$sSieveHost = empty($aDomain['sieve_host']) ? '' : (string) $aDomain['sieve_host'];
$iSievePort = empty($aDomain['sieve_port']) ? 2000 : (int) $aDomain['sieve_port'];
@ -207,6 +215,8 @@ class Domain
$bUseSieve, $sSieveHost, $iSievePort, $iSieveSecure,
$sOutHost, $iOutPort, $iOutSecure, $bOutShortLogin, $bOutAuth, $bOutUsePhpMail,
$sWhiteList);
$oDomain->SetSieveAllowRaw($bSieveAllowRaw);
}
return $oDomain;
@ -257,6 +267,7 @@ class Domain
'imap_secure = "'.self::ConstConnectionSecurityTypeToStr($this->iIncSecure).'"',
'imap_short_login = '.($this->bIncShortLogin ? 'On' : 'Off'),
'sieve_use = '.($this->bUseSieve ? 'On' : 'Off'),
'sieve_allow_raw = '.($this->bSieveAllowRaw ? 'On' : 'Off'),
'sieve_host = "'.$this->encodeIniString($this->sSieveHost).'"',
'sieve_port = '.$this->iSievePort,
'sieve_secure = "'.self::ConstConnectionSecurityTypeToStr($this->iSieveSecure).'"',
@ -427,6 +438,22 @@ class Domain
return $this->iSieveSecure;
}
/**
* @return bool
*/
public function SieveAllowRaw()
{
return $this->bSieveAllowRaw;
}
/**
* @param bool $bSieveAllowRaw
*/
public function SetSieveAllowRaw($bSieveAllowRaw)
{
$this->bSieveAllowRaw = !!$bSieveAllowRaw;
}
/**
* @return string
*/
@ -524,6 +551,7 @@ class Domain
'SieveHost' => $bAjax ? \MailSo\Base\Utils::IdnToUtf8($this->SieveHost()) : $this->SieveHost(),
'SievePort' => $this->SievePort(),
'SieveSecure' => $this->SieveSecure(),
'SieveAllowRaw' => $this->SieveAllowRaw(),
'OutHost' => $bAjax ? \MailSo\Base\Utils::IdnToUtf8($this->OutHost()) : $this->OutHost(),
'OutPort' => $this->OutPort(),
'OutSecure' => $this->OutSecure(),

View file

@ -130,6 +130,7 @@ class Domain extends \RainLoop\Providers\AbstractProvider
$iIncSecure = (int) $oActions->GetActionParam('IncSecure', \MailSo\Net\Enumerations\ConnectionSecurityType::NONE);
$bIncShortLogin = '1' === (string) $oActions->GetActionParam('IncShortLogin', '0');
$bUseSieve = '1' === (string) $oActions->GetActionParam('UseSieve', '0');
$bSieveAllowRaw = '1' === (string) $oActions->GetActionParam('SieveAllowRaw', '0');
$sSieveHost = (string) $oActions->GetActionParam('SieveHost', '');
$iSievePort = (int) $oActions->GetActionParam('SievePort', 2000);
$iSieveSecure = (int) $oActions->GetActionParam('SieveSecure', \MailSo\Net\Enumerations\ConnectionSecurityType::NONE);
@ -173,6 +174,11 @@ class Domain extends \RainLoop\Providers\AbstractProvider
$sWhiteList);
}
}
if ($oDomain)
{
$oDomain->SetSieveAllowRaw($bSieveAllowRaw);
}
}
return $oDomain;

View file

@ -18,21 +18,28 @@ class Filters extends \RainLoop\Providers\AbstractProvider
}
/**
* @param \RainLoop\Account $oAccount
* @param bool $bAllowRaw = false
*
* @return array
*/
public function Load()
public function Load($oAccount, $bAllowRaw = false)
{
return $this->IsActive() ? $this->oDriver->Load() : array();
return $this->IsActive() ? $this->oDriver->Load($oAccount, $bAllowRaw) : array();
}
/**
* @param \RainLoop\Account $oAccount
* @param array $aFilters
* @param string $sRaw = ''
* @param bool $bRawIsActive = false
*
* @return bool
*/
public function Save($aFilters)
public function Save($oAccount, $aFilters, $sRaw = '', $bRawIsActive = false)
{
return $this->IsActive() ? $this->oDriver->Save($aFilters) : false;
return $this->IsActive() ? $this->oDriver->Save($oAccount,
$aFilters, $sRaw, $bRawIsActive) : false;
}
/**

View file

@ -57,7 +57,7 @@ class Filter
/**
* @var bool
*/
private $bKeepForward;
private $bKeep;
public function __construct()
{
@ -81,7 +81,7 @@ class Filter
$this->bMarkAsRead = false;
$this->bSkipOthers = false;
$this->bKeepForward = true;
$this->bKeep = true;
}
/**
@ -167,9 +167,9 @@ class Filter
/**
* @return bool
*/
public function KeepForward()
public function Keep()
{
return $this->bKeepForward;
return $this->bKeep;
}
/**
@ -217,9 +217,9 @@ class Filter
$this->sActionValue = isset($aFilter['ActionValue']) ? $aFilter['ActionValue'] : '';
$this->sActionValueSecond = isset($aFilter['ActionValueSecond']) ? $aFilter['ActionValueSecond'] : '';
$this->bKeep = isset($aFilter['Keep']) ? '1' === (string) $aFilter['Keep'] : true;
$this->bMarkAsRead = isset($aFilter['MarkAsRead']) ? '1' === (string) $aFilter['MarkAsRead'] : false;
$this->bSkipOthers = isset($aFilter['SkipOthers']) ? '1' === (string) $aFilter['SkipOthers'] : false;
$this->bKeepForward = isset($aFilter['KeepForward']) ? '1' === (string) $aFilter['KeepForward'] : true;
$this->aConditions = \RainLoop\Providers\Filters\Classes\FilterCondition::CollectionFromJSON(
isset($aFilter['Conditions']) ? $aFilter['Conditions'] : array());
@ -255,6 +255,7 @@ class Filter
'ActionType' => $this->ActionType(),
'ActionValue' => $this->ActionValue(),
'ActionValueSecond' => $this->ActionValueSecond(),
'Keep' => $this->Keep(),
'MarkAsRead' => $this->MarkAsRead(),
'SkipOthers' => $this->SkipOthers()
);

View file

@ -8,5 +8,6 @@ class ActionType
const MOVE_TO = 'MoveTo';
const DISCARD = 'Discard';
const VACATION = 'Vacation';
const REJECT = 'Reject';
const FORWARD = 'Forward';
}

View file

@ -5,14 +5,18 @@ namespace RainLoop\Providers\Filters;
interface FiltersInterface
{
/**
* @param \RainLoop\Account $oAccount
* @param bool $bAllowRaw = false
*
* @return array
*/
public function Load();
public function Load($oAccount, $bAllowRaw = false);
/**
* @param \RainLoop\Account $oAccount
* @param array $aFilters
*
* @return bool
*/
public function Save($aFilters);
public function Save($oAccount, $aFilters);
}

View file

@ -4,7 +4,25 @@ namespace RainLoop\Providers\Filters;
class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
{
const NEW_LINE = "\n";
const NEW_LINE = "\r\n";
const SIEVE_FILE_NAME = 'rainloop.user';
const SIEVE_FILE_NAME_RAW = 'rainloop.raw';
/**
* @var \MailSo\Log\Logger
*/
private $oLogger;
/**
* @var \RainLoop\Plugins\Manager
*/
private $oPlugins;
/**
* @var \RainLoop\Application
*/
private $oConfig;
/**
* @var bool
@ -12,31 +30,130 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
private $bUtf8FolderName;
/**
* @param bool $bUtf8FolderName = true
*
* @return void
*/
public function __construct($bUtf8FolderName = true)
public function __construct($oPlugins, $oConfig)
{
$this->bUtf8FolderName = !!$bUtf8FolderName;
$this->oLogger = null;
$this->oPlugins = $oPlugins;
$this->oConfig = $oConfig;
$this->bUtf8FolderName = !!$this->oConfig->Get('labs', 'sieve_utf8_folder_name', true);
}
/**
* @param \RainLoop\Model\Account $oAccount
* @param bool $bAllowRaw = false
*
* @return array
*/
public function Load()
public function Load($oAccount, $bAllowRaw = false)
{
return $this->fileStringToCollection(@\file_get_contents('e:/sieve.txt'));
$sRaw = '';
$bBasicIsActive = false;
$bRawIsActive = false;
$aModules = array();
$aFilters = array();
$oSieveClient = \MailSo\Sieve\ManageSieveClient::NewInstance()->SetLogger($this->oLogger);
if ($oAccount->SieveConnectAndLoginHelper($this->oPlugins, $oSieveClient, $this->oConfig))
{
$aModules = $oSieveClient->Modules();
$aList = $oSieveClient->ListScripts();
if (\is_array($aList) && 0 < \count($aList))
{
if (isset($aList[self::SIEVE_FILE_NAME]))
{
$bBasicIsActive = !!$aList[self::SIEVE_FILE_NAME];
$sS = $oSieveClient->GetScript(self::SIEVE_FILE_NAME);
if ($sS)
{
$aFilters = $this->fileStringToCollection($sS);
}
}
if ($bAllowRaw && isset($aList[self::SIEVE_FILE_NAME_RAW]))
{
$bRawIsActive = !!$aList[self::SIEVE_FILE_NAME_RAW];
$sRaw = \trim($oSieveClient->GetScript(self::SIEVE_FILE_NAME_RAW));
}
}
$oSieveClient->LogoutAndDisconnect();
}
return array(
'RawIsAllow' => $bAllowRaw,
'RawIsActive' => $bRawIsActive,
'Raw' => $bAllowRaw ? $sRaw : '',
'Filters' => !$bBasicIsActive && !$bRawIsActive ? array() : $aFilters,
'Capa' => $bAllowRaw ? $aModules : array(),
'Modules' => array(
'redirect' => \in_array('fileinto', $aModules),
'moveto' => \in_array('fileinto', $aModules),
'reject' => \in_array('reject', $aModules),
'vacation' => \in_array('vacation', $aModules),
'markasread' => \in_array('imap4flags', $aModules) && false
)
);
}
/**
* @param \RainLoop\Model\Account $oAccount
* @param array $aFilters
* @param string $sRaw = ''
* @param bool $bRawIsActive = false
*
* @return bool
*/
public function Save($aFilters)
public function Save($oAccount, $aFilters, $sRaw = '', $bRawIsActive = false)
{
return @\file_put_contents('e:/sieve.txt', $this->collectionToFileString($aFilters));
$oSieveClient = \MailSo\Sieve\ManageSieveClient::NewInstance()->SetLogger($this->oLogger);
if ($oAccount->SieveConnectAndLoginHelper($this->oPlugins, $oSieveClient, $this->oConfig))
{
$aList = $oSieveClient->ListScripts();
$sUserFilter = $this->collectionToFileString($aFilters);
if (!empty($sUserFilter))
{
$oSieveClient->PutScript(self::SIEVE_FILE_NAME, $sUserFilter);
if (!$bRawIsActive)
{
$oSieveClient->SetActiveScript(self::SIEVE_FILE_NAME);
}
}
else if (isset($aList[self::SIEVE_FILE_NAME]))
{
$oSieveClient->DeleteScript(self::SIEVE_FILE_NAME);
}
$sRaw = \trim($sRaw);
if (!empty($sRaw))
{
$oSieveClient->PutScript(self::SIEVE_FILE_NAME_RAW, $sRaw);
if ($bRawIsActive)
{
$oSieveClient->SetActiveScript(self::SIEVE_FILE_NAME_RAW);
}
}
else if (isset($aList[self::SIEVE_FILE_NAME_RAW]))
{
$oSieveClient->DeleteScript(self::SIEVE_FILE_NAME_RAW);
}
$oSieveClient->LogoutAndDisconnect();
return true;
}
return false;
}
/**
@ -221,20 +338,31 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
$aResult[] = $sTab.'# @Error (vacation): empty action value';
}
break;
case \RainLoop\Providers\Filters\Enumerations\ActionType::REJECT:
$sValue = \trim($oFilter->ActionValue());
if (0 < \strlen($sValue))
{
$aCapa['reject'] = true;
$aResult[] = $sTab.'reject "'.$this->quote($sValue).'";';
$aResult[] = $sTab.'stop;';
}
else
{
$aResult[] = $sTab.'# @Error (reject): empty action value';
}
break;
case \RainLoop\Providers\Filters\Enumerations\ActionType::FORWARD:
$sValue = $oFilter->ActionValue();
if (0 < \strlen($sValue))
{
if ($oFilter->KeepForward())
if ($oFilter->Keep())
{
$aCapa['copy'] = true;
$aResult[] = $sTab.'redirect :copy "'.$this->quote($sValue).'";';
}
else
{
$aResult[] = $sTab.'redirect "'.$this->quote($sValue).'";';
$aCapa['fileinto'] = true;
$aResult[] = $sTab.'fileinto "INBOX";';
}
$aResult[] = $sTab.'redirect "'.$this->quote($sValue).'";';
$aResult[] = $sTab.'stop;';
}
else
@ -246,8 +374,6 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
$sValue = $oFilter->ActionValue();
if (0 < \strlen($sValue))
{
$aCapa['fileinto'] = true;
$sFolderName = $sValue; // utf7-imap
if ($this->bUtf8FolderName) // to utf-8
{
@ -256,6 +382,7 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
\MailSo\Base\Enumerations\Charset::UTF_8);
}
$aCapa['fileinto'] = true;
$aResult[] = $sTab.'fileinto "'.$this->quote($sFolderName).'";';
$aResult[] = $sTab.'stop;';
}
@ -359,4 +486,12 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
{
return \str_replace(array('\\', '"'), array('\\\\', '\\"'), \trim($sValue));
}
/**
* @param \MailSo\Log\Logger $oLogger
*/
public function SetLogger($oLogger)
{
$this->oLogger = $oLogger instanceof \MailSo\Log\Logger ? $oLogger : null;
}
}

View file

@ -84,13 +84,25 @@
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'use Sieve for this domain',
label: 'Use Sieve for this domain',
value: useSieve
}
}"></div>
</div>
</div>
<div data-bind="visible: useSieve">
<br />
<div class="row">
<div class="span5">
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'Allow custom raw script',
value: sieveAllowRaw
}
}"></div>
</div>
</div>
<br />
<div class="row">
<div class="span3">

View file

@ -11,7 +11,12 @@
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_FILTERS/BUTTON_ADD_FILTER"></span>
</a>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;
<a class="btn" data-bind="visible: filterRaw.allow, click: function () { filterRaw.active(!filterRaw.active()) },
css: {'active': filterRaw.active }">
<i class="icon-file-code"></i>
</a>
&nbsp;&nbsp;
<a class="btn" data-bind="command: saveChanges">
<i data-bind="css: {'icon-floppy': !filters.saving(), 'icon-spinner animated': filters.saving()}"></i>
&nbsp;&nbsp;
@ -36,7 +41,11 @@
&nbsp;&nbsp;
<span data-bind="text: processText"></span>
</div>
<table class="table table-hover list-table g-ui-user-select-none" data-bind="i18nUpdate: filters">
<div data-bind="visible: filterRaw.allow() && filterRaw.active()">
<textarea class="span8" style="height: 300px" data-bind="value: filterRaw, valueUpdate: 'afterkeydown'"></textarea>
</div>
<table class="table table-hover list-table g-ui-user-select-none"
data-bind="visible: !filterRaw.active() || !filterRaw.active(), i18nUpdate: filters">
<colgroup>
<col style="width: 30px" />
<col />

View file

@ -9,10 +9,10 @@
name: 'Checkbox',
params: {
label: 'keep in INBOX @i18n',
value: keepForward
value: actionKeep
}
}"></div>
<div data-bind="component: {
<div data-bind="visible: $root.allowMarkAsRead, component: {
name: 'Checkbox',
params: {
label: 'Mark as read @i18n',

View file

@ -6,7 +6,7 @@
</div>
<div class="control-group">
<div class="controls">
<div data-bind="component: {
<div data-bind="visible: $root.allowMarkAsRead, component: {
name: 'Checkbox',
params: {
label: 'Mark as read @i18n',

View file

@ -0,0 +1,5 @@
<div class="control-group" data-bind="css: {'error': actionValue.error}" style="margin-bottom: 0">
<div class="controls">
<textarea class="span5" data-bind="value: actionValue" style="height: 100px;" placeholder="Reject message @i18n"></textarea>
</div>
</div>

View file

@ -5,7 +5,8 @@
&nbsp;
<input class="span3" type="text" data-bind="value: value" />
&nbsp;
<span class="delete-action button-delete pull-right" data-bind="click: function (oCondition) { $root.removeCondition(oCondition); }">
<span class="delete-action button-delete pull-right" style="margin-top: 5px;"
data-bind="click: function (oCondition) { $root.removeCondition(oCondition); }">
<i class="icon-trash"></i>
</span>
</div>

View file

@ -173,7 +173,6 @@ LABEL_EMAIL = "Email"
LABEL_PHONE = "Phone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LABEL_TAGS = "Tags"
LINK_ADD_EMAIL = "Add an email address"
LINK_ADD_PHONE = "Add a phone"
LINK_BIRTHDAY = "Birthday"
@ -376,10 +375,8 @@ TITLE_EDIT_FILTER = "Update filter?"
LEGEND_FILTERS = "Filters"
BUTTON_SAVE = "Save"
BUTTON_DONE = "Done"
BUTTON_ADD_FILTER = "Add Filter"
BUTTON_SAVE_FILTER = "Save Filter"
BUTTON_DELETE_FILTER = "Delete Filter"
BUTTON_ADD_CONDITION = "Add Condition"
BUTTON_ADD_FILTER = "Add a Filter"
BUTTON_ADD_CONDITION = "Add a Condition"
BUTTON_DELETE = "Delete"
LOADING_PROCESS = "Updating filter list"
DELETING_ASK = "Are you sure?"

View file

@ -548,7 +548,7 @@ LABEL_SAVE_MESSAGE = "Guardar aviso"
LABEL_SEND_MESSAGE = "Enviar mensaje"
LABEL_CLOSE_COMPOSE = "Cerrar componer"
[ PGP_NOTIFICATIONS ]
[PGP_NOTIFICATIONS]
NO_PUBLIC_KEYS_FOUND = "No se encontraron claves públicas"
NO_PUBLIC_KEYS_FOUND_FOR = "No hay claves públicas encontrados para el email %MAIL%"
NO_PRIVATE_KEY_FOUND = "No se encontró la clave privada "

View file

@ -619,8 +619,8 @@ NO_SCRIPT_TITLE = "È richiesto JavaScript per questa applicazione."
NO_SCRIPT_DESC = "Il supporto a JavaScript è disabilitato.
Abilitare il supporto a JavaScript e riprovare."
NO_SCRIPT_TITLE = "Sono richiesti i Cookie per questa applicazione."
NO_SCRIPT_DESC = "Il supporto ai Cookie è disabilitato.
NO_COOKIE_TITLE = "Sono richiesti i Cookie per questa applicazione."
NO_COOKIE_DESC = "Il supporto ai Cookie è disabilitato.
Abilitare il supporto ai Cookie e riprovare."
BAD_BROWSER_TITLE = "Il tuo browser è vecchio"

View file

@ -585,7 +585,7 @@ CANT_MOVE_MESSAGE = "Kan ikke flytte meldingen"
CANT_SAVE_MESSAGE = "Kan ikke lagre melding"
CANT_SEND_MESSAGE = "Kan ikke sende melding"
INVALID_RECIPIENTS = "Ugyldige mottakere"
Cant_create_folder = "Kan ikke opprette mappe"
CANT_CREATE_FOLDER = "Kan ikke opprette mappe"
CANT_RENAME_FOLDER = "Kan ikke endre navn på mappen"
CANT_DELETE_FOLDER = "Kan ikke slette mappen"
CANT_DELETE_NON_EMPTY_FOLDER = "Kan ikke slette katalog med innhold"

View file

@ -599,8 +599,8 @@ CANT_DELETE_PACKAGE = "Nu pot șterge pachetul. Încercați din nou"
INVALID_PLUGIN_PACKAGE = "Pachetul este invalid"
UNSUPPORTED_PLUGIN_PACKAGE = "Plugin-ul necesită sprijinul complet al serverului"
LICENSING_SERVER_IS_UNAVAILABLE = "Server de abonamente este temporar indisponibil."
LICENSING_EXPIRED = "Licența a expirat."
LICENSING_BANNED = "Licență restricționată."
LICENSING_DOMAIN_EXPIRED = "Subscription for this domain has expired."
LICENSING_DOMAIN_BANNED = "Subscription for this domain is banned."
DEMO_SEND_MESSAGE_ERROR = "Cont demo trimite e-mail la adresele de e-mail externe este interzisă!"
ACCOUNT_ALREADY_EXISTS = "Contul deja există"
MAIL_SERVER_ERROR = "Nu am reușit să accesez serverul de e-mail"

View file

@ -601,8 +601,8 @@ CANT_DELETE_PACKAGE = "Ошибка удаления пакета"
INVALID_PLUGIN_PACKAGE = "Ошибка пакета плагина"
UNSUPPORTED_PLUGIN_PACKAGE = "Для работы плагина необходима полная поддержка сервера"
LICENSING_SERVER_IS_UNAVAILABLE = "Сервер подписок временно не доступен."
LICENSING_EXPIRED = "Подписка на данный домен устарела."
LICENSING_BANNED = "Подписка на данный домен заблокирована."
LICENSING_DOMAIN_EXPIRED = "Подписка на данный домен устарела."
LICENSING_DOMAIN_BANNED = "Подписка на данный домен заблокирована."
DEMO_SEND_MESSAGE_ERROR = "Демо аккаунту отправка писем на внешние почтовые адреса запрещена!"
ACCOUNT_ALREADY_EXISTS = "Аккаунт уже добавлен"
MAIL_SERVER_ERROR = "Ошибка доступа к почтовому серверу"

View file

@ -601,8 +601,8 @@ CANT_DELETE_PACKAGE = "Помилка видалення пакету"
INVALID_PLUGIN_PACKAGE = "Помилка пакету плагіну"
UNSUPPORTED_PLUGIN_PACKAGE = "Для роботи плагину необхідна повна підтримка серверу"
LICENSING_SERVER_IS_UNAVAILABLE = "Сервер підписок тимчасово не доступний."
LICENSING_EXPIRED = "Підписка на цей домен застаріла."
LICENSING_BANNED = "Підписка на цей домен заблокована."
LICENSING_DOMAIN_EXPIRED = "Підписка на цей домен застаріла."
LICENSING_DOMAIN_BANNED = "Підписка на цей домен заблокована."
DEMO_SEND_MESSAGE_ERROR = "Демо акаунту надсилання листів на зовнішні поштові адреси заборонена!"
ACCOUNT_ALREADY_EXISTS = "акаунт вже додано"
MAIL_SERVER_ERROR = "Помилка доступу до поштового серверу"