Page MenuHomePhorge

No OneTemporary

Size
1 MB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/config/config.inc.php.dist b/config/config.inc.php.dist
index 5f0a7c2..e8734eb 100644
--- a/config/config.inc.php.dist
+++ b/config/config.inc.php.dist
@@ -1,88 +1,88 @@
<?php
// This file lists all ActiveSync-related configuration options
// Enables ActiveSync protocol debuging
$config['activesync_debug'] = true;
// Enables logging to a separate directory for every user/device
$config['activesync_user_log'] = false;
-// Enable per-user debugging only if /var/log/syncroton/<username>/ folder exists
+// Enable per-user debugging only if /var/log/kolab-syncroton/<username>/ folder exists
$config['activesync_user_debug'] = false;
// If specified all ActiveSync-related logs will be saved to this file
// Note: This doesn't change Roundcube Framework log locations
$config['activesync_log_file'] = null;
// Type of ActiveSync cache. Supported values: 'db', 'apc' and 'memcache'.
// Note: This is only for some additional data like timezones mapping.
$config['activesync_cache'] = 'db';
// lifetime of ActiveSync cache
// possible units: s, m, h, d, w
$config['activesync_cache_ttl'] = '1d';
// Type of ActiveSync Auth cache. Supported values: 'db', 'apc' and 'memcache'.
// Note: This is only for username canonification map.
$config['activesync_auth_cache'] = 'db';
// lifetime of ActiveSync Auth cache
// possible units: s, m, h, d, w
$config['activesync_auth_cache_ttl'] = '1d';
// List of global addressbooks (GAL)
// Note: If empty 'autocomplete_addressbooks' setting will be used
$config['activesync_addressbooks'] = array();
// ActiveSync => Roundcube contact fields map for GAL search
/* Default: array(
'alias' => 'nickname',
'company' => 'organization',
'displayName' => 'name',
'emailAddress' => 'email',
'firstName' => 'firstname',
'lastName' => 'surname',
'mobilePhone' => 'phone.mobile',
'office' => 'office',
'picture' => 'photo',
'phone' => 'phone',
'title' => 'jobtitle',
);
*/
$config['activesync_gal_fieldmap'] = null;
// List of Roundcube plugins
// WARNING: Not all plugins used in Roundcube can be listed here
$config['activesync_plugins'] = array();
// Defines for how many seconds we'll sleep between every
// action for detecting changes in folders. Default: 60
$config['activesync_ping_timeout'] = 60;
// Defines maximum Ping interval in seconds. Default: 900 (15 minutes)
$config['activesync_ping_interval'] = 900;
// We start detecting changes n seconds since the last sync of a folder
// Default: 180
$config['activesync_quiet_time'] = 180;
// When a device is reqistered, by default a set of folders are
// subscribed for syncronization, i.e. INBOX and personal folders with
// defined folder type:
// mail.drafts, mail.wastebasket, mail.sentitems, mail.outbox,
// event, event.default,
// contact, contact.default,
// task, task.default
// This default set can be extended by adding following values:
// 1 - all subscribed folders in personal namespace
// 2 - all folders in personal namespace
// 4 - all subscribed folders in other users namespace
// 8 - all folders in other users namespace
// 16 - all subscribed folders in shared namespace
// 32 - all folders in shared namespace
$config['activesync_init_subscriptions'] = 0;
// Enables adding sender name in the From: header of send email
// when a device uses email address only (e.g. iOS devices)
$config['activesync_fix_from'] = false;
diff --git a/lib/ext/Syncroton/Command/FolderCreate.php b/lib/ext/Syncroton/Command/FolderCreate.php
index f3787f3..f1764d5 100644
--- a/lib/ext/Syncroton/Command/FolderCreate.php
+++ b/lib/ext/Syncroton/Command/FolderCreate.php
@@ -1,115 +1,115 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync FolderSync command
*
* @package Syncroton
* @subpackage Command
*/
-class Syncroton_Command_FolderCreate extends Syncroton_Command_Wbxml
-{
+class Syncroton_Command_FolderCreate extends Syncroton_Command_Wbxml
+{
protected $_defaultNameSpace = 'uri:FolderHierarchy';
protected $_documentElement = 'FolderCreate';
/**
- *
+ *
* @var Syncroton_Model_Folder
*/
protected $_folder;
/**
* parse FolderCreate request
*
*/
public function handle()
{
$xml = simplexml_import_dom($this->_requestBody);
$syncKey = (int)$xml->SyncKey;
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " synckey is $syncKey");
if (!($this->_syncState = $this->_syncStateBackend->validate($this->_device, 'FolderSync', $syncKey)) instanceof Syncroton_Model_SyncState) {
return;
}
$folder = new Syncroton_Model_Folder($xml);
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " parentId: {$folder->parentId} displayName: {$folder->displayName}");
switch($folder->type) {
case Syncroton_Command_FolderSync::FOLDERTYPE_CALENDAR_USER_CREATED:
$folder->class = Syncroton_Data_Factory::CLASS_CALENDAR;
break;
case Syncroton_Command_FolderSync::FOLDERTYPE_CONTACT_USER_CREATED:
$folder->class = Syncroton_Data_Factory::CLASS_CONTACTS;
break;
case Syncroton_Command_FolderSync::FOLDERTYPE_MAIL_USER_CREATED:
$folder->class = Syncroton_Data_Factory::CLASS_EMAIL;
break;
case Syncroton_Command_FolderSync::FOLDERTYPE_NOTE_USER_CREATED:
$folder->class = Syncroton_Data_Factory::CLASS_NOTES;
break;
case Syncroton_Command_FolderSync::FOLDERTYPE_TASK_USER_CREATED:
$folder->class = Syncroton_Data_Factory::CLASS_TASKS;
break;
default:
- throw new Syncroton_Exception_UnexpectedValue('invalid type defined');
- break;
+ // unsupported type
+ return;
}
-
$dataController = Syncroton_Data_Factory::factory($folder->class, $this->_device, $this->_syncTimeStamp);
$this->_folder = $dataController->createFolder($folder);
$this->_folder->class = $folder->class;
$this->_folder->deviceId = $this->_device;
$this->_folder->creationTime = $this->_syncTimeStamp;
$this->_folderBackend->create($this->_folder);
}
/**
* generate FolderCreate response
*/
public function getResponse()
{
$folderCreate = $this->_outputDom->documentElement;
if (!$this->_syncState instanceof Syncroton_Model_SyncState) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " invalid synckey provided. FolderSync 0 needed.");
$folderCreate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', Syncroton_Command_FolderSync::STATUS_INVALID_SYNC_KEY));
-
+ } else if (!$this->_folder) {
+ $folderCreate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', Syncroton_Command_FolderSync::STATUS_UNKNOWN_ERROR));
} else {
$this->_syncState->counter++;
$this->_syncState->lastsync = $this->_syncTimeStamp;
// store folder in state backend
$this->_syncStateBackend->update($this->_syncState);
// create xml output
$folderCreate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', Syncroton_Command_FolderSync::STATUS_SUCCESS));
$folderCreate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'SyncKey', $this->_syncState->counter));
$folderCreate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'ServerId', $this->_folder->serverId));
}
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/Ping.php b/lib/ext/Syncroton/Command/Ping.php
index a2bd926..2e71fd2 100644
--- a/lib/ext/Syncroton/Command/Ping.php
+++ b/lib/ext/Syncroton/Command/Ping.php
@@ -1,233 +1,237 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync Ping command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_Ping extends Syncroton_Command_Wbxml
{
const STATUS_NO_CHANGES_FOUND = 1;
const STATUS_CHANGES_FOUND = 2;
const STATUS_MISSING_PARAMETERS = 3;
const STATUS_REQUEST_FORMAT_ERROR = 4;
const STATUS_INTERVAL_TO_GREAT_OR_SMALL = 5;
const STATUS_TO_MUCH_FOLDERS = 6;
const STATUS_FOLDER_NOT_FOUND = 7;
const STATUS_GENERAL_ERROR = 8;
protected $_skipValidatePolicyKey = true;
protected $_changesDetected = false;
/**
* @var Syncroton_Backend_StandAlone_Abstract
*/
protected $_dataBackend;
protected $_defaultNameSpace = 'uri:Ping';
protected $_documentElement = 'Ping';
protected $_foldersWithChanges = array();
/**
* process the XML file and add, change, delete or fetches data
*
* @todo can we get rid of LIBXML_NOWARNING
* @todo we need to stored the initial data for folders and lifetime as the phone is sending them only when they change
* @return resource
*/
public function handle()
{
$intervalStart = time();
$status = self::STATUS_NO_CHANGES_FOUND;
// the client does not send a wbxml document, if the Ping parameters did not change compared with the last request
if ($this->_requestBody instanceof DOMDocument) {
$xml = simplexml_import_dom($this->_requestBody);
$xml->registerXPathNamespace('Ping', 'Ping');
if(isset($xml->HeartbeatInterval)) {
$this->_device->pinglifetime = (int)$xml->HeartbeatInterval;
}
if (isset($xml->Folders->Folder)) {
$folders = array();
foreach ($xml->Folders->Folder as $folderXml) {
try {
// does the folder exist?
$folder = $this->_folderBackend->getFolder($this->_device, (string)$folderXml->Id);
$folders[$folder->id] = $folder;
} catch (Syncroton_Exception_NotFound $senf) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $senf->getMessage());
$status = self::STATUS_FOLDER_NOT_FOUND;
break;
}
}
$this->_device->pingfolder = serialize(array_keys($folders));
}
}
$this->_device->lastping = new DateTime('now', new DateTimeZone('utc'));
if ($status == self::STATUS_NO_CHANGES_FOUND) {
$this->_device = $this->_deviceBackend->update($this->_device);
}
$lifeTime = $this->_device->pinglifetime;
- $maxLifeTime = Syncroton_Registry::getPingInterval();
+ $maxInterval = Syncroton_Registry::getPingInterval();
- if ($maxLifeTime > 0 && $lifeTime > $maxLifeTime) {
+ if ($maxInterval <= 0 || $maxInterval > Syncroton_Server::MAX_HEARTBEAT_INTERVAL) {
+ $maxInterval = Syncroton_Server::MAX_HEARTBEAT_INTERVAL;
+ }
+
+ if ($lifeTime > $maxInterval) {
$ping = $this->_outputDom->documentElement;
$ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Status', self::STATUS_INTERVAL_TO_GREAT_OR_SMALL));
- $ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'HeartbeatInterval', $maxLifeTime));
+ $ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'HeartbeatInterval', $maxInterval));
return;
}
$intervalEnd = $intervalStart + $lifeTime;
$secondsLeft = $intervalEnd;
$folders = unserialize($this->_device->pingfolder);
if ($status === self::STATUS_NO_CHANGES_FOUND && (!is_array($folders) || count($folders) == 0)) {
$status = self::STATUS_MISSING_PARAMETERS;
}
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " Folders to monitor($lifeTime / $intervalStart / $intervalEnd / $status): " . print_r($folders, true));
if ($status === self::STATUS_NO_CHANGES_FOUND) {
do {
// take a break to save battery lifetime
sleep(Syncroton_Registry::getPingTimeout());
try {
$device = $this->_deviceBackend->get($this->_device->id);
} catch (Syncroton_Exception_NotFound $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
$status = self::STATUS_FOLDER_NOT_FOUND;
break;
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->err(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
// do nothing, maybe temporal issue, should we stop?
continue;
}
// if another Ping command updated lastping property, we can stop processing this Ping command request
if ((isset($device->lastping) && $device->lastping instanceof DateTime) &&
$device->pingfolder === $this->_device->pingfolder &&
$device->lastping->getTimestamp() > $this->_device->lastping->getTimestamp() ) {
break;
}
$now = new DateTime('now', new DateTimeZone('utc'));
foreach ($folders as $folderId) {
try {
$folder = $this->_folderBackend->get($folderId);
$dataController = Syncroton_Data_Factory::factory($folder->class, $this->_device, $this->_syncTimeStamp);
} catch (Syncroton_Exception_NotFound $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
$status = self::STATUS_FOLDER_NOT_FOUND;
break;
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->err(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
// do nothing, maybe temporal issue, should we stop?
continue;
}
try {
$syncState = $this->_syncStateBackend->getSyncState($this->_device, $folder);
// another process synchronized data of this folder already. let's skip it
if ($syncState->lastsync > $this->_syncTimeStamp) {
continue;
}
// safe battery time by skipping folders which got synchronied less than Syncroton_Registry::getQuietTime() seconds ago
if (($now->getTimestamp() - $syncState->lastsync->getTimestamp()) < Syncroton_Registry::getQuietTime()) {
continue;
}
$foundChanges = $dataController->hasChanges($this->_contentStateBackend, $folder, $syncState);
} catch (Syncroton_Exception_NotFound $e) {
// folder got never synchronized to client
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . ' syncstate not found. enforce sync for folder: ' . $folder->serverId);
$foundChanges = true;
}
if ($foundChanges == true) {
$this->_foldersWithChanges[] = $folder;
$status = self::STATUS_CHANGES_FOUND;
}
}
if ($status != self::STATUS_NO_CHANGES_FOUND) {
break;
}
$secondsLeft = $intervalEnd - time();
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " seconds left: " . $secondsLeft);
// See: http://www.tine20.org/forum/viewtopic.php?f=12&t=12146
//
// break if there are less than PingTimeout + 10 seconds left for the next loop
// otherwise the response will be returned after the client has finished his Ping
// request already maybe
} while ($secondsLeft > (Syncroton_Registry::getPingTimeout() + 10));
}
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " Lifetime: $lifeTime SecondsLeft: $secondsLeft Status: $status)");
$ping = $this->_outputDom->documentElement;
$ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Status', $status));
if($status === self::STATUS_CHANGES_FOUND) {
$folders = $ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Folders'));
foreach($this->_foldersWithChanges as $changedFolder) {
$folder = $folders->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Folder', $changedFolder->serverId));
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " changes in folder: " . $changedFolder->serverId);
}
}
}
/**
* generate ping command response
*
*/
public function getResponse()
{
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/Sync.php b/lib/ext/Syncroton/Command/Sync.php
index 3a3bc26..052eb4c 100644
--- a/lib/ext/Syncroton/Command/Sync.php
+++ b/lib/ext/Syncroton/Command/Sync.php
@@ -1,1108 +1,1121 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync Sync command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_Sync extends Syncroton_Command_Wbxml
{
const STATUS_SUCCESS = 1;
const STATUS_PROTOCOL_VERSION_MISMATCH = 2;
const STATUS_INVALID_SYNC_KEY = 3;
const STATUS_PROTOCOL_ERROR = 4;
const STATUS_SERVER_ERROR = 5;
const STATUS_ERROR_IN_CLIENT_SERVER_CONVERSION = 6;
const STATUS_CONFLICT_MATCHING_THE_CLIENT_AND_SERVER_OBJECT = 7;
const STATUS_OBJECT_NOT_FOUND = 8;
const STATUS_USER_ACCOUNT_MAYBE_OUT_OF_DISK_SPACE = 9;
const STATUS_ERROR_SETTING_NOTIFICATION_GUID = 10;
const STATUS_DEVICE_NOT_PROVISIONED_FOR_NOTIFICATIONS = 11;
const STATUS_FOLDER_HIERARCHY_HAS_CHANGED = 12;
const STATUS_RESEND_FULL_XML = 13;
const STATUS_WAIT_INTERVAL_OUT_OF_RANGE = 14;
const CONFLICT_OVERWRITE_SERVER = 0;
const CONFLICT_OVERWRITE_PIM = 1;
const MIMESUPPORT_DONT_SEND_MIME = 0;
const MIMESUPPORT_SMIME_ONLY = 1;
const MIMESUPPORT_SEND_MIME = 2;
const BODY_TYPE_PLAIN_TEXT = 1;
const BODY_TYPE_HTML = 2;
const BODY_TYPE_RTF = 3;
const BODY_TYPE_MIME = 4;
/**
* truncate types
*/
const TRUNCATE_ALL = 0;
const TRUNCATE_4096 = 1;
const TRUNCATE_5120 = 2;
const TRUNCATE_7168 = 3;
const TRUNCATE_10240 = 4;
const TRUNCATE_20480 = 5;
const TRUNCATE_51200 = 6;
const TRUNCATE_102400 = 7;
const TRUNCATE_NOTHING = 8;
/**
* filter types
*/
const FILTER_NOTHING = 0;
const FILTER_1_DAY_BACK = 1;
const FILTER_3_DAYS_BACK = 2;
const FILTER_1_WEEK_BACK = 3;
const FILTER_2_WEEKS_BACK = 4;
const FILTER_1_MONTH_BACK = 5;
const FILTER_3_MONTHS_BACK = 6;
const FILTER_6_MONTHS_BACK = 7;
const FILTER_INCOMPLETE = 8;
protected $_defaultNameSpace = 'uri:AirSync';
protected $_documentElement = 'Sync';
/**
* list of collections
*
* @var array
*/
protected $_collections = array();
protected $_modifications = array();
/**
* the global WindowSize
*
* @var integer
*/
protected $_globalWindowSize;
/**
* there are more entries than WindowSize available
* the MoreAvailable tag hot added to the xml output
*
* @var boolean
*/
protected $_moreAvailable = false;
/**
* @var Syncroton_Model_SyncState
*/
protected $_syncState;
protected $_maxWindowSize = 100;
protected $_heartbeatInterval = null;
/**
* process the XML file and add, change, delete or fetches data
*/
public function handle()
{
// input xml
$requestXML = simplexml_import_dom($this->_mergeSyncRequest($this->_requestBody, $this->_device));
if (! isset($requestXML->Collections)) {
$this->_outputDom->documentElement->appendChild(
$this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_RESEND_FULL_XML)
);
return $this->_outputDom;
}
-
if (isset($requestXML->HeartbeatInterval)) {
+ $intervalDiv = 1;
$this->_heartbeatInterval = (int)$requestXML->HeartbeatInterval;
- } elseif (isset($requestXML->Wait)) {
- $this->_heartbeatInterval = (int)$requestXML->Wait * 60;
+ } else if (isset($requestXML->Wait)) {
+ $intervalDiv = 60;
+ $this->_heartbeatInterval = (int)$requestXML->Wait * $intervalDiv;
+ }
+
+ $maxInterval = Syncroton_Registry::getPingInterval();
+ if ($maxInterval <= 0 || $maxInterval > Syncroton_Server::MAX_HEARTBEAT_INTERVAL) {
+ $maxInterval = Syncroton_Server::MAX_HEARTBEAT_INTERVAL;
+ }
+
+ if ($this->_heartbeatInterval && $this->_heartbeatInterval > $maxInterval) {
+ $sync = $this->_outputDom->documentElement;
+ $sync->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_WAIT_INTERVAL_OUT_OF_RANGE));
+ $sync->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Limit', floor($maxInterval/$intervalDiv)));
+ $this->_heartbeatInterval = null;
}
$this->_globalWindowSize = isset($requestXML->WindowSize) ? (int)$requestXML->WindowSize : 100;
if (!$this->_globalWindowSize || $this->_globalWindowSize > 512) {
$this->_globalWindowSize = 512;
}
if ($this->_globalWindowSize > $this->_maxWindowSize) {
$this->_globalWindowSize = $this->_maxWindowSize;
}
// load options from lastsynccollection
$lastSyncCollection = array('options' => array());
if (!empty($this->_device->lastsynccollection)) {
$lastSyncCollection = Zend_Json::decode($this->_device->lastsynccollection);
if (!array_key_exists('options', $lastSyncCollection) || !is_array($lastSyncCollection['options'])) {
$lastSyncCollection['options'] = array();
}
}
$collections = array();
foreach ($requestXML->Collections->Collection as $xmlCollection) {
$collectionId = (string)$xmlCollection->CollectionId;
$collections[$collectionId] = new Syncroton_Model_SyncCollection($xmlCollection);
// do we have to reuse the options from the previous request?
if (!isset($xmlCollection->Options) && array_key_exists($collectionId, $lastSyncCollection['options'])) {
$collections[$collectionId]->options = $lastSyncCollection['options'][$collectionId];
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " restored options to " . print_r($collections[$collectionId]->options, TRUE));
}
// store current options for next Sync command request (sticky options)
$lastSyncCollection['options'][$collectionId] = $collections[$collectionId]->options;
}
$this->_device->lastsynccollection = Zend_Json::encode($lastSyncCollection);
if ($this->_device->isDirty()) {
Syncroton_Registry::getDeviceBackend()->update($this->_device);
}
foreach ($collections as $collectionData) {
// has the folder been synchronised to the device already
try {
$collectionData->folder = $this->_folderBackend->getFolder($this->_device, $collectionData->collectionId);
} catch (Syncroton_Exception_NotFound $senf) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " folder {$collectionData->collectionId} not found");
// trigger INVALID_SYNCKEY instead of OBJECT_NOTFOUND when synckey is higher than 0
// to avoid a syncloop for the iPhone
if ($collectionData->syncKey > 0) {
$collectionData->folder = new Syncroton_Model_Folder(array(
'deviceId' => $this->_device,
'serverId' => $collectionData->collectionId
));
}
$this->_collections[$collectionData->collectionId] = $collectionData;
continue;
}
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " SyncKey is {$collectionData->syncKey} Class: {$collectionData->folder->class} CollectionId: {$collectionData->collectionId}");
// initial synckey
if($collectionData->syncKey === 0) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " initial client synckey 0 provided");
// reset sync state for this folder
$this->_syncStateBackend->resetState($this->_device, $collectionData->folder);
$this->_contentStateBackend->resetState($this->_device, $collectionData->folder);
$collectionData->syncState = new Syncroton_Model_SyncState(array(
'device_id' => $this->_device,
'counter' => 0,
'type' => $collectionData->folder,
'lastsync' => $this->_syncTimeStamp
));
$this->_collections[$collectionData->collectionId] = $collectionData;
continue;
}
// check for invalid sycnkey
if(($collectionData->syncState = $this->_syncStateBackend->validate($this->_device, $collectionData->folder, $collectionData->syncKey)) === false) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " invalid synckey {$collectionData->syncKey} provided");
// reset sync state for this folder
$this->_syncStateBackend->resetState($this->_device, $collectionData->folder);
$this->_contentStateBackend->resetState($this->_device, $collectionData->folder);
$this->_collections[$collectionData->collectionId] = $collectionData;
continue;
}
$dataController = Syncroton_Data_Factory::factory($collectionData->folder->class, $this->_device, $this->_syncTimeStamp);
switch($collectionData->folder->class) {
case Syncroton_Data_Factory::CLASS_CALENDAR:
$dataClass = 'Syncroton_Model_Event';
break;
case Syncroton_Data_Factory::CLASS_CONTACTS:
$dataClass = 'Syncroton_Model_Contact';
break;
case Syncroton_Data_Factory::CLASS_EMAIL:
$dataClass = 'Syncroton_Model_Email';
break;
case Syncroton_Data_Factory::CLASS_NOTES:
$dataClass = 'Syncroton_Model_Note';
break;
case Syncroton_Data_Factory::CLASS_TASKS:
$dataClass = 'Syncroton_Model_Task';
break;
default:
throw new Syncroton_Exception_UnexpectedValue('invalid class provided');
break;
}
$clientModifications = array(
'added' => array(),
'changed' => array(),
'deleted' => array(),
'forceAdd' => array(),
'forceChange' => array(),
'toBeFetched' => array(),
);
// handle incoming data
if($collectionData->hasClientAdds()) {
$adds = $collectionData->getClientAdds();
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($adds) . " entries to be added to server");
foreach ($adds as $add) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " add entry with clientId " . (string) $add->ClientId);
try {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " adding entry as new");
$serverId = $dataController->createEntry($collectionData->collectionId, new $dataClass($add->ApplicationData));
$clientModifications['added'][$serverId] = array(
'clientId' => (string)$add->ClientId,
'serverId' => $serverId,
'status' => self::STATUS_SUCCESS,
'contentState' => $this->_contentStateBackend->create(new Syncroton_Model_Content(array(
'device_id' => $this->_device,
'folder_id' => $collectionData->folder,
'contentid' => $serverId,
'creation_time' => $this->_syncTimeStamp,
'creation_synckey' => $collectionData->syncKey + 1
)))
);
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " failed to add entry " . $e->getMessage());
$clientModifications['added'][] = array(
'clientId' => (string)$add->ClientId,
'status' => self::STATUS_SERVER_ERROR
);
}
}
}
// handle changes, but only if not first sync
if($collectionData->syncKey > 1 && $collectionData->hasClientChanges()) {
$changes = $collectionData->getClientChanges();
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($changes) . " entries to be updated on server");
foreach ($changes as $change) {
$serverId = (string)$change->ServerId;
try {
$dataController->updateEntry($collectionData->collectionId, $serverId, new $dataClass($change->ApplicationData));
$clientModifications['changed'][$serverId] = self::STATUS_SUCCESS;
} catch (Syncroton_Exception_AccessDenied $e) {
$clientModifications['changed'][$serverId] = self::STATUS_CONFLICT_MATCHING_THE_CLIENT_AND_SERVER_OBJECT;
$clientModifications['forceChange'][$serverId] = $serverId;
} catch (Syncroton_Exception_NotFound $e) {
// entry does not exist anymore, will get deleted automaticaly
$clientModifications['changed'][$serverId] = self::STATUS_OBJECT_NOT_FOUND;
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " failed to update entry " . $e);
// something went wrong while trying to update the entry
$clientModifications['changed'][$serverId] = self::STATUS_SERVER_ERROR;
}
}
}
// handle deletes, but only if not first sync
if($collectionData->hasClientDeletes()) {
$deletes = $collectionData->getClientDeletes();
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($deletes) . " entries to be deleted on server");
foreach ($deletes as $delete) {
$serverId = (string)$delete->ServerId;
try {
// check if we have sent this entry to the phone
$state = $this->_contentStateBackend->getContentState($this->_device, $collectionData->folder, $serverId);
try {
$dataController->deleteEntry($collectionData->collectionId, $serverId, $collectionData);
} catch(Syncroton_Exception_NotFound $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . ' tried to delete entry ' . $serverId . ' but entry was not found');
} catch (Syncroton_Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . ' tried to delete entry ' . $serverId . ' but a error occured: ' . $e->getMessage());
$clientModifications['forceAdd'][$serverId] = $serverId;
}
$this->_contentStateBackend->delete($state);
} catch (Syncroton_Exception_NotFound $senf) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . ' ' . $serverId . ' should have been removed from client already');
// should we send a special status???
//$collectionData->deleted[$serverId] = self::STATUS_SUCCESS;
}
$clientModifications['deleted'][$serverId] = self::STATUS_SUCCESS;
}
}
// handle fetches, but only if not first sync
if($collectionData->syncKey > 1 && $collectionData->hasClientFetches()) {
// the default value for GetChanges is 1. If the phone don't want the changes it must set GetChanges to 0
// some prevoius versions of iOS did not set GetChanges to 0 for fetches. Let's enforce getChanges to false here.
$collectionData->getChanges = false;
$fetches = $collectionData->getClientFetches();
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($fetches) . " entries to be fetched from server");
$toBeFecthed = array();
foreach ($fetches as $fetch) {
$serverId = (string)$fetch->ServerId;
$toBeFetched[$serverId] = $serverId;
}
$collectionData->toBeFetched = $toBeFetched;
}
$this->_collections[$collectionData->collectionId] = $collectionData;
$this->_modifications[$collectionData->collectionId] = $clientModifications;
}
}
/**
* (non-PHPdoc)
* @see Syncroton_Command_Wbxml::getResponse()
*/
public function getResponse()
{
$sync = $this->_outputDom->documentElement;
$collections = $this->_outputDom->createElementNS('uri:AirSync', 'Collections');
$totalChanges = 0;
// Detect devices that do not support empty Sync reponse
$emptySyncSupported = !preg_match('/(meego|nokian800)/i', $this->_device->useragent);
// continue only if there are changes or no time is left
if ($this->_heartbeatInterval > 0) {
$intervalStart = time();
do {
// take a break to save battery lifetime
sleep(Syncroton_Registry::getPingTimeout());
$now = new DateTime(null, new DateTimeZone('utc'));
foreach($this->_collections as $collectionData) {
// continue immediately if folder does not exist
if (! ($collectionData->folder instanceof Syncroton_Model_IFolder)) {
break 2;
// countinue immediately if syncstate is invalid
} elseif (! ($collectionData->syncState instanceof Syncroton_Model_ISyncState)) {
break 2;
} else {
if ($collectionData->getChanges !== true) {
continue;
}
try {
// just check if the folder still exists
$this->_folderBackend->get($collectionData->folder);
} catch (Syncroton_Exception_NotFound $senf) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " collection does not exist anymore: " . $collectionData->collectionId);
$collectionData->getChanges = false;
// make sure this is the last while loop
// no break 2 here, as we like to check the other folders too
$intervalStart -= $this->_heartbeatInterval;
}
// check that the syncstate still exists and is still valid
try {
$syncState = $this->_syncStateBackend->getSyncState($this->_device, $collectionData->folder);
// another process synchronized data of this folder already. let's skip it
if ($syncState->id !== $collectionData->syncState->id) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " syncstate changed during heartbeat interval for collection: " . $collectionData->folder->serverId);
$collectionData->getChanges = false;
// make sure this is the last while loop
// no break 2 here, as we like to check the other folders too
$intervalStart -= $this->_heartbeatInterval;
}
} catch (Syncroton_Exception_NotFound $senf) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " no syncstate found anymore for collection: " . $collectionData->folder->serverId);
$collectionData->syncState = null;
// make sure this is the last while loop
// no break 2 here, as we like to check the other folders too
$intervalStart -= $this->_heartbeatInterval;
}
// safe battery time by skipping folders which got synchronied less than Syncroton_Command_Ping::$quietTime seconds ago
if ( ! $collectionData->syncState instanceof Syncroton_Model_SyncState ||
($now->getTimestamp() - $collectionData->syncState->lastsync->getTimestamp()) < Syncroton_Registry::getQuietTime()) {
continue;
}
$dataController = Syncroton_Data_Factory::factory($collectionData->folder->class , $this->_device, $this->_syncTimeStamp);
// countinue immediately if there are any changes available
if($dataController->hasChanges($this->_contentStateBackend, $collectionData->folder, $collectionData->syncState)) {
break 2;
}
}
}
// See: http://www.tine20.org/forum/viewtopic.php?f=12&t=12146
//
// break if there are less than PingTimeout + 10 seconds left for the next loop
// otherwise the response will be returned after the client has finished his Ping
// request already maybe
} while (time() - $intervalStart < $this->_heartbeatInterval - (Syncroton_Registry::getPingTimeout() + 10));
}
foreach($this->_collections as $collectionData) {
$collectionChanges = 0;
/**
* keep track of entries added on server side
*/
$newContentStates = array();
/**
* keep track of entries deleted on server side
*/
$deletedContentStates = array();
// invalid collectionid provided
if (! ($collectionData->folder instanceof Syncroton_Model_IFolder)) {
$collection = $collections->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Collection'));
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'SyncKey', 0));
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $collectionData->collectionId));
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_FOLDER_HIERARCHY_HAS_CHANGED));
// invalid synckey provided
} elseif (! ($collectionData->syncState instanceof Syncroton_Model_ISyncState)) {
// set synckey to 0
$collection = $collections->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Collection'));
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'SyncKey', 0));
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $collectionData->collectionId));
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_INVALID_SYNC_KEY));
// initial sync
} elseif ($collectionData->syncState->counter === 0) {
$collectionData->syncState->counter++;
// initial sync
// send back a new SyncKey only
$collection = $collections->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Collection'));
if (!empty($collectionData->folder->class)) {
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Class', $collectionData->folder->class));
}
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'SyncKey', $collectionData->syncState->counter));
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $collectionData->collectionId));
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_SUCCESS));
} else {
$dataController = Syncroton_Data_Factory::factory($collectionData->folder->class , $this->_device, $this->_syncTimeStamp);
$clientModifications = $this->_modifications[$collectionData->collectionId];
$serverModifications = array(
'added' => array(),
'changed' => array(),
'deleted' => array(),
);
if($collectionData->getChanges === true) {
// continue sync session?
if(is_array($collectionData->syncState->pendingdata)) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " restored from sync state ");
$serverModifications = $collectionData->syncState->pendingdata;
} elseif ($dataController->hasChanges($this->_contentStateBackend, $collectionData->folder, $collectionData->syncState)) {
// update _syncTimeStamp as $dataController->hasChanges might have spent some time
$this->_syncTimeStamp = new DateTime(null, new DateTimeZone('utc'));
try {
// fetch entries added since last sync
$allClientEntries = $this->_contentStateBackend->getFolderState(
$this->_device,
$collectionData->folder
);
$allServerEntries = $dataController->getServerEntries(
$collectionData->collectionId,
$collectionData->options['filterType']
);
// add entries
$serverDiff = array_diff($allServerEntries, $allClientEntries);
// add entries which produced problems during delete from client
$serverModifications['added'] = $clientModifications['forceAdd'];
// add entries not yet sent to client
$serverModifications['added'] = array_unique(array_merge($serverModifications['added'], $serverDiff));
// @todo still needed?
foreach($serverModifications['added'] as $id => $serverId) {
// skip entries added by client during this sync session
if(isset($clientModifications['added'][$serverId]) && !isset($clientModifications['forceAdd'][$serverId])) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped added entry: " . $serverId);
unset($serverModifications['added'][$id]);
}
}
// entries to be deleted
$serverModifications['deleted'] = array_diff($allClientEntries, $allServerEntries);
// fetch entries changed since last sync
$serverModifications['changed'] = $dataController->getChangedEntries(
$collectionData->collectionId,
$collectionData->syncState->lastsync,
$this->_syncTimeStamp,
$collectionData->options['filterType']
);
$serverModifications['changed'] = array_merge($serverModifications['changed'], $clientModifications['forceChange']);
foreach($serverModifications['changed'] as $id => $serverId) {
// skip entry, if it got changed by client during current sync
if(isset($clientModifications['changed'][$serverId]) && !isset($clientModifications['forceChange'][$serverId])) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped changed entry: " . $serverId);
unset($serverModifications['changed'][$id]);
}
// skip entry, make sure we don't sent entries already added by client in this request
else if (isset($clientModifications['added'][$serverId]) && !isset($clientModifications['forceAdd'][$serverId])) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped change for added entry: " . $serverId);
unset($serverModifications['changed'][$id]);
}
}
// entries comeing in scope are already in $serverModifications['added'] and do not need to
// be send with $serverCanges
$serverModifications['changed'] = array_diff($serverModifications['changed'], $serverModifications['added']);
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . " Folder state checking failed: " . $e->getMessage());
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " Folder state checking failed: " . $e->getTraceAsString());
// Prevent from removing client entries when getServerEntries() fails
// @todo: should we set Status and break the loop here?
$serverModifications = array(
'added' => array(),
'changed' => array(),
'deleted' => array(),
);
}
}
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " found (added/changed/deleted) " . count($serverModifications['added']) . '/' . count($serverModifications['changed']) . '/' . count($serverModifications['deleted']) . ' entries for sync from server to client');
}
// collection header
$collection = $this->_outputDom->createElementNS('uri:AirSync', 'Collection');
if (!empty($collectionData->folder->class)) {
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Class', $collectionData->folder->class));
}
$syncKeyElement = $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'SyncKey'));
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $collectionData->collectionId));
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_SUCCESS));
$responses = $this->_outputDom->createElementNS('uri:AirSync', 'Responses');
// send reponse for newly added entries
if(!empty($clientModifications['added'])) {
foreach($clientModifications['added'] as $entryData) {
$add = $responses->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Add'));
$add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ClientId', $entryData['clientId']));
// we have no serverId is the add failed
if(isset($entryData['serverId'])) {
$add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $entryData['serverId']));
}
$add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', $entryData['status']));
}
}
// send reponse for changed entries
if(!empty($clientModifications['changed'])) {
foreach($clientModifications['changed'] as $serverId => $status) {
if ($status !== Syncroton_Command_Sync::STATUS_SUCCESS) {
$change = $responses->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Change'));
$change->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $serverId));
$change->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', $status));
}
}
}
// send response for to be fetched entries
if(!empty($collectionData->toBeFetched)) {
// unset all truncation settings as entries are not allowed to be truncated during fetch
$fetchCollectionData = clone $collectionData;
// unset truncationSize
if (isset($fetchCollectionData->options['bodyPreferences']) && is_array($fetchCollectionData->options['bodyPreferences'])) {
foreach($fetchCollectionData->options['bodyPreferences'] as $key => $bodyPreference) {
unset($fetchCollectionData->options['bodyPreferences'][$key]['truncationSize']);
}
}
$fetchCollectionData->options['mimeTruncation'] = Syncroton_Command_Sync::TRUNCATE_NOTHING;
foreach($collectionData->toBeFetched as $serverId) {
$fetch = $responses->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Fetch'));
$fetch->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $serverId));
try {
$applicationData = $this->_outputDom->createElementNS('uri:AirSync', 'ApplicationData');
$dataController
->getEntry($fetchCollectionData, $serverId)
->appendXML($applicationData, $this->_device);
$fetch->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_SUCCESS));
$fetch->appendChild($applicationData);
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage());
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getTraceAsString());
$fetch->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_OBJECT_NOT_FOUND));
}
}
}
if ($responses->hasChildNodes() === true) {
$collection->appendChild($responses);
}
$commands = $this->_outputDom->createElementNS('uri:AirSync', 'Commands');
foreach($serverModifications['added'] as $id => $serverId) {
if($collectionChanges == $collectionData->windowSize || $totalChanges + $collectionChanges >= $this->_globalWindowSize) {
break;
}
#/**
# * somewhere is a problem in the logic for handling moreAvailable
# *
# * it can happen, that we have a contentstate (which means we sent the entry to the client
# * and that this entry is yet in $collectionData->syncState->pendingdata['serverAdds']
# * I have no idea how this can happen, but the next lines of code work around this problem
# */
#try {
# $this->_contentStateBackend->getContentState($this->_device, $collectionData->folder, $serverId);
#
# if ($this->_logger instanceof Zend_Log)
# $this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped an entry($serverId) which is already on the client");
#
# unset($serverModifications['added'][$id]);
# continue;
#
#} catch (Syncroton_Exception_NotFound $senf) {
# // do nothing => content state should not exist yet
#}
try {
$add = $this->_outputDom->createElementNS('uri:AirSync', 'Add');
$add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $serverId));
$applicationData = $add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ApplicationData'));
$dataController
->getEntry($collectionData, $serverId)
->appendXML($applicationData, $this->_device);
$commands->appendChild($add);
$collectionChanges++;
} catch (Syncroton_Exception_MemoryExhausted $seme) {
// continue to next entry, as there is not enough memory left for the current entry
// this will lead to MoreAvailable at the end and the entry will be synced during the next Sync command
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " memory exhausted for entry: " . $serverId);
continue;
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage());
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getTraceAsString());
}
// mark as sent to the client, even the conversion to xml might have failed
$newContentStates[] = new Syncroton_Model_Content(array(
'device_id' => $this->_device,
'folder_id' => $collectionData->folder,
'contentid' => $serverId,
'creation_time' => $this->_syncTimeStamp,
'creation_synckey' => $collectionData->syncState->counter + 1
));
unset($serverModifications['added'][$id]);
}
/**
* process entries changed on server side
*/
foreach($serverModifications['changed'] as $id => $serverId) {
if($collectionChanges == $collectionData->windowSize || $totalChanges + $collectionChanges >= $this->_globalWindowSize) {
break;
}
try {
$change = $this->_outputDom->createElementNS('uri:AirSync', 'Change');
$change->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $serverId));
$applicationData = $change->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ApplicationData'));
$dataController
->getEntry($collectionData, $serverId)
->appendXML($applicationData, $this->_device);
$commands->appendChild($change);
$collectionChanges++;
} catch (Syncroton_Exception_MemoryExhausted $seme) {
// continue to next entry, as there is not enough memory left for the current entry
// this will lead to MoreAvailable at the end and the entry will be synced during the next Sync command
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " memory exhausted for entry: " . $serverId);
continue;
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage());
}
unset($serverModifications['changed'][$id]);
}
foreach($serverModifications['deleted'] as $id => $serverId) {
if($collectionChanges == $collectionData->windowSize || $totalChanges + $collectionChanges >= $this->_globalWindowSize) {
break;
}
try {
// check if we have sent this entry to the phone
$state = $this->_contentStateBackend->getContentState($this->_device, $collectionData->folder, $serverId);
$delete = $this->_outputDom->createElementNS('uri:AirSync', 'Delete');
$delete->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $serverId));
$deletedContentStates[] = $state;
$commands->appendChild($delete);
$collectionChanges++;
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage());
}
unset($serverModifications['deleted'][$id]);
}
$countOfPendingChanges = (count($serverModifications['added']) + count($serverModifications['changed']) + count($serverModifications['deleted']));
if ($countOfPendingChanges > 0) {
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'MoreAvailable'));
} else {
$serverModifications = null;
}
if ($commands->hasChildNodes() === true) {
$collection->appendChild($commands);
}
$totalChanges += $collectionChanges;
// increase SyncKey if needed
if ((
// sent the clients updates... ?
!empty($clientModifications['added']) ||
!empty($clientModifications['changed']) ||
!empty($clientModifications['deleted'])
) || (
// is the server sending updates to the client... ?
$commands->hasChildNodes() === true
) || (
// changed the pending data... ?
$collectionData->syncState->pendingdata != $serverModifications
)
) {
// ...then increase SyncKey
$collectionData->syncState->counter++;
}
$syncKeyElement->appendChild($this->_outputDom->createTextNode($collectionData->syncState->counter));
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " current synckey is ". $collectionData->syncState->counter);
if (!$emptySyncSupported || $collection->childNodes->length > 4 || $collectionData->syncState->counter != $collectionData->syncKey) {
$collections->appendChild($collection);
}
}
if (isset($collectionData->syncState) &&
$collectionData->syncState instanceof Syncroton_Model_ISyncState &&
$collectionData->syncState->counter != $collectionData->syncKey
) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " update syncState for collection: " . $collectionData->collectionId);
// store pending data in sync state when needed
if(isset($countOfPendingChanges) && $countOfPendingChanges > 0) {
$collectionData->syncState->pendingdata = array(
'added' => (array)$serverModifications['added'],
'changed' => (array)$serverModifications['changed'],
'deleted' => (array)$serverModifications['deleted']
);
} else {
$collectionData->syncState->pendingdata = null;
}
if (!empty($clientModifications['added'])) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " remove previous synckey as client added new entries");
$keepPreviousSyncKey = false;
} else {
$keepPreviousSyncKey = true;
}
$collectionData->syncState->lastsync = clone $this->_syncTimeStamp;
// increment sync timestamp by 1 second
$collectionData->syncState->lastsync->modify('+1 sec');
try {
$transactionId = Syncroton_Registry::getTransactionManager()->startTransaction(Syncroton_Registry::getDatabase());
// store new synckey
$this->_syncStateBackend->create($collectionData->syncState, $keepPreviousSyncKey);
// store contentstates for new entries added to client
foreach($newContentStates as $state) {
$this->_contentStateBackend->create($state);
}
// remove contentstates for entries to be deleted on client
foreach($deletedContentStates as $state) {
$this->_contentStateBackend->delete($state);
}
Syncroton_Registry::getTransactionManager()->commitTransaction($transactionId);
} catch (Zend_Db_Statement_Exception $zdse) {
// something went wrong
// maybe another parallel request added a new synckey
// we must remove data added from client
if (!empty($clientModifications['added'])) {
foreach ($clientModifications['added'] as $added) {
$this->_contentStateBackend->delete($added['contentState']);
$dataController->deleteEntry($collectionData->collectionId, $added['serverId'], array());
}
}
Syncroton_Registry::getTransactionManager()->rollBack();
throw $zdse;
}
}
// store current filter type
try {
$folderState = $this->_folderBackend->get($collectionData->folder);
$folderState->lastfiltertype = $collectionData->options['filterType'];
if ($folderState->isDirty()) {
$this->_folderBackend->update($folderState);
}
} catch (Syncroton_Exception_NotFound $senf) {
// failed to get folderstate => should not happen but is also no problem in this state
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . ' failed to get folder state for: ' . $collectionData->collectionId);
}
}
if ($collections->hasChildNodes() === true) {
$sync->appendChild($collections);
}
if ($sync->hasChildNodes()) {
return $this->_outputDom;
}
return null;
}
/**
* remove Commands and Supported from collections XML tree
*
* @param DOMDocument $document
* @return DOMDocument
*/
protected function _cleanUpXML(DOMDocument $document)
{
$cleanedDocument = clone $document;
$xpath = new DomXPath($cleanedDocument);
$xpath->registerNamespace('AirSync', 'uri:AirSync');
$collections = $xpath->query("//AirSync:Sync/AirSync:Collections/AirSync:Collection");
// remove Commands and Supported elements
foreach ($collections as $collection) {
foreach (array('Commands', 'Supported') as $element) {
$childrenToRemove = $collection->getElementsByTagName($element);
foreach ($childrenToRemove as $childToRemove) {
$collection->removeChild($childToRemove);
}
}
}
return $cleanedDocument;
}
/**
* merge a partial XML document with the XML document from the previous request
*
* @param DOMDocument|null $requestBody
* @return SimpleXMLElement
*/
protected function _mergeSyncRequest($requestBody, Syncroton_Model_Device $device)
{
$lastSyncCollection = array();
if (!empty($device->lastsynccollection)) {
$lastSyncCollection = Zend_Json::decode($device->lastsynccollection);
if (!empty($lastSyncCollection['lastXML'])) {
$lastXML = new DOMDocument();
$lastXML->loadXML($lastSyncCollection['lastXML']);
}
}
if (! $requestBody instanceof DOMDocument && isset($lastXML) && $lastXML instanceof DOMDocument) {
$requestBody = $lastXML;
} elseif (! $requestBody instanceof DOMDocument) {
throw new Syncroton_Exception_UnexpectedValue('no xml body found');
}
if ($requestBody->getElementsByTagName('Partial')->length > 0) {
$partialBody = clone $requestBody;
$requestBody = $lastXML;
$xpath = new DomXPath($requestBody);
$xpath->registerNamespace('AirSync', 'uri:AirSync');
foreach ($partialBody->documentElement->childNodes as $child) {
if (! $child instanceof DOMElement) {
continue;
}
if ($child->tagName == 'Partial') {
continue;
}
if ($child->tagName == 'Collections') {
foreach ($child->getElementsByTagName('Collection') as $updatedCollection) {
$collectionId = $updatedCollection->getElementsByTagName('CollectionId')->item(0)->nodeValue;
$existingCollections = $xpath->query("//AirSync:Sync/AirSync:Collections/AirSync:Collection[AirSync:CollectionId='$collectionId']");
if ($existingCollections->length > 0) {
$existingCollection = $existingCollections->item(0);
foreach ($updatedCollection->childNodes as $updatedCollectionChild) {
if (! $updatedCollectionChild instanceof DOMElement) {
continue;
}
$duplicateChild = $existingCollection->getElementsByTagName($updatedCollectionChild->tagName);
if ($duplicateChild->length > 0) {
$existingCollection->replaceChild($requestBody->importNode($updatedCollectionChild, TRUE), $duplicateChild->item(0));
} else {
$existingCollection->appendChild($requestBody->importNode($updatedCollectionChild, TRUE));
}
}
} else {
$importedCollection = $requestBody->importNode($updatedCollection, TRUE);
}
}
} else {
$duplicateChild = $xpath->query("//AirSync:Sync/AirSync:{$child->tagName}");
if ($duplicateChild->length > 0) {
$requestBody->documentElement->replaceChild($requestBody->importNode($child, TRUE), $duplicateChild->item(0));
} else {
$requestBody->documentElement->appendChild($requestBody->importNode($child, TRUE));
}
}
}
}
$lastSyncCollection['lastXML'] = $this->_cleanUpXML($requestBody)->saveXML();
$device->lastsynccollection = Zend_Json::encode($lastSyncCollection);
return $requestBody;
}
}
diff --git a/lib/ext/Syncroton/Server.php b/lib/ext/Syncroton/Server.php
index e89b0fe..0fa4855 100644
--- a/lib/ext/Syncroton/Server.php
+++ b/lib/ext/Syncroton/Server.php
@@ -1,445 +1,446 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle incoming http ActiveSync requests
*
* @package Syncroton
*/
class Syncroton_Server
{
const PARAMETER_ATTACHMENTNAME = 0;
const PARAMETER_COLLECTIONID = 1;
const PARAMETER_ITEMID = 3;
const PARAMETER_OPTIONS = 7;
+ const MAX_HEARTBEAT_INTERVAL = 3540; // 59 minutes
protected $_body;
/**
* informations about the currently device
*
* @var Syncroton_Backend_IDevice
*/
protected $_deviceBackend;
/**
* @var Zend_Log
*/
protected $_logger;
/**
* @var Zend_Controller_Request_Http
*/
protected $_request;
protected $_userId;
public function __construct($userId, Zend_Controller_Request_Http $request = null, $body = null)
{
if (Syncroton_Registry::isRegistered('loggerBackend')) {
$this->_logger = Syncroton_Registry::get('loggerBackend');
}
$this->_userId = $userId;
$this->_request = $request instanceof Zend_Controller_Request_Http ? $request : new Zend_Controller_Request_Http();
$this->_body = $body !== null ? $body : fopen('php://input', 'r');
$this->_deviceBackend = Syncroton_Registry::getDeviceBackend();
}
public function handle()
{
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . ' REQUEST METHOD: ' . $this->_request->getMethod());
switch($this->_request->getMethod()) {
case 'OPTIONS':
$this->_handleOptions();
break;
case 'POST':
$this->_handlePost();
break;
case 'GET':
echo "It works!<br>Your userid is: {$this->_userId} and your IP address is: {$_SERVER['REMOTE_ADDR']}.";
break;
}
}
/**
* handle options request
*/
protected function _handleOptions()
{
$command = new Syncroton_Command_Options();
$this->_sendHeaders($command->getHeaders());
}
protected function _sendHeaders(array $headers)
{
foreach ($headers as $name => $value) {
header($name . ': ' . $value);
}
}
/**
* handle post request
*/
protected function _handlePost()
{
$requestParameters = $this->_getRequestParameters($this->_request);
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . ' REQUEST ' . print_r($requestParameters, true));
$className = 'Syncroton_Command_' . $requestParameters['command'];
if(!class_exists($className)) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . " command not supported: " . $requestParameters['command']);
header("HTTP/1.1 501 not implemented");
return;
}
// get user device
$device = $this->_getUserDevice($this->_userId, $requestParameters);
if ($requestParameters['contentType'] == 'application/vnd.ms-sync.wbxml' || $requestParameters['contentType'] == 'application/vnd.ms-sync') {
// decode wbxml request
try {
$decoder = new Syncroton_Wbxml_Decoder($this->_body);
$requestBody = $decoder->decode();
if ($this->_logger instanceof Zend_Log) {
$requestBody->formatOutput = true;
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml request:\n" . $requestBody->saveXML());
}
} catch(Syncroton_Wbxml_Exception_UnexpectedEndOfFile $e) {
$requestBody = NULL;
}
} else {
$requestBody = $this->_body;
}
header("MS-Server-ActiveSync: 14.00.0536.000");
// avoid sending HTTP header "Content-Type: text/html" for empty sync responses
ini_set('default_mimetype', 'application/vnd.ms-sync.wbxml');
try {
$command = new $className($requestBody, $device, $requestParameters);
$command->handle();
$response = $command->getResponse();
} catch (Syncroton_Exception_ProvisioningNeeded $sepn) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " provisioning needed");
header("HTTP/1.1 449 Retry after sending a PROVISION command");
if (version_compare($device->acsversion, '14.0', '>=')) {
$response = $sepn->domDocument;
} else {
// pre 14.0 method
return;
}
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . " unexpected exception occured: " . get_class($e));
if ($this->_logger instanceof Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . " exception message: " . $e->getMessage());
if ($this->_logger instanceof Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . " " . $e->getTraceAsString());
header("HTTP/1.1 500 Internal server error");
return;
}
if ($response instanceof DOMDocument) {
if ($this->_logger instanceof Zend_Log) {
$this->_logDomDocument(Zend_Log::DEBUG, $response, __METHOD__, __LINE__);
}
if (isset($command) && $command instanceof Syncroton_Command_ICommand) {
$this->_sendHeaders($command->getHeaders());
}
$outputStream = fopen("php://temp", 'r+');
$encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
try {
$encoder->encode($response);
} catch (Syncroton_Wbxml_Exception $swe) {
if ($this->_logger instanceof Zend_Log) {
$this->_logger->err(__METHOD__ . '::' . __LINE__ . " Could not encode output: " . $swe);
$this->_logDomDocument(Zend_Log::ERR, $response, __METHOD__, __LINE__);
}
header("HTTP/1.1 500 Internal server error");
return;
}
if ($requestParameters['acceptMultipart'] == true) {
$parts = $command->getParts();
// output multipartheader
$bodyPartCount = 1 + count($parts);
// number of parts (4 bytes)
$header = pack('i', $bodyPartCount);
$partOffset = 4 + (($bodyPartCount * 2) * 4);
// wbxml body start and length
$streamStat = fstat($outputStream);
$header .= pack('ii', $partOffset, $streamStat['size']);
$partOffset += $streamStat['size'];
// calculate start and length of parts
foreach ($parts as $partId => $partStream) {
rewind($partStream);
$streamStat = fstat($partStream);
// part start and length
$header .= pack('ii', $partOffset, $streamStat['size']);
$partOffset += $streamStat['size'];
}
echo $header;
}
// output body
rewind($outputStream);
fpassthru($outputStream);
// output multiparts
if (isset($parts)) {
foreach ($parts as $partStream) {
rewind($partStream);
fpassthru($partStream);
}
}
}
}
/**
* write (possible big) DOMDocument in smaller chunks to log file
*
* @param unknown $priority
* @param DOMDocument $dom
* @param string $method
* @param int $line
*/
protected function _logDomDocument($priority, DOMDocument $dom, $method, $line)
{
$loops = 0;
$tempStream = fopen('php://temp/maxmemory:5242880', 'r+');
$dom->formatOutput = true;
fwrite($tempStream, $dom->saveXML());
$dom->formatOutput = false;
rewind($tempStream);
// log data in 1MByte chunks
while (!feof($tempStream)) {
$this->_logger->log($method . '::' . $line . " xml response($loops):\n" . fread($tempStream, 1048576), $priority);
$loops++;
}
fclose($tempStream);
}
/**
* return request params
*
* @return array
*/
protected function _getRequestParameters(Zend_Controller_Request_Http $request)
{
if (strpos($request->getRequestUri(), '&') === false) {
$commands = array(
0 => 'Sync',
1 => 'SendMail',
2 => 'SmartForward',
3 => 'SmartReply',
4 => 'GetAttachment',
9 => 'FolderSync',
10 => 'FolderCreate',
11 => 'FolderDelete',
12 => 'FolderUpdate',
13 => 'MoveItems',
14 => 'GetItemEstimate',
15 => 'MeetingResponse',
16 => 'Search',
17 => 'Settings',
18 => 'Ping',
19 => 'ItemOperations',
20 => 'Provision',
21 => 'ResolveRecipients',
22 => 'ValidateCert'
);
$requestParameters = substr($request->getRequestUri(), strpos($request->getRequestUri(), '?'));
$stream = fopen("php://temp", 'r+');
fwrite($stream, base64_decode($requestParameters));
rewind($stream);
// unpack the first 4 bytes
$unpacked = unpack('CprotocolVersion/Ccommand/vlocale', fread($stream, 4));
// 140 => 14.0
$protocolVersion = substr($unpacked['protocolVersion'], 0, -1) . '.' . substr($unpacked['protocolVersion'], -1);
$command = $commands[$unpacked['command']];
$locale = $unpacked['locale'];
// unpack deviceId
$length = ord(fread($stream, 1));
if ($length > 0) {
$toUnpack = fread($stream, $length);
$unpacked = unpack("H" . ($length * 2) . "string", $toUnpack);
$deviceId = $unpacked['string'];
}
// unpack policyKey
$length = ord(fread($stream, 1));
if ($length > 0) {
$unpacked = unpack('Vstring', fread($stream, $length));
$policyKey = $unpacked['string'];
}
// unpack device type
$length = ord(fread($stream, 1));
if ($length > 0) {
$unpacked = unpack('A' . $length . 'string', fread($stream, $length));
$deviceType = $unpacked['string'];
}
while (! feof($stream)) {
$tag = ord(fread($stream, 1));
$length = ord(fread($stream, 1));
switch ($tag) {
case self::PARAMETER_ATTACHMENTNAME:
$unpacked = unpack('A' . $length . 'string', fread($stream, $length));
$attachmentName = $unpacked['string'];
break;
case self::PARAMETER_COLLECTIONID:
$unpacked = unpack('A' . $length . 'string', fread($stream, $length));
$collectionId = $unpacked['string'];
break;
case self::PARAMETER_ITEMID:
$unpacked = unpack('A' . $length . 'string', fread($stream, $length));
$itemId = $unpacked['string'];
break;
case self::PARAMETER_OPTIONS:
$options = ord(fread($stream, 1));
$saveInSent = !!($options & 0x01);
$acceptMultiPart = !!($options & 0x02);
break;
default:
if ($this->_logger instanceof Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . " found unhandled command parameters");
}
}
$result = array(
'protocolVersion' => $protocolVersion,
'command' => $command,
'deviceId' => $deviceId,
'deviceType' => isset($deviceType) ? $deviceType : null,
'policyKey' => isset($policyKey) ? $policyKey : null,
'saveInSent' => isset($saveInSent) ? $saveInSent : false,
'collectionId' => isset($collectionId) ? $collectionId : null,
'itemId' => isset($itemId) ? $itemId : null,
'attachmentName' => isset($attachmentName) ? $attachmentName : null,
'acceptMultipart' => isset($acceptMultiPart) ? $acceptMultiPart : false
);
} else {
$result = array(
'protocolVersion' => $request->getServer('HTTP_MS_ASPROTOCOLVERSION'),
'command' => $request->getQuery('Cmd'),
'deviceId' => $request->getQuery('DeviceId'),
'deviceType' => $request->getQuery('DeviceType'),
'policyKey' => $request->getServer('HTTP_X_MS_POLICYKEY'),
'saveInSent' => $request->getQuery('SaveInSent') == 'T',
'collectionId' => $request->getQuery('CollectionId'),
'itemId' => $request->getQuery('ItemId'),
'attachmentName' => $request->getQuery('AttachmentName'),
'acceptMultipart' => $request->getServer('HTTP_MS_ASACCEPTMULTIPART') == 'T'
);
}
$result['userAgent'] = $request->getServer('HTTP_USER_AGENT', $result['deviceType']);
$result['contentType'] = $request->getServer('CONTENT_TYPE');
return $result;
}
/**
* get existing device of owner or create new device for owner
*
* @param unknown_type $ownerId
* @param unknown_type $deviceId
* @param unknown_type $deviceType
* @param unknown_type $userAgent
* @param unknown_type $protocolVersion
* @return Syncroton_Model_Device
*/
protected function _getUserDevice($ownerId, $requestParameters)
{
try {
$device = $this->_deviceBackend->getUserDevice($ownerId, $requestParameters['deviceId']);
$device->useragent = $requestParameters['userAgent'];
$device->acsversion = $requestParameters['protocolVersion'];
if ($device->isDirty()) {
$device = $this->_deviceBackend->update($device);
}
} catch (Syncroton_Exception_NotFound $senf) {
$device = $this->_deviceBackend->create(new Syncroton_Model_Device(array(
'owner_id' => $ownerId,
'deviceid' => $requestParameters['deviceId'],
'devicetype' => $requestParameters['deviceType'],
'useragent' => $requestParameters['userAgent'],
'acsversion' => $requestParameters['protocolVersion'],
'policyId' => Syncroton_Registry::isRegistered(Syncroton_Registry::DEFAULT_POLICY) ? Syncroton_Registry::get(Syncroton_Registry::DEFAULT_POLICY) : null
)));
}
return $device;
}
}
diff --git a/lib/init.php b/lib/init.php
index 5a35fa8..1941ad2 100644
--- a/lib/init.php
+++ b/lib/init.php
@@ -1,67 +1,64 @@
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com> |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
define('KOLAB_SYNC_START', microtime(true));
// Roundcube Framework constants
define('RCUBE_INSTALL_PATH', realpath(dirname(__FILE__) . '/../') . '/');
define('RCUBE_PLUGINS_DIR', RCUBE_INSTALL_PATH . 'lib/plugins/');
// Define include path
$include_path = RCUBE_INSTALL_PATH . 'lib' . PATH_SEPARATOR;
$include_path .= RCUBE_INSTALL_PATH . 'lib/ext' . PATH_SEPARATOR;
$include_path .= ini_get('include_path');
set_include_path($include_path);
-// @TODO: what is a reasonable value for ActiveSync?
-@set_time_limit(600);
-
// include global functions from Roundcube Framework
require_once 'Roundcube/bootstrap.php';
// Register main autoloader
spl_autoload_register('kolab_sync_autoload');
// Autoloader for Syncroton
//require_once 'Zend/Loader/Autoloader.php';
//$autoloader = Zend_Loader_Autoloader::getInstance();
//$autoloader->setFallbackAutoloader(true);
/**
* Use PHP5 autoload for dynamic class loading
*/
function kolab_sync_autoload($classname)
{
// Syncroton, replacement for Zend autoloader
$filename = str_replace('_', DIRECTORY_SEPARATOR, $classname);
if ($fp = @fopen("$filename.php", 'r', true)) {
fclose($fp);
include_once "$filename.php";
return true;
}
return false;
}
diff --git a/lib/kolab_sync.php b/lib/kolab_sync.php
index 803752d..839f7f1 100644
--- a/lib/kolab_sync.php
+++ b/lib/kolab_sync.php
@@ -1,474 +1,477 @@
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com> |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
/**
* Main application class (based on Roundcube Framework)
*/
class kolab_sync extends rcube
{
/**
* Application name
*
* @var string
*/
public $app_name = 'ActiveSync for Kolab'; // no double quotes inside
/**
* Current user
*
* @var rcube_user
*/
public $user;
public $username;
public $password;
const CHARSET = 'UTF-8';
- const VERSION = "2.3";
+ const VERSION = "2.3.2";
/**
* This implements the 'singleton' design pattern
*
* @return kolab_sync The one and only instance
*/
static function get_instance()
{
if (!self::$instance || !is_a(self::$instance, 'kolab_sync')) {
self::$instance = new kolab_sync();
self::$instance->startup(); // init AFTER object was linked with self::$instance
}
return self::$instance;
}
/**
* Initialization of class instance
*/
public function startup()
{
// Initialize Syncroton Logger
$debug_mode = $this->config->get('activesync_debug') ? kolab_sync_logger::DEBUG : kolab_sync_logger::WARN;
$this->logger = new kolab_sync_logger($debug_mode);
// Get list of plugins
// WARNING: We can use only plugins that are prepared for this
// e.g. are not using output or rcmail objects or
// doesn't throw errors when using them
$plugins = (array)$this->config->get('activesync_plugins', array('kolab_auth'));
- $required = array('libkolab');
+ $plugins = array_unique(array_merge($plugins, array('libkolab')));
// Initialize/load plugins
$this->plugins = kolab_sync_plugin_api::get_instance();
$this->plugins->init($this, $this->task);
- $this->plugins->load_plugins($plugins, $required);
+
+ // this way we're compatible with Roundcube Framework 1.2
+ // we can't use load_plugins() here
+ foreach ($plugins as $plugin) {
+ $this->plugins->load_plugin($plugin, true);
+ }
}
/**
* Application execution (authentication and ActiveSync)
*/
public function run()
{
- $this->plugins->exec_hook('startup', array('task' => 'login'));
-
// when used with (f)cgi no PHP_AUTH* variables are available without defining a special rewrite rule
if (!isset($_SERVER['PHP_AUTH_USER'])) {
// "Basic didhfiefdhfu4fjfjdsa34drsdfterrde..."
if (isset($_SERVER["REMOTE_USER"])) {
$basicAuthData = base64_decode(substr($_SERVER["REMOTE_USER"], 6));
} elseif (isset($_SERVER["REDIRECT_REMOTE_USER"])) {
$basicAuthData = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6));
} elseif (isset($_SERVER["Authorization"])) {
$basicAuthData = base64_decode(substr($_SERVER["Authorization"], 6));
} elseif (isset($_SERVER["HTTP_AUTHORIZATION"])) {
$basicAuthData = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6));
}
if (isset($basicAuthData) && !empty($basicAuthData)) {
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(":", $basicAuthData);
}
}
if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
// Convert domain.tld\username into username@domain (?)
$username = explode("\\", $_SERVER['PHP_AUTH_USER']);
if (count($username) == 2) {
$_SERVER['PHP_AUTH_USER'] = $username[1];
if (!strpos($_SERVER['PHP_AUTH_USER'], '@') && !empty($username[0])) {
$_SERVER['PHP_AUTH_USER'] .= '@' . $username[0];
}
}
// Authenticate the user
$userid = $this->authenticate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
}
if (empty($userid)) {
header('WWW-Authenticate: Basic realm="' . $this->app_name .'"');
header('HTTP/1.1 401 Unauthorized');
exit;
}
// Set log directory per-user
$this->set_log_dir($this->username ?: $_SERVER['PHP_AUTH_USER']);
// Save user password for Roundcube Framework
$this->password = $_SERVER['PHP_AUTH_PW'];
// Register Syncroton backends
Syncroton_Registry::set('loggerBackend', $this->logger);
Syncroton_Registry::set(Syncroton_Registry::DATABASE, $this->get_dbh());
Syncroton_Registry::set(Syncroton_Registry::TRANSACTIONMANAGER, kolab_sync_transaction_manager::getInstance());
Syncroton_Registry::set(Syncroton_Registry::DEVICEBACKEND, new kolab_sync_backend_device);
Syncroton_Registry::set(Syncroton_Registry::FOLDERBACKEND, new kolab_sync_backend_folder);
Syncroton_Registry::set(Syncroton_Registry::SYNCSTATEBACKEND, new kolab_sync_backend_state);
Syncroton_Registry::set(Syncroton_Registry::CONTENTSTATEBACKEND, new kolab_sync_backend_content);
Syncroton_Registry::set(Syncroton_Registry::POLICYBACKEND, new kolab_sync_backend_policy);
Syncroton_Registry::setContactsDataClass('kolab_sync_data_contacts');
Syncroton_Registry::setCalendarDataClass('kolab_sync_data_calendar');
Syncroton_Registry::setEmailDataClass('kolab_sync_data_email');
Syncroton_Registry::setNotesDataClass('kolab_sync_data_notes');
Syncroton_Registry::setTasksDataClass('kolab_sync_data_tasks');
Syncroton_Registry::setGALDataClass('kolab_sync_data_gal');
// Configuration
Syncroton_Registry::set(Syncroton_Registry::PING_TIMEOUT, $this->config->get('activesync_ping_timeout', 60));
Syncroton_Registry::set(Syncroton_Registry::PING_INTERVAL, $this->config->get('activesync_ping_interval', 15 * 60));
Syncroton_Registry::set(Syncroton_Registry::QUIET_TIME, $this->config->get('activesync_quiet_time', 3 * 60));
// Run Syncroton
$syncroton = new Syncroton_Server($userid);
$syncroton->handle();
}
/**
* Authenticates a user
*
* @param string $username User name
* @param string $password User password
*
* @param int User ID
*/
public function authenticate($username, $password)
{
// use shared cache for kolab_auth plugin result (username canonification)
$cache = $this->get_cache_shared('activesync_auth');
$host = $this->select_host($username);
$cache_key = sha1($username . '::' . $host);
if (!$cache || !($auth = $cache->get($cache_key))) {
$auth = $this->plugins->exec_hook('authenticate', array(
'host' => $host,
'user' => $username,
'pass' => $password,
));
if (!$auth['abort'] && $cache) {
$cache->set($cache_key, array(
'user' => $auth['user'],
'host' => $auth['host'],
));
}
// LDAP server failure... send 503 error
if ($auth['kolab_ldap_error']) {
self::server_error();
}
}
else {
$auth['pass'] = $password;
}
// Authenticate - get Roundcube user ID
if (!$auth['abort'] && ($userid = $this->login($auth['user'], $auth['pass'], $auth['host'], $err))) {
// set real username
$this->username = $auth['user'];
return $userid;
}
$this->plugins->exec_hook('login_failed', array(
'host' => $auth['host'],
'user' => $auth['user'],
));
// IMAP server failure... send 503 error
if ($err == rcube_imap_generic::ERROR_BAD) {
self::server_error();
}
}
/**
* Storage host selection
*/
private function select_host($username)
{
// Get IMAP host
$host = $this->config->get('default_host');
if (is_array($host)) {
list($user, $domain) = explode('@', $username);
// try to select host by mail domain
if (!empty($domain)) {
foreach ($host as $storage_host => $mail_domains) {
if (is_array($mail_domains) && in_array_nocase($domain, $mail_domains)) {
$host = $storage_host;
break;
}
else if (stripos($storage_host, $domain) !== false || stripos(strval($mail_domains), $domain) !== false) {
$host = is_numeric($storage_host) ? $mail_domains : $storage_host;
break;
}
}
}
// take the first entry if $host is not found
if (is_array($host)) {
list($key, $val) = each($host);
$host = is_numeric($key) ? $val : $key;
}
}
return rcube_utils::parse_host($host);
}
/**
* Authenticates a user in IMAP and returns Roundcube user ID.
*/
private function login($username, $password, $host, &$error = null)
{
if (empty($username)) {
return null;
}
$login_lc = $this->config->get('login_lc');
$default_port = $this->config->get('default_port', 143);
// parse $host
$a_host = parse_url($host);
if ($a_host['host']) {
$host = $a_host['host'];
$ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
if (!empty($a_host['port'])) {
$port = $a_host['port'];
}
else if ($ssl && $ssl != 'tls' && (!$default_port || $default_port == 143)) {
$port = 993;
}
}
if (!$port) {
$port = $default_port;
}
// Convert username to lowercase. If storage backend
// is case-insensitive we need to store always the same username
if ($login_lc) {
if ($login_lc == 2 || $login_lc === true) {
$username = mb_strtolower($username);
}
else if (strpos($username, '@')) {
// lowercase domain name
list($local, $domain) = explode('@', $username);
$username = $local . '@' . mb_strtolower($domain);
}
}
// Here we need IDNA ASCII
// Only rcube_contacts class is using domain names in Unicode
$host = rcube_utils::idn_to_ascii($host);
$username = rcube_utils::idn_to_ascii($username);
// user already registered?
if ($user = rcube_user::query($username, $host)) {
$username = $user->data['username'];
}
// authenticate user in IMAP
$storage = $this->get_storage();
if (!$storage->connect($host, $username, $password, $port, $ssl)) {
$error = $storage->get_error_code();
return null;
}
// No user in database, but IMAP auth works
if (!is_object($user)) {
if ($this->config->get('auto_create_user')) {
// create a new user record
$user = rcube_user::create($username, $host);
if (!$user) {
self::raise_error(array(
'code' => 620, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__,
'message' => "Failed to create a user record",
), true, false);
return null;
}
}
else {
self::raise_error(array(
'code' => 620, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__,
'message' => "Access denied for new user $username. 'auto_create_user' is disabled",
), true, false);
return null;
}
}
// overwrite config with user preferences
$this->user = $user;
$this->config->set_user_prefs((array)$this->user->get_prefs());
$this->set_storage_prop();
// required by rcube_utils::parse_host() later
$_SESSION['storage_host'] = $host;
setlocale(LC_ALL, 'en_US.utf8', 'en_US.UTF-8');
// force reloading of mailboxes list/data
//$storage->clear_cache('mailboxes', true);
return $user->ID;
}
/**
* Set logging directory per-user
*/
protected function set_log_dir($username)
{
if (empty($username)) {
return;
}
$this->logger->set_username($username);
$user_debug = $this->config->get('activesync_user_debug');
$user_log = $user_debug || $this->config->get('activesync_user_log');
if (!$user_log) {
return;
}
$log_dir = $this->config->get('log_dir');
$log_dir .= DIRECTORY_SEPARATOR . $username;
// in user_debug mode enable logging only if user directory exists
if ($user_debug) {
if (!is_dir($log_dir)) {
return;
}
}
else if (!is_dir($log_dir)) {
if (!mkdir($log_dir, 0770)) {
return;
}
}
if (!empty($_GET['DeviceId'])) {
$log_dir .= DIRECTORY_SEPARATOR . $_GET['DeviceId'];
}
if (!is_dir($log_dir)) {
if (!mkdir($log_dir, 0770)) {
return;
}
}
// make sure we're using debug mode where possible,
if ($user_debug) {
$this->config->set('debug_level', 1);
$this->config->set('memcache_debug', true);
$this->config->set('imap_debug', true);
$this->config->set('ldap_debug', true);
$this->config->set('smtp_debug', true);
$this->config->set('sql_debug', true);
// SQL/IMAP debug need to be set directly on the object instance
// it's already initialized/configured
if ($db = $this->get_dbh()) {
$db->set_debug(true);
}
if ($storage = $this->get_storage()) {
$storage->set_debug(true);
}
$this->logger->mode = kolab_sync_logger::DEBUG;
}
$this->config->set('log_dir', $log_dir);
// re-set PHP error logging
if (($this->config->get('debug_level') & 1) && $this->config->get('log_driver') != 'syslog') {
ini_set('error_log', $log_dir . '/errors');
}
}
/**
* Send HTTP 503 response.
* We send it on LDAP/IMAP server error instead of 401 (Unauth),
* so devices will not ask for new password.
*/
public static function server_error()
{
header("HTTP/1.1 503 Service Temporarily Unavailable");
header("Retry-After: 120");
exit;
}
/**
* Function to be executed in script shutdown
*/
public function shutdown()
{
parent::shutdown();
// cache garbage collector
$this->gc_run();
// write performance stats to logs/console
if ($this->config->get('devel_mode')) {
if (function_exists('memory_get_usage'))
$mem = sprintf('%.1f', memory_get_usage() / 1048576);
if (function_exists('memory_get_peak_usage'))
$mem .= '/' . sprintf('%.1f', memory_get_peak_usage() / 1048576);
$query = $_SERVER['QUERY_STRING'];
$log = $query . ($mem ? ($query ? ' ' : '') . "[$mem]" : '');
if (defined('KOLAB_SYNC_START'))
self::print_timer(KOLAB_SYNC_START, $log);
else
self::console($log);
}
}
}
diff --git a/lib/kolab_sync_backend.php b/lib/kolab_sync_backend.php
index 56a411d..a1c24a8 100644
--- a/lib/kolab_sync_backend.php
+++ b/lib/kolab_sync_backend.php
@@ -1,952 +1,953 @@
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com> |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
class kolab_sync_backend
{
/**
* Singleton instace of kolab_sync_backend
*
* @var kolab_sync_backend
*/
static protected $instance;
protected $storage;
protected $folder_meta;
protected $folder_uids;
protected $root_meta;
static protected $types = array(
1 => '',
2 => 'mail.inbox',
3 => 'mail.drafts',
4 => 'mail.wastebasket',
5 => 'mail.sentitems',
6 => 'mail.outbox',
7 => 'task.default',
8 => 'event.default',
9 => 'contact.default',
10 => 'note.default',
11 => 'journal.default',
12 => 'mail',
13 => 'event',
14 => 'contact',
15 => 'task',
16 => 'journal',
17 => 'note',
);
static protected $classes = array(
Syncroton_Data_Factory::CLASS_CALENDAR => 'event',
Syncroton_Data_Factory::CLASS_CONTACTS => 'contact',
Syncroton_Data_Factory::CLASS_EMAIL => 'mail',
Syncroton_Data_Factory::CLASS_NOTES => 'note',
Syncroton_Data_Factory::CLASS_TASKS => 'task',
);
const ROOT_MAILBOX = 'INBOX';
// const ROOT_MAILBOX = '';
const ASYNC_KEY = '/private/vendor/kolab/activesync';
const UID_KEY = '/shared/vendor/cmu/cyrus-imapd/uniqueid';
/**
* This implements the 'singleton' design pattern
*
* @return kolab_sync_backend The one and only instance
*/
static function get_instance()
{
if (!self::$instance) {
self::$instance = new kolab_sync_backend;
self::$instance->startup(); // init AFTER object was linked with self::$instance
}
return self::$instance;
}
/**
* Class initialization
*/
public function startup()
{
$this->storage = rcube::get_instance()->get_storage();
// @TODO: reset cache? if we do this for every request the cache would be useless
// There's no session here
//$this->storage->clear_cache('mailboxes.', true);
// set additional header used by libkolab
$this->storage->set_options(array(
// @TODO: there can be Roundcube plugins defining additional headers,
// we maybe would need to add them here
'fetch_headers' => 'X-KOLAB-TYPE X-KOLAB-MIME-VERSION',
'skip_deleted' => true,
'threading' => false,
));
// Disable paging
$this->storage->set_pagesize(999999);
}
/**
* List known devices
*
* @return array Device list as hash array
*/
public function devices_list()
{
if ($this->root_meta === null) {
// @TODO: consider server annotation instead of INBOX
if ($meta = $this->storage->get_metadata(self::ROOT_MAILBOX, self::ASYNC_KEY)) {
$this->root_meta = $this->unserialize_metadata($meta[self::ROOT_MAILBOX][self::ASYNC_KEY]);
}
else {
$this->root_meta = array();
}
}
if (!empty($this->root_meta['DEVICE']) && is_array($this->root_meta['DEVICE'])) {
return $this->root_meta['DEVICE'];
}
return array();
}
/**
* Get list of folders available for sync
*
* @param string $deviceid Device identifier
* @param string $type Folder type
*
* @return array|bool List of mailbox folders, False on backend failure
*/
public function folders_list($deviceid, $type)
{
// get all folders of specified type
$folders = (array) kolab_storage::list_folders('', '*', $type, false, $typedata);
// get folders activesync config
$folderdata = $this->folder_meta();
if (!is_array($folders) || !is_array($folderdata)) {
return false;
}
$folders_list = array();
// check if folders are "subscribed" for activesync
foreach ($folderdata as $folder => $meta) {
if (empty($meta['FOLDER']) || empty($meta['FOLDER'][$deviceid])
|| empty($meta['FOLDER'][$deviceid]['S'])
) {
continue;
}
if (!empty($type) && !in_array($folder, $folders)) {
continue;
}
// Activesync folder identifier (serverId)
$folder_type = $typedata[$folder];
$folder_id = self::folder_id($folder, $folder_type);
$folders_list[$folder_id] = $this->folder_data($folder, $folder_type);
}
return $folders_list;
}
/**
* Getter for folder metadata
*
* @return array|bool Hash array with meta data for each folder, False on backend failure
*/
public function folder_meta()
{
if (!isset($this->folder_meta)) {
$this->folder_meta = array();
// get folders activesync config
$folderdata = $this->storage->get_metadata("*", self::ASYNC_KEY);
if (!is_array($folderdata)) {
return false;
}
foreach ($folderdata as $folder => $meta) {
if ($asyncdata = $meta[self::ASYNC_KEY]) {
if ($metadata = $this->unserialize_metadata($asyncdata)) {
$this->folder_meta[$folder] = $metadata;
}
}
}
}
return $this->folder_meta;
}
/**
* Creates folder and subscribes to the device
*
* @param string $name Folder name (UTF7-IMAP)
* @param int $type Folder (ActiveSync) type
* @param string $deviceid Device identifier
*
* @return bool True on success, False on failure
*/
public function folder_create($name, $type, $deviceid)
{
if ($this->storage->folder_exists($name)) {
$created = true;
}
else {
$type = self::type_activesync2kolab($type);
$created = kolab_storage::folder_create($name, $type, true);
}
if ($created) {
// Set ActiveSync subscription flag
$this->folder_set($name, $deviceid, 1);
return true;
}
return false;
}
/**
* Renames a folder
*
* @param string $old_name Old folder name (UTF7-IMAP)
* @param string $new_name New folder name (UTF7-IMAP)
* @param int $type Folder (ActiveSync) type
*
* @return bool True on success, False on failure
*/
public function folder_rename($old_name, $new_name, $type)
{
$this->folder_meta = null;
$type = self::type_activesync2kolab($type);
// don't use kolab_storage for moving mail folders
if (preg_match('/^mail/', $type)) {
return $this->storage->rename_folder($old_name, $new_name);
}
else {
return kolab_storage::folder_rename($old_name, $new_name);
}
}
/**
* Deletes folder
*
* @param string $name Folder name (UTF7-IMAP)
* @param string $deviceid Device identifier
*
*/
public function folder_delete($name, $deviceid)
{
unset($this->folder_meta[$name]);
return kolab_storage::folder_delete($name);
}
/**
* Sets ActiveSync subscription flag on a folder
*
* @param string $name Folder name (UTF7-IMAP)
* @param string $deviceid Device identifier
* @param int $flag Flag value (0|1|2)
*/
public function folder_set($name, $deviceid, $flag)
{
if (empty($deviceid)) {
return false;
}
// get folders activesync config
$metadata = $this->folder_meta();
if (!is_array($metadata)) {
return false;
}
$metadata = $metadata[$name];
if ($flag) {
if (empty($metadata)) {
$metadata = array();
}
if (empty($metadata['FOLDER'])) {
$metadata['FOLDER'] = array();
}
if (empty($metadata['FOLDER'][$deviceid])) {
$metadata['FOLDER'][$deviceid] = array();
}
// Z-Push uses:
// 1 - synchronize, no alarms
// 2 - synchronize with alarms
$metadata['FOLDER'][$deviceid]['S'] = $flag;
}
if (!$flag) {
unset($metadata['FOLDER'][$deviceid]['S']);
if (empty($metadata['FOLDER'][$deviceid])) {
unset($metadata['FOLDER'][$deviceid]);
}
if (empty($metadata['FOLDER'])) {
unset($metadata['FOLDER']);
}
if (empty($metadata)) {
$metadata = null;
}
}
// Return if nothing's been changed
if (!self::data_array_diff($this->folder_meta[$name], $metadata)) {
return true;
}
$this->folder_meta[$name] = $metadata;
return $this->storage->set_metadata($name, array(
self::ASYNC_KEY => $this->serialize_metadata($metadata)));
}
public function device_get($id)
{
$devices_list = $this->devices_list();
$result = $devices_list[$id];
return $result;
}
/**
* Registers new device on server
*
* @param array $device Device data
* @param string $id Device ID
*
* @return bool True on success, False on failure
*/
public function device_create($device, $id)
{
// Fill local cache
$this->devices_list();
// Some devices create dummy devices with name "validate" (#1109)
// This device entry is used in two initial requests, but later
// the device registers a real name. We can remove this dummy entry
// on new device creation
$this->device_delete('validate');
// Old Kolab_ZPush device parameters
// MODE: -1 | 0 | 1 (not set | flatmode | foldermode)
// TYPE: device type string
// ALIAS: user-friendly device name
// Syncroton (kolab_sync_backend_device) uses
// ID: internal identifier in syncroton database
// TYPE: device type string
// ALIAS: user-friendly device name
$metadata = $this->root_meta;
$metadata['DEVICE'][$id] = $device;
$metadata = array(self::ASYNC_KEY => $this->serialize_metadata($metadata));
$result = $this->storage->set_metadata(self::ROOT_MAILBOX, $metadata);
if ($result) {
// Update local cache
$this->root_meta['DEVICE'][$id] = $device;
// subscribe default set of folders
$this->device_init_subscriptions($id);
}
return $result;
}
/**
* Device update.
*
* @param array $device Device data
* @param string $id Device ID
*
* @return bool True on success, False on failure
*/
public function device_update($device, $id)
{
$devices_list = $this->devices_list();
$old_device = $devices_list[$id];
if (!$old_device) {
return false;
}
// Do nothing if nothing is changed
if (!self::data_array_diff($old_device, $device)) {
return true;
}
$device = array_merge($old_device, $device);
$metadata = $this->root_meta;
$metadata['DEVICE'][$id] = $device;
$metadata = array(self::ASYNC_KEY => $this->serialize_metadata($metadata));
$result = $this->storage->set_metadata(self::ROOT_MAILBOX, $metadata);
if ($result) {
// Update local cache
$this->root_meta['DEVICE'][$id] = $device;
}
return $result;
}
/**
* Device delete.
*
* @param string $id Device ID
*
* @return bool True on success, False on failure
*/
public function device_delete($id)
{
$device = $this->device_get($id);
if (!$device) {
return false;
}
unset($this->root_meta['DEVICE'][$id], $this->root_meta['FOLDER'][$id]);
if (empty($this->root_meta['DEVICE'])) {
unset($this->root_meta['DEVICE']);
}
if (empty($this->root_meta['FOLDER'])) {
unset($this->root_meta['FOLDER']);
}
$metadata = $this->serialize_metadata($this->root_meta);
$metadata = array(self::ASYNC_KEY => $metadata);
// update meta data
$result = $this->storage->set_metadata(self::ROOT_MAILBOX, $metadata);
if ($result) {
// remove device annotation for every folder
foreach ($this->folder_meta() as $folder => $meta) {
// skip root folder (already handled above)
if ($folder == self::ROOT_MAILBOX)
continue;
if (!empty($meta['FOLDER']) && isset($meta['FOLDER'][$id])) {
unset($meta['FOLDER'][$id]);
if (empty($meta['FOLDER'])) {
unset($this->folder_meta[$folder]['FOLDER']);
unset($meta['FOLDER']);
}
if (empty($meta)) {
unset($this->folder_meta[$folder]);
$meta = null;
}
$metadata = array(self::ASYNC_KEY => $this->serialize_metadata($meta));
$res = $this->storage->set_metadata($folder, $metadata);
if ($res && $meta) {
$this->folder_meta[$folder] = $meta;
}
}
}
}
return $result;
}
/**
* Subscribe default set of folders on device registration
*/
private function device_init_subscriptions($deviceid)
{
// INBOX always exists
$this->folder_set('INBOX', $deviceid, 1);
$supported_types = array(
'mail.drafts',
'mail.wastebasket',
'mail.sentitems',
'mail.outbox',
'event.default',
'contact.default',
'note.default',
'task.default',
'event',
'contact',
'note',
'task'
);
// This default set can be extended by adding following values:
$modes = array(
'SUB_PERSONAL' => 1, // all subscribed folders in personal namespace
'ALL_PERSONAL' => 2, // all folders in personal namespace
'SUB_OTHER' => 4, // all subscribed folders in other users namespace
'ALL_OTHER' => 8, // all folders in other users namespace
'SUB_SHARED' => 16, // all subscribed folders in shared namespace
'ALL_SHARED' => 32, // all folders in shared namespace
);
$rcube = rcube::get_instance();
$config = $rcube->config;
$mode = (int) $config->get('activesync_init_subscriptions');
$folders = array();
// Subscribe to default folders
$foldertypes = kolab_storage::folders_typedata();
if (!empty($foldertypes)) {
$_foldertypes = array_intersect($foldertypes, $supported_types);
// get default folders
foreach ($_foldertypes as $folder => $type) {
// only personal folders
if ($this->storage->folder_namespace($folder) == 'personal') {
$flag = preg_match('/^(event|task)/', $type) ? 2 : 1;
$this->folder_set($folder, $deviceid, $flag);
$folders[] = $folder;
}
}
}
// we're in default mode, exit
if (!$mode) {
return;
}
// below we support additionally all mail folders
$supported_types[] = 'mail';
$supported_types[] = 'mail.junkemail';
// get configured special folders
$special_folders = array();
$map = array(
'drafts' => 'mail.drafts',
'junk' => 'mail.junkemail',
'sent' => 'mail.sentitems',
'trash' => 'mail.wastebasket',
);
foreach ($map as $folder => $type) {
if ($folder = $config->get($folder . '_mbox')) {
$special_folders[$folder] = $type;
}
}
// get folders list(s)
if (($mode & $modes['ALL_PERSONAL']) || ($mode & $modes['ALL_OTHER']) || ($mode & $modes['ALL_SHARED'])) {
$all_folders = $this->storage->list_folders();
if (($mode & $modes['SUB_PERSONAL']) || ($mode & $modes['SUB_OTHER']) || ($mode & $modes['SUB_SHARED'])) {
$subscribed_folders = $this->storage->list_folders_subscribed();
}
}
else {
$all_folders = $this->storage->list_folders_subscribed();
}
foreach ($all_folders as $folder) {
// folder already subscribed
if (in_array($folder, $folders)) {
continue;
}
$type = $foldertypes[$folder] ?: 'mail';
if ($type == 'mail' && isset($special_folders[$folder])) {
$type = $special_folders[$folder];
}
if (!in_array($type, $supported_types)) {
continue;
}
$ns = strtoupper($this->storage->folder_namespace($folder));
// subscribe the folder according to configured mode
// and folder namespace/subscription status
if (($mode & $modes["ALL_$ns"])
|| (($mode & $modes["SUB_$ns"])
&& (!isset($subscribed_folders) || in_array($folder, $subscribed_folders)))
) {
$flag = preg_match('/^(event|task)/', $type) ? 2 : 1;
$this->folder_set($folder, $deviceid, $flag);
}
}
}
/**
* Helper method to decode saved IMAP metadata
*/
private function unserialize_metadata($str)
{
if (!empty($str)) {
// Support old Z-Push annotation format
if ($str[0] != '{') {
$str = base64_decode($str);
}
$data = json_decode($str, true);
return $data;
}
return null;
}
/**
* Helper method to encode IMAP metadata for saving
*/
private function serialize_metadata($data)
{
if (!empty($data) && is_array($data)) {
$data = json_encode($data);
// $data = base64_encode($data);
return $data;
}
return null;
}
/**
* Returns Kolab folder type for specified ActiveSync type ID
*/
public static function type_activesync2kolab($type)
{
if (!empty(self::$types[$type])) {
return self::$types[$type];
}
return '';
}
/**
* Returns ActiveSync folder type for specified Kolab type
*/
public static function type_kolab2activesync($type)
{
if ($key = array_search($type, self::$types)) {
return $key;
}
return key(self::$types);
}
/**
* Returns Kolab folder type for specified ActiveSync class name
*/
public static function class_activesync2kolab($class)
{
if (!empty(self::$classes[$class])) {
return self::$classes[$class];
}
return '';
}
private function folder_data($folder, $type)
{
// Folder name parameters
$delim = $this->storage->get_hierarchy_delimiter();
$items = explode($delim, $folder);
$name = array_pop($items);
// Folder UID
$folder_id = $this->folder_id($folder, $type);
// Folder type
$type = self::type_kolab2activesync($type);
// fix type, if there's no type annotation it's detected as UNKNOWN
// we'll use 'mail' (12) or 'mail.inbox' (2)
if ($type == 1) {
$type = $folder == 'INBOX' ? 2 : 12;
}
// Syncroton folder data array
return array(
'serverId' => $folder_id,
'parentId' => count($items) ? self::folder_id(implode($delim, $items)) : 0,
'displayName' => rcube_charset::convert($name, 'UTF7-IMAP', kolab_sync::CHARSET),
'type' => $type,
);
}
/**
* Builds folder ID based on folder name
*/
public function folder_id($name, $type = null)
{
// ActiveSync expects folder identifiers to be max.64 characters
// So we can't use just folder name
if ($name === '' || !is_string($name)) {
return null;
}
if (isset($this->folder_uids[$name])) {
return $this->folder_uids[$name];
}
/*
@TODO: For now uniqueid annotation doesn't work, we will create UIDs by ourselves.
There's one inconvenience of this solution: folder name/type change
would be handled in ActiveSync as delete + create.
// get folders unique identifier
$folderdata = $this->storage->get_metadata($name, self::UID_KEY);
if ($folderdata && !empty($folderdata[$name])) {
$uid = $folderdata[$name][self::UID_KEY];
return $this->folder_uids[$name] = $uid;
}
*/
// Add type to folder UID hash, so type change can be detected by Syncroton
$uid = $name . '!!' . ($type !== null ? $type : kolab_storage::folder_type($name));
$uid = md5($uid);
return $this->folder_uids[$name] = $uid;
}
/**
* Returns IMAP folder name
*
* @param string $id Folder identifier
* @param string $deviceid Device dentifier
*
* @return string Folder name (UTF7-IMAP)
*/
public function folder_id2name($id, $deviceid)
{
// check in cache first
if (!empty($this->folder_uids)) {
if (($name = array_search($id, $this->folder_uids)) !== false) {
return $name;
}
}
/*
@TODO: see folder_id()
// get folders unique identifier
$folderdata = $this->storage->get_metadata('*', self::UID_KEY);
foreach ((array)$folderdata as $folder => $data) {
if (!empty($data[self::UID_KEY])) {
$uid = $data[self::UID_KEY];
$this->folder_uids[$folder] = $uid;
if ($uid == $id) {
$name = $folder;
}
}
}
*/
// get all folders of specified type
$folderdata = $this->folder_meta();
- if (!is_array($folderdata)) {
+ if (!is_array($folderdata) || $id === null) {
return null;
}
// check if folders are "subscribed" for activesync
foreach ($folderdata as $folder => $meta) {
if (empty($meta['FOLDER']) || empty($meta['FOLDER'][$deviceid])
|| empty($meta['FOLDER'][$deviceid]['S'])
) {
continue;
}
- $uid = self::folder_id($folder);
- $this->folder_uids[$folder] = $uid;
+ if ($uid = self::folder_id($folder)) {
+ $this->folder_uids[$folder] = $uid;
+ }
- if ($uid == $id) {
+ if ($uid === $id) {
$name = $folder;
}
}
return $name;
}
/**
*/
public function modseq_set($deviceid, $folderid, $synctime, $data)
{
$synctime = $synctime->format('Y-m-d H:i:s');
$rcube = rcube::get_instance();
$db = $rcube->get_dbh();
$old_data = $this->modseq[$folderid][$synctime];
if (empty($old_data)) {
$this->modseq[$folderid][$synctime] = $data;
$data = json_encode($data);
$db->set_option('ignore_key_errors', true);
$db->query("INSERT INTO `syncroton_modseq` (`device_id`, `folder_id`, `synctime`, `data`)"
." VALUES (?, ?, ?, ?)",
$deviceid, $folderid, $synctime, $data);
$db->set_option('ignore_key_errors', false);
}
}
public function modseq_get($deviceid, $folderid, $synctime)
{
$synctime = $synctime->format('Y-m-d H:i:s');
if (empty($this->modseq[$folderid][$synctime])) {
$this->modseq[$folderid] = array();
$rcube = rcube::get_instance();
$db = $rcube->get_dbh();
$db->limitquery("SELECT `data`, `synctime` FROM `syncroton_modseq`"
." WHERE `device_id` = ? AND `folder_id` = ? AND `synctime` <= ?"
." ORDER BY `synctime` DESC",
0, 1, $deviceid, $folderid, $synctime);
if ($row = $db->fetch_assoc()) {
$synctime = $row['synctime'];
// @TODO: make sure synctime from sql is in "Y-m-d H:i:s" format
$this->modseq[$folderid][$synctime] = json_decode($row['data'], true);
}
// Cleanup: remove all records except the current one
$db->query("DELETE FROM `syncroton_modseq`"
." WHERE `device_id` = ? AND `folder_id` = ? AND `synctime` <> ?",
$deviceid, $folderid, $synctime);
}
return @$this->modseq[$folderid][$synctime];
}
/**
* Set state of relation objects at specified point in time
*/
public function relations_state_set($deviceid, $folderid, $synctime, $relations)
{
$synctime = $synctime->format('Y-m-d H:i:s');
$rcube = rcube::get_instance();
$db = $rcube->get_dbh();
$old_data = $this->relations[$folderid][$synctime];
if (empty($old_data)) {
$this->relations[$folderid][$synctime] = $relations;
$data = rcube_charset::clean(json_encode($relations));
$db->set_option('ignore_key_errors', true);
$db->query("INSERT INTO `syncroton_relations_state`"
." (`device_id`, `folder_id`, `synctime`, `data`)"
." VALUES (?, ?, ?, ?)",
$deviceid, $folderid, $synctime, $data);
$db->set_option('ignore_key_errors', false);
}
}
/**
* Get state of relation objects at specified point in time
*/
public function relations_state_get($deviceid, $folderid, $synctime)
{
$synctime = $synctime->format('Y-m-d H:i:s');
if (empty($this->relations[$folderid][$synctime])) {
$this->relations[$folderid] = array();
$rcube = rcube::get_instance();
$db = $rcube->get_dbh();
$db->limitquery("SELECT `data`, `synctime` FROM `syncroton_relations_state`"
." WHERE `device_id` = ? AND `folder_id` = ? AND `synctime` <= ?"
." ORDER BY `synctime` DESC",
0, 1, $deviceid, $folderid, $synctime);
if ($row = $db->fetch_assoc()) {
$synctime = $row['synctime'];
// @TODO: make sure synctime from sql is in "Y-m-d H:i:s" format
$this->relations[$folderid][$synctime] = json_decode($row['data'], true);
}
// Cleanup: remove all records except the current one
$db->query("DELETE FROM `syncroton_relations_state`"
." WHERE `device_id` = ? AND `folder_id` = ? AND `synctime` <> ?",
$deviceid, $folderid, $synctime);
}
return @$this->relations[$folderid][$synctime];
}
/**
* Compares two arrays
*
* @param array $array1
* @param array $array2
*
* @return bool True if arrays differs, False otherwise
*/
private static function data_array_diff($array1, $array2)
{
if (!is_array($array1) || !is_array($array2)) {
return $array1 != $array2;
}
if (count($array1) != count($array2)) {
return true;
}
foreach ($array1 as $key => $val) {
if (!array_key_exists($key, $array2)) {
return true;
}
if ($val !== $array2[$key]) {
return true;
}
}
return false;
}
}
diff --git a/lib/kolab_sync_data.php b/lib/kolab_sync_data.php
index 59bff31..3c650ad 100644
--- a/lib/kolab_sync_data.php
+++ b/lib/kolab_sync_data.php
@@ -1,1779 +1,1780 @@
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com> |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
/**
* Base class for Syncroton data backends
*/
abstract class kolab_sync_data implements Syncroton_Data_IData
{
/**
* ActiveSync protocol version
*
* @var int
*/
protected $asversion = 0;
/**
* information about the current device
*
* @var Syncroton_Model_IDevice
*/
protected $device;
/**
* timestamp to use for all sync requests
*
* @var DateTime
*/
protected $syncTimeStamp;
/**
* name of model to use
*
* @var string
*/
protected $modelName;
/**
* type of the default folder
*
* @var int
*/
protected $defaultFolderType;
/**
* default container for new entries
*
* @var string
*/
protected $defaultFolder;
/**
* type of user created folders
*
* @var int
*/
protected $folderType;
/**
* Internal cache for kolab_storage folder objects
*
* @var array
*/
protected $folders = array();
/**
* Internal cache for IMAP folders list
*
* @var array
*/
protected $imap_folders = array();
/**
* Timezone
*
* @var string
*/
protected $timezone;
/**
* List of device types with multiple folders support
*
* @var array
*/
protected $ext_devices = array(
'iphone',
'ipad',
'thundertine',
'windowsphone',
'wp',
'wp8',
'playbook',
);
const RESULT_OBJECT = 0;
const RESULT_UID = 1;
const RESULT_COUNT = 2;
/**
* Recurrence types
*/
const RECUR_TYPE_DAILY = 0; // Recurs daily.
const RECUR_TYPE_WEEKLY = 1; // Recurs weekly
const RECUR_TYPE_MONTHLY = 2; // Recurs monthly
const RECUR_TYPE_MONTHLY_DAYN = 3; // Recurs monthly on the nth day
const RECUR_TYPE_YEARLY = 5; // Recurs yearly
const RECUR_TYPE_YEARLY_DAYN = 6; // Recurs yearly on the nth day
/**
* Day of week constants
*/
const RECUR_DOW_SUNDAY = 1;
const RECUR_DOW_MONDAY = 2;
const RECUR_DOW_TUESDAY = 4;
const RECUR_DOW_WEDNESDAY = 8;
const RECUR_DOW_THURSDAY = 16;
const RECUR_DOW_FRIDAY = 32;
const RECUR_DOW_SATURDAY = 64;
const RECUR_DOW_LAST = 127; // The last day of the month. Used as a special value in monthly or yearly recurrences.
/**
* Mapping of recurrence types
*
* @var array
*/
protected $recurTypeMap = array(
self::RECUR_TYPE_DAILY => 'DAILY',
self::RECUR_TYPE_WEEKLY => 'WEEKLY',
self::RECUR_TYPE_MONTHLY => 'MONTHLY',
self::RECUR_TYPE_MONTHLY_DAYN => 'MONTHLY',
self::RECUR_TYPE_YEARLY => 'YEARLY',
self::RECUR_TYPE_YEARLY_DAYN => 'YEARLY',
);
/**
* Mapping of weekdays
* NOTE: ActiveSync uses a bitmask
*
* @var array
*/
protected $recurDayMap = array(
'SU' => self::RECUR_DOW_SUNDAY,
'MO' => self::RECUR_DOW_MONDAY,
'TU' => self::RECUR_DOW_TUESDAY,
'WE' => self::RECUR_DOW_WEDNESDAY,
'TH' => self::RECUR_DOW_THURSDAY,
'FR' => self::RECUR_DOW_FRIDAY,
'SA' => self::RECUR_DOW_SATURDAY,
);
/**
* the constructor
*
* @param Syncroton_Model_IDevice $device
* @param DateTime $syncTimeStamp
*/
public function __construct(Syncroton_Model_IDevice $device, DateTime $syncTimeStamp)
{
$this->backend = kolab_sync_backend::get_instance();
$this->device = $device;
$this->asversion = floatval($device->acsversion);
$this->syncTimeStamp = $syncTimeStamp;
$this->defaultRootFolder = $this->defaultFolder . '::Syncroton';
// set internal timezone of kolab_format to user timezone
try {
$this->timezone = rcube::get_instance()->config->get('timezone', 'GMT');
kolab_format::$timezone = new DateTimeZone($this->timezone);
}
catch (Exception $e) {
//rcube::raise_error($e, true);
$this->timezone = 'GMT';
kolab_format::$timezone = new DateTimeZone('GMT');
}
}
/**
* return list of supported folders for this backend
*
* @return array
*/
public function getAllFolders()
{
$list = array();
// device supports multiple folders ?
if (in_array(strtolower($this->device->devicetype), $this->ext_devices)) {
// get the folders the user has access to
$list = $this->listFolders();
}
else if ($default = $this->getDefaultFolder()) {
$list = array($default['serverId'] => $default);
}
// getAllFolders() is called only in FolderSync
// throw Syncroton_Exception_Status_FolderSync exception
if (!is_array($list)) {
throw new Syncroton_Exception_Status_FolderSync(Syncroton_Exception_Status_FolderSync::FOLDER_SERVER_ERROR);
}
foreach ($list as $idx => $folder) {
$list[$idx] = new Syncroton_Model_Folder($folder);
}
return $list;
}
/**
* Retrieve folders which were modified since last sync
*
* @param DateTime $startTimeStamp
* @param DateTime $endTimeStamp
*
* @return array List of folders
*/
public function getChangedFolders(DateTime $startTimeStamp, DateTime $endTimeStamp)
{
return array();
}
/**
* Returns default folder for current class type.
*/
protected function getDefaultFolder()
{
// Check if there's any folder configured for sync
$folders = $this->listFolders();
if (empty($folders)) {
return $folders;
}
foreach ($folders as $folder) {
if ($folder['type'] == $this->defaultFolderType) {
$default = $folder;
break;
}
}
// Return first on the list if there's no default
if (empty($default)) {
$key = array_shift(array_keys($folders));
$default = $folders[$key];
// make sure the type is default here
$default['type'] = $this->defaultFolderType;
}
// Remember real folder ID and set ID/name to root folder
$default['realid'] = $default['serverId'];
$default['serverId'] = $this->defaultRootFolder;
$default['displayName'] = $this->defaultFolder;
return $default;
}
/**
* Creates a folder
*/
public function createFolder(Syncroton_Model_IFolder $folder)
{
$parentid = $folder->parentId;
$type = $folder->type;
$display_name = $folder->displayName;
if ($parentid) {
$parent = $this->backend->folder_id2name($parentid, $this->device->deviceid);
}
$name = rcube_charset::convert($display_name, kolab_sync::CHARSET, 'UTF7-IMAP');
if ($parent !== null) {
$rcube = rcube::get_instance();
$storage = $rcube->get_storage();
$delim = $storage->get_hierarchy_delimiter();
$name = $parent . $delim . $name;
}
// Create IMAP folder
$result = $this->backend->folder_create($name, $type, $this->device->deviceid);
if ($result) {
$folder->serverId = $this->backend->folder_id($name);
return $folder;
}
// @TODO: throw exception
}
/**
* Updates a folder
*/
public function updateFolder(Syncroton_Model_IFolder $folder)
{
$parentid = $folder->parentId;
$type = $folder->type;
$display_name = $folder->displayName;
$old_name = $this->backend->folder_id2name($folder->serverId, $this->device->deviceid);
if ($parentid) {
$parent = $this->backend->folder_id2name($parentid, $this->device->deviceid);
}
$name = rcube_charset::convert($display_name, kolab_sync::CHARSET, 'UTF7-IMAP');
if ($parent !== null) {
$rcube = rcube::get_instance();
$storage = $rcube->get_storage();
$delim = $storage->get_hierarchy_delimiter();
$name = $parent . $delim . $name;
}
// Rename/move IMAP folder
if ($name == $old_name) {
$result = true;
// @TODO: folder type change?
}
else {
$result = $this->backend->folder_rename($old_name, $name, $type);
}
if ($result) {
$folder->serverId = $this->backend->folder_id($name);
return $folder;
}
// @TODO: throw exception
}
/**
* Deletes a folder
*/
public function deleteFolder($folder)
{
if ($folder instanceof Syncroton_Model_IFolder) {
$folder = $folder->serverId;
}
$name = $this->backend->folder_id2name($folder, $this->device->deviceid);
// @TODO: throw exception
return $this->backend->folder_delete($name, $this->device->deviceid);
}
/**
* Empty folder (remove all entries and optionally subfolders)
*
* @param string $folderId Folder identifier
* @param array $options Options
*/
public function emptyFolderContents($folderid, $options)
{
$folders = $this->extractFolders($folderid);
foreach ($folders as $folderid) {
$foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid);
+ $folder = $this->getFolderObject($foldername);
- if ($foldername === null) {
- continue;
+ if (!$folder || !$folder->valid) {
+ throw new Syncroton_Exception_Status_ItemOperations(Syncroton_Exception_Status_ItemOperations::ITEM_SERVER_ERROR);
}
- $folder = $this->getFolderObject($foldername);
-
// Remove all entries
$folder->delete_all();
// Remove subfolders
if (!empty($options['deleteSubFolders'])) {
$list = $this->listFolders($folderid);
foreach ($list as $folderid => $folder) {
$foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid);
+ $folder = $this->getFolderObject($foldername);
- if ($foldername === null) {
- continue;
+ if (!$folder || !$folder->valid) {
+ throw new Syncroton_Exception_Status_ItemOperations(Syncroton_Exception_Status_ItemOperations::ITEM_SERVER_ERROR);
}
- $folder = $this->getFolderObject($foldername);
-
// Remove all entries
$folder->delete_all();
}
}
}
}
/**
* Moves object into another location (folder)
*
* @param string $srcFolderId Source folder identifier
* @param string $serverId Object identifier
* @param string $dstFolderId Destination folder identifier
*
* @throws Syncroton_Exception_Status
* @return string New object identifier
*/
public function moveItem($srcFolderId, $serverId, $dstFolderId)
{
$item = $this->getObject($srcFolderId, $serverId, $folder);
if (!$item || !$folder) {
throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
}
$dstname = $this->backend->folder_id2name($dstFolderId, $this->device->deviceid);
if ($dstname === null) {
throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_DESTINATION);
}
if (!$folder->move($serverId, $dstname)) {
throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
}
return $item['uid'];
}
/**
* Add entry
*
* @param string $folderId Folder identifier
* @param Syncroton_Model_IEntry $entry Entry object
*
* @return string ID of the created entry
*/
public function createEntry($folderId, Syncroton_Model_IEntry $entry)
{
$entry = $this->toKolab($entry, $folderId);
$entry = $this->createObject($folderId, $entry);
if (empty($entry)) {
throw new Syncroton_Exception_Status_Sync(Syncroton_Exception_Status_Sync::SYNC_SERVER_ERROR);
}
return $entry['uid'];
}
/**
* update existing entry
*
* @param string $folderId
* @param string $serverId
* @param SimpleXMLElement $entry
*
* @return string ID of the updated entry
*/
public function updateEntry($folderId, $serverId, Syncroton_Model_IEntry $entry)
{
$oldEntry = $this->getObject($folderId, $serverId);
if (empty($oldEntry)) {
throw new Syncroton_Exception_NotFound('id not found');
}
$entry = $this->toKolab($entry, $folderId, $oldEntry);
$entry = $this->updateObject($folderId, $serverId, $entry);
if (empty($entry)) {
throw new Syncroton_Exception_Status_Sync(Syncroton_Exception_Status_Sync::SYNC_SERVER_ERROR);
}
return $entry['uid'];
}
/**
* delete entry
*
* @param string $folderId
* @param string $serverId
* @param array $collectionData
*/
public function deleteEntry($folderId, $serverId, $collectionData)
{
$deleted = $this->deleteObject($folderId, $serverId);
if (!$deleted) {
throw new Syncroton_Exception_Status_Sync(Syncroton_Exception_Status_Sync::SYNC_SERVER_ERROR);
}
}
public function getFileReference($fileReference)
{
// to be implemented by Email data class
// @TODO: throw "unimplemented" exception here?
}
/**
* Search for existing entries
*
* @param string $folderid Folder identifier
* @param array $filter Search filter
* @param int $result_type Type of the result (see RESULT_* constants)
*
* @return array|int Search result as count or array of uids/objects
*/
protected function searchEntries($folderid, $filter = array(), $result_type = self::RESULT_UID)
{
if ($folderid == $this->defaultRootFolder) {
$folders = $this->listFolders();
if (!is_array($folders)) {
throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR);
}
$folders = array_keys($folders);
}
else {
$folders = array($folderid);
}
// there's a PHP Warning from kolab_storage if $filter isn't an array
if (empty($filter)) {
$filter = array();
}
else {
$changed_objects = $this->getChangesByRelations($folderid, $filter);
}
$result = $result_type == self::RESULT_COUNT ? 0 : array();
$found = 0;
foreach ($folders as $folder_id) {
$foldername = $this->backend->folder_id2name($folder_id, $this->device->deviceid);
+ $folder = $this->getFolderObject($foldername);
- if ($foldername === null || !($folder = $this->getFolderObject($foldername))) {
- continue;
+ if (!$folder || !$folder->valid) {
+ throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR);
}
$found++;
$error = false;
switch ($result_type) {
case self::RESULT_COUNT:
$count = $folder->count($filter);
if ($count === null || $count === false) {
$error = true;
}
else {
$result += (int) $count;
}
break;
case self::RESULT_UID:
$uids = $folder->get_uids($filter);
if (!is_array($uids)) {
$error = true;
}
else {
$result = array_merge($result, $uids);
}
break;
}
if ($error) {
throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR);
}
// handle tag modifications
if (!empty($changed_objects)) {
// build new filter
// search objects mathing current filter,
// relations may contain members of many types, we need to
// search them by UID in all requested folders to get
// only these with requested type (and that really exist
// in specified folders)
$tag_filter = array(array('uid', '=', $changed_objects));
foreach ($filter as $f) {
if ($f[0] != 'changed') {
$tag_filter[] = $f;
}
}
switch ($result_type) {
case self::RESULT_COUNT:
// Note: this way we're potentally counting the same objects twice
// I'm not sure if this is a problem, we most likely do not
// need a precise result here
$count = $folder->count($tag_filter);
if ($count !== null && $count !== false) {
$result += (int) $count;
}
break;
case self::RESULT_UID:
$uids = $folder->get_uids($tag_filter);
if (is_array($uids)) {
$result = array_unique(array_merge($result, $uids));
}
break;
}
}
}
if (!$found) {
throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR);
}
return $result;
}
/**
* Detect changes of relation (tag) objects data and assigned objects
* Returns relation member identifiers
*/
protected function getChangesByRelations($folderid, $filter)
{
if (!$this->tag_categories) {
return;
}
// get period filter, create new objects filter
foreach ($filter as $f) {
if ($f[0] == 'changed' && $f[1] == '>') {
$since = $f[2];
}
}
// this is not search for changes, do nothing
if (empty($since)) {
return;
}
// get relations state from the last sync
$last_state = (array) $this->backend->relations_state_get($this->device->id, $folderid, $since);
// get current relations state
$config = kolab_storage_config::get_instance();
$default = true;
$filter = array(
array('type', '=', 'relation'),
array('category', '=', 'tag')
);
$relations = $config->get_objects($filter, $default, 100);
$result = array();
$changed = false;
// compare states, get members of changed relations
foreach ($relations as $relation) {
$rel_id = $relation['uid'];
if ($relation['changed']) {
$relation['changed']->setTimezone(new DateTimeZone('UTC'));
}
// last state unknown...
if (empty($last_state[$rel_id])) {
// ...get all members
if (!empty($relation['members'])) {
$changed = true;
$result = array_merge($result, $relation['members']);
}
}
// last state known, changed tag name...
else if ($last_state[$rel_id]['name'] != $relation['name']) {
// ...get all (old and new) members
$members_old = explode("\n", $last_state[$rel_id]['members']);
$changed = true;
$members = array_unique(array_merge($relation['members'], $members_old));
$result = array_merge($result, $members);
}
// last state known, any other change change...
else if ($last_state[$rel_id]['changed'] < $relation['changed']->format('U')) {
// ...find new and removed members
$members_old = explode("\n", $last_state[$rel_id]['members']);
$new = array_diff($relation['members'], $members_old);
$removed = array_diff($members_old, $relation['members']);
if (!empty($new) || !empty($removed)) {
$changed = true;
$result = array_merge($result, $new, $removed);
}
}
unset($last_state[$rel_id]);
}
// get members of deleted relations
if (!empty($last_state)) {
$changed = true;
foreach ($last_state as $relation) {
$members = explode("\n", $relation['members']);
$result = array_merge($result, $members);
}
}
// save current state
if ($changed) {
$data = array();
foreach ($relations as $relation) {
$data[$relation['uid']] = array(
'name' => $relation['name'],
'changed' => $relation['changed']->format('U'),
'members' => implode("\n", $relation['members']),
);
}
$now = new DateTime('now', new DateTimeZone('UTC'));
$this->backend->relations_state_set($this->device->id, $folderid, $now, $data);
}
// in mail mode return only message URIs
if ($this->modelName == 'mail') {
// lambda function to skip email members
$filter_func = function($value) {
return strpos($value, 'imap://') === 0;
};
$result = array_filter(array_unique($result), $filter_func);
}
// otherwise return only object UIDs
else {
// lambda function to skip email members
$filter_func = function($value) {
return strpos($value, 'urn:uuid:') === 0;
};
// lambda function to parse member URI
$member_func = function($value) {
if (strpos($value, 'urn:uuid:') === 0) {
$value = substr($value, 9);
}
return $value;
};
$result = array_map($member_func, array_filter(array_unique($result), $filter_func));
}
return $result;
}
/**
* Returns filter query array according to specified ActiveSync FilterType
*
* @param int $filter_type Filter type
*
* @param array Filter query
*/
protected function filter($filter_type = 0)
{
// overwrite by child class according to specified type
return array();
}
/**
* get all entries changed between two dates
*
* @param string $folderId
* @param DateTime $start
* @param DateTime $end
* @param int $filterType
*
* @return array
*/
public function getChangedEntries($folderId, DateTime $start, DateTime $end = null, $filter_type = null)
{
$filter = $this->filter($filter_type);
$filter[] = array('changed', '>', $start);
if ($end) {
$filter[] = array('changed', '<=', $end);
}
return $this->searchEntries($folderId, $filter, self::RESULT_UID);
}
/**
* Get count of entries changed between two dates
*
* @param string $folderId
* @param DateTime $start
* @param DateTime $end
* @param int $filterType
*
* @return int
*/
public function getChangedEntriesCount($folderId, DateTime $start, DateTime $end = null, $filter_type = null)
{
$filter = $this->filter($filter_type);
$filter[] = array('changed', '>', $start);
if ($end) {
$filter[] = array('changed', '<=', $end);
}
return $this->searchEntries($folderId, $filter, self::RESULT_COUNT);
}
/**
* get id's of all entries available on the server
*
* @param string $folderId
* @param int $filterType
*
* @return array
*/
public function getServerEntries($folder_id, $filter_type)
{
$filter = $this->filter($filter_type);
$result = $this->searchEntries($folder_id, $filter, self::RESULT_UID);
return $result;
}
/**
* get count of all entries available on the server
*
* @param string $folderId
* @param int $filterType
*
* @return int
*/
public function getServerEntriesCount($folder_id, $filter_type)
{
$filter = $this->filter($filter_type);
$result = $this->searchEntries($folder_id, $filter, self::RESULT_COUNT);
return $result;
}
/**
* Returns number of changed objects in the backend folder
*
* @param Syncroton_Backend_IContent $contentBackend
* @param Syncroton_Model_IFolder $folder
* @param Syncroton_Model_ISyncState $syncState
*
* @return int
*/
public function getCountOfChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState)
{
$allClientEntries = $contentBackend->getFolderState($this->device, $folder);
$allServerEntries = $this->getServerEntries($folder->serverId, $folder->lastfiltertype);
$changedEntries = $this->getChangedEntriesCount($folder->serverId, $syncState->lastsync, null, $folder->lastfiltertype);
$addedEntries = array_diff($allServerEntries, $allClientEntries);
$deletedEntries = array_diff($allClientEntries, $allServerEntries);
return count($addedEntries) + count($deletedEntries) + $changedEntries;
}
/**
* Returns true if any data got modified in the backend folder
*
* @param Syncroton_Backend_IContent $contentBackend
* @param Syncroton_Model_IFolder $folder
* @param Syncroton_Model_ISyncState $syncState
*
* @return bool
*/
public function hasChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState)
{
try {
if ($this->getChangedEntriesCount($folder->serverId, $syncState->lastsync, null, $folder->lastfiltertype)) {
return true;
}
$allClientEntries = $contentBackend->getFolderState($this->device, $folder);
// @TODO: Consider looping over all folders here, not in getServerEntries() and
// getChangedEntriesCount(). This way we could break the loop and not check all folders
// or at least skip redundant cache sync of the same folder
$allServerEntries = $this->getServerEntries($folder->serverId, $folder->lastfiltertype);
$addedEntries = array_diff($allServerEntries, $allClientEntries);
$deletedEntries = array_diff($allClientEntries, $allServerEntries);
return count($addedEntries) > 0 || count($deletedEntries) > 0;
}
catch (Exception $e) {
// return "no changes" if something failed
return false;
}
}
/**
* Fetches the entry from the backend
*/
protected function getObject($folderid, $entryid, &$folder = null)
{
$folders = $this->extractFolders($folderid);
foreach ($folders as $folderid) {
$foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid);
+ $folder = $this->getFolderObject($foldername);
- if ($foldername === null) {
- continue;
- }
-
- $folder = $this->getFolderObject($foldername);
-
- if ($folder && ($object = $folder->get_object($entryid))) {
+ if ($folder && $folder->valid && ($object = $folder->get_object($entryid))) {
$object['_folderid'] = $folderid;
return $object;
}
}
}
/**
* Saves the entry on the backend
*/
protected function createObject($folderid, $data)
{
if ($folderid == $this->defaultRootFolder) {
$default = $this->getDefaultFolder();
if (!is_array($default)) {
return null;
}
$folderid = isset($default['realid']) ? $default['realid'] : $default['serverId'];
}
// convert categories into tags, save them after creating an object
if ($this->tag_categories) {
$tags = $data['categories'];
unset($data['categories']);
}
$foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid);
$folder = $this->getFolderObject($foldername);
- if ($folder && $folder->save($data)) {
+ if ($folder && $folder->valid && $folder->save($data)) {
if (!empty($tags)) {
$this->setKolabTags($data['uid'], $tags);
}
return $data;
}
}
/**
* Updates the entry on the backend
*/
protected function updateObject($folderid, $entryid, $data)
{
$object = $this->getObject($folderid, $entryid);
if ($object) {
$folder = $this->getFolderObject($object['_mailbox']);
// convert categories into tags, save them after updating an object
if ($this->tag_categories && array_key_exists('categories', $data)) {
$tags = (array) $data['categories'];
unset($data['categories']);
}
- if ($folder && $folder->save($data)) {
+ if ($folder && $folder->valid && $folder->save($data)) {
if (isset($tags)) {
$this->setKolabTags($data['uid'], $tags);
}
return $data;
}
}
}
/**
* Removes the entry from the backend
*/
protected function deleteObject($folderid, $entryid)
{
$object = $this->getObject($folderid, $entryid);
if ($object) {
$folder = $this->getFolderObject($object['_mailbox']);
- if ($folder && $folder->delete($entryid)) {
+ if ($folder && $folder->valid && $folder->delete($entryid)) {
if ($this->tag_categories) {
$this->setKolabTags($object['uid'], null);
}
return true;
}
return false;
}
// object doesn't exist, confirm deletion
return true;
}
/**
* Returns internal folder IDs
*
* @param string $folderid Folder identifier
*
* @return array List of folder identifiers
*/
protected function extractFolders($folderid)
{
if ($folderid instanceof Syncroton_Model_IFolder) {
$folderid = $folderid->serverId;
}
if ($folderid == $this->defaultRootFolder) {
$folders = $this->listFolders();
if (!is_array($folders)) {
return null;
}
$folders = array_keys($folders);
}
else {
$folders = array($folderid);
}
return $folders;
}
/**
* List of all IMAP folders (or subtree)
*
* @param string $parentid Parent folder identifier
*
* @return array List of folder identifiers
*/
protected function listFolders($parentid = null)
{
if (empty($this->imap_folders)) {
$this->imap_folders = $this->backend->folders_list($this->device->deviceid, $this->modelName);
}
if ($parentid === null) {
return $this->imap_folders;
}
$folders = array();
$parents = array($parentid);
foreach ($this->imap_folders as $folder_id => $folder) {
if ($folder['parentId'] && in_array($folder['parentId'], $parents)) {
$folders[$folder_id] = $folder;
$parents[] = $folder_id;
}
}
return $folders;
}
/**
* Returns Folder object (uses internal cache)
*
* @param string $name Folder name (UTF7-IMAP)
*
* @return kolab_storage_folder Folder object
*/
protected function getFolderObject($name)
{
- if ($name === null) {
+ if ($name === null || $name === '') {
return null;
}
if (!isset($this->folders[$name])) {
- $this->folders[$name] = kolab_storage::get_folder($name);
+ $this->folders[$name] = kolab_storage::get_folder($name, $this->modelName);
}
return $this->folders[$name];
}
/**
* Returns ActiveSync settings of specified folder
*
* @param string $name Folder name (UTF7-IMAP)
*
* @return array Folder settings
*/
protected function getFolderConfig($name)
{
$metadata = $this->backend->folder_meta();
if (!is_array($metadata)) {
return array();
}
$deviceid = $this->device->deviceid;
$config = $metadata[$name]['FOLDER'][$deviceid];
return array(
'ALARMS' => $config['S'] == 2,
);
}
/**
* Returns real folder name for specified folder ID
*/
protected function getFolderName($folderid)
{
if ($folderid == $this->defaultRootFolder) {
$default = $this->getDefaultFolder();
if (!is_array($default)) {
return null;
}
$folderid = isset($default['realid']) ? $default['realid'] : $default['serverId'];
}
return $this->backend->folder_id2name($folderid, $this->device->deviceid);
}
/**
* Convert contact from xml to kolab format
*
* @param Syncroton_Model_IEntry $data Contact data
* @param string $folderId Folder identifier
* @param array $entry Old Contact data for merge
*
* @return array
*/
abstract function toKolab(Syncroton_Model_IEntry $data, $folderId, $entry = null);
/**
* Extracts data from kolab data array
*/
protected function getKolabDataItem($data, $name)
{
$name_items = explode('.', $name);
$count = count($name_items);
// multi-level array (e.g. address, phone)
if ($count == 3) {
$name = $name_items[0];
$type = $name_items[1];
$key_name = $name_items[2];
if (!empty($data[$name]) && is_array($data[$name])) {
foreach ($data[$name] as $element) {
if ($element['type'] == $type) {
return $element[$key_name];
}
}
}
return null;
}
/*
// hash array e.g. organizer
else if ($count == 2) {
$name = $name_items[0];
$type = $name_items[1];
$key_name = $name_items[2];
if (!empty($data[$name]) && is_array($data[$name])) {
foreach ($data[$name] as $element) {
if ($element['type'] == $type) {
return $element[$key_name];
}
}
}
return null;
}
*/
$name_items = explode(':', $name);
$name = $name_items[0];
if (empty($data[$name])) {
return null;
}
// simple array (e.g. email)
if (count($name_items) == 2) {
return $data[$name][$name_items[1]];
}
return $data[$name];
}
/**
* Saves data in kolab data array
*/
protected function setKolabDataItem(&$data, $name, $value)
{
if (empty($value)) {
return $this->unsetKolabDataItem($data, $name);
}
$name_items = explode('.', $name);
// multi-level array (e.g. address, phone)
if (count($name_items) == 3) {
$name = $name_items[0];
$type = $name_items[1];
$key_name = $name_items[2];
if (!isset($data[$name])) {
$data[$name] = array();
}
foreach ($data[$name] as $idx => $element) {
if ($element['type'] == $type) {
$found = $idx;
break;
}
}
if (!isset($found)) {
$data[$name] = array_values($data[$name]);
$found = count($data[$name]);
$data[$name][$found] = array('type' => $type);
}
$data[$name][$found][$key_name] = $value;
return;
}
$name_items = explode(':', $name);
$name = $name_items[0];
// simple array (e.g. email)
if (count($name_items) == 2) {
$data[$name][$name_items[1]] = $value;
return;
}
$data[$name] = $value;
}
/**
* Unsets data item in kolab data array
*/
protected function unsetKolabDataItem(&$data, $name)
{
$name_items = explode('.', $name);
// multi-level array (e.g. address, phone)
if (count($name_items) == 3) {
$name = $name_items[0];
$type = $name_items[1];
$key_name = $name_items[2];
if (!isset($data[$name])) {
return;
}
foreach ($data[$name] as $idx => $element) {
if ($element['type'] == $type) {
$found = $idx;
break;
}
}
if (!isset($found)) {
return;
}
unset($data[$name][$found][$key_name]);
// if there's only one element and it's 'type', remove it
if (count($data[$name][$found]) == 1 && isset($data[$name][$found]['type'])) {
unset($data[$name][$found]['type']);
}
if (empty($data[$name][$found])) {
unset($data[$name][$found]);
}
if (empty($data[$name])) {
unset($data[$name]);
}
return;
}
$name_items = explode(':', $name);
$name = $name_items[0];
// simple array (e.g. email)
if (count($name_items) == 2) {
unset($data[$name][$name_items[1]]);
if (empty($data[$name])) {
unset($data[$name]);
}
return;
}
unset($data[$name]);
}
/**
* Setter for Body attribute according to client version
*
* @param string $value Body
* @param array $param Body parameters
*
* @reurn Syncroton_Model_EmailBody Body element
*/
protected function setBody($value, $params = array())
{
if (empty($value) && empty($params)) {
return;
}
// Old protocol version doesn't support AirSyncBase:Body, it's eg. WindowsCE
if ($this->asversion < 12) {
return;
}
if (!empty($value)) {
// cast to string to workaround issue described in Bug #1635
$params['data'] = (string) $value;
}
if (!isset($params['type'])) {
$params['type'] = Syncroton_Model_EmailBody::TYPE_PLAINTEXT;
}
return new Syncroton_Model_EmailBody($params);
}
/**
* Getter for Body attribute value according to client version
*
* @param mixed $body Body element
* @param int $type Result data type (to which the body will be converted, if specified).
* One or array of Syncroton_Model_EmailBody constants.
*
* @return string Body value
*/
protected function getBody($body, $type = null)
{
if ($body && $body->data) {
$data = $body->data;
}
if (!$data || empty($type)) {
return;
}
$type = (array) $type;
// Convert to specified type
if (!in_array($body->type, $type)) {
$converter = new kolab_sync_body_converter($data, $body->type);
$data = $converter->convert($type[0]);
}
return $data;
}
/**
* Converts text (plain or html) into ActiveSync Body element.
* Takes bodyPreferences into account and detects if the text is plain or html.
*/
protected function body_from_kolab($body, $collection)
{
if (empty($body)) {
return;
}
$opts = $collection->options;
$prefs = $opts['bodyPreferences'];
$html_type = Syncroton_Command_Sync::BODY_TYPE_HTML;
$type = Syncroton_Command_Sync::BODY_TYPE_PLAIN_TEXT;
$params = array();
// HTML? check for opening and closing <html> or <body> tags
$is_html = preg_match('/<(html|body)(\s+[a-z]|>)/', $body, $m) && strpos($body, '</'.$m[1].'>') > 0;
// here we assume that all devices support plain text
if ($is_html) {
// device supports HTML...
if (!empty($prefs[$html_type])) {
$type = $html_type;
}
// ...else convert to plain text
else {
$txt = new rcube_html2text($body, false, true);
$body = $txt->get_text();
}
}
// strip out any non utf-8 characters
$body = rcube_charset::clean($body);
$real_length = $body_length = strlen($body);
// truncate the body if needed
if (($truncateAt = $prefs[$type]['truncationSize']) && $body_length > $truncateAt) {
$body = mb_strcut($body, 0, $truncateAt);
$body_length = strlen($body);
$params['truncated'] = 1;
$params['estimatedDataSize'] = $real_length;
}
$params['type'] = $type;
return $this->setBody($body, $params);
}
/**
* Converts PHP DateTime, date (YYYY-MM-DD) or unixtimestamp into PHP DateTime in UTC
*
* @param DateTime|int|string $date Unix timestamp, date (YYYY-MM-DD) or PHP DateTime object
*
* @return DateTime Datetime object
*/
protected static function date_from_kolab($date)
{
if (!empty($date)) {
if (is_numeric($date)) {
$date = new DateTime('@' . $date);
}
else if (is_string($date)) {
$date = new DateTime($date, new DateTimeZone('UTC'));
}
else if ($date instanceof DateTime) {
$date = clone $date;
$tz = $date->getTimezone();
$tz_name = $tz->getName();
// convert to UTC if needed
if ($tz_name != 'UTC') {
$utc = new DateTimeZone('UTC');
// safe dateonly object conversion to UTC
// note: _dateonly flag is set by libkolab e.g. for birthdays
if ($date->_dateonly) {
// avoid time change
$date = new DateTime($date->format('Y-m-d'), $utc);
// set time to noon to avoid timezone troubles
$date->setTime(12, 0, 0);
}
else {
$date->setTimezone($utc);
}
}
}
else {
return null; // invalid input
}
return $date;
}
}
/**
* Convert Kolab event/task recurrence into ActiveSync
*/
protected function recurrence_from_kolab($collection, $data, &$result, $type = 'Event')
{
if (empty($data['recurrence'])) {
return;
}
$recurrence = array();
$r = $data['recurrence'];
// required fields
switch($r['FREQ']) {
case 'DAILY':
$recurrence['type'] = self::RECUR_TYPE_DAILY;
break;
case 'WEEKLY':
$recurrence['type'] = self::RECUR_TYPE_WEEKLY;
$recurrence['dayOfWeek'] = $this->day2bitmask($r['BYDAY']);
break;
case 'MONTHLY':
if (!empty($r['BYMONTHDAY'])) {
// @TODO: ActiveSync doesn't support multi-valued month days,
// should we replicate the recurrence element for each day of month?
$month_day = array_shift(explode(',', $r['BYMONTHDAY']));
$recurrence['type'] = self::RECUR_TYPE_MONTHLY;
$recurrence['dayOfMonth'] = $month_day;
}
else {
$week = (int) substr($r['BYDAY'], 0, -2);
$week = ($week == -1) ? 5 : $week;
$day = substr($r['BYDAY'], -2);
$recurrence['type'] = self::RECUR_TYPE_MONTHLY_DAYN;
$recurrence['weekOfMonth'] = $week;
$recurrence['dayOfWeek'] = $this->day2bitmask($day);
}
break;
case 'YEARLY':
// @TODO: ActiveSync doesn't support multi-valued months,
// should we replicate the recurrence element for each month?
$month = array_shift(explode(',', $r['BYMONTH']));
if (!empty($r['BYDAY'])) {
$week = (int) substr($r['BYDAY'], 0, -2);
$week = ($week == -1) ? 5 : $week;
$day = substr($r['BYDAY'], -2);
$recurrence['type'] = self::RECUR_TYPE_YEARLY_DAYN;
$recurrence['weekOfMonth'] = $week;
$recurrence['dayOfWeek'] = $this->day2bitmask($day);
$recurrence['monthOfYear'] = $month;
}
else if (!empty($r['BYMONTHDAY'])) {
// @TODO: ActiveSync doesn't support multi-valued month days,
// should we replicate the recurrence element for each day of month?
$month_day = array_shift(explode(',', $r['BYMONTHDAY']));
$recurrence['type'] = self::RECUR_TYPE_YEARLY;
$recurrence['dayOfMonth'] = $month_day;
$recurrence['monthOfYear'] = $month;
}
else {
$recurrence['type'] = self::RECUR_TYPE_YEARLY;
$recurrence['monthOfYear'] = $month;
}
break;
}
// required field
$recurrence['interval'] = $r['INTERVAL'] ? $r['INTERVAL'] : 1;
if (!empty($r['UNTIL'])) {
$recurrence['until'] = self::date_from_kolab($r['UNTIL']);
}
else if (!empty($r['COUNT'])) {
$recurrence['occurrences'] = $r['COUNT'];
}
$class = 'Syncroton_Model_' . $type . 'Recurrence';
$result['recurrence'] = new $class($recurrence);
// Tasks do not support exceptions
if ($type == 'Event') {
$result['exceptions'] = $this->exceptions_from_kolab($collection, $data);
}
}
/**
* Convert ActiveSync event/task recurrence into Kolab
*/
protected function recurrence_to_kolab($data, $folderid, $timezone = null)
{
if (!($data->recurrence instanceof Syncroton_Model_EventRecurrence) || !isset($data->recurrence->type)) {
return null;
}
$recurrence = $data->recurrence;
$type = $recurrence->type;
switch ($type) {
case self::RECUR_TYPE_DAILY:
break;
case self::RECUR_TYPE_WEEKLY:
$rrule['BYDAY'] = $this->bitmask2day($recurrence->dayOfWeek);
break;
case self::RECUR_TYPE_MONTHLY:
$rrule['BYMONTHDAY'] = $recurrence->dayOfMonth;
break;
case self::RECUR_TYPE_MONTHLY_DAYN:
$week = $recurrence->weekOfMonth;
$day = $recurrence->dayOfWeek;
$byDay = $week == 5 ? -1 : $week;
$byDay .= $this->bitmask2day($day);
$rrule['BYDAY'] = $byDay;
break;
case self::RECUR_TYPE_YEARLY:
$rrule['BYMONTH'] = $recurrence->monthOfYear;
$rrule['BYMONTHDAY'] = $recurrence->dayOfMonth;
break;
case self::RECUR_TYPE_YEARLY_DAYN:
$rrule['BYMONTH'] = $recurrence->monthOfYear;
$week = $recurrence->weekOfMonth;
$day = $recurrence->dayOfWeek;
$byDay = $week == 5 ? -1 : $week;
$byDay .= $this->bitmask2day($day);
$rrule['BYDAY'] = $byDay;
break;
}
$rrule['FREQ'] = $this->recurTypeMap[$type];
$rrule['INTERVAL'] = isset($recurrence->interval) ? $recurrence->interval : 1;
if (isset($recurrence->until)) {
if ($timezone) {
$recurrence->until->setTimezone($timezone);
}
$rrule['UNTIL'] = $recurrence->until;
}
else if (!empty($recurrence->occurrences)) {
$rrule['COUNT'] = $recurrence->occurrences;
}
// recurrence exceptions (not supported by Tasks)
if ($data instanceof Syncroton_Model_Event) {
$this->exceptions_to_kolab($data, $rrule, $folderid, $timezone);
}
return $rrule;
}
/**
* Convert Kolab event recurrence exceptions into ActiveSync
*/
protected function exceptions_from_kolab($collection, $data)
{
if (empty($data['recurrence']['EXCEPTIONS']) && empty($data['recurrence']['EXDATE'])) {
return null;
}
$ex_list = array();
// exceptions (modified occurences)
foreach ((array)$data['recurrence']['EXCEPTIONS'] as $exception) {
$exception['_mailbox'] = $data['_mailbox'];
$ex = $this->getEntry($collection, $exception, true);
$ex['exceptionStartTime'] = clone $ex['startTime'];
// remove fields not supported by Syncroton_Model_EventException
unset($ex['uID']);
// @TODO: 'thisandfuture=true' is not supported in Activesync
// we'd need to slit the event into two separate events
$ex_list[] = new Syncroton_Model_EventException($ex);
}
// exdate (deleted occurences)
foreach ((array)$data['recurrence']['EXDATE'] as $exception) {
if (!($exception instanceof DateTime)) {
continue;
}
// set event start time to exception date
// that can't be any time, tested with Android
$hour = $data['_start']->format('H');
$minute = $data['_start']->format('i');
$second = $data['_start']->format('s');
$exception->setTime($hour, $minute, $second);
$exception->_dateonly = false;
$ex = array(
'deleted' => 1,
'exceptionStartTime' => self::date_from_kolab($exception),
);
$ex_list[] = new Syncroton_Model_EventException($ex);
}
return $ex_list;
}
/**
* Convert ActiveSync event recurrence exceptions into Kolab
*/
protected function exceptions_to_kolab($data, &$rrule, $folderid, $timezone = null)
{
$rrule['EXDATE'] = array();
$rrule['EXCEPTIONS'] = array();
// handle exceptions from recurrence
if (!empty($data->exceptions)) {
foreach ($data->exceptions as $exception) {
if ($exception->deleted) {
$date = clone $exception->exceptionStartTime;
if ($timezone) {
$date->setTimezone($timezone);
}
$date->setTime(0, 0, 0);
$rrule['EXDATE'][] = $date;
}
else if (!$exception->deleted) {
$ex = $this->toKolab($exception, $folderid, null, $timezone);
if ($data->allDayEvent) {
$ex['allday'] = 1;
}
$rrule['EXCEPTIONS'][] = $ex;
}
}
}
+
+ if (empty($rrule['EXDATE'])) {
+ unset($rrule['EXDATE']);
+ }
+ if (empty($rrule['EXCEPTIONS'])) {
+ unset($rrule['EXCEPTIONS']);
+ }
}
/**
* Returns list of tag names assigned to kolab object
*/
protected function getKolabTags($uid, $categories = null)
{
$config = kolab_storage_config::get_instance();
$tags = $config->get_tags($uid);
$tags = array_filter(array_map(function($v) { return $v['name']; }, $tags));
// merge result with old categories
if (!empty($categories)) {
$tags = array_unique(array_merge($tags, (array) $categories));
}
return $tags;
}
/**
* Set tags to kolab object
*/
protected function setKolabTags($uid, $tags)
{
$config = kolab_storage_config::get_instance();
$config->save_tags($uid, $tags);
}
/**
* Converts string of days (TU,TH) to bitmask used by ActiveSync
*
* @param string $days
*
* @return int
*/
protected function day2bitmask($days)
{
$days = explode(',', $days);
$result = 0;
foreach ($days as $day) {
$result = $result + $this->recurDayMap[$day];
}
return $result;
}
/**
* Convert bitmask used by ActiveSync to string of days (TU,TH)
*
* @param int $days
*
* @return string
*/
protected function bitmask2day($days)
{
$days_arr = array();
for ($bitmask = 1; $bitmask <= self::RECUR_DOW_SATURDAY; $bitmask = $bitmask << 1) {
$dayMatch = $days & $bitmask;
if ($dayMatch === $bitmask) {
$days_arr[] = array_search($bitmask, $this->recurDayMap);
}
}
$result = implode(',', $days_arr);
return $result;
}
}
diff --git a/lib/kolab_sync_data_calendar.php b/lib/kolab_sync_data_calendar.php
index 9e9ddc9..9ae9dd8 100644
--- a/lib/kolab_sync_data_calendar.php
+++ b/lib/kolab_sync_data_calendar.php
@@ -1,574 +1,599 @@
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com> |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
/**
* Calendar (Events) data class for Syncroton
*/
class kolab_sync_data_calendar extends kolab_sync_data implements Syncroton_Data_IDataCalendar
{
/**
* Mapping from ActiveSync Calendar namespace fields
*/
protected $mapping = array(
'allDayEvent' => 'allday',
//'attendees' => 'attendees',
'body' => 'description',
//'bodyTruncated' => 'bodytruncated',
'busyStatus' => 'free_busy',
//'categories' => 'categories',
'dtStamp' => 'changed',
'endTime' => 'end',
//'exceptions' => 'exceptions',
'location' => 'location',
//'meetingStatus' => 'meetingstatus',
//'organizerEmail' => 'organizeremail',
//'organizerName' => 'organizername',
//'recurrence' => 'recurrence',
//'reminder' => 'reminder',
//'responseRequested' => 'responserequested',
//'responseType' => 'responsetype',
'sensitivity' => 'sensitivity',
'startTime' => 'start',
'subject' => 'title',
//'timezone' => 'timezone',
'uID' => 'uid',
);
/**
* Kolab object type
*
* @var string
*/
protected $modelName = 'event';
/**
* Type of the default folder
*
* @var int
*/
protected $defaultFolderType = Syncroton_Command_FolderSync::FOLDERTYPE_CALENDAR;
/**
* Default container for new entries
*
* @var string
*/
protected $defaultFolder = 'Calendar';
/**
* Type of user created folders
*
* @var int
*/
protected $folderType = Syncroton_Command_FolderSync::FOLDERTYPE_CALENDAR_USER_CREATED;
/**
* attendee status
*/
const ATTENDEE_STATUS_UNKNOWN = 0;
const ATTENDEE_STATUS_TENTATIVE = 2;
const ATTENDEE_STATUS_ACCEPTED = 3;
const ATTENDEE_STATUS_DECLINED = 4;
const ATTENDEE_STATUS_NOTRESPONDED = 5;
/**
* attendee types
*/
const ATTENDEE_TYPE_REQUIRED = 1;
const ATTENDEE_TYPE_OPTIONAL = 2;
const ATTENDEE_TYPE_RESOURCE = 3;
/**
* busy status constants
*/
const BUSY_STATUS_FREE = 0;
const BUSY_STATUS_TENTATIVE = 1;
const BUSY_STATUS_BUSY = 2;
const BUSY_STATUS_OUTOFOFFICE = 3;
/**
* Sensitivity values
*/
const SENSITIVITY_NORMAL = 0;
const SENSITIVITY_PERSONAL = 1;
const SENSITIVITY_PRIVATE = 2;
const SENSITIVITY_CONFIDENTIAL = 3;
/**
* Mapping of attendee status
*
* @var array
*/
protected $attendeeStatusMap = array(
'UNKNOWN' => self::ATTENDEE_STATUS_UNKNOWN,
'TENTATIVE' => self::ATTENDEE_STATUS_TENTATIVE,
'ACCEPTED' => self::ATTENDEE_STATUS_ACCEPTED,
'DECLINED' => self::ATTENDEE_STATUS_DECLINED,
'DELEGATED' => self::ATTENDEE_STATUS_UNKNOWN,
'NEEDS-ACTION' => self::ATTENDEE_STATUS_UNKNOWN,
//self::ATTENDEE_STATUS_NOTRESPONDED,
);
/**
* Mapping of attendee type
*
* NOTE: recurrences need extra handling!
* @var array
*/
protected $attendeeTypeMap = array(
'REQ-PARTICIPANT' => self::ATTENDEE_TYPE_REQUIRED,
'OPT-PARTICIPANT' => self::ATTENDEE_TYPE_OPTIONAL,
// 'NON-PARTICIPANT' => self::ATTENDEE_TYPE_RESOURCE,
// 'CHAIR' => self::ATTENDEE_TYPE_RESOURCE,
);
/**
* Mapping of busy status
*
* @var array
*/
protected $busyStatusMap = array(
'free' => self::BUSY_STATUS_FREE,
'tentative' => self::BUSY_STATUS_TENTATIVE,
'busy' => self::BUSY_STATUS_BUSY,
'outofoffice' => self::BUSY_STATUS_OUTOFOFFICE,
);
/**
* mapping of sensitivity
*
* @var array
*/
protected $sensitivityMap = array(
'public' => self::SENSITIVITY_PERSONAL,
'private' => self::SENSITIVITY_PRIVATE,
'confidential' => self::SENSITIVITY_CONFIDENTIAL,
);
/**
* Appends contact data to xml element
*
* @param Syncroton_Model_SyncCollection $collection Collection data
* @param string $serverId Local entry identifier
* @param boolean $as_array Return entry as array
*
* @return array|Syncroton_Model_Event|array Event object
*/
public function getEntry(Syncroton_Model_SyncCollection $collection, $serverId, $as_array = false)
{
$event = is_array($serverId) ? $serverId : $this->getObject($collection->collectionId, $serverId);
$config = $this->getFolderConfig($event['_mailbox']);
$result = array();
// Timezone
// Kolab Format 3.0 and xCal does support timezone per-date, but ActiveSync allows
// only one timezone per-event. We'll use timezone of the start date
if ($event['start'] instanceof DateTime) {
$timezone = $event['start']->getTimezone();
if ($timezone && ($tz_name = $timezone->getName()) != 'UTC') {
$tzc = kolab_sync_timezone_converter::getInstance();
if ($tz_name = $tzc->encodeTimezone($tz_name)) {
$result['timezone'] = $tz_name;
}
}
}
// Calendar namespace fields
foreach ($this->mapping as $key => $name) {
$value = $this->getKolabDataItem($event, $name);
switch ($name) {
case 'changed':
case 'end':
case 'start':
// For all-day events Kolab uses different times
// At least Android doesn't display such event as all-day event
if ($value && is_a($value, 'DateTime')) {
$date = clone $value;
if ($event['allday']) {
// need this for self::date_from_kolab()
$date->_dateonly = false;
if ($name == 'start') {
$date->setTime(0, 0, 0);
}
else if ($name == 'end') {
$date->setTime(0, 0, 0);
$date->modify('+1 day');
}
}
// set this date for use in recurrence exceptions handling
if ($name == 'start') {
$event['_start'] = $date;
}
$value = self::date_from_kolab($date);
}
break;
case 'sensitivity':
$value = intval($this->sensitivityMap[$value]);
break;
case 'free_busy':
$value = $this->busyStatusMap[$value];
break;
case 'description':
$value = $this->body_from_kolab($value, $collection);
break;
}
if (empty($value) || is_array($value)) {
continue;
}
$result[$key] = $value;
}
// Event reminder time
- if ($config['ALARMS'] && ($minutes = $this->from_kolab_alarm($event['alarms']))) {
- $result['reminder'] = $minutes;
+ if ($config['ALARMS']) {
+ $result['reminder'] = $this->from_kolab_alarm($event);
}
$result['categories'] = array();
- $result['attendees'] = array();
+ $result['attendees'] = array();
// Categories, Roundcube Calendar plugin supports only one category at a time
if (!empty($event['categories'])) {
$result['categories'] = (array) $event['categories'];
}
// Organizer
if (!empty($event['attendees'])) {
foreach ($event['attendees'] as $idx => $attendee) {
if ($attendee['role'] == 'ORGANIZER') {
if ($name = $attendee['name']) {
$result['organizerName'] = $name;
}
if ($email = $attendee['email']) {
$result['organizerEmail'] = $email;
}
unset($event['attendees'][$idx]);
break;
}
}
}
// Attendees
if (!empty($event['attendees'])) {
foreach ($event['attendees'] as $idx => $attendee) {
$att = array();
if ($name = $attendee['name']) {
$att['name'] = $name;
}
if ($email = $attendee['email']) {
$att['email'] = $email;
}
if ($this->asversion >= 12) {
$type = isset($attendee['role']) ? $this->attendeeTypeMap[$attendee['role']] : null;
$status = isset($attendee['status']) ? $this->attendeeStatusMap[$attendee['status']] : null;
$att['attendeeType'] = $type ? $type : self::ATTENDEE_TYPE_REQUIRED;
$att['attendeeStatus'] = $status ? $status : self::ATTENDEE_STATUS_UNKNOWN;
}
$result['attendees'][] = new Syncroton_Model_EventAttendee($att);
}
}
// Event meeting status
$result['meetingStatus'] = intval(!empty($result['attendees']));
// Recurrence (and exceptions)
$this->recurrence_from_kolab($collection, $event, $result);
return $as_array ? $result : new Syncroton_Model_Event($result);
}
/**
* convert contact from xml to libkolab array
*
* @param Syncroton_Model_IEntry $data Contact to convert
* @param string $folderid Folder identifier
* @param array $entry Existing entry
* @param DateTimeZone $timezone Timezone of the event
*
* @return array
*/
public function toKolab(Syncroton_Model_IEntry $data, $folderid, $entry = null, $timezone = null)
{
$event = !empty($entry) ? $entry : array();
$foldername = isset($event['_mailbox']) ? $event['_mailbox'] : $this->getFolderName($folderid);
$config = $this->getFolderConfig($foldername);
$is_exception = $data instanceof Syncroton_Model_EventException;
$event['allday'] = 0;
// Timezone
if (!$timezone && isset($data->timezone)) {
$tzc = kolab_sync_timezone_converter::getInstance();
$expected = kolab_format::$timezone->getName();
if (!empty($event['start']) && ($event['start'] instanceof DateTime)) {
$expected = $event['start']->getTimezone()->getName();
}
$timezone = $tzc->getTimezone($data->timezone, $expected);
try {
$timezone = new DateTimeZone($timezone);
}
catch (Exception $e) {
$timezone = null;
}
}
if (empty($timezone)) {
$timezone = new DateTimeZone('UTC');
}
// Calendar namespace fields
foreach ($this->mapping as $key => $name) {
// skip UID field, unsupported in event exceptions
// we need to do this here, because the next line (data getter) will throw an exception
if ($is_exception && $key == 'uID') {
continue;
}
$value = $data->$key;
switch ($name) {
case 'changed':
$value = null;
break;
case 'end':
case 'start':
if ($timezone && $value) {
$value->setTimezone($timezone);
}
// In ActiveSync all-day event ends on 00:00:00 next day
if ($value && $data->allDayEvent && $name == 'end') {
$value->modify('-1 second');
}
break;
case 'sensitivity':
$map = array_flip($this->sensitivityMap);
$value = $map[$value];
break;
case 'free_busy':
$map = array_flip($this->busyStatusMap);
$value = $map[$value];
break;
case 'description':
$value = $this->getBody($value, Syncroton_Model_EmailBody::TYPE_PLAINTEXT);
// If description isn't specified keep old description
if ($value === null) {
continue 2;
}
break;
case 'uid':
// If UID is too long, use auto-generated UID (#1034)
// It's because UID is used as ServerId which cannot be longer than 64 chars
if (strlen($value) > 64) {
$value = null;
}
break;
}
$this->setKolabDataItem($event, $name, $value);
}
// Try to fix allday events from Android
// It doesn't set all-day flag but the period is a whole day
if (!$event['allday'] && $event['end'] && $event['start']) {
$interval = @date_diff($event['start'], $event['end']);
- if ($interval && $interval->format('%y%m%d%h%i%s') == '001000') {
+ if ($interval && $interval->format('%y%m%d%h%i%s') === '001000') {
$event['allday'] = 1;
$event['end'] = clone $event['start'];
}
}
// Reminder
// @TODO: should alarms be used when importing event from phone?
if ($config['ALARMS']) {
- $event['alarms'] = $this->to_kolab_alarm($data->reminder, $event);
+ $event['valarms'] = $this->to_kolab_alarm($data->reminder, $event);
}
$event['attendees'] = array();
$event['categories'] = array();
// Categories
if (isset($data->categories)) {
foreach ($data->categories as $category) {
$event['categories'][] = $category;
}
}
// Organizer
if (!$is_exception) {
$name = $data->organizerName;
$email = $data->organizerEmail;
if ($name || $email) {
$event['attendees'][] = array(
'role' => 'ORGANIZER',
'name' => $name,
'email' => $email,
);
}
}
// Attendees
if (isset($data->attendees)) {
foreach ($data->attendees as $attendee) {
$role = false;
if (isset($attendee->attendeeType)) {
$role = array_search($attendee->attendeeType, $this->attendeeTypeMap);
}
if ($role === false) {
$role = array_search(self::ATTENDEE_TYPE_REQUIRED, $this->attendeeTypeMap);
}
// AttendeeStatus send only on repsonse (?)
$event['attendees'][] = array(
'role' => $role,
'name' => $attendee->name,
'email' => $attendee->email,
);
}
}
// recurrence (and exceptions)
if (!$is_exception) {
$event['recurrence'] = $this->recurrence_to_kolab($data, $folderid, $timezone);
}
return $event;
}
/**
* Set attendee status for meeting
*
* @param Syncroton_Model_MeetingResponse $request The meeting response
*
* @return string ID of new calendar entry
*/
public function setAttendeeStatus(Syncroton_Model_MeetingResponse $request)
{
// @TODO: not implemented
throw new Syncroton_Exception_Status_MeetingResponse(Syncroton_Exception_Status_MeetingResponse::MEETING_ERROR);
}
/**
* Returns filter query array according to specified ActiveSync FilterType
*
* @param int $filter_type Filter type
*
* @param array Filter query
*/
protected function filter($filter_type = 0)
{
$filter = array(array('type', '=', $this->modelName));
switch ($filter_type) {
case Syncroton_Command_Sync::FILTER_2_WEEKS_BACK:
$mod = '-2 weeks';
break;
case Syncroton_Command_Sync::FILTER_1_MONTH_BACK:
$mod = '-1 month';
break;
case Syncroton_Command_Sync::FILTER_3_MONTHS_BACK:
$mod = '-3 months';
break;
case Syncroton_Command_Sync::FILTER_6_MONTHS_BACK:
$mod = '-6 months';
break;
}
if (!empty($mod)) {
$dt = new DateTime('now', new DateTimeZone('UTC'));
$dt->modify($mod);
$filter[] = array('dtend', '>', $dt);
}
return $filter;
}
/**
- * Converts libkolab alarms string into number of minutes
+ * Converts libkolab alarms spec. into a number of minutes
*/
- protected function from_kolab_alarm($value)
+ protected function from_kolab_alarm($event)
{
- // e.g. '-15M:DISPLAY'
- // Ignore EMAIL alarms
- if (preg_match('/^-([0-9]+)([WDHMS]):(DISPLAY|AUDIO)$/', $value, $matches)) {
- $value = intval($matches[1]);
+ if (isset($event['valarms'])) {
+ foreach ($event['valarms'] as $alarm) {
+ if (in_array($alarm['action'], array('DISPLAY', 'AUDIO'))) {
+ $value = $alarm['trigger'];
+ break;
+ }
+ }
+ }
+
+ if ($value && preg_match('/^([-+]*)[PT]*([0-9]+)([WDHMS])$/', $value, $matches)) {
+ $value = intval($matches[2]);
+
+ if ($value && $matches[1] != '-') {
+ return null;
+ }
- switch ($matches[2]) {
- case 'S': $value = 1; break;
+ switch ($matches[3]) {
+ case 'S': $value = intval(round($value/60)); break;
case 'H': $value *= 60; break;
case 'D': $value *= 24 * 60; break;
case 'W': $value *= 7 * 24 * 60; break;
}
return $value;
}
}
/**
- * Converts ActiveSync libkolab alarms string into number of minutes
+ * Converts ActiveSync reminder into libkolab alarms spec.
*/
protected function to_kolab_alarm($value, $event)
{
- // Get alarm type from old event object if exists
- if (!empty($event['alarms']) && preg_match('/:(.*)$/', $event['alarms'], $matches)) {
- $type = $matches[1];
+ if ($value === null || $value === '') {
+ return (array) $event['valarms'];
}
- if ($value) {
- return sprintf('-%dM:%s', $value, $type ? $type : 'DISPLAY');
+ $valarms = array();
+ $unsupported = array();
+
+ if (!empty($event['valarms'])) {
+ foreach ($event['valarms'] as $alarm) {
+ if (!$current && in_array($alarm['action'], array('DISPLAY', 'AUDIO'))) {
+ $current = $alarm;
+ }
+ else {
+ $unsupported[] = $alarm;
+ }
+ }
}
- if ($type == 'DISPLAY' || $type == 'AUDIO') {
- return null;
+ $valarms[] = array(
+ 'action' => $current['action'] ?: 'DISPLAY',
+ 'description' => $current['description'] ?: '',
+ 'trigger' => sprintf('-PT%dM', $value),
+ );
+
+ if (!empty($unsupported)) {
+ $valarms = array_merge($valarms, $unsupported);
}
- return $event['alarms'];
+ return $valarms;
}
-
}
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
index 48e8d0d..ce032de 100644
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -1,1603 +1,1603 @@
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com> |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
/**
* Email data class for Syncroton
*/
class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_IDataSearch
{
const MAX_SEARCH_RESULT = 200;
/**
* Mapping from ActiveSync Email namespace fields
*/
protected $mapping = array(
'cc' => 'cc',
//'contentClass' => 'contentclass',
'dateReceived' => 'internaldate',
//'displayTo' => 'displayto', //?
//'flag' => 'flag',
'from' => 'from',
//'importance' => 'importance',
'internetCPID' => 'charset',
//'messageClass' => 'messageclass',
'replyTo' => 'replyto',
//'read' => 'read',
'subject' => 'subject',
//'threadTopic' => 'threadtopic',
'to' => 'to',
);
/**
* Special folder type/name map
*
* @var array
*/
protected $folder_types = array(
2 => 'Inbox',
3 => 'Drafts',
4 => 'Deleted Items',
5 => 'Sent Items',
6 => 'Outbox',
);
/**
* Kolab object type
*
* @var string
*/
protected $modelName = 'mail';
/**
* Type of the default folder
*
* @var int
*/
protected $defaultFolderType = Syncroton_Command_FolderSync::FOLDERTYPE_INBOX;
/**
* Default container for new entries
*
* @var string
*/
protected $defaultFolder = 'INBOX';
/**
* Type of user created folders
*
* @var int
*/
protected $folderType = Syncroton_Command_FolderSync::FOLDERTYPE_MAIL_USER_CREATED;
/**
* the constructor
*
* @param Syncroton_Model_IDevice $device
* @param DateTime $syncTimeStamp
*/
public function __construct(Syncroton_Model_IDevice $device, DateTime $syncTimeStamp)
{
parent::__construct($device, $syncTimeStamp);
$this->storage = rcube::get_instance()->get_storage();
// Outlook 2013 support multi-folder
$this->ext_devices[] = 'windowsoutlook15';
if ($this->asversion >= 14) {
$this->tag_categories = true;
}
}
/**
* Creates model object
*
* @param Syncroton_Model_SyncCollection $collection Collection data
* @param string $serverId Local entry identifier
*
* @return Syncroton_Model_Email Email object
*/
public function getEntry(Syncroton_Model_SyncCollection $collection, $serverId)
{
$message = $this->getObject($serverId);
// error (message doesn't exist?)
if (empty($message)) {
throw new Syncroton_Exception_NotFound("Message $serverId not found");
}
$headers = $message->headers; // rcube_message_header
// Calendar namespace fields
foreach ($this->mapping as $key => $name) {
$value = null;
switch ($name) {
case 'internaldate':
$value = self::date_from_kolab(rcube_imap_generic::strToTime($headers->internaldate));
break;
case 'cc':
case 'to':
case 'replyto':
case 'from':
$addresses = rcube_mime::decode_address_list($headers->$name, null, true, $headers->charset);
foreach ($addresses as $idx => $part) {
// @FIXME: set name + address or address only?
$addresses[$idx] = format_email_recipient($part['mailto'], $part['name']);
}
$value = implode(',', $addresses);
break;
case 'subject':
$value = $headers->get('subject');
break;
case 'charset':
$value = self::charset_to_cp($headers->charset);
break;
}
if (empty($value) || is_array($value)) {
continue;
}
if (is_string($value)) {
$value = rcube_charset::clean($value);
}
$result[$key] = $value;
}
// $result['ConversationId'] = 'FF68022058BD485996BE15F6F6D99320';
// $result['ConversationIndex'] = 'CA2CFA8A23';
// Read flag
$result['read'] = intval(!empty($headers->flags['SEEN']));
// Flagged message
if (!empty($headers->flags['FLAGGED'])) {
// Use FollowUp flag which is used in Android when message is marked with a star
$result['flag'] = new Syncroton_Model_EmailFlag(array(
'flagType' => 'FollowUp',
'status' => Syncroton_Model_EmailFlag::STATUS_ACTIVE,
));
}
// Importance/Priority
if ($headers->priority) {
if ($headers->priority < 3) {
$result['importance'] = 2; // High
}
else if ($headers->priority > 3) {
$result['importance'] = 0; // Low
}
}
// get truncation and body type
$airSyncBaseType = Syncroton_Command_Sync::BODY_TYPE_PLAIN_TEXT;
$truncateAt = null;
$opts = $collection->options;
$prefs = $opts['bodyPreferences'];
if ($opts['mimeSupport'] == Syncroton_Command_Sync::MIMESUPPORT_SEND_MIME) {
$airSyncBaseType = Syncroton_Command_Sync::BODY_TYPE_MIME;
if (isset($prefs[Syncroton_Command_Sync::BODY_TYPE_MIME]['truncationSize'])) {
$truncateAt = $prefs[Syncroton_Command_Sync::BODY_TYPE_MIME]['truncationSize'];
}
else if (isset($opts['mimeTruncation']) && $opts['mimeTruncation'] < Syncroton_Command_Sync::TRUNCATE_NOTHING) {
switch ($opts['mimeTruncation']) {
case Syncroton_Command_Sync::TRUNCATE_ALL:
$truncateAt = 0;
break;
case Syncroton_Command_Sync::TRUNCATE_4096:
$truncateAt = 4096;
break;
case Syncroton_Command_Sync::TRUNCATE_5120:
$truncateAt = 5120;
break;
case Syncroton_Command_Sync::TRUNCATE_7168:
$truncateAt = 7168;
break;
case Syncroton_Command_Sync::TRUNCATE_10240:
$truncateAt = 10240;
break;
case Syncroton_Command_Sync::TRUNCATE_20480:
$truncateAt = 20480;
break;
case Syncroton_Command_Sync::TRUNCATE_51200:
$truncateAt = 51200;
break;
case Syncroton_Command_Sync::TRUNCATE_102400:
$truncateAt = 102400;
break;
}
}
}
else {
// The spec is not very clear, but it looks that if MimeSupport is not set
// we can't add Syncroton_Command_Sync::BODY_TYPE_MIME to the supported types
// list below (Bug #1688)
$types = array(
Syncroton_Command_Sync::BODY_TYPE_HTML,
Syncroton_Command_Sync::BODY_TYPE_PLAIN_TEXT,
);
// @TODO: if client can support both HTML and TEXT use one of
// them which is better according to the real message body type
foreach ($types as $type) {
if (!empty($prefs[$type])) {
if (!empty($prefs[$type]['truncationSize'])) {
$truncateAt = $prefs[$type]['truncationSize'];
}
$preview = (int) $prefs[$type]['preview'];
$airSyncBaseType = $type;
break;
}
}
}
$body_params = array('type' => $airSyncBaseType);
// Message body
// In Sync examples there's one in which bodyPreferences is not defined
// in such case Truncated=1 and there's no body sent to the client
// only it's estimated size
if (empty($prefs)) {
$messageBody = '';
$real_length = $message->size;
$truncateAt = 0;
$body_length = 0;
$isTruncated = 1;
}
else if ($airSyncBaseType == Syncroton_Command_Sync::BODY_TYPE_MIME) {
$messageBody = $this->storage->get_raw_body($message->uid);
// make the source safe (Bug #2715, #2757)
$messageBody = kolab_sync_message::recode_message($messageBody);
// strip out any non utf-8 characters
$messageBody = rcube_charset::clean($messageBody);
$real_length = $body_length = strlen($messageBody);
}
else {
$messageBody = $this->getMessageBody($message, $airSyncBaseType == Syncroton_Command_Sync::BODY_TYPE_HTML);
// strip out any non utf-8 characters
$messageBody = rcube_charset::clean($messageBody);
$real_length = $body_length = strlen($messageBody);
}
// add Preview element to the Body result
if (!empty($preview) && $body_length) {
$body_params['preview'] = $this->getPreview($messageBody, $airSyncBaseType, $preview);
}
// truncate the body if needed
if ($truncateAt && $body_length > $truncateAt) {
$messageBody = mb_strcut($messageBody, 0, $truncateAt);
$body_length = strlen($messageBody);
$isTruncated = 1;
}
if ($isTruncated) {
$body_params['truncated'] = 1;
$body_params['estimatedDataSize'] = $real_length;
}
// add Body element to the result
$result['body'] = $this->setBody($messageBody, $body_params);
// original body type
// @TODO: get this value from getMessageBody()
$result['nativeBodyType'] = $message->has_html_part() ? 2 : 1;
// Message class
// @TODO: add messageClass suffix for encrypted messages
$result['messageClass'] = 'IPM.Note';
$result['contentClass'] = 'urn:content-classes:message';
// Categories (Tags)
if ($this->tag_categories) {
// convert kolab tags into categories
$result['categories'] = $this->getKolabTags($message);
}
// attachments
$attachments = array_merge($message->attachments, $message->inline_parts);
if (!empty($attachments)) {
$result['attachments'] = array();
foreach ($attachments as $attachment) {
$att = array();
$filename = rcube_charset::clean($attachment->filename);
if (empty($filename) && $attachment->mimetype == 'text/html') {
$filename = 'HTML Part';
}
$att['displayName'] = $filename;
$att['fileReference'] = $serverId . '::' . $attachment->mime_id;
$att['method'] = 1;
$att['estimatedDataSize'] = $attachment->size;
if (!empty($attachment->content_id)) {
$att['contentId'] = rcube_charset::clean($attachment->content_id);
}
if (!empty($attachment->content_location)) {
$att['contentLocation'] = rcube_charset::clean($attachment->content_location);
}
if (in_array($attachment, $message->inline_parts)) {
$att['isInline'] = 1;
}
$result['attachments'][] = new Syncroton_Model_EmailAttachment($att);
}
}
return new Syncroton_Model_Email($result);
}
/**
* Returns properties of a message for Search response
*
* @param string $longId Message identifier
* @param array $options Search options
*
* @return Syncroton_Model_Email Email object
*/
public function getSearchEntry($longId, $options)
{
$collection = new Syncroton_Model_SyncCollection(array(
'options' => $options,
));
return $this->getEntry($collection, $longId);
}
/**
* convert contact from xml to libkolab array
*
* @param Syncroton_Model_IEntry $data Contact to convert
* @param string $folderid Folder identifier
* @param array $entry Existing entry
*
* @return array
*/
public function toKolab(Syncroton_Model_IEntry $data, $folderid, $entry = null)
{
// does nothing => you can't add emails via ActiveSync
}
/**
* Returns filter query array according to specified ActiveSync FilterType
*
* @param int $filter_type Filter type
*
* @param array Filter query
*/
protected function filter($filter_type = 0)
{
$filter = array();
switch ($filter_type) {
case Syncroton_Command_Sync::FILTER_1_DAY_BACK:
$mod = '-1 day';
break;
case Syncroton_Command_Sync::FILTER_3_DAYS_BACK:
$mod = '-3 days';
break;
case Syncroton_Command_Sync::FILTER_1_WEEK_BACK:
$mod = '-1 week';
break;
case Syncroton_Command_Sync::FILTER_2_WEEKS_BACK:
$mod = '-2 weeks';
break;
case Syncroton_Command_Sync::FILTER_1_MONTH_BACK:
$mod = '-1 month';
break;
}
if (!empty($mod)) {
$dt = new DateTime('now', new DateTimeZone('UTC'));
$dt->modify($mod);
// RFC3501: IMAP SEARCH
$filter[] = 'SINCE ' . $dt->format('d-M-Y');
}
return $filter;
}
/**
* Return list of supported folders for this backend
*
* @return array
*/
public function getAllFolders()
{
$list = $this->listFolders();
if (!is_array($list)) {
throw new Syncroton_Exception_Status_FolderSync(Syncroton_Exception_Status_FolderSync::FOLDER_SERVER_ERROR);
}
// device doesn't support multiple folders
if (!in_array(strtolower($this->device->devicetype), $this->ext_devices)) {
// We'll return max. one folder of supported type
$result = array();
$types = $this->folder_types;
foreach ($list as $idx => $folder) {
$type = $folder['type'] == 12 ? 2 : $folder['type']; // unknown to Inbox
if ($folder_id = $types[$type]) {
$result[$folder_id] = array(
'displayName' => $folder_id,
'serverId' => $folder_id,
'parentId' => 0,
'type' => $type,
);
}
}
$list = $result;
}
foreach ($list as $idx => $folder) {
$list[$idx] = new Syncroton_Model_Folder($folder);
}
return $list;
}
/**
* Return list of folders for specified folder ID
*
* @return array Folder identifiers list
*/
protected function extractFolders($folder_id)
{
$list = $this->listFolders();
$result = array();
if (!is_array($list)) {
throw new Syncroton_Exception_NotFound('Folder not found');
}
// device supports multiple folders?
if (in_array(strtolower($this->device->devicetype), $this->ext_devices)) {
if ($list[$folder_id]) {
$result[] = $folder_id;
}
}
else if ($type = array_search($folder_id, $this->folder_types)) {
foreach ($list as $id => $folder) {
if ($folder['type'] == $type || ($folder_id == 'Inbox' && $folder['type'] == 12)) {
$result[] = $id;
}
}
}
if (empty($result)) {
throw new Syncroton_Exception_NotFound('Folder not found');
}
return $result;
}
/**
* Moves object into another location (folder)
*
* @param string $srcFolderId Source folder identifier
* @param string $serverId Object identifier
* @param string $dstFolderId Destination folder identifier
*
* @throws Syncroton_Exception_Status
* @return string New object identifier
*/
public function moveItem($srcFolderId, $serverId, $dstFolderId)
{
$msg = $this->parseMessageId($serverId);
$dest = $this->extractFolders($dstFolderId);
$dest_id = array_shift($dest);
$dest_name = $this->backend->folder_id2name($dest_id, $this->device->deviceid);
if (empty($msg)) {
throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
}
if ($dest_name === null) {
throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_DESTINATION);
}
if (!$this->storage->move_message($msg['uid'], $dest_name, $msg['foldername'])) {
throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
}
// Use COPYUID feature (RFC2359) to get the new UID of the copied message
$copyuid = $this->storage->conn->data['COPYUID'];
if (is_array($copyuid) && ($uid = $copyuid[1])) {
return $this->createMessageId($dest_id, $uid);
}
}
/**
* add entry from xml data
*
* @param string $folderId Folder identifier
* @param Syncroton_Model_IEntry $entry Entry
*
* @return array
*/
public function createEntry($folderId, Syncroton_Model_IEntry $entry)
{
// Throw exception here for better handling of unsupported
// entry creation, it can be object of class Email or SMS here
throw new Syncroton_Exception_Status_Sync(Syncroton_Exception_Status_Sync::SYNC_SERVER_ERROR);
}
/**
* Update existing message
*
* @param string $folderId Folder identifier
* @param string $serverId Entry identifier
* @param Syncroton_Model_IEntry $entry Entry
*/
public function updateEntry($folderId, $serverId, Syncroton_Model_IEntry $entry)
{
$msg = $this->parseMessageId($serverId);
$message = $this->getObject($serverId);
if (empty($message)) {
throw new Syncroton_Exception_Status_Sync(Syncroton_Exception_Status_Sync::SYNC_SERVER_ERROR);
}
$is_flagged = !empty($message->headers->flags['FLAGGED']);
// Read status change
if (isset($entry->read)) {
// here we update only Read flag
$flag = (((int)$entry->read != 1) ? 'UN' : '') . 'SEEN';
$this->storage->set_flag($msg['uid'], $flag, $msg['foldername']);
}
// Flag change
if (isset($entry->flag) && (empty($entry->flag) || empty($entry->flag->flagType))) {
if ($is_flagged) {
$this->storage->set_flag($msg['uid'], 'UNFLAGGED', $msg['foldername']);
}
}
else if (!$is_flagged && !empty($entry->flag)) {
if ($entry->flag->flagType && preg_match('/follow\s*up/i', $entry->flag->flagType)) {
$this->storage->set_flag($msg['uid'], 'FLAGGED', $msg['foldername']);
}
}
// Categories (Tags) change
if (isset($entry->categories)) {
$this->setKolabTags($message, $entry->categories);
}
}
/**
* delete entry
*
* @param string $folderId
* @param string $serverId
* @param Syncroton_Model_SyncCollection $collection
*/
public function deleteEntry($folderId, $serverId, $collection)
{
$trash = kolab_sync::get_instance()->config->get('trash_mbox');
$msg = $this->parseMessageId($serverId);
if (empty($msg)) {
throw new Syncroton_Exception_Status_Sync(Syncroton_Exception_Status_Sync::SYNC_SERVER_ERROR);
}
// move message to trash folder
if ($collection->deletesAsMoves
&& strlen($trash)
&& $trash != $msg['foldername']
&& $this->storage->folder_exists($trash)
) {
$this->storage->move_message($msg['uid'], $trash, $msg['foldername']);
}
// set delete flag
else {
$this->storage->set_flag($msg['uid'], 'DELETED', $msg['foldername']);
}
}
/**
* Send an email
*
* @param mixed $message MIME message
* @param boolean $saveInSent Enables saving the sent message in Sent folder
*
* @throws Syncroton_Exception_Status
*/
public function sendEmail($message, $saveInSent)
{
if (!($message instanceof kolab_sync_message)) {
$message = new kolab_sync_message($message);
}
$sent = $message->send($smtp_error);
if (!$sent) {
throw new Syncroton_Exception_Status(Syncroton_Exception_Status::MAIL_SUBMISSION_FAILED);
}
// Save sent message in Sent folder
if ($saveInSent) {
$sent_folder = kolab_sync::get_instance()->config->get('sent_mbox');
if (strlen($sent_folder) && $this->storage->folder_exists($sent_folder)) {
return $this->storage->save_message($sent_folder, $message->source(), '', false, array('SEEN'));
}
}
}
/**
* Forward an email
*
* @param array|string $itemId A string LongId or an array with following properties:
* collectionId, itemId and instanceId
* @param resource|string $body MIME message
* @param boolean $saveInSent Enables saving the sent message in Sent folder
* @param boolean $replaceMime If enabled, original message would be appended
*
* @throws Syncroton_Exception_Status
*/
public function forwardEmail($itemId, $body, $saveInSent, $replaceMime)
{
/*
@TODO:
The SmartForward command can be applied to a meeting. When SmartForward is applied to a recurring meeting,
the InstanceId element (section 2.2.3.83.2) specifies the ID of a particular occurrence in the recurring meeting.
If SmartForward is applied to a recurring meeting and the InstanceId element is absent, the server SHOULD
forward the entire recurring meeting. If the value of the InstanceId element is invalid, the server responds
with Status element (section 2.2.3.162.15) value 104, as specified in section 2.2.4.
When the SmartForward command is used for an appointment, the original message is included by the server
as an attachment to the outgoing message. When the SmartForward command is used for a normal message
or a meeting, the behavior of the SmartForward command is the same as that of the SmartReply command (section 2.2.2.18).
*/
$msg = $this->parseMessageId($itemId);
$message = $this->getObject($itemId);
if (empty($message)) {
throw new Syncroton_Exception_Status(Syncroton_Exception_Status::ITEM_NOT_FOUND);
}
// Parse message
$sync_msg = new kolab_sync_message($body);
// forward original message as attachment
if (!$replaceMime) {
$this->storage->set_folder($msg['foldername']);
$attachment = $this->storage->get_raw_body($msg['uid']);
if (empty($attachment)) {
throw new Syncroton_Exception_Status(Syncroton_Exception_Status::ITEM_NOT_FOUND);
}
$sync_msg->add_attachment($attachment, array(
'encoding' => '8bit',
'content_type' => 'message/rfc822',
'disposition' => 'inline',
//'name' => 'message.eml',
));
}
// Send message
$this->sendEmail($sync_msg, $saveInSent);
// Set FORWARDED flag on the replied message
if (empty($message->headers->flags['FORWARDED'])) {
$this->storage->set_flag($msg['uid'], 'FORWARDED', $msg['foldername']);
}
}
/**
* Reply to an email
*
* @param array|string $itemId A string LongId or an array with following properties:
* collectionId, itemId and instanceId
* @param resource|string $body MIME message
* @param boolean $saveInSent Enables saving the sent message in Sent folder
* @param boolean $replaceMime If enabled, original message would be appended
*
* @throws Syncroton_Exception_Status
*/
public function replyEmail($itemId, $body, $saveInSent, $replaceMime)
{
$msg = $this->parseMessageId($itemId);
$message = $this->getObject($itemId);
if (empty($message)) {
throw new Syncroton_Exception_Status(Syncroton_Exception_Status::ITEM_NOT_FOUND);
}
$sync_msg = new kolab_sync_message($body);
$headers = $sync_msg->headers();
// Add References header
if (empty($headers['References'])) {
$sync_msg->set_header('References', trim($message->headers->references . ' ' . $message->headers->messageID));
}
// Get original message body
if (!$replaceMime) {
// @TODO: here we're assuming that reply message is in text/plain format
// So, original message will be converted to plain text if needed
$message_body = $this->getMessageBody($message, false);
// Quote original message body
$message_body = self::wrap_and_quote(trim($message_body), 72);
// Join bodies
$sync_msg->append("\n" . ltrim($message_body));
}
// Send message
$this->sendEmail($sync_msg, $saveInSent);
// Set ANSWERED flag on the replied message
if (empty($message->headers->flags['ANSWERED'])) {
$this->storage->set_flag($msg['uid'], 'ANSWERED', $msg['foldername']);
}
}
/**
* Search for existing entries
*
* @param string $folderid
* @param array $filter
* @param int $result_type Type of the result (see RESULT_* constants)
*
* @return array|int Search result as count or array of uids/objects
*/
protected function searchEntries($folderid, $filter = array(), $result_type = self::RESULT_UID)
{
$folders = $this->extractFolders($folderid);
$filter_str = 'ALL UNDELETED';
// convert filter into one IMAP search string
foreach ($filter as $idx => $filter_item) {
if (is_array($filter_item)) {
// This is a request for changes since last time
// we'll use HIGHESTMODSEQ value from the last Sync
if ($filter_item[0] == 'changed' && $filter_item[1] == '>') {
$modseq_lasttime = $filter_item[2];
$modseq_data = array();
$modseq = (array) $this->backend->modseq_get($this->device->id, $folderid, $modseq_lasttime);
}
}
else {
$filter_str .= ' ' . $filter_item;
}
}
// get members of modified relations
$changed_msgs = $this->getChangesByRelations($folderid, $filter);
$result = $result_type == self::RESULT_COUNT ? 0 : array();
$found = 0;
$ts = time();
foreach ($folders as $folder_id) {
$foldername = $this->backend->folder_id2name($folder_id, $this->device->deviceid);
if ($foldername === null) {
continue;
}
$found++;
$this->storage->set_folder($foldername);
// Synchronize folder (if it wasn't synced in this request already)
if ($this->lastsync_folder != $folderid
|| $this->lastsync_time <= $ts - Syncroton_Registry::getPingTimeout()
) {
$this->storage->folder_sync($foldername);
}
// We're in "get changes" mode
if (isset($modseq_data)) {
$folder_data = $this->storage->folder_data($foldername);
$modified = false;
// If previous HIGHESTMODSEQ doesn't exist we can't get changes
// We can only get folder's HIGHESTMODSEQ value and store it for the next try
// Skip search if HIGHESTMODSEQ didn't change
if ($folder_data['HIGHESTMODSEQ']) {
$modseq_data[$foldername] = $folder_data['HIGHESTMODSEQ'];
if ($modseq_data[$foldername] != $modseq[$foldername]) {
$modseq_update = true;
- }
- else if ($modseq && $modseq[$foldername]) {
- $modified = true;
- $filter_str .= " MODSEQ " . ($modseq[$foldername] + 1);
+ if ($modseq && $modseq[$foldername]) {
+ $modified = true;
+ $filter_str .= " MODSEQ " . ($modseq[$foldername] + 1);
+ }
}
}
}
else {
$modified = true;
}
// We could use messages cache by replacing search() with index()
// in some cases. This however is possible only if user has skip_deleted=true,
// in his Roundcube preferences, otherwise we'd make often cache re-initialization,
// because Roundcube message cache can work only with one skip_deleted
// setting at a time. We'd also need to make sure folder_sync() was called
// before (see above).
//
// if ($filter_str == 'ALL UNDELETED')
// $search = $this->storage->index($foldername, null, null, true, true);
// else
if ($modified) {
$search = $this->storage->search_once($foldername, $filter_str);
if (!($search instanceof rcube_result_index) || $search->is_error()) {
throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR);
}
switch ($result_type) {
case self::RESULT_COUNT:
$result += (int) $search->count();
break;
case self::RESULT_UID:
if ($uids = $search->get()) {
foreach ($uids as $idx => $uid) {
$uids[$idx] = $this->createMessageId($folder_id, $uid);
}
$result = array_merge($result, $uids);
}
break;
}
}
// handle relation changes
if (!empty($changed_msgs)) {
$uids = $this->findRelationMembersInFolder($foldername, $changed_msgs, $filter);
switch ($result_type) {
case self::RESULT_COUNT:
$result += (int) count($uids);
break;
case self::RESULT_UID:
foreach ($uids as $idx => $uid) {
$uids[$idx] = $this->createMessageId($folder_id, $uid);
}
$result = array_unique(array_merge($result, $uids));
break;
}
}
}
if (!$found) {
throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR);
}
$this->lastsync_folder = $folderid;
$this->lastsync_time = $ts;
if (!empty($modseq_update)) {
$this->backend->modseq_set($this->device->id, $folderid,
$this->syncTimeStamp, $modseq_data);
// if previous modseq information does not exist save current set as it,
// we would at least be able to detect changes since now
if (empty($result) && empty($modseq)) {
$this->backend->modseq_set($this->device->id, $folderid,
$modseq_lasttime, $modseq_data);
}
}
return $result;
}
/**
* Find members (messages) in specified folder
*/
protected function findRelationMembersInFolder($foldername, $members, $filter)
{
foreach ($members as $member) {
// IMAP URI members
if ($url = kolab_storage_config::parse_member_url($member)) {
$result[$url['folder']][$url['uid']] = $url['params'];
}
}
// convert filter into one IMAP search string
$filter_str = 'ALL UNDELETED';
foreach ($filter as $filter_item) {
if (is_string($filter_item)) {
$filter_str .= ' ' . $filter_item;
}
}
$rcube = rcube::get_instance();
$storage = $rcube->get_storage();
$found = array();
// first find messages by UID
if (!empty($result[$foldername])) {
$index = $storage->search_once($foldername, 'UID '
. rcube_imap_generic::compressMessageSet(array_keys($result[$foldername])));
$found = $index->get();
// remove found messages from the $result
if (!empty($found)) {
$result[$foldername] = array_diff_key($result[$foldername], array_flip($found));
if (empty($result[$foldername])) {
unset($result[$foldername]);
}
// now apply the current filter to the found messages
$index = $storage->search_once($foldername, $filter_str . ' UID '
. rcube_imap_generic::compressMessageSet($found));
$found = $index->get();
}
}
// search by message parameters
if (!empty($result)) {
// @TODO: do this search in chunks (for e.g. 25 messages)?
$search = '';
$search_count = 0;
foreach ($result as $data) {
foreach ($data as $p) {
$search_params = array();
$search_count++;
foreach ($p as $key => $val) {
$key = strtoupper($key);
// don't search by subject, we don't want false-positives
if ($key != 'SUBJECT') {
$search_params[] = 'HEADER ' . $key . ' ' . rcube_imap_generic::escape($val);
}
}
$search .= ' (' . implode(' ', $search_params) . ')';
}
}
$search_str .= ' ' . str_repeat(' OR', $search_count-1) . $search;
// search messages in current folder
$search = $storage->search_once($foldername, $search_str);
$uids = $search->get();
if (!empty($uids)) {
// add UIDs into the result
$found = array_unique(array_merge($found, $uids));
}
}
return $found;
}
/**
* ActiveSync Search handler
*
* @param Syncroton_Model_StoreRequest $store Search query
*
* @return Syncroton_Model_StoreResponse Complete Search response
*/
public function search(Syncroton_Model_StoreRequest $store)
{
list($folders, $search_str) = $this->parse_search_query($store);
if (empty($search_str)) {
throw new Exception('Empty/invalid search request');
}
if (!is_array($folders)) {
throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR);
}
$result = array();
// @TODO: caching with Options->RebuildResults support
foreach ($folders as $folderid) {
$foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid);
if ($foldername === null) {
continue;
}
// $this->storage->set_folder($foldername);
// $this->storage->folder_sync($foldername);
$search = $this->storage->search_once($foldername, $search_str);
if (!($search instanceof rcube_result_index)) {
continue;
}
$uids = $search->get();
foreach ($uids as $idx => $uid) {
$uids[$idx] = new Syncroton_Model_StoreResponseResult(array(
'longId' => $this->createMessageId($folderid, $uid),
'collectionId' => $folderid,
'class' => 'Email',
));
}
$result = array_merge($result, $uids);
// We don't want to search all folders if we've got already a lot messages
if (count($result) >= self::MAX_SEARCH_RESULT) {
break;
}
}
$result = array_values($result);
$response = new Syncroton_Model_StoreResponse();
// Calculate requested range
$start = (int) $store->options['range'][0];
$limit = (int) $store->options['range'][1] + 1;
$total = count($result);
$response->total = $total;
// Get requested chunk of data set
if ($total) {
if ($start > $total) {
$start = $total;
}
if ($limit > $total) {
$limit = max($start+1, $total);
}
if ($start > 0 || $limit < $total) {
$result = array_slice($result, $start, $limit-$start);
}
$response->range = array($start, $start + count($result) - 1);
}
// Build result array, convert to ActiveSync format
foreach ($result as $idx => $rec) {
$rec->properties = $this->getSearchEntry($rec->longId, $store->options);
$response->result[] = $rec;
unset($result[$idx]);
}
return $response;
}
/**
* Converts ActiveSync search parameters into IMAP search string
*/
protected function parse_search_query($store)
{
$options = $store->options;
$query = $store->query;
$search_str = '';
$folders = array();
if (empty($query) || !is_array($query)) {
return array();
}
if (isset($query['and']['freeText']) && strlen($query['and']['freeText'])) {
$search = $query['and']['freeText'];
}
if (!empty($query['and']['collections'])) {
foreach ($query['and']['collections'] as $collection) {
$folders = array_merge($folders, $this->extractFolders($collection));
}
}
if (!empty($query['and']['greaterThan'])
&& !empty($query['and']['greaterThan']['dateReceived'])
&& !empty($query['and']['greaterThan']['value'])
) {
$search_str .= ' SINCE ' . $query['and']['greaterThan']['value']->format('d-M-Y');
}
if (!empty($query['and']['lessThan'])
&& !empty($query['and']['lessThan']['dateReceived'])
&& !empty($query['and']['lessThan']['value'])
) {
$search_str .= ' BEFORE ' . $query['and']['lessThan']['value']->format('d-M-Y');
}
if ($search !== null) {
// @FIXME: should we use TEXT/BODY search?
// ActiveSync protocol specification says "indexed fields"
$search_keys = array('SUBJECT', 'TO', 'FROM', 'CC');
$search_str .= str_repeat(' OR', count($search_keys)-1);
foreach ($search_keys as $key) {
$search_str .= sprintf(" %s {%d}\r\n%s", $key, strlen($search), $search);
}
}
if (empty($search_str)) {
return array();
}
$search_str = 'ALL UNDELETED ' . trim($search_str);
// @TODO: DeepTraversal
if (empty($folders)) {
$folders = $this->listFolders();
if (is_array($folders)) {
$folders = array_keys($folders);
}
}
return array($folders, $search_str);
}
/**
* Fetches the entry from the backend
*/
protected function getObject($entryid, &$folder = null)
{
$message = $this->parseMessageId($entryid);
if (empty($message)) {
// @TODO: exception?
return null;
}
// get message
$message = new rcube_message($message['uid'], $message['foldername']);
return $message && !empty($message->headers) ? $message : null;
}
/**
* @return Syncroton_Model_FileReference
*/
public function getFileReference($fileReference)
{
list($folderid, $uid, $part_id) = explode('::', $fileReference);
$message = $this->getObject($fileReference);
if (!$message) {
throw new Syncroton_Exception_NotFound('Message not found');
}
$part = $message->mime_parts[$part_id];
$body = $message->get_part_body($part_id);
return new Syncroton_Model_FileReference(array(
'contentType' => $part->mimetype,
'data' => $body,
));
}
/**
* Parses entry ID to get folder name and UID of the message
*/
protected function parseMessageId($entryid)
{
// replyEmail/forwardEmail
if (is_array($entryid)) {
$entryid = $entryid['itemId'];
}
list($folderid, $uid) = explode('::', $entryid);
$foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid);
if ($foldername === null || $foldername === false) {
// @TODO exception?
return null;
}
return array(
'uid' => $uid,
'folderid' => $folderid,
'foldername' => $foldername,
);
}
/**
* Creates entry ID of the message
*/
public function createMessageId($folderid, $uid)
{
return $folderid . '::' . $uid;
}
/**
* Returns body of the message in specified format
*/
protected function getMessageBody($message, $html = false)
{
if (!is_array($message->parts) && empty($message->body)) {
return '';
}
if (!empty($message->parts)) {
foreach ($message->parts as $part) {
// skip no-content and attachment parts (#1488557)
if ($part->type != 'content' || !$part->size || $message->is_attachment($part)) {
continue;
}
return $this->getMessagePartBody($message, $part, $html);
}
}
return $this->getMessagePartBody($message, $message, $html);
}
/**
* Returns body of the message part in specified format
*/
protected function getMessagePartBody($message, $part, $html = false)
{
// Check if we have enough memory to handle the message in it
// @FIXME: we need up to 5x more memory than the body
if (!rcube_utils::mem_check($part->size * 5)) {
return '';
}
$body = $message->get_part_body($part->mime_id, true);
// message is cached but not exists, or other error
if ($body === false) {
return '';
}
if ($html) {
if ($part->ctype_secondary == 'html') {
// charset was converted to UTF-8 in rcube_storage::get_message_part(),
// change/add charset specification in HTML accordingly
$meta = '<meta http-equiv="Content-Type" content="text/html; charset='.RCUBE_CHARSET.'" />';
// remove old meta tag and add the new one, making sure
// that it is placed in the head
$body = preg_replace('/<meta[^>]+charset=[a-z0-9-_]+[^>]*>/Ui', '', $body);
$body = preg_replace('/(<head[^>]*>)/Ui', '\\1'.$meta, $body, -1, $rcount);
if (!$rcount) {
$body = '<head>' . $meta . '</head>' . $body;
}
}
else if ($part->ctype_secondary == 'enriched') {
$body = rcube_enriched::to_html($body);
}
else {
$body = '<pre>' . $body . '</pre>';
}
}
else {
if ($part->ctype_secondary == 'enriched') {
$body = rcube_enriched::to_html($body);
$part->ctype_secondary = 'html';
}
if ($part->ctype_secondary == 'html') {
$txt = new rcube_html2text($body, false, true);
$body = $txt->get_text();
}
else {
if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed') {
$body = rcube_mime::unfold_flowed($body);
}
}
}
return $body;
}
/**
* Converts and truncates message body for use in <Preview>
*
* @return string Truncated plain text message
*/
protected function getPreview($body, $type, $size)
{
if ($type == Syncroton_Command_Sync::BODY_TYPE_HTML) {
$txt = new rcube_html2text($body, false, true);
$body = $txt->get_text();
}
// size limit defined in ActiveSync protocol
if ($size > 255) {
$size = 255;
}
return mb_strcut(trim($body), 0, $size);
}
/**
* Returns list of tag names assigned to an email message
*/
protected function getKolabTags($message)
{
// support only messages with message-id
if (!($msg_id = $message->headers->get('message-id', false))) {
return null;
}
$config = kolab_storage_config::get_instance();
$delta = Syncroton_Registry::getPingTimeout();
$folder = $message->folder;
$uid = $message->uid;
// get tag objects raleted to specified message-id
$tags = $config->get_tags($msg_id);
foreach ($tags as $idx => $tag) {
// resolve members if it wasn't done recently
$force = empty($this->tag_rts[$tag['uid']]) || $this->tag_rts[$tag['uid']] <= time() - $delta;
$members = $config->resolve_members($tag, $force);
if (empty($members[$folder]) || !in_array($uid, $members[$folder])) {
unset($tags[$idx]);
}
if ($force) {
$this->tag_rts[$tag['uid']] = time();
}
}
$tags = array_filter(array_map(function($v) { return $v['name']; }, $tags));
// make sure current folder is set correctly again
$this->storage->set_folder($folder);
return !empty($tags) ? $tags : null;
}
/**
* Set tags to an email message
*/
protected function setKolabTags($message, $tags)
{
$config = kolab_storage_config::get_instance();
$delta = Syncroton_Registry::getPingTimeout();
$folder = $message->folder;
$uri = kolab_storage_config::get_message_uri($message->headers, $folder);
// for all tag objects...
foreach ($config->get_tags() as $relation) {
// resolve members if it wasn't done recently
$uid = $relation['uid'];
$force = empty($this->tag_rts[$uid]) || $this->tag_rts[$uid] <= time() - $delta;
if ($force) {
$config->resolve_members($relation, $force);
$this->tag_rts[$tag['uid']] = time();
}
$selected = !empty($tags) && in_array($relation['name'], $tags);
$found = !empty($relation['members']) && in_array($uri, $relation['members']);
$update = false;
// remove member from the relation
if ($found && !$selected) {
$relation['members'] = array_diff($relation['members'], (array) $uri);
$update = true;
}
// add member to the relation
else if (!$found && $selected) {
$relation['members'][] = $uri;
$update = true;
}
if ($update) {
$config->save($relation, 'relation');
}
$tags = array_diff($tags, (array) $relation['name']);
}
// create new relations
if (!empty($tags)) {
foreach ($tags as $tag) {
$relation = array(
'name' => $tag,
'members' => (array) $uri,
'category' => 'tag',
);
$config->save($relation, 'relation');
}
}
// make sure current folder is set correctly again
$this->storage->set_folder($folder);
}
public static function charset_to_cp($charset)
{
// @TODO: ?????
// The body is converted to utf-8 in get_part_body(), what about headers?
return 65001; // UTF-8
$aliases = array(
'asmo708' => 708,
'shiftjis' => 932,
'gb2312' => 936,
'ksc56011987' => 949,
'big5' => 950,
'utf16' => 1200,
'utf16le' => 1200,
'unicodefffe' => 1201,
'utf16be' => 1201,
'johab' => 1361,
'macintosh' => 10000,
'macjapanese' => 10001,
'macchinesetrad' => 10002,
'mackorean' => 10003,
'macarabic' => 10004,
'machebrew' => 10005,
'macgreek' => 10006,
'maccyrillic' => 10007,
'macchinesesimp' => 10008,
'macromanian' => 10010,
'macukrainian' => 10017,
'macthai' => 10021,
'macce' => 10029,
'macicelandic' => 10079,
'macturkish' => 10081,
'maccroatian' => 10082,
'utf32' => 12000,
'utf32be' => 12001,
'chinesecns' => 20000,
'chineseeten' => 20002,
'ia5' => 20105,
'ia5german' => 20106,
'ia5swedish' => 20107,
'ia5norwegian' => 20108,
'usascii' => 20127,
'ibm273' => 20273,
'ibm277' => 20277,
'ibm278' => 20278,
'ibm280' => 20280,
'ibm284' => 20284,
'ibm285' => 20285,
'ibm290' => 20290,
'ibm297' => 20297,
'ibm420' => 20420,
'ibm423' => 20423,
'ibm424' => 20424,
'ebcdickoreanextended' => 20833,
'ibmthai' => 20838,
'koi8r' => 20866,
'ibm871' => 20871,
'ibm880' => 20880,
'ibm905' => 20905,
'ibm00924' => 20924,
'cp1025' => 21025,
'koi8u' => 21866,
'iso88591' => 28591,
'iso88592' => 28592,
'iso88593' => 28593,
'iso88594' => 28594,
'iso88595' => 28595,
'iso88596' => 28596,
'iso88597' => 28597,
'iso88598' => 28598,
'iso88599' => 28599,
'iso885913' => 28603,
'iso885915' => 28605,
'xeuropa' => 29001,
'iso88598i' => 38598,
'iso2022jp' => 50220,
'csiso2022jp' => 50221,
'iso2022jp' => 50222,
'iso2022kr' => 50225,
'eucjp' => 51932,
'euccn' => 51936,
'euckr' => 51949,
'hzgb2312' => 52936,
'gb18030' => 54936,
'isciide' => 57002,
'isciibe' => 57003,
'isciita' => 57004,
'isciite' => 57005,
'isciias' => 57006,
'isciior' => 57007,
'isciika' => 57008,
'isciima' => 57009,
'isciigu' => 57010,
'isciipa' => 57011,
'utf7' => 65000,
'utf8' => 65001,
);
$charset = strtolower($charset);
$charset = preg_replace(array('/^x-/', '/[^a-z0-9]/'), '', $charset);
if (isset($aliases[$charset])) {
return $aliases[$charset];
}
if (preg_match('/^(ibm|dos|cp|windows|win)[0-9]+/', $charset, $m)) {
return substr($charset, strlen($m[1]) + 1);
}
}
/**
* Wrap text to a given number of characters per line
* but respect the mail quotation of replies messages (>).
* Finally add another quotation level by prepending the lines
* with >
*
* @param string $text Text to wrap
* @param int $length The line width
*
* @return string The wrapped text
*/
protected static function wrap_and_quote($text, $length = 72)
{
// Function stolen from Roundcube ;)
// Rebuild the message body with a maximum of $max chars, while keeping quoted message.
$max = min(77, $length + 8);
$lines = preg_split('/\r?\n/', trim($text));
$out = '';
foreach ($lines as $line) {
// don't wrap already quoted lines
if ($line[0] == '>') {
$line = '>' . rtrim($line);
}
else if (mb_strlen($line) > $max) {
$newline = '';
foreach (explode("\n", rcube_mime::wordwrap($line, $length - 2)) as $l) {
if (strlen($l)) {
$newline .= '> ' . $l . "\n";
}
else {
$newline .= ">\n";
}
}
$line = rtrim($newline);
}
else {
$line = '> ' . $line;
}
// Append the line
$out .= $line . "\n";
}
return $out;
}
}
diff --git a/lib/plugins/libkolab/composer.json b/lib/plugins/kolab_auth/composer.json
similarity index 71%
copy from lib/plugins/libkolab/composer.json
copy to lib/plugins/kolab_auth/composer.json
index 8926037..3e7012f 100644
--- a/lib/plugins/libkolab/composer.json
+++ b/lib/plugins/kolab_auth/composer.json
@@ -1,30 +1,30 @@
{
- "name": "kolab/libkolab",
+ "name": "kolab/kolab_auth",
"type": "roundcube-plugin",
- "description": "Plugin to setup a basic environment for the interaction with a Kolab server.",
+ "description": "Kolab authentication",
"homepage": "http://git.kolab.org/roundcubemail-plugins-kolab/",
"license": "AGPLv3",
- "version": "1.1.0",
+ "version": "3.2.2",
"authors": [
{
"name": "Thomas Bruederli",
"email": "bruederli@kolabsys.com",
"role": "Lead"
},
{
- "name": "Alensader Machniak",
+ "name": "Aleksander Machniak",
"email": "machniak@kolabsys.com",
- "role": "Developer"
+ "role": "Lead"
}
],
"repositories": [
{
"type": "composer",
"url": "http://plugins.roundcube.net"
}
],
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3"
}
}
diff --git a/lib/plugins/kolab_auth/config.inc.php.dist b/lib/plugins/kolab_auth/config.inc.php.dist
index 785fb78..8c01d56 100644
--- a/lib/plugins/kolab_auth/config.inc.php.dist
+++ b/lib/plugins/kolab_auth/config.inc.php.dist
@@ -1,84 +1,91 @@
<?php
// The id of the LDAP address book (which refers to the $rcmail_config['ldap_public'])
// or complete addressbook definition array.
// --------------------------------------------------------------------
// Note: Multi-domain (hosted) installations can resolve domain aliases
// by adding following settings in kolab_auth_addressbook spec.:
//
// 'domain_base_dn' => 'cn=kolab,cn=config',
// 'domain_filter' => '(&(objectclass=domainrelatedobject)(associateddomain=%s))',
// 'domain_name_attr' => 'associateddomain',
//
// With this %dc variable in base_dn and groups/base_dn will be
// replaced with DN string of resolved domain
//---------------------------------------------------------------------
-$rcmail_config['kolab_auth_addressbook'] = '';
+$config['kolab_auth_addressbook'] = '';
// This will overwrite defined filter
-$rcmail_config['kolab_auth_filter'] = '(&(objectClass=kolabInetOrgPerson)(|(uid=%u)(mail=%fu)(alias=%fu)))';
+$config['kolab_auth_filter'] = '(&(objectClass=kolabInetOrgPerson)(|(uid=%u)(mail=%fu)(alias=%fu)))';
-// Use this fields (from fieldmap configuration) to get authentication ID
-$rcmail_config['kolab_auth_login'] = 'email';
+// Use this field (from fieldmap configuration) to get authentication ID. Don't use an array here!
+$config['kolab_auth_login'] = 'email';
-// Use this fields (from fieldmap configuration) for default identity.
+// Use these fields (from fieldmap configuration) for default identity.
// If the value array contains more than one field, first non-empty will be used
// Note: These aren't LDAP attributes, but field names in config
// Note: If there's more than one email address, as many identities will be created
-$rcmail_config['kolab_auth_name'] = array('name', 'cn');
-$rcmail_config['kolab_auth_email'] = array('email');
-$rcmail_config['kolab_auth_organization'] = array('organization');
+$config['kolab_auth_name'] = array('name', 'cn');
+$config['kolab_auth_email'] = array('email');
+$config['kolab_auth_organization'] = array('organization');
+
+// Role field (from fieldmap configuration)
+$config['kolab_auth_role'] = 'role';
// Template for user names displayed in the UI.
// You can use all attributes from the 'fieldmap' property of the 'kolab_auth_addressbook' configuration
-$rcmail_config['kolab_auth_user_displayname'] = '{name} ({ou})';
+$config['kolab_auth_user_displayname'] = '{name} ({ou})';
// Login and password of the admin user. Enables "Login As" feature.
-$rcmail_config['kolab_auth_admin_login'] = '';
-$rcmail_config['kolab_auth_admin_password'] = '';
+$config['kolab_auth_admin_login'] = '';
+$config['kolab_auth_admin_password'] = '';
// Enable audit logging for abuse of administrative privileges.
-$rcmail_config['kolab_auth_auditlog'] = false;
-
-// Role field (from fieldmap configuration)
-$rcmail_config['kolab_auth_role'] = 'role';
-// The required value for the role attribute to contain should the user be allowed
-// to login as another user.
-$rcmail_config['kolab_auth_role_value'] = '';
+$config['kolab_auth_auditlog'] = false;
-// Administrative group name to which user must be assigned to
-// which adds privilege to login as another user.
-$rcmail_config['kolab_auth_group'] = '';
+// As set of rules to define the required rights on the target entry
+// which allow an admin user to login as another user (the target).
+// The effective rights value refers to either entry level attribute level rights:
+// * entry:[read|add|delete]
+// * attrib:<attribute-name>:[read|write|delete]
+$config['kolab_auth_admin_rights'] = array(
+ // Roundcube task => required effective right
+ 'settings' => 'entry:read',
+ 'mail' => 'entry:delete',
+ 'addressbook' => 'entry:delete',
+ // or use a wildcard entry like this:
+ '*' => 'entry:read',
+);
// Enable plugins on a role-by-role basis. In this example, the 'acl' plugin
// is enabled for people with a 'cn=professional-user,dc=mykolab,dc=ch' role.
//
// Note that this does NOT mean the 'acl' plugin is disabled for other people.
-$rcmail_config['kolab_auth_role_plugins'] = Array(
+$config['kolab_auth_role_plugins'] = Array(
'cn=professional-user,dc=mykolab,dc=ch' => Array(
'acl',
),
);
// Settings on a role-by-role basis. In this example, the 'htmleditor' setting
// is enabled(1) for people with a 'cn=professional-user,dc=mykolab,dc=ch' role,
// and it cannot be overridden. Sample use-case: disable htmleditor for normal people,
// do not allow the setting to be controlled through the preferences, enable the
// html editor for professional users and allow them to override the setting in
// the preferences.
-$rcmail_config['kolab_auth_role_settings'] = Array(
+$config['kolab_auth_role_settings'] = Array(
'cn=professional-user,dc=mykolab,dc=ch' => Array(
'htmleditor' => Array(
'mode' => 'override',
'value' => 1,
'allow_override' => true
),
),
);
// List of LDAP addressbooks (keys of ldap_public configuration array)
// for which base_dn variables (%dc, etc.) will be replaced according to authenticated user DN
// Note: special name '*' for all LDAP addressbooks
-$rcmail_config['kolab_auth_ldap_addressbooks'] = array('*');
+$config['kolab_auth_ldap_addressbooks'] = array('*');
?>
diff --git a/lib/plugins/kolab_auth/kolab_auth.php b/lib/plugins/kolab_auth/kolab_auth.php
index 2b685a7..033d5b1 100644
--- a/lib/plugins/kolab_auth/kolab_auth.php
+++ b/lib/plugins/kolab_auth/kolab_auth.php
@@ -1,680 +1,788 @@
<?php
/**
* Kolab Authentication (based on ldap_authentication plugin)
*
* Authenticates on LDAP server, finds canonized authentication ID for IMAP
* and for new users creates identity based on LDAP information.
*
* Supports impersonate feature (login as another user). To use this feature
* imap_auth_type/smtp_auth_type must be set to DIGEST-MD5 or PLAIN.
*
* @version @package_version@
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2011-2013, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_auth extends rcube_plugin
{
static $ldap;
private $username;
private $data = array();
public function init()
{
$rcmail = rcube::get_instance();
$this->load_config();
$this->add_hook('authenticate', array($this, 'authenticate'));
$this->add_hook('startup', array($this, 'startup'));
$this->add_hook('user_create', array($this, 'user_create'));
// Hook for password change
$this->add_hook('password_ldap_bind', array($this, 'password_ldap_bind'));
// Hooks related to "Login As" feature
$this->add_hook('template_object_loginform', array($this, 'login_form'));
$this->add_hook('storage_connect', array($this, 'imap_connect'));
$this->add_hook('managesieve_connect', array($this, 'imap_connect'));
$this->add_hook('smtp_connect', array($this, 'smtp_connect'));
$this->add_hook('identity_form', array($this, 'identity_form'));
// Hook to modify some configuration, e.g. ldap
$this->add_hook('config_get', array($this, 'config_get'));
// Hook to modify logging directory
$this->add_hook('write_log', array($this, 'write_log'));
$this->username = $_SESSION['username'];
// Enable debug logs (per-user), when logged as another user
if (!empty($_SESSION['kolab_auth_admin']) && $rcmail->config->get('kolab_auth_auditlog')) {
$rcmail->config->set('debug_level', 1);
$rcmail->config->set('devel_mode', true);
$rcmail->config->set('smtp_log', true);
$rcmail->config->set('log_logins', true);
$rcmail->config->set('log_session', true);
$rcmail->config->set('memcache_debug', true);
$rcmail->config->set('imap_debug', true);
$rcmail->config->set('ldap_debug', true);
$rcmail->config->set('smtp_debug', true);
$rcmail->config->set('sql_debug', true);
// SQL debug need to be set directly on DB object
// setting config variable will not work here because
// the object is already initialized/configured
if ($db = $rcmail->get_dbh()) {
$db->set_debug(true);
}
}
}
+ /**
+ * Startup hook handler
+ */
public function startup($args)
{
+ // Check access rights when logged in as another user
+ if (!empty($_SESSION['kolab_auth_admin']) && $args['task'] != 'login' && $args['task'] != 'logout') {
+ // access to specified task is forbidden,
+ // redirect to the first task on the list
+ if (!empty($_SESSION['kolab_auth_allowed_tasks'])) {
+ $tasks = (array)$_SESSION['kolab_auth_allowed_tasks'];
+ if (!in_array($args['task'], $tasks) && !in_array('*', $tasks)) {
+ header('Location: ?_task=' . array_shift($tasks));
+ die;
+ }
+
+ // add script that will remove disabled taskbar buttons
+ if (!in_array('*', $tasks)) {
+ $this->add_hook('render_page', array($this, 'render_page'));
+ }
+ }
+ }
+
+ // load per-user settings
$this->load_user_role_plugins_and_settings();
return $args;
}
/**
* Modify some configuration according to LDAP user record
*/
public function config_get($args)
{
// Replaces ldap_vars (%dc, etc) in public kolab ldap addressbooks
// config based on the users base_dn. (for multi domain support)
if ($args['name'] == 'ldap_public' && !empty($args['result'])) {
$rcmail = rcube::get_instance();
$kolab_books = (array) $rcmail->config->get('kolab_auth_ldap_addressbooks');
foreach ($args['result'] as $name => $config) {
if (in_array($name, $kolab_books) || in_array('*', $kolab_books)) {
- $args['result'][$name]['base_dn'] = self::parse_ldap_vars($config['base_dn']);
- $args['result'][$name]['search_base_dn'] = self::parse_ldap_vars($config['search_base_dn']);
- $args['result'][$name]['bind_dn'] = str_replace('%dn', $_SESSION['kolab_dn'], $config['bind_dn']);
-
- if (!empty($config['groups'])) {
- $args['result'][$name]['groups']['base_dn'] = self::parse_ldap_vars($config['groups']['base_dn']);
- }
+ $args['result'][$name] = $this->patch_ldap_config($config);
}
}
}
+ else if ($args['name'] == 'kolab_users_directory' && !empty($args['result'])) {
+ $args['result'] = $this->patch_ldap_config($args['result']);
+ }
return $args;
}
+ /**
+ * Helper method to patch the given LDAP directory config with user-specific values
+ */
+ protected function patch_ldap_config($config)
+ {
+ if (is_array($config)) {
+ $config['base_dn'] = self::parse_ldap_vars($config['base_dn']);
+ $config['search_base_dn'] = self::parse_ldap_vars($config['search_base_dn']);
+ $config['bind_dn'] = str_replace('%dn', $_SESSION['kolab_dn'], $config['bind_dn']);
+
+ if (!empty($config['groups'])) {
+ $config['groups']['base_dn'] = self::parse_ldap_vars($config['groups']['base_dn']);
+ }
+ }
+
+ return $config;
+ }
+
/**
* Modifies list of plugins and settings according to
* specified LDAP roles
*/
public function load_user_role_plugins_and_settings()
{
if (empty($_SESSION['user_roledns'])) {
return;
}
$rcmail = rcube::get_instance();
// Example 'kolab_auth_role_plugins' =
//
// Array(
// '<role_dn>' => Array('plugin1', 'plugin2'),
// );
//
// NOTE that <role_dn> may in fact be something like: 'cn=role,%dc'
$role_plugins = $rcmail->config->get('kolab_auth_role_plugins');
// Example $rcmail_config['kolab_auth_role_settings'] =
//
// Array(
// '<role_dn>' => Array(
// '$setting' => Array(
// 'mode' => '(override|merge)', (default: override)
// 'value' => <>,
// 'allow_override' => (true|false) (default: false)
// ),
// ),
// );
//
// NOTE that <role_dn> may in fact be something like: 'cn=role,%dc'
$role_settings = $rcmail->config->get('kolab_auth_role_settings');
if (!empty($role_plugins)) {
foreach ($role_plugins as $role_dn => $plugins) {
$role_dn = self::parse_ldap_vars($role_dn);
if (!empty($role_plugins[$role_dn])) {
$role_plugins[$role_dn] = array_unique(array_merge((array)$role_plugins[$role_dn], $plugins));
} else {
$role_plugins[$role_dn] = $plugins;
}
}
}
if (!empty($role_settings)) {
foreach ($role_settings as $role_dn => $settings) {
$role_dn = self::parse_ldap_vars($role_dn);
if (!empty($role_settings[$role_dn])) {
$role_settings[$role_dn] = array_merge((array)$role_settings[$role_dn], $settings);
} else {
$role_settings[$role_dn] = $settings;
}
}
}
foreach ($_SESSION['user_roledns'] as $role_dn) {
if (!empty($role_settings[$role_dn]) && is_array($role_settings[$role_dn])) {
foreach ($role_settings[$role_dn] as $setting_name => $setting) {
if (!isset($setting['mode'])) {
$setting['mode'] = 'override';
}
if ($setting['mode'] == "override") {
$rcmail->config->set($setting_name, $setting['value']);
} elseif ($setting['mode'] == "merge") {
$orig_setting = $rcmail->config->get($setting_name);
if (!empty($orig_setting)) {
if (is_array($orig_setting)) {
$rcmail->config->set($setting_name, array_merge($orig_setting, $setting['value']));
}
} else {
$rcmail->config->set($setting_name, $setting['value']);
}
}
$dont_override = (array) $rcmail->config->get('dont_override');
if (empty($setting['allow_override'])) {
$rcmail->config->set('dont_override', array_merge($dont_override, array($setting_name)));
}
else {
if (in_array($setting_name, $dont_override)) {
$_dont_override = array();
foreach ($dont_override as $_setting) {
if ($_setting != $setting_name) {
$_dont_override[] = $_setting;
}
}
$rcmail->config->set('dont_override', $_dont_override);
}
}
if ($setting_name == 'skin') {
if ($rcmail->output->type == 'html') {
$rcmail->output->set_skin($setting['value']);
$rcmail->output->set_env('skin', $setting['value']);
}
}
}
}
if (!empty($role_plugins[$role_dn])) {
foreach ((array)$role_plugins[$role_dn] as $plugin) {
$this->api->load_plugin($plugin);
}
}
}
}
/**
* Logging method replacement to print debug/errors into
* a separate (sub)folder for each user
*/
public function write_log($args)
{
$rcmail = rcube::get_instance();
if ($rcmail->config->get('log_driver') == 'syslog') {
return $args;
}
// log_driver == 'file' is assumed here
$log_dir = $rcmail->config->get('log_dir', RCUBE_INSTALL_PATH . 'logs');
// Append original username + target username for audit-logging
if ($rcmail->config->get('kolab_auth_auditlog') && !empty($_SESSION['kolab_auth_admin'])) {
$args['dir'] = $log_dir . '/' . strtolower($_SESSION['kolab_auth_admin']) . '/' . strtolower($this->username);
// Attempt to create the directory
if (!is_dir($args['dir'])) {
@mkdir($args['dir'], 0750, true);
}
}
// Define the user log directory if a username is provided
else if ($rcmail->config->get('per_user_logging') && !empty($this->username)) {
$user_log_dir = $log_dir . '/' . strtolower($this->username);
if (is_writable($user_log_dir)) {
$args['dir'] = $user_log_dir;
}
else if ($args['name'] != 'errors') {
$args['abort'] = true; // don't log if unauthenticed
}
}
return $args;
}
/**
* Sets defaults for new user.
*/
public function user_create($args)
{
if (!empty($this->data['user_email'])) {
// addresses list is supported
if (array_key_exists('email_list', $args)) {
$email_list = array_unique($this->data['user_email']);
// add organization to the list
if (!empty($this->data['user_organization'])) {
foreach ($email_list as $idx => $email) {
$email_list[$idx] = array(
'organization' => $this->data['user_organization'],
'email' => $email,
);
}
}
$args['email_list'] = $email_list;
}
else {
$args['user_email'] = $this->data['user_email'][0];
}
}
if (!empty($this->data['user_name'])) {
$args['user_name'] = $this->data['user_name'];
}
return $args;
}
/**
* Modifies login form adding additional "Login As" field
*/
public function login_form($args)
{
$this->add_texts('localization/');
$rcmail = rcube::get_instance();
$admin_login = $rcmail->config->get('kolab_auth_admin_login');
$group = $rcmail->config->get('kolab_auth_group');
$role_attr = $rcmail->config->get('kolab_auth_role');
// Show "Login As" input
if (empty($admin_login) || (empty($group) && empty($role_attr))) {
return $args;
}
$input = new html_inputfield(array('name' => '_loginas', 'id' => 'rcmloginas',
'type' => 'text', 'autocomplete' => 'off'));
$row = html::tag('tr', null,
html::tag('td', 'title', html::label('rcmloginas', Q($this->gettext('loginas'))))
. html::tag('td', 'input', $input->show(trim(rcube_utils::get_input_value('_loginas', rcube_utils::INPUT_POST))))
);
$args['content'] = preg_replace('/<\/tbody>/i', $row . '</tbody>', $args['content']);
return $args;
}
/**
* Find user credentials In LDAP.
*/
public function authenticate($args)
{
// get username and host
$host = $args['host'];
$user = $args['user'];
$pass = $args['pass'];
$loginas = trim(rcube_utils::get_input_value('_loginas', rcube_utils::INPUT_POST));
if (empty($user) || empty($pass)) {
$args['abort'] = true;
return $args;
}
// temporarily set the current username to the one submitted
$this->username = $user;
$ldap = self::ldap();
if (!$ldap || !$ldap->ready) {
$args['abort'] = true;
$args['kolab_ldap_error'] = true;
$message = sprintf(
'Login failure for user %s from %s in session %s (error %s)',
$user,
rcube_utils::remote_ip(),
session_id(),
"LDAP not ready"
);
rcube::write_log('userlogins', $message);
return $args;
}
// Find user record in LDAP
$record = $ldap->get_user_record($user, $host);
if (empty($record)) {
$args['abort'] = true;
$message = sprintf(
'Login failure for user %s from %s in session %s (error %s)',
$user,
rcube_utils::remote_ip(),
session_id(),
"No user record found"
);
rcube::write_log('userlogins', $message);
return $args;
}
$rcmail = rcube::get_instance();
$admin_login = $rcmail->config->get('kolab_auth_admin_login');
$admin_pass = $rcmail->config->get('kolab_auth_admin_password');
$login_attr = $rcmail->config->get('kolab_auth_login');
$name_attr = $rcmail->config->get('kolab_auth_name');
$email_attr = $rcmail->config->get('kolab_auth_email');
$org_attr = $rcmail->config->get('kolab_auth_organization');
$role_attr = $rcmail->config->get('kolab_auth_role');
$imap_attr = $rcmail->config->get('kolab_auth_mailhost');
if (!empty($role_attr) && !empty($record[$role_attr])) {
$_SESSION['user_roledns'] = (array)($record[$role_attr]);
}
if (!empty($imap_attr) && !empty($record[$imap_attr])) {
$default_host = $rcmail->config->get('default_host');
if (!empty($default_host)) {
rcube::write_log("errors", "Both default host and kolab_auth_mailhost set. Incompatible.");
} else {
$args['host'] = "tls://" . $record[$imap_attr];
}
}
// Login As...
if (!empty($loginas) && $admin_login) {
// Authenticate to LDAP
$result = $ldap->bind($record['dn'], $pass);
if (!$result) {
$args['abort'] = true;
$message = sprintf(
'Login failure for user %s from %s in session %s (error %s)',
$user,
rcube_utils::remote_ip(),
session_id(),
"Unable to bind with '" . $record['dn'] . "'"
);
rcube::write_log('userlogins', $message);
return $args;
}
- // check if the original user has/belongs to administrative role/group
$isadmin = false;
- $group = $rcmail->config->get('kolab_auth_group');
- $role_dn = $rcmail->config->get('kolab_auth_role_value');
-
- // check role attribute
- if (!empty($role_attr) && !empty($role_dn) && !empty($record[$role_attr])) {
- $role_dn = $ldap->parse_vars($role_dn, $user, $host);
- if (in_array($role_dn, (array)$record[$role_attr])) {
- $isadmin = true;
+ $admin_rights = $rcmail->config->get('kolab_auth_admin_rights', array());
+
+ // @deprecated: fall-back to the old check if the original user has/belongs to administrative role/group
+ if (empty($admin_rights)) {
+ $group = $rcmail->config->get('kolab_auth_group');
+ $role_dn = $rcmail->config->get('kolab_auth_role_value');
+
+ // check role attribute
+ if (!empty($role_attr) && !empty($role_dn) && !empty($record[$role_attr])) {
+ $role_dn = $ldap->parse_vars($role_dn, $user, $host);
+ if (in_array($role_dn, (array)$record[$role_attr])) {
+ $isadmin = true;
+ }
+ }
+
+ // check group
+ if (!$isadmin && !empty($group)) {
+ $groups = $ldap->get_user_groups($record['dn'], $user, $host);
+ if (in_array($group, $groups)) {
+ $isadmin = true;
+ }
+ }
+
+ if ($isadmin) {
+ // user has admin privileges privilage, get "login as" user credentials
+ $target_entry = $ldap->get_user_record($loginas, $host);
+ $allowed_tasks = $rcmail->config->get('kolab_auth_allowed_tasks');
}
}
+ else {
+ // get "login as" user credentials
+ $target_entry = $ldap->get_user_record($loginas, $host);
+
+ if (!empty($target_entry)) {
+ // get effective rights to determine login-as permissions
+ $effective_rights = (array)$ldap->effective_rights($target_entry['dn']);
+
+ if (!empty($effective_rights)) {
+ $effective_rights['attrib'] = $effective_rights['attributeLevelRights'];
+ $effective_rights['entry'] = $effective_rights['entryLevelRights'];
+
+ // compare the rights with the permissions mapping
+ $allowed_tasks = array();
+ foreach ($admin_rights as $task => $perms) {
+ $perms_ = explode(':', $perms);
+ $type = array_shift($perms_);
+ $req = array_pop($perms_);
+ $attrib = array_pop($perms_);
+
+ if (array_key_exists($type, $effective_rights)) {
+ if ($type == 'entry' && in_array($req, $effective_rights[$type])) {
+ $allowed_tasks[] = $task;
+ }
+ else if ($type == 'attrib' && array_key_exists($attrib, $effective_rights[$type]) &&
+ in_array($req, $effective_rights[$type][$attrib])) {
+ $allowed_tasks[] = $task;
+ }
+ }
+ }
- // check group
- if (!$isadmin && !empty($group)) {
- $groups = $ldap->get_user_groups($record['dn'], $user, $host);
- if (in_array($group, $groups)) {
- $isadmin = true;
+ $isadmin = !empty($allowed_tasks);
+ }
}
}
// Save original user login for log (see below)
if ($login_attr) {
$origname = is_array($record[$login_attr]) ? $record[$login_attr][0] : $record[$login_attr];
}
else {
$origname = $user;
}
- $record = null;
-
- // user has the privilage, get "login as" user credentials
- if ($isadmin) {
- $record = $ldap->get_user_record($loginas, $host);
- }
+ if (!$isadmin || empty($target_entry)) {
+ $this->add_texts('localization/');
- if (empty($record)) {
$args['abort'] = true;
+ $args['error'] = $this->gettext(array(
+ 'name' => 'loginasnotallowed',
+ 'vars' => array('user' => Q($loginas)),
+ ));
+
$message = sprintf(
'Login failure for user %s (as user %s) from %s in session %s (error %s)',
$user,
$loginas,
rcube_utils::remote_ip(),
session_id(),
- "No user record found for '" . $loginas . "'"
+ "No privileges to login as '" . $loginas . "'"
);
rcube::write_log('userlogins', $message);
return $args;
}
+ // replace $record with target entry
+ $record = $target_entry;
+
$args['user'] = $this->username = $loginas;
// Mark session to use SASL proxy for IMAP authentication
$_SESSION['kolab_auth_admin'] = strtolower($origname);
$_SESSION['kolab_auth_login'] = $rcmail->encrypt($admin_login);
$_SESSION['kolab_auth_password'] = $rcmail->encrypt($admin_pass);
+ $_SESSION['kolab_auth_allowed_tasks'] = $allowed_tasks;
}
// Store UID and DN of logged user in session for use by other plugins
$_SESSION['kolab_uid'] = is_array($record['uid']) ? $record['uid'][0] : $record['uid'];
$_SESSION['kolab_dn'] = $record['dn'];
// Store LDAP replacement variables used for current user
// This improves performance of load_user_role_plugins_and_settings()
// which is executed on every request (via startup hook) and where
// we don't like to use LDAP (connection + bind + search)
$_SESSION['kolab_auth_vars'] = $ldap->get_parse_vars();
// Set user login
if ($login_attr) {
$this->data['user_login'] = is_array($record[$login_attr]) ? $record[$login_attr][0] : $record[$login_attr];
}
if ($this->data['user_login']) {
$args['user'] = $this->username = $this->data['user_login'];
}
// User name for identity (first log in)
foreach ((array)$name_attr as $field) {
$name = is_array($record[$field]) ? $record[$field][0] : $record[$field];
if (!empty($name)) {
$this->data['user_name'] = $name;
break;
}
}
// User email(s) for identity (first log in)
foreach ((array)$email_attr as $field) {
$email = is_array($record[$field]) ? array_filter($record[$field]) : $record[$field];
if (!empty($email)) {
$this->data['user_email'] = array_merge((array)$this->data['user_email'], (array)$email);
}
}
// Organization name for identity (first log in)
foreach ((array)$org_attr as $field) {
$organization = is_array($record[$field]) ? $record[$field][0] : $record[$field];
if (!empty($organization)) {
$this->data['user_organization'] = $organization;
break;
}
}
// Log "Login As" usage
if (!empty($origname)) {
rcube::write_log('userlogins', sprintf('Admin login for %s by %s from %s',
$args['user'], $origname, rcube_utils::remote_ip()));
}
// load per-user settings/plugins
$this->load_user_role_plugins_and_settings();
return $args;
}
/**
* Set user DN for password change (password plugin with ldap_simple driver)
*/
public function password_ldap_bind($args)
{
$args['user_dn'] = $_SESSION['kolab_dn'];
$rcmail = rcube::get_instance();
$rcmail->config->set('password_ldap_method', 'user');
return $args;
}
/**
* Sets SASL Proxy login/password for IMAP and Managesieve auth
*/
public function imap_connect($args)
{
if (!empty($_SESSION['kolab_auth_admin'])) {
$rcmail = rcube::get_instance();
$admin_login = $rcmail->decrypt($_SESSION['kolab_auth_login']);
$admin_pass = $rcmail->decrypt($_SESSION['kolab_auth_password']);
$args['auth_cid'] = $admin_login;
$args['auth_pw'] = $admin_pass;
}
return $args;
}
/**
* Sets SASL Proxy login/password for SMTP auth
*/
public function smtp_connect($args)
{
if (!empty($_SESSION['kolab_auth_admin'])) {
$rcmail = rcube::get_instance();
$admin_login = $rcmail->decrypt($_SESSION['kolab_auth_login']);
$admin_pass = $rcmail->decrypt($_SESSION['kolab_auth_password']);
$args['smtp_auth_cid'] = $admin_login;
$args['smtp_auth_pw'] = $admin_pass;
}
return $args;
}
/**
* Hook to replace the plain text input field for email address by a drop-down list
* with all email addresses (including aliases) from this user's LDAP record.
*/
public function identity_form($args)
{
$rcmail = rcube::get_instance();
$ident_level = intval($rcmail->config->get('identities_level', 0));
// do nothing if email address modification is disabled
if ($ident_level == 1 || $ident_level == 3) {
return $args;
}
$ldap = self::ldap();
if (!$ldap || !$ldap->ready || empty($_SESSION['kolab_dn'])) {
return $args;
}
$emails = array();
$user_record = $ldap->get_record($_SESSION['kolab_dn']);
foreach ((array)$rcmail->config->get('kolab_auth_email', array()) as $col) {
$values = rcube_addressbook::get_col_values($col, $user_record, true);
if (!empty($values))
$emails = array_merge($emails, array_filter($values));
}
// kolab_delegation might want to modify this addresses list
$plugin = $rcmail->plugins->exec_hook('kolab_auth_emails', array('emails' => $emails));
$emails = $plugin['emails'];
if (!empty($emails)) {
$args['form']['addressing']['content']['email'] = array(
'type' => 'select',
'options' => array_combine($emails, $emails),
);
}
return $args;
}
+ /**
+ * Action executed before the page is rendered to add an onload script
+ * that will remove all taskbar buttons for disabled tasks
+ */
+ public function render_page($args)
+ {
+ $rcmail = rcube::get_instance();
+ $tasks = (array)$_SESSION['kolab_auth_allowed_tasks'];
+ $tasks[] = 'logout';
+
+ // disable buttons in taskbar
+ $script = "
+ \$('a').filter(function() {
+ var ev = \$(this).attr('onclick');
+ return ev && ev.match(/'switch-task','([a-z]+)'/)
+ && \$.inArray(RegExp.\$1, " . json_encode($tasks) . ") < 0;
+ }).remove();
+ ";
+
+ $rcmail->output->add_script($script, 'docready');
+ }
+
/**
* Initializes LDAP object and connects to LDAP server
*/
public static function ldap()
{
if (self::$ldap) {
return self::$ldap;
}
$rcmail = rcube::get_instance();
$addressbook = $rcmail->config->get('kolab_auth_addressbook');
if (!is_array($addressbook)) {
$ldap_config = (array)$rcmail->config->get('ldap_public');
$addressbook = $ldap_config[$addressbook];
}
if (empty($addressbook)) {
return null;
}
require_once __DIR__ . '/kolab_auth_ldap.php';
self::$ldap = new kolab_auth_ldap($addressbook);
return self::$ldap;
}
/**
* Parses LDAP DN string with replacing supported variables.
* See kolab_auth_ldap::parse_vars()
*
* @param string $str LDAP DN string
*
* @return string Parsed DN string
*/
public static function parse_ldap_vars($str)
{
if (!empty($_SESSION['kolab_auth_vars'])) {
$str = strtr($str, $_SESSION['kolab_auth_vars']);
}
return $str;
}
}
diff --git a/lib/plugins/kolab_auth/kolab_auth_ldap.php b/lib/plugins/kolab_auth/kolab_auth_ldap.php
index 303bbf3..431133b 100644
--- a/lib/plugins/kolab_auth/kolab_auth_ldap.php
+++ b/lib/plugins/kolab_auth/kolab_auth_ldap.php
@@ -1,550 +1,548 @@
<?php
/**
* Kolab Authentication
*
* @version @package_version@
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2011-2013, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Wrapper class for rcube_ldap_generic
*/
class kolab_auth_ldap extends rcube_ldap_generic
{
private $icache = array();
private $conf = array();
private $fieldmap = array();
function __construct($p)
{
$rcmail = rcube::get_instance();
$this->conf = $p;
$this->conf['kolab_auth_user_displayname'] = $rcmail->config->get('kolab_auth_user_displayname', '{name}');
$this->fieldmap = $p['fieldmap'];
$this->fieldmap['uid'] = 'uid';
$p['attributes'] = array_values($this->fieldmap);
$p['debug'] = (bool) $rcmail->config->get('ldap_debug');
// Connect to the server (with bind)
parent::__construct($p);
$this->_connect();
$rcmail->add_shutdown_function(array($this, 'close'));
}
/**
* Establish a connection to the LDAP server
*/
private function _connect()
{
- $rcube = rcube::get_instance();
-
// try to connect + bind for every host configured
// with OpenLDAP 2.x ldap_connect() always succeeds but ldap_bind will fail if host isn't reachable
// see http://www.php.net/manual/en/function.ldap-connect.php
foreach ((array)$this->config['hosts'] as $host) {
// skip host if connection failed
if (!$this->connect($host)) {
continue;
}
$bind_pass = $this->config['bind_pass'];
$bind_user = $this->config['bind_user'];
$bind_dn = $this->config['bind_dn'];
if (empty($bind_pass)) {
$this->ready = true;
}
else {
if (!empty($bind_dn)) {
$this->ready = $this->bind($bind_dn, $bind_pass);
}
else if (!empty($this->config['auth_cid'])) {
$this->ready = $this->sasl_bind($this->config['auth_cid'], $bind_pass, $bind_user);
}
else {
$this->ready = $this->sasl_bind($bind_user, $bind_pass);
}
}
// connection established, we're done here
if ($this->ready) {
break;
}
} // end foreach hosts
if (!is_resource($this->conn)) {
rcube::raise_error(array('code' => 100, 'type' => 'ldap',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not connect to any LDAP server, last tried $host"), true);
$this->ready = false;
}
return $this->ready;
}
/**
* Fetches user data from LDAP addressbook
*/
function get_user_record($user, $host)
{
$rcmail = rcube::get_instance();
$filter = $rcmail->config->get('kolab_auth_filter');
$filter = $this->parse_vars($filter, $user, $host);
$base_dn = $this->parse_vars($this->config['base_dn'], $user, $host);
$scope = $this->config['scope'];
// @TODO: print error if filter is empty
// get record
if ($result = parent::search($base_dn, $filter, $scope, $this->attributes)) {
if ($result->count() == 1) {
$entries = $result->entries(true);
$dn = key($entries);
$entry = array_pop($entries);
$entry = $this->field_mapping($dn, $entry);
return $entry;
}
}
}
/**
* Fetches user data from LDAP addressbook
*/
function get_user_groups($dn, $user, $host)
{
if (empty($dn) || empty($this->config['groups'])) {
return array();
}
$base_dn = $this->parse_vars($this->config['groups']['base_dn'], $user, $host);
$name_attr = $this->config['groups']['name_attr'] ? $this->config['groups']['name_attr'] : 'cn';
$member_attr = $this->get_group_member_attr();
$filter = "(member=$dn)(uniqueMember=$dn)";
if ($member_attr != 'member' && $member_attr != 'uniqueMember')
$filter .= "($member_attr=$dn)";
$filter = strtr("(|$filter)", array("\\" => "\\\\"));
$result = parent::search($base_dn, $filter, 'sub', array('dn', $name_attr));
if (!$result) {
return array();
}
$groups = array();
foreach ($result as $entry) {
+ $dn = $entry['dn'];
$entry = rcube_ldap_generic::normalize_entry($entry);
- if (!$entry['dn']) {
- $entry['dn'] = key($result->entries(true));
- }
- $groups[$entry['dn']] = $entry[$name_attr];
+
+ $groups[$dn] = $entry[$name_attr];
}
return $groups;
}
/**
* Get a specific LDAP record
*
* @param string DN
*
* @return array Record data
*/
function get_record($dn)
{
if (!$this->ready) {
return;
}
if ($rec = $this->get_entry($dn)) {
$rec = rcube_ldap_generic::normalize_entry($rec);
$rec = $this->field_mapping($dn, $rec);
}
return $rec;
}
/**
* Replace LDAP record data items
*
* @param string $dn DN
* @param array $entry LDAP entry
*
* return bool True on success, False on failure
*/
function replace($dn, $entry)
{
// fields mapping
foreach ($this->fieldmap as $field => $attr) {
if (array_key_exists($field, $entry)) {
$entry[$attr] = $entry[$field];
if ($attr != $field) {
unset($entry[$field]);
}
}
}
return $this->mod_replace($dn, $entry);
}
/**
* Search records (simplified version of rcube_ldap::search)
*
* @param mixed $fields The field name or array of field names to search in
* @param mixed $value Search value (or array of values when $fields is array)
* @param int $mode Matching mode:
* 0 - partial (*abc*),
* 1 - strict (=),
* 2 - prefix (abc*)
* @param array $required List of fields that cannot be empty
* @param int $limit Number of records
* @param int $count Returns the number of records found
*
* @return array List or false on error
*/
function dosearch($fields, $value, $mode=1, $required = array(), $limit = 0, &$count = 0)
{
if (empty($fields)) {
return array();
}
$mode = intval($mode);
// use AND operator for advanced searches
$filter = is_array($value) ? '(&' : '(|';
// set wildcards
$wp = $ws = '';
if (!empty($this->config['fuzzy_search']) && $mode != 1) {
$ws = '*';
if (!$mode) {
$wp = '*';
}
}
foreach ((array)$fields as $idx => $field) {
$val = is_array($value) ? $value[$idx] : $value;
$attrs = (array) $this->fieldmap[$field];
if (empty($attrs)) {
$filter .= "($field=$wp" . rcube_ldap_generic::quote_string($val) . "$ws)";
}
else {
if (count($attrs) > 1)
$filter .= '(|';
foreach ($attrs as $f)
$filter .= "($f=$wp" . rcube_ldap_generic::quote_string($val) . "$ws)";
if (count($attrs) > 1)
$filter .= ')';
}
}
$filter .= ')';
// add required (non empty) fields filter
$req_filter = '';
foreach ((array)$required as $field) {
if (in_array($field, (array)$fields)) // required field is already in search filter
continue;
$attrs = (array) $this->fieldmap[$field];
if (empty($attrs)) {
$req_filter .= "($field=*)";
}
else {
if (count($attrs) > 1)
$req_filter .= '(|';
foreach ($attrs as $f)
$req_filter .= "($f=*)";
if (count($attrs) > 1)
$req_filter .= ')';
}
}
if (!empty($req_filter)) {
$filter = '(&' . $req_filter . $filter . ')';
}
// avoid double-wildcard if $value is empty
$filter = preg_replace('/\*+/', '*', $filter);
// add general filter to query
if (!empty($this->config['filter'])) {
$filter = '(&(' . preg_replace('/^\(|\)$/', '', $this->config['filter']) . ')' . $filter . ')';
}
$base_dn = $this->parse_vars($this->config['base_dn']);
$scope = $this->config['scope'];
$attrs = array_values($this->fieldmap);
$list = array();
if ($result = $this->search($base_dn, $filter, $scope, $attrs)) {
$count = $result->count();
$i = 0;
foreach ($result as $entry) {
if ($limit && $limit <= $i) {
break;
}
- $dn = key($result->entries(true));
+
+ $dn = $entry['dn'];
$entry = rcube_ldap_generic::normalize_entry($entry);
$list[$dn] = $this->field_mapping($dn, $entry);
$i++;
}
}
return $list;
}
/**
* Set filter used in search()
*/
function set_filter($filter)
{
$this->config['filter'] = $filter;
}
/**
* Maps LDAP attributes to defined fields
*/
protected function field_mapping($dn, $entry)
{
$entry['dn'] = $dn;
// fields mapping
foreach ($this->fieldmap as $field => $attr) {
// $entry might be indexed by lower-case attribute names
$attr_lc = strtolower($attr);
if (isset($entry[$attr_lc])) {
$entry[$field] = $entry[$attr_lc];
}
else if (isset($entry[$attr])) {
$entry[$field] = $entry[$attr];
}
}
// compose display name according to config
if (empty($this->fieldmap['displayname'])) {
$entry['displayname'] = rcube_addressbook::compose_search_name(
$entry,
$entry['email'],
$entry['name'],
$this->conf['kolab_auth_user_displayname']
);
}
return $entry;
}
/**
* Detects group member attribute name
*/
private function get_group_member_attr($object_classes = array())
{
if (empty($object_classes)) {
$object_classes = $this->config['groups']['object_classes'];
}
if (!empty($object_classes)) {
foreach ((array)$object_classes as $oc) {
switch (strtolower($oc)) {
case 'group':
case 'groupofnames':
case 'kolabgroupofnames':
$member_attr = 'member';
break;
case 'groupofuniquenames':
case 'kolabgroupofuniquenames':
$member_attr = 'uniqueMember';
break;
}
}
}
if (!empty($member_attr)) {
return $member_attr;
}
if (!empty($this->config['groups']['member_attr'])) {
return $this->config['groups']['member_attr'];
}
return 'member';
}
/**
* Prepares filter query for LDAP search
*/
function parse_vars($str, $user = null, $host = null)
{
// When authenticating user $user is always set
// if not set it means we use this LDAP object for other
// purposes, e.g. kolab_delegation, then username with
// correct domain is in a session
if (!$user) {
$user = $_SESSION['username'];
}
if (isset($this->icache[$user])) {
list($user, $dc) = $this->icache[$user];
}
else {
$orig_user = $user;
$rcmail = rcube::get_instance();
// get default domain
if ($username_domain = $rcmail->config->get('username_domain')) {
if ($host && is_array($username_domain) && isset($username_domain[$host])) {
$domain = rcube_utils::parse_host($username_domain[$host], $host);
}
else if (is_string($username_domain)) {
$domain = rcube_utils::parse_host($username_domain, $host);
}
}
// realmed username (with domain)
if (strpos($user, '@')) {
list($usr, $dom) = explode('@', $user);
// unrealm domain, user login can contain a domain alias
if ($dom != $domain && ($dc = $this->find_domain($dom))) {
// @FIXME: we should replace domain in $user, I suppose
}
}
else if ($domain) {
$user .= '@' . $domain;
}
$this->icache[$orig_user] = array($user, $dc);
}
// replace variables in filter
list($u, $d) = explode('@', $user);
// hierarchal domain string
if (empty($dc)) {
$dc = 'dc=' . strtr($d, array('.' => ',dc='));
}
$replaces = array('%dc' => $dc, '%d' => $d, '%fu' => $user, '%u' => $u);
$this->parse_replaces = $replaces;
return strtr($str, $replaces);
}
/**
* Find root domain for specified domain
*
* @param string $domain Domain name
*
* @return string Domain DN string
*/
function find_domain($domain)
{
if (empty($domain) || empty($this->config['domain_base_dn']) || empty($this->config['domain_filter'])) {
return null;
}
$base_dn = $this->config['domain_base_dn'];
$filter = $this->config['domain_filter'];
$name_attr = $this->config['domain_name_attribute'];
if (empty($name_attr)) {
$name_attr = 'associateddomain';
}
$filter = str_replace('%s', rcube_ldap_generic::quote_string($domain), $filter);
$result = parent::search($base_dn, $filter, 'sub', array($name_attr, 'inetdomainbasedn'));
if (!$result) {
return null;
}
$entries = $result->entries(true);
$entry_dn = key($entries);
$entry = $entries[$entry_dn];
if (is_array($entry)) {
if (!empty($entry['inetdomainbasedn'])) {
return $entry['inetdomainbasedn'];
}
$domain = is_array($entry[$name_attr]) ? $entry[$name_attr][0] : $entry[$name_attr];
return $domain ? 'dc=' . implode(',dc=', explode('.', $domain)) : null;
}
}
/**
* Returns variables used for replacement in (last) parse_vars() call
*
* @return array Variable-value hash array
*/
public function get_parse_vars()
{
return $this->parse_replaces;
}
/**
* Register additional fields
*/
public function extend_fieldmap($map)
{
foreach ((array)$map as $name => $attr) {
if (!in_array($attr, $this->attributes)) {
$this->attributes[] = $attr;
$this->fieldmap[$name] = $attr;
}
}
}
/**
* HTML-safe DN string encoding
*
* @param string $str DN string
*
* @return string Encoded HTML identifier string
*/
static function dn_encode($str)
{
return rtrim(strtr(base64_encode($str), '+/', '-_'), '=');
}
/**
* Decodes DN string encoded with _dn_encode()
*
* @param string $str Encoded HTML identifier string
*
* @return string DN string
*/
static function dn_decode($str)
{
$str = str_pad(strtr($str, '-_', '+/'), strlen($str) % 4, '=', STR_PAD_RIGHT);
return base64_decode($str);
}
}
diff --git a/lib/plugins/kolab_auth/localization/de_CH.inc b/lib/plugins/kolab_auth/localization/de_CH.inc
index 5e85a01..0332070 100644
--- a/lib/plugins/kolab_auth/localization/de_CH.inc
+++ b/lib/plugins/kolab_auth/localization/de_CH.inc
@@ -1,3 +1,10 @@
<?php
+/**
+ * Localizations for the Kolab Auth plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_auth/
+ */
$labels['loginas'] = 'Anmelden als';
?>
diff --git a/lib/plugins/kolab_auth/localization/de_DE.inc b/lib/plugins/kolab_auth/localization/de_DE.inc
index 5e85a01..3918e6e 100644
--- a/lib/plugins/kolab_auth/localization/de_DE.inc
+++ b/lib/plugins/kolab_auth/localization/de_DE.inc
@@ -1,3 +1,11 @@
<?php
+/**
+ * Localizations for the Kolab Auth plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_auth/
+ */
$labels['loginas'] = 'Anmelden als';
+$labels['loginasnotallowed'] = 'Keine Privilegien zum Anmelden als $user';
?>
diff --git a/lib/plugins/kolab_auth/localization/en_US.inc b/lib/plugins/kolab_auth/localization/en_US.inc
index 2a7b246..4882bdc 100644
--- a/lib/plugins/kolab_auth/localization/en_US.inc
+++ b/lib/plugins/kolab_auth/localization/en_US.inc
@@ -1,13 +1,14 @@
<?php
/**
* Localizations for the Kolab Auth plugin
*
* Copyright (C) 2014, Kolab Systems AG
*
* For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_auth/
*/
$labels['loginas'] = 'Login As';
+$labels['loginasnotallowed'] = 'No privileges to login as $user';
?>
diff --git a/lib/plugins/kolab_auth/localization/fr_FR.inc b/lib/plugins/kolab_auth/localization/fr_FR.inc
index 6f72695..6538f5b 100644
--- a/lib/plugins/kolab_auth/localization/fr_FR.inc
+++ b/lib/plugins/kolab_auth/localization/fr_FR.inc
@@ -1,3 +1,11 @@
<?php
+/**
+ * Localizations for the Kolab Auth plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_auth/
+ */
$labels['loginas'] = 'Se connecter en tant que';
+$labels['loginasnotallowed'] = 'Pas de privilège de se connecter comme $utilisateur';
?>
diff --git a/lib/plugins/kolab_auth/localization/ja_JP.inc b/lib/plugins/kolab_auth/localization/ja_JP.inc
index ed0358a..e360737 100644
--- a/lib/plugins/kolab_auth/localization/ja_JP.inc
+++ b/lib/plugins/kolab_auth/localization/ja_JP.inc
@@ -1,3 +1,10 @@
<?php
+/**
+ * Localizations for the Kolab Auth plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_auth/
+ */
$labels['loginas'] = 'ログイン';
?>
diff --git a/lib/plugins/kolab_auth/localization/nl_NL.inc b/lib/plugins/kolab_auth/localization/nl_NL.inc
index a98283f..ea3a1c0 100644
--- a/lib/plugins/kolab_auth/localization/nl_NL.inc
+++ b/lib/plugins/kolab_auth/localization/nl_NL.inc
@@ -1,3 +1,10 @@
<?php
+/**
+ * Localizations for the Kolab Auth plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_auth/
+ */
$labels['loginas'] = 'Log in als';
?>
diff --git a/lib/plugins/kolab_auth/localization/pl_PL.inc b/lib/plugins/kolab_auth/localization/pl_PL.inc
index 124c373..ca67859 100644
--- a/lib/plugins/kolab_auth/localization/pl_PL.inc
+++ b/lib/plugins/kolab_auth/localization/pl_PL.inc
@@ -1,3 +1,10 @@
<?php
+/**
+ * Localizations for the Kolab Auth plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_auth/
+ */
$labels['loginas'] = 'Zaloguj jako';
?>
diff --git a/lib/plugins/kolab_auth/localization/ru_RU.inc b/lib/plugins/kolab_auth/localization/ru_RU.inc
index 9e28c12..ac9e5a7 100644
--- a/lib/plugins/kolab_auth/localization/ru_RU.inc
+++ b/lib/plugins/kolab_auth/localization/ru_RU.inc
@@ -1,3 +1,11 @@
<?php
+/**
+ * Localizations for the Kolab Auth plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_auth/
+ */
$labels['loginas'] = 'Войти как';
+$labels['loginasnotallowed'] = 'Нет привилегий войти как $user';
?>
diff --git a/lib/plugins/kolab_auth/package.xml b/lib/plugins/kolab_auth/package.xml
deleted file mode 100644
index 5a2093b..0000000
--- a/lib/plugins/kolab_auth/package.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
- http://pear.php.net/dtd/tasks-1.0.xsd
- http://pear.php.net/dtd/package-2.0
- http://pear.php.net/dtd/package-2.0.xsd">
- <name>kolab_auth</name>
- <uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
- <summary>Kolab Authentication</summary>
- <description>
- Authenticates on LDAP server, finds canonized authentication ID for IMAP
- and for new users creates identity based on LDAP information.
- Supports impersonate feature (login as another user). To use this feature
- imap_auth_type/smtp_auth_type must be set to DIGEST-MD5 or PLAIN.
- </description>
- <lead>
- <name>Aleksander Machniak</name>
- <user>machniak</user>
- <email>machniak@kolabsys.com</email>
- <active>yes</active>
- </lead>
- <date>2013-10-04</date>
- <version>
- <release>1.0</release>
- <api>1.0</api>
- </version>
- <stability>
- <release>stable</release>
- <api>stable</api>
- </stability>
- <license uri="http://www.gnu.org/licenses/agpl.html">GNU AGPLv3</license>
- <notes>-</notes>
- <contents>
- <dir baseinstalldir="/" name="/">
- <file name="kolab_auth.php" role="php">
- <tasks:replace from="@name@" to="name" type="package-info"/>
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="kolab_auth_ldap.php" role="php">
- <tasks:replace from="@name@" to="name" type="package-info"/>
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="config.inc.php.dist" role="data"></file>
- <file name="LICENSE" role="data"></file>
-
- <file name="localization/de_CH.inc" role="data"></file>
- <file name="localization/de_DE.inc" role="data"></file>
- <file name="localization/en_US.inc" role="data"></file>
- <file name="localization/pl_PL.inc" role="data"></file>
- </dir>
- <!-- / -->
- </contents>
- <dependencies>
- <required>
- <php>
- <min>5.2.1</min>
- </php>
- <pearinstaller>
- <min>1.7.0</min>
- </pearinstaller>
- </required>
- </dependencies>
- <phprelease/>
-</package>
diff --git a/lib/plugins/libkolab/composer.json b/lib/plugins/kolab_folders/composer.json
similarity index 52%
copy from lib/plugins/libkolab/composer.json
copy to lib/plugins/kolab_folders/composer.json
index 8926037..a4a9877 100644
--- a/lib/plugins/libkolab/composer.json
+++ b/lib/plugins/kolab_folders/composer.json
@@ -1,30 +1,26 @@
{
- "name": "kolab/libkolab",
+ "name": "kolab/kolab_folders",
"type": "roundcube-plugin",
- "description": "Plugin to setup a basic environment for the interaction with a Kolab server.",
+ "description": "Type-aware folder management/listing for Kolab",
"homepage": "http://git.kolab.org/roundcubemail-plugins-kolab/",
"license": "AGPLv3",
- "version": "1.1.0",
+ "version": "3.2.3",
"authors": [
{
- "name": "Thomas Bruederli",
- "email": "bruederli@kolabsys.com",
- "role": "Lead"
- },
- {
- "name": "Alensader Machniak",
+ "name": "Aleksander Machniak",
"email": "machniak@kolabsys.com",
- "role": "Developer"
+ "role": "Lead"
}
],
"repositories": [
{
"type": "composer",
"url": "http://plugins.roundcube.net"
}
],
"require": {
"php": ">=5.3.0",
- "roundcube/plugin-installer": ">=0.1.3"
+ "roundcube/plugin-installer": ">=0.1.3",
+ "kolab/libkolab": ">=3.2.3"
}
}
diff --git a/lib/plugins/kolab_folders/config.inc.php.dist b/lib/plugins/kolab_folders/config.inc.php.dist
index ffa1e15..0c9bd12 100644
--- a/lib/plugins/kolab_folders/config.inc.php.dist
+++ b/lib/plugins/kolab_folders/config.inc.php.dist
@@ -1,38 +1,36 @@
<?php
// Default kolab-specific folders. Set values to non-empty
// strings to create default folders of apropriate type.
// If there is no default folder with specified type in user mailbox,
// it will be created.
// Note: Mail folders will be also subscribed.
// Default Configuration folder
-$rcmail_config['kolab_folders_configuration_default'] = '';
+$config['kolab_folders_configuration_default'] = '';
// Default Calendar folder
-$rcmail_config['kolab_folders_event_default'] = '';
+$config['kolab_folders_event_default'] = '';
// Default Contacts (Addressbook) folder
-$rcmail_config['kolab_folders_contact_default'] = '';
+$config['kolab_folders_contact_default'] = '';
// Default Tasks folder
-$rcmail_config['kolab_folders_task_default'] = '';
+$config['kolab_folders_task_default'] = '';
// Default Notes folder
-$rcmail_config['kolab_folders_note_default'] = '';
+$config['kolab_folders_note_default'] = '';
// Default Journal folder
-$rcmail_config['kolab_folders_journal_default'] = '';
+$config['kolab_folders_journal_default'] = '';
// Default Files folder
-$rcmail_config['kolab_folders_file_default'] = '';
+$config['kolab_folders_file_default'] = '';
// Default FreeBusy folder
-$rcmail_config['kolab_folders_freebusy_default'] = '';
+$config['kolab_folders_freebusy_default'] = '';
// INBOX folder
-$rcmail_config['kolab_folders_mail_inbox'] = '';
+$config['kolab_folders_mail_inbox'] = '';
// Drafts folder
-$rcmail_config['kolab_folders_mail_drafts'] = '';
+$config['kolab_folders_mail_drafts'] = '';
// Sent folder
-$rcmail_config['kolab_folders_mail_sentitems'] = '';
+$config['kolab_folders_mail_sentitems'] = '';
// Trash folder
-$rcmail_config['kolab_folders_mail_wastebasket'] = '';
+$config['kolab_folders_mail_wastebasket'] = '';
// Others folders
-$rcmail_config['kolab_folders_mail_outbox'] = '';
-$rcmail_config['kolab_folders_mail_junkemail'] = '';
-
-?>
+$config['kolab_folders_mail_outbox'] = '';
+$config['kolab_folders_mail_junkemail'] = '';
diff --git a/lib/plugins/kolab_folders/kolab_folders.js b/lib/plugins/kolab_folders/kolab_folders.js
index ac50543..b9d9225 100644
--- a/lib/plugins/kolab_folders/kolab_folders.js
+++ b/lib/plugins/kolab_folders/kolab_folders.js
@@ -1,149 +1,125 @@
/**
* Client script for the Kolab folder management/listing extension
*
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* @licstart The following is the entire license notice for the
* JavaScript code in this file.
*
* Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @licend The above is the entire license notice
* for the JavaScript code in this file.
*/
window.rcmail && rcmail.env.action == 'folders' && rcmail.addEventListener('init', function() {
var filter = $(rcmail.gui_objects.foldersfilter),
optgroup = $('<optgroup>').attr('label', rcmail.gettext('kolab_folders.folderctype'));
// remove disabled namespaces
filter.children('option').each(function(i, opt) {
$.each(rcmail.env.skip_roots || [], function() {
if (opt.value == this) {
$(opt).remove();
}
});
});
// add type options to the filter
$.each(rcmail.env.foldertypes, function() {
optgroup.append($('<option>').attr('value', 'type-' + this).text(rcmail.gettext('kolab_folders.foldertype' + this)));
});
// overwrite default onchange handler
filter.attr('onchange', '')
.on('change', function() { return kolab_folders_filter(this.value); })
.append(optgroup);
});
window.rcmail && rcmail.env.action != 'folders' && $(document).ready(function() {
- // IE doesn't allow setting OPTION's display/visibility
- // We'll need to remove SELECT's options, see below
- if (bw.ie) {
- rcmail.env.subtype_html = $('#_subtype').html();
- }
-
// Add onchange handler for folder type SELECT, and call it on form init
$('#_ctype').change(function() {
var type = $(this).val(),
sub = $('#_subtype'),
- subtype = sub.val();
+ subtypes = rcmail.env.kolab_folder_subtypes[type] || {};
- // For IE we need to revert the whole SELECT to the original state
- if (bw.ie) {
- sub.html(rcmail.env.subtype_html).val(subtype);
- }
+ // reset subtype selector
+ sub.html('<option value=""></option>');
- // For non-mail folders we must hide mail-specific subtypes
- $('option', sub).each(function() {
- var opt = $(this), val = opt.val();
- if (val == '')
- return;
- // there's no mail.default
- if (val == 'default' && type != 'mail') {
- opt.show();
- return;
- };
-
- if (type == 'mail' && val != 'default')
- opt.show();
- else if (bw.ie)
- opt.remove();
- else
- opt.hide();
+ // append available subtypes for the given folder type
+ $.each(subtypes, function(val, label) {
+ $('<option>').attr('value', val).text(label).appendTo(sub);
});
// And re-set subtype
- if (type != 'mail' && subtype != '' && subtype != 'default') {
- sub.val('');
- }
- }).change();
+ sub.val(rcmail.env.kolab_folder_subtype);
+ });
});
function kolab_folders_filter(filter)
{
var type = filter.match(/^type-([a-z]+)$/) ? RegExp.$1 : null;
rcmail.subscription_list.reset_search();
if (!type) {
// clear type filter
if (rcmail.folder_filter_type) {
$('li', rcmail.subscription_list.container).removeData('filtered').show();
rcmail.folder_filter_type = null;
}
// apply namespace filter
rcmail.folder_filter(filter);
}
else {
rcmail.folder_filter_type = type;
rcmail.subscription_list.container.children('li').each(function() {
kolab_folder_filter_match(this, type);
});
}
return false;
}
function kolab_folder_filter_match(elem, type)
{
var found = 0, cl = elem.className || '',
$elem = $(elem),
children = $('ul', elem).children('li');
// subfolders...
children.each(function() {
found += kolab_folder_filter_match(this, type);
});
if (found || cl.match(new RegExp('type-' + type))
|| (type == 'mail' && !children.length && !cl.match(/(^| )type-([a-z]+)/))
) {
if (found || !$elem.is('.virtual')) {
found++;
}
}
if (found) {
$elem.removeData('filtered').show();
}
else {
$elem.data('filtered', true).hide();
}
return found;
}
diff --git a/lib/plugins/kolab_folders/kolab_folders.php b/lib/plugins/kolab_folders/kolab_folders.php
index 510388c..b3c2e8e 100644
--- a/lib/plugins/kolab_folders/kolab_folders.php
+++ b/lib/plugins/kolab_folders/kolab_folders.php
@@ -1,621 +1,638 @@
<?php
/**
* Type-aware folder management/listing for Kolab
*
* @version @package_version@
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_folders extends rcube_plugin
{
public $task = '?(?!login).*';
public $types = array('mail', 'event', 'journal', 'task', 'note', 'contact', 'configuration', 'file', 'freebusy');
- public $mail_types = array('inbox', 'drafts', 'sentitems', 'outbox', 'wastebasket', 'junkemail');
+ public $subtypes = array(
+ 'mail' => array('inbox', 'drafts', 'sentitems', 'outbox', 'wastebasket', 'junkemail'),
+ 'event' => array('default', 'confidential'),
+ 'task' => array('default', 'confidential'),
+ 'journal' => array('default'),
+ 'note' => array('default'),
+ 'contact' => array('default'),
+ 'configuration' => array('default'),
+ 'file' => array('default'),
+ 'freebusy' => array('default'),
+ );
public $act_types = array('event', 'task');
private $rc;
private static $instance;
/**
* Plugin initialization.
*/
function init()
{
self::$instance = $this;
$this->rc = rcube::get_instance();
// load required plugin
$this->require_plugin('libkolab');
// Folder listing hooks
$this->add_hook('storage_folders', array($this, 'mailboxes_list'));
// Folder manager hooks
$this->add_hook('folder_form', array($this, 'folder_form'));
$this->add_hook('folder_update', array($this, 'folder_save'));
$this->add_hook('folder_create', array($this, 'folder_save'));
$this->add_hook('folder_delete', array($this, 'folder_save'));
$this->add_hook('folder_rename', array($this, 'folder_save'));
$this->add_hook('folders_list', array($this, 'folders_list'));
// Special folders setting
$this->add_hook('preferences_save', array($this, 'prefs_save'));
}
/**
* Handler for mailboxes_list hook. Enables type-aware lists filtering.
*/
function mailboxes_list($args)
{
// infinite loop prevention
if ($this->is_processing) {
return $args;
}
if (!$this->metadata_support()) {
return $args;
}
$this->is_processing = true;
// get folders
$folders = kolab_storage::list_folders($args['root'], $args['name'], $args['filter'], $args['mode'] == 'LSUB', $folderdata);
$this->is_processing = false;
if (!is_array($folders)) {
return $args;
}
// Create default folders
if ($args['root'] == '' && $args['name'] = '*') {
$this->create_default_folders($folders, $args['filter'], $folderdata, $args['mode'] == 'LSUB');
}
$args['folders'] = $folders;
return $args;
}
/**
* Handler for folders_list hook. Add css classes to folder rows.
*/
function folders_list($args)
{
if (!$this->metadata_support()) {
return $args;
}
// load translations
$this->add_texts('localization/', false);
// Add javascript script to the client
$this->include_script('kolab_folders.js');
$this->add_label('folderctype');
foreach ($this->types as $type) {
$this->add_label('foldertype' . $type);
}
$skip_namespace = $this->rc->config->get('kolab_skip_namespace');
$skip_roots = array();
if (!empty($skip_namespace)) {
$storage = $this->rc->get_storage();
foreach ((array)$skip_namespace as $ns) {
foreach((array)$storage->get_namespace($ns) as $root) {
$skip_roots[] = rtrim($root[0], $root[1]);
}
}
}
$this->rc->output->set_env('skip_roots', $skip_roots);
$this->rc->output->set_env('foldertypes', $this->types);
// get folders types
$folderdata = kolab_storage::folders_typedata();
if (!is_array($folderdata)) {
return $args;
}
// Add type-based style for table rows
// See kolab_folders::folder_class_name()
if ($table = $args['table']) {
for ($i=1, $cnt=$table->size(); $i<=$cnt; $i++) {
$attrib = $table->get_row_attribs($i);
$folder = $attrib['foldername']; // UTF7-IMAP
$type = $folderdata[$folder];
if (!$type) {
$type = 'mail';
}
$class_name = self::folder_class_name($type);
$attrib['class'] = trim($attrib['class'] . ' ' . $class_name);
$table->set_row_attribs($attrib, $i);
}
}
// Add type-based class for list items
if (is_array($args['list'])) {
foreach ((array)$args['list'] as $k => $item) {
$folder = $item['folder_imap']; // UTF7-IMAP
$type = $folderdata[$folder];
if (!$type) {
$type = 'mail';
}
$class_name = self::folder_class_name($type);
$args['list'][$k]['class'] = trim($item['class'] . ' ' . $class_name);
}
}
return $args;
}
/**
* Handler for folder info/edit form (folder_form hook).
* Adds folder type selector.
*/
function folder_form($args)
{
if (!$this->metadata_support()) {
return $args;
}
// load translations
$this->add_texts('localization/', false);
// INBOX folder is of type mail.inbox and this cannot be changed
if ($args['name'] == 'INBOX') {
$args['form']['props']['fieldsets']['settings']['content']['foldertype'] = array(
'label' => $this->gettext('folderctype'),
'value' => sprintf('%s (%s)', $this->gettext('foldertypemail'), $this->gettext('inbox')),
);
return $args;
}
if ($args['options']['is_root']) {
return $args;
}
$mbox = strlen($args['name']) ? $args['name'] : $args['parent_name'];
if (isset($_POST['_ctype'])) {
- $new_ctype = trim(get_input_value('_ctype', RCUBE_INPUT_POST));
- $new_subtype = trim(get_input_value('_subtype', RCUBE_INPUT_POST));
+ $new_ctype = trim(rcube_utils::get_input_value('_ctype', rcube_utils::INPUT_POST));
+ $new_subtype = trim(rcube_utils::get_input_value('_subtype', rcube_utils::INPUT_POST));
}
// Get type of the folder or the parent
if (strlen($mbox)) {
list($ctype, $subtype) = $this->get_folder_type($mbox);
if (strlen($args['parent_name']) && $subtype == 'default')
$subtype = ''; // there can be only one
}
if (!$ctype) {
$ctype = 'mail';
}
$storage = $this->rc->get_storage();
// Don't allow changing type of shared folder, according to ACL
if (strlen($mbox)) {
$options = $storage->folder_info($mbox);
if ($options['namespace'] != 'personal' && !in_array('a', (array)$options['rights'])) {
if (in_array($ctype, $this->types)) {
$value = $this->gettext('foldertype'.$ctype);
}
else {
$value = $ctype;
}
if ($subtype) {
$value .= ' ('. ($subtype == 'default' ? $this->gettext('default') : $subtype) .')';
}
$args['form']['props']['fieldsets']['settings']['content']['foldertype'] = array(
'label' => $this->gettext('folderctype'),
'value' => $value,
);
return $args;
}
}
// Add javascript script to the client
$this->include_script('kolab_folders.js');
// build type SELECT fields
$type_select = new html_select(array('name' => '_ctype', 'id' => '_ctype'));
$sub_select = new html_select(array('name' => '_subtype', 'id' => '_subtype'));
+ $sub_select->add('', '');
foreach ($this->types as $type) {
$type_select->add($this->gettext('foldertype'.$type), $type);
}
// add non-supported type
if (!in_array($ctype, $this->types)) {
$type_select->add($ctype, $ctype);
}
- $sub_select->add('', '');
- $sub_select->add($this->gettext('default'), 'default');
- foreach ($this->mail_types as $type) {
- $sub_select->add($this->gettext($type), $type);
+ $sub_types = array();
+ foreach ($this->subtypes as $ftype => $subtypes) {
+ $sub_types[$ftype] = array_combine($subtypes, array_map(array($this, 'gettext'), $subtypes));
+
+ // fill options for the current folder type
+ if ($ftype == $ctype || $ftype == $new_ctype) {
+ $sub_select->add(array_values($sub_types[$ftype]), $subtypes);
+ }
}
$args['form']['props']['fieldsets']['settings']['content']['foldertype'] = array(
'label' => $this->gettext('folderctype'),
'value' => $type_select->show(isset($new_ctype) ? $new_ctype : $ctype)
. $sub_select->show(isset($new_subtype) ? $new_subtype : $subtype),
);
+ $this->rc->output->set_env('kolab_folder_subtypes', $sub_types);
+ $this->rc->output->set_env('kolab_folder_subtype', isset($new_subtype) ? $new_subtype : $subtype);
+
return $args;
}
/**
* Handler for folder update/create action (folder_update/folder_create hook).
*/
function folder_save($args)
{
// Folder actions from folders list
if (empty($args['record'])) {
return $args;
}
// Folder create/update with form
- $ctype = trim(get_input_value('_ctype', RCUBE_INPUT_POST));
- $subtype = trim(get_input_value('_subtype', RCUBE_INPUT_POST));
+ $ctype = trim(rcube_utils::get_input_value('_ctype', rcube_utils::INPUT_POST));
+ $subtype = trim(rcube_utils::get_input_value('_subtype', rcube_utils::INPUT_POST));
$mbox = $args['record']['name'];
$old_mbox = $args['record']['oldname'];
$subscribe = $args['record']['subscribe'];
if (empty($ctype)) {
return $args;
}
// load translations
$this->add_texts('localization/', false);
// Skip folder creation/rename in core
// @TODO: Maybe we should provide folder_create_after and folder_update_after hooks?
// Using create_mailbox/rename_mailbox here looks bad
$args['abort'] = true;
// There can be only one default folder of specified type
if ($subtype == 'default') {
$default = $this->get_default_folder($ctype);
if ($default !== null && $old_mbox != $default) {
$args['result'] = false;
$args['message'] = $this->gettext('defaultfolderexists');
return $args;
}
}
// Subtype sanity-checks
- else if ($subtype && ($ctype != 'mail' || !in_array($subtype, $this->mail_types))) {
+ else if ($subtype && (!($subtypes = $this->subtypes[$ctype]) || !in_array($subtype, $subtypes))) {
$subtype = '';
}
$ctype .= $subtype ? '.'.$subtype : '';
$storage = $this->rc->get_storage();
// Create folder
if (!strlen($old_mbox)) {
// By default don't subscribe to non-mail folders
if ($subscribe)
$subscribe = (bool) preg_match('/^mail/', $ctype);
$result = $storage->create_folder($mbox, $subscribe);
// Set folder type
if ($result) {
$this->set_folder_type($mbox, $ctype);
}
}
// Rename folder
else {
if ($old_mbox != $mbox) {
$result = $storage->rename_folder($old_mbox, $mbox);
}
else {
$result = true;
}
if ($result) {
list($oldtype, $oldsubtype) = $this->get_folder_type($mbox);
$oldtype .= $oldsubtype ? '.'.$oldsubtype : '';
if ($ctype != $oldtype) {
$this->set_folder_type($mbox, $ctype);
}
}
}
$args['record']['class'] = self::folder_class_name($ctype);
$args['record']['subscribe'] = $subscribe;
$args['result'] = $result;
return $args;
}
/**
* Handler for user preferences save (preferences_save hook)
*
* @param array $args Hash array with hook parameters
*
* @return array Hash array with modified hook parameters
*/
public function prefs_save($args)
{
if ($args['section'] != 'folders') {
return $args;
}
$dont_override = (array) $this->rc->config->get('dont_override', array());
// map config option name to kolab folder type annotation
$opts = array(
'drafts_mbox' => 'mail.drafts',
'sent_mbox' => 'mail.sentitems',
'junk_mbox' => 'mail.junkemail',
'trash_mbox' => 'mail.wastebasket',
);
// check if any of special folders has been changed
foreach ($opts as $opt_name => $type) {
$new = $args['prefs'][$opt_name];
$old = $this->rc->config->get($opt_name);
if (!strlen($new) || $new === $old || in_array($opt_name, $dont_override)) {
unset($opts[$opt_name]);
}
}
if (empty($opts)) {
return $args;
}
$folderdata = kolab_storage::folders_typedata();
if (!is_array($folderdata)) {
return $args;
}
foreach ($opts as $opt_name => $type) {
$foldername = $args['prefs'][$opt_name];
// get all folders of specified type
$folders = array_intersect($folderdata, array($type));
// folder already annotated with specified type
if (!empty($folders[$foldername])) {
continue;
}
// set type to the new folder
$this->set_folder_type($foldername, $type);
// unset old folder(s) type annotation
list($maintype, $subtype) = explode('.', $type);
foreach (array_keys($folders) as $folder) {
$this->set_folder_type($folder, $maintype);
}
}
return $args;
}
/**
* Checks if IMAP server supports any of METADATA, ANNOTATEMORE, ANNOTATEMORE2
*
* @return boolean
*/
function metadata_support()
{
$storage = $this->rc->get_storage();
return $storage->get_capability('METADATA') ||
$storage->get_capability('ANNOTATEMORE') ||
$storage->get_capability('ANNOTATEMORE2');
}
/**
* Checks if IMAP server supports any of METADATA, ANNOTATEMORE, ANNOTATEMORE2
*
* @param string $folder Folder name
*
* @return array Folder content-type
*/
function get_folder_type($folder)
{
return explode('.', (string)kolab_storage::folder_type($folder));
}
/**
* Sets folder content-type.
*
* @param string $folder Folder name
* @param string $type Content type
*
* @return boolean True on success
*/
function set_folder_type($folder, $type = 'mail')
{
return kolab_storage::set_folder_type($folder, $type);
}
/**
* Returns the name of default folder
*
* @param string $type Folder type
*
* @return string Folder name
*/
function get_default_folder($type)
{
$folderdata = kolab_storage::folders_typedata();
if (!is_array($folderdata)) {
return null;
}
// get all folders of specified type
$folderdata = array_intersect($folderdata, array($type.'.default'));
return key($folderdata);
}
/**
* Returns CSS class name for specified folder type
*
* @param string $type Folder type
*
* @return string Class name
*/
static function folder_class_name($type)
{
list($ctype, $subtype) = explode('.', $type);
$class[] = 'type-' . ($ctype ? $ctype : 'mail');
if ($subtype)
$class[] = 'subtype-' . $subtype;
return implode(' ', $class);
}
/**
* Creates default folders if they doesn't exist
*/
private function create_default_folders(&$folders, $filter, $folderdata = null, $lsub = false)
{
$storage = $this->rc->get_storage();
$namespace = $storage->get_namespace();
$defaults = array();
$prefix = '';
// Find personal namespace prefix
if (is_array($namespace['personal']) && count($namespace['personal']) == 1) {
$prefix = $namespace['personal'][0][0];
}
$this->load_config();
// get configured defaults
foreach ($this->types as $type) {
- $subtypes = $type == 'mail' ? $this->mail_types : array('default');
- foreach ($subtypes as $subtype) {
+ foreach ((array)$this->subtypes[$type] as $subtype) {
$opt_name = 'kolab_folders_' . $type . '_' . $subtype;
if ($folder = $this->rc->config->get($opt_name)) {
// convert configuration value to UTF7-IMAP charset
$folder = rcube_charset::convert($folder, RCUBE_CHARSET, 'UTF7-IMAP');
// and namespace prefix if needed
if ($prefix && strpos($folder, $prefix) === false && $folder != 'INBOX') {
$folder = $prefix . $folder;
}
$defaults[$type . '.' . $subtype] = $folder;
}
}
}
if (empty($defaults)) {
return;
}
if ($folderdata === null) {
$folderdata = kolab_storage::folders_typedata();
}
if (!is_array($folderdata)) {
return;
}
// find default folders
foreach ($defaults as $type => $foldername) {
// get all folders of specified type
$_folders = array_intersect($folderdata, array($type));
// default folder found
if (!empty($_folders)) {
continue;
}
list($type1, $type2) = explode('.', $type);
$activate = in_array($type1, $this->act_types);
$exists = false;
$result = false;
// check if folder exists
if (!empty($folderdata[$foldername]) || $foldername == 'INBOX') {
$exists = true;
}
else if ((!$filter || $filter == $type1) && in_array($foldername, $folders)) {
// this assumes also that subscribed folder exists
$exists = true;
}
else {
$exists = $storage->folder_exists($foldername);
}
// create folder
if (!$exists) {
$exists = $storage->create_folder($foldername);
}
// set type + subscribe + activate
if ($exists) {
if ($result = kolab_storage::set_folder_type($foldername, $type)) {
// check if folder is subscribed
if ((!$filter || $filter == $type1) && $lsub && in_array($foldername, $folders)) {
// already subscribed
$subscribed = true;
}
else {
$subscribed = $storage->subscribe($foldername);
}
// activate folder
if ($activate) {
kolab_storage::folder_activate($foldername, true);
}
}
}
// add new folder to the result
if ($result && (!$filter || $filter == $type1) && (!$lsub || $subscribed)) {
$folders[] = $foldername;
}
}
}
/**
* Static getter for default folder of the given type
*
* @param string $type Folder type
* @return string Folder name
*/
public static function default_folder($type)
{
return self::$instance->get_default_folder($type);
}
}
diff --git a/lib/plugins/kolab_folders/localization/de_CH.inc b/lib/plugins/kolab_folders/localization/de_CH.inc
index ebac855..91c24d3 100644
--- a/lib/plugins/kolab_folders/localization/de_CH.inc
+++ b/lib/plugins/kolab_folders/localization/de_CH.inc
@@ -1,20 +1,28 @@
<?php
+/**
+ * Localizations for the Kolab Folders plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_folders/
+ */
$labels['folderctype'] = 'Ordnerinhalt';
$labels['foldertypemail'] = 'E-Mail';
$labels['foldertypeevent'] = 'Kalender';
$labels['foldertypejournal'] = 'Journal';
$labels['foldertypetask'] = 'Aufgaben';
$labels['foldertypenote'] = 'Notizen';
$labels['foldertypecontact'] = 'Kontakte';
$labels['foldertypeconfiguration'] = 'Konfiguration';
$labels['foldertypefile'] = 'Dateien';
$labels['foldertypefreebusy'] = 'Frei-Besetzt';
$labels['default'] = 'Standard';
$labels['inbox'] = 'Posteingang';
$labels['drafts'] = 'Entwürfe';
$labels['sentitems'] = 'Gesendet';
$labels['outbox'] = 'Postausgang';
$labels['wastebasket'] = 'Gelöscht';
$labels['junkemail'] = 'Spam';
+$labels['confidential'] = 'Confidential';
$messages['defaultfolderexists'] = 'Es existiert bereits ein Standardordner für den angegebenen Typ';
?>
diff --git a/lib/plugins/kolab_folders/localization/de_DE.inc b/lib/plugins/kolab_folders/localization/de_DE.inc
index 9921e25..24749ad 100644
--- a/lib/plugins/kolab_folders/localization/de_DE.inc
+++ b/lib/plugins/kolab_folders/localization/de_DE.inc
@@ -1,20 +1,28 @@
<?php
+/**
+ * Localizations for the Kolab Folders plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_folders/
+ */
$labels['folderctype'] = 'Ordnerinhalt';
$labels['foldertypemail'] = 'E-Mail';
$labels['foldertypeevent'] = 'Kalender';
$labels['foldertypejournal'] = 'Journal';
$labels['foldertypetask'] = 'Aufgaben';
$labels['foldertypenote'] = 'Notizen';
$labels['foldertypecontact'] = 'Kontakte';
$labels['foldertypeconfiguration'] = 'Konfiguration';
$labels['foldertypefile'] = 'Dateien';
$labels['foldertypefreebusy'] = 'Frei/Belegt';
$labels['default'] = 'Standard';
$labels['inbox'] = 'Posteingang';
$labels['drafts'] = 'Entwürfe';
$labels['sentitems'] = 'Gesendet';
$labels['outbox'] = 'Postausgang';
$labels['wastebasket'] = 'Mülleimer';
$labels['junkemail'] = 'Spam';
+$labels['confidential'] = 'Vertraulich';
$messages['defaultfolderexists'] = 'Es gibt bereits einen Standardordner dieses Typs';
?>
diff --git a/lib/plugins/kolab_folders/localization/en_US.inc b/lib/plugins/kolab_folders/localization/en_US.inc
index 0d8d86c..0910d9d 100644
--- a/lib/plugins/kolab_folders/localization/en_US.inc
+++ b/lib/plugins/kolab_folders/localization/en_US.inc
@@ -1,34 +1,35 @@
<?php
/**
* Localizations for the Kolab Folders plugin
*
* Copyright (C) 2014, Kolab Systems AG
*
* For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_folders/
*/
$labels = array();
$labels['folderctype'] = 'Content type';
$labels['foldertypemail'] = 'Mail';
$labels['foldertypeevent'] = 'Calendar';
$labels['foldertypejournal'] = 'Journal';
$labels['foldertypetask'] = 'Tasks';
$labels['foldertypenote'] = 'Notes';
$labels['foldertypecontact'] = 'Contacts';
$labels['foldertypeconfiguration'] = 'Configuration';
$labels['foldertypefile'] = 'Files';
$labels['foldertypefreebusy'] = 'Free-Busy';
$labels['default'] = 'Default';
$labels['inbox'] = 'Inbox';
$labels['drafts'] = 'Drafts';
$labels['sentitems'] = 'Sent';
$labels['outbox'] = 'Outbox';
$labels['wastebasket'] = 'Trash';
$labels['junkemail'] = 'Junk';
+$labels['confidential'] = 'Confidential';
$messages['defaultfolderexists'] = 'There is already default folder of specified type';
?>
diff --git a/lib/plugins/kolab_folders/localization/es_ES.inc b/lib/plugins/kolab_folders/localization/es_ES.inc
index cdbc62c..b53712b 100644
--- a/lib/plugins/kolab_folders/localization/es_ES.inc
+++ b/lib/plugins/kolab_folders/localization/es_ES.inc
@@ -1,7 +1,28 @@
<?php
+/**
+ * Localizations for the Kolab Folders plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_folders/
+ */
+$labels['folderctype'] = 'Content type';
+$labels['foldertypemail'] = 'Mail';
$labels['foldertypeevent'] = 'Calendar';
+$labels['foldertypejournal'] = 'Journal';
$labels['foldertypetask'] = 'Tareas';
$labels['foldertypenote'] = 'Notas';
$labels['foldertypecontact'] = 'Contactos';
$labels['foldertypeconfiguration'] = 'Configuración';
+$labels['foldertypefile'] = 'Files';
+$labels['foldertypefreebusy'] = 'Free-Busy';
+$labels['default'] = 'Default';
+$labels['inbox'] = 'Inbox';
+$labels['drafts'] = 'Drafts';
+$labels['sentitems'] = 'Sent';
+$labels['outbox'] = 'Outbox';
+$labels['wastebasket'] = 'Trash';
+$labels['junkemail'] = 'Junk';
+$labels['confidential'] = 'Confidential';
+$messages['defaultfolderexists'] = 'There is already default folder of specified type';
?>
diff --git a/lib/plugins/kolab_folders/localization/et_EE.inc b/lib/plugins/kolab_folders/localization/et_EE.inc
index efb51b6..5a12004 100644
--- a/lib/plugins/kolab_folders/localization/et_EE.inc
+++ b/lib/plugins/kolab_folders/localization/et_EE.inc
@@ -1,3 +1,28 @@
<?php
+/**
+ * Localizations for the Kolab Folders plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_folders/
+ */
+$labels['folderctype'] = 'Content type';
+$labels['foldertypemail'] = 'Mail';
$labels['foldertypeevent'] = 'Calendar';
+$labels['foldertypejournal'] = 'Journal';
+$labels['foldertypetask'] = 'Tasks';
+$labels['foldertypenote'] = 'Notes';
+$labels['foldertypecontact'] = 'Contacts';
+$labels['foldertypeconfiguration'] = 'Configuration';
+$labels['foldertypefile'] = 'Files';
+$labels['foldertypefreebusy'] = 'Free-Busy';
+$labels['default'] = 'Default';
+$labels['inbox'] = 'Inbox';
+$labels['drafts'] = 'Drafts';
+$labels['sentitems'] = 'Sent';
+$labels['outbox'] = 'Outbox';
+$labels['wastebasket'] = 'Trash';
+$labels['junkemail'] = 'Junk';
+$labels['confidential'] = 'Confidential';
+$messages['defaultfolderexists'] = 'There is already default folder of specified type';
?>
diff --git a/lib/plugins/kolab_folders/localization/fr_FR.inc b/lib/plugins/kolab_folders/localization/fr_FR.inc
index ac3d2dd..41e506e 100644
--- a/lib/plugins/kolab_folders/localization/fr_FR.inc
+++ b/lib/plugins/kolab_folders/localization/fr_FR.inc
@@ -1,20 +1,28 @@
<?php
+/**
+ * Localizations for the Kolab Folders plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_folders/
+ */
$labels['folderctype'] = 'Type de contenu';
$labels['foldertypemail'] = 'Courriel';
$labels['foldertypeevent'] = 'Calendrier';
$labels['foldertypejournal'] = 'Journal';
$labels['foldertypetask'] = 'Tâches';
$labels['foldertypenote'] = 'Notes';
$labels['foldertypecontact'] = 'Contacts';
$labels['foldertypeconfiguration'] = 'Configuration';
$labels['foldertypefile'] = 'Fichiers';
$labels['foldertypefreebusy'] = 'Disponible/Occupé';
$labels['default'] = 'Par Défaut';
$labels['inbox'] = 'Courrier entrant';
$labels['drafts'] = 'Brouillons';
$labels['sentitems'] = 'Envoyés';
$labels['outbox'] = 'Courrier sortant';
$labels['wastebasket'] = 'Corbeille';
$labels['junkemail'] = 'Indésirables';
+$labels['confidential'] = 'Confidentiel';
$messages['defaultfolderexists'] = 'Il existe déjà un répertoire par défaut pour le type spécifié';
?>
diff --git a/lib/plugins/kolab_folders/localization/ja_JP.inc b/lib/plugins/kolab_folders/localization/ja_JP.inc
index 2ac10c2..6dfd611 100644
--- a/lib/plugins/kolab_folders/localization/ja_JP.inc
+++ b/lib/plugins/kolab_folders/localization/ja_JP.inc
@@ -1,20 +1,28 @@
<?php
+/**
+ * Localizations for the Kolab Folders plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_folders/
+ */
$labels['folderctype'] = 'コンテンツタイプ';
$labels['foldertypemail'] = 'メール';
$labels['foldertypeevent'] = 'カレンダー';
$labels['foldertypejournal'] = 'ジャーナル';
$labels['foldertypetask'] = 'タスク';
$labels['foldertypenote'] = 'ノート';
$labels['foldertypecontact'] = 'コンタクト';
$labels['foldertypeconfiguration'] = '設定';
$labels['foldertypefile'] = 'ファイル';
$labels['foldertypefreebusy'] = '空状況';
$labels['default'] = 'デフォルト';
$labels['inbox'] = '受信箱';
$labels['drafts'] = '下書き';
$labels['sentitems'] = '送信済';
$labels['outbox'] = '送信箱';
$labels['wastebasket'] = 'ごみ箱';
$labels['junkemail'] = '迷惑メール';
+$labels['confidential'] = 'Confidential';
$messages['defaultfolderexists'] = '指定したタイプの初期フォルダは既にあります。';
?>
diff --git a/lib/plugins/kolab_folders/localization/nl_NL.inc b/lib/plugins/kolab_folders/localization/nl_NL.inc
index 4946a24..a80aca5 100644
--- a/lib/plugins/kolab_folders/localization/nl_NL.inc
+++ b/lib/plugins/kolab_folders/localization/nl_NL.inc
@@ -1,20 +1,28 @@
<?php
+/**
+ * Localizations for the Kolab Folders plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_folders/
+ */
$labels['folderctype'] = 'Inhoudstype';
$labels['foldertypemail'] = 'Mail';
$labels['foldertypeevent'] = 'Agenda';
$labels['foldertypejournal'] = 'Dagboek';
$labels['foldertypetask'] = 'Taken';
$labels['foldertypenote'] = 'Notities';
$labels['foldertypecontact'] = 'Adresboek';
$labels['foldertypeconfiguration'] = 'Configuratie';
$labels['foldertypefile'] = 'Bestanden';
$labels['foldertypefreebusy'] = 'Vrij/Bezet';
$labels['default'] = 'Standaard';
$labels['inbox'] = 'Inbox';
$labels['drafts'] = 'Concepten';
$labels['sentitems'] = 'Verzonden';
$labels['outbox'] = 'Te versturen';
$labels['wastebasket'] = 'Prullenbak';
$labels['junkemail'] = 'Ongewenst';
+$labels['confidential'] = 'Vertrouwelijk';
$messages['defaultfolderexists'] = 'Er is reeds een standaard map voor dit type inhoud';
?>
diff --git a/lib/plugins/kolab_folders/localization/pl_PL.inc b/lib/plugins/kolab_folders/localization/pl_PL.inc
index 0b13bdd..8d9f61a 100644
--- a/lib/plugins/kolab_folders/localization/pl_PL.inc
+++ b/lib/plugins/kolab_folders/localization/pl_PL.inc
@@ -1,20 +1,28 @@
<?php
+/**
+ * Localizations for the Kolab Folders plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_folders/
+ */
$labels['folderctype'] = 'Typ treści';
$labels['foldertypemail'] = 'Poczta';
$labels['foldertypeevent'] = 'Kalendarz';
$labels['foldertypejournal'] = 'Dziennik';
$labels['foldertypetask'] = 'Zadania';
$labels['foldertypenote'] = 'Notatki';
$labels['foldertypecontact'] = 'Kontakty';
$labels['foldertypeconfiguration'] = 'Konfiguracja';
$labels['foldertypefile'] = 'Pliki';
$labels['foldertypefreebusy'] = 'Wolny-Zajęty';
$labels['default'] = 'Domyślny';
$labels['inbox'] = 'Odebrane';
$labels['drafts'] = 'Kopie robocze';
$labels['sentitems'] = 'Wysłane';
$labels['outbox'] = 'Poczta wychodząca';
$labels['wastebasket'] = 'Kosz';
$labels['junkemail'] = 'Spam';
+$labels['confidential'] = 'Confidential';
$messages['defaultfolderexists'] = 'Folder domyślny wybranego typu już istnieje';
?>
diff --git a/lib/plugins/kolab_folders/localization/ru_RU.inc b/lib/plugins/kolab_folders/localization/ru_RU.inc
index 83df476..4e50bff 100644
--- a/lib/plugins/kolab_folders/localization/ru_RU.inc
+++ b/lib/plugins/kolab_folders/localization/ru_RU.inc
@@ -1,20 +1,28 @@
<?php
+/**
+ * Localizations for the Kolab Folders plugin
+ *
+ * Copyright (C) 2014, Kolab Systems AG
+ *
+ * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_folders/
+ */
$labels['folderctype'] = 'Тип ящика';
$labels['foldertypemail'] = 'Почта';
$labels['foldertypeevent'] = 'Календарь';
$labels['foldertypejournal'] = 'Журнал';
$labels['foldertypetask'] = 'Задачи';
$labels['foldertypenote'] = 'Заметки';
$labels['foldertypecontact'] = 'Контакты';
$labels['foldertypeconfiguration'] = 'Настройки';
$labels['foldertypefile'] = 'Файлы';
$labels['foldertypefreebusy'] = 'Занят/Свободен';
$labels['default'] = 'По умолчанию';
$labels['inbox'] = 'Входящие';
$labels['drafts'] = 'Черновики';
$labels['sentitems'] = 'Отправленные';
$labels['outbox'] = 'Исходящие';
$labels['wastebasket'] = 'Корзина';
$labels['junkemail'] = 'Спам';
+$labels['confidential'] = 'Конфиденциально';
$messages['defaultfolderexists'] = 'Уже назначен ящик по умолчанию для указанного типа';
?>
diff --git a/lib/plugins/kolab_folders/package.xml b/lib/plugins/kolab_folders/package.xml
deleted file mode 100644
index b40acab..0000000
--- a/lib/plugins/kolab_folders/package.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
- http://pear.php.net/dtd/tasks-1.0.xsd
- http://pear.php.net/dtd/package-2.0
- http://pear.php.net/dtd/package-2.0.xsd">
- <name>kolab_folders</name>
- <uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
- <summary>Type-aware folder management/listing for Kolab</summary>
- <description>
- The plugin extends folders handling with features of the Kolab Suite
- according to specified format (http://www.kolab.org/doc/kolabformat-2.0-html).
- With this plugin enabled it is possible to:
- - set/get/change folder's type,
- - filter folders list by folder type,
- - style folders list rows (in folder manager),
- - create default folders with specified type.
- </description>
- <lead>
- <name>Aleksander Machniak</name>
- <user>machniak</user>
- <email>machniak@kolabsys.com</email>
- <active>yes</active>
- </lead>
- <date>2012-10.25</date>
- <version>
- <release>2.1</release>
- <api>2.0</api>
- </version>
- <stability>
- <release>stable</release>
- <api>stable</api>
- </stability>
- <license uri="http://www.gnu.org/licenses/agpl.html">GNU AGPLv3</license>
- <notes>-</notes>
- <contents>
- <dir baseinstalldir="/" name="/">
- <file name="kolab_folders.php" role="php">
- <tasks:replace from="@name@" to="name" type="package-info"/>
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="kolab_folders.js" role="data">
- <tasks:replace from="@name@" to="name" type="package-info"/>
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="config.inc.php.dist" role="data"></file>
- <file name="localization/en_US.inc" role="data"></file>
- <file name="localization/pl_PL.inc" role="data"></file>
- <file name="LICENSE" role="data"></file>
- </dir>
- <!-- / -->
- </contents>
- <dependencies>
- <required>
- <php>
- <min>5.2.1</min>
- </php>
- <pearinstaller>
- <min>1.7.0</min>
- </pearinstaller>
- </required>
- </dependencies>
- <phprelease />
-</package>
diff --git a/lib/plugins/libkolab/SQL/mysql.initial.sql b/lib/plugins/libkolab/SQL/mysql.initial.sql
index 2aa046d..98e7e78 100644
--- a/lib/plugins/libkolab/SQL/mysql.initial.sql
+++ b/lib/plugins/libkolab/SQL/mysql.initial.sql
@@ -1,187 +1,187 @@
/**
* libkolab database schema
*
* @version 1.1
* @author Thomas Bruederli
* @licence GNU AGPL
**/
DROP TABLE IF EXISTS `kolab_folders`;
CREATE TABLE `kolab_folders` (
`folder_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`resource` VARCHAR(255) NOT NULL,
`type` VARCHAR(32) NOT NULL,
`synclock` INT(10) NOT NULL DEFAULT '0',
`ctag` VARCHAR(40) DEFAULT NULL,
PRIMARY KEY(`folder_id`),
INDEX `resource_type` (`resource`, `type`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
DROP TABLE IF EXISTS `kolab_cache`;
DROP TABLE IF EXISTS `kolab_cache_contact`;
CREATE TABLE `kolab_cache_contact` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
`xml` LONGBLOB NOT NULL,
- `tags` VARCHAR(255) NOT NULL,
+ `tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`type` VARCHAR(32) CHARACTER SET ascii NOT NULL,
`name` VARCHAR(255) NOT NULL,
`firstname` VARCHAR(255) NOT NULL,
`surname` VARCHAR(255) NOT NULL,
`email` VARCHAR(255) NOT NULL,
CONSTRAINT `fk_kolab_cache_contact_folder` FOREIGN KEY (`folder_id`)
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY(`folder_id`,`msguid`),
INDEX `contact_type` (`folder_id`,`type`),
INDEX `contact_uid2msguid` (`folder_id`,`uid`,`msguid`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
DROP TABLE IF EXISTS `kolab_cache_event`;
CREATE TABLE `kolab_cache_event` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
`xml` LONGBLOB NOT NULL,
- `tags` VARCHAR(255) NOT NULL,
+ `tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`dtstart` DATETIME,
`dtend` DATETIME,
CONSTRAINT `fk_kolab_cache_event_folder` FOREIGN KEY (`folder_id`)
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY(`folder_id`,`msguid`),
INDEX `event_uid2msguid` (`folder_id`,`uid`,`msguid`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
DROP TABLE IF EXISTS `kolab_cache_task`;
CREATE TABLE `kolab_cache_task` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
`xml` LONGBLOB NOT NULL,
- `tags` VARCHAR(255) NOT NULL,
+ `tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`dtstart` DATETIME,
`dtend` DATETIME,
CONSTRAINT `fk_kolab_cache_task_folder` FOREIGN KEY (`folder_id`)
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY(`folder_id`,`msguid`),
INDEX `task_uid2msguid` (`folder_id`,`uid`,`msguid`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
DROP TABLE IF EXISTS `kolab_cache_journal`;
CREATE TABLE `kolab_cache_journal` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
`xml` LONGBLOB NOT NULL,
- `tags` VARCHAR(255) NOT NULL,
+ `tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`dtstart` DATETIME,
`dtend` DATETIME,
CONSTRAINT `fk_kolab_cache_journal_folder` FOREIGN KEY (`folder_id`)
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY(`folder_id`,`msguid`),
INDEX `journal_uid2msguid` (`folder_id`,`uid`,`msguid`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
DROP TABLE IF EXISTS `kolab_cache_note`;
CREATE TABLE `kolab_cache_note` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
`xml` LONGBLOB NOT NULL,
- `tags` VARCHAR(255) NOT NULL,
+ `tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
CONSTRAINT `fk_kolab_cache_note_folder` FOREIGN KEY (`folder_id`)
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY(`folder_id`,`msguid`),
INDEX `note_uid2msguid` (`folder_id`,`uid`,`msguid`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
DROP TABLE IF EXISTS `kolab_cache_file`;
CREATE TABLE `kolab_cache_file` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
`xml` LONGBLOB NOT NULL,
- `tags` VARCHAR(255) NOT NULL,
+ `tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`filename` varchar(255) DEFAULT NULL,
CONSTRAINT `fk_kolab_cache_file_folder` FOREIGN KEY (`folder_id`)
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY(`folder_id`,`msguid`),
INDEX `folder_filename` (`folder_id`, `filename`),
INDEX `file_uid2msguid` (`folder_id`,`uid`,`msguid`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
DROP TABLE IF EXISTS `kolab_cache_configuration`;
CREATE TABLE `kolab_cache_configuration` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
`xml` LONGBLOB NOT NULL,
- `tags` VARCHAR(255) NOT NULL,
+ `tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`type` VARCHAR(32) CHARACTER SET ascii NOT NULL,
CONSTRAINT `fk_kolab_cache_configuration_folder` FOREIGN KEY (`folder_id`)
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY(`folder_id`,`msguid`),
INDEX `configuration_type` (`folder_id`,`type`),
INDEX `configuration_uid2msguid` (`folder_id`,`uid`,`msguid`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
DROP TABLE IF EXISTS `kolab_cache_freebusy`;
CREATE TABLE `kolab_cache_freebusy` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
`xml` LONGBLOB NOT NULL,
- `tags` VARCHAR(255) NOT NULL,
+ `tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`dtstart` DATETIME,
`dtend` DATETIME,
CONSTRAINT `fk_kolab_cache_freebusy_folder` FOREIGN KEY (`folder_id`)
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY(`folder_id`,`msguid`),
INDEX `freebusy_uid2msguid` (`folder_id`,`uid`,`msguid`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-INSERT INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2014021000');
+INSERT INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2015011600');
diff --git a/lib/plugins/libkolab/SQL/mysql/2014112700.sql b/lib/plugins/libkolab/SQL/mysql/2014112700.sql
new file mode 100644
index 0000000..90c77b8
--- /dev/null
+++ b/lib/plugins/libkolab/SQL/mysql/2014112700.sql
@@ -0,0 +1,2 @@
+-- delete cache entries for old folder identifiers
+DELETE FROM `kolab_folders` WHERE `resource` LIKE 'imap://anonymous@%';
diff --git a/lib/plugins/libkolab/SQL/mysql/2015011600.sql b/lib/plugins/libkolab/SQL/mysql/2015011600.sql
new file mode 100644
index 0000000..be523ae
--- /dev/null
+++ b/lib/plugins/libkolab/SQL/mysql/2015011600.sql
@@ -0,0 +1,8 @@
+ALTER TABLE `kolab_cache_contact` MODIFY `tags` text NOT NULL;
+ALTER TABLE `kolab_cache_event` MODIFY `tags` text NOT NULL;
+ALTER TABLE `kolab_cache_task` MODIFY `tags` text NOT NULL;
+ALTER TABLE `kolab_cache_journal` MODIFY `tags` text NOT NULL;
+ALTER TABLE `kolab_cache_note` MODIFY `tags` text NOT NULL;
+ALTER TABLE `kolab_cache_file` MODIFY `tags` text NOT NULL;
+ALTER TABLE `kolab_cache_configuration` MODIFY `tags` text NOT NULL;
+ALTER TABLE `kolab_cache_freebusy` MODIFY `tags` text NOT NULL;
diff --git a/lib/plugins/libkolab/SQL/oracle.initial.sql b/lib/plugins/libkolab/SQL/oracle.initial.sql
new file mode 100644
index 0000000..8f1ed64
--- /dev/null
+++ b/lib/plugins/libkolab/SQL/oracle.initial.sql
@@ -0,0 +1,184 @@
+/**
+ * libkolab database schema
+ *
+ * @version 1.1
+ * @author Aleksander Machniak
+ * @licence GNU AGPL
+ **/
+
+
+CREATE TABLE "kolab_folders" (
+ "folder_id" number NOT NULL PRIMARY KEY,
+ "resource" VARCHAR(255) NOT NULL,
+ "type" VARCHAR(32) NOT NULL,
+ "synclock" integer DEFAULT 0 NOT NULL,
+ "ctag" VARCHAR(40) DEFAULT NULL
+);
+
+CREATE INDEX "kolab_folders_resource_idx" ON "kolab_folders" ("resource", "type");
+
+CREATE SEQUENCE "kolab_folders_seq"
+ START WITH 1 INCREMENT BY 1 NOMAXVALUE;
+
+CREATE TRIGGER "kolab_folders_seq_trig"
+BEFORE INSERT ON "kolab_folders" FOR EACH ROW
+BEGIN
+ :NEW."folder_id" := "kolab_folders_seq".nextval;
+END;
+/
+
+CREATE TABLE "kolab_cache_contact" (
+ "folder_id" number NOT NULL
+ REFERENCES "kolab_folders" ("folder_id") ON DELETE CASCADE,
+ "msguid" number NOT NULL,
+ "uid" varchar(128) NOT NULL,
+ "created" timestamp DEFAULT NULL,
+ "changed" timestamp DEFAULT NULL,
+ "data" clob NOT NULL,
+ "xml" clob NOT NULL,
+ "tags" clob DEFAULT NULL,
+ "words" clob DEFAULT NULL,
+ "type" varchar(32) NOT NULL,
+ "name" varchar(255) DEFAULT NULL,
+ "firstname" varchar(255) DEFAULT NULL,
+ "surname" varchar(255) DEFAULT NULL,
+ "email" varchar(255) DEFAULT NULL,
+ PRIMARY KEY ("folder_id", "msguid")
+);
+
+CREATE INDEX "kolab_cache_contact_type_idx" ON "kolab_cache_contact" ("folder_id", "type");
+CREATE INDEX "kolab_cache_contact_uid2msguid" ON "kolab_cache_contact" ("folder_id", "uid", "msguid");
+
+
+CREATE TABLE "kolab_cache_event" (
+ "folder_id" number NOT NULL
+ REFERENCES "kolab_folders" ("folder_id") ON DELETE CASCADE,
+ "msguid" number NOT NULL,
+ "uid" varchar(128) NOT NULL,
+ "created" timestamp DEFAULT NULL,
+ "changed" timestamp DEFAULT NULL,
+ "data" clob NOT NULL,
+ "xml" clob NOT NULL,
+ "tags" clob DEFAULT NULL,
+ "words" clob DEFAULT NULL,
+ "dtstart" timestamp DEFAULT NULL,
+ "dtend" timestamp DEFAULT NULL,
+ PRIMARY KEY ("folder_id", "msguid")
+);
+
+CREATE INDEX "kolab_cache_event_uid2msguid" ON "kolab_cache_event" ("folder_id", "uid", "msguid");
+
+
+CREATE TABLE "kolab_cache_task" (
+ "folder_id" number NOT NULL
+ REFERENCES "kolab_folders" ("folder_id") ON DELETE CASCADE,
+ "msguid" number NOT NULL,
+ "uid" varchar(128) NOT NULL,
+ "created" timestamp DEFAULT NULL,
+ "changed" timestamp DEFAULT NULL,
+ "data" clob NOT NULL,
+ "xml" clob NOT NULL,
+ "tags" clob DEFAULT NULL,
+ "words" clob DEFAULT NULL,
+ "dtstart" timestamp DEFAULT NULL,
+ "dtend" timestamp DEFAULT NULL,
+ PRIMARY KEY ("folder_id", "msguid")
+);
+
+CREATE INDEX "kolab_cache_task_uid2msguid" ON "kolab_cache_task" ("folder_id", "uid", "msguid");
+
+
+CREATE TABLE "kolab_cache_journal" (
+ "folder_id" number NOT NULL
+ REFERENCES "kolab_folders" ("folder_id") ON DELETE CASCADE,
+ "msguid" number NOT NULL,
+ "uid" varchar(128) NOT NULL,
+ "created" timestamp DEFAULT NULL,
+ "changed" timestamp DEFAULT NULL,
+ "data" clob NOT NULL,
+ "xml" clob NOT NULL,
+ "tags" clob DEFAULT NULL,
+ "words" clob DEFAULT NULL,
+ "dtstart" timestamp DEFAULT NULL,
+ "dtend" timestamp DEFAULT NULL,
+ PRIMARY KEY ("folder_id", "msguid")
+);
+
+CREATE INDEX "kolab_cache_journal_uid2msguid" ON "kolab_cache_journal" ("folder_id", "uid", "msguid");
+
+
+CREATE TABLE "kolab_cache_note" (
+ "folder_id" number NOT NULL
+ REFERENCES "kolab_folders" ("folder_id") ON DELETE CASCADE,
+ "msguid" number NOT NULL,
+ "uid" varchar(128) NOT NULL,
+ "created" timestamp DEFAULT NULL,
+ "changed" timestamp DEFAULT NULL,
+ "data" clob NOT NULL,
+ "xml" clob NOT NULL,
+ "tags" clob DEFAULT NULL,
+ "words" clob DEFAULT NULL,
+ PRIMARY KEY ("folder_id", "msguid")
+);
+
+CREATE INDEX "kolab_cache_note_uid2msguid" ON "kolab_cache_note" ("folder_id", "uid", "msguid");
+
+
+CREATE TABLE "kolab_cache_file" (
+ "folder_id" number NOT NULL
+ REFERENCES "kolab_folders" ("folder_id") ON DELETE CASCADE,
+ "msguid" number NOT NULL,
+ "uid" varchar(128) NOT NULL,
+ "created" timestamp DEFAULT NULL,
+ "changed" timestamp DEFAULT NULL,
+ "data" clob NOT NULL,
+ "xml" clob NOT NULL,
+ "tags" clob DEFAULT NULL,
+ "words" clob DEFAULT NULL,
+ "filename" varchar(255) DEFAULT NULL,
+ PRIMARY KEY ("folder_id", "msguid")
+);
+
+CREATE INDEX "kolab_cache_file_filename" ON "kolab_cache_file" ("folder_id", "filename");
+CREATE INDEX "kolab_cache_file_uid2msguid" ON "kolab_cache_file" ("folder_id", "uid", "msguid");
+
+
+CREATE TABLE "kolab_cache_configuration" (
+ "folder_id" number NOT NULL
+ REFERENCES "kolab_folders" ("folder_id") ON DELETE CASCADE,
+ "msguid" number NOT NULL,
+ "uid" varchar(128) NOT NULL,
+ "created" timestamp DEFAULT NULL,
+ "changed" timestamp DEFAULT NULL,
+ "data" clob NOT NULL,
+ "xml" clob NOT NULL,
+ "tags" clob DEFAULT NULL,
+ "words" clob DEFAULT NULL,
+ "type" varchar(32) NOT NULL,
+ PRIMARY KEY ("folder_id", "msguid")
+);
+
+CREATE INDEX "kolab_cache_config_type" ON "kolab_cache_configuration" ("folder_id", "type");
+CREATE INDEX "kolab_cache_config_uid2msguid" ON "kolab_cache_configuration" ("folder_id", "uid", "msguid");
+
+
+CREATE TABLE "kolab_cache_freebusy" (
+ "folder_id" number NOT NULL
+ REFERENCES "kolab_folders" ("folder_id") ON DELETE CASCADE,
+ "msguid" number NOT NULL,
+ "uid" varchar(128) NOT NULL,
+ "created" timestamp DEFAULT NULL,
+ "changed" timestamp DEFAULT NULL,
+ "data" clob NOT NULL,
+ "xml" clob NOT NULL,
+ "tags" clob DEFAULT NULL,
+ "words" clob DEFAULT NULL,
+ "dtstart" timestamp DEFAULT NULL,
+ "dtend" timestamp DEFAULT NULL,
+ PRIMARY KEY("folder_id", "msguid")
+);
+
+CREATE INDEX "kolab_cache_fb_uid2msguid" ON "kolab_cache_freebusy" ("folder_id", "uid", "msguid");
+
+
+INSERT INTO "system" ("name", "value") VALUES ('libkolab-version', '2015011600');
diff --git a/lib/plugins/libkolab/SQL/oracle/2015011600.sql b/lib/plugins/libkolab/SQL/oracle/2015011600.sql
new file mode 100644
index 0000000..69f7953
--- /dev/null
+++ b/lib/plugins/libkolab/SQL/oracle/2015011600.sql
@@ -0,0 +1,40 @@
+-- direct change from varchar to clob does not work, need temp column (#4257)
+ALTER TABLE "kolab_cache_contact" ADD "tags1" clob DEFAULT NULL;
+UPDATE "kolab_cache_contact" SET "tags1" = "tags";
+ALTER TABLE "kolab_cache_contact" DROP COLUMN "tags";
+ALTER TABLE "kolab_cache_contact" RENAME COLUMN "tags1" TO "tags";
+
+ALTER TABLE "kolab_cache_event" ADD "tags1" clob DEFAULT NULL;
+UPDATE "kolab_cache_event" SET "tags1" = "tags";
+ALTER TABLE "kolab_cache_event" DROP COLUMN "tags";
+ALTER TABLE "kolab_cache_event" RENAME COLUMN "tags1" TO "tags";
+
+ALTER TABLE "kolab_cache_task" ADD "tags1" clob DEFAULT NULL;
+UPDATE "kolab_cache_task" SET "tags1" = "tags";
+ALTER TABLE "kolab_cache_task" DROP COLUMN "tags";
+ALTER TABLE "kolab_cache_task" RENAME COLUMN "tags1" TO "tags";
+
+ALTER TABLE "kolab_cache_journal" ADD "tags1" clob DEFAULT NULL;
+UPDATE "kolab_cache_journal" SET "tags1" = "tags";
+ALTER TABLE "kolab_cache_journal" DROP COLUMN "tags";
+ALTER TABLE "kolab_cache_journal" RENAME COLUMN "tags1" TO "tags";
+
+ALTER TABLE "kolab_cache_note" ADD "tags1" clob DEFAULT NULL;
+UPDATE "kolab_cache_note" SET "tags1" = "tags";
+ALTER TABLE "kolab_cache_note" DROP COLUMN "tags";
+ALTER TABLE "kolab_cache_note" RENAME COLUMN "tags1" TO "tags";
+
+ALTER TABLE "kolab_cache_file" ADD "tags1" clob DEFAULT NULL;
+UPDATE "kolab_cache_file" SET "tags1" = "tags";
+ALTER TABLE "kolab_cache_file" DROP COLUMN "tags";
+ALTER TABLE "kolab_cache_file" RENAME COLUMN "tags1" TO "tags";
+
+ALTER TABLE "kolab_cache_configuration" ADD "tags1" clob DEFAULT NULL;
+UPDATE "kolab_cache_configuration" SET "tags1" = "tags";
+ALTER TABLE "kolab_cache_configuration" DROP COLUMN "tags";
+ALTER TABLE "kolab_cache_configuration" RENAME COLUMN "tags1" TO "tags";
+
+ALTER TABLE "kolab_cache_freebusy" ADD "tags1" clob DEFAULT NULL;
+UPDATE "kolab_cache_freebusy" SET "tags1" = "tags";
+ALTER TABLE "kolab_cache_freebusy" DROP COLUMN "tags";
+ALTER TABLE "kolab_cache_freebusy" RENAME COLUMN "tags1" TO "tags";
diff --git a/lib/plugins/libkolab/bin/readcache.sh b/lib/plugins/libkolab/bin/readcache.sh
new file mode 100755
index 0000000..7e6a3a3
--- /dev/null
+++ b/lib/plugins/libkolab/bin/readcache.sh
@@ -0,0 +1,150 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * Kolab storage cache testing script
+ *
+ * @author Thomas Bruederli <bruederli@kolabsys.com>
+ *
+ * Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALL_PATH', realpath('.') . '/' );
+ini_set('display_errors', 1);
+libxml_use_internal_errors(true);
+
+if (!file_exists(INSTALL_PATH . 'program/include/clisetup.php'))
+ die("Execute this from the Roundcube installation dir!\n\n");
+
+require_once INSTALL_PATH . 'program/include/clisetup.php';
+
+function print_usage()
+{
+ print "Usage: readcache.sh [OPTIONS] FOLDER\n";
+ print "-h, --host IMAP host name\n";
+ print "-l, --limit Limit the number of records to be listed\n";
+}
+
+// read arguments
+$opts = get_opt(array(
+ 'h' => 'host',
+ 'l' => 'limit',
+ 'v' => 'verbose',
+));
+
+$folder = $opts[0];
+$imap_host = $opts['host'];
+
+$rcmail = rcube::get_instance(rcube::INIT_WITH_DB | rcube::INIT_WITH_PLUGINS);
+
+if (empty($imap_host)) {
+ $default_host = $rcmail->config->get('default_host');
+ if (is_array($default_host)) {
+ list($k,$v) = each($default_host);
+ $imap_host = is_numeric($k) ? $v : $k;
+ }
+ else {
+ $imap_host = $default_host;
+ }
+
+ // strip protocol prefix
+ $imap_host = preg_replace('!^[a-z]+://!', '', $imap_host);
+}
+
+if (empty($folder) || empty($imap_host)) {
+ print_usage();
+ exit;
+}
+
+// connect to database
+$db = $rcmail->get_dbh();
+$db->db_connect('r');
+if (!$db->is_connected() || $db->is_error())
+ die("No DB connection\n");
+
+
+// resolve folder_id
+if (!is_numeric($folder)) {
+ if (strpos($folder, '@')) {
+ list($mailbox, $domain) = explode('@', $folder);
+ list($username, $subpath) = explode('/', preg_replace('!^user/!', '', $mailbox), 2);
+ $folder_uri = 'imap://' . urlencode($username.'@'.$domain) . '@' . $imap_host . '/' . $subpath;
+ }
+ else {
+ die("Invalid mailbox identifier! Example: user/john.doe/Calendar@example.org\n");
+ }
+
+ print "Resolving folder $folder_uri...";
+ $sql_result = $db->query('SELECT * FROM `kolab_folders` WHERE `resource`=?', $folder_uri);
+ if ($sql_result && ($folder_data = $db->fetch_assoc($sql_result))) {
+ $folder_id = $folder_data['folder_id'];
+ print $folder_id;
+ }
+ print "\n";
+}
+else {
+ $folder_id = intval($folder);
+ $sql_result = $db->query('SELECT * FROM `kolab_folders` WHERE `folder_id`=?', $folder_id);
+ if ($sql_result) {
+ $folder_data = $db->fetch_assoc($sql_result);
+ }
+}
+
+if (empty($folder_data)) {
+ die("Can't find cache mailbox for '$folder'\n");
+}
+
+print "Querying cache for folder $folder_id ($folder_data[type])...\n";
+
+$extra_cols = array(
+ 'event' => array('dtstart','dtend'),
+ 'contact' => array('type'),
+);
+
+$cache_table = $db->table_name('kolab_cache_' . $folder_data['type']);
+$extra_cols_ = $extra_cols[$folder_data['type']] ?: array();
+$sql_arr = $db->fetch_assoc($db->query("SELECT COUNT(*) as cnt FROM `$cache_table` WHERE `folder_id`=?", intval($folder_id)));
+
+print "CTag = " . $folder_data['ctag'] . "\n";
+print "Lock = " . $folder_data['synclock'] . "\n";
+print "Count = " . $sql_arr['cnt'] . "\n";
+print "----------------------------------------------------------------------------------\n";
+print "<MSG>\t<UUID>\t<CHANGED>\t<DATA>\t<XML>\t";
+print join("\t", array_map(function($c) { return '<' . strtoupper($c) . '>'; }, $extra_cols_));
+print "\n----------------------------------------------------------------------------------\n";
+
+$result = $db->limitquery("SELECT * FROM `$cache_table` WHERE `folder_id`=?", 0, $opts['limit'], intval($folder_id));
+while ($result && ($sql_arr = $db->fetch_assoc($result))) {
+ print $sql_arr['msguid'] . "\t" . $sql_arr['uid'] . "\t" . $sql_arr['changed'];
+
+ // try to unserialize data block
+ $object = @unserialize(@base64_decode($sql_arr['data']));
+ print "\t" . ($object === false ? 'FAIL!' : ($object['uid'] == $sql_arr['uid'] ? 'OK' : '!!!'));
+
+ // check XML validity
+ $xml = simplexml_load_string($sql_arr['xml']);
+ print "\t" . ($xml === false ? 'FAIL!' : 'OK');
+
+ // print extra cols
+ array_walk($extra_cols_, function($c) use ($sql_arr) {
+ print "\t" . $sql_arr[$c];
+ });
+
+ print "\n";
+}
+
+print "----------------------------------------------------------------------------------\n";
+echo "Done.\n";
diff --git a/lib/plugins/libkolab/composer.json b/lib/plugins/libkolab/composer.json
index 8926037..b458df6 100644
--- a/lib/plugins/libkolab/composer.json
+++ b/lib/plugins/libkolab/composer.json
@@ -1,30 +1,30 @@
{
"name": "kolab/libkolab",
"type": "roundcube-plugin",
"description": "Plugin to setup a basic environment for the interaction with a Kolab server.",
"homepage": "http://git.kolab.org/roundcubemail-plugins-kolab/",
"license": "AGPLv3",
- "version": "1.1.0",
+ "version": "3.2.3",
"authors": [
{
"name": "Thomas Bruederli",
"email": "bruederli@kolabsys.com",
"role": "Lead"
},
{
"name": "Alensader Machniak",
"email": "machniak@kolabsys.com",
"role": "Developer"
}
],
"repositories": [
{
"type": "composer",
"url": "http://plugins.roundcube.net"
}
],
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3"
}
}
diff --git a/lib/plugins/libkolab/config.inc.php.dist b/lib/plugins/libkolab/config.inc.php.dist
index b043bb7..7efa8d1 100644
--- a/lib/plugins/libkolab/config.inc.php.dist
+++ b/lib/plugins/libkolab/config.inc.php.dist
@@ -1,61 +1,62 @@
<?php
/* Configuration for libkolab */
// Enable caching of Kolab objects in local database
-$rcmail_config['kolab_cache'] = true;
+$config['kolab_cache'] = true;
// Specify format version to write Kolab objects (must be a string value!)
-$rcmail_config['kolab_format_version'] = '3.0';
+$config['kolab_format_version'] = '3.0';
// Optional override of the URL to read and trigger Free/Busy information of Kolab users
// Defaults to https://<imap-server->/freebusy
-$rcmail_config['kolab_freebusy_server'] = 'https://<some-host>/<freebusy-path>';
+$config['kolab_freebusy_server'] = null;
// Enables listing of only subscribed folders. This e.g. will limit
// folders in calendar view or available addressbooks
-$rcmail_config['kolab_use_subscriptions'] = false;
+$config['kolab_use_subscriptions'] = false;
// List any of 'personal','shared','other' namespaces to be excluded from groupware folder listing
// example: array('other');
-$rcmail_config['kolab_skip_namespace'] = null;
+$config['kolab_skip_namespace'] = null;
// Enables the use of displayname folder annotations as introduced in KEP:?
// for displaying resource folder names (experimental!)
-$rcmail_config['kolab_custom_display_names'] = false;
+$config['kolab_custom_display_names'] = false;
// Configuration of HTTP requests.
// See http://pear.php.net/manual/en/package.http.http-request2.config.php
// for list of supported configuration options (array keys)
-$rcmail_config['kolab_http_request'] = array();
+$config['kolab_http_request'] = array();
// When kolab_cache is enabled Roundcube's messages cache will be redundant
// when working on kolab folders. Here we can:
// 2 - bypass messages/indexes cache completely
// 1 - bypass only messages, but use index cache
-$rcmail_config['kolab_messages_cache_bypass'] = 0;
+$config['kolab_messages_cache_bypass'] = 0;
// LDAP directory to find avilable users for folder sharing.
// Either contains an array with LDAP addressbook configuration or refers to entry in $config['ldap_public'].
// If not specified, the configuraton from 'kolab_auth_addressbook' will be used.
-$rcmail_config['kolab_users_directory'] = null;
+// Should be provided for multi-domain setups with placeholders like %dc, %d, %u, %fu or %dn.
+$config['kolab_users_directory'] = null;
// Filter to be used for resolving user folders in LDAP.
// Defaults to the 'kolab_auth_filter' configuration option.
-$rcmail_config['kolab_users_filter'] = '(&(objectclass=kolabInetOrgPerson)(|(uid=%u)(mail=%fu)))';
+$config['kolab_users_filter'] = '(&(objectclass=kolabInetOrgPerson)(|(uid=%u)(mail=%fu)))';
// Which property of the LDAP user record to use for user folder mapping in IMAP.
// Defaults to the 'kolab_auth_login' configuration option.
-$rcmail_config['kolab_users_id_attrib'] = null;
+$config['kolab_users_id_attrib'] = null;
// Use these attributes when searching users in LDAP
-$rcmail_config['kolab_users_search_attrib'] = array('cn','mail','alias');
+$config['kolab_users_search_attrib'] = array('cn','mail','alias');
// JSON-RPC endpoint configuration of the Bonnie web service providing historic data for groupware objects
-$rcmail_config['kolab_bonnie_api'] = array(
+$config['kolab_bonnie_api'] = array(
'uri' => 'https://<kolab-hostname>:8080/api/rpc',
'user' => 'webclient',
'pass' => 'Welcome2KolabSystems',
'secret' => '8431f191707fffffff00000000cccc',
'debug' => true, // logs requests/responses to <log-dir>/bonnie
);
diff --git a/lib/plugins/libkolab/js/folderlist.js b/lib/plugins/libkolab/js/folderlist.js
index 1c8ce2f..62a60ef 100644
--- a/lib/plugins/libkolab/js/folderlist.js
+++ b/lib/plugins/libkolab/js/folderlist.js
@@ -1,264 +1,350 @@
/**
* Kolab groupware folders treelist widget
*
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* @licstart The following is the entire license notice for the
* JavaScript code in this file.
*
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @licend The above is the entire license notice
* for the JavaScript code in this file.
*/
function kolab_folderlist(node, p)
{
// extends treelist.js
rcube_treelist_widget.call(this, node, p);
// private vars
var me = this;
var search_results;
var search_results_widget;
var search_results_container;
var listsearch_request;
var search_messagebox;
var Q = rcmail.quote_html;
// render the results for folderlist search
function render_search_results(results)
{
if (results.length) {
// create treelist widget to present the search results
if (!search_results_widget) {
var list_id = (me.container.attr('id') || p.id_prefix || '0')
search_results_container = $('<div class="searchresults"></div>')
.html(p.search_title ? '<h2 class="boxtitle" id="st:' + list_id + '">' + p.search_title + '</h2>' : '')
.insertAfter(me.container);
search_results_widget = new rcube_treelist_widget('<ul>', {
id_prefix: p.id_prefix,
id_encode: p.id_encode,
id_decode: p.id_decode,
selectable: false
});
// copy classes from main list
search_results_widget.container.addClass(me.container.attr('class')).attr('aria-labelledby', 'st:' + list_id);
// register click handler on search result's checkboxes to select the given item for listing
search_results_widget.container
.appendTo(search_results_container)
.on('click', 'input[type=checkbox], a.subscribed, span.subscribed', function(e) {
var node, has_children, li = $(this).closest('li'),
id = li.attr('id').replace(new RegExp('^'+p.id_prefix), '');
if (p.id_decode)
id = p.id_decode(id);
- node = search_results_widget.get_node(id),
+ node = search_results_widget.get_node(id);
has_children = node.children && node.children.length;
e.stopPropagation();
e.bubbles = false;
// activate + subscribe
if ($(e.target).hasClass('subscribed')) {
search_results[id].subscribed = true;
$(e.target).attr('aria-checked', 'true');
li.children().first()
.toggleClass('subscribed')
.find('input[type=checkbox]').get(0).checked = true;
+
+ if (has_children && search_results[id].group == 'other user') {
+ li.find('ul li > div').addClass('subscribed')
+ .find('a.subscribed').attr('aria-checked', 'true');;
+ }
}
else if (!this.checked) {
return;
}
// copy item to the main list
add_result2list(id, li, true);
if (has_children) {
li.find('input[type=checkbox]').first().prop('disabled', true).prop('checked', true);
li.find('a.subscribed, span.subscribed').first().hide();
}
else {
li.remove();
}
+ // set partial subscription status
+ if (search_results[id].subscribed && search_results[id].parent && search_results[id].group == 'other') {
+ parent_subscription_status($(me.get_item(id, true)));
+ }
+
// set focus to cloned checkbox
if (rcube_event.is_keyboard(e)) {
$(me.get_item(id, true)).find('input[type=checkbox]').first().focus();
}
})
.on('click', function(e) {
var prop, id = String($(e.target).closest('li').attr('id')).replace(new RegExp('^'+p.id_prefix), '');
if (p.id_decode)
id = p.id_decode(id);
// forward event
if (prop = search_results[id]) {
e.data = prop;
if (me.triggerEvent('click-item', e) === false) {
e.stopPropagation();
return false;
}
}
});
}
// add results to list
for (var prop, item, i=0; i < results.length; i++) {
prop = results[i];
item = $(prop.html);
search_results[prop.id] = prop;
search_results_widget.insert({
id: prop.id,
classes: [ prop.group || '' ],
html: item,
collapsed: true,
virtual: prop.virtual
}, prop.parent);
// disable checkbox if item already exists in main list
if (me.get_node(prop.id) && !me.get_node(prop.id).virtual) {
item.find('input[type=checkbox]').first().prop('disabled', true).prop('checked', true);
item.find('a.subscribed, span.subscribed').hide();
}
}
search_results_container.show();
}
}
// helper method to (recursively) add a search result item to the main list widget
function add_result2list(id, li, active)
{
var node = search_results_widget.get_node(id),
prop = search_results[id],
parent_id = prop.parent || null,
has_children = node.children && node.children.length,
- dom_node = has_children ? li.children().first().clone(true, true) : li.children().first();
+ dom_node = has_children ? li.children().first().clone(true, true) : li.children().first(),
+ childs = [];
// find parent node and insert at the right place
if (parent_id && me.get_node(parent_id)) {
dom_node.children('span,a').first().html(Q(prop.editname || prop.listname));
}
else if (parent_id && search_results[parent_id]) {
// copy parent tree from search results
add_result2list(parent_id, $(search_results_widget.get_item(parent_id)), false);
}
else if (parent_id) {
// use full name for list display
dom_node.children('span,a').first().html(Q(prop.name));
}
// replace virtual node with a real one
if (me.get_node(id)) {
$(me.get_item(id, true)).children().first()
.replaceWith(dom_node)
.removeClass('virtual');
}
else {
+ // copy childs, too
+ if (has_children && prop.group == 'other user') {
+ for (var cid, j=0; j < node.children.length; j++) {
+ if ((cid = node.children[j].id) && search_results[cid]) {
+ childs.push(search_results_widget.get_node(cid));
+ }
+ }
+ }
+
// move this result item to the main list widget
me.insert({
id: id,
classes: [ prop.group || '' ],
virtual: prop.virtual,
html: dom_node,
+ level: node.level,
+ collapsed: true,
+ children: childs
}, parent_id, prop.group);
}
delete prop.html;
prop.active = active;
me.triggerEvent('insert-item', { id: id, data: prop, item: li });
+
+ // register childs, too
+ if (childs.length) {
+ for (var cid, j=0; j < node.children.length; j++) {
+ if ((cid = node.children[j].id) && search_results[cid]) {
+ prop = search_results[cid];
+ delete prop.html;
+ prop.active = false;
+ me.triggerEvent('insert-item', { id: cid, data: prop });
+ }
+ }
+ }
+ }
+
+ // update the given item's parent's (partial) subscription state
+ function parent_subscription_status(li)
+ {
+ var top_li = li.closest(me.container.children('li')),
+ all_childs = $('li > div:not(.treetoggle)', top_li),
+ subscribed = all_childs.filter('.subscribed').length;
+
+ if (subscribed == 0) {
+ top_li.children('div:first').removeClass('subscribed partial');
+ }
+ else {
+ top_li.children('div:first')
+ .addClass('subscribed')[subscribed < all_childs.length ? 'addClass' : 'removeClass']('partial');
+ }
}
// do some magic when search is performed on the widget
this.addEventListener('search', function(search) {
// hide search results
if (search_results_widget) {
search_results_container.hide();
search_results_widget.reset();
}
search_results = {};
if (search_messagebox)
rcmail.hide_message(search_messagebox);
// send search request(s) to server
if (search.query && search.execute) {
// require a minimum length for the search string
if (rcmail.env.autocomplete_min_length && search.query.length < rcmail.env.autocomplete_min_length && search.query != '*') {
search_messagebox = rcmail.display_message(
rcmail.get_label('autocompletechars').replace('$min', rcmail.env.autocomplete_min_length));
return;
}
if (listsearch_request) {
// ignore, let the currently running request finish
if (listsearch_request.query == search.query) {
return;
}
else { // cancel previous search request
rcmail.multi_thread_request_abort(listsearch_request.id);
listsearch_request = null;
}
}
var sources = p.search_sources || [ 'folders' ];
var reqid = rcmail.multi_thread_http_request({
items: sources,
threads: rcmail.env.autocomplete_threads || 1,
action: p.search_action || 'listsearch',
postdata: { action:'search', q:search.query, source:'%s' },
lock: rcmail.display_message(rcmail.get_label('searching'), 'loading'),
onresponse: render_search_results,
whendone: function(data){
listsearch_request = null;
me.triggerEvent('search-complete', data);
}
});
listsearch_request = { id:reqid, query:search.query };
}
else if (!search.query && listsearch_request) {
rcmail.multi_thread_request_abort(listsearch_request.id);
listsearch_request = null;
}
});
- this.container.on('click', 'a.subscribed, span.subscribed', function(e){
+ this.container.on('click', 'a.subscribed, span.subscribed', function(e) {
var li = $(this).closest('li'),
id = li.attr('id').replace(new RegExp('^'+p.id_prefix), ''),
- div = li.children().first();
+ div = li.children().first(),
+ is_subscribed;
- if (me.is_search())
- id = id.replace(/--xsR$/, '');
+ if (me.is_search()) {
+ id = id.replace(/--xsR$/, '');
+ li = $(me.get_item(id, true));
+ div = $(div).add(li.children().first());
+ }
if (p.id_decode)
id = p.id_decode(id);
div.toggleClass('subscribed');
- $(this).attr('aria-checked', div.hasClass('subscribed') ? 'true' : 'false');
- me.triggerEvent('subscribe', { id: id, subscribed: div.hasClass('subscribed'), item: li });
+ is_subscribed = div.hasClass('subscribed');
+ $(this).attr('aria-checked', is_subscribed ? 'true' : 'false');
+ me.triggerEvent('subscribe', { id: id, subscribed: is_subscribed, item: li });
+
+ // update subscribe state of all 'virtual user' child folders
+ if (li.hasClass('other user')) {
+ $('ul li > div', li).each(function() {
+ $(this)[is_subscribed ? 'addClass' : 'removeClass']('subscribed');
+ $('.subscribed', div).attr('aria-checked', is_subscribed ? 'true' : 'false');
+ });
+ div.removeClass('partial');
+ }
+ // propagate subscription state to parent 'virtual user' folder
+ else if (li.closest('li.other.user').length) {
+ parent_subscription_status(li);
+ }
e.stopPropagation();
return false;
- })
+ });
+
+ this.container.on('click', 'a.remove', function(e) {
+ var li = $(this).closest('li'),
+ id = li.attr('id').replace(new RegExp('^'+p.id_prefix), '');
+
+ if (me.is_search()) {
+ id = id.replace(/--xsR$/, '');
+ li = $(me.get_item(id, true));
+ }
+ if (p.id_decode)
+ id = p.id_decode(id);
+
+ me.triggerEvent('remove', { id: id, item: li });
+
+ e.stopPropagation();
+ return false;
+ });
}
// link prototype from base class
kolab_folderlist.prototype = rcube_treelist_widget.prototype;
diff --git a/lib/plugins/libkolab/lib/kolab_bonnie_api.php b/lib/plugins/libkolab/lib/kolab_bonnie_api.php
index 23dafd8..e8ac131 100644
--- a/lib/plugins/libkolab/lib/kolab_bonnie_api.php
+++ b/lib/plugins/libkolab/lib/kolab_bonnie_api.php
@@ -1,82 +1,82 @@
<?php
/**
* Provider class for accessing historic groupware object data through the Bonnie service
*
* API Specification at https://wiki.kolabsys.com/User:Bruederli/Draft:Bonnie_Client_API
*
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_bonnie_api
{
public $ready = false;
private $config = array();
private $client = null;
/**
* Default constructor
*/
public function __construct($config)
{
- $this->config = $confg;
+ $this->config = $config;
$this->client = new kolab_bonnie_api_client($config['uri'], $config['timeout'] ?: 5, (bool)$config['debug']);
$this->client->set_secret($config['secret']);
$this->client->set_authentication($config['user'], $config['pass']);
$this->client->set_request_user(rcube::get_instance()->get_user_name());
$this->ready = !empty($config['secret']) && !empty($config['user']) && !empty($config['pass']);
}
/**
* Wrapper function for <object>.changelog() API call
*/
public function changelog($type, $uid, $mailbox=null)
{
return $this->client->execute($type.'.changelog', array('uid' => $uid, 'mailbox' => $mailbox));
}
/**
* Wrapper function for <object>.diff() API call
*/
public function diff($type, $uid, $rev, $mailbox=null)
{
return $this->client->execute($type.'.diff', array('uid' => $uid, 'rev' => $rev, 'mailbox' => $mailbox));
}
/**
* Wrapper function for <object>.get() API call
*/
public function get($type, $uid, $rev, $mailbox=null)
{
return $this->client->execute($type.'.get', array('uid' => $uid, 'rev' => intval($rev), 'mailbox' => $mailbox));
}
/**
* Generic wrapper for direct API calls
*/
public function _execute($method, $params = array())
{
return $this->client->execute($method, $params);
}
}
\ No newline at end of file
diff --git a/lib/plugins/libkolab/lib/kolab_format.php b/lib/plugins/libkolab/lib/kolab_format.php
index ae7705c..625483b 100644
--- a/lib/plugins/libkolab/lib/kolab_format.php
+++ b/lib/plugins/libkolab/lib/kolab_format.php
@@ -1,583 +1,699 @@
<?php
/**
* Kolab format model class wrapping libkolabxml bindings
*
* Abstract base class for different Kolab groupware objects read from/written
* to the new Kolab 3 format using the PHP bindings of libkolabxml.
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
abstract class kolab_format
{
public static $timezone;
public /*abstract*/ $CTYPE;
public /*abstract*/ $CTYPEv2;
protected /*abstract*/ $objclass;
protected /*abstract*/ $read_func;
protected /*abstract*/ $write_func;
protected $obj;
protected $data;
protected $xmldata;
protected $xmlobject;
protected $formaterror;
protected $loaded = false;
protected $version = '3.0';
const KTYPE_PREFIX = 'application/x-vnd.kolab.';
const PRODUCT_ID = 'Roundcube-libkolab-1.1';
+ // mapping table for valid PHP timezones not supported by libkolabxml
+ // basically the entire list of ftp://ftp.iana.org/tz/data/backward
+ protected static $timezone_map = array(
+ 'Africa/Asmera' => 'Africa/Asmara',
+ 'Africa/Timbuktu' => 'Africa/Abidjan',
+ 'America/Argentina/ComodRivadavia' => 'America/Argentina/Catamarca',
+ 'America/Atka' => 'America/Adak',
+ 'America/Buenos_Aires' => 'America/Argentina/Buenos_Aires',
+ 'America/Catamarca' => 'America/Argentina/Catamarca',
+ 'America/Coral_Harbour' => 'America/Atikokan',
+ 'America/Cordoba' => 'America/Argentina/Cordoba',
+ 'America/Ensenada' => 'America/Tijuana',
+ 'America/Fort_Wayne' => 'America/Indiana/Indianapolis',
+ 'America/Indianapolis' => 'America/Indiana/Indianapolis',
+ 'America/Jujuy' => 'America/Argentina/Jujuy',
+ 'America/Knox_IN' => 'America/Indiana/Knox',
+ 'America/Louisville' => 'America/Kentucky/Louisville',
+ 'America/Mendoza' => 'America/Argentina/Mendoza',
+ 'America/Porto_Acre' => 'America/Rio_Branco',
+ 'America/Rosario' => 'America/Argentina/Cordoba',
+ 'America/Virgin' => 'America/Port_of_Spain',
+ 'Asia/Ashkhabad' => 'Asia/Ashgabat',
+ 'Asia/Calcutta' => 'Asia/Kolkata',
+ 'Asia/Chungking' => 'Asia/Shanghai',
+ 'Asia/Dacca' => 'Asia/Dhaka',
+ 'Asia/Katmandu' => 'Asia/Kathmandu',
+ 'Asia/Macao' => 'Asia/Macau',
+ 'Asia/Saigon' => 'Asia/Ho_Chi_Minh',
+ 'Asia/Tel_Aviv' => 'Asia/Jerusalem',
+ 'Asia/Thimbu' => 'Asia/Thimphu',
+ 'Asia/Ujung_Pandang' => 'Asia/Makassar',
+ 'Asia/Ulan_Bator' => 'Asia/Ulaanbaatar',
+ 'Atlantic/Faeroe' => 'Atlantic/Faroe',
+ 'Atlantic/Jan_Mayen' => 'Europe/Oslo',
+ 'Australia/ACT' => 'Australia/Sydney',
+ 'Australia/Canberra' => 'Australia/Sydney',
+ 'Australia/LHI' => 'Australia/Lord_Howe',
+ 'Australia/NSW' => 'Australia/Sydney',
+ 'Australia/North' => 'Australia/Darwin',
+ 'Australia/Queensland' => 'Australia/Brisbane',
+ 'Australia/South' => 'Australia/Adelaide',
+ 'Australia/Tasmania' => 'Australia/Hobart',
+ 'Australia/Victoria' => 'Australia/Melbourne',
+ 'Australia/West' => 'Australia/Perth',
+ 'Australia/Yancowinna' => 'Australia/Broken_Hill',
+ 'Brazil/Acre' => 'America/Rio_Branco',
+ 'Brazil/DeNoronha' => 'America/Noronha',
+ 'Brazil/East' => 'America/Sao_Paulo',
+ 'Brazil/West' => 'America/Manaus',
+ 'Canada/Atlantic' => 'America/Halifax',
+ 'Canada/Central' => 'America/Winnipeg',
+ 'Canada/East-Saskatchewan' => 'America/Regina',
+ 'Canada/Eastern' => 'America/Toronto',
+ 'Canada/Mountain' => 'America/Edmonton',
+ 'Canada/Newfoundland' => 'America/St_Johns',
+ 'Canada/Pacific' => 'America/Vancouver',
+ 'Canada/Saskatchewan' => 'America/Regina',
+ 'Canada/Yukon' => 'America/Whitehorse',
+ 'Chile/Continental' => 'America/Santiago',
+ 'Chile/EasterIsland' => 'Pacific/Easter',
+ 'Cuba' => 'America/Havana',
+ 'Egypt' => 'Africa/Cairo',
+ 'Eire' => 'Europe/Dublin',
+ 'Europe/Belfast' => 'Europe/London',
+ 'Europe/Tiraspol' => 'Europe/Chisinau',
+ 'GB' => 'Europe/London',
+ 'GB-Eire' => 'Europe/London',
+ 'Greenwich' => 'Etc/GMT',
+ 'Hongkong' => 'Asia/Hong_Kong',
+ 'Iceland' => 'Atlantic/Reykjavik',
+ 'Iran' => 'Asia/Tehran',
+ 'Israel' => 'Asia/Jerusalem',
+ 'Jamaica' => 'America/Jamaica',
+ 'Japan' => 'Asia/Tokyo',
+ 'Kwajalein' => 'Pacific/Kwajalein',
+ 'Libya' => 'Africa/Tripoli',
+ 'Mexico/BajaNorte' => 'America/Tijuana',
+ 'Mexico/BajaSur' => 'America/Mazatlan',
+ 'Mexico/General' => 'America/Mexico_City',
+ 'NZ' => 'Pacific/Auckland',
+ 'NZ-CHAT' => 'Pacific/Chatham',
+ 'Navajo' => 'America/Denver',
+ 'PRC' => 'Asia/Shanghai',
+ 'Pacific/Ponape' => 'Pacific/Pohnpei',
+ 'Pacific/Samoa' => 'Pacific/Pago_Pago',
+ 'Pacific/Truk' => 'Pacific/Chuuk',
+ 'Pacific/Yap' => 'Pacific/Chuuk',
+ 'Poland' => 'Europe/Warsaw',
+ 'Portugal' => 'Europe/Lisbon',
+ 'ROC' => 'Asia/Taipei',
+ 'ROK' => 'Asia/Seoul',
+ 'Singapore' => 'Asia/Singapore',
+ 'Turkey' => 'Europe/Istanbul',
+ 'UCT' => 'Etc/UCT',
+ 'US/Alaska' => 'America/Anchorage',
+ 'US/Aleutian' => 'America/Adak',
+ 'US/Arizona' => 'America/Phoenix',
+ 'US/Central' => 'America/Chicago',
+ 'US/East-Indiana' => 'America/Indiana/Indianapolis',
+ 'US/Eastern' => 'America/New_York',
+ 'US/Hawaii' => 'Pacific/Honolulu',
+ 'US/Indiana-Starke' => 'America/Indiana/Knox',
+ 'US/Michigan' => 'America/Detroit',
+ 'US/Mountain' => 'America/Denver',
+ 'US/Pacific' => 'America/Los_Angeles',
+ 'US/Samoa' => 'Pacific/Pago_Pago',
+ 'Universal' => 'Etc/UTC',
+ 'W-SU' => 'Europe/Moscow',
+ 'Zulu' => 'Etc/UTC',
+ );
+
/**
* Factory method to instantiate a kolab_format object of the given type and version
*
* @param string Object type to instantiate
* @param float Format version
* @param string Cached xml data to initialize with
* @return object kolab_format
*/
public static function factory($type, $version = '3.0', $xmldata = null)
{
if (!isset(self::$timezone))
self::$timezone = new DateTimeZone('UTC');
if (!self::supports($version))
return PEAR::raiseError("No support for Kolab format version " . $version);
- $type = preg_replace('/configuration\.[a-z.]+$/', 'configuration', $type);
+ $type = preg_replace('/configuration\.[a-z._]+$/', 'configuration', $type);
$suffix = preg_replace('/[^a-z]+/', '', $type);
$classname = 'kolab_format_' . $suffix;
if (class_exists($classname))
return new $classname($xmldata, $version);
return PEAR::raiseError("Failed to load Kolab Format wrapper for type " . $type);
}
/**
* Determine support for the given format version
*
* @param float Format version to check
* @return boolean True if supported, False otherwise
*/
public static function supports($version)
{
if ($version == '2.0')
return class_exists('kolabobject');
// default is version 3
return class_exists('kolabformat');
}
/**
* Convert the given date/time value into a cDateTime object
*
* @param mixed Date/Time value either as unix timestamp, date string or PHP DateTime object
* @param DateTimeZone The timezone the date/time is in. Use global default if Null, local time if False
* @param boolean True of the given date has no time component
* @return object The libkolabxml date/time object
*/
public static function get_datetime($datetime, $tz = null, $dateonly = false)
{
// use timezone information from datetime of global setting
if (!$tz && $tz !== false) {
if ($datetime instanceof DateTime)
$tz = $datetime->getTimezone();
if (!$tz)
$tz = self::$timezone;
}
$result = new cDateTime();
try {
// got a unix timestamp (in UTC)
if (is_numeric($datetime)) {
$datetime = new DateTime('@'.$datetime, new DateTimeZone('UTC'));
if ($tz) $datetime->setTimezone($tz);
}
else if (is_string($datetime) && strlen($datetime)) {
- $datetime = new DateTime($datetime, $tz ?: null);
+ $datetime = $tz ? new DateTime($datetime, $tz) : new DateTime($datetime);
}
}
catch (Exception $e) {}
if ($datetime instanceof DateTime) {
$result->setDate($datetime->format('Y'), $datetime->format('n'), $datetime->format('j'));
if (!$dateonly)
$result->setTime($datetime->format('G'), $datetime->format('i'), $datetime->format('s'));
- if ($tz && in_array($tz->getName(), array('UTC', 'GMT', '+00:00', 'Z')))
+ if ($tz && in_array($tz->getName(), array('UTC', 'GMT', '+00:00', 'Z'))) {
$result->setUTC(true);
- else if ($tz !== false)
- $result->setTimezone($tz->getName());
+ }
+ else if ($tz !== false) {
+ $tzid = $tz->getName();
+ if (array_key_exists($tzid, self::$timezone_map))
+ $tzid = self::$timezone_map[$tzid];
+ $result->setTimezone($tzid);
+ }
}
return $result;
}
/**
* Convert the given cDateTime into a PHP DateTime object
*
* @param object cDateTime The libkolabxml datetime object
* @return object DateTime PHP datetime instance
*/
public static function php_datetime($cdt)
{
if (!is_object($cdt) || !$cdt->isValid())
return null;
$d = new DateTime;
$d->setTimezone(self::$timezone);
try {
if ($tzs = $cdt->timezone()) {
$tz = new DateTimeZone($tzs);
$d->setTimezone($tz);
}
else if ($cdt->isUTC()) {
$d->setTimezone(new DateTimeZone('UTC'));
}
}
catch (Exception $e) { }
$d->setDate($cdt->year(), $cdt->month(), $cdt->day());
if ($cdt->isDateOnly()) {
$d->_dateonly = true;
$d->setTime(12, 0, 0); // set time to noon to avoid timezone troubles
}
else {
$d->setTime($cdt->hour(), $cdt->minute(), $cdt->second());
}
return $d;
}
/**
* Convert a libkolabxml vector to a PHP array
*
* @param object vector Object
* @return array Indexed array containing vector elements
*/
public static function vector2array($vec, $max = PHP_INT_MAX)
{
$arr = array();
for ($i=0; $i < $vec->size() && $i < $max; $i++)
$arr[] = $vec->get($i);
return $arr;
}
/**
* Build a libkolabxml vector (string) from a PHP array
*
* @param array Array with vector elements
* @return object vectors
*/
public static function array2vector($arr)
{
$vec = new vectors;
foreach ((array)$arr as $val) {
if (strlen($val))
$vec->push($val);
}
return $vec;
}
/**
* Parse the X-Kolab-Type header from MIME messages and return the object type in short form
*
* @param string X-Kolab-Type header value
* @return string Kolab object type (contact,event,task,note,etc.)
*/
public static function mime2object_type($x_kolab_type)
{
return preg_replace(
array('/dictionary.[a-z.]+$/', '/contact.distlist$/'),
array( 'dictionary', 'distribution-list'),
substr($x_kolab_type, strlen(self::KTYPE_PREFIX))
);
}
/**
* Default constructor of all kolab_format_* objects
*/
public function __construct($xmldata = null, $version = null)
{
$this->obj = new $this->objclass;
$this->xmldata = $xmldata;
if ($version)
$this->version = $version;
// use libkolab module if available
if (class_exists('kolabobject'))
$this->xmlobject = new XMLObject();
}
/**
* Check for format errors after calling kolabformat::write*()
*
* @return boolean True if there were errors, False if OK
*/
protected function format_errors()
{
$ret = $log = false;
switch (kolabformat::error()) {
case kolabformat::NoError:
$ret = false;
break;
case kolabformat::Warning:
$ret = false;
$uid = is_object($this->obj) ? $this->obj->uid() : $this->data['uid'];
$log = "Warning @ $uid";
break;
default:
$ret = true;
$log = "Error";
}
if ($log && !isset($this->formaterror)) {
rcube::raise_error(array(
'code' => 660,
'type' => 'php',
'file' => __FILE__,
'line' => __LINE__,
'message' => "kolabformat $log: " . kolabformat::errorMessage(),
), true);
$this->formaterror = $ret;
}
return $ret;
}
/**
* Save the last generated UID to the object properties.
* Should be called after kolabformat::writeXXXX();
*/
protected function update_uid()
{
// get generated UID
if (!$this->data['uid']) {
if ($this->xmlobject) {
$this->data['uid'] = $this->xmlobject->getSerializedUID();
}
if (empty($this->data['uid'])) {
$this->data['uid'] = kolabformat::getSerializedUID();
}
$this->obj->setUid($this->data['uid']);
}
}
/**
* Initialize libkolabxml object with cached xml data
*/
protected function init()
{
if (!$this->loaded) {
if ($this->xmldata) {
$this->load($this->xmldata);
$this->xmldata = null;
}
$this->loaded = true;
}
}
/**
* Get constant value for libkolab's version parameter
*
* @param float Version value to convert
* @return int Constant value of either kolabobject::KolabV2 or kolabobject::KolabV3 or false if kolabobject module isn't available
*/
protected function libversion($v = null)
{
if (class_exists('kolabobject')) {
$version = $v ?: $this->version;
if ($version <= '2.0')
return kolabobject::KolabV2;
else
return kolabobject::KolabV3;
}
return false;
}
/**
* Determine the correct libkolab(xml) wrapper function for the given call
* depending on the available PHP modules
*/
protected function libfunc($func)
{
if (is_array($func) || strpos($func, '::'))
return $func;
else if (class_exists('kolabobject'))
return array($this->xmlobject, $func);
else
return 'kolabformat::' . $func;
}
/**
* Direct getter for object properties
*/
public function __get($var)
{
return $this->data[$var];
}
/**
* Load Kolab object data from the given XML block
*
* @param string XML data
* @return boolean True on success, False on failure
*/
public function load($xml)
{
$this->formaterror = null;
$read_func = $this->libfunc($this->read_func);
if (is_array($read_func))
$r = call_user_func($read_func, $xml, $this->libversion());
else
$r = call_user_func($read_func, $xml, false);
if (is_resource($r))
$this->obj = new $this->objclass($r);
else if (is_a($r, $this->objclass))
$this->obj = $r;
$this->loaded = !$this->format_errors();
}
/**
* Write object data to XML format
*
* @param float Format version to write
* @return string XML data
*/
public function write($version = null)
{
$this->formaterror = null;
$this->init();
$write_func = $this->libfunc($this->write_func);
if (is_array($write_func))
$this->xmldata = call_user_func($write_func, $this->obj, $this->libversion($version), self::PRODUCT_ID);
else
$this->xmldata = call_user_func($write_func, $this->obj, self::PRODUCT_ID);
if (!$this->format_errors())
$this->update_uid();
else
$this->xmldata = null;
return $this->xmldata;
}
/**
* Set properties to the kolabformat object
*
* @param array Object data as hash array
*/
public function set(&$object)
{
$this->init();
if (!empty($object['uid']))
$this->obj->setUid($object['uid']);
// set some automatic values if missing
if (empty($object['created']) && method_exists($this->obj, 'setCreated')) {
$cdt = $this->obj->created();
$object['created'] = $cdt && $cdt->isValid() ? self::php_datetime($cdt) : new DateTime('now', new DateTimeZone('UTC'));
if (!$cdt || !$cdt->isValid())
$this->obj->setCreated(self::get_datetime($object['created']));
}
$object['changed'] = new DateTime('now', new DateTimeZone('UTC'));
$this->obj->setLastModified(self::get_datetime($object['changed']));
// Save custom properties of the given object
if (isset($object['x-custom']) && method_exists($this->obj, 'setCustomProperties')) {
$vcustom = new vectorcs;
foreach ((array)$object['x-custom'] as $cp) {
if (is_array($cp))
$vcustom->push(new CustomProperty($cp[0], $cp[1]));
}
$this->obj->setCustomProperties($vcustom);
}
// load custom properties from XML for caching (#2238) if method exists (#3125)
else if (method_exists($this->obj, 'customProperties')) {
$object['x-custom'] = array();
$vcustom = $this->obj->customProperties();
for ($i=0; $i < $vcustom->size(); $i++) {
$cp = $vcustom->get($i);
$object['x-custom'][] = array($cp->identifier, $cp->value);
}
}
}
/**
* Convert the Kolab object into a hash array data structure
*
* @param array Additional data for merge
*
* @return array Kolab object data as hash array
*/
public function to_array($data = array())
{
$this->init();
// read object properties into local data object
$object = array(
'uid' => $this->obj->uid(),
'changed' => self::php_datetime($this->obj->lastModified()),
);
// not all container support the created property
if (method_exists($this->obj, 'created')) {
$object['created'] = self::php_datetime($this->obj->created());
}
// read custom properties
if (method_exists($this->obj, 'customProperties')) {
$vcustom = $this->obj->customProperties();
for ($i=0; $i < $vcustom->size(); $i++) {
$cp = $vcustom->get($i);
$object['x-custom'][] = array($cp->identifier, $cp->value);
}
}
// merge with additional data, e.g. attachments from the message
if ($data) {
foreach ($data as $idx => $value) {
if (is_array($value)) {
$object[$idx] = array_merge((array)$object[$idx], $value);
}
else {
$object[$idx] = $value;
}
}
}
return $object;
}
/**
* Object validation method to be implemented by derived classes
*/
abstract public function is_valid();
/**
* Callback for kolab_storage_cache to get object specific tags to cache
*
* @return array List of tags to save in cache
*/
public function get_tags()
{
return array();
}
/**
* Callback for kolab_storage_cache to get words to index for fulltext search
*
* @return array List of words to save in cache
*/
public function get_words()
{
return array();
}
/**
* Utility function to extract object attachment data
*
* @param array Hash array reference to append attachment data into
*/
public function get_attachments(&$object)
{
$this->init();
// handle attachments
$vattach = $this->obj->attachments();
for ($i=0; $i < $vattach->size(); $i++) {
$attach = $vattach->get($i);
// skip cid: attachments which are mime message parts handled by kolab_storage_folder
if (substr($attach->uri(), 0, 4) != 'cid:' && $attach->label()) {
$name = $attach->label();
$key = $name . (isset($object['_attachments'][$name]) ? '.'.$i : '');
$content = $attach->data();
$object['_attachments'][$key] = array(
'id' => 'i:'.$i,
'name' => $name,
'mimetype' => $attach->mimetype(),
'size' => strlen($content),
'content' => $content,
);
}
else if (in_array(substr($attach->uri(), 0, 4), array('http','imap'))) {
$object['links'][] = $attach->uri();
}
}
}
/**
* Utility function to set attachment properties to the kolabformat object
*
* @param array Object data as hash array
* @param boolean True to always overwrite attachment information
*/
protected function set_attachments($object, $write = true)
{
// save attachments
$vattach = new vectorattachment;
foreach ((array) $object['_attachments'] as $cid => $attr) {
if (empty($attr))
continue;
$attach = new Attachment;
$attach->setLabel((string)$attr['name']);
$attach->setUri('cid:' . $cid, $attr['mimetype'] ?: 'application/octet-stream');
if ($attach->isValid()) {
$vattach->push($attach);
$write = true;
}
else {
rcube::raise_error(array(
'code' => 660,
'type' => 'php',
'file' => __FILE__,
'line' => __LINE__,
'message' => "Invalid attributes for attachment $cid: " . var_export($attr, true),
), true);
}
}
foreach ((array) $object['links'] as $link) {
$attach = new Attachment;
$attach->setUri($link, 'unknown');
$vattach->push($attach);
$write = true;
}
if ($write) {
$this->obj->setAttachments($vattach);
}
}
}
diff --git a/lib/plugins/libkolab/lib/kolab_format_configuration.php b/lib/plugins/libkolab/lib/kolab_format_configuration.php
index 17b46a7..ceb7ebb 100644
--- a/lib/plugins/libkolab/lib/kolab_format_configuration.php
+++ b/lib/plugins/libkolab/lib/kolab_format_configuration.php
@@ -1,248 +1,284 @@
<?php
/**
* Kolab Configuration data model class
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_format_configuration extends kolab_format
{
- public $CTYPE = 'application/x-vnd.kolab.configuration';
+ public $CTYPE = 'application/vnd.kolab+xml';
public $CTYPEv2 = 'application/x-vnd.kolab.configuration';
protected $objclass = 'Configuration';
protected $read_func = 'readConfiguration';
protected $write_func = 'writeConfiguration';
private $type_map = array(
- 'category' => Configuration::TypeCategoryColor,
- 'dictionary' => Configuration::TypeDictionary,
- 'relation' => Configuration::TypeRelation,
- 'snippet' => Configuration::TypeSnippet,
+ 'category' => Configuration::TypeCategoryColor,
+ 'dictionary' => Configuration::TypeDictionary,
+ 'file_driver' => Configuration::TypeFileDriver,
+ 'relation' => Configuration::TypeRelation,
+ 'snippet' => Configuration::TypeSnippet,
);
+ private $driver_settings_fields = array('host', 'port', 'username', 'password');
/**
* Set properties to the kolabformat object
*
* @param array Object data as hash array
*/
public function set(&$object)
{
- // set common object properties
- parent::set($object);
-
// read type-specific properties
switch ($object['type']) {
case 'dictionary':
$dict = new Dictionary($object['language']);
$dict->setEntries(self::array2vector($object['e']));
$this->obj = new Configuration($dict);
break;
case 'category':
// TODO: implement this
$categories = new vectorcategorycolor;
$this->obj = new Configuration($categories);
break;
+ case 'file_driver':
+ $driver = new FileDriver($object['driver'], $object['title']);
+
+ $driver->setEnabled((bool) $object['enabled']);
+
+ foreach ($this->driver_settings_fields as $field) {
+ $value = $object[$field];
+ if ($value !== null) {
+ $driver->{'set' . ucfirst($field)}($value);
+ }
+ }
+
+ $this->obj = new Configuration($driver);
+ break;
+
case 'relation':
$relation = new Relation(strval($object['name']), strval($object['category']));
if ($object['color']) {
$relation->setColor($object['color']);
}
if ($object['parent']) {
$relation->setParent($object['parent']);
}
if ($object['iconName']) {
$relation->setIconName($object['iconName']);
}
if ($object['priority'] > 0) {
$relation->setPriority((int) $object['priority']);
}
if (!empty($object['members'])) {
$relation->setMembers(self::array2vector($object['members']));
}
$this->obj = new Configuration($relation);
break;
case 'snippet':
$collection = new SnippetCollection($object['name']);
$snippets = new vectorsnippets;
foreach ((array) $object['snippets'] as $item) {
$snippet = new snippet($item['name'], $item['text']);
$snippet->setTextType(strtolower($item['type']) == 'html' ? Snippet::HTML : Snippet::Plain);
if ($item['shortcut']) {
$snippet->setShortCut($item['shortcut']);
}
$snippets->push($snippet);
}
$collection->setSnippets($snippets);
$this->obj = new Configuration($collection);
break;
default:
return false;
}
// adjust content-type string
- $this->CTYPE = $this->CTYPEv2 = 'application/x-vnd.kolab.configuration.' . $object['type'];
+ $this->CTYPEv2 = 'application/x-vnd.kolab.configuration.' . $object['type'];
+
+ // reset old object data, otherwise set() will overwrite current data (#4095)
+ $this->xmldata = null;
+ // set common object properties
+ parent::set($object);
// cache this data
$this->data = $object;
unset($this->data['_formatobj']);
}
/**
*
*/
public function is_valid()
{
return $this->data || (is_object($this->obj) && $this->obj->isValid());
}
/**
* Convert the Configuration object into a hash array data structure
*
* @param array Additional data for merge
*
* @return array Config object data as hash array
*/
public function to_array($data = array())
{
// return cached result
if (!empty($this->data)) {
return $this->data;
}
// read common object props into local data object
$object = parent::to_array($data);
$type_map = array_flip($this->type_map);
$object['type'] = $type_map[$this->obj->type()];
// read type-specific properties
switch ($object['type']) {
case 'dictionary':
$dict = $this->obj->dictionary();
$object['language'] = $dict->language();
$object['e'] = self::vector2array($dict->entries());
break;
case 'category':
// TODO: implement this
break;
+ case 'file_driver':
+ $driver = $this->obj->fileDriver();
+
+ $object['driver'] = $driver->driver();
+ $object['title'] = $driver->title();
+ $object['enabled'] = $driver->enabled();
+
+ foreach ($this->driver_settings_fields as $field) {
+ $object[$field] = $driver->{$field}();
+ }
+
+ break;
+
case 'relation':
$relation = $this->obj->relation();
$object['name'] = $relation->name();
$object['category'] = $relation->type();
$object['color'] = $relation->color();
$object['parent'] = $relation->parent();
$object['iconName'] = $relation->iconName();
$object['priority'] = $relation->priority();
$object['members'] = self::vector2array($relation->members());
break;
case 'snippet':
$collection = $this->obj->snippets();
$object['name'] = $collection->name();
$object['snippets'] = array();
$snippets = $collection->snippets();
for ($i=0; $i < $snippets->size(); $i++) {
$snippet = $snippets->get($i);
$object['snippets'][] = array(
'name' => $snippet->name(),
'text' => $snippet->text(),
'type' => $snippet->textType() == Snippet::HTML ? 'html' : 'plain',
'shortcut' => $snippet->shortCut(),
);
}
break;
}
// adjust content-type string
if ($object['type']) {
- $this->CTYPE = $this->CTYPEv2 = 'application/x-vnd.kolab.configuration.' . $object['type'];
+ $this->CTYPEv2 = 'application/x-vnd.kolab.configuration.' . $object['type'];
}
$this->data = $object;
return $this->data;
}
/**
* Callback for kolab_storage_cache to get object specific tags to cache
*
* @return array List of tags to save in cache
*/
public function get_tags()
{
$tags = array();
switch ($this->data['type']) {
case 'dictionary':
$tags = array($this->data['language']);
break;
case 'relation':
$tags = array('category:' . $this->data['category']);
break;
}
return $tags;
}
/**
* Callback for kolab_storage_cache to get words to index for fulltext search
*
* @return array List of words to save in cache
*/
public function get_words()
{
$words = array();
foreach ((array)$this->data['members'] as $url) {
$member = kolab_storage_config::parse_member_url($url);
if (empty($member)) {
if (strpos($url, 'urn:uuid:') === 0) {
$words[] = substr($url, 9);
}
}
else if (!empty($member['params']['message-id'])) {
$words[] = $member['params']['message-id'];
}
+ else {
+ // derive message identifier from URI
+ $words[] = md5($url);
+ }
}
return $words;
}
}
diff --git a/lib/plugins/libkolab/lib/kolab_format_event.php b/lib/plugins/libkolab/lib/kolab_format_event.php
index c233f44..8cad89a 100644
--- a/lib/plugins/libkolab/lib/kolab_format_event.php
+++ b/lib/plugins/libkolab/lib/kolab_format_event.php
@@ -1,236 +1,244 @@
<?php
/**
* Kolab Event model class
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_format_event extends kolab_format_xcal
{
public $CTYPEv2 = 'application/x-vnd.kolab.event';
public $scheduling_properties = array('start', 'end', 'allday', 'location', 'status', 'cancelled');
protected $objclass = 'Event';
protected $read_func = 'readEvent';
protected $write_func = 'writeEvent';
/**
* Default constructor
*/
function __construct($data = null, $version = 3.0)
{
parent::__construct(is_string($data) ? $data : null, $version);
// got an Event object as argument
if (is_object($data) && is_a($data, $this->objclass)) {
$this->obj = $data;
$this->loaded = true;
}
}
/**
* Clones into an instance of libcalendaring's extended EventCal class
*
* @return mixed EventCal object or false on failure
*/
public function to_libcal()
{
static $error_logged = false;
if (class_exists('kolabcalendaring')) {
return new EventCal($this->obj);
}
else if (!$error_logged) {
$error_logged = true;
rcube::raise_error(array(
'code' => 900, 'type' => 'php',
'message' => "required kolabcalendaring module not found"
), true);
}
return false;
}
/**
* Set event properties to the kolabformat object
*
* @param array Event data as hash array
*/
public function set(&$object)
{
// set common xcal properties
parent::set($object);
// do the hard work of setting object values
$this->obj->setStart(self::get_datetime($object['start'], null, $object['allday']));
$this->obj->setEnd(self::get_datetime($object['end'], null, $object['allday']));
$this->obj->setTransparency($object['free_busy'] == 'free');
$status = kolabformat::StatusUndefined;
if ($object['free_busy'] == 'tentative')
$status = kolabformat::StatusTentative;
if ($object['cancelled'])
$status = kolabformat::StatusCancelled;
else if ($object['status'] && array_key_exists($object['status'], $this->status_map))
$status = $this->status_map[$object['status']];
$this->obj->setStatus($status);
// save recurrence exceptions
if (is_array($object['recurrence']) && $object['recurrence']['EXCEPTIONS']) {
$vexceptions = new vectorevent;
- foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
+ foreach((array)$object['recurrence']['EXCEPTIONS'] as $i => $exception) {
$exevent = new kolab_format_event;
- $exevent->set($this->compact_exception($exception, $object)); // only save differing values
+ $exevent->set(($compacted = $this->compact_exception($exception, $object))); // only save differing values
$exevent->obj->setRecurrenceID(self::get_datetime($exception['start'], null, true), (bool)$exception['thisandfuture']);
$vexceptions->push($exevent->obj);
+ // write cleaned-up exception data back to memory/cache
+ $object['recurrence']['EXCEPTIONS'][$i] = $this->expand_exception($compacted, $object);
}
$this->obj->setExceptions($vexceptions);
}
// cache this data
$this->data = $object;
unset($this->data['_formatobj']);
}
/**
*
*/
public function is_valid()
{
return !$this->formaterror && (($this->data && !empty($this->data['start']) && !empty($this->data['end'])) ||
(is_object($this->obj) && $this->obj->isValid() && $this->obj->uid()));
}
/**
* Convert the Event object into a hash array data structure
*
* @param array Additional data for merge
*
* @return array Event data as hash array
*/
public function to_array($data = array())
{
// return cached result
if (!empty($this->data))
return $this->data;
// read common xcal props
$object = parent::to_array($data);
// read object properties
$object += array(
'end' => self::php_datetime($this->obj->end()),
'allday' => $this->obj->start()->isDateOnly(),
'free_busy' => $this->obj->transparency() ? 'free' : 'busy', // TODO: transparency is only boolean
'attendees' => array(),
);
// derive event end from duration (#1916)
if (!$object['end'] && $object['start'] && ($duration = $this->obj->duration()) && $duration->isValid()) {
$interval = new DateInterval('PT0S');
$interval->d = $duration->weeks() * 7 + $duration->days();
$interval->h = $duration->hours();
$interval->i = $duration->minutes();
$interval->s = $duration->seconds();
$object['end'] = clone $object['start'];
$object['end']->add($interval);
}
// organizer is part of the attendees list in Roundcube
if ($object['organizer']) {
$object['organizer']['role'] = 'ORGANIZER';
array_unshift($object['attendees'], $object['organizer']);
}
// status defines different event properties...
$status = $this->obj->status();
if ($status == kolabformat::StatusTentative)
$object['free_busy'] = 'tentative';
else if ($status == kolabformat::StatusCancelled)
$object['cancelled'] = true;
// this is an exception object
if ($this->obj->recurrenceID()->isValid()) {
$object['thisandfuture'] = $this->obj->thisAndFuture();
}
// read exception event objects
else if (($exceptions = $this->obj->exceptions()) && is_object($exceptions) && $exceptions->size()) {
$recurrence_exceptions = array();
for ($i=0; $i < $exceptions->size(); $i++) {
if (($exobj = $exceptions->get($i))) {
$exception = new kolab_format_event($exobj);
if ($exception->is_valid()) {
$recurrence_exceptions[] = $this->expand_exception($exception->to_array(), $object);
}
}
}
$object['recurrence']['EXCEPTIONS'] = $recurrence_exceptions;
}
return $this->data = $object;
}
/**
* Callback for kolab_storage_cache to get object specific tags to cache
*
* @return array List of tags to save in cache
*/
public function get_tags()
{
$tags = parent::get_tags();
foreach ((array)$this->data['categories'] as $cat) {
$tags[] = rcube_utils::normalize_string($cat);
}
return $tags;
}
/**
* Remove some attributes from the exception container
*/
private function compact_exception($exception, $master)
{
$forbidden = array('recurrence','organizer','attendees','sequence');
foreach ($forbidden as $prop) {
if (array_key_exists($prop, $exception)) {
unset($exception[$prop]);
}
}
+ foreach ($master as $prop => $value) {
+ if (isset($exception[$prop]) && gettype($exception[$prop]) == gettype($value) && $exception[$prop] == $value) {
+ unset($exception[$prop]);
+ }
+ }
+
return $exception;
}
/**
* Copy attributes not specified by the exception from the master event
*/
private function expand_exception($exception, $master)
{
foreach ($master as $prop => $value) {
if (empty($exception[$prop]) && !empty($value))
$exception[$prop] = $value;
}
return $exception;
}
}
diff --git a/lib/plugins/libkolab/lib/kolab_format_xcal.php b/lib/plugins/libkolab/lib/kolab_format_xcal.php
index 421ee92..ad54505 100644
--- a/lib/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/lib/plugins/libkolab/lib/kolab_format_xcal.php
@@ -1,627 +1,630 @@
<?php
/**
* Xcal based Kolab format class wrapping libkolabxml bindings
*
* Base class for xcal-based Kolab groupware objects such as event, todo, journal
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
abstract class kolab_format_xcal extends kolab_format
{
public $CTYPE = 'application/calendar+xml';
public static $fulltext_cols = array('title', 'description', 'location', 'attendees:name', 'attendees:email', 'categories');
public $scheduling_properties = array('start', 'end', 'location');
protected $sensitivity_map = array(
'public' => kolabformat::ClassPublic,
'private' => kolabformat::ClassPrivate,
'confidential' => kolabformat::ClassConfidential,
);
protected $role_map = array(
'REQ-PARTICIPANT' => kolabformat::Required,
'OPT-PARTICIPANT' => kolabformat::Optional,
'NON-PARTICIPANT' => kolabformat::NonParticipant,
'CHAIR' => kolabformat::Chair,
);
protected $cutype_map = array(
'INDIVIDUAL' => kolabformat::CutypeIndividual,
'GROUP' => kolabformat::CutypeGroup,
'ROOM' => kolabformat::CutypeRoom,
'RESOURCE' => kolabformat::CutypeResource,
'UNKNOWN' => kolabformat::CutypeUnknown,
);
protected $rrule_type_map = array(
'MINUTELY' => RecurrenceRule::Minutely,
'HOURLY' => RecurrenceRule::Hourly,
'DAILY' => RecurrenceRule::Daily,
'WEEKLY' => RecurrenceRule::Weekly,
'MONTHLY' => RecurrenceRule::Monthly,
'YEARLY' => RecurrenceRule::Yearly,
);
protected $weekday_map = array(
'MO' => kolabformat::Monday,
'TU' => kolabformat::Tuesday,
'WE' => kolabformat::Wednesday,
'TH' => kolabformat::Thursday,
'FR' => kolabformat::Friday,
'SA' => kolabformat::Saturday,
'SU' => kolabformat::Sunday,
);
protected $alarm_type_map = array(
'DISPLAY' => Alarm::DisplayAlarm,
'EMAIL' => Alarm::EMailAlarm,
'AUDIO' => Alarm::AudioAlarm,
);
protected $status_map = array(
'NEEDS-ACTION' => kolabformat::StatusNeedsAction,
'IN-PROCESS' => kolabformat::StatusInProcess,
'COMPLETED' => kolabformat::StatusCompleted,
'CANCELLED' => kolabformat::StatusCancelled,
'TENTATIVE' => kolabformat::StatusTentative,
'CONFIRMED' => kolabformat::StatusConfirmed,
'DRAFT' => kolabformat::StatusDraft,
'FINAL' => kolabformat::StatusFinal,
);
protected $part_status_map = array(
'UNKNOWN' => kolabformat::PartNeedsAction,
'NEEDS-ACTION' => kolabformat::PartNeedsAction,
'TENTATIVE' => kolabformat::PartTentative,
'ACCEPTED' => kolabformat::PartAccepted,
'DECLINED' => kolabformat::PartDeclined,
'DELEGATED' => kolabformat::PartDelegated,
);
/**
* Convert common xcard properties into a hash array data structure
*
* @param array Additional data for merge
*
* @return array Object data as hash array
*/
public function to_array($data = array())
{
// read common object props
$object = parent::to_array($data);
$status_map = array_flip($this->status_map);
$sensitivity_map = array_flip($this->sensitivity_map);
$object += array(
'sequence' => intval($this->obj->sequence()),
'title' => $this->obj->summary(),
'location' => $this->obj->location(),
'description' => $this->obj->description(),
'url' => $this->obj->url(),
'status' => $status_map[$this->obj->status()],
'sensitivity' => $sensitivity_map[$this->obj->classification()],
'priority' => $this->obj->priority(),
'categories' => self::vector2array($this->obj->categories()),
'start' => self::php_datetime($this->obj->start()),
);
if (method_exists($this->obj, 'comment')) {
$object['comment'] = $this->obj->comment();
}
// read organizer and attendees
if (($organizer = $this->obj->organizer()) && ($organizer->email() || $organizer->name())) {
$object['organizer'] = array(
'email' => $organizer->email(),
'name' => $organizer->name(),
);
}
$role_map = array_flip($this->role_map);
$cutype_map = array_flip($this->cutype_map);
$part_status_map = array_flip($this->part_status_map);
$attvec = $this->obj->attendees();
for ($i=0; $i < $attvec->size(); $i++) {
$attendee = $attvec->get($i);
$cr = $attendee->contact();
if ($cr->email() != $object['organizer']['email']) {
$delegators = $delegatees = array();
$vdelegators = $attendee->delegatedFrom();
for ($j=0; $j < $vdelegators->size(); $j++) {
$delegators[] = $vdelegators->get($j)->email();
}
$vdelegatees = $attendee->delegatedTo();
for ($j=0; $j < $vdelegatees->size(); $j++) {
$delegatees[] = $vdelegatees->get($j)->email();
}
$object['attendees'][] = array(
'role' => $role_map[$attendee->role()],
'cutype' => $cutype_map[$attendee->cutype()],
'status' => $part_status_map[$attendee->partStat()],
'rsvp' => $attendee->rsvp(),
'email' => $cr->email(),
'name' => $cr->name(),
'delegated-from' => $delegators,
'delegated-to' => $delegatees,
);
}
}
// read recurrence rule
if (($rr = $this->obj->recurrenceRule()) && $rr->isValid()) {
$rrule_type_map = array_flip($this->rrule_type_map);
$object['recurrence'] = array('FREQ' => $rrule_type_map[$rr->frequency()]);
if ($intvl = $rr->interval())
$object['recurrence']['INTERVAL'] = $intvl;
if (($count = $rr->count()) && $count > 0) {
$object['recurrence']['COUNT'] = $count;
}
else if ($until = self::php_datetime($rr->end())) {
$until->setTime($object['start']->format('G'), $object['start']->format('i'), 0);
$object['recurrence']['UNTIL'] = $until;
}
if (($byday = $rr->byday()) && $byday->size()) {
$weekday_map = array_flip($this->weekday_map);
$weekdays = array();
for ($i=0; $i < $byday->size(); $i++) {
$daypos = $byday->get($i);
$prefix = $daypos->occurence();
$weekdays[] = ($prefix ? $prefix : '') . $weekday_map[$daypos->weekday()];
}
$object['recurrence']['BYDAY'] = join(',', $weekdays);
}
if (($bymday = $rr->bymonthday()) && $bymday->size()) {
$object['recurrence']['BYMONTHDAY'] = join(',', self::vector2array($bymday));
}
if (($bymonth = $rr->bymonth()) && $bymonth->size()) {
$object['recurrence']['BYMONTH'] = join(',', self::vector2array($bymonth));
}
if ($exdates = $this->obj->exceptionDates()) {
for ($i=0; $i < $exdates->size(); $i++) {
if ($exdate = self::php_datetime($exdates->get($i)))
$object['recurrence']['EXDATE'][] = $exdate;
}
}
}
if ($rdates = $this->obj->recurrenceDates()) {
for ($i=0; $i < $rdates->size(); $i++) {
if ($rdate = self::php_datetime($rdates->get($i)))
$object['recurrence']['RDATE'][] = $rdate;
}
}
// read alarm
$valarms = $this->obj->alarms();
$alarm_types = array_flip($this->alarm_type_map);
$object['valarms'] = array();
for ($i=0; $i < $valarms->size(); $i++) {
$alarm = $valarms->get($i);
$type = $alarm_types[$alarm->type()];
if ($type == 'DISPLAY' || $type == 'EMAIL' || $type == 'AUDIO') { // only some alarms are supported
$valarm = array(
'action' => $type,
'summary' => $alarm->summary(),
'description' => $alarm->description(),
);
if ($type == 'EMAIL') {
$valarm['attendees'] = array();
- $attvec = $this->obj->attendees();
+ $attvec = $alarm->attendees();
for ($j=0; $j < $attvec->size(); $j++) {
$cr = $attvec->get($j);
$valarm['attendees'][] = $cr->email();
}
}
else if ($type == 'AUDIO') {
$attach = $alarm->audioFile();
$valarm['uri'] = $attach->uri();
}
if ($start = self::php_datetime($alarm->start())) {
$object['alarms'] = '@' . $start->format('U');
$valarm['trigger'] = $start;
}
else if ($offset = $alarm->relativeStart()) {
$prefix = $alarm->relativeTo() == kolabformat::End ? '+' : '-';
$value = $time = '';
if ($w = $offset->weeks()) $value .= $w . 'W';
else if ($d = $offset->days()) $value .= $d . 'D';
else if ($h = $offset->hours()) $time .= $h . 'H';
else if ($m = $offset->minutes()) $time .= $m . 'M';
else if ($s = $offset->seconds()) $time .= $s . 'S';
// assume 'at event time'
if (empty($value) && empty($time)) {
$prefix = '';
$time = '0S';
}
$object['alarms'] = $prefix . $value . $time;
$valarm['trigger'] = $prefix . 'P' . $value . ($time ? 'T' . $time : '');
}
// read alarm duration and repeat properties
if (($duration = $alarm->duration()) && $duration->isValid()) {
$value = $time = '';
if ($w = $duration->weeks()) $value .= $w . 'W';
else if ($d = $duration->days()) $value .= $d . 'D';
else if ($h = $duration->hours()) $time .= $h . 'H';
else if ($m = $duration->minutes()) $time .= $m . 'M';
else if ($s = $duration->seconds()) $time .= $s . 'S';
$valarm['duration'] = 'P' . $value . ($time ? 'T' . $time : '');
$valarm['repeat'] = $alarm->numrepeat();
}
$object['alarms'] .= ':' . $type; // legacy property
$object['valarms'][] = array_filter($valarm);
}
}
$this->get_attachments($object);
return $object;
}
/**
* Set common xcal properties to the kolabformat object
*
* @param array Event data as hash array
*/
public function set(&$object)
{
$this->init();
$is_new = !$this->obj->uid();
$old_sequence = $this->obj->sequence();
$reschedule = $is_new;
// set common object properties
parent::set($object);
// set sequence value
if (!isset($object['sequence'])) {
if ($is_new) {
$object['sequence'] = 0;
}
else {
$object['sequence'] = $old_sequence;
$old = $this->data['uid'] ? $this->data : $this->to_array();
// increment sequence when updating properties relevant for scheduling.
// RFC 5545: "It is incremented [...] each time the Organizer makes a significant revision to the calendar component."
// TODO: make the list of properties considered 'significant' for scheduling configurable
foreach ($this->scheduling_properties as $prop) {
$a = $old[$prop];
$b = $object[$prop];
if ($object['allday'] && ($prop == 'start' || $prop == 'end') && $a instanceof DateTime && $b instanceof DateTime) {
$a = $a->format('Y-m-d');
$b = $b->format('Y-m-d');
}
if ($a != $b) {
$object['sequence']++;
break;
}
}
}
}
$this->obj->setSequence(intval($object['sequence']));
if ($object['sequence'] > $old_sequence) {
$reschedule = true;
}
$this->obj->setSummary($object['title']);
$this->obj->setLocation($object['location']);
$this->obj->setDescription($object['description']);
$this->obj->setPriority($object['priority']);
$this->obj->setClassification($this->sensitivity_map[$object['sensitivity']]);
$this->obj->setCategories(self::array2vector($object['categories']));
$this->obj->setUrl(strval($object['url']));
if (method_exists($this->obj, 'setComment')) {
$this->obj->setComment($object['comment']);
}
// process event attendees
$attendees = new vectorattendee;
foreach ((array)$object['attendees'] as $i => $attendee) {
if ($attendee['role'] == 'ORGANIZER') {
$object['organizer'] = $attendee;
}
else if ($attendee['email'] != $object['organizer']['email']) {
$cr = new ContactReference(ContactReference::EmailReference, $attendee['email']);
$cr->setName($attendee['name']);
+ // set attendee RSVP if missing
+ if (!isset($attendee['rsvp'])) {
+ $object['attendees'][$i]['rsvp'] = $attendee['rsvp'] = true;
+ }
+
$att = new Attendee;
$att->setContact($cr);
$att->setPartStat($this->part_status_map[$attendee['status']]);
$att->setRole($this->role_map[$attendee['role']] ? $this->role_map[$attendee['role']] : kolabformat::Required);
$att->setCutype($this->cutype_map[$attendee['cutype']] ? $this->cutype_map[$attendee['cutype']] : kolabformat::CutypeIndividual);
- $att->setRSVP((bool)$attendee['rsvp'] || $reschedule);
-
- $object['attendees'][$i]['rsvp'] = $attendee['rsvp'] || $reschedule;
+ $att->setRSVP((bool)$attendee['rsvp']);
if (!empty($attendee['delegated-from'])) {
$vdelegators = new vectorcontactref;
foreach ((array)$attendee['delegated-from'] as $delegator) {
$vdelegators->push(new ContactReference(ContactReference::EmailReference, $delegator));
}
$att->setDelegatedFrom($vdelegators);
}
if (!empty($attendee['delegated-to'])) {
$vdelegatees = new vectorcontactref;
foreach ((array)$attendee['delegated-to'] as $delegatee) {
$vdelegatees->push(new ContactReference(ContactReference::EmailReference, $delegatee));
}
$att->setDelegatedTo($vdelegatees);
}
if ($att->isValid()) {
$attendees->push($att);
}
else {
rcube::raise_error(array(
'code' => 600, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Invalid event attendee: " . json_encode($attendee),
), true);
}
}
}
$this->obj->setAttendees($attendees);
if ($object['organizer']) {
$organizer = new ContactReference(ContactReference::EmailReference, $object['organizer']['email']);
$organizer->setName($object['organizer']['name']);
$this->obj->setOrganizer($organizer);
}
// save recurrence rule
$rr = new RecurrenceRule;
$rr->setFrequency(RecurrenceRule::FreqNone);
if ($object['recurrence'] && !empty($object['recurrence']['FREQ'])) {
$rr->setFrequency($this->rrule_type_map[$object['recurrence']['FREQ']]);
if ($object['recurrence']['INTERVAL'])
$rr->setInterval(intval($object['recurrence']['INTERVAL']));
if ($object['recurrence']['BYDAY']) {
$byday = new vectordaypos;
foreach (explode(',', $object['recurrence']['BYDAY']) as $day) {
$occurrence = 0;
if (preg_match('/^([\d-]+)([A-Z]+)$/', $day, $m)) {
$occurrence = intval($m[1]);
$day = $m[2];
}
if (isset($this->weekday_map[$day]))
$byday->push(new DayPos($occurrence, $this->weekday_map[$day]));
}
$rr->setByday($byday);
}
if ($object['recurrence']['BYMONTHDAY']) {
$bymday = new vectori;
foreach (explode(',', $object['recurrence']['BYMONTHDAY']) as $day)
$bymday->push(intval($day));
$rr->setBymonthday($bymday);
}
if ($object['recurrence']['BYMONTH']) {
$bymonth = new vectori;
foreach (explode(',', $object['recurrence']['BYMONTH']) as $month)
$bymonth->push(intval($month));
$rr->setBymonth($bymonth);
}
if ($object['recurrence']['COUNT'])
$rr->setCount(intval($object['recurrence']['COUNT']));
else if ($object['recurrence']['UNTIL'])
$rr->setEnd(self::get_datetime($object['recurrence']['UNTIL'], null, true));
if ($rr->isValid()) {
// add exception dates (only if recurrence rule is valid)
$exdates = new vectordatetime;
foreach ((array)$object['recurrence']['EXDATE'] as $exdate)
$exdates->push(self::get_datetime($exdate, null, true));
$this->obj->setExceptionDates($exdates);
}
else {
rcube::raise_error(array(
'code' => 600, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Invalid event recurrence rule: " . json_encode($object['recurrence']),
), true);
}
}
$this->obj->setRecurrenceRule($rr);
// save recurrence dates (aka RDATE)
if (!empty($object['recurrence']['RDATE'])) {
$rdates = new vectordatetime;
foreach ((array)$object['recurrence']['RDATE'] as $rdate)
$rdates->push(self::get_datetime($rdate, null, true));
$this->obj->setRecurrenceDates($rdates);
}
// save alarm
$valarms = new vectoralarm;
if ($object['valarms']) {
foreach ($object['valarms'] as $valarm) {
if (!array_key_exists($valarm['action'], $this->alarm_type_map)) {
continue; // skip unknown alarm types
}
if ($valarm['action'] == 'EMAIL') {
$recipients = new vectorcontactref;
foreach (($valarm['attendees'] ?: array($object['_owner'])) as $email) {
$recipients->push(new ContactReference(ContactReference::EmailReference, $email));
}
$alarm = new Alarm(
strval($valarm['summary'] ?: $object['title']),
strval($valarm['description'] ?: $object['description']),
$recipients
);
}
else if ($valarm['action'] == 'AUDIO') {
$attach = new Attachment;
$attach->setUri($valarm['uri'] ?: 'null', 'unknown');
$alarm = new Alarm($attach);
}
else {
// action == DISPLAY
$alarm = new Alarm(strval($valarm['summary'] ?: $object['title']));
}
if (is_object($valarm['trigger']) && $valarm['trigger'] instanceof DateTime) {
$alarm->setStart(self::get_datetime($valarm['trigger'], new DateTimeZone('UTC')));
}
else {
try {
$prefix = $valarm['trigger'][0];
$period = new DateInterval(preg_replace('/[^0-9PTWDHMS]/', '', $valarm['trigger']));
$duration = new Duration($period->d, $period->h, $period->i, $period->s, $prefix == '-');
}
catch (Exception $e) {
// skip alarm with invalid trigger values
rcube::raise_error($e, true);
continue;
}
$alarm->setRelativeStart($duration, $prefix == '-' ? kolabformat::Start : kolabformat::End);
}
if ($valarm['duration']) {
try {
$d = new DateInterval($valarm['duration']);
$duration = new Duration($d->d, $d->h, $d->i, $d->s);
$alarm->setDuration($duration, intval($valarm['repeat']));
}
catch (Exception $e) {
// ignore
}
}
$valarms->push($alarm);
}
}
// legacy support
else if ($object['alarms']) {
list($offset, $type) = explode(":", $object['alarms']);
if ($type == 'EMAIL' && !empty($object['_owner'])) { // email alarms implicitly go to event owner
$recipients = new vectorcontactref;
$recipients->push(new ContactReference(ContactReference::EmailReference, $object['_owner']));
$alarm = new Alarm($object['title'], strval($object['description']), $recipients);
}
else { // default: display alarm
$alarm = new Alarm($object['title']);
}
if (preg_match('/^@(\d+)/', $offset, $d)) {
$alarm->setStart(self::get_datetime($d[1], new DateTimeZone('UTC')));
}
else if (preg_match('/^([-+]?)P?T?(\d+)([SMHDW])/', $offset, $d)) {
$days = $hours = $minutes = $seconds = 0;
switch ($d[3]) {
case 'W': $days = 7*intval($d[2]); break;
case 'D': $days = intval($d[2]); break;
case 'H': $hours = intval($d[2]); break;
case 'M': $minutes = intval($d[2]); break;
case 'S': $seconds = intval($d[2]); break;
}
$alarm->setRelativeStart(new Duration($days, $hours, $minutes, $seconds, $d[1] == '-'), $d[1] == '-' ? kolabformat::Start : kolabformat::End);
}
$valarms->push($alarm);
}
$this->obj->setAlarms($valarms);
$this->set_attachments($object);
}
/**
* Callback for kolab_storage_cache to get words to index for fulltext search
*
* @return array List of words to save in cache
*/
public function get_words()
{
$data = '';
foreach (self::$fulltext_cols as $colname) {
list($col, $field) = explode(':', $colname);
if ($field) {
$a = array();
foreach ((array)$this->data[$col] as $attr)
$a[] = $attr[$field];
$val = join(' ', $a);
}
else {
$val = is_array($this->data[$col]) ? join(' ', $this->data[$col]) : $this->data[$col];
}
if (strlen($val))
$data .= $val . ' ';
}
return array_unique(rcube_utils::normalize_string($data, true));
}
/**
* Callback for kolab_storage_cache to get object specific tags to cache
*
* @return array List of tags to save in cache
*/
public function get_tags()
{
$tags = array();
if (!empty($this->data['valarms'])) {
$tags[] = 'x-has-alarms';
}
// create tags reflecting participant status
if (is_array($this->data['attendees'])) {
foreach ($this->data['attendees'] as $attendee) {
if (!empty($attendee['email']) && !empty($attendee['status']))
$tags[] = 'x-partstat:' . $attendee['email'] . ':' . strtolower($attendee['status']);
}
}
return $tags;
}
}
\ No newline at end of file
diff --git a/lib/plugins/libkolab/lib/kolab_storage.php b/lib/plugins/libkolab/lib/kolab_storage.php
index 7287fc2..47c1e4b 100644
--- a/lib/plugins/libkolab/lib/kolab_storage.php
+++ b/lib/plugins/libkolab/lib/kolab_storage.php
@@ -1,1556 +1,1571 @@
<?php
/**
* Kolab storage class providing static methods to access groupware objects on a Kolab server.
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2012-2014, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_storage
{
const CTYPE_KEY = '/shared/vendor/kolab/folder-type';
const CTYPE_KEY_PRIVATE = '/private/vendor/kolab/folder-type';
const COLOR_KEY_SHARED = '/shared/vendor/kolab/color';
const COLOR_KEY_PRIVATE = '/private/vendor/kolab/color';
const NAME_KEY_SHARED = '/shared/vendor/kolab/displayname';
const NAME_KEY_PRIVATE = '/private/vendor/kolab/displayname';
const UID_KEY_SHARED = '/shared/vendor/kolab/uniqueid';
const UID_KEY_PRIVATE = '/private/vendor/kolab/uniqueid';
const UID_KEY_CYRUS = '/shared/vendor/cmu/cyrus-imapd/uniqueid';
+ const ERROR_IMAP_CONN = 1;
+ const ERROR_CACHE_DB = 2;
+ const ERROR_NO_PERMISSION = 3;
+ const ERROR_INVALID_FOLDER = 4;
+
public static $version = '3.0';
public static $last_error;
public static $encode_ids = false;
private static $ready = false;
private static $with_tempsubs = true;
private static $subscriptions;
private static $typedata = array();
private static $states;
private static $config;
private static $imap;
private static $ldap;
// Default folder names
private static $default_folders = array(
'event' => 'Calendar',
'contact' => 'Contacts',
'task' => 'Tasks',
'note' => 'Notes',
'file' => 'Files',
'configuration' => 'Configuration',
'journal' => 'Journal',
'mail.inbox' => 'INBOX',
'mail.drafts' => 'Drafts',
'mail.sentitems' => 'Sent',
'mail.wastebasket' => 'Trash',
'mail.outbox' => 'Outbox',
'mail.junkemail' => 'Junk',
);
/**
* Setup the environment needed by the libs
*/
public static function setup()
{
if (self::$ready)
return true;
$rcmail = rcube::get_instance();
self::$config = $rcmail->config;
self::$version = strval($rcmail->config->get('kolab_format_version', self::$version));
self::$imap = $rcmail->get_storage();
self::$ready = class_exists('kolabformat') &&
(self::$imap->get_capability('METADATA') || self::$imap->get_capability('ANNOTATEMORE') || self::$imap->get_capability('ANNOTATEMORE2'));
if (self::$ready) {
// set imap options
self::$imap->set_options(array(
'skip_deleted' => true,
'threading' => false,
));
}
else if (!class_exists('kolabformat')) {
rcube::raise_error(array(
'code' => 900, 'type' => 'php',
'message' => "required kolabformat module not found"
), true);
}
else {
rcube::raise_error(array(
'code' => 900, 'type' => 'php',
'message' => "IMAP server doesn't support METADATA or ANNOTATEMORE"
), true);
}
return self::$ready;
}
/**
* Initializes LDAP object to resolve Kolab users
*/
public static function ldap()
{
if (self::$ldap) {
return self::$ldap;
}
self::setup();
$config = self::$config->get('kolab_users_directory', self::$config->get('kolab_auth_addressbook'));
if (!is_array($config)) {
$ldap_config = (array)self::$config->get('ldap_public');
$config = $ldap_config[$config];
}
if (empty($config)) {
return null;
}
// overwrite filter option
if ($filter = self::$config->get('kolab_users_filter')) {
self::$config->set('kolab_auth_filter', $filter);
}
// re-use the LDAP wrapper class from kolab_auth plugin
require_once rtrim(RCUBE_PLUGINS_DIR, '/') . '/kolab_auth/kolab_auth_ldap.php';
self::$ldap = new kolab_auth_ldap($config);
return self::$ldap;
}
-
/**
* Get a list of storage folders for the given data type
*
* @param string Data type to list folders for (contact,distribution-list,event,task,note)
* @param boolean Enable to return subscribed folders only (null to use configured subscription mode)
*
* @return array List of Kolab_Folder objects (folder names in UTF7-IMAP)
*/
public static function get_folders($type, $subscribed = null)
{
$folders = $folderdata = array();
if (self::setup()) {
foreach ((array)self::list_folders('', '*', $type, $subscribed, $folderdata) as $foldername) {
- $folders[$foldername] = new kolab_storage_folder($foldername, $folderdata[$foldername]);
+ $folders[$foldername] = new kolab_storage_folder($foldername, $type, $folderdata[$foldername]);
}
}
return $folders;
}
/**
* Getter for the storage folder for the given type
*
* @param string Data type to list folders for (contact,distribution-list,event,task,note)
* @return object kolab_storage_folder The folder object
*/
public static function get_default_folder($type)
{
if (self::setup()) {
foreach ((array)self::list_folders('', '*', $type . '.default', false, $folderdata) as $foldername) {
- return new kolab_storage_folder($foldername, $folderdata[$foldername]);
+ return new kolab_storage_folder($foldername, $type, $folderdata[$foldername]);
}
}
return null;
}
-
/**
* Getter for a specific storage folder
*
- * @param string IMAP folder to access (UTF7-IMAP)
+ * @param string IMAP folder to access (UTF7-IMAP)
+ * @param string Expected folder type
+ *
* @return object kolab_storage_folder The folder object
*/
- public static function get_folder($folder)
+ public static function get_folder($folder, $type = null)
{
- return self::setup() ? new kolab_storage_folder($folder) : null;
+ return self::setup() ? new kolab_storage_folder($folder, $type) : null;
}
-
/**
* Getter for a single Kolab object, identified by its UID.
* This will search all folders storing objects of the given type.
*
* @param string Object UID
* @param string Object type (contact,event,task,journal,file,note,configuration)
* @return array The Kolab object represented as hash array or false if not found
*/
public static function get_object($uid, $type)
{
self::setup();
$folder = null;
- foreach ((array)self::list_folders('', '*', $type) as $foldername) {
+ foreach ((array)self::list_folders('', '*', $type, null, $folderdata) as $foldername) {
if (!$folder)
- $folder = new kolab_storage_folder($foldername);
+ $folder = new kolab_storage_folder($foldername, $type, $folderdata[$foldername]);
else
- $folder->set_folder($foldername);
+ $folder->set_folder($foldername, $type, $folderdata[$foldername]);
if ($object = $folder->get_object($uid, '*'))
return $object;
}
return false;
}
/**
* Execute cross-folder searches with the given query.
*
* @param array Pseudo-SQL query as list of filter parameter triplets
* @param string Object type (contact,event,task,journal,file,note,configuration)
* @return array List of Kolab data objects (each represented as hash array)
* @see kolab_storage_format::select()
*/
public static function select($query, $type)
{
self::setup();
$folder = null;
$result = array();
- foreach ((array)self::list_folders('', '*', $type) as $foldername) {
+ foreach ((array)self::list_folders('', '*', $type, null, $folderdata) as $foldername) {
if (!$folder)
- $folder = new kolab_storage_folder($foldername);
+ $folder = new kolab_storage_folder($foldername, $type, $folderdata[$foldername]);
else
- $folder->set_folder($foldername);
+ $folder->set_folder($foldername, $type, $folderdata[$foldername]);
foreach ($folder->select($query, '*') as $object) {
$result[] = $object;
}
}
return $result;
}
/**
- *
+ * Returns Free-busy server URL
*/
public static function get_freebusy_server()
{
- return unslashify(self::$config->get('kolab_freebusy_server', 'https://' . $_SESSION['imap_host'] . '/freebusy'));
- }
+ $url = 'https://' . $_SESSION['imap_host'] . '/freebusy';
+ $url = self::$config->get('kolab_freebusy_server', $url);
+ $url = rcube_utils::resolve_url($url);
+ return unslashify($url);
+ }
/**
* Compose an URL to query the free/busy status for the given user
*/
public static function get_freebusy_url($email)
{
return self::get_freebusy_server() . '/' . $email . '.ifb';
}
-
/**
* Creates folder ID from folder name
*
* @param string $folder Folder name (UTF7-IMAP)
* @param boolean $enc Use lossless encoding
* @return string Folder ID string
*/
public static function folder_id($folder, $enc = null)
{
return $enc == true || ($enc === null && self::$encode_ids) ?
self::id_encode($folder) :
asciiwords(strtr($folder, '/.-', '___'));
}
-
/**
* Encode the given ID to a safe ascii representation
*
* @param string $id Arbitrary identifier string
*
* @return string Ascii representation
*/
public static function id_encode($id)
{
return rtrim(strtr(base64_encode($id), '+/', '-_'), '=');
}
/**
* Convert the given identifier back to it's raw value
*
* @param string $id Ascii identifier
* @return string Raw identifier string
*/
public static function id_decode($id)
{
return base64_decode(str_pad(strtr($id, '-_', '+/'), strlen($id) % 4, '=', STR_PAD_RIGHT));
}
-
/**
* Return the (first) path of the requested IMAP namespace
*
* @param string Namespace name (personal, shared, other)
* @return string IMAP root path for that namespace
*/
public static function namespace_root($name)
{
foreach ((array)self::$imap->get_namespace($name) as $paths) {
if (strlen($paths[0]) > 1) {
return $paths[0];
}
}
return '';
}
/**
* Deletes IMAP folder
*
* @param string $name Folder name (UTF7-IMAP)
*
* @return bool True on success, false on failure
*/
public static function folder_delete($name)
{
// clear cached entries first
if ($folder = self::get_folder($name))
$folder->cache->purge();
$rcmail = rcube::get_instance();
$plugin = $rcmail->plugins->exec_hook('folder_delete', array('name' => $name));
$success = self::$imap->delete_folder($name);
self::$last_error = self::$imap->get_error_str();
return $success;
}
/**
* Creates IMAP folder
*
* @param string $name Folder name (UTF7-IMAP)
* @param string $type Folder type
* @param bool $subscribed Sets folder subscription
* @param bool $active Sets folder state (client-side subscription)
*
* @return bool True on success, false on failure
*/
public static function folder_create($name, $type = null, $subscribed = false, $active = false)
{
self::setup();
$rcmail = rcube::get_instance();
$plugin = $rcmail->plugins->exec_hook('folder_create', array('record' => array(
'name' => $name,
'subscribe' => $subscribed,
)));
if ($saved = self::$imap->create_folder($name, $subscribed)) {
// set metadata for folder type
if ($type) {
$saved = self::set_folder_type($name, $type);
// revert if metadata could not be set
if (!$saved) {
self::$imap->delete_folder($name);
}
// activate folder
else if ($active) {
self::set_state($name, true);
}
}
}
if ($saved) {
return true;
}
self::$last_error = self::$imap->get_error_str();
return false;
}
/**
* Renames IMAP folder
*
* @param string $oldname Old folder name (UTF7-IMAP)
* @param string $newname New folder name (UTF7-IMAP)
*
* @return bool True on success, false on failure
*/
public static function folder_rename($oldname, $newname)
{
self::setup();
$rcmail = rcube::get_instance();
$plugin = $rcmail->plugins->exec_hook('folder_rename', array(
'oldname' => $oldname, 'newname' => $newname));
$oldfolder = self::get_folder($oldname);
- $active = self::folder_is_active($oldname);
- $success = self::$imap->rename_folder($oldname, $newname);
+ $active = self::folder_is_active($oldname);
+ $success = self::$imap->rename_folder($oldname, $newname);
self::$last_error = self::$imap->get_error_str();
// pass active state to new folder name
if ($success && $active) {
- self::set_state($oldnam, false);
+ self::set_state($oldname, false);
self::set_state($newname, true);
}
// assign existing cache entries to new resource uri
if ($success && $oldfolder) {
$oldfolder->cache->rename($newname);
}
return $success;
}
/**
* Rename or Create a new IMAP folder.
*
* Does additional checks for permissions and folder name restrictions
*
* @param array Hash array with folder properties and metadata
* - name: Folder name
* - oldname: Old folder name when changed
* - parent: Parent folder to create the new one in
* - type: Folder type to create
* - subscribed: Subscribed flag (IMAP subscription)
* - active: Activation flag (client-side subscription)
* @return mixed New folder name or False on failure
*/
public static function folder_update(&$prop)
{
self::setup();
$folder = rcube_charset::convert($prop['name'], RCUBE_CHARSET, 'UTF7-IMAP');
$oldfolder = $prop['oldname']; // UTF7
$parent = $prop['parent']; // UTF7
$delimiter = self::$imap->get_hierarchy_delimiter();
if (strlen($oldfolder)) {
$options = self::$imap->folder_info($oldfolder);
}
if (!empty($options) && ($options['norename'] || $options['protected'])) {
}
// sanity checks (from steps/settings/save_folder.inc)
else if (!strlen($folder)) {
self::$last_error = 'cannotbeempty';
return false;
}
else if (strlen($folder) > 128) {
self::$last_error = 'nametoolong';
return false;
}
else {
// these characters are problematic e.g. when used in LIST/LSUB
foreach (array($delimiter, '%', '*') as $char) {
if (strpos($folder, $char) !== false) {
self::$last_error = 'forbiddencharacter';
return false;
}
}
}
if (!empty($options) && ($options['protected'] || $options['norename'])) {
$folder = $oldfolder;
}
else if (strlen($parent)) {
$folder = $parent . $delimiter . $folder;
}
else {
// add namespace prefix (when needed)
$folder = self::$imap->mod_folder($folder, 'in');
}
// Check access rights to the parent folder
if (strlen($parent) && (!strlen($oldfolder) || $oldfolder != $folder)) {
$parent_opts = self::$imap->folder_info($parent);
if ($parent_opts['namespace'] != 'personal'
&& (empty($parent_opts['rights']) || !preg_match('/[ck]/', implode($parent_opts['rights'])))
) {
self::$last_error = 'No permission to create folder';
return false;
}
}
// update the folder name
if (strlen($oldfolder)) {
if ($oldfolder != $folder) {
$result = self::folder_rename($oldfolder, $folder);
}
else
$result = true;
}
// create new folder
else {
$result = self::folder_create($folder, $prop['type'], $prop['subscribed'], $prop['active']);
}
if ($result) {
self::set_folder_props($folder, $prop);
}
return $result ? $folder : false;
}
/**
* Getter for human-readable name of Kolab object (folder)
* See http://wiki.kolab.org/UI-Concepts/Folder-Listing for reference
*
* @param string $folder IMAP folder name (UTF7-IMAP)
* @param string $folder_ns Will be set to namespace name of the folder
*
* @return string Name of the folder-object
*/
public static function object_name($folder, &$folder_ns=null)
{
self::setup();
// find custom display name in folder METADATA
if ($name = self::custom_displayname($folder)) {
return $name;
}
$found = false;
$namespace = self::$imap->get_namespace();
if (!empty($namespace['shared'])) {
foreach ($namespace['shared'] as $ns) {
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
$prefix = '';
$folder = substr($folder, strlen($ns[0]));
$delim = $ns[1];
$found = true;
$folder_ns = 'shared';
break;
}
}
}
if (!$found && !empty($namespace['other'])) {
foreach ($namespace['other'] as $ns) {
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
// remove namespace prefix
$folder = substr($folder, strlen($ns[0]));
$delim = $ns[1];
// get username
$pos = strpos($folder, $delim);
if ($pos) {
$prefix = '('.substr($folder, 0, $pos).')';
$folder = substr($folder, $pos+1);
}
else {
$prefix = '('.$folder.')';
$folder = '';
}
$found = true;
$folder_ns = 'other';
break;
}
}
}
if (!$found && !empty($namespace['personal'])) {
foreach ($namespace['personal'] as $ns) {
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
// remove namespace prefix
$folder = substr($folder, strlen($ns[0]));
$prefix = '';
$delim = $ns[1];
$found = true;
break;
}
}
}
if (empty($delim))
$delim = self::$imap->get_hierarchy_delimiter();
$folder = rcube_charset::convert($folder, 'UTF7-IMAP');
$folder = html::quote($folder);
$folder = str_replace(html::quote($delim), ' &raquo; ', $folder);
if ($prefix)
$folder = html::quote($prefix) . ($folder !== '' ? ' ' . $folder : '');
if (!$folder_ns)
$folder_ns = 'personal';
return $folder;
}
/**
* Get custom display name (saved in metadata) for the given folder
*/
public static function custom_displayname($folder)
{
// find custom display name in folder METADATA
if (self::$config->get('kolab_custom_display_names', true)) {
$metadata = self::$imap->get_metadata($folder, array(self::NAME_KEY_PRIVATE, self::NAME_KEY_SHARED));
if (($name = $metadata[$folder][self::NAME_KEY_PRIVATE]) || ($name = $metadata[$folder][self::NAME_KEY_SHARED])) {
return $name;
}
}
return false;
}
/**
* Helper method to generate a truncated folder name to display.
* Note: $origname is a string returned by self::object_name()
*/
public static function folder_displayname($origname, &$names)
{
$name = $origname;
// find folder prefix to truncate
for ($i = count($names)-1; $i >= 0; $i--) {
if (strpos($name, $names[$i] . ' &raquo; ') === 0) {
$length = strlen($names[$i] . ' &raquo; ');
$prefix = substr($name, 0, $length);
$count = count(explode(' &raquo; ', $prefix));
$diff = 1;
// check if prefix folder is in other users namespace
for ($n = count($names)-1; $n >= 0; $n--) {
if (strpos($prefix, '(' . $names[$n] . ') ') === 0) {
$diff = 0;
break;
}
}
$name = str_repeat('&nbsp;&nbsp;&nbsp;', $count - $diff) . '&raquo; ' . substr($name, $length);
break;
}
// other users namespace and parent folder exists
else if (strpos($name, '(' . $names[$i] . ') ') === 0) {
$length = strlen('(' . $names[$i] . ') ');
$prefix = substr($name, 0, $length);
$count = count(explode(' &raquo; ', $prefix));
$name = str_repeat('&nbsp;&nbsp;&nbsp;', $count) . '&raquo; ' . substr($name, $length);
break;
}
}
$names[] = $origname;
return $name;
}
/**
* Creates a SELECT field with folders list
*
* @param string $type Folder type
* @param array $attrs SELECT field attributes (e.g. name)
* @param string $current The name of current folder (to skip it)
*
* @return html_select SELECT object
*/
public static function folder_selector($type, $attrs, $current = '')
{
// get all folders of specified type (sorted)
$folders = self::get_folders($type, true);
$delim = self::$imap->get_hierarchy_delimiter();
$names = array();
$len = strlen($current);
if ($len && ($rpos = strrpos($current, $delim))) {
$parent = substr($current, 0, $rpos);
$p_len = strlen($parent);
}
// Filter folders list
foreach ($folders as $c_folder) {
$name = $c_folder->name;
// skip current folder and it's subfolders
if ($len) {
if ($name == $current) {
// Make sure parent folder is listed (might be skipped e.g. if it's namespace root)
if ($p_len && !isset($names[$parent])) {
$names[$parent] = self::object_name($parent);
}
continue;
}
if (strpos($name, $current.$delim) === 0) {
continue;
}
}
// always show the parent of current folder
if ($p_len && $name == $parent) {
}
// skip folders where user have no rights to create subfolders
else if ($c_folder->get_owner() != $_SESSION['username']) {
$rights = $c_folder->get_myrights();
if (!preg_match('/[ck]/', $rights)) {
continue;
}
}
$names[$name] = self::object_name($name);
}
// Build SELECT field of parent folder
$attrs['is_escaped'] = true;
$select = new html_select($attrs);
$select->add('---', '');
$listnames = array();
foreach (array_keys($names) as $imap_name) {
$name = $origname = $names[$imap_name];
// find folder prefix to truncate
for ($i = count($listnames)-1; $i >= 0; $i--) {
if (strpos($name, $listnames[$i].' &raquo; ') === 0) {
$length = strlen($listnames[$i].' &raquo; ');
$prefix = substr($name, 0, $length);
$count = count(explode(' &raquo; ', $prefix));
$name = str_repeat('&nbsp;&nbsp;', $count-1) . '&raquo; ' . substr($name, $length);
break;
}
}
$listnames[] = $origname;
$select->add($name, $imap_name);
}
return $select;
}
/**
* Returns a list of folder names
*
* @param string Optional root folder
* @param string Optional name pattern
* @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration)
* @param boolean Enable to return subscribed folders only (null to use configured subscription mode)
* @param array Will be filled with folder-types data
*
* @return array List of folders
*/
public static function list_folders($root = '', $mbox = '*', $filter = null, $subscribed = null, &$folderdata = array())
{
if (!self::setup()) {
return null;
}
// use IMAP subscriptions
if ($subscribed === null && self::$config->get('kolab_use_subscriptions')) {
$subscribed = true;
}
if (!$filter) {
// Get ALL folders list, standard way
if ($subscribed) {
$folders = self::$imap->list_folders_subscribed($root, $mbox);
// add temporarily subscribed folders
if (self::$with_tempsubs && is_array($_SESSION['kolab_subscribed_folders'])) {
$folders = array_unique(array_merge($folders, $_SESSION['kolab_subscribed_folders']));
}
}
else {
$folders = self::_imap_list_folders($root, $mbox);
}
return $folders;
}
$prefix = $root . $mbox;
$regexp = '/^' . preg_quote($filter, '/') . '(\..+)?$/';
// get folders types for all folders
if (!$subscribed || $prefix == '*' || !self::$config->get('kolab_skip_namespace')) {
$folderdata = self::folders_typedata($prefix);
}
else {
// fetch folder types for the effective list of (subscribed) folders when post-filtering
$folderdata = array();
}
if (!is_array($folderdata)) {
return array();
}
// In some conditions we can skip LIST command (?)
if (!$subscribed && $filter != 'mail' && $prefix == '*') {
foreach ($folderdata as $folder => $type) {
if (!preg_match($regexp, $type)) {
unset($folderdata[$folder]);
}
}
return self::$imap->sort_folder_list(array_keys($folderdata), true);
}
// Get folders list
if ($subscribed) {
$folders = self::$imap->list_folders_subscribed($root, $mbox);
// add temporarily subscribed folders
if (self::$with_tempsubs && is_array($_SESSION['kolab_subscribed_folders'])) {
$folders = array_unique(array_merge($folders, $_SESSION['kolab_subscribed_folders']));
}
}
else {
$folders = self::_imap_list_folders($root, $mbox);
}
// In case of an error, return empty list (?)
if (!is_array($folders)) {
return array();
}
// Filter folders list
foreach ($folders as $idx => $folder) {
// lookup folder type
if (!array_key_exists($folder, $folderdata)) {
$folderdata[$folder] = self::folder_type($folder);
}
$type = $folderdata[$folder];
if ($filter == 'mail' && empty($type)) {
continue;
}
if (empty($type) || !preg_match($regexp, $type)) {
unset($folders[$idx]);
}
}
return $folders;
}
/**
* Wrapper for rcube_imap::list_folders() with optional post-filtering
*/
protected static function _imap_list_folders($root, $mbox)
{
$postfilter = null;
// compose a post-filter expression for the excluded namespaces
if ($root . $mbox == '*' && ($skip_ns = self::$config->get('kolab_skip_namespace'))) {
$excludes = array();
foreach ((array)$skip_ns as $ns) {
if ($ns_root = self::namespace_root($ns)) {
$excludes[] = $ns_root;
}
}
if (count($excludes)) {
$postfilter = '!^(' . join(')|(', array_map('preg_quote', $excludes)) . ')!';
}
}
// use normal LIST command to return all folders, it's fast enough
$folders = self::$imap->list_folders($root, $mbox, null, null, !empty($postfilter));
if (!empty($postfilter)) {
$folders = array_filter($folders, function($folder) use ($postfilter) { return !preg_match($postfilter, $folder); });
$folders = self::$imap->sort_folder_list($folders);
}
return $folders;
}
/**
* Search for shared or otherwise not listed groupware folders the user has access
*
* @param string Folder type of folders to search for
* @param string Search string
* @param array Namespace(s) to exclude results from
*
* @return array List of matching kolab_storage_folder objects
*/
public static function search_folders($type, $query, $exclude_ns = array())
{
if (!self::setup()) {
return array();
}
$folders = array();
$query = str_replace('*', '', $query);
// find unsubscribed IMAP folders of the given type
foreach ((array)self::list_folders('', '*', $type, false, $folderdata) as $foldername) {
// FIXME: only consider the last part of the folder path for searching?
$realname = strtolower(rcube_charset::convert($foldername, 'UTF7-IMAP'));
if (($query == '' || strpos($realname, $query) !== false) &&
!self::folder_is_subscribed($foldername, true) &&
!in_array(self::$imap->folder_namespace($foldername), (array)$exclude_ns)
) {
- $folders[] = new kolab_storage_folder($foldername, $folderdata[$foldername]);
+ $folders[] = new kolab_storage_folder($foldername, $type, $folderdata[$foldername]);
}
}
return $folders;
}
/**
* Sort the given list of kolab folders by namespace/name
*
* @param array List of kolab_storage_folder objects
* @return array Sorted list of folders
*/
public static function sort_folders($folders)
{
$pad = ' ';
$out = array();
$nsnames = array('personal' => array(), 'shared' => array(), 'other' => array());
foreach ($folders as $folder) {
$folders[$folder->name] = $folder;
$ns = $folder->get_namespace();
$nsnames[$ns][$folder->name] = strtolower(html_entity_decode(self::object_name($folder->name, $ns), ENT_COMPAT, RCUBE_CHARSET)) . $pad; // decode &raquo;
}
// $folders is a result of get_folders() we can assume folders were already sorted
foreach (array_keys($nsnames) as $ns) {
asort($nsnames[$ns], SORT_LOCALE_STRING);
foreach (array_keys($nsnames[$ns]) as $utf7name) {
$out[] = $folders[$utf7name];
}
}
return $out;
}
/**
* Check the folder tree and add the missing parents as virtual folders
*
* @param array $folders Folders list
* @param object $tree Reference to the root node of the folder tree
*
* @return array Flat folders list
*/
public static function folder_hierarchy($folders, &$tree = null)
{
$_folders = array();
$delim = self::$imap->get_hierarchy_delimiter();
$other_ns = rtrim(self::namespace_root('other'), $delim);
$tree = new kolab_storage_folder_virtual('', '<root>', ''); // create tree root
$refs = array('' => $tree);
foreach ($folders as $idx => $folder) {
$path = explode($delim, $folder->name);
array_pop($path);
$folder->parent = join($delim, $path);
$folder->children = array(); // reset list
// skip top folders or ones with a custom displayname
if (count($path) < 1 || kolab_storage::custom_displayname($folder->name)) {
$tree->children[] = $folder;
}
else {
$parents = array();
$depth = $folder->get_namespace() == 'personal' ? 1 : 2;
while (count($path) >= $depth && ($parent = join($delim, $path))) {
array_pop($path);
$parent_parent = join($delim, $path);
if (!$refs[$parent]) {
if ($folder->type && self::folder_type($parent) == $folder->type) {
- $refs[$parent] = new kolab_storage_folder($parent, $folder->type);
+ $refs[$parent] = new kolab_storage_folder($parent, $folder->type, $folder->type);
$refs[$parent]->parent = $parent_parent;
}
else if ($parent_parent == $other_ns) {
$refs[$parent] = new kolab_storage_folder_user($parent, $parent_parent);
}
else {
$name = kolab_storage::object_name($parent, $folder->get_namespace());
$refs[$parent] = new kolab_storage_folder_virtual($parent, $name, $folder->get_namespace(), $parent_parent);
}
$parents[] = $refs[$parent];
}
}
if (!empty($parents)) {
$parents = array_reverse($parents);
foreach ($parents as $parent) {
$parent_node = $refs[$parent->parent] ?: $tree;
$parent_node->children[] = $parent;
$_folders[] = $parent;
}
}
$parent_node = $refs[$folder->parent] ?: $tree;
$parent_node->children[] = $folder;
}
$refs[$folder->name] = $folder;
$_folders[] = $folder;
unset($folders[$idx]);
}
return $_folders;
}
/**
* Returns folder types indexed by folder name
*
* @param string $prefix Folder prefix (Default '*' for all folders)
*
* @return array|bool List of folders, False on failure
*/
public static function folders_typedata($prefix = '*')
{
if (!self::setup()) {
return false;
}
// return cached result
if (is_array(self::$typedata[$prefix])) {
return self::$typedata[$prefix];
}
$type_keys = array(self::CTYPE_KEY, self::CTYPE_KEY_PRIVATE);
// fetch metadata from *some* folders only
if (($prefix == '*' || $prefix == '') && ($skip_ns = self::$config->get('kolab_skip_namespace'))) {
$delimiter = self::$imap->get_hierarchy_delimiter();
$folderdata = $blacklist = array();
foreach ((array)$skip_ns as $ns) {
if ($ns_root = rtrim(self::namespace_root($ns), $delimiter)) {
$blacklist[] = $ns_root;
}
}
foreach (array('personal','other','shared') as $ns) {
if (!in_array($ns, (array)$skip_ns)) {
$ns_root = rtrim(self::namespace_root($ns), $delimiter);
// list top-level folders and their childs one by one
// GETMETADATA "%" doesn't list shared or other namespace folders but "*" would
if ($ns_root == '') {
foreach ((array)self::$imap->get_metadata('%', $type_keys) as $folder => $metadata) {
if (!in_array($folder, $blacklist)) {
$folderdata[$folder] = $metadata;
- if ($data = self::$imap->get_metadata($folder.$delimiter.'*', $type_keys)) {
+ $opts = self::$imap->folder_attributes($folder);
+ if (!in_array('\\HasNoChildren', $opts) && ($data = self::$imap->get_metadata($folder.$delimiter.'*', $type_keys))) {
$folderdata += $data;
}
}
}
}
else if ($data = self::$imap->get_metadata($ns_root.$delimiter.'*', $type_keys)) {
$folderdata += $data;
}
}
}
}
else {
$folderdata = self::$imap->get_metadata($prefix, $type_keys);
}
if (!is_array($folderdata)) {
return false;
}
// keep list in memory
self::$typedata[$prefix] = array_map(array('kolab_storage', 'folder_select_metadata'), $folderdata);
return self::$typedata[$prefix];
}
/**
* Callback for array_map to select the correct annotation value
*/
public static function folder_select_metadata($types)
{
if (!empty($types[self::CTYPE_KEY_PRIVATE])) {
return $types[self::CTYPE_KEY_PRIVATE];
}
else if (!empty($types[self::CTYPE_KEY])) {
- list($ctype, $suffix) = explode('.', $types[self::CTYPE_KEY]);
+ list($ctype, ) = explode('.', $types[self::CTYPE_KEY]);
return $ctype;
}
return null;
}
/**
* Returns type of IMAP folder
*
* @param string $folder Folder name (UTF7-IMAP)
*
* @return string Folder type
*/
public static function folder_type($folder)
{
self::setup();
// return in-memory cached result
- if (is_array(self::$typedata['*']) && array_key_exists($folder, self::$typedata['*'])) {
- return self::$typedata['*'][$folder];
+ foreach (self::$typedata as $typedata) {
+ if (array_key_exists($folder, $typedata)) {
+ return $typedata[$folder];
+ }
}
$metadata = self::$imap->get_metadata($folder, array(self::CTYPE_KEY, self::CTYPE_KEY_PRIVATE));
if (!is_array($metadata)) {
return null;
}
if (!empty($metadata[$folder])) {
return self::folder_select_metadata($metadata[$folder]);
}
return 'mail';
}
/**
* Sets folder content-type.
*
* @param string $folder Folder name
* @param string $type Content type
*
* @return boolean True on success
*/
public static function set_folder_type($folder, $type='mail')
{
self::setup();
list($ctype, $subtype) = explode('.', $type);
$success = self::$imap->set_metadata($folder, array(self::CTYPE_KEY => $ctype, self::CTYPE_KEY_PRIVATE => $subtype ? $type : null));
if (!$success) // fallback: only set private annotation
$success |= self::$imap->set_metadata($folder, array(self::CTYPE_KEY_PRIVATE => $type));
return $success;
}
/**
* Check subscription status of this folder
*
* @param string $folder Folder name
* @param boolean $temp Include temporary/session subscriptions
*
* @return boolean True if subscribed, false if not
*/
public static function folder_is_subscribed($folder, $temp = false)
{
if (self::$subscriptions === null) {
self::setup();
self::$with_tempsubs = false;
self::$subscriptions = self::$imap->list_folders_subscribed();
self::$with_tempsubs = true;
}
return in_array($folder, self::$subscriptions) ||
($temp && in_array($folder, (array)$_SESSION['kolab_subscribed_folders']));
}
/**
* Change subscription status of this folder
*
* @param string $folder Folder name
* @param boolean $temp Only subscribe temporarily for the current session
*
* @return True on success, false on error
*/
public static function folder_subscribe($folder, $temp = false)
{
self::setup();
// temporary/session subscription
if ($temp) {
if (self::folder_is_subscribed($folder)) {
return true;
}
else if (!is_array($_SESSION['kolab_subscribed_folders']) || !in_array($folder, $_SESSION['kolab_subscribed_folders'])) {
$_SESSION['kolab_subscribed_folders'][] = $folder;
return true;
}
}
else if (self::$imap->subscribe($folder)) {
- self::$subscriptions === null;
+ self::$subscriptions = null;
return true;
}
return false;
}
/**
* Change subscription status of this folder
*
* @param string $folder Folder name
* @param boolean $temp Only remove temporary subscription
*
* @return True on success, false on error
*/
public static function folder_unsubscribe($folder, $temp = false)
{
self::setup();
// temporary/session subscription
if ($temp) {
if (is_array($_SESSION['kolab_subscribed_folders']) && ($i = array_search($folder, $_SESSION['kolab_subscribed_folders'])) !== false) {
unset($_SESSION['kolab_subscribed_folders'][$i]);
}
return true;
}
else if (self::$imap->unsubscribe($folder)) {
- self::$subscriptions === null;
+ self::$subscriptions = null;
return true;
}
return false;
}
/**
* Check activation status of this folder
*
* @param string $folder Folder name
*
* @return boolean True if active, false if not
*/
public static function folder_is_active($folder)
{
$active_folders = self::get_states();
return in_array($folder, $active_folders);
}
/**
* Change activation status of this folder
*
* @param string $folder Folder name
*
* @return True on success, false on error
*/
public static function folder_activate($folder)
{
// activation implies temporary subscription
self::folder_subscribe($folder, true);
return self::set_state($folder, true);
}
/**
* Change activation status of this folder
*
* @param string $folder Folder name
*
* @return True on success, false on error
*/
public static function folder_deactivate($folder)
{
// remove from temp subscriptions, really?
self::folder_unsubscribe($folder, true);
return self::set_state($folder, false);
}
/**
* Return list of active folders
*/
private static function get_states()
{
if (self::$states !== null) {
return self::$states;
}
$rcube = rcube::get_instance();
$folders = $rcube->config->get('kolab_active_folders');
if ($folders !== null) {
self::$states = !empty($folders) ? explode('**', $folders) : array();
}
// for backward-compatibility copy server-side subscriptions to activation states
else {
self::setup();
if (self::$subscriptions === null) {
self::$with_tempsubs = false;
self::$subscriptions = self::$imap->list_folders_subscribed();
self::$with_tempsubs = true;
}
self::$states = self::$subscriptions;
$folders = implode(self::$states, '**');
$rcube->user->save_prefs(array('kolab_active_folders' => $folders));
}
return self::$states;
}
/**
* Update list of active folders
*/
private static function set_state($folder, $state)
{
self::get_states();
// update in-memory list
$idx = array_search($folder, self::$states);
if ($state && $idx === false) {
self::$states[] = $folder;
}
else if (!$state && $idx !== false) {
unset(self::$states[$idx]);
}
// update user preferences
$folders = implode(self::$states, '**');
$rcube = rcube::get_instance();
return $rcube->user->save_prefs(array('kolab_active_folders' => $folders));
}
/**
* Creates default folder of specified type
* To be run when none of subscribed folders (of specified type) is found
*
* @param string $type Folder type
* @param string $props Folder properties (color, etc)
*
* @return string Folder name
*/
public static function create_default_folder($type, $props = array())
{
if (!self::setup()) {
return;
}
$folders = self::$imap->get_metadata('*', array(kolab_storage::CTYPE_KEY_PRIVATE));
// from kolab_folders config
$folder_type = strpos($type, '.') ? str_replace('.', '_', $type) : $type . '_default';
$default_name = self::$config->get('kolab_folders_' . $folder_type);
$folder_type = str_replace('_', '.', $folder_type);
// check if we have any folder in personal namespace
// folder(s) may exist but not subscribed
foreach ((array)$folders as $f => $data) {
if (strpos($data[self::CTYPE_KEY_PRIVATE], $type) === 0) {
$folder = $f;
break;
}
}
if (!$folder) {
if (!$default_name) {
$default_name = self::$default_folders[$type];
}
if (!$default_name) {
return;
}
$folder = rcube_charset::convert($default_name, RCUBE_CHARSET, 'UTF7-IMAP');
$prefix = self::$imap->get_namespace('prefix');
// add personal namespace prefix if needed
if ($prefix && strpos($folder, $prefix) !== 0 && $folder != 'INBOX') {
$folder = $prefix . $folder;
}
if (!self::$imap->folder_exists($folder)) {
if (!self::$imap->create_folder($folder)) {
return;
}
}
self::set_folder_type($folder, $folder_type);
}
self::folder_subscribe($folder);
if ($props['active']) {
self::set_state($folder, true);
}
if (!empty($props)) {
self::set_folder_props($folder, $props);
}
return $folder;
}
/**
* Sets folder metadata properties
*
* @param string $folder Folder name
* @param array $prop Folder properties
*/
public static function set_folder_props($folder, &$prop)
{
if (!self::setup()) {
return;
}
// TODO: also save 'showalarams' and other properties here
$ns = self::$imap->folder_namespace($folder);
$supported = array(
'color' => array(self::COLOR_KEY_SHARED, self::COLOR_KEY_PRIVATE),
'displayname' => array(self::NAME_KEY_SHARED, self::NAME_KEY_PRIVATE),
);
foreach ($supported as $key => $metakeys) {
if (array_key_exists($key, $prop)) {
$meta_saved = false;
if ($ns == 'personal') // save in shared namespace for personal folders
$meta_saved = self::$imap->set_metadata($folder, array($metakeys[0] => $prop[$key]));
if (!$meta_saved) // try in private namespace
$meta_saved = self::$imap->set_metadata($folder, array($metakeys[1] => $prop[$key]));
if ($meta_saved)
unset($prop[$key]); // unsetting will prevent fallback to local user prefs
}
}
}
/**
*
* @param mixed $query Search value (or array of field => value pairs)
* @param int $mode Matching mode: 0 - partial (*abc*), 1 - strict (=), 2 - prefix (abc*)
* @param array $required List of fields that shall ot be empty
* @param int $limit Maximum number of records
* @param int $count Returns the number of records found
*
* @return array List or false on error
*/
public static function search_users($query, $mode = 1, $required = array(), $limit = 0, &$count = 0)
{
$query = str_replace('*', '', $query);
// requires a working LDAP setup
if (!self::ldap() || strlen($query) == 0) {
return array();
}
// search users using the configured attributes
$results = self::$ldap->dosearch(self::$config->get('kolab_users_search_attrib', array('cn','mail','alias')), $query, $mode, $required, $limit, $count);
// exclude myself
if ($_SESSION['kolab_dn']) {
unset($results[$_SESSION['kolab_dn']]);
}
// resolve to IMAP folder name
$root = self::namespace_root('other');
$user_attrib = self::$config->get('kolab_users_id_attrib', self::$config->get('kolab_auth_login', 'mail'));
array_walk($results, function(&$user, $dn) use ($root, $user_attrib) {
- list($localpart, $domain) = explode('@', $user[$user_attrib]);
+ list($localpart, ) = explode('@', $user[$user_attrib]);
$user['kolabtargetfolder'] = $root . $localpart;
});
return $results;
}
/**
* Returns a list of IMAP folders shared by the given user
*
* @param array User entry from LDAP
* @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration)
* @param boolean Return subscribed folders only (null to use configured subscription mode)
* @param array Will be filled with folder-types data
*
* @return array List of folders
*/
public static function list_user_folders($user, $type, $subscribed = null, &$folderdata = array())
{
self::setup();
$folders = array();
// use localpart of user attribute as root for folder listing
$user_attrib = self::$config->get('kolab_users_id_attrib', self::$config->get('kolab_auth_login', 'mail'));
if (!empty($user[$user_attrib])) {
list($mbox) = explode('@', $user[$user_attrib]);
$delimiter = self::$imap->get_hierarchy_delimiter();
$other_ns = self::namespace_root('other');
$folders = self::list_folders($other_ns . $mbox . $delimiter, '*', $type, $subscribed, $folderdata);
}
return $folders;
}
/**
* Get a list of (virtual) top-level folders from the other users namespace
*
* @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration)
* @param boolean Enable to return subscribed folders only (null to use configured subscription mode)
*
* @return array List of kolab_storage_folder_user objects
*/
public static function get_user_folders($type, $subscribed)
{
$folders = $folderdata = array();
if (self::setup()) {
$delimiter = self::$imap->get_hierarchy_delimiter();
$other_ns = rtrim(self::namespace_root('other'), $delimiter);
$path_len = count(explode($delimiter, $other_ns));
foreach ((array)self::list_folders($other_ns . $delimiter, '*', '', $subscribed) as $foldername) {
if ($foldername == 'INBOX') // skip INBOX which is added by default
continue;
$path = explode($delimiter, $foldername);
// compare folder type if a subfolder is listed
if ($type && count($path) > $path_len + 1 && $type != self::folder_type($foldername)) {
continue;
}
// truncate folder path to top-level folders of the 'other' namespace
$foldername = join($delimiter, array_slice($path, 0, $path_len + 1));
if (!$folders[$foldername]) {
$folders[$foldername] = new kolab_storage_folder_user($foldername, $other_ns);
}
}
+
+ // for every (subscribed) user folder, list all (unsubscribed) subfolders
+ foreach ($folders as $userfolder) {
+ foreach ((array)self::list_folders($userfolder->name . $delimiter, '*', $type, false, $folderdata) as $foldername) {
+ if (!$folders[$foldername]) {
+ $folders[$foldername] = new kolab_storage_folder($foldername, $type, $folderdata[$foldername]);
+ $userfolder->children[] = $folders[$foldername];
+ }
+ }
+ }
}
return $folders;
}
/**
* Handler for user_delete plugin hooks
*
* Remove all cache data from the local database related to the given user.
*/
public static function delete_user_folders($args)
{
$db = rcmail::get_instance()->get_dbh();
$prefix = 'imap://' . urlencode($args['username']) . '@' . $args['host'] . '/%';
- $db->query("DELETE FROM " . $db->table_name('kolab_folders') . " WHERE resource LIKE ?", $prefix);
+ $db->query("DELETE FROM " . $db->table_name('kolab_folders', true) . " WHERE `resource` LIKE ?", $prefix);
}
-
}
-
diff --git a/lib/plugins/libkolab/lib/kolab_storage_cache.php b/lib/plugins/libkolab/lib/kolab_storage_cache.php
index d56f04d..227fa4e 100644
--- a/lib/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/lib/plugins/libkolab/lib/kolab_storage_cache.php
@@ -1,1012 +1,1135 @@
<?php
/**
* Kolab storage cache class providing a local caching layer for Kolab groupware objects.
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2012-2013, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_storage_cache
{
const DB_DATE_FORMAT = 'Y-m-d H:i:s';
+ public $sync_complete = false;
+
protected $db;
protected $imap;
protected $folder;
protected $uid2msg;
protected $objects;
protected $metadata = array();
protected $folder_id;
protected $resource_uri;
protected $enabled = true;
protected $synched = false;
protected $synclock = false;
protected $ready = false;
protected $cache_table;
protected $folders_table;
protected $max_sql_packet;
protected $max_sync_lock_time = 600;
protected $binary_items = array();
protected $extra_cols = array();
protected $order_by = null;
protected $limit = null;
+ protected $error = 0;
/**
* Factory constructor
*/
public static function factory(kolab_storage_folder $storage_folder)
{
$subclass = 'kolab_storage_cache_' . $storage_folder->type;
if (class_exists($subclass)) {
return new $subclass($storage_folder);
}
else {
rcube::raise_error(array(
'code' => 900,
'type' => 'php',
'message' => "No kolab_storage_cache class found for folder '$storage_folder->name' of type '$storage_folder->type'"
), true);
return new kolab_storage_cache($storage_folder);
}
}
/**
* Default constructor
*/
public function __construct(kolab_storage_folder $storage_folder = null)
{
$rcmail = rcube::get_instance();
$this->db = $rcmail->get_dbh();
$this->imap = $rcmail->get_storage();
$this->enabled = $rcmail->config->get('kolab_cache', false);
+ $this->folders_table = $this->db->table_name('kolab_folders');
if ($this->enabled) {
// always read folder cache and lock state from DB master
$this->db->set_table_dsn('kolab_folders', 'w');
// remove sync-lock on script termination
$rcmail->add_shutdown_function(array($this, '_sync_unlock'));
}
if ($storage_folder)
$this->set_folder($storage_folder);
}
/**
* Direct access to cache by folder_id
* (only for internal use)
*/
public function select_by_id($folder_id)
{
- $folders_table = $this->db->table_name('kolab_folders');
- $sql_arr = $this->db->fetch_assoc($this->db->query("SELECT * FROM $folders_table WHERE folder_id=?", $folder_id));
+ $sql_arr = $this->db->fetch_assoc($this->db->query("SELECT * FROM `{$this->folders_table}` WHERE `folder_id` = ?", $folder_id));
if ($sql_arr) {
$this->metadata = $sql_arr;
$this->folder_id = $sql_arr['folder_id'];
$this->folder = new StdClass;
$this->folder->type = $sql_arr['type'];
$this->resource_uri = $sql_arr['resource'];
$this->cache_table = $this->db->table_name('kolab_cache_' . $sql_arr['type']);
$this->ready = true;
}
}
/**
* Connect cache with a storage folder
*
* @param kolab_storage_folder The storage folder instance to connect with
*/
public function set_folder(kolab_storage_folder $storage_folder)
{
$this->folder = $storage_folder;
- if (empty($this->folder->name)) {
+ if (empty($this->folder->name) || !$this->folder->valid) {
$this->ready = false;
return;
}
// compose fully qualified ressource uri for this instance
$this->resource_uri = $this->folder->get_resource_uri();
- $this->folders_table = $this->db->table_name('kolab_folders');
$this->cache_table = $this->db->table_name('kolab_cache_' . $this->folder->type);
$this->ready = $this->enabled && !empty($this->folder->type);
$this->folder_id = null;
}
/**
* Returns true if this cache supports query by type
*/
public function has_type_col()
{
return in_array('type', $this->extra_cols);
}
/**
* Getter for the numeric ID used in cache tables
*/
public function get_folder_id()
{
$this->_read_folder_data();
return $this->folder_id;
}
+ /**
+ * Returns code of last error
+ *
+ * @return int Error code
+ */
+ public function get_error()
+ {
+ return $this->error;
+ }
+
/**
* Synchronize local cache data with remote
*/
public function synchronize()
{
// only sync once per request cycle
if ($this->synched)
return;
// increase time limit
- @set_time_limit($this->max_sync_lock_time);
+ @set_time_limit($this->max_sync_lock_time - 60);
+
+ // get effective time limit we have for synchronization (~70% of the execution time)
+ $time_limit = ini_get('max_execution_time') * 0.7;
+ $sync_start = time();
+
+ // assume sync will be completed
+ $this->sync_complete = true;
if (!$this->ready) {
// kolab cache is disabled, synchronize IMAP mailbox cache only
$this->imap->folder_sync($this->folder->name);
}
else {
// read cached folder metadata
$this->_read_folder_data();
// check cache status hash first ($this->metadata is set in _read_folder_data())
if ($this->metadata['ctag'] != $this->folder->get_ctag()) {
// lock synchronization for this folder or wait if locked
$this->_sync_lock();
// disable messages cache if configured to do so
$this->bypass(true);
// synchronize IMAP mailbox cache
$this->imap->folder_sync($this->folder->name);
// compare IMAP index with object cache index
$imap_index = $this->imap->index($this->folder->name, null, null, true, true);
// determine objects to fetch or to invalidate
if (!$imap_index->is_error()) {
$imap_index = $imap_index->get();
// read cache index
$sql_result = $this->db->query(
- "SELECT msguid, uid FROM $this->cache_table WHERE folder_id=?",
+ "SELECT `msguid`, `uid` FROM `{$this->cache_table}` WHERE `folder_id` = ?",
$this->folder_id
);
$old_index = array();
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
$old_index[] = $sql_arr['msguid'];
- $this->uid2msg[$sql_arr['uid']] = $sql_arr['msguid'];
}
// fetch new objects from imap
+ $i = 0;
foreach (array_diff($imap_index, $old_index) as $msguid) {
if ($object = $this->folder->read_object($msguid, '*')) {
$this->_extended_insert($msguid, $object);
+
+ // check time limit and abort sync if running too long
+ if (++$i % 50 == 0 && time() - $sync_start > $time_limit) {
+ $this->sync_complete = false;
+ break;
+ }
}
}
$this->_extended_insert(0, null);
// delete invalid entries from local DB
$del_index = array_diff($old_index, $imap_index);
if (!empty($del_index)) {
$quoted_ids = join(',', array_map(array($this->db, 'quote'), $del_index));
$this->db->query(
- "DELETE FROM $this->cache_table WHERE folder_id=? AND msguid IN ($quoted_ids)",
+ "DELETE FROM `{$this->cache_table}` WHERE `folder_id` = ? AND `msguid` IN ($quoted_ids)",
$this->folder_id
);
}
// update ctag value (will be written to database in _sync_unlock())
- $this->metadata['ctag'] = $this->folder->get_ctag();
+ if ($this->sync_complete) {
+ $this->metadata['ctag'] = $this->folder->get_ctag();
+ }
}
$this->bypass(false);
// remove lock
$this->_sync_unlock();
}
}
+ $this->check_error();
$this->synched = time();
}
/**
* Read a single entry from cache or from IMAP directly
*
* @param string Related IMAP message UID
* @param string Object type to read
* @param string IMAP folder name the entry relates to
* @param array Hash array with object properties or null if not found
*/
public function get($msguid, $type = null, $foldername = null)
{
// delegate to another cache instance
if ($foldername && $foldername != $this->folder->name) {
- return kolab_storage::get_folder($foldername)->cache->get($msguid, $type);
+ $success = false;
+ if ($targetfolder = kolab_storage::get_folder($foldername)) {
+ $success = $targetfolder->cache->get($msguid, $type);
+ $this->error = $targetfolder->cache->get_error();
+ }
+ return $success;
}
// load object if not in memory
if (!isset($this->objects[$msguid])) {
if ($this->ready) {
$this->_read_folder_data();
$sql_result = $this->db->query(
- "SELECT * FROM $this->cache_table ".
- "WHERE folder_id=? AND msguid=?",
+ "SELECT * FROM `{$this->cache_table}` ".
+ "WHERE `folder_id` = ? AND `msguid` = ?",
$this->folder_id,
$msguid
);
if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
$this->objects = array($msguid => $this->_unserialize($sql_arr)); // store only this object in memory (#2827)
}
}
// fetch from IMAP if not present in cache
if (empty($this->objects[$msguid])) {
- $result = $this->_fetch(array($msguid), $type, $foldername);
- $this->objects = array($msguid => $result[0]); // store only this object in memory (#2827)
+ if ($object = $this->folder->read_object($msguid, $type ?: '*', $foldername)) {
+ $this->objects = array($msguid => $object);
+ $this->set($msguid, $object);
+ }
}
}
+ $this->check_error();
return $this->objects[$msguid];
}
/**
* Insert/Update a cache entry
*
* @param string Related IMAP message UID
* @param mixed Hash array with object properties to save or false to delete the cache entry
* @param string IMAP folder name the entry relates to
*/
public function set($msguid, $object, $foldername = null)
{
if (!$msguid) {
return;
}
// delegate to another cache instance
if ($foldername && $foldername != $this->folder->name) {
- kolab_storage::get_folder($foldername)->cache->set($msguid, $object);
- return;
+ if ($targetfolder = kolab_storage::get_folder($foldername)) {
+ $targetfolder->cache->set($msguid, $object);
+ $this->error = $targetfolder->cache->get_error();
+ }
+ return;
}
// remove old entry
if ($this->ready) {
$this->_read_folder_data();
- $this->db->query("DELETE FROM $this->cache_table WHERE folder_id=? AND msguid=?",
+ $this->db->query("DELETE FROM `{$this->cache_table}` WHERE `folder_id` = ? AND `msguid` = ?",
$this->folder_id, $msguid);
}
if ($object) {
// insert new object data...
$this->save($msguid, $object);
}
else {
// ...or set in-memory cache to false
$this->objects[$msguid] = $object;
}
+
+ $this->check_error();
}
/**
* Insert (or update) a cache entry
*
* @param int Related IMAP message UID
* @param mixed Hash array with object properties to save or false to delete the cache entry
* @param int Optional old message UID (for update)
*/
public function save($msguid, $object, $olduid = null)
{
// write to cache
if ($this->ready) {
$this->_read_folder_data();
$sql_data = $this->_serialize($object);
$sql_data['folder_id'] = $this->folder_id;
$sql_data['msguid'] = $msguid;
$sql_data['uid'] = $object['uid'];
$args = array();
$cols = array('folder_id', 'msguid', 'uid', 'changed', 'data', 'xml', 'tags', 'words');
$cols = array_merge($cols, $this->extra_cols);
foreach ($cols as $idx => $col) {
$cols[$idx] = $this->db->quote_identifier($col);
$args[] = $sql_data[$col];
}
if ($olduid) {
foreach ($cols as $idx => $col) {
$cols[$idx] = "$col = ?";
}
- $query = "UPDATE $this->cache_table SET " . implode(', ', $cols)
- . " WHERE folder_id = ? AND msguid = ?";
+ $query = "UPDATE `{$this->cache_table}` SET " . implode(', ', $cols)
+ . " WHERE `folder_id` = ? AND `msguid` = ?";
$args[] = $this->folder_id;
$args[] = $olduid;
}
else {
- $query = "INSERT INTO $this->cache_table (created, " . implode(', ', $cols)
+ $query = "INSERT INTO `{$this->cache_table}` (`created`, " . implode(', ', $cols)
. ") VALUES (" . $this->db->now() . str_repeat(', ?', count($cols)) . ")";
}
$result = $this->db->query($query, $args);
if (!$this->db->affected_rows($result)) {
rcube::raise_error(array(
'code' => 900, 'type' => 'php',
'message' => "Failed to write to kolab cache"
), true);
}
}
// keep a copy in memory for fast access
$this->objects = array($msguid => $object);
$this->uid2msg = array($object['uid'] => $msguid);
+
+ $this->check_error();
}
/**
* Move an existing cache entry to a new resource
*
* @param string Entry's IMAP message UID
* @param string Entry's Object UID
- * @param string Target IMAP folder to move it to
+ * @param object kolab_storage_folder Target storage folder instance
*/
- public function move($msguid, $uid, $target_folder)
+ public function move($msguid, $uid, $target)
{
if ($this->ready) {
- $target = kolab_storage::get_folder($target_folder);
+ // clear cached uid mapping and force new lookup
+ unset($target->cache->uid2msg[$uid]);
// resolve new message UID in target folder
if ($new_msguid = $target->cache->uid2msguid($uid)) {
$this->_read_folder_data();
$this->db->query(
- "UPDATE $this->cache_table SET folder_id=?, msguid=? ".
- "WHERE folder_id=? AND msguid=?",
+ "UPDATE `{$this->cache_table}` SET `folder_id` = ?, `msguid` = ? ".
+ "WHERE `folder_id` = ? AND `msguid` = ?",
$target->cache->get_folder_id(),
$new_msguid,
$this->folder_id,
$msguid
);
$result = $this->db->affected_rows();
}
}
if (empty($result)) {
// just clear cache entry
$this->set($msguid, false);
}
unset($this->uid2msg[$uid]);
+ $this->check_error();
}
/**
* Remove all objects from local cache
*/
- public function purge($type = null)
+ public function purge()
{
if (!$this->ready) {
return true;
}
$this->_read_folder_data();
$result = $this->db->query(
- "DELETE FROM $this->cache_table WHERE folder_id=?",
+ "DELETE FROM `{$this->cache_table}` WHERE `folder_id` = ?",
$this->folder_id
);
return $this->db->affected_rows($result);
}
/**
* Update resource URI for existing cache entries
*
* @param string Target IMAP folder to move it to
*/
public function rename($new_folder)
{
if (!$this->ready) {
return;
}
- $target = kolab_storage::get_folder($new_folder);
+ if ($target = kolab_storage::get_folder($new_folder)) {
+ // resolve new message UID in target folder
+ $this->db->query(
+ "UPDATE `{$this->folders_table}` SET `resource` = ? ".
+ "WHERE `resource` = ?",
+ $target->get_resource_uri(),
+ $this->resource_uri
+ );
- // resolve new message UID in target folder
- $this->db->query(
- "UPDATE $this->folders_table SET resource=? ".
- "WHERE resource=?",
- $target->get_resource_uri(),
- $this->resource_uri
- );
+ $this->check_error();
+ }
+ else {
+ $this->error = kolab_storage::ERROR_IMAP_CONN;
+ }
}
/**
* Select Kolab objects filtered by the given query
*
* @param array Pseudo-SQL query as list of filter parameter triplets
* triplet: array('<colname>', '<comparator>', '<value>')
* @param boolean Set true to only return UIDs instead of complete objects
* @return array List of Kolab data objects (each represented as hash array) or UIDs
*/
public function select($query = array(), $uids = false)
{
$result = $uids ? array() : new kolab_storage_dataset($this);
// read from local cache DB (assume it to be synchronized)
if ($this->ready) {
$this->_read_folder_data();
// fetch full object data on one query if a small result set is expected
$fetchall = !$uids && ($this->limit ? $this->limit[0] : $this->count($query)) < 500;
- $sql_query = "SELECT " . ($fetchall ? '*' : 'msguid AS _msguid, uid') . " FROM $this->cache_table ".
- "WHERE folder_id=? " . $this->_sql_where($query);
+ $sql_query = "SELECT " . ($fetchall ? '*' : '`msguid` AS `_msguid`, `uid`') . " FROM `{$this->cache_table}` ".
+ "WHERE `folder_id` = ? " . $this->_sql_where($query);
if (!empty($this->order_by)) {
$sql_query .= ' ORDER BY ' . $this->order_by;
}
$sql_result = $this->limit ?
$this->db->limitquery($sql_query, $this->limit[1], $this->limit[0], $this->folder_id) :
$this->db->query($sql_query, $this->folder_id);
if ($this->db->is_error($sql_result)) {
if ($uids) {
return null;
}
$result->set_error(true);
return $result;
}
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
if ($uids) {
$this->uid2msg[$sql_arr['uid']] = $sql_arr['_msguid'];
$result[] = $sql_arr['uid'];
}
else if ($fetchall && ($object = $this->_unserialize($sql_arr))) {
$result[] = $object;
}
else if (!$fetchall) {
// only add msguid to dataset index
$result[] = $sql_arr;
}
}
}
// use IMAP
else {
$filter = $this->_query2assoc($query);
if ($filter['type']) {
$search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type'];
$index = $this->imap->search_once($this->folder->name, $search);
}
else {
$index = $this->imap->index($this->folder->name, null, null, true, true);
}
if ($index->is_error()) {
+ $this->check_error();
if ($uids) {
return null;
}
$result->set_error(true);
return $result;
}
$index = $index->get();
$result = $uids ? $index : $this->_fetch($index, $filter['type']);
// TODO: post-filter result according to query
}
// We don't want to cache big results in-memory, however
// if we select only one object here, there's a big chance we will need it later
if (!$uids && count($result) == 1) {
if ($msguid = $result[0]['_msguid']) {
$this->uid2msg[$result[0]['uid']] = $msguid;
$this->objects = array($msguid => $result[0]);
}
}
+ $this->check_error();
+
return $result;
}
/**
* Get number of objects mathing the given query
*
* @param array $query Pseudo-SQL query as list of filter parameter triplets
* @return integer The number of objects of the given type
*/
public function count($query = array())
{
// read from local cache DB (assume it to be synchronized)
if ($this->ready) {
$this->_read_folder_data();
$sql_result = $this->db->query(
- "SELECT COUNT(*) AS numrows FROM $this->cache_table ".
- "WHERE folder_id=? " . $this->_sql_where($query),
+ "SELECT COUNT(*) AS `numrows` FROM `{$this->cache_table}` ".
+ "WHERE `folder_id` = ?" . $this->_sql_where($query),
$this->folder_id
);
if ($this->db->is_error($sql_result)) {
return null;
}
$sql_arr = $this->db->fetch_assoc($sql_result);
$count = intval($sql_arr['numrows']);
}
// use IMAP
else {
$filter = $this->_query2assoc($query);
if ($filter['type']) {
$search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type'];
$index = $this->imap->search_once($this->folder->name, $search);
}
else {
$index = $this->imap->index($this->folder->name, null, null, true, true);
}
if ($index->is_error()) {
+ $this->check_error();
return null;
}
// TODO: post-filter result according to query
$count = $index->count();
}
+ $this->check_error();
return $count;
}
/**
* Define ORDER BY clause for cache queries
*/
public function set_order_by($sortcols)
{
if (!empty($sortcols)) {
- $this->order_by = join(', ', (array)$sortcols);
+ $this->order_by = '`' . join('`, `', (array)$sortcols) . '`';
}
else {
$this->order_by = null;
}
}
/**
* Define LIMIT clause for cache queries
*/
public function set_limit($length, $offset = 0)
{
$this->limit = array($length, $offset);
}
/**
* Helper method to compose a valid SQL query from pseudo filter triplets
*/
protected function _sql_where($query)
{
$sql_where = '';
foreach ((array) $query as $param) {
if (is_array($param[0])) {
$subq = array();
foreach ($param[0] as $q) {
$subq[] = preg_replace('/^\s*AND\s+/i', '', $this->_sql_where(array($q)));
}
if (!empty($subq)) {
$sql_where .= ' AND (' . implode($param[1] == 'OR' ? ' OR ' : ' AND ', $subq) . ')';
}
continue;
}
else if ($param[1] == '=' && is_array($param[2])) {
$qvalue = '(' . join(',', array_map(array($this->db, 'quote'), $param[2])) . ')';
$param[1] = 'IN';
}
else if ($param[1] == '~' || $param[1] == 'LIKE' || $param[1] == '!~' || $param[1] == '!LIKE') {
$not = ($param[1] == '!~' || $param[1] == '!LIKE') ? 'NOT ' : '';
$param[1] = $not . 'LIKE';
$qvalue = $this->db->quote('%'.preg_replace('/(^\^|\$$)/', ' ', $param[2]).'%');
}
else if ($param[0] == 'tags') {
$param[1] = ($param[1] == '!=' ? 'NOT ' : '' ) . 'LIKE';
$qvalue = $this->db->quote('% '.$param[2].' %');
}
else {
$qvalue = $this->db->quote($param[2]);
}
$sql_where .= sprintf(' AND %s %s %s',
$this->db->quote_identifier($param[0]),
$param[1],
$qvalue
);
}
return $sql_where;
}
/**
* Helper method to convert the given pseudo-query triplets into
* an associative filter array with 'equals' values only
*/
protected function _query2assoc($query)
{
// extract object type from query parameter
$filter = array();
foreach ($query as $param) {
if ($param[1] == '=')
$filter[$param[0]] = $param[2];
}
return $filter;
}
/**
* Fetch messages from IMAP
*
* @param array List of message UIDs to fetch
* @param string Requested object type or * for all
* @param string IMAP folder to read from
* @return array List of parsed Kolab objects
*/
protected function _fetch($index, $type = null, $folder = null)
{
$results = new kolab_storage_dataset($this);
foreach ((array)$index as $msguid) {
if ($object = $this->folder->read_object($msguid, $type, $folder)) {
$results[] = $object;
$this->set($msguid, $object);
}
}
return $results;
}
/**
* Helper method to convert the given Kolab object into a dataset to be written to cache
*/
protected function _serialize($object)
{
$sql_data = array('changed' => null, 'xml' => '', 'tags' => '', 'words' => '');
if ($object['changed']) {
$sql_data['changed'] = date('Y-m-d H:i:s', is_object($object['changed']) ? $object['changed']->format('U') : $object['changed']);
}
if ($object['_formatobj']) {
$sql_data['xml'] = preg_replace('!(</?[a-z0-9:-]+>)[\n\r\t\s]+!ms', '$1', (string)$object['_formatobj']->write(3.0));
$sql_data['tags'] = ' ' . join(' ', $object['_formatobj']->get_tags()) . ' '; // pad with spaces for strict/prefix search
$sql_data['words'] = ' ' . join(' ', $object['_formatobj']->get_words()) . ' ';
}
// extract object data
$data = array();
foreach ($object as $key => $val) {
// skip empty properties
if ($val === "" || $val === null) {
continue;
}
// mark binary data to be extracted from xml on unserialize()
if (isset($this->binary_items[$key])) {
$data[$key] = true;
}
else if ($key[0] != '_') {
$data[$key] = $val;
}
else if ($key == '_attachments') {
foreach ($val as $k => $att) {
unset($att['content'], $att['path']);
if ($att['id'])
$data[$key][$k] = $att;
}
}
}
// use base64 encoding (Bug #1912, #2662)
$sql_data['data'] = base64_encode(serialize($data));
return $sql_data;
}
/**
* Helper method to turn stored cache data into a valid storage object
*/
protected function _unserialize($sql_arr)
{
// check if data is a base64-encoded string, for backward compat.
if (strpos(substr($sql_arr['data'], 0, 64), ':') === false) {
$sql_arr['data'] = base64_decode($sql_arr['data']);
}
$object = unserialize($sql_arr['data']);
// de-serialization failed
if ($object === false) {
rcube::raise_error(array(
'code' => 900, 'type' => 'php',
'message' => "Malformed data for {$this->resource_uri}/{$sql_arr['msguid']} object."
), true);
return null;
}
// decode binary properties
foreach ($this->binary_items as $key => $regexp) {
if (!empty($object[$key]) && preg_match($regexp, $sql_arr['xml'], $m)) {
$object[$key] = base64_decode($m[1]);
}
}
$object_type = $sql_arr['type'] ?: $this->folder->type;
$format_type = $this->folder->type == 'configuration' ? 'configuration' : $object_type;
// add meta data
$object['_type'] = $object_type;
$object['_msguid'] = $sql_arr['msguid'];
$object['_mailbox'] = $this->folder->name;
$object['_size'] = strlen($sql_arr['xml']);
$object['_formatobj'] = kolab_format::factory($format_type, 3.0, $sql_arr['xml']);
return $object;
}
/**
* Write records into cache using extended inserts to reduce the number of queries to be executed
*
* @param int Message UID. Set 0 to commit buffered inserts
* @param array Kolab object to cache
*/
protected function _extended_insert($msguid, $object)
{
static $buffer = '';
$line = '';
if ($object) {
$sql_data = $this->_serialize($object);
+
+ // Skip multifolder insert for Oracle, we can't put long data inline
+ if ($this->db->db_provider == 'oracle') {
+ $extra_cols = '';
+ if ($this->extra_cols) {
+ $extra_cols = array_map(function($n) { return "`{$n}`"; }, $this->extra_cols);
+ $extra_cols = ', ' . join(', ', $extra_cols);
+ $extra_args = str_repeat(', ?', count($this->extra_cols));
+ }
+
+ $params = array($this->folder_id, $msguid, $object['uid'], $sql_data['changed'],
+ $sql_data['data'], $sql_data['xml'], $sql_data['tags'], $sql_data['words']);
+
+ foreach ($this->extra_cols as $col) {
+ $params[] = $sql_data[$col];
+ }
+
+ $result = $this->db->query(
+ "INSERT INTO `{$this->cache_table}` "
+ . " (`folder_id`, `msguid`, `uid`, `created`, `changed`, `data`, `xml`, `tags`, `words` $extra_cols)"
+ . " VALUES (?, ?, ?, " . $this->db->now() . ", ?, ?, ?, ?, ? $extra_args)",
+ $params
+ );
+
+ if (!$this->db->affected_rows($result)) {
+ rcube::raise_error(array(
+ 'code' => 900, 'type' => 'php',
+ 'message' => "Failed to write to kolab cache"
+ ), true);
+ }
+
+ return;
+ }
+
$values = array(
$this->db->quote($this->folder_id),
$this->db->quote($msguid),
$this->db->quote($object['uid']),
$this->db->now(),
$this->db->quote($sql_data['changed']),
$this->db->quote($sql_data['data']),
$this->db->quote($sql_data['xml']),
$this->db->quote($sql_data['tags']),
$this->db->quote($sql_data['words']),
);
foreach ($this->extra_cols as $col) {
$values[] = $this->db->quote($sql_data[$col]);
}
$line = '(' . join(',', $values) . ')';
}
if ($buffer && (!$msguid || (strlen($buffer) + strlen($line) > $this->max_sql_packet()))) {
- $extra_cols = $this->extra_cols ? ', ' . join(', ', $this->extra_cols) : '';
+ $extra_cols = '';
+ if ($this->extra_cols) {
+ $extra_cols = array_map(function($n) { return "`{$n}`"; }, $this->extra_cols);
+ $extra_cols = ', ' . join(', ', $extra_cols);
+ }
+
$result = $this->db->query(
- "INSERT INTO $this->cache_table ".
- " (folder_id, msguid, uid, created, changed, data, xml, tags, words $extra_cols)".
+ "INSERT INTO `{$this->cache_table}` ".
+ " (`folder_id`, `msguid`, `uid`, `created`, `changed`, `data`, `xml`, `tags`, `words` $extra_cols)".
" VALUES $buffer"
);
+
if (!$this->db->affected_rows($result)) {
rcube::raise_error(array(
'code' => 900, 'type' => 'php',
'message' => "Failed to write to kolab cache"
), true);
}
$buffer = '';
}
$buffer .= ($buffer ? ',' : '') . $line;
}
/**
* Returns max_allowed_packet from mysql config
*/
protected function max_sql_packet()
{
if (!$this->max_sql_packet) {
// mysql limit or max 4 MB
$value = $this->db->get_variable('max_allowed_packet', 1048500);
$this->max_sql_packet = min($value, 4*1024*1024) - 2000;
}
return $this->max_sql_packet;
}
/**
* Read this folder's ID and cache metadata
*/
protected function _read_folder_data()
{
// already done
if (!empty($this->folder_id) || !$this->ready)
return;
- $sql_arr = $this->db->fetch_assoc($this->db->query("SELECT folder_id, synclock, ctag FROM $this->folders_table WHERE resource=?", $this->resource_uri));
+ $sql_arr = $this->db->fetch_assoc($this->db->query(
+ "SELECT `folder_id`, `synclock`, `ctag`"
+ . " FROM `{$this->folders_table}` WHERE `resource` = ?",
+ $this->resource_uri
+ ));
+
if ($sql_arr) {
$this->metadata = $sql_arr;
$this->folder_id = $sql_arr['folder_id'];
}
else {
- $this->db->query("INSERT INTO $this->folders_table (resource, type) VALUES (?, ?)", $this->resource_uri, $this->folder->type);
+ $this->db->query("INSERT INTO `{$this->folders_table}` (`resource`, `type`)"
+ . " VALUES (?, ?)", $this->resource_uri, $this->folder->type);
+
$this->folder_id = $this->db->insert_id('kolab_folders');
$this->metadata = array();
}
}
/**
* Check lock record for this folder and wait if locked or set lock
*/
protected function _sync_lock()
{
if (!$this->ready)
return;
$this->_read_folder_data();
- $sql_query = "SELECT synclock, ctag FROM $this->folders_table WHERE folder_id=?";
// abort if database is not set-up
if ($this->db->is_error()) {
+ $this->check_error();
$this->ready = false;
return;
}
- $this->synclock = true;
+ $read_query = "SELECT `synclock`, `ctag` FROM `{$this->folders_table}` WHERE `folder_id` = ?";
+ $write_query = "UPDATE `{$this->folders_table}` SET `synclock` = ? WHERE `folder_id` = ? AND `synclock` = ?";
- // wait if locked (expire locks after 10 minutes)
- while ($this->metadata && intval($this->metadata['synclock']) > 0 && $this->metadata['synclock'] + $this->max_sync_lock_time > time()) {
+ // wait if locked (expire locks after 10 minutes) ...
+ // ... or if setting lock fails (another process meanwhile set it)
+ while (
+ (intval($this->metadata['synclock']) + $this->max_sync_lock_time > time()) ||
+ (($res = $this->db->query($write_query, time(), $this->folder_id, intval($this->metadata['synclock']))) &&
+ !($affected = $this->db->affected_rows($res)))
+ ) {
usleep(500000);
- $this->metadata = $this->db->fetch_assoc($this->db->query($sql_query, $this->folder_id));
+ $this->metadata = $this->db->fetch_assoc($this->db->query($read_query, $this->folder_id));
}
- // set lock
- $this->db->query("UPDATE $this->folders_table SET synclock = ? WHERE folder_id = ?", time(), $this->folder_id);
+ $this->synclock = $affected > 0;
}
/**
* Remove lock for this folder
*/
public function _sync_unlock()
{
if (!$this->ready || !$this->synclock)
return;
$this->db->query(
- "UPDATE $this->folders_table SET synclock = 0, ctag = ? WHERE folder_id = ?",
+ "UPDATE `{$this->folders_table}` SET `synclock` = 0, `ctag` = ? WHERE `folder_id` = ?",
$this->metadata['ctag'],
$this->folder_id
);
$this->synclock = false;
}
+ /**
+ * Check IMAP connection error state
+ */
+ protected function check_error()
+ {
+ if (($err_code = $this->imap->get_error_code()) < 0) {
+ $this->error = kolab_storage::ERROR_IMAP_CONN;
+ if (($res_code = $this->imap->get_response_code()) !== 0 && in_array($res_code, array(rcube_storage::NOPERM, rcube_storage::READONLY))) {
+ $this->error = kolab_storage::ERROR_NO_PERMISSION;
+ }
+ }
+ else if ($this->db->is_error()) {
+ $this->error = kolab_storage::ERROR_CACHE_DB;
+ }
+ }
+
/**
* Resolve an object UID into an IMAP message UID
*
* @param string Kolab object UID
* @param boolean Include deleted objects
* @return int The resolved IMAP message UID
*/
public function uid2msguid($uid, $deleted = false)
{
// query local database if available
if (!isset($this->uid2msg[$uid]) && $this->ready) {
$this->_read_folder_data();
$sql_result = $this->db->query(
- "SELECT msguid FROM $this->cache_table ".
- "WHERE folder_id=? AND uid=? ORDER BY msguid DESC",
+ "SELECT `msguid` FROM `{$this->cache_table}` ".
+ "WHERE `folder_id` = ? AND `uid` = ? ORDER BY `msguid` DESC",
$this->folder_id,
$uid
);
if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
$this->uid2msg[$uid] = $sql_arr['msguid'];
}
}
if (!isset($this->uid2msg[$uid])) {
// use IMAP SEARCH to get the right message
$index = $this->imap->search_once($this->folder->name, ($deleted ? '' : 'UNDELETED ') .
'HEADER SUBJECT ' . rcube_imap_generic::escape($uid));
$results = $index->get();
$this->uid2msg[$uid] = end($results);
}
return $this->uid2msg[$uid];
}
/**
* Getter for protected member variables
*/
public function __get($name)
{
if ($name == 'folder_id') {
$this->_read_folder_data();
}
return $this->$name;
}
/**
* Bypass Roundcube messages cache.
* Roundcube cache duplicates information already stored in kolab_cache.
*
* @param bool $disable True disables, False enables messages cache
*/
public function bypass($disable = false)
{
// if kolab cache is disabled do nothing
if (!$this->enabled) {
return;
}
static $messages_cache, $cache_bypass;
if ($messages_cache === null) {
$rcmail = rcube::get_instance();
$messages_cache = (bool) $rcmail->config->get('messages_cache');
$cache_bypass = (int) $rcmail->config->get('kolab_messages_cache_bypass');
}
if ($messages_cache) {
// handle recurrent (multilevel) bypass() calls
if ($disable) {
$this->cache_bypassed += 1;
if ($this->cache_bypassed > 1) {
return;
}
}
else {
$this->cache_bypassed -= 1;
if ($this->cache_bypassed > 0) {
return;
}
}
switch ($cache_bypass) {
case 2:
// Disable messages cache completely
$this->imap->set_messages_caching(!$disable);
break;
case 1:
// We'll disable messages cache, but keep index cache.
// Default mode is both (MODE_INDEX | MODE_MESSAGE)
$mode = rcube_imap_cache::MODE_INDEX;
if (!$disable) {
$mode |= rcube_imap_cache::MODE_MESSAGE;
}
$this->imap->set_messages_caching(true, $mode);
}
}
}
}
diff --git a/lib/plugins/libkolab/lib/kolab_storage_cache_configuration.php b/lib/plugins/libkolab/lib/kolab_storage_cache_configuration.php
index ec015dd..c3c7ac4 100644
--- a/lib/plugins/libkolab/lib/kolab_storage_cache_configuration.php
+++ b/lib/plugins/libkolab/lib/kolab_storage_cache_configuration.php
@@ -1,66 +1,88 @@
<?php
/**
* Kolab storage cache class for configuration objects
*
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_storage_cache_configuration extends kolab_storage_cache
{
protected $extra_cols = array('type');
/**
* Helper method to convert the given Kolab object into a dataset to be written to cache
*
* @override
*/
protected function _serialize($object)
{
$sql_data = parent::_serialize($object);
$sql_data['type'] = $object['type'];
return $sql_data;
}
+ /**
+ * Select Kolab objects filtered by the given query
+ *
+ * @param array Pseudo-SQL query as list of filter parameter triplets
+ * @param boolean Set true to only return UIDs instead of complete objects
+ * @return array List of Kolab data objects (each represented as hash array) or UIDs
+ */
+ public function select($query = array(), $uids = false)
+ {
+ // modify query for IMAP search: query param 'type' is actually a subtype
+ if (!$this->ready) {
+ foreach ($query as $i => $tuple) {
+ if ($tuple[0] == 'type') {
+ $tuple[2] = 'configuration.' . $tuple[2];
+ $query[$i] = $tuple;
+ }
+ }
+ }
+
+ return parent::select($query, $uids);
+ }
+
/**
* Helper method to compose a valid SQL query from pseudo filter triplets
*/
protected function _sql_where($query)
{
if (is_array($query)) {
foreach ($query as $idx => $param) {
// convert category filter
if ($param[0] == 'category') {
$param[2] = array_map(function($n) { return 'category:' . $n; }, (array) $param[2]);
$query[$idx][0] = 'tags';
$query[$idx][2] = count($param[2]) > 1 ? $param[2] : $param[2][0];
}
// convert member filter (we support only = operator with single value)
else if ($param[0] == 'member') {
$query[$idx][0] = 'words';
$query[$idx][1] = '~';
$query[$idx][2] = '^' . $param[2] . '$';
}
}
}
return parent::_sql_where($query);
}
}
diff --git a/lib/plugins/libkolab/lib/kolab_storage_config.php b/lib/plugins/libkolab/lib/kolab_storage_config.php
index 9bc5d50..036b827 100644
--- a/lib/plugins/libkolab/lib/kolab_storage_config.php
+++ b/lib/plugins/libkolab/lib/kolab_storage_config.php
@@ -1,723 +1,866 @@
<?php
/**
* Kolab storage class providing access to configuration objects on a Kolab server.
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2012-2014, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_storage_config
{
const FOLDER_TYPE = 'configuration';
/**
* Singleton instace of kolab_storage_config
*
* @var kolab_storage_config
*/
static protected $instance;
private $folders;
private $default;
private $enabled;
/**
* This implements the 'singleton' design pattern
*
* @return kolab_storage_config The one and only instance
*/
static function get_instance()
{
if (!self::$instance) {
self::$instance = new kolab_storage_config();
}
return self::$instance;
}
/**
* Private constructor (finds default configuration folder as a config source)
*/
private function __construct()
{
// get all configuration folders
$this->folders = kolab_storage::get_folders(self::FOLDER_TYPE, false);
foreach ($this->folders as $folder) {
if ($folder->default) {
$this->default = $folder;
break;
}
}
// if no folder is set as default, choose the first one
if (!$this->default) {
$this->default = reset($this->folders);
}
// attempt to create a default folder if it does not exist
if (!$this->default) {
$folder_name = 'Configuration';
$folder_type = self::FOLDER_TYPE . '.default';
if (kolab_storage::folder_create($folder_name, $folder_type, true)) {
$this->default = new kolab_storage_folder($folder_name, $folder_type);
}
}
// check if configuration folder exist
if ($this->default && $this->default->name) {
$this->enabled = true;
}
}
/**
* Check wether any configuration storage (folder) exists
*
* @return bool
*/
public function is_enabled()
{
return $this->enabled;
}
/**
* Get configuration objects
*
* @param array $filter Search filter
* @param bool $default Enable to get objects only from default folder
* @param int $limit Max. number of records (per-folder)
*
* @return array List of objects
*/
public function get_objects($filter = array(), $default = false, $limit = 0)
{
$list = array();
foreach ($this->folders as $folder) {
// we only want to read from default folder
if ($default && !$folder->default) {
continue;
}
// for better performance it's good to assume max. number of records
if ($limit) {
$folder->set_order_and_limit(null, $limit);
}
foreach ($folder->select($filter) as $object) {
+ unset($object['_formatobj']);
$list[] = $object;
}
}
return $list;
}
/**
* Get configuration object
*
* @param string $uid Object UID
* @param bool $default Enable to get objects only from default folder
*
* @return array Object data
*/
public function get_object($uid, $default = false)
{
foreach ($this->folders as $folder) {
// we only want to read from default folder
if ($default && !$folder->default) {
continue;
}
if ($object = $folder->get_object($uid)) {
return $object;
}
}
}
/**
* Create/update configuration object
*
* @param array $object Object data
* @param string $type Object type
*
* @return bool True on success, False on failure
*/
public function save(&$object, $type)
{
if (!$this->enabled) {
return false;
}
$folder = $this->find_folder($object);
if ($type) {
$object['type'] = $type;
}
return $folder->save($object, self::FOLDER_TYPE . '.' . $object['type'], $object['uid']);
}
/**
* Remove configuration object
*
* @param string $uid Object UID
*
* @return bool True on success, False on failure
*/
public function delete($uid)
{
if (!$this->enabled) {
return false;
}
// fetch the object to find folder
$object = $this->get_object($uid);
if (!$object) {
return false;
}
$folder = $this->find_folder($object);
return $folder->delete($uid);
}
/**
* Find folder
*/
public function find_folder($object = array())
{
// find folder object
if ($object['_mailbox']) {
foreach ($this->folders as $folder) {
if ($folder->name == $object['_mailbox']) {
break;
}
}
}
else {
$folder = $this->default;
}
return $folder;
}
/**
* Builds relation member URI
*
* @param string|array Object UUID or Message folder, UID, Search headers (Message-Id, Date)
*
* @return string $url Member URI
*/
public static function build_member_url($params)
{
// param is object UUID
if (is_string($params) && !empty($params)) {
return 'urn:uuid:' . $params;
}
if (empty($params) || !strlen($params['folder'])) {
return null;
}
$rcube = rcube::get_instance();
$storage = $rcube->get_storage();
// modify folder spec. according to namespace
$folder = $params['folder'];
$ns = $storage->folder_namespace($folder);
if ($ns == 'shared') {
// Note: this assumes there's only one shared namespace root
if ($ns = $storage->get_namespace('shared')) {
if ($prefix = $ns[0][0]) {
$folder = 'shared' . substr($folder, strlen($prefix));
}
}
}
else {
if ($ns == 'other') {
// Note: this assumes there's only one other users namespace root
if ($ns = $storage->get_namespace('shared')) {
if ($prefix = $ns[0][0]) {
$folder = 'user' . substr($folder, strlen($prefix));
}
}
}
else {
$folder = 'user' . '/' . $rcube->get_user_name() . '/' . $folder;
}
}
$folder = implode('/', array_map('rawurlencode', explode('/', $folder)));
// build URI
$url = 'imap:///' . $folder;
// UID is optional here because sometimes we want
// to build just a member uri prefix
if ($params['uid']) {
$url .= '/' . $params['uid'];
}
unset($params['folder']);
unset($params['uid']);
if (!empty($params)) {
$url .= '?' . http_build_query($params, '', '&');
}
return $url;
}
/**
* Parses relation member string
*
* @param string $url Member URI
*
* @return array Message folder, UID, Search headers (Message-Id, Date)
*/
public static function parse_member_url($url)
{
// Look for IMAP URI:
// imap:///(user/username@domain|shared)/<folder>/<UID>?<search_params>
if (strpos($url, 'imap:///') === 0) {
$rcube = rcube::get_instance();
$storage = $rcube->get_storage();
// parse_url does not work with imap:/// prefix
$url = parse_url(substr($url, 8));
$path = explode('/', $url['path']);
parse_str($url['query'], $params);
$uid = array_pop($path);
$ns = array_shift($path);
$path = array_map('rawurldecode', $path);
// resolve folder name
if ($ns == 'shared') {
$folder = implode('/', $path);
// Note: this assumes there's only one shared namespace root
if ($ns = $storage->get_namespace('shared')) {
if ($prefix = $ns[0][0]) {
$folder = $prefix . '/' . $folder;
}
}
}
else if ($ns == 'user') {
$username = array_shift($path);
$folder = implode('/', $path);
if ($username != $rcube->get_user_name()) {
// Note: this assumes there's only one other users namespace root
if ($ns = $storage->get_namespace('other')) {
if ($prefix = $ns[0][0]) {
$folder = $prefix . '/' . $username . '/' . $folder;
}
}
}
else if (!strlen($folder)) {
$folder = 'INBOX';
}
}
else {
return;
}
return array(
'folder' => $folder,
'uid' => $uid,
'params' => $params,
);
}
+
+ return false;
}
/**
* Build array of member URIs from set of messages
*
* @param string $folder Folder name
* @param array $messages Array of rcube_message objects
*
* @return array List of members (IMAP URIs)
*/
public static function build_members($folder, $messages)
{
$members = array();
foreach ((array) $messages as $msg) {
$params = array(
'folder' => $folder,
'uid' => $msg->uid,
);
// add search parameters:
// we don't want to build "invalid" searches e.g. that
// will return false positives (more or wrong messages)
if (($messageid = $msg->get('message-id', false)) && ($date = $msg->get('date', false))) {
$params['message-id'] = $messageid;
$params['date'] = $date;
if ($subject = $msg->get('subject', false)) {
$params['subject'] = substr($subject, 0, 256);
}
}
$members[] = self::build_member_url($params);
}
return $members;
}
/**
* Resolve/validate/update members (which are IMAP URIs) of relation object.
*
* @param array $tag Tag object
* @param bool $force Force members list update
*
* @return array Folder/UIDs list
*/
public static function resolve_members(&$tag, $force = true)
{
$result = array();
foreach ((array) $tag['members'] as $member) {
// IMAP URI members
if ($url = self::parse_member_url($member)) {
$folder = $url['folder'];
if (!$force) {
$result[$folder][] = $url['uid'];
}
else {
$result[$folder]['uid'][] = $url['uid'];
$result[$folder]['params'][] = $url['params'];
$result[$folder]['member'][] = $member;
}
}
}
if (empty($result) || !$force) {
return $result;
}
$rcube = rcube::get_instance();
$storage = $rcube->get_storage();
$search = array();
$missing = array();
// first we search messages by Folder+UID
foreach ($result as $folder => $data) {
// @FIXME: maybe better use index() which is cached?
// @TODO: consider skip_deleted option
$index = $storage->search_once($folder, 'UID ' . rcube_imap_generic::compressMessageSet($data['uid']));
$uids = $index->get();
// messages that were not found need to be searched by search parameters
$not_found = array_diff($data['uid'], $uids);
if (!empty($not_found)) {
foreach ($not_found as $uid) {
$idx = array_search($uid, $data['uid']);
if ($p = $data['params'][$idx]) {
$search[] = $p;
}
$missing[] = $result[$folder]['member'][$idx];
unset($result[$folder]['uid'][$idx]);
unset($result[$folder]['params'][$idx]);
unset($result[$folder]['member'][$idx]);
}
}
$result[$folder] = $uids;
}
// search in all subscribed mail folders using search parameters
if (!empty($search)) {
// remove not found members from the members list
$tag['members'] = array_diff($tag['members'], $missing);
// get subscribed folders
$folders = $storage->list_folders_subscribed('', '*', 'mail', null, true);
// @TODO: do this search in chunks (for e.g. 10 messages)?
$search_str = '';
foreach ($search as $p) {
$search_params = array();
foreach ($p as $key => $val) {
$key = strtoupper($key);
// don't search by subject, we don't want false-positives
if ($key != 'SUBJECT') {
$search_params[] = 'HEADER ' . $key . ' ' . rcube_imap_generic::escape($val);
}
}
$search_str .= ' (' . implode(' ', $search_params) . ')';
}
$search_str = trim(str_repeat(' OR', count($search)-1) . $search_str);
// search
$search = $storage->search_once($folders, $search_str);
// handle search result
$folders = (array) $search->get_parameters('MAILBOX');
foreach ($folders as $folder) {
$set = $search->get_set($folder);
$uids = $set->get();
if (!empty($uids)) {
$msgs = $storage->fetch_headers($folder, $uids, false);
$members = self::build_members($folder, $msgs);
// merge new members into the tag members list
$tag['members'] = array_merge($tag['members'], $members);
// add UIDs into the result
$result[$folder] = array_unique(array_merge((array)$result[$folder], $uids));
}
}
// update tag object with new members list
$tag['members'] = array_unique($tag['members']);
kolab_storage_config::get_instance()->save($tag, 'relation', false);
}
return $result;
}
/**
* Assign tags to kolab objects
*
* @param array $records List of kolab objects
*
* @return array List of tags
*/
public function apply_tags(&$records)
{
// first convert categories into tags
foreach ($records as $i => $rec) {
if (!empty($rec['categories'])) {
$folder = new kolab_storage_folder($rec['_mailbox']);
if ($object = $folder->get_object($rec['uid'])) {
$tags = $rec['categories'];
unset($object['categories']);
unset($records[$i]['categories']);
$this->save_tags($rec['uid'], $tags);
$folder->save($object, $rec['_type'], $rec['uid']);
}
}
}
$tags = array();
// assign tags to objects
foreach ($this->get_tags() as $tag) {
foreach ($records as $idx => $rec) {
$uid = self::build_member_url($rec['uid']);
if (in_array($uid, (array) $tag['members'])) {
$records[$idx]['tags'][] = $tag['name'];
}
}
$tags[] = $tag['name'];
}
$tags = array_unique($tags);
return $tags;
}
/**
* Update object tags
*
* @param string $uid Kolab object UID
* @param array $tags List of tag names
*/
public function save_tags($uid, $tags)
{
$url = self::build_member_url($uid);
$relations = $this->get_tags();
foreach ($relations as $idx => $relation) {
$selected = !empty($tags) && in_array($relation['name'], $tags);
$found = !empty($relation['members']) && in_array($url, $relation['members']);
$update = false;
// remove member from the relation
if ($found && !$selected) {
$relation['members'] = array_diff($relation['members'], (array) $url);
$update = true;
}
// add member to the relation
else if (!$found && $selected) {
$relation['members'][] = $url;
$update = true;
}
if ($update) {
if ($this->save($relation, 'relation')) {
$this->tags[$idx] = $relation; // update in-memory cache
}
}
if ($selected) {
$tags = array_diff($tags, (array)$relation['name']);
}
}
// create new relations
if (!empty($tags)) {
foreach ($tags as $tag) {
$relation = array(
'name' => $tag,
'members' => (array) $url,
'category' => 'tag',
);
if ($this->save($relation, 'relation')) {
$this->tags[] = $relation; // update in-memory cache
}
}
}
}
/**
* Get tags (all or referring to specified object)
*
* @param string $uid Optional object UID
*
* @return array List of Relation objects
*/
public function get_tags($uid = '*')
{
if (!isset($this->tags)) {
$default = true;
$filter = array(
array('type', '=', 'relation'),
array('category', '=', 'tag')
);
// use faster method
if ($uid && $uid != '*') {
$filter[] = array('member', '=', $uid);
- return $this->get_objects($filter, $default);
+ $tags = $this->get_objects($filter, $default);
+ }
+ else {
+ $this->tags = $tags = $this->get_objects($filter, $default);
}
-
- $this->tags = $this->get_objects($filter, $default);
+ }
+ else {
+ $tags = $this->tags;
}
if ($uid === '*') {
- return $this->tags;
+ return $tags;
}
$result = array();
$search = self::build_member_url($uid);
- foreach ($this->tags as $tag) {
+ foreach ($tags as $tag) {
if (in_array($search, (array) $tag['members'])) {
$result[] = $tag;
}
}
return $result;
}
+ /**
+ * Find objects linked with the given groupware object through a relation
+ *
+ * @param string Object UUID
+ * @param array List of related URIs
+ */
+ public function get_object_links($uid)
+ {
+ $links = array();
+ $object_uri = self::build_member_url($uid);
+
+ foreach ($this->get_relations_for_member($uid) as $relation) {
+ if (in_array($object_uri, (array) $relation['members'])) {
+ // make relation members up-to-date
+ kolab_storage_config::resolve_members($relation);
+
+ foreach ($relation['members'] as $member) {
+ if ($member != $object_uri) {
+ $links[] = $member;
+ }
+ }
+ }
+ }
+
+ return array_unique($links);
+ }
+
+ /**
+ *
+ */
+ public function save_object_links($uid, $links, $remove = array())
+ {
+ $object_uri = self::build_member_url($uid);
+ $relations = $this->get_relations_for_member($uid);
+ $done = false;
+
+ foreach ($relations as $relation) {
+ // make relation members up-to-date
+ kolab_storage_config::resolve_members($relation);
+
+ // remove and add links
+ $members = array_diff($relation['members'], (array)$remove);
+ $members = array_unique(array_merge($members, $links));
+
+ // make sure the object_uri is still a member
+ if (!in_array($object_uri, $members)) {
+ $members[$object_uri];
+ }
+
+ // remove relation if no other members remain
+ if (count($members) <= 1) {
+ $done = $this->delete($relation['uid']);
+ }
+ // update relation object if members changed
+ else if (count(array_diff($members, $relation['members'])) || count(array_diff($relation['members'], $members))) {
+ $relation['members'] = $members;
+ $done = $this->save($relation, 'relation');
+ $links = array();
+ }
+ // no changes, we're happy
+ else {
+ $done = true;
+ $links = array();
+ }
+ }
+
+ // create a new relation
+ if (!$done && !empty($links)) {
+ $relation = array(
+ 'members' => array_merge($links, array($object_uri)),
+ 'category' => 'generic',
+ );
+
+ $ret = $this->save($relation, 'relation');
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Find relation objects referring to specified note
+ */
+ public function get_relations_for_member($uid, $reltype = 'generic')
+ {
+ $default = true;
+ $filter = array(
+ array('type', '=', 'relation'),
+ array('category', '=', $reltype),
+ array('member', '=', $uid),
+ );
+
+ return $this->get_objects($filter, $default, 100);
+ }
+
/**
* Find kolab objects assigned to specified e-mail message
*
* @param rcube_message $message E-mail message
* @param string $folder Folder name
* @param string $type Result objects type
*
* @return array List of kolab objects
*/
public function get_message_relations($message, $folder, $type)
{
+ static $_cache = array();
+
$result = array();
$uids = array();
$default = true;
$uri = self::get_message_uri($message, $folder);
$filter = array(
array('type', '=', 'relation'),
array('category', '=', 'generic'),
- // @TODO: what if Message-Id (and Date) does not exist?
- array('member', '=', $message->get('message-id', false)),
);
- // get UIDs of assigned notes
- foreach ($this->get_objects($filter, $default) as $relation) {
- // we don't need to update members if the URI is found
- if (in_array($uri, $relation['members'])) {
- // update members...
- $messages = kolab_storage_config::resolve_members($relation);
- // ...and check again
- if (empty($messages[$folder]) || !in_array($message->uid, $messages[$folder])) {
- continue;
+ // query by message-id
+ $member_id = $message->get('message-id', false);
+ if (empty($member_id)) {
+ // derive message identifier from URI
+ $member_id = md5($uri);
+ }
+ $filter[] = array('member', '=', $member_id);
+
+ if (!isset($_cache[$uri])) {
+ // get UIDs of related groupware objects
+ foreach ($this->get_objects($filter, $default) as $relation) {
+ // we don't need to update members if the URI is found
+ if (!in_array($uri, $relation['members'])) {
+ // update members...
+ $messages = kolab_storage_config::resolve_members($relation);
+ // ...and check again
+ if (empty($messages[$folder]) || !in_array($message->uid, $messages[$folder])) {
+ continue;
+ }
}
- }
- // find note UID(s)
- foreach ($relation['members'] as $member) {
- if (strpos($member, 'urn:uuid:') === 0) {
- $uids[] = substr($member, 9);
+ // find groupware object UID(s)
+ foreach ($relation['members'] as $member) {
+ if (strpos($member, 'urn:uuid:') === 0) {
+ $uids[] = substr($member, 9);
+ }
}
}
+
+ // remember this lookup
+ $_cache[$uri] = $uids;
+ }
+ else {
+ $uids = $_cache[$uri];
}
// get kolab objects of specified type
if (!empty($uids)) {
$query = array(array('uid', '=', array_unique($uids)));
$result = kolab_storage::select($query, $type);
}
return $result;
}
/**
* Build a URI representing the given message reference
*/
public static function get_message_uri($headers, $folder)
{
$params = array(
'folder' => $headers->folder ?: $folder,
'uid' => $headers->uid,
);
if (($messageid = $headers->get('message-id', false)) && ($date = $headers->get('date', false))) {
$params['message-id'] = $messageid;
$params['date'] = $date;
if ($subject = $headers->get('subject')) {
$params['subject'] = $subject;
}
}
return self::build_member_url($params);
}
+
+ /**
+ * Resolve the email message reference from the given URI
+ */
+ public function get_message_reference($uri, $rel = null)
+ {
+ if ($linkref = self::parse_member_url($uri)) {
+ $linkref['subject'] = $linkref['params']['subject'];
+ $linkref['uri'] = $uri;
+
+ $rcmail = rcube::get_instance();
+ if (method_exists($rcmail, 'url')) {
+ $linkref['mailurl'] = $rcmail->url(array(
+ 'task' => 'mail',
+ 'action' => 'show',
+ 'mbox' => $linkref['folder'],
+ 'uid' => $linkref['uid'],
+ 'rel' => $rel,
+ ));
+ }
+
+ unset($linkref['params']);
+ }
+
+ return $linkref;
+ }
}
diff --git a/lib/plugins/libkolab/lib/kolab_storage_folder.php b/lib/plugins/libkolab/lib/kolab_storage_folder.php
index ad6d5c0..ab3c63f 100644
--- a/lib/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/lib/plugins/libkolab/lib/kolab_storage_folder.php
@@ -1,1070 +1,1166 @@
<?php
/**
* The kolab_storage_folder class represents an IMAP folder on the Kolab server.
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2012-2013, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_storage_folder extends kolab_storage_folder_api
{
/**
* The kolab_storage_cache instance for caching operations
* @var object
*/
public $cache;
- private $type_annotation;
- private $resource_uri;
+ /**
+ * Indicate validity status
+ * @var boolean
+ */
+ public $valid = false;
+
+ protected $error = 0;
+
+ protected $resource_uri;
/**
* Default constructor
+ *
+ * @param string The folder name/path
+ * @param string Expected folder type
*/
- function __construct($name, $type = null)
+ function __construct($name, $type = null, $type_annotation = null)
{
parent::__construct($name);
$this->imap->set_options(array('skip_deleted' => true));
- $this->set_folder($name, $type);
+ $this->set_folder($name, $type, $type_annotation);
}
/**
* Set the IMAP folder this instance connects to
*
* @param string The folder name/path
+ * @param string Expected folder type
* @param string Optional folder type if known
*/
- public function set_folder($name, $type = null)
+ public function set_folder($name, $type = null, $type_annotation = null)
{
- $this->type_annotation = $type ? $type : kolab_storage::folder_type($name);
+ if (empty($type_annotation)) {
+ $type_annotation = kolab_storage::folder_type($name);
+ }
$oldtype = $this->type;
- list($this->type, $suffix) = explode('.', $this->type_annotation);
+ list($this->type, $suffix) = explode('.', $type_annotation);
$this->default = $suffix == 'default';
+ $this->subtype = $this->default ? '' : $suffix;
$this->name = $name;
$this->id = kolab_storage::folder_id($name);
+ $this->valid = !empty($this->type) && $this->type != 'mail' && (!$type || $this->type == $type);
+
+ if (!$this->valid) {
+ $this->error = $this->imap->get_error_code() < 0 ? kolab_storage::ERROR_IMAP_CONN : kolab_storage::ERROR_INVALID_FOLDER;
+ }
// reset cached object properties
$this->owner = $this->namespace = $this->resource_uri = $this->info = $this->idata = null;
- // get a new cache instance of folder type changed
- if (!$this->cache || $type != $oldtype)
+ // get a new cache instance if folder type changed
+ if (!$this->cache || $this->type != $oldtype)
$this->cache = kolab_storage_cache::factory($this);
+ else
+ $this->cache->set_folder($this);
$this->imap->set_folder($this->name);
- $this->cache->set_folder($this);
}
+ /**
+ * Returns code of last error
+ *
+ * @return int Error code
+ */
+ public function get_error()
+ {
+ return $this->error ?: $this->cache->get_error();
+ }
+
+ /**
+ * Check IMAP connection error state
+ */
+ public function check_error()
+ {
+ if (($err_code = $this->imap->get_error_code()) < 0) {
+ $this->error = kolab_storage::ERROR_IMAP_CONN;
+ if (($res_code = $this->imap->get_response_code()) !== 0 && in_array($res_code, array(rcube_storage::NOPERM, rcube_storage::READONLY))) {
+ $this->error = kolab_storage::ERROR_NO_PERMISSION;
+ }
+ }
+
+ return $this->error;
+ }
/**
* Compose a unique resource URI for this IMAP folder
*/
public function get_resource_uri()
{
if (!empty($this->resource_uri))
return $this->resource_uri;
// strip namespace prefix from folder name
$ns = $this->get_namespace();
$nsdata = $this->imap->get_namespace($ns);
if (is_array($nsdata[0]) && strlen($nsdata[0][0]) && strpos($this->name, $nsdata[0][0]) === 0) {
$subpath = substr($this->name, strlen($nsdata[0][0]));
if ($ns == 'other') {
list($user, $suffix) = explode($nsdata[0][1], $subpath, 2);
$subpath = $suffix;
}
}
else {
$subpath = $this->name;
}
// compose fully qualified ressource uri for this instance
- $this->resource_uri = 'imap://' . urlencode($this->get_owner()) . '@' . $this->imap->options['host'] . '/' . $subpath;
+ $this->resource_uri = 'imap://' . urlencode($this->get_owner(true)) . '@' . $this->imap->options['host'] . '/' . $subpath;
return $this->resource_uri;
}
/**
* Helper method to extract folder UID metadata
*
* @return string Folder's UID
*/
public function get_uid()
{
// UID is defined in folder METADATA
$metakeys = array(kolab_storage::UID_KEY_SHARED, kolab_storage::UID_KEY_PRIVATE, kolab_storage::UID_KEY_CYRUS);
$metadata = $this->get_metadata($metakeys);
foreach ($metakeys as $key) {
if (($uid = $metadata[$key])) {
return $uid;
}
}
// generate a folder UID and set it to IMAP
$uid = rtrim(chunk_split(md5($this->name . $this->get_owner() . uniqid('-', true)), 12, '-'), '-');
- $this->set_uid($uid);
+ if ($this->set_uid($uid)) {
+ return $uid;
+ }
- return $uid;
+ // create hash from folder name if we can't write the UID metadata
+ return md5($this->name . $this->get_owner());
}
/**
* Helper method to set an UID value to the given IMAP folder instance
*
* @param string Folder's UID
* @return boolean True on succes, False on failure
*/
public function set_uid($uid)
{
if (!($success = $this->set_metadata(array(kolab_storage::UID_KEY_SHARED => $uid)))) {
$success = $this->set_metadata(array(kolab_storage::UID_KEY_PRIVATE => $uid));
}
+
+ $this->check_error();
return $success;
}
/**
* Compose a folder Etag identifier
*/
public function get_ctag()
{
$fdata = $this->get_imap_data();
+ $this->check_error();
return sprintf('%d-%d-%d', $fdata['UIDVALIDITY'], $fdata['HIGHESTMODSEQ'], $fdata['UIDNEXT']);
}
/**
* Check activation status of this folder
*
* @return boolean True if enabled, false if not
*/
public function is_active()
{
return kolab_storage::folder_is_active($this->name);
}
/**
* Change activation status of this folder
*
* @param boolean The desired subscription status: true = active, false = not active
*
* @return True on success, false on error
*/
public function activate($active)
{
return $active ? kolab_storage::folder_activate($this->name) : kolab_storage::folder_deactivate($this->name);
}
/**
* Check subscription status of this folder
*
* @return boolean True if subscribed, false if not
*/
public function is_subscribed()
{
return kolab_storage::folder_is_subscribed($this->name);
}
/**
* Change subscription status of this folder
*
* @param boolean The desired subscription status: true = subscribed, false = not subscribed
*
* @return True on success, false on error
*/
public function subscribe($subscribed)
{
return $subscribed ? kolab_storage::folder_subscribe($this->name) : kolab_storage::folder_unsubscribe($this->name);
}
/**
* Get number of objects stored in this folder
*
* @param mixed Pseudo-SQL query as list of filter parameter triplets
* or string with object type (e.g. contact, event, todo, journal, note, configuration)
* @return integer The number of objects of the given type
* @see self::select()
*/
public function count($query = null)
{
+ if (!$this->valid) {
+ return 0;
+ }
+
// synchronize cache first
$this->cache->synchronize();
return $this->cache->count($this->_prepare_query($query));
}
/**
* List all Kolab objects of the given type
*
* @param string $type Object type (e.g. contact, event, todo, journal, note, configuration)
* @return array List of Kolab data objects (each represented as hash array)
*/
public function get_objects($type = null)
{
if (!$type) $type = $this->type;
+ if (!$this->valid) {
+ return array();
+ }
+
// synchronize caches
$this->cache->synchronize();
// fetch objects from cache
return $this->cache->select($this->_prepare_query($type));
}
/**
* Select *some* Kolab objects matching the given query
*
* @param array Pseudo-SQL query as list of filter parameter triplets
* triplet: array('<colname>', '<comparator>', '<value>')
* @return array List of Kolab data objects (each represented as hash array)
*/
public function select($query = array())
{
+ if (!$this->valid) {
+ return array();
+ }
+
// check query argument
- if (empty($query))
+ if (empty($query)) {
return $this->get_objects();
+ }
// synchronize caches
$this->cache->synchronize();
// fetch objects from cache
return $this->cache->select($this->_prepare_query($query));
}
/**
* Getter for object UIDs only
*
* @param array Pseudo-SQL query as list of filter parameter triplets
* @return array List of Kolab object UIDs
*/
public function get_uids($query = array())
{
+ if (!$this->valid) {
+ return array();
+ }
+
// synchronize caches
$this->cache->synchronize();
// fetch UIDs from cache
return $this->cache->select($this->_prepare_query($query), true);
}
/**
* Setter for ORDER BY and LIMIT parameters for cache queries
*
* @param array List of columns to order by
* @param integer Limit result set to this length
* @param integer Offset row
*/
public function set_order_and_limit($sortcols, $length = null, $offset = 0)
{
$this->cache->set_order_by($sortcols);
if ($length !== null) {
$this->cache->set_limit($length, $offset);
}
}
/**
* Helper method to sanitize query arguments
*/
private function _prepare_query($query)
{
// string equals type query
// FIXME: should not be called this way!
if (is_string($query)) {
return $this->cache->has_type_col() && !empty($query) ? array(array('type','=',$query)) : array();
}
foreach ((array)$query as $i => $param) {
if ($param[0] == 'type' && !$this->cache->has_type_col()) {
unset($query[$i]);
}
else if (($param[0] == 'dtstart' || $param[0] == 'dtend' || $param[0] == 'changed')) {
if (is_object($param[2]) && is_a($param[2], 'DateTime'))
$param[2] = $param[2]->format('U');
if (is_numeric($param[2]))
$query[$i][2] = date('Y-m-d H:i:s', $param[2]);
}
}
return $query;
}
/**
* Getter for a single Kolab object, identified by its UID
*
* @param string $uid Object UID
* @param string $type Object type (e.g. contact, event, todo, journal, note, configuration)
* Defaults to folder type
*
* @return array The Kolab object represented as hash array
*/
public function get_object($uid, $type = null)
{
+ if (!$this->valid) {
+ return false;
+ }
+
// synchronize caches
$this->cache->synchronize();
$msguid = $this->cache->uid2msguid($uid);
if ($msguid && ($object = $this->cache->get($msguid, $type))) {
return $object;
}
return false;
}
/**
* Fetch a Kolab object attachment which is stored in a separate part
* of the mail MIME message that represents the Kolab record.
*
* @param string Object's UID
* @param string The attachment's mime number
* @param string IMAP folder where message is stored;
* If set, that also implies that the given UID is an IMAP UID
* @param bool True to print the part content
* @param resource File pointer to save the message part
* @param boolean Disables charset conversion
*
* @return mixed The attachment content as binary string
*/
public function get_attachment($uid, $part, $mailbox = null, $print = false, $fp = null, $skip_charset_conv = false)
{
- if ($msguid = ($mailbox ? $uid : $this->cache->uid2msguid($uid))) {
+ if ($this->valid && ($msguid = ($mailbox ? $uid : $this->cache->uid2msguid($uid)))) {
$this->imap->set_folder($mailbox ? $mailbox : $this->name);
if (substr($part, 0, 2) == 'i:') {
// attachment data is stored in XML
if ($object = $this->cache->get($msguid)) {
// load data from XML (attachment content is not stored in cache)
if ($object['_formatobj'] && isset($object['_size'])) {
$object['_attachments'] = array();
$object['_formatobj']->get_attachments($object);
}
- foreach ($object['_attachments'] as $k => $attach) {
+ foreach ($object['_attachments'] as $attach) {
if ($attach['id'] == $part) {
if ($print) echo $attach['content'];
else if ($fp) fwrite($fp, $attach['content']);
else return $attach['content'];
return true;
}
}
}
}
else {
// return message part from IMAP directly
return $this->imap->get_message_part($msguid, $part, null, $print, $fp, $skip_charset_conv);
}
}
return null;
}
/**
* Fetch the mime message from the storage server and extract
* the Kolab groupware object from it
*
* @param string The IMAP message UID to fetch
* @param string The object type expected (use wildcard '*' to accept all types)
* @param string The folder name where the message is stored
*
* @return mixed Hash array representing the Kolab object, a kolab_format instance or false if not found
*/
public function read_object($msguid, $type = null, $folder = null)
{
+ if (!$this->valid) {
+ return false;
+ }
+
if (!$type) $type = $this->type;
if (!$folder) $folder = $this->name;
$this->imap->set_folder($folder);
$this->cache->bypass(true);
$message = new rcube_message($msguid);
$this->cache->bypass(false);
// Message doesn't exist?
if (empty($message->headers)) {
return false;
}
// extract the X-Kolab-Type header from the XML attachment part if missing
if (empty($message->headers->others['x-kolab-type'])) {
foreach ((array)$message->attachments as $part) {
if (strpos($part->mimetype, kolab_format::KTYPE_PREFIX) === 0) {
$message->headers->others['x-kolab-type'] = $part->mimetype;
break;
}
}
}
// fix buggy messages stating the X-Kolab-Type header twice
else if (is_array($message->headers->others['x-kolab-type'])) {
$message->headers->others['x-kolab-type'] = reset($message->headers->others['x-kolab-type']);
}
// no object type header found: abort
if (empty($message->headers->others['x-kolab-type'])) {
rcube::raise_error(array(
'code' => 600,
'type' => 'php',
'file' => __FILE__,
'line' => __LINE__,
'message' => "No X-Kolab-Type information found in message $msguid ($this->name).",
), true);
return false;
}
$object_type = kolab_format::mime2object_type($message->headers->others['x-kolab-type']);
$content_type = kolab_format::KTYPE_PREFIX . $object_type;
// check object type header and abort on mismatch
if ($type != '*' && $object_type != $type)
return false;
$attachments = array();
// get XML part
foreach ((array)$message->attachments as $part) {
if (!$xml && ($part->mimetype == $content_type || preg_match('!application/([a-z.]+\+)?xml!', $part->mimetype))) {
- $xml = $part->body ? $part->body : $message->get_part_content($part->mime_id);
+ $xml = $message->get_part_body($part->mime_id, true);
}
else if ($part->filename || $part->content_id) {
$key = $part->content_id ? trim($part->content_id, '<>') : $part->filename;
$size = null;
// Use Content-Disposition 'size' as for the Kolab Format spec.
if (isset($part->d_parameters['size'])) {
$size = $part->d_parameters['size'];
}
// we can trust part size only if it's not encoded
else if ($part->encoding == 'binary' || $part->encoding == '7bit' || $part->encoding == '8bit') {
$size = $part->size;
}
$attachments[$key] = array(
'id' => $part->mime_id,
'name' => $part->filename,
'mimetype' => $part->mimetype,
'size' => $size,
);
}
}
if (!$xml) {
rcube::raise_error(array(
'code' => 600,
'type' => 'php',
'file' => __FILE__,
'line' => __LINE__,
'message' => "Could not find Kolab data part in message $msguid ($this->name).",
), true);
return false;
}
// check kolab format version
$format_version = $message->headers->others['x-kolab-mime-version'];
if (empty($format_version)) {
list($xmltype, $subtype) = explode('.', $object_type);
$xmlhead = substr($xml, 0, 512);
// detect old Kolab 2.0 format
if (strpos($xmlhead, '<' . $xmltype) !== false && strpos($xmlhead, 'xmlns=') === false)
$format_version = '2.0';
else
$format_version = '3.0'; // assume 3.0
}
// get Kolab format handler for the given type
$format = kolab_format::factory($object_type, $format_version);
if (is_a($format, 'PEAR_Error'))
return false;
// load Kolab object from XML part
$format->load($xml);
if ($format->is_valid()) {
$object = $format->to_array(array('_attachments' => $attachments));
$object['_type'] = $object_type;
$object['_msguid'] = $msguid;
$object['_mailbox'] = $this->name;
$object['_formatobj'] = $format;
return $object;
}
else {
// try to extract object UID from XML block
if (preg_match('!<uid>(.+)</uid>!Uims', $xml, $m))
$msgadd = " UID = " . trim(strip_tags($m[1]));
rcube::raise_error(array(
'code' => 600,
'type' => 'php',
'file' => __FILE__,
'line' => __LINE__,
'message' => "Could not parse Kolab object data in message $msguid ($this->name)." . $msgadd,
), true);
}
return false;
}
/**
* Save an object in this folder.
*
* @param array $object The array that holds the data of the object.
* @param string $type The type of the kolab object.
* @param string $uid The UID of the old object if it existed before
* @return boolean True on success, false on error
*/
public function save(&$object, $type = null, $uid = null)
{
+ if (!$this->valid) {
+ return false;
+ }
+
if (!$type)
$type = $this->type;
// copy attachments from old message
if (!empty($object['_msguid']) && ($old = $this->cache->get($object['_msguid'], $type, $object['_mailbox']))) {
foreach ((array)$old['_attachments'] as $key => $att) {
if (!isset($object['_attachments'][$key])) {
$object['_attachments'][$key] = $old['_attachments'][$key];
}
// unset deleted attachment entries
if ($object['_attachments'][$key] == false) {
unset($object['_attachments'][$key]);
}
// load photo.attachment from old Kolab2 format to be directly embedded in xcard block
else if ($type == 'contact' && ($key == 'photo.attachment' || $key == 'kolab-picture.png') && $att['id']) {
if (!isset($object['photo']))
$object['photo'] = $this->get_attachment($object['_msguid'], $att['id'], $object['_mailbox']);
unset($object['_attachments'][$key]);
}
}
}
// save contact photo to attachment for Kolab2 format
if (kolab_storage::$version == '2.0' && $object['photo']) {
$attkey = 'kolab-picture.png'; // this file name is hard-coded in libkolab/kolabformatV2/contact.cpp
$object['_attachments'][$attkey] = array(
'mimetype'=> rcube_mime::image_content_type($object['photo']),
'content' => preg_match('![^a-z0-9/=+-]!i', $object['photo']) ? $object['photo'] : base64_decode($object['photo']),
);
}
// process attachments
if (is_array($object['_attachments'])) {
$numatt = count($object['_attachments']);
foreach ($object['_attachments'] as $key => $attachment) {
// FIXME: kolab_storage and Roundcube attachment hooks use different fields!
if (empty($attachment['content']) && !empty($attachment['data'])) {
$attachment['content'] = $attachment['data'];
unset($attachment['data'], $object['_attachments'][$key]['data']);
}
// make sure size is set, so object saved in cache contains this info
if (!isset($attachment['size'])) {
if (!empty($attachment['content'])) {
if (is_resource($attachment['content'])) {
// this need to be a seekable resource, otherwise
// fstat() failes and we're unable to determine size
// here nor in rcube_imap_generic before IMAP APPEND
$stat = fstat($attachment['content']);
$attachment['size'] = $stat ? $stat['size'] : 0;
}
else {
$attachment['size'] = strlen($attachment['content']);
}
}
else if (!empty($attachment['path'])) {
$attachment['size'] = filesize($attachment['path']);
}
$object['_attachments'][$key] = $attachment;
}
// generate unique keys (used as content-id) for attachments
if (is_numeric($key) && $key < $numatt) {
// derrive content-id from attachment file name
$ext = preg_match('/(\.[a-z0-9]{1,6})$/i', $attachment['name'], $m) ? $m[1] : null;
$basename = preg_replace('/[^a-z0-9_.-]/i', '', basename($attachment['name'], $ext)); // to 7bit ascii
if (!$basename) $basename = 'noname';
$cid = $basename . '.' . microtime(true) . $ext;
$object['_attachments'][$cid] = $attachment;
unset($object['_attachments'][$key]);
}
}
}
// save recurrence exceptions as individual objects due to lack of support in Kolab v2 format
if (kolab_storage::$version == '2.0' && $object['recurrence']['EXCEPTIONS']) {
$this->save_recurrence_exceptions($object, $type);
}
// check IMAP BINARY extension support for 'file' objects
// allow configuration to workaround bug in Cyrus < 2.4.17
$rcmail = rcube::get_instance();
$binary = $type == 'file' && !$rcmail->config->get('kolab_binary_disable') && $this->imap->get_capability('BINARY');
// generate and save object message
if ($raw_msg = $this->build_message($object, $type, $binary, $body_file)) {
// resolve old msguid before saving
if ($uid && empty($object['_msguid']) && ($msguid = $this->cache->uid2msguid($uid))) {
$object['_msguid'] = $msguid;
$object['_mailbox'] = $this->name;
}
$result = $this->imap->save_message($this->name, $raw_msg, null, false, null, null, $binary);
// update cache with new UID
if ($result) {
$old_uid = $object['_msguid'];
$object['_msguid'] = $result;
$object['_mailbox'] = $this->name;
if ($old_uid) {
// delete old message
$this->cache->bypass(true);
$this->imap->delete_message($old_uid, $object['_mailbox']);
$this->cache->bypass(false);
}
// insert/update message in cache
$this->cache->save($result, $object, $old_uid);
}
// remove temp file
if ($body_file) {
@unlink($body_file);
}
}
return $result;
}
/**
* Save recurrence exceptions as individual objects.
* The Kolab v2 format doesn't allow us to save fully embedded exception objects.
*
* @param array Hash array with event properties
* @param string Object type
*/
private function save_recurrence_exceptions(&$object, $type = null)
{
if ($object['recurrence']['EXCEPTIONS']) {
$exdates = array();
foreach ((array)$object['recurrence']['EXDATE'] as $exdate) {
$key = is_a($exdate, 'DateTime') ? $exdate->format('Y-m-d') : strval($exdate);
$exdates[$key] = 1;
}
// save every exception as individual object
foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
$exception['uid'] = self::recurrence_exception_uid($object['uid'], $exception['start']->format('Ymd'));
$exception['sequence'] = $object['sequence'] + 1;
if ($exception['thisandfuture']) {
$exception['recurrence'] = $object['recurrence'];
// adjust the recurrence duration of the exception
if ($object['recurrence']['COUNT']) {
$recurrence = new kolab_date_recurrence($object['_formatobj']);
if ($end = $recurrence->end()) {
unset($exception['recurrence']['COUNT']);
$exception['recurrence']['UNTIL'] = $end;
}
}
// set UNTIL date if we have a thisandfuture exception
$untildate = clone $exception['start'];
$untildate->sub(new DateInterval('P1D'));
$object['recurrence']['UNTIL'] = $untildate;
unset($object['recurrence']['COUNT']);
}
else {
if (!$exdates[$exception['start']->format('Y-m-d')])
$object['recurrence']['EXDATE'][] = clone $exception['start'];
unset($exception['recurrence']);
}
unset($exception['recurrence']['EXCEPTIONS'], $exception['_formatobj'], $exception['_msguid']);
$this->save($exception, $type, $exception['uid']);
}
unset($object['recurrence']['EXCEPTIONS']);
}
}
/**
* Generate an object UID with the given recurrence-ID in a way that it is
* unique (the original UID is not a substring) but still recoverable.
*/
private static function recurrence_exception_uid($uid, $recurrence_id)
{
$offset = -2;
return substr($uid, 0, $offset) . '-' . $recurrence_id . '-' . substr($uid, $offset);
}
/**
* Delete the specified object from this folder.
*
* @param mixed $object The Kolab object to delete or object UID
* @param boolean $expunge Should the folder be expunged?
*
* @return boolean True if successful, false on error
*/
public function delete($object, $expunge = true)
{
+ if (!$this->valid) {
+ return false;
+ }
+
$msguid = is_array($object) ? $object['_msguid'] : $this->cache->uid2msguid($object);
$success = false;
$this->cache->bypass(true);
if ($msguid && $expunge) {
$success = $this->imap->delete_message($msguid, $this->name);
}
else if ($msguid) {
$success = $this->imap->set_flag($msguid, 'DELETED', $this->name);
}
$this->cache->bypass(false);
if ($success) {
$this->cache->set($msguid, false);
}
return $success;
}
/**
*
*/
public function delete_all()
{
+ if (!$this->valid) {
+ return false;
+ }
+
$this->cache->purge();
$this->cache->bypass(true);
$result = $this->imap->clear_folder($this->name);
$this->cache->bypass(false);
return $result;
}
/**
* Restore a previously deleted object
*
* @param string Object UID
* @return mixed Message UID on success, false on error
*/
public function undelete($uid)
{
+ if (!$this->valid) {
+ return false;
+ }
+
if ($msguid = $this->cache->uid2msguid($uid, true)) {
$this->cache->bypass(true);
$result = $this->imap->set_flag($msguid, 'UNDELETED', $this->name);
$this->cache->bypass(false);
if ($result) {
return $msguid;
}
}
return false;
}
/**
* Move a Kolab object message to another IMAP folder
*
* @param string Object UID
* @param string IMAP folder to move object to
* @return boolean True on success, false on failure
*/
public function move($uid, $target_folder)
{
+ if (!$this->valid) {
+ return false;
+ }
+
+ if (is_string($target_folder))
+ $target_folder = kolab_storage::get_folder($target_folder);
+
if ($msguid = $this->cache->uid2msguid($uid)) {
$this->cache->bypass(true);
- $result = $this->imap->move_message($msguid, $target_folder, $this->name);
+ $result = $this->imap->move_message($msguid, $target_folder->name, $this->name);
$this->cache->bypass(false);
if ($result) {
$this->cache->move($msguid, $uid, $target_folder);
return true;
}
else {
rcube::raise_error(array(
'code' => 600, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Failed to move message $msguid to $target_folder: " . $this->imap->get_error_str(),
), true);
}
}
return false;
}
/**
* Creates source of the configuration object message
*
* @param array $object The array that holds the data of the object.
* @param string $type The type of the kolab object.
* @param bool $binary Enables use of binary encoding of attachment(s)
* @param string $body_file Reference to filename of message body
*
* @return mixed Message as string or array with two elements
* (one for message file path, second for message headers)
*/
private function build_message(&$object, $type, $binary, &$body_file)
{
// load old object to preserve data we don't understand/process
if (is_object($object['_formatobj']))
$format = $object['_formatobj'];
else if ($object['_msguid'] && ($old = $this->cache->get($object['_msguid'], $type, $object['_mailbox'])))
$format = $old['_formatobj'];
// create new kolab_format instance
if (!$format)
$format = kolab_format::factory($type, kolab_storage::$version);
if (PEAR::isError($format))
return false;
$format->set($object);
$xml = $format->write(kolab_storage::$version);
$object['uid'] = $format->uid; // read UID from format
$object['_formatobj'] = $format;
if (empty($xml) || !$format->is_valid() || empty($object['uid'])) {
return false;
}
$mime = new Mail_mime("\r\n");
$rcmail = rcube::get_instance();
$headers = array();
$files = array();
$part_id = 1;
$encoding = $binary ? 'binary' : 'base64';
if ($user_email = $rcmail->get_user_email()) {
$headers['From'] = $user_email;
$headers['To'] = $user_email;
}
$headers['Date'] = date('r');
$headers['X-Kolab-Type'] = kolab_format::KTYPE_PREFIX . $type;
$headers['X-Kolab-Mime-Version'] = kolab_storage::$version;
$headers['Subject'] = $object['uid'];
// $headers['Message-ID'] = $rcmail->gen_message_id();
$headers['User-Agent'] = $rcmail->config->get('useragent');
// Check if we have enough memory to handle the message in it
// It's faster than using files, so we'll do this if we only can
if (!empty($object['_attachments']) && ($mem_limit = parse_bytes(ini_get('memory_limit'))) > 0) {
$memory = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
- foreach ($object['_attachments'] as $id => $attachment) {
+ foreach ($object['_attachments'] as $attachment) {
$memory += $attachment['size'];
}
// 1.33 is for base64, we need at least 4x more memory than the message size
if ($memory * ($binary ? 1 : 1.33) * 4 > $mem_limit) {
$marker = '%%%~~~' . md5(microtime(true) . $memory) . '~~~%%%';
$is_file = true;
$temp_dir = unslashify($rcmail->config->get('temp_dir'));
$mime->setParam('delay_file_io', true);
}
}
$mime->headers($headers);
$mime->setTXTBody("This is a Kolab Groupware object. "
. "To view this object you will need an email client that understands the Kolab Groupware format. "
. "For a list of such email clients please visit http://www.kolab.org/\n\n");
$ctype = kolab_storage::$version == '2.0' ? $format->CTYPEv2 : $format->CTYPE;
// Convert new lines to \r\n, to wrokaround "NO Message contains bare newlines"
// when APPENDing from temp file
$xml = preg_replace('/\r?\n/', "\r\n", $xml);
$mime->addAttachment($xml, // file
$ctype, // content-type
'kolab.xml', // filename
false, // is_file
'8bit', // encoding
'attachment', // disposition
RCUBE_CHARSET // charset
);
$part_id++;
// save object attachments as separate parts
foreach ((array)$object['_attachments'] as $key => $att) {
if (empty($att['content']) && !empty($att['id'])) {
// @TODO: use IMAP CATENATE to skip attachment fetch+push operation
$msguid = !empty($object['_msguid']) ? $object['_msguid'] : $object['uid'];
if ($is_file) {
$att['path'] = tempnam($temp_dir, 'rcmAttmnt');
if (($fp = fopen($att['path'], 'w')) && $this->get_attachment($msguid, $att['id'], $object['_mailbox'], false, $fp, true)) {
fclose($fp);
}
else {
return false;
}
}
else {
$att['content'] = $this->get_attachment($msguid, $att['id'], $object['_mailbox'], false, null, true);
}
}
$headers = array('Content-ID' => Mail_mimePart::encodeHeader('Content-ID', '<' . $key . '>', RCUBE_CHARSET, 'quoted-printable'));
$name = !empty($att['name']) ? $att['name'] : $key;
// To store binary files we can use faster method
// without writting full message content to a temporary file but
// directly to IMAP, see rcube_imap_generic::append().
// I.e. use file handles where possible
if (!empty($att['path'])) {
if ($is_file && $binary) {
$files[] = fopen($att['path'], 'r');
$mime->addAttachment($marker, $att['mimetype'], $name, false, $encoding, 'attachment', '', '', '', null, null, '', RCUBE_CHARSET, $headers);
}
else {
$mime->addAttachment($att['path'], $att['mimetype'], $name, true, $encoding, 'attachment', '', '', '', null, null, '', RCUBE_CHARSET, $headers);
}
}
else {
if (is_resource($att['content']) && $is_file && $binary) {
$files[] = $att['content'];
$mime->addAttachment($marker, $att['mimetype'], $name, false, $encoding, 'attachment', '', '', '', null, null, '', RCUBE_CHARSET, $headers);
}
else {
if (is_resource($att['content'])) {
@rewind($att['content']);
$att['content'] = stream_get_contents($att['content']);
}
$mime->addAttachment($att['content'], $att['mimetype'], $name, false, $encoding, 'attachment', '', '', '', null, null, '', RCUBE_CHARSET, $headers);
}
}
$object['_attachments'][$key]['id'] = ++$part_id;
}
if (!$is_file || !empty($files)) {
$message = $mime->getMessage();
}
// parse message and build message array with
// attachment file pointers in place of file markers
if (!empty($files)) {
$message = explode($marker, $message);
$tmp = array();
foreach ($message as $msg_part) {
$tmp[] = $msg_part;
if ($file = array_shift($files)) {
$tmp[] = $file;
}
}
$message = $tmp;
}
// write complete message body into temp file
else if ($is_file) {
// use common temp dir
$body_file = tempnam($temp_dir, 'rcmMsg');
if (PEAR::isError($mime_result = $mime->saveMessageBody($body_file))) {
self::raise_error(array('code' => 650, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not create message: ".$mime_result->getMessage()),
true, false);
return false;
}
$message = array(trim($mime->txtHeaders()) . "\r\n\r\n", fopen($body_file, 'r'));
}
return $message;
}
/**
* Triggers any required updates after changes within the
* folder. This is currently only required for handling free/busy
* information with Kolab.
*
* @return boolean|PEAR_Error True if successfull.
*/
public function trigger()
{
$owner = $this->get_owner();
$result = false;
switch($this->type) {
case 'event':
if ($this->get_namespace() == 'personal') {
$result = $this->trigger_url(
sprintf('%s/trigger/%s/%s.pfb',
kolab_storage::get_freebusy_server(),
urlencode($owner),
urlencode($this->imap->mod_folder($this->name))
),
$this->imap->options['user'],
$this->imap->options['password']
);
}
break;
default:
return true;
}
if ($result && is_object($result) && is_a($result, 'PEAR_Error')) {
return PEAR::raiseError(sprintf("Failed triggering folder %s. Error was: %s",
$this->name, $result->getMessage()));
}
return $result;
}
/**
* Triggers a URL.
*
* @param string $url The URL to be triggered.
* @param string $auth_user Username to authenticate with
* @param string $auth_passwd Password for basic auth
* @return boolean|PEAR_Error True if successfull.
*/
private function trigger_url($url, $auth_user = null, $auth_passwd = null)
{
- require_once('HTTP/Request2.php');
-
try {
$request = libkolab::http_request($url);
// set authentication credentials
if ($auth_user && $auth_passwd)
$request->setAuth($auth_user, $auth_passwd);
$result = $request->send();
// rcube::write_log('trigger', $result->getBody());
}
catch (Exception $e) {
return PEAR::raiseError($e->getMessage());
}
return true;
}
}
diff --git a/lib/plugins/libkolab/lib/kolab_storage_folder_api.php b/lib/plugins/libkolab/lib/kolab_storage_folder_api.php
index ef3309e..7280389 100644
--- a/lib/plugins/libkolab/lib/kolab_storage_folder_api.php
+++ b/lib/plugins/libkolab/lib/kolab_storage_folder_api.php
@@ -1,330 +1,351 @@
<?php
/**
* Abstract interface class for Kolab storage IMAP folder objects
*
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
abstract class kolab_storage_folder_api
{
/**
* Folder identifier
* @var string
*/
public $id;
/**
* The folder name.
* @var string
*/
public $name;
/**
* The type of this folder.
* @var string
*/
public $type;
+ /**
+ * The subtype of this folder.
+ * @var string
+ */
+ public $subtype;
+
/**
* Is this folder set to be the default for its type
* @var boolean
*/
public $default = false;
/**
* List of direct child folders
* @var array
*/
public $children = array();
/**
* Name of the parent folder
* @var string
*/
public $parent = '';
protected $imap;
protected $owner;
protected $info;
protected $idata;
protected $namespace;
/**
* Private constructor
*/
protected function __construct($name)
{
$this->name = $name;
$this->id = kolab_storage::folder_id($name);
$this->imap = rcube::get_instance()->get_storage();
}
/**
* Returns the owner of the folder.
*
+ * @param boolean Return a fully qualified owner name (i.e. including domain for shared folders)
* @return string The owner of this folder.
*/
- public function get_owner()
+ public function get_owner($fully_qualified = false)
{
// return cached value
if (isset($this->owner))
return $this->owner;
$info = $this->get_folder_info();
$rcmail = rcube::get_instance();
switch ($info['namespace']) {
case 'personal':
$this->owner = $rcmail->get_user_name();
break;
case 'shared':
$this->owner = 'anonymous';
break;
default:
- list($prefix, $user) = explode($this->imap->get_hierarchy_delimiter(), $info['name']);
- if (strpos($user, '@') === false) {
- $domain = strstr($rcmail->get_user_name(), '@');
- if (!empty($domain))
- $user .= $domain;
- }
- $this->owner = $user;
+ list($prefix, $this->owner) = explode($this->imap->get_hierarchy_delimiter(), $info['name']);
+ $fully_qualified = true; // enforce email addresses (backwards compatibility)
break;
}
+ if ($fully_qualified && strpos($this->owner, '@') === false) {
+ // extract domain from current user name
+ $domain = strstr($rcmail->get_user_name(), '@');
+ // fall back to mail_domain config option
+ if (empty($domain) && ($mdomain = $rcmail->config->mail_domain($this->imap->options['host']))) {
+ $domain = '@' . $mdomain;
+ }
+ $this->owner .= $domain;
+ }
+
return $this->owner;
}
/**
* Getter for the name of the namespace to which the IMAP folder belongs
*
* @return string Name of the namespace (personal, other, shared)
*/
public function get_namespace()
{
if (!isset($this->namespace))
$this->namespace = $this->imap->folder_namespace($this->name);
return $this->namespace;
}
/**
* Get the display name value of this folder
*
* @return string Folder name
*/
public function get_name()
{
return kolab_storage::object_name($this->name, $this->get_namespace());
}
/**
* Getter for the top-end folder name (not the entire path)
*
* @return string Name of this folder
*/
public function get_foldername()
{
$parts = explode('/', $this->name);
return rcube_charset::convert(end($parts), 'UTF7-IMAP');
}
/**
* Getter for parent folder path
*
* @return string Full path to parent folder
*/
public function get_parent()
{
$path = explode('/', $this->name);
array_pop($path);
// don't list top-level namespace folder
if (count($path) == 1 && in_array($this->get_namespace(), array('other', 'shared'))) {
$path = array();
}
return join('/', $path);
}
/**
* Getter for the Cyrus mailbox identifier corresponding to this folder
* (e.g. user/john.doe/Calendar/Personal@example.org)
*
* @return string Mailbox ID
*/
public function get_mailbox_id()
{
$info = $this->get_folder_info();
$owner = $this->get_owner();
list($user, $domain) = explode('@', $owner);
switch ($info['namespace']) {
case 'personal':
return sprintf('user/%s/%s@%s', $user, $this->name, $domain);
case 'shared':
$ns = $this->imap->get_namespace('shared');
$prefix = is_array($ns) ? $ns[0][0] : '';
list(, $domain) = explode('@', rcube::get_instance()->get_user_name());
return substr($this->name, strlen($prefix)) . '@' . $domain;
default:
$ns = $this->imap->get_namespace('other');
$prefix = is_array($ns) ? $ns[0][0] : '';
list($user, $folder) = explode($this->imap->get_hierarchy_delimiter(), substr($info['name'], strlen($prefix)), 2);
if (strpos($user, '@')) {
list($user, $domain) = explode('@', $user);
}
return sprintf('user/%s/%s@%s', $user, $folder, $domain);
}
}
/**
* Get the color value stored in metadata
*
* @param string Default color value to return if not set
* @return mixed Color value from IMAP metadata or $default is not set
*/
public function get_color($default = null)
{
// color is defined in folder METADATA
$metadata = $this->get_metadata(array(kolab_storage::COLOR_KEY_PRIVATE, kolab_storage::COLOR_KEY_SHARED));
if (($color = $metadata[kolab_storage::COLOR_KEY_PRIVATE]) || ($color = $metadata[kolab_storage::COLOR_KEY_SHARED])) {
return $color;
}
return $default;
}
/**
* Returns IMAP metadata/annotations (GETMETADATA/GETANNOTATION)
*
* @param array List of metadata keys to read
* @return array Metadata entry-value hash array on success, NULL on error
*/
public function get_metadata($keys)
{
$metadata = rcube::get_instance()->get_storage()->get_metadata($this->name, (array)$keys);
return $metadata[$this->name];
}
/**
* Sets IMAP metadata/annotations (SETMETADATA/SETANNOTATION)
*
* @param array $entries Entry-value array (use NULL value as NIL)
* @return boolean True on success, False on failure
*/
public function set_metadata($entries)
{
return $this->imap->set_metadata($this->name, $entries);
}
/**
*
*/
public function get_folder_info()
{
if (!isset($this->info))
$this->info = $this->imap->folder_info($this->name);
return $this->info;
}
/**
* Make IMAP folder data available for this folder
*/
public function get_imap_data()
{
if (!isset($this->idata))
$this->idata = $this->imap->folder_data($this->name);
return $this->idata;
}
/**
* Get IMAP ACL information for this folder
*
* @return string Permissions as string
*/
public function get_myrights()
{
$rights = $this->info['rights'];
if (!is_array($rights))
$rights = $this->imap->my_rights($this->name);
return join('', (array)$rights);
}
/**
* Check activation status of this folder
*
* @return boolean True if enabled, false if not
*/
public function is_active()
{
return kolab_storage::folder_is_active($this->name);
}
/**
* Change activation status of this folder
*
* @param boolean The desired subscription status: true = active, false = not active
*
* @return True on success, false on error
*/
public function activate($active)
{
return $active ? kolab_storage::folder_activate($this->name) : kolab_storage::folder_deactivate($this->name);
}
/**
* Check subscription status of this folder
*
* @return boolean True if subscribed, false if not
*/
public function is_subscribed()
{
return kolab_storage::folder_is_subscribed($this->name);
}
/**
* Change subscription status of this folder
*
* @param boolean The desired subscription status: true = subscribed, false = not subscribed
*
* @return True on success, false on error
*/
public function subscribe($subscribed)
{
return $subscribed ? kolab_storage::folder_subscribe($this->name) : kolab_storage::folder_unsubscribe($this->name);
}
+ /**
+ * Return folder name as string representation of this object
+ *
+ * @return string Full IMAP folder name
+ */
+ public function __toString()
+ {
+ return $this->name;
+ }
}
diff --git a/lib/plugins/libkolab/lib/kolab_storage_folder_user.php b/lib/plugins/libkolab/lib/kolab_storage_folder_user.php
index 1c37da9..7c141c5 100644
--- a/lib/plugins/libkolab/lib/kolab_storage_folder_user.php
+++ b/lib/plugins/libkolab/lib/kolab_storage_folder_user.php
@@ -1,111 +1,135 @@
<?php
/**
* Class that represents a (virtual) folder in the 'other' namespace
* implementing a subset of the kolab_storage_folder API.
*
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_storage_folder_user extends kolab_storage_folder_virtual
{
protected static $ldapcache = array();
public $ldaprec;
+ public $type;
/**
* Default constructor
*/
public function __construct($name, $parent = '', $ldaprec = null)
{
parent::__construct($name, $name, 'other', $parent);
if (!empty($ldaprec)) {
self::$ldapcache[$name] = $this->ldaprec = $ldaprec;
}
// use value cached in memory for repeated lookups
else if (array_key_exists($name, self::$ldapcache)) {
$this->ldaprec = self::$ldapcache[$name];
}
// lookup user in LDAP and set $this->ldaprec
else if ($ldap = kolab_storage::ldap()) {
// get domain from current user
list(,$domain) = explode('@', rcube::get_instance()->get_user_name());
$this->ldaprec = $ldap->get_user_record(parent::get_foldername($this->name) . '@' . $domain, $_SESSION['imap_host']);
if (!empty($this->ldaprec)) {
$this->ldaprec['kolabtargetfolder'] = $name;
}
self::$ldapcache[$name] = $this->ldaprec;
}
}
/**
* Getter for the top-end folder name to be displayed
*
* @return string Name of this folder
*/
public function get_foldername()
{
return $this->ldaprec ? ($this->ldaprec['displayname'] ?: $this->ldaprec['name']) :
parent::get_foldername();
}
/**
* Getter for a more informative title of this user folder
*
* @return string Title for the given user record
*/
public function get_title()
{
return trim($this->ldaprec['displayname'] . '; ' . $this->ldaprec['mail'], '; ');
}
/**
* Returns the owner of the folder.
*
* @return string The owner of this folder.
*/
public function get_owner()
{
return $this->ldaprec['mail'];
}
/**
- * Check subscription status of this folder
+ * Check subscription status of this folder.
+ * Subscription of a virtual user folder depends on the subscriptions of subfolders.
*
* @return boolean True if subscribed, false if not
*/
public function is_subscribed()
{
- return kolab_storage::folder_is_subscribed($this->name, true);
+ if (!empty($this->type)) {
+ $children = $subscribed = 0;
+ $delimiter = $this->imap->get_hierarchy_delimiter();
+ foreach ((array)kolab_storage::list_folders($this->name . $delimiter, '*', $this->type, false) as $subfolder) {
+ if (kolab_storage::folder_is_subscribed($subfolder)) {
+ $subscribed++;
+ }
+ $children++;
+ }
+ if ($subscribed > 0) {
+ return $subscribed == $children ? true : 2;
+ }
+ }
+
+ return false;
}
/**
* Change subscription status of this folder
*
* @param boolean The desired subscription status: true = subscribed, false = not subscribed
*
* @return True on success, false on error
*/
public function subscribe($subscribed)
{
- return $subscribed ?
- kolab_storage::folder_subscribe($this->name, true) :
- kolab_storage::folder_unsubscribe($this->name, true);
+ $success = false;
+
+ // (un)subscribe all subfolders of a given type
+ if (!empty($this->type)) {
+ $delimiter = $this->imap->get_hierarchy_delimiter();
+ foreach ((array)kolab_storage::list_folders($this->name . $delimiter, '*', $this->type, false) as $subfolder) {
+ $success |= ($subscribed ? kolab_storage::folder_subscribe($subfolder) : kolab_storage::folder_unsubscribe($subfolder));
+ }
+ }
+
+ return $success;
}
}
\ No newline at end of file
diff --git a/lib/plugins/libkolab/package.xml b/lib/plugins/libkolab/package.xml
deleted file mode 100644
index cd3e3a0..0000000
--- a/lib/plugins/libkolab/package.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
- http://pear.php.net/dtd/tasks-1.0.xsd
- http://pear.php.net/dtd/package-2.0
- http://pear.php.net/dtd/package-2.0.xsd">
- <name>libkolab</name>
- <uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
- <summary>Kolab core library</summary>
- <description>Plugin to setup a basic environment for the interaction with a Kolab server.</description>
- <lead>
- <name>Thomas Bruederli</name>
- <user>bruederli</user>
- <email>bruederli@kolabsys.com</email>
- <active>yes</active>
- </lead>
- <developer>
- <name>Alensader Machniak</name>
- <user>machniak</user>
- <email>machniak@kolabsys.com</email>
- <active>yes</active>
- </developer>
- <date>2013-04-19</date>
- <version>
- <release>0.9</release>
- <api>0.9</api>
- </version>
- <stability>
- <release>stable</release>
- <api>stable</api>
- </stability>
- <license uri="http://www.gnu.org/licenses/agpl.html">GNU AGPLv3</license>
- <notes>-</notes>
- <contents>
- <dir baseinstalldir="/" name="/">
- <file name="libkolab.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_format.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_format_configuration.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_format_contact.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_format_distributionlist.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_format_event.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_format_file.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_format_journal.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_format_note.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_format_task.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_format_xcal.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_storage.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_storage_cache.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_storage_folder.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/kolab_date_recurrence.php" role="php">
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
-
- <file name="bin/modcache.php" role="php"></file>
-
- <file name="config.inc.php.dist" role="data"></file>
- <file name="LICENSE" role="data"></file>
- <file name="README" role="data"></file>
- <file name="UPGRADING" role="data"></file>
- </dir>
- <!-- / -->
- </contents>
- <dependencies>
- <required>
- <php>
- <min>5.3.1</min>
- </php>
- <pearinstaller>
- <min>1.7.0</min>
- </pearinstaller>
- </required>
- </dependencies>
- <phprelease/>
-</package>
diff --git a/lib/plugins/managesieve/Changelog b/lib/plugins/managesieve/Changelog
index 01afe69..5255d5b 100644
--- a/lib/plugins/managesieve/Changelog
+++ b/lib/plugins/managesieve/Changelog
@@ -1,314 +1,325 @@
+* version 8.2 [2015-01-14]
+-----------------------------------------------------------
+- Fix bug where actions without if/elseif/else in sieve scripts were skipped
+- Support "not allof" test as a negation of all sub-tests
+- Fix bug where vacation rule was saved to wrong script if managesieve_kolab_master=true
+- Improve procedure of script selection to write a vacation rule
+
+* version 8.1 [2014-12-09]
+-----------------------------------------------------------
+- Added simple API to manage vacation rule
- Fix missing css/js scripts in filter form in mail task
- Fix default vacation status (#1490019)
- Make possible to set vacation start/end date and time
+- Fix compatibility with contextmenu plugin
* version 8.0 [2014-07-16]
-----------------------------------------------------------
- Fix bug where non-existing (or unsubscribed) folder wasn't listed in folder selector (#1489956)
- Added optional separate interface for out-of-office management (#1488266)
- Fix disabled "create filter" action
- Fix enotify/notify extension handling
- Improved UI accessibility
- Added option to specify connection socket parameters - managesieve_conn_options
- Support vacation date rules without date extension (#1489978)
* version 7.2 [2014-02-14]
-----------------------------------------------------------
- Nicely handle server-side modification of script names (#1489412)
- Add Filters tab/section using plugin API hook
- Fix issue where folder selector wasn't visible on new filter form
- Fix issue where multi-select fields were not visible in new filter action rows (#1489600)
- Fix issue in displaying filter form when managesieve_kolab_master=true
and sieve variables extension is supported by the server (#1489599)
- Fix wrong action folder selection if managesieve_domains is not empty (#1489617)
- Fix filter creation from an email when preview frame is disabled (#1489647)
* version 7.1 [2013-11-22]
-----------------------------------------------------------
- lib/Net_Sieve.php moved to Roundcube /lib directory
- Added managesieve_domains option to limit redirect destinations
- Fix bug where at least one additional address of vacation message was required (#1489345)
- Fix so i;ascii-numeric comparator is not forced as default for :count and :value operators
- Fix date/currentdate related form issues and comparators handling (#1489346)
- Fix a bug where deleted filter was not removed from the list
* version 7.0 [2013-09-09]
-----------------------------------------------------------
- Add vacation-seconds extension support (RFC 6131)
- Several script parser code improvements
- Support string list arguments in filter form (#1489018)
- Support date, currendate and index tests - RFC5260 (#1488120)
- Split plugin file into two files
- Fix handling of &, <, > characters in scripts/filter names (#1489208)
- Support 'keep' action (#1489226)
- Add common headers to header selector (#1489271)
* version 6.2 [2013-02-17]
-----------------------------------------------------------
- Support tls:// prefix in managesieve_host option
- Removed depracated functions usage
- Don't trim whitespace in folder names (#1488955)
* version 6.1 [2012-12-21]
-----------------------------------------------------------
- Fixed filter activation/deactivation confirmation message (#1488765)
- Moved rcube_* classes to <plugin>/lib/Roundcube for compat. with Roundcube Framework autoloader
- Fixed filter selection after filter deletion (#1488832)
- Fixed compatibility with jQueryUI-1.9
- Don't force 'stop' action on last rule in a script
* version 6.0 [2012-10-03]
-----------------------------------------------------------
- Fixed issue with DBMail bug [http://pear.php.net/bugs/bug.php?id=19077] (#1488594)
- Added support for enotify/notify (RFC5435, RFC5436, draft-ietf-sieve-notify-00)
- Change default port to 4190 (IANA-allocated), add port auto-detection (#1488713)
- Added request size limits detection and script corruption prevention (#1488648)
- Fix so scripts listed in managesieve_filename_exceptions aren't displayed on the list (#1488724)
* version 5.2 [2012-07-24]
-----------------------------------------------------------
- Added GUI for variables setting - RFC5229 (patch from Paweł Słowik)
- Fixed scrollbars in Larry's iframes
- Fix performance issue in message_headers_output hook handling
* version 5.1 [2012-06-21]
-----------------------------------------------------------
- Fixed filter popup width (for non-english localizations)
- Fixed tokenizer infinite loop on invalid script content
- Larry skin support
- Fixed custom header name validity check, made RFC2822-compliant
* version 5.0 [2012-01-05]
-----------------------------------------------------------
- Fixed setting test type to :is when none is specified
- Fixed javascript error in IE8
- Fixed possible ID duplication when adding filter rules very fast (#1488288)
- Fixed bug where drag layer wasn't removed when dragging was ended over sets list
* version 5.0-rc1 [2011-11-17]
-----------------------------------------------------------
- Fixed sorting of scripts, scripts including aware of the sort order
- Fixed import of rules with unsupported tests
- Added 'address' and 'envelope' tests support
- Added 'body' extension support (RFC5173)
- Added 'subaddress' extension support (RFC5233)
- Added comparators support
- Changed Sender/Recipient labels to From/To
- Fixed importing rule names from Ingo
- Fixed handling of extensions disabled in config
* version 5.0-beta [2011-10-17]
-----------------------------------------------------------
- Added possibility to create a filter based on selected message "in-place"
- Fixed import from Horde-INGO (#1488064)
- Add managesieve_script_name option for default name of the script (#1487956)
- Fixed handling of enabled magic_quotes_gpc setting
- Fixed PHP warning on connection error when submitting filter form
- Fixed bug where new action row with flags wasn't handled properly
- Added managesieve_connect hook for plugins
- Fixed doubled Filter tab on page refresh
- Added filters set selector in filter form when invoked in mail task
- Improved script parser, added support for include and variables extensions
- Added Kolab's KEP:14 support (http://wiki.kolab.org/User:Greve/Drafts/KEP:14)
- Use smaller action/rule buttons
- UI redesign: added possibility to move filter to any place using drag&drop
(instead of up/down buttons), added filter sets list object, added more
'loading' messages
- Added option to hide some scripts (managesieve_filename_exceptions)
* version 4.3 [2011-07-28]
-----------------------------------------------------------
- Fixed handling of error in Net_Sieve::listScripts()
- Fixed handling of REFERRAL responses (http://pear.php.net/bugs/bug.php?id=17107)
- Fixed bug where wrong folders hierarchy was displayed on folders listing
* version 4.2 [2011-05-24]
-----------------------------------------------------------
- Moved elsif replacement code to handle only imports from other formats
- Fixed mod_mailbox() usage for newer Roundcube versions
- Fixed regex extension (error: regex require missing)
* version 4.1 [2011-03-07]
-----------------------------------------------------------
- Fix fileinto target is always INBOX (#1487776)
- Fix escaping of backslash character in quoted strings (#1487780)
- Fix handling of non-safe characters (double-quote, backslash)
or UTF-8 characters (dovecot's implementation bug workaround)
in script names
- Fix saving of a script using flags extension on servers with imap4flags support (#1487825)
* version 4.0 [2011-02-10]
-----------------------------------------------------------
- Fix STARTTLS for timsieved < 2.3.10
- Added :regex and :matches support (#1487746)
- Added setflag/addflag/removeflag support (#1487449)
- Added support for vacation :subject field (#1487120)
- rcube_sieve_script class moved to separate file
- Moved javascript code from skin templates into managesieve.js file
* version 3.0 [2011-02-01]
-----------------------------------------------------------
- Added support for SASL proxy authentication (#1486691)
- Fixed parsing of scripts with \r\n line separator
- Apply forgotten changes for form errors handling
- Fix multi-line strings parsing (#1487685)
- Added tests for script parser
- Rewritten script parser
- Fix double request when clicking on Filters tab using Firefox
* version 2.10 [2010-10-10]
-----------------------------------------------------------
- Fixed import from Avelsieve
- Use localized size units (#1486976)
- Added support for relational operators and i;ascii-numeric comparator
- Added popups with form errors
* version 2.9 [2010-08-02]
-----------------------------------------------------------
- Fixed vacation parameters parsing (#1486883)
* version 2.8 [2010-07-08]
-----------------------------------------------------------
- Added managesieve_auth_type option (#1486731)
* version 2.7 [2010-07-06]
-----------------------------------------------------------
- Update Net_Sieve to version 1.3.0 (fixes LOGIN athentication)
- Added support for copying and copy sending of messages (COPY extension)
* version 2.6 [2010-06-03]
-----------------------------------------------------------
- Support %n and %d variables in managesieve_host option
* version 2.5 [2010-05-04]
-----------------------------------------------------------
- Fix filters set label after activation
- Fix filters set activation, add possibility to deactivate sets (#1486699)
- Fix download button state when sets list is empty
- Fix errors when sets list is empty
* version 2.4 [2010-04-01]
-----------------------------------------------------------
- Fixed bug in DIGEST-MD5 authentication (http://pear.php.net/bugs/bug.php?id=17285)
- Fixed disabling rules with many tests
- Small css unification with core
- Scripts import/export
* version 2.3 [2010-03-18]
-----------------------------------------------------------
- Added import from Horde-INGO
- Support for more than one match using if+stop instead of if+elsif structures (#1486078)
- Support for selectively disabling rules within a single sieve script (#1485882)
- Added vertical splitter
* version 2.2 [2010-02-06]
-----------------------------------------------------------
- Fix handling of "<>" characters in filter names (#1486477)
* version 2.1 [2010-01-12]
-----------------------------------------------------------
- Fix "require" structure generation when many modules are used
- Fix problem with '<' and '>' characters in header tests
* version 2.0 [2009-11-02]
-----------------------------------------------------------
- Added 'managesieve_debug' option
- Added multi-script support
- Small css improvements + sprite image buttons
- PEAR::NetSieve 1.2.0b1
* version 1.7 [2009-09-20]
-----------------------------------------------------------
- Support multiple managesieve hosts using %h variable
in managesieve_host option
- Fix first rule deleting (#1486140)
* version 1.6 [2009-09-08]
-----------------------------------------------------------
- Fix warning when importing squirrelmail rules
- Fix handling of "true" as "anyof (true)" test
* version 1.5 [2009-09-04]
-----------------------------------------------------------
- Added es_ES, ua_UA localizations
- Added 'managesieve_mbox_encoding' option
* version 1.4 [2009-07-29]
-----------------------------------------------------------
- Updated PEAR::Net_Sieve to 1.1.7
* version 1.3 [2009-07-24]
-----------------------------------------------------------
- support more languages
- support config.inc.php file
* version 1.2 [2009-06-28]
-----------------------------------------------------------
- Support IMAP namespaces in fileinto (#1485943)
- Added it_IT localization
* version 1.1 [2009-05-27]
-----------------------------------------------------------
- Added new icons
- Added support for headers lists (coma-separated) in rules
- Added de_CH localization
* version 1.0 [2009-05-21]
-----------------------------------------------------------
- Rewritten using plugin API
- Added hu_HU localization (Tamas Tevesz)
* version beta7 (svn-r2300) [2009-03-01]
-----------------------------------------------------------
- Added SquirrelMail script auto-import (Jonathan Ernst)
- Added 'vacation' support (Jonathan Ernst & alec)
- Added 'stop' support (Jonathan Ernst)
- Added option for extensions disabling (Jonathan Ernst & alec)
- Added fi_FI, nl_NL, bg_BG localization
- Small style fixes
* version 0.2-stable1 (svn-r2205) [2009-01-03]
-----------------------------------------------------------
- Fix moving down filter row
- Fixes for compressed js files in stable release package
- Created patch for svn version r2205
* version 0.2-stable [2008-12-31]
-----------------------------------------------------------
- Added ru_RU, fr_FR, zh_CN translation
- Fixes for Roundcube 0.2-stable
* version 0.2-beta [2008-09-21]
-----------------------------------------------------------
- Small css fixes for IE
- Fixes for Roundcube 0.2-beta
* version beta6 [2008-08-08]
-----------------------------------------------------------
- Added de_DE translation
- Fix for Roundcube r1634
* version beta5 [2008-06-10]
-----------------------------------------------------------
- Fixed 'exists' operators
- Fixed 'not*' operators for custom headers
- Fixed filters deleting
* version beta4 [2008-06-09]
-----------------------------------------------------------
- Fix for Roundcube r1490
* version beta3 [2008-05-22]
-----------------------------------------------------------
- Fixed textarea error class setting
- Added pagetitle setting
- Added option 'managesieve_replace_delimiter'
- Fixed errors on IE (still need some css fixes)
* version beta2 [2008-05-20]
-----------------------------------------------------------
- Use 'if' only for first filter and 'elsif' for the rest
* version beta1 [2008-05-15]
-----------------------------------------------------------
- Initial version for Roundcube r1388.
diff --git a/lib/plugins/managesieve/composer.json b/lib/plugins/managesieve/composer.json
index 51e76bc..6d640da 100644
--- a/lib/plugins/managesieve/composer.json
+++ b/lib/plugins/managesieve/composer.json
@@ -1,29 +1,29 @@
{
"name": "roundcube/managesieve",
"type": "roundcube-plugin",
"description": "Adds a possibility to manage Sieve scripts (incoming mail filters). It's clickable interface which operates on text scripts and communicates with server using managesieve protocol. Adds Filters tab in Settings.",
- "license": "GNU GPLv3+",
- "version": "7.2",
+ "license": "GPLv3+",
+ "version": "8.2",
"authors": [
{
"name": "Aleksander Machniak",
"email": "alec@alec.pl",
"role": "Lead"
}
],
"repositories": [
{
"type": "composer",
"url": "http://plugins.roundcube.net"
},
{
"type": "pear",
"url": "http://pear.php.net/"
}
],
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"pear-pear/Net_Sieve": ">=1.3.2"
}
}
diff --git a/lib/plugins/managesieve/config.inc.php.dist b/lib/plugins/managesieve/config.inc.php.dist
index 1f20b5a..b9f9a50 100644
--- a/lib/plugins/managesieve/config.inc.php.dist
+++ b/lib/plugins/managesieve/config.inc.php.dist
@@ -1,90 +1,90 @@
<?php
// managesieve server port. When empty the port will be determined automatically
// using getservbyname() function, with 4190 as a fallback.
$config['managesieve_port'] = null;
// managesieve server address, default is localhost.
// Replacement variables supported in host name:
// %h - user's IMAP hostname
// %n - http hostname ($_SERVER['SERVER_NAME'])
// %d - domain (http hostname without the first part)
// For example %n = mail.domain.tld, %d = domain.tld
$config['managesieve_host'] = 'localhost';
// authentication method. Can be CRAM-MD5, DIGEST-MD5, PLAIN, LOGIN, EXTERNAL
// or none. Optional, defaults to best method supported by server.
$config['managesieve_auth_type'] = null;
// Optional managesieve authentication identifier to be used as authorization proxy.
// Authenticate as a different user but act on behalf of the logged in user.
// Works with PLAIN and DIGEST-MD5 auth.
$config['managesieve_auth_cid'] = null;
// Optional managesieve authentication password to be used for imap_auth_cid
$config['managesieve_auth_pw'] = null;
// use or not TLS for managesieve server connection
// Note: tls:// prefix in managesieve_host is also supported
$config['managesieve_usetls'] = false;
// Connection scket context options
// See http://php.net/manual/en/context.ssl.php
// The example below enables server certificate validation
-//$config['imap_conn_options'] = array(
+//$config['managesieve_conn_options'] = array(
// 'ssl' => array(
// 'verify_peer' => true,
// 'verify_depth' => 3,
// 'cafile' => '/etc/openssl/certs/ca.crt',
// ),
// );
$config['managesieve_conn_options'] = null;
// default contents of filters script (eg. default spam filter)
$config['managesieve_default'] = '/etc/dovecot/sieve/global';
// The name of the script which will be used when there's no user script
$config['managesieve_script_name'] = 'managesieve';
// Sieve RFC says that we should use UTF-8 endcoding for mailbox names,
// but some implementations does not covert UTF-8 to modified UTF-7.
// Defaults to UTF7-IMAP
$config['managesieve_mbox_encoding'] = 'UTF-8';
// I need this because my dovecot (with listescape plugin) uses
// ':' delimiter, but creates folders with dot delimiter
$config['managesieve_replace_delimiter'] = '';
// disabled sieve extensions (body, copy, date, editheader, encoded-character,
// envelope, environment, ereject, fileinto, ihave, imap4flags, index,
// mailbox, mboxmetadata, regex, reject, relational, servermetadata,
// spamtest, spamtestplus, subaddress, vacation, variables, virustest, etc.
// Note: not all extensions are implemented
$config['managesieve_disabled_extensions'] = array();
// Enables debugging of conversation with sieve server. Logs it into <log_dir>/sieve
$config['managesieve_debug'] = false;
// Enables features described in http://wiki.kolab.org/KEP:14
$config['managesieve_kolab_master'] = false;
// Script name extension used for scripts including. Dovecot uses '.sieve',
// Cyrus uses '.siv'. Doesn't matter if you have managesieve_kolab_master disabled.
$config['managesieve_filename_extension'] = '.sieve';
// List of reserved script names (without extension).
// Scripts listed here will be not presented to the user.
$config['managesieve_filename_exceptions'] = array();
// List of domains limiting destination emails in redirect action
// If not empty, user will need to select domain from a list
$config['managesieve_domains'] = array();
// Enables separate management interface for vacation responses (out-of-office)
// 0 - no separate section (default),
// 1 - add Vacation section,
// 2 - add Vacation section, but hide Filters section
$config['managesieve_vacation'] = 0;
// Supported methods of notify extension. Default: 'mailto'
$config['managesieve_notify_methods'] = array('mailto');
diff --git a/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
index 302c7c7..d412e17 100644
--- a/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
+++ b/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
@@ -1,2348 +1,2381 @@
<?php
/**
* Managesieve (Sieve Filters) Engine
*
* Engine part of Managesieve plugin implementing UI and backend access.
*
- * Copyright (C) 2008-2013, The Roundcube Dev Team
- * Copyright (C) 2011-2013, Kolab Systems AG
+ * Copyright (C) 2008-2014, The Roundcube Dev Team
+ * Copyright (C) 2011-2014, Kolab Systems AG
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
class rcube_sieve_engine
{
protected $rc;
protected $sieve;
protected $errors;
protected $form;
protected $tips = array();
protected $script = array();
protected $exts = array();
protected $list;
protected $active = array();
protected $headers = array(
'subject' => 'Subject',
'from' => 'From',
'to' => 'To',
);
protected $addr_headers = array(
// Required
"from", "to", "cc", "bcc", "sender", "resent-from", "resent-to",
// Additional (RFC 822 / RFC 2822)
"reply-to", "resent-reply-to", "resent-sender", "resent-cc", "resent-bcc",
// Non-standard (RFC 2076, draft-palme-mailext-headers-08.txt)
"for-approval", "for-handling", "for-comment", "apparently-to", "errors-to",
"delivered-to", "return-receipt-to", "x-admin", "read-receipt-to",
"x-confirm-reading-to", "return-receipt-requested",
"registered-mail-reply-requested-by", "mail-followup-to", "mail-reply-to",
"abuse-reports-to", "x-complaints-to", "x-report-abuse-to",
// Undocumented
"x-beenthere",
);
protected $notify_methods = array(
'mailto',
// 'sms',
// 'tel',
);
protected $notify_importance_options = array(
3 => 'notifyimportancelow',
2 => 'notifyimportancenormal',
1 => 'notifyimportancehigh'
);
- const VERSION = '8.0';
+ const VERSION = '8.2';
const PROGNAME = 'Roundcube (Managesieve)';
const PORT = 4190;
/**
* Class constructor
*/
function __construct($plugin)
{
$this->rc = rcube::get_instance();
$this->plugin = $plugin;
}
/**
* Loads configuration, initializes plugin (including sieve connection)
*/
function start($mode = null)
{
// register UI objects
$this->rc->output->add_handlers(array(
'filterslist' => array($this, 'filters_list'),
'filtersetslist' => array($this, 'filtersets_list'),
'filterframe' => array($this, 'filter_frame'),
'filterform' => array($this, 'filter_form'),
'filtersetform' => array($this, 'filterset_form'),
));
// connect to managesieve server
$error = $this->connect($_SESSION['username'], $this->rc->decrypt($_SESSION['password']));
// load current/active script
if (!$error) {
// Get list of scripts
$list = $this->list_scripts();
// reset current script when entering filters UI (#1489412)
if ($this->rc->action == 'plugin.managesieve') {
$this->rc->session->remove('managesieve_current');
}
if ($mode != 'vacation') {
if (!empty($_GET['_set']) || !empty($_POST['_set'])) {
$script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
}
else if (!empty($_SESSION['managesieve_current'])) {
$script_name = $_SESSION['managesieve_current'];
}
}
$error = $this->load_script($script_name);
}
// finally set script objects
if ($error) {
switch ($error) {
case rcube_sieve::ERROR_CONNECTION:
case rcube_sieve::ERROR_LOGIN:
$this->rc->output->show_message('managesieve.filterconnerror', 'error');
rcube::raise_error(array('code' => 403, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Unable to connect to managesieve on $host:$port"), true, false);
break;
default:
$this->rc->output->show_message('managesieve.filterunknownerror', 'error');
break;
}
// reload interface in case of possible error when specified script wasn't found (#1489412)
if ($script_name !== null && !empty($list) && !in_array($script_name, $list)) {
$this->rc->output->command('reload', 500);
}
// to disable 'Add filter' button set env variable
$this->rc->output->set_env('filterconnerror', true);
$this->script = array();
}
else {
$this->exts = $this->sieve->get_extensions();
$this->init_script();
$this->rc->output->set_env('currentset', $this->sieve->current);
$_SESSION['managesieve_current'] = $this->sieve->current;
}
return $error;
}
/**
* Connect to configured managesieve server
*
* @param string $username User login
* @param string $password User password
*
* @return int Connection status: 0 on success, >0 on failure
*/
public function connect($username, $password)
{
// Get connection parameters
$host = $this->rc->config->get('managesieve_host', 'localhost');
$port = $this->rc->config->get('managesieve_port');
$tls = $this->rc->config->get('managesieve_usetls', false);
$host = rcube_utils::parse_host($host);
$host = rcube_utils::idn_to_ascii($host);
// remove tls:// prefix, set TLS flag
if (($host = preg_replace('|^tls://|i', '', $host, 1, $cnt)) && $cnt) {
$tls = true;
}
if (empty($port)) {
$port = getservbyname('sieve', 'tcp');
if (empty($port)) {
$port = self::PORT;
}
}
$plugin = $this->rc->plugins->exec_hook('managesieve_connect', array(
'user' => $username,
'password' => $password,
'host' => $host,
'port' => $port,
'usetls' => $tls,
'auth_type' => $this->rc->config->get('managesieve_auth_type'),
'disabled' => $this->rc->config->get('managesieve_disabled_extensions'),
'debug' => $this->rc->config->get('managesieve_debug', false),
'auth_cid' => $this->rc->config->get('managesieve_auth_cid'),
'auth_pw' => $this->rc->config->get('managesieve_auth_pw'),
'socket_options' => $this->rc->config->get('managesieve_conn_options'),
));
// try to connect to managesieve server and to fetch the script
$this->sieve = new rcube_sieve(
$plugin['user'],
$plugin['password'],
$plugin['host'],
$plugin['port'],
$plugin['auth_type'],
$plugin['usetls'],
$plugin['disabled'],
$plugin['debug'],
$plugin['auth_cid'],
$plugin['auth_pw'],
$plugin['socket_options']
);
return $this->sieve->error();
}
/**
* Load specified (or active) script
*
* @param string $script_name Optional script name
*
* @return int Connection status: 0 on success, >0 on failure
*/
- public function load_script($script_name = null)
+ protected function load_script($script_name = null)
{
// Get list of scripts
$list = $this->list_scripts();
if ($script_name === null || $script_name === '') {
// get (first) active script
- if (!empty($this->active[0])) {
+ if (!empty($this->active)) {
$script_name = $this->active[0];
}
else if ($list) {
$script_name = $list[0];
}
// create a new (initial) script
else {
// if script not exists build default script contents
$script_file = $this->rc->config->get('managesieve_default');
$script_name = $this->rc->config->get('managesieve_script_name');
if (empty($script_name)) {
$script_name = 'roundcube';
}
if ($script_file && is_readable($script_file)) {
$content = file_get_contents($script_file);
}
// add script and set it active
if ($this->sieve->save_script($script_name, $content)) {
$this->activate_script($script_name);
$this->list[] = $script_name;
}
}
}
if ($script_name) {
$this->sieve->load($script_name);
}
return $this->sieve->error();
}
/**
* User interface actions handler
*/
function actions()
{
$error = $this->start();
// Handle user requests
if ($action = rcube_utils::get_input_value('_act', rcube_utils::INPUT_GPC)) {
$fid = (int) rcube_utils::get_input_value('_fid', rcube_utils::INPUT_POST);
if ($action == 'delete' && !$error) {
if (isset($this->script[$fid])) {
if ($this->sieve->script->delete_rule($fid))
$result = $this->save_script();
if ($result === true) {
$this->rc->output->show_message('managesieve.filterdeleted', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'del', array('id' => $fid));
} else {
$this->rc->output->show_message('managesieve.filterdeleteerror', 'error');
}
}
}
else if ($action == 'move' && !$error) {
if (isset($this->script[$fid])) {
$to = (int) rcube_utils::get_input_value('_to', rcube_utils::INPUT_POST);
$rule = $this->script[$fid];
// remove rule
unset($this->script[$fid]);
$this->script = array_values($this->script);
// add at target position
if ($to >= count($this->script)) {
$this->script[] = $rule;
}
else {
$script = array();
foreach ($this->script as $idx => $r) {
if ($idx == $to)
$script[] = $rule;
$script[] = $r;
}
$this->script = $script;
}
$this->sieve->script->content = $this->script;
$result = $this->save_script();
if ($result === true) {
$result = $this->list_rules();
$this->rc->output->show_message('managesieve.moved', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'list',
array('list' => $result, 'clear' => true, 'set' => $to));
} else {
$this->rc->output->show_message('managesieve.moveerror', 'error');
}
}
}
else if ($action == 'act' && !$error) {
if (isset($this->script[$fid])) {
$rule = $this->script[$fid];
$disabled = $rule['disabled'] ? true : false;
$rule['disabled'] = !$disabled;
$result = $this->sieve->script->update_rule($fid, $rule);
if ($result !== false)
$result = $this->save_script();
if ($result === true) {
if ($rule['disabled'])
$this->rc->output->show_message('managesieve.deactivated', 'confirmation');
else
$this->rc->output->show_message('managesieve.activated', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'update',
array('id' => $fid, 'disabled' => $rule['disabled']));
} else {
if ($rule['disabled'])
$this->rc->output->show_message('managesieve.deactivateerror', 'error');
else
$this->rc->output->show_message('managesieve.activateerror', 'error');
}
}
}
else if ($action == 'setact' && !$error) {
- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
+ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true);
$result = $this->activate_script($script_name);
$kep14 = $this->rc->config->get('managesieve_kolab_master');
if ($result === true) {
$this->rc->output->set_env('active_sets', $this->active);
$this->rc->output->show_message('managesieve.setactivated', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'setact',
array('name' => $script_name, 'active' => true, 'all' => !$kep14));
} else {
$this->rc->output->show_message('managesieve.setactivateerror', 'error');
}
}
else if ($action == 'deact' && !$error) {
- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
+ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true);
$result = $this->deactivate_script($script_name);
if ($result === true) {
$this->rc->output->set_env('active_sets', $this->active);
$this->rc->output->show_message('managesieve.setdeactivated', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'setact',
array('name' => $script_name, 'active' => false));
} else {
$this->rc->output->show_message('managesieve.setdeactivateerror', 'error');
}
}
else if ($action == 'setdel' && !$error) {
- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
+ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true);
$result = $this->remove_script($script_name);
if ($result === true) {
$this->rc->output->show_message('managesieve.setdeleted', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'setdel',
array('name' => $script_name));
$this->rc->session->remove('managesieve_current');
} else {
$this->rc->output->show_message('managesieve.setdeleteerror', 'error');
}
}
else if ($action == 'setget') {
$script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
$script = $this->sieve->get_script($script_name);
if (PEAR::isError($script))
exit;
$browser = new rcube_browser;
// send download headers
header("Content-Type: application/octet-stream");
header("Content-Length: ".strlen($script));
if ($browser->ie) {
header("Content-Type: application/force-download");
$filename = rawurlencode($script_name);
}
else {
$filename = addcslashes($script_name, '\\"');
}
header("Content-Disposition: attachment; filename=\"$filename.txt\"");
echo $script;
exit;
}
else if ($action == 'list') {
$result = $this->list_rules();
$this->rc->output->command('managesieve_updatelist', 'list', array('list' => $result));
}
else if ($action == 'ruleadd') {
- $rid = rcube_utils::get_input_value('_rid', rcube_utils::INPUT_GPC);
+ $rid = rcube_utils::get_input_value('_rid', rcube_utils::INPUT_POST);
$id = $this->genid();
$content = $this->rule_div($fid, $id, false);
$this->rc->output->command('managesieve_rulefill', $content, $id, $rid);
}
else if ($action == 'actionadd') {
- $aid = rcube_utils::get_input_value('_aid', rcube_utils::INPUT_GPC);
+ $aid = rcube_utils::get_input_value('_aid', rcube_utils::INPUT_POST);
$id = $this->genid();
$content = $this->action_div($fid, $id, false);
$this->rc->output->command('managesieve_actionfill', $content, $id, $aid);
}
$this->rc->output->send();
}
else if ($this->rc->task == 'mail') {
// Initialize the form
$rules = rcube_utils::get_input_value('r', rcube_utils::INPUT_GET);
if (!empty($rules)) {
$i = 0;
foreach ($rules as $rule) {
list($header, $value) = explode(':', $rule, 2);
$tests[$i] = array(
'type' => 'contains',
'test' => 'header',
'arg1' => $header,
'arg2' => $value,
);
$i++;
}
$this->form = array(
'join' => count($tests) > 1 ? 'allof' : 'anyof',
'name' => '',
'tests' => $tests,
'actions' => array(
0 => array('type' => 'fileinto'),
1 => array('type' => 'stop'),
),
);
}
}
$this->send();
}
function save()
{
// Init plugin and handle managesieve connection
$error = $this->start();
// get request size limits (#1488648)
$max_post = max(array(
ini_get('max_input_vars'),
ini_get('suhosin.request.max_vars'),
ini_get('suhosin.post.max_vars'),
));
$max_depth = max(array(
ini_get('suhosin.request.max_array_depth'),
ini_get('suhosin.post.max_array_depth'),
));
// check request size limit
if ($max_post && count($_POST, COUNT_RECURSIVE) >= $max_post) {
rcube::raise_error(array(
'code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Request size limit exceeded (one of max_input_vars/suhosin.request.max_vars/suhosin.post.max_vars)"
), true, false);
$this->rc->output->show_message('managesieve.filtersaveerror', 'error');
}
// check request depth limits
else if ($max_depth && count($_POST['_header']) > $max_depth) {
rcube::raise_error(array(
'code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Request size limit exceeded (one of suhosin.request.max_array_depth/suhosin.post.max_array_depth)"
), true, false);
$this->rc->output->show_message('managesieve.filtersaveerror', 'error');
}
// filters set add action
else if (!empty($_POST['_newset'])) {
$name = rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST, true);
$copy = rcube_utils::get_input_value('_copy', rcube_utils::INPUT_POST, true);
$from = rcube_utils::get_input_value('_from', rcube_utils::INPUT_POST);
$exceptions = $this->rc->config->get('managesieve_filename_exceptions');
$kolab = $this->rc->config->get('managesieve_kolab_master');
$name_uc = mb_strtolower($name);
$list = $this->list_scripts();
if (!$name) {
$this->errors['name'] = $this->plugin->gettext('cannotbeempty');
}
else if (mb_strlen($name) > 128) {
$this->errors['name'] = $this->plugin->gettext('nametoolong');
}
else if (!empty($exceptions) && in_array($name, (array)$exceptions)) {
$this->errors['name'] = $this->plugin->gettext('namereserved');
}
else if (!empty($kolab) && in_array($name_uc, array('MASTER', 'USER', 'MANAGEMENT'))) {
$this->errors['name'] = $this->plugin->gettext('namereserved');
}
else if (in_array($name, $list)) {
$this->errors['name'] = $this->plugin->gettext('setexist');
}
else if ($from == 'file') {
// from file
if (is_uploaded_file($_FILES['_file']['tmp_name'])) {
$file = file_get_contents($_FILES['_file']['tmp_name']);
$file = preg_replace('/\r/', '', $file);
// for security don't save script directly
// check syntax before, like this...
$this->sieve->load_script($file);
if (!$this->save_script($name)) {
$this->errors['file'] = $this->plugin->gettext('setcreateerror');
}
}
else { // upload failed
$err = $_FILES['_file']['error'];
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
$msg = $this->rc->gettext(array('name' => 'filesizeerror',
'vars' => array('size' =>
$this->rc->show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
}
else {
$this->errors['file'] = $this->plugin->gettext('fileuploaderror');
}
}
}
else if (!$this->sieve->copy($name, $from == 'set' ? $copy : '')) {
$error = 'managesieve.setcreateerror';
}
if (!$error && empty($this->errors)) {
// Find position of the new script on the list
$list[] = $name;
asort($list, SORT_LOCALE_STRING);
$list = array_values($list);
$index = array_search($name, $list);
$this->rc->output->show_message('managesieve.setcreated', 'confirmation');
$this->rc->output->command('parent.managesieve_updatelist', 'setadd',
array('name' => $name, 'index' => $index));
} else if ($msg) {
$this->rc->output->command('display_message', $msg, 'error');
} else if ($error) {
$this->rc->output->show_message($error, 'error');
}
}
// filter add/edit action
else if (isset($_POST['_name'])) {
$name = trim(rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST, true));
$fid = trim(rcube_utils::get_input_value('_fid', rcube_utils::INPUT_POST));
$join = trim(rcube_utils::get_input_value('_join', rcube_utils::INPUT_POST));
// and arrays
$headers = rcube_utils::get_input_value('_header', rcube_utils::INPUT_POST);
$cust_headers = rcube_utils::get_input_value('_custom_header', rcube_utils::INPUT_POST);
$ops = rcube_utils::get_input_value('_rule_op', rcube_utils::INPUT_POST);
$sizeops = rcube_utils::get_input_value('_rule_size_op', rcube_utils::INPUT_POST);
$sizeitems = rcube_utils::get_input_value('_rule_size_item', rcube_utils::INPUT_POST);
$sizetargets = rcube_utils::get_input_value('_rule_size_target', rcube_utils::INPUT_POST);
$targets = rcube_utils::get_input_value('_rule_target', rcube_utils::INPUT_POST, true);
$mods = rcube_utils::get_input_value('_rule_mod', rcube_utils::INPUT_POST);
$mod_types = rcube_utils::get_input_value('_rule_mod_type', rcube_utils::INPUT_POST);
$body_trans = rcube_utils::get_input_value('_rule_trans', rcube_utils::INPUT_POST);
$body_types = rcube_utils::get_input_value('_rule_trans_type', rcube_utils::INPUT_POST, true);
$comparators = rcube_utils::get_input_value('_rule_comp', rcube_utils::INPUT_POST);
$indexes = rcube_utils::get_input_value('_rule_index', rcube_utils::INPUT_POST);
$lastindexes = rcube_utils::get_input_value('_rule_index_last', rcube_utils::INPUT_POST);
$dateheaders = rcube_utils::get_input_value('_rule_date_header', rcube_utils::INPUT_POST);
$dateparts = rcube_utils::get_input_value('_rule_date_part', rcube_utils::INPUT_POST);
$act_types = rcube_utils::get_input_value('_action_type', rcube_utils::INPUT_POST, true);
$mailboxes = rcube_utils::get_input_value('_action_mailbox', rcube_utils::INPUT_POST, true);
$act_targets = rcube_utils::get_input_value('_action_target', rcube_utils::INPUT_POST, true);
$domain_targets = rcube_utils::get_input_value('_action_target_domain', rcube_utils::INPUT_POST);
$area_targets = rcube_utils::get_input_value('_action_target_area', rcube_utils::INPUT_POST, true);
$reasons = rcube_utils::get_input_value('_action_reason', rcube_utils::INPUT_POST, true);
$addresses = rcube_utils::get_input_value('_action_addresses', rcube_utils::INPUT_POST, true);
$intervals = rcube_utils::get_input_value('_action_interval', rcube_utils::INPUT_POST);
$interval_types = rcube_utils::get_input_value('_action_interval_type', rcube_utils::INPUT_POST);
$subject = rcube_utils::get_input_value('_action_subject', rcube_utils::INPUT_POST, true);
$flags = rcube_utils::get_input_value('_action_flags', rcube_utils::INPUT_POST);
$varnames = rcube_utils::get_input_value('_action_varname', rcube_utils::INPUT_POST);
$varvalues = rcube_utils::get_input_value('_action_varvalue', rcube_utils::INPUT_POST);
$varmods = rcube_utils::get_input_value('_action_varmods', rcube_utils::INPUT_POST);
$notifymethods = rcube_utils::get_input_value('_action_notifymethod', rcube_utils::INPUT_POST);
$notifytargets = rcube_utils::get_input_value('_action_notifytarget', rcube_utils::INPUT_POST, true);
$notifyoptions = rcube_utils::get_input_value('_action_notifyoption', rcube_utils::INPUT_POST, true);
$notifymessages = rcube_utils::get_input_value('_action_notifymessage', rcube_utils::INPUT_POST, true);
$notifyfrom = rcube_utils::get_input_value('_action_notifyfrom', rcube_utils::INPUT_POST);
$notifyimp = rcube_utils::get_input_value('_action_notifyimportance', rcube_utils::INPUT_POST);
// we need a "hack" for radiobuttons
foreach ($sizeitems as $item)
$items[] = $item;
$this->form['disabled'] = $_POST['_disabled'] ? true : false;
$this->form['join'] = $join=='allof' ? true : false;
$this->form['name'] = $name;
$this->form['tests'] = array();
$this->form['actions'] = array();
if ($name == '')
$this->errors['name'] = $this->plugin->gettext('cannotbeempty');
else {
foreach($this->script as $idx => $rule)
if($rule['name'] == $name && $idx != $fid) {
$this->errors['name'] = $this->plugin->gettext('ruleexist');
break;
}
}
$i = 0;
// rules
if ($join == 'any') {
$this->form['tests'][0]['test'] = 'true';
}
else {
foreach ($headers as $idx => $header) {
// targets are indexed differently (assume form order)
$target = $this->strip_value(array_shift($targets), true);
$header = $this->strip_value($header);
$operator = $this->strip_value($ops[$idx]);
$comparator = $this->strip_value($comparators[$idx]);
if ($header == 'size') {
$sizeop = $this->strip_value($sizeops[$idx]);
$sizeitem = $this->strip_value($items[$idx]);
$sizetarget = $this->strip_value($sizetargets[$idx]);
$this->form['tests'][$i]['test'] = 'size';
$this->form['tests'][$i]['type'] = $sizeop;
$this->form['tests'][$i]['arg'] = $sizetarget;
if ($sizetarget == '')
$this->errors['tests'][$i]['sizetarget'] = $this->plugin->gettext('cannotbeempty');
else if (!preg_match('/^[0-9]+(K|M|G)?$/i', $sizetarget.$sizeitem, $m)) {
$this->errors['tests'][$i]['sizetarget'] = $this->plugin->gettext('forbiddenchars');
$this->form['tests'][$i]['item'] = $sizeitem;
}
else
$this->form['tests'][$i]['arg'] .= $m[1];
}
else if ($header == 'currentdate') {
$datepart = $this->strip_value($dateparts[$idx]);
if (preg_match('/^not/', $operator))
$this->form['tests'][$i]['not'] = true;
$type = preg_replace('/^not/', '', $operator);
if ($type == 'exists') {
$this->errors['tests'][$i]['op'] = true;
}
$this->form['tests'][$i]['test'] = 'currentdate';
$this->form['tests'][$i]['type'] = $type;
$this->form['tests'][$i]['part'] = $datepart;
$this->form['tests'][$i]['arg'] = $target;
if ($type != 'exists') {
if (!count($target)) {
$this->errors['tests'][$i]['target'] = $this->plugin->gettext('cannotbeempty');
}
else if (strpos($type, 'count-') === 0) {
foreach ($target as $arg) {
if (preg_match('/[^0-9]/', $arg)) {
$this->errors['tests'][$i]['target'] = $this->plugin->gettext('forbiddenchars');
}
}
}
else if (strpos($type, 'value-') === 0) {
// Some date/time formats do not support i;ascii-numeric comparator
if ($comparator == 'i;ascii-numeric' && in_array($datepart, array('date', 'time', 'iso8601', 'std11'))) {
$comparator = '';
}
}
if (!preg_match('/^(regex|matches|count-)/', $type) && count($target)) {
foreach ($target as $arg) {
if (!$this->validate_date_part($datepart, $arg)) {
$this->errors['tests'][$i]['target'] = $this->plugin->gettext('invaliddateformat');
break;
}
}
}
}
}
else if ($header == 'date') {
$datepart = $this->strip_value($dateparts[$idx]);
$dateheader = $this->strip_value($dateheaders[$idx]);
$index = $this->strip_value($indexes[$idx]);
$indexlast = $this->strip_value($lastindexes[$idx]);
if (preg_match('/^not/', $operator))
$this->form['tests'][$i]['not'] = true;
$type = preg_replace('/^not/', '', $operator);
if ($type == 'exists') {
$this->errors['tests'][$i]['op'] = true;
}
if (!empty($index) && $mod != 'envelope') {
$this->form['tests'][$i]['index'] = intval($index);
$this->form['tests'][$i]['last'] = !empty($indexlast);
}
if (empty($dateheader)) {
$dateheader = 'Date';
}
else if (!preg_match('/^[\x21-\x39\x41-\x7E]+$/i', $dateheader)) {
$this->errors['tests'][$i]['dateheader'] = $this->plugin->gettext('forbiddenchars');
}
$this->form['tests'][$i]['test'] = 'date';
$this->form['tests'][$i]['type'] = $type;
$this->form['tests'][$i]['part'] = $datepart;
$this->form['tests'][$i]['arg'] = $target;
$this->form['tests'][$i]['header'] = $dateheader;
if ($type != 'exists') {
if (!count($target)) {
$this->errors['tests'][$i]['target'] = $this->plugin->gettext('cannotbeempty');
}
else if (strpos($type, 'count-') === 0) {
foreach ($target as $arg) {
if (preg_match('/[^0-9]/', $arg)) {
$this->errors['tests'][$i]['target'] = $this->plugin->gettext('forbiddenchars');
}
}
}
else if (strpos($type, 'value-') === 0) {
// Some date/time formats do not support i;ascii-numeric comparator
if ($comparator == 'i;ascii-numeric' && in_array($datepart, array('date', 'time', 'iso8601', 'std11'))) {
$comparator = '';
}
}
if (count($target) && !preg_match('/^(regex|matches|count-)/', $type)) {
foreach ($target as $arg) {
if (!$this->validate_date_part($datepart, $arg)) {
$this->errors['tests'][$i]['target'] = $this->plugin->gettext('invaliddateformat');
break;
}
}
}
}
}
else if ($header == 'body') {
$trans = $this->strip_value($body_trans[$idx]);
$trans_type = $this->strip_value($body_types[$idx], true);
if (preg_match('/^not/', $operator))
$this->form['tests'][$i]['not'] = true;
$type = preg_replace('/^not/', '', $operator);
if ($type == 'exists') {
$this->errors['tests'][$i]['op'] = true;
}
$this->form['tests'][$i]['test'] = 'body';
$this->form['tests'][$i]['type'] = $type;
$this->form['tests'][$i]['arg'] = $target;
if (empty($target) && $type != 'exists') {
$this->errors['tests'][$i]['target'] = $this->plugin->gettext('cannotbeempty');
}
else if (preg_match('/^(value|count)-/', $type)) {
foreach ($target as $target_value) {
if (preg_match('/[^0-9]/', $target_value)) {
$this->errors['tests'][$i]['target'] = $this->plugin->gettext('forbiddenchars');
}
}
}
$this->form['tests'][$i]['part'] = $trans;
if ($trans == 'content') {
$this->form['tests'][$i]['content'] = $trans_type;
}
}
else {
$cust_header = $headers = $this->strip_value(array_shift($cust_headers));
$mod = $this->strip_value($mods[$idx]);
$mod_type = $this->strip_value($mod_types[$idx]);
$index = $this->strip_value($indexes[$idx]);
$indexlast = $this->strip_value($lastindexes[$idx]);
if (preg_match('/^not/', $operator))
$this->form['tests'][$i]['not'] = true;
$type = preg_replace('/^not/', '', $operator);
if (!empty($index) && $mod != 'envelope') {
$this->form['tests'][$i]['index'] = intval($index);
$this->form['tests'][$i]['last'] = !empty($indexlast);
}
if ($header == '...') {
if (!count($headers))
$this->errors['tests'][$i]['header'] = $this->plugin->gettext('cannotbeempty');
else {
foreach ($headers as $hr) {
// RFC2822: printable ASCII except colon
if (!preg_match('/^[\x21-\x39\x41-\x7E]+$/i', $hr)) {
$this->errors['tests'][$i]['header'] = $this->plugin->gettext('forbiddenchars');
}
}
}
if (empty($this->errors['tests'][$i]['header']))
$cust_header = (is_array($headers) && count($headers) == 1) ? $headers[0] : $headers;
}
$header = $header == '...' ? $cust_header : $header;
if (is_array($header)) {
foreach ($header as $h_index => $val) {
if (isset($this->headers[$val])) {
$header[$h_index] = $this->headers[$val];
}
}
}
if ($type == 'exists') {
$this->form['tests'][$i]['test'] = 'exists';
$this->form['tests'][$i]['arg'] = $header;
}
else {
$test = 'header';
if ($mod == 'address' || $mod == 'envelope') {
$found = false;
if (empty($this->errors['tests'][$i]['header'])) {
foreach ((array)$header as $hdr) {
if (!in_array(strtolower(trim($hdr)), $this->addr_headers))
$found = true;
}
}
if (!$found)
$test = $mod;
}
$this->form['tests'][$i]['type'] = $type;
$this->form['tests'][$i]['test'] = $test;
$this->form['tests'][$i]['arg1'] = $header;
$this->form['tests'][$i]['arg2'] = $target;
if (empty($target)) {
$this->errors['tests'][$i]['target'] = $this->plugin->gettext('cannotbeempty');
}
else if (preg_match('/^(value|count)-/', $type)) {
foreach ($target as $target_value) {
if (preg_match('/[^0-9]/', $target_value)) {
$this->errors['tests'][$i]['target'] = $this->plugin->gettext('forbiddenchars');
}
}
}
if ($mod) {
$this->form['tests'][$i]['part'] = $mod_type;
}
}
}
if ($header != 'size' && $comparator) {
$this->form['tests'][$i]['comparator'] = $comparator;
}
$i++;
}
}
$i = 0;
// actions
foreach ($act_types as $idx => $type) {
$type = $this->strip_value($type);
switch ($type) {
case 'fileinto':
case 'fileinto_copy':
$mailbox = $this->strip_value($mailboxes[$idx], false, false);
$this->form['actions'][$i]['target'] = $this->mod_mailbox($mailbox, 'in');
if ($type == 'fileinto_copy') {
$type = 'fileinto';
$this->form['actions'][$i]['copy'] = true;
}
break;
case 'reject':
case 'ereject':
$target = $this->strip_value($area_targets[$idx]);
$this->form['actions'][$i]['target'] = str_replace("\r\n", "\n", $target);
// if ($target == '')
// $this->errors['actions'][$i]['targetarea'] = $this->plugin->gettext('cannotbeempty');
break;
case 'redirect':
case 'redirect_copy':
$target = $this->strip_value($act_targets[$idx]);
$domain = $this->strip_value($domain_targets[$idx]);
// force one of the configured domains
$domains = (array) $this->rc->config->get('managesieve_domains');
if (!empty($domains) && !empty($target)) {
if (!$domain || !in_array($domain, $domains)) {
$domain = $domains[0];
}
$target .= '@' . $domain;
}
$this->form['actions'][$i]['target'] = $target;
if ($target == '')
$this->errors['actions'][$i]['target'] = $this->plugin->gettext('cannotbeempty');
else if (!rcube_utils::check_email($target))
$this->errors['actions'][$i]['target'] = $this->plugin->gettext(!empty($domains) ? 'forbiddenchars' : 'noemailwarning');
if ($type == 'redirect_copy') {
$type = 'redirect';
$this->form['actions'][$i]['copy'] = true;
}
break;
case 'addflag':
case 'setflag':
case 'removeflag':
$_target = array();
if (empty($flags[$idx])) {
$this->errors['actions'][$i]['target'] = $this->plugin->gettext('noflagset');
}
else {
foreach ($flags[$idx] as $flag) {
$_target[] = $this->strip_value($flag);
}
}
$this->form['actions'][$i]['target'] = $_target;
break;
case 'vacation':
$reason = $this->strip_value($reasons[$idx]);
$interval_type = $interval_types[$idx] == 'seconds' ? 'seconds' : 'days';
$this->form['actions'][$i]['reason'] = str_replace("\r\n", "\n", $reason);
$this->form['actions'][$i]['subject'] = $subject[$idx];
$this->form['actions'][$i]['addresses'] = array_shift($addresses);
$this->form['actions'][$i][$interval_type] = $intervals[$idx];
// @TODO: vacation :mime, :from, :handle
foreach ((array)$this->form['actions'][$i]['addresses'] as $aidx => $address) {
$this->form['actions'][$i]['addresses'][$aidx] = $address = trim($address);
if (empty($address)) {
unset($this->form['actions'][$i]['addresses'][$aidx]);
}
else if (!rcube_utils::check_email($address)) {
$this->errors['actions'][$i]['addresses'] = $this->plugin->gettext('noemailwarning');
break;
}
}
if ($this->form['actions'][$i]['reason'] == '')
$this->errors['actions'][$i]['reason'] = $this->plugin->gettext('cannotbeempty');
if ($this->form['actions'][$i][$interval_type] && !preg_match('/^[0-9]+$/', $this->form['actions'][$i][$interval_type]))
$this->errors['actions'][$i]['interval'] = $this->plugin->gettext('forbiddenchars');
break;
case 'set':
$this->form['actions'][$i]['name'] = $varnames[$idx];
$this->form['actions'][$i]['value'] = $varvalues[$idx];
foreach ((array)$varmods[$idx] as $v_m) {
$this->form['actions'][$i][$v_m] = true;
}
if (empty($varnames[$idx])) {
$this->errors['actions'][$i]['name'] = $this->plugin->gettext('cannotbeempty');
}
else if (!preg_match('/^[0-9a-z_]+$/i', $varnames[$idx])) {
$this->errors['actions'][$i]['name'] = $this->plugin->gettext('forbiddenchars');
}
if (!isset($varvalues[$idx]) || $varvalues[$idx] === '') {
$this->errors['actions'][$i]['value'] = $this->plugin->gettext('cannotbeempty');
}
break;
case 'notify':
if (empty($notifymethods[$idx])) {
$this->errors['actions'][$i]['method'] = $this->plugin->gettext('cannotbeempty');
}
if (empty($notifytargets[$idx])) {
$this->errors['actions'][$i]['target'] = $this->plugin->gettext('cannotbeempty');
}
if (!empty($notifyfrom[$idx]) && !rcube_utils::check_email($notifyfrom[$idx])) {
$this->errors['actions'][$i]['from'] = $this->plugin->gettext('noemailwarning');
}
// skip empty options
foreach ((array)$notifyoptions[$idx] as $opt_idx => $opt) {
if (!strlen(trim($opt))) {
unset($notifyoptions[$idx][$opt_idx]);
}
}
$this->form['actions'][$i]['method'] = $notifymethods[$idx] . ':' . $notifytargets[$idx];
$this->form['actions'][$i]['options'] = $notifyoptions[$idx];
$this->form['actions'][$i]['message'] = $notifymessages[$idx];
$this->form['actions'][$i]['from'] = $notifyfrom[$idx];
$this->form['actions'][$i]['importance'] = $notifyimp[$idx];
break;
}
$this->form['actions'][$i]['type'] = $type;
$i++;
}
if (!$this->errors && !$error) {
// save the script
if (!isset($this->script[$fid])) {
$fid = $this->sieve->script->add_rule($this->form);
$new = true;
}
else {
$fid = $this->sieve->script->update_rule($fid, $this->form);
}
if ($fid !== false)
$save = $this->save_script();
if ($save && $fid !== false) {
$this->rc->output->show_message('managesieve.filtersaved', 'confirmation');
if ($this->rc->task != 'mail') {
$this->rc->output->command('parent.managesieve_updatelist',
isset($new) ? 'add' : 'update',
array(
'name' => $this->form['name'],
'id' => $fid,
'disabled' => $this->form['disabled']
));
}
else {
$this->rc->output->command('managesieve_dialog_close');
$this->rc->output->send('iframe');
}
}
else {
$this->rc->output->show_message('managesieve.filtersaveerror', 'error');
// $this->rc->output->send();
}
}
}
$this->send();
}
protected function send()
{
// Handle form action
if (isset($_GET['_framed']) || isset($_POST['_framed'])) {
if (isset($_GET['_newset']) || isset($_POST['_newset'])) {
$this->rc->output->send('managesieve.setedit');
}
else {
$this->rc->output->send('managesieve.filteredit');
}
}
else {
$this->rc->output->set_pagetitle($this->plugin->gettext('filters'));
$this->rc->output->send('managesieve.managesieve');
}
}
// return the filters list as HTML table
function filters_list($attrib)
{
// add id to message list table if not specified
if (!strlen($attrib['id']))
$attrib['id'] = 'rcmfilterslist';
// define list of cols to be displayed
$a_show_cols = array('name');
$result = $this->list_rules();
// create XHTML table
$out = $this->rc->table_output($attrib, $result, $a_show_cols, 'id');
// set client env
$this->rc->output->add_gui_object('filterslist', $attrib['id']);
$this->rc->output->include_script('list.js');
// add some labels to client
$this->rc->output->add_label('managesieve.filterdeleteconfirm');
return $out;
}
// return the filters list as <SELECT>
function filtersets_list($attrib, $no_env = false)
{
// add id to message list table if not specified
if (!strlen($attrib['id']))
$attrib['id'] = 'rcmfiltersetslist';
$list = $this->list_scripts();
if ($list) {
asort($list, SORT_LOCALE_STRING);
}
if (!empty($attrib['type']) && $attrib['type'] == 'list') {
// define list of cols to be displayed
$a_show_cols = array('name');
if ($list) {
foreach ($list as $idx => $set) {
$scripts['S'.$idx] = $set;
$result[] = array(
'name' => $set,
'id' => 'S'.$idx,
'class' => !in_array($set, $this->active) ? 'disabled' : '',
);
}
}
// create XHTML table
$out = $this->rc->table_output($attrib, $result, $a_show_cols, 'id');
$this->rc->output->set_env('filtersets', $scripts);
$this->rc->output->include_script('list.js');
}
else {
$select = new html_select(array('name' => '_set', 'id' => $attrib['id'],
'onchange' => $this->rc->task != 'mail' ? 'rcmail.managesieve_set()' : ''));
if ($list) {
foreach ($list as $set)
$select->add($set, $set);
}
$out = $select->show($this->sieve->current);
}
// set client env
if (!$no_env) {
$this->rc->output->add_gui_object('filtersetslist', $attrib['id']);
$this->rc->output->add_label('managesieve.setdeleteconfirm');
}
return $out;
}
function filter_frame($attrib)
{
- if (!$attrib['id'])
- $attrib['id'] = 'rcmfilterframe';
-
- $attrib['name'] = $attrib['id'];
-
- $this->rc->output->set_env('contentframe', $attrib['name']);
- $this->rc->output->set_env('blankpage', $attrib['src'] ?
- $this->rc->output->abs_url($attrib['src']) : 'program/resources/blank.gif');
-
- return $this->rc->output->frame($attrib);
+ return $this->rc->output->frame($attrib, true);
}
function filterset_form($attrib)
{
if (!$attrib['id'])
$attrib['id'] = 'rcmfiltersetform';
$out = '<form name="filtersetform" action="./" method="post" enctype="multipart/form-data">'."\n";
$hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $this->rc->task));
$hiddenfields->add(array('name' => '_action', 'value' => 'plugin.managesieve-save'));
$hiddenfields->add(array('name' => '_framed', 'value' => ($_POST['_framed'] || $_GET['_framed'] ? 1 : 0)));
$hiddenfields->add(array('name' => '_newset', 'value' => 1));
$out .= $hiddenfields->show();
$name = rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST);
$copy = rcube_utils::get_input_value('_copy', rcube_utils::INPUT_POST);
$selected = rcube_utils::get_input_value('_from', rcube_utils::INPUT_POST);
// filter set name input
$input_name = new html_inputfield(array('name' => '_name', 'id' => '_name', 'size' => 30,
'class' => ($this->errors['name'] ? 'error' : '')));
$out .= sprintf('<label for="%s"><b>%s:</b></label> %s<br /><br />',
'_name', rcube::Q($this->plugin->gettext('filtersetname')), $input_name->show($name));
$out .="\n<fieldset class=\"itemlist\"><legend>" . $this->plugin->gettext('filters') . ":</legend>\n";
$out .= '<input type="radio" id="from_none" name="_from" value="none"'
.(!$selected || $selected=='none' ? ' checked="checked"' : '').'></input>';
$out .= sprintf('<label for="%s">%s</label> ', 'from_none', rcube::Q($this->plugin->gettext('none')));
// filters set list
$list = $this->list_scripts();
$select = new html_select(array('name' => '_copy', 'id' => '_copy'));
if (is_array($list)) {
asort($list, SORT_LOCALE_STRING);
if (!$copy)
$copy = $_SESSION['managesieve_current'];
foreach ($list as $set) {
$select->add($set, $set);
}
$out .= '<br /><input type="radio" id="from_set" name="_from" value="set"'
.($selected=='set' ? ' checked="checked"' : '').'></input>';
$out .= sprintf('<label for="%s">%s:</label> ', 'from_set', rcube::Q($this->plugin->gettext('fromset')));
$out .= $select->show($copy);
}
// script upload box
$upload = new html_inputfield(array('name' => '_file', 'id' => '_file', 'size' => 30,
'type' => 'file', 'class' => ($this->errors['file'] ? 'error' : '')));
$out .= '<br /><input type="radio" id="from_file" name="_from" value="file"'
.($selected=='file' ? ' checked="checked"' : '').'></input>';
$out .= sprintf('<label for="%s">%s:</label> ', 'from_file', rcube::Q($this->plugin->gettext('fromfile')));
$out .= $upload->show();
$out .= '</fieldset>';
$this->rc->output->add_gui_object('sieveform', 'filtersetform');
if ($this->errors['name'])
$this->add_tip('_name', $this->errors['name'], true);
if ($this->errors['file'])
$this->add_tip('_file', $this->errors['file'], true);
$this->print_tips();
return $out;
}
function filter_form($attrib)
{
if (!$attrib['id'])
$attrib['id'] = 'rcmfilterform';
$fid = rcube_utils::get_input_value('_fid', rcube_utils::INPUT_GPC);
$scr = isset($this->form) ? $this->form : $this->script[$fid];
$hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $this->rc->task));
$hiddenfields->add(array('name' => '_action', 'value' => 'plugin.managesieve-save'));
$hiddenfields->add(array('name' => '_framed', 'value' => ($_POST['_framed'] || $_GET['_framed'] ? 1 : 0)));
$hiddenfields->add(array('name' => '_fid', 'value' => $fid));
$out = '<form name="filterform" action="./" method="post">'."\n";
$out .= $hiddenfields->show();
// 'any' flag
- if (sizeof($scr['tests']) == 1 && $scr['tests'][0]['test'] == 'true' && !$scr['tests'][0]['not'])
+ if ((!isset($this->form) && empty($scr['tests']) && !empty($scr))
+ || (sizeof($scr['tests']) == 1 && $scr['tests'][0]['test'] == 'true' && !$scr['tests'][0]['not'])
+ ) {
$any = true;
+ }
// filter name input
$field_id = '_name';
$input_name = new html_inputfield(array('name' => '_name', 'id' => $field_id, 'size' => 30,
'class' => ($this->errors['name'] ? 'error' : '')));
if ($this->errors['name'])
$this->add_tip($field_id, $this->errors['name'], true);
if (isset($scr))
$input_name = $input_name->show($scr['name']);
else
$input_name = $input_name->show();
$out .= sprintf("\n<label for=\"%s\"><b>%s:</b></label> %s\n",
$field_id, rcube::Q($this->plugin->gettext('filtername')), $input_name);
// filter set selector
if ($this->rc->task == 'mail') {
$out .= sprintf("\n&nbsp;<label for=\"%s\"><b>%s:</b></label> %s\n",
$field_id, rcube::Q($this->plugin->gettext('filterset')),
$this->filtersets_list(array('id' => 'sievescriptname'), true));
}
$out .= '<br /><br /><fieldset><legend>' . rcube::Q($this->plugin->gettext('messagesrules')) . "</legend>\n";
// any, allof, anyof radio buttons
$field_id = '_allof';
$input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'allof',
'onclick' => 'rule_join_radio(\'allof\')', 'class' => 'radio'));
if (isset($scr) && !$any)
$input_join = $input_join->show($scr['join'] ? 'allof' : '');
else
$input_join = $input_join->show();
$out .= sprintf("%s<label for=\"%s\">%s</label>&nbsp;\n",
$input_join, $field_id, rcube::Q($this->plugin->gettext('filterallof')));
$field_id = '_anyof';
$input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'anyof',
'onclick' => 'rule_join_radio(\'anyof\')', 'class' => 'radio'));
if (isset($scr) && !$any)
$input_join = $input_join->show($scr['join'] ? '' : 'anyof');
else
$input_join = $input_join->show('anyof'); // default
$out .= sprintf("%s<label for=\"%s\">%s</label>\n",
$input_join, $field_id, rcube::Q($this->plugin->gettext('filteranyof')));
$field_id = '_any';
$input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'any',
'onclick' => 'rule_join_radio(\'any\')', 'class' => 'radio'));
$input_join = $input_join->show($any ? 'any' : '');
$out .= sprintf("%s<label for=\"%s\">%s</label>\n",
$input_join, $field_id, rcube::Q($this->plugin->gettext('filterany')));
- $rows_num = isset($scr) ? sizeof($scr['tests']) : 1;
+ $rows_num = !empty($scr['tests']) ? sizeof($scr['tests']) : 1;
$out .= '<div id="rules"'.($any ? ' style="display: none"' : '').'>';
for ($x=0; $x<$rows_num; $x++)
$out .= $this->rule_div($fid, $x);
$out .= "</div>\n";
$out .= "</fieldset>\n";
// actions
$out .= '<fieldset><legend>' . rcube::Q($this->plugin->gettext('messagesactions')) . "</legend>\n";
$rows_num = isset($scr) ? sizeof($scr['actions']) : 1;
$out .= '<div id="actions">';
for ($x=0; $x<$rows_num; $x++)
$out .= $this->action_div($fid, $x);
$out .= "</div>\n";
$out .= "</fieldset>\n";
$this->print_tips();
if ($scr['disabled']) {
$this->rc->output->set_env('rule_disabled', true);
}
$this->rc->output->add_label(
'managesieve.ruledeleteconfirm',
'managesieve.actiondeleteconfirm'
);
$this->rc->output->add_gui_object('sieveform', 'filterform');
return $out;
}
function rule_div($fid, $id, $div=true)
{
$rule = isset($this->form) ? $this->form['tests'][$id] : $this->script[$fid]['tests'][$id];
$rows_num = isset($this->form) ? sizeof($this->form['tests']) : sizeof($this->script[$fid]['tests']);
// headers select
$select_header = new html_select(array('name' => "_header[]", 'id' => 'header'.$id,
'onchange' => 'rule_header_select(' .$id .')'));
foreach ($this->headers as $index => $header) {
$header = $this->rc->text_exists($index) ? $this->plugin->gettext($index) : $header;
$select_header->add($header, $index);
}
$select_header->add($this->plugin->gettext('...'), '...');
if (in_array('body', $this->exts))
$select_header->add($this->plugin->gettext('body'), 'body');
$select_header->add($this->plugin->gettext('size'), 'size');
if (in_array('date', $this->exts)) {
$select_header->add($this->plugin->gettext('datetest'), 'date');
$select_header->add($this->plugin->gettext('currdate'), 'currentdate');
}
if (isset($rule['test'])) {
if (in_array($rule['test'], array('header', 'address', 'envelope'))
&& !is_array($rule['arg1'])
&& ($header = strtolower($rule['arg1']))
&& isset($this->headers[$header])
) {
$test = $header;
}
else if ($rule['test'] == 'exists'
&& !is_array($rule['arg'])
&& ($header = strtolower($rule['arg']))
&& isset($this->headers[$header])
) {
$test = $header;
}
else if (in_array($rule['test'], array('size', 'body', 'date', 'currentdate'))) {
$test = $rule['test'];
}
else if ($rule['test'] != 'true') {
$test = '...';
}
}
$aout = $select_header->show($test);
// custom headers input
if (isset($rule['test']) && in_array($rule['test'], array('header', 'address', 'envelope'))) {
$custom = (array) $rule['arg1'];
if (count($custom) == 1 && isset($this->headers[strtolower($custom[0])])) {
unset($custom);
}
}
else if (isset($rule['test']) && $rule['test'] == 'exists') {
$custom = (array) $rule['arg'];
if (count($custom) == 1 && isset($this->headers[strtolower($custom[0])])) {
unset($custom);
}
}
$tout = $this->list_input($id, 'custom_header', $custom, isset($custom),
$this->error_class($id, 'test', 'header', 'custom_header'), 15) . "\n";
// matching type select (operator)
$select_op = new html_select(array('name' => "_rule_op[]", 'id' => 'rule_op'.$id,
'style' => 'display:' .($rule['test']!='size' ? 'inline' : 'none'),
'class' => 'operator_selector',
'onchange' => 'rule_op_select(this, '.$id.')'));
$select_op->add(rcube::Q($this->plugin->gettext('filtercontains')), 'contains');
$select_op->add(rcube::Q($this->plugin->gettext('filternotcontains')), 'notcontains');
$select_op->add(rcube::Q($this->plugin->gettext('filteris')), 'is');
$select_op->add(rcube::Q($this->plugin->gettext('filterisnot')), 'notis');
$select_op->add(rcube::Q($this->plugin->gettext('filterexists')), 'exists');
$select_op->add(rcube::Q($this->plugin->gettext('filternotexists')), 'notexists');
$select_op->add(rcube::Q($this->plugin->gettext('filtermatches')), 'matches');
$select_op->add(rcube::Q($this->plugin->gettext('filternotmatches')), 'notmatches');
if (in_array('regex', $this->exts)) {
$select_op->add(rcube::Q($this->plugin->gettext('filterregex')), 'regex');
$select_op->add(rcube::Q($this->plugin->gettext('filternotregex')), 'notregex');
}
if (in_array('relational', $this->exts)) {
$select_op->add(rcube::Q($this->plugin->gettext('countisgreaterthan')), 'count-gt');
$select_op->add(rcube::Q($this->plugin->gettext('countisgreaterthanequal')), 'count-ge');
$select_op->add(rcube::Q($this->plugin->gettext('countislessthan')), 'count-lt');
$select_op->add(rcube::Q($this->plugin->gettext('countislessthanequal')), 'count-le');
$select_op->add(rcube::Q($this->plugin->gettext('countequals')), 'count-eq');
$select_op->add(rcube::Q($this->plugin->gettext('countnotequals')), 'count-ne');
$select_op->add(rcube::Q($this->plugin->gettext('valueisgreaterthan')), 'value-gt');
$select_op->add(rcube::Q($this->plugin->gettext('valueisgreaterthanequal')), 'value-ge');
$select_op->add(rcube::Q($this->plugin->gettext('valueislessthan')), 'value-lt');
$select_op->add(rcube::Q($this->plugin->gettext('valueislessthanequal')), 'value-le');
$select_op->add(rcube::Q($this->plugin->gettext('valueequals')), 'value-eq');
$select_op->add(rcube::Q($this->plugin->gettext('valuenotequals')), 'value-ne');
}
+ $test = self::rule_test($rule);
+ $target = '';
+
// target(s) input
if (in_array($rule['test'], array('header', 'address', 'envelope'))) {
- $test = ($rule['not'] ? 'not' : '').($rule['type'] ? $rule['type'] : 'is');
$target = $rule['arg2'];
}
else if (in_array($rule['test'], array('body', 'date', 'currentdate'))) {
- $test = ($rule['not'] ? 'not' : '').($rule['type'] ? $rule['type'] : 'is');
$target = $rule['arg'];
}
else if ($rule['test'] == 'size') {
- $test = '';
- $target = '';
if (preg_match('/^([0-9]+)(K|M|G)?$/', $rule['arg'], $matches)) {
$sizetarget = $matches[1];
- $sizeitem = $matches[2];
+ $sizeitem = $matches[2];
}
else {
$sizetarget = $rule['arg'];
- $sizeitem = $rule['item'];
+ $sizeitem = $rule['item'];
}
}
- else {
- $test = ($rule['not'] ? 'not' : '').$rule['test'];
- $target = '';
- }
// (current)date part select
if (in_array('date', $this->exts) || in_array('currentdate', $this->exts)) {
$date_parts = array('date', 'iso8601', 'std11', 'julian', 'time',
'year', 'month', 'day', 'hour', 'minute', 'second', 'weekday', 'zone');
$select_dp = new html_select(array('name' => "_rule_date_part[]", 'id' => 'rule_date_part'.$id,
'style' => in_array($rule['test'], array('currentdate', 'date')) && !preg_match('/^(notcount|count)-/', $test) ? '' : 'display:none',
'class' => 'datepart_selector',
));
foreach ($date_parts as $part) {
$select_dp->add(rcube::Q($this->plugin->gettext($part)), $part);
}
$tout .= $select_dp->show($rule['test'] == 'currentdate' || $rule['test'] == 'date' ? $rule['part'] : '');
}
$tout .= $select_op->show($test);
$tout .= $this->list_input($id, 'rule_target', $target,
$rule['test'] != 'size' && $rule['test'] != 'exists',
$this->error_class($id, 'test', 'target', 'rule_target')) . "\n";
$select_size_op = new html_select(array('name' => "_rule_size_op[]", 'id' => 'rule_size_op'.$id));
$select_size_op->add(rcube::Q($this->plugin->gettext('filterover')), 'over');
$select_size_op->add(rcube::Q($this->plugin->gettext('filterunder')), 'under');
$tout .= '<div id="rule_size' .$id. '" style="display:' . ($rule['test']=='size' ? 'inline' : 'none') .'">';
$tout .= $select_size_op->show($rule['test']=='size' ? $rule['type'] : '');
$tout .= '<input type="text" name="_rule_size_target[]" id="rule_size_i'.$id.'" value="'.$sizetarget.'" size="10" '
. $this->error_class($id, 'test', 'sizetarget', 'rule_size_i') .' />
<label><input type="radio" name="_rule_size_item['.$id.']" value=""'
. (!$sizeitem ? ' checked="checked"' : '') .' class="radio" />'.$this->rc->gettext('B').'</label>
<label><input type="radio" name="_rule_size_item['.$id.']" value="K"'
. ($sizeitem=='K' ? ' checked="checked"' : '') .' class="radio" />'.$this->rc->gettext('KB').'</label>
<label><input type="radio" name="_rule_size_item['.$id.']" value="M"'
. ($sizeitem=='M' ? ' checked="checked"' : '') .' class="radio" />'.$this->rc->gettext('MB').'</label>
<label><input type="radio" name="_rule_size_item['.$id.']" value="G"'
. ($sizeitem=='G' ? ' checked="checked"' : '') .' class="radio" />'.$this->rc->gettext('GB').'</label>';
$tout .= '</div>';
// Advanced modifiers (address, envelope)
$select_mod = new html_select(array('name' => "_rule_mod[]", 'id' => 'rule_mod_op'.$id,
'onchange' => 'rule_mod_select(' .$id .')'));
$select_mod->add(rcube::Q($this->plugin->gettext('none')), '');
$select_mod->add(rcube::Q($this->plugin->gettext('address')), 'address');
if (in_array('envelope', $this->exts))
$select_mod->add(rcube::Q($this->plugin->gettext('envelope')), 'envelope');
$select_type = new html_select(array('name' => "_rule_mod_type[]", 'id' => 'rule_mod_type'.$id));
$select_type->add(rcube::Q($this->plugin->gettext('allparts')), 'all');
$select_type->add(rcube::Q($this->plugin->gettext('domain')), 'domain');
$select_type->add(rcube::Q($this->plugin->gettext('localpart')), 'localpart');
if (in_array('subaddress', $this->exts)) {
$select_type->add(rcube::Q($this->plugin->gettext('user')), 'user');
$select_type->add(rcube::Q($this->plugin->gettext('detail')), 'detail');
}
$need_mod = !in_array($rule['test'], array('size', 'body', 'date', 'currentdate'));
$mout = '<div id="rule_mod' .$id. '" class="adv"' . (!$need_mod ? ' style="display:none"' : '') . '>';
$mout .= ' <span class="label">' . rcube::Q($this->plugin->gettext('modifier')) . ' </span>';
$mout .= $select_mod->show($rule['test']);
$mout .= ' <span id="rule_mod_type' . $id . '"';
$mout .= ' style="display:' . (in_array($rule['test'], array('address', 'envelope')) ? 'inline' : 'none') .'">';
$mout .= rcube::Q($this->plugin->gettext('modtype')) . ' ';
$mout .= $select_type->show($rule['part']);
$mout .= '</span>';
$mout .= '</div>';
// Advanced modifiers (body transformations)
$select_mod = new html_select(array('name' => "_rule_trans[]", 'id' => 'rule_trans_op'.$id,
'onchange' => 'rule_trans_select(' .$id .')'));
$select_mod->add(rcube::Q($this->plugin->gettext('text')), 'text');
$select_mod->add(rcube::Q($this->plugin->gettext('undecoded')), 'raw');
$select_mod->add(rcube::Q($this->plugin->gettext('contenttype')), 'content');
$mout .= '<div id="rule_trans' .$id. '" class="adv"' . ($rule['test'] != 'body' ? ' style="display:none"' : '') . '>';
$mout .= '<span class="label">' . rcube::Q($this->plugin->gettext('modifier')) . '</span>';
$mout .= $select_mod->show($rule['part']);
$mout .= '<input type="text" name="_rule_trans_type[]" id="rule_trans_type'.$id
. '" value="'.(is_array($rule['content']) ? implode(',', $rule['content']) : $rule['content'])
.'" size="20"' . ($rule['part'] != 'content' ? ' style="display:none"' : '')
. $this->error_class($id, 'test', 'part', 'rule_trans_type') .' />';
$mout .= '</div>';
// Advanced modifiers (body transformations)
$select_comp = new html_select(array('name' => "_rule_comp[]", 'id' => 'rule_comp_op'.$id));
$select_comp->add(rcube::Q($this->plugin->gettext('default')), '');
$select_comp->add(rcube::Q($this->plugin->gettext('octet')), 'i;octet');
$select_comp->add(rcube::Q($this->plugin->gettext('asciicasemap')), 'i;ascii-casemap');
if (in_array('comparator-i;ascii-numeric', $this->exts)) {
$select_comp->add(rcube::Q($this->plugin->gettext('asciinumeric')), 'i;ascii-numeric');
}
// Comparators
$mout .= '<div id="rule_comp' .$id. '" class="adv"' . ($rule['test'] == 'size' ? ' style="display:none"' : '') . '>';
$mout .= '<span class="label">' . rcube::Q($this->plugin->gettext('comparator')) . '</span>';
$mout .= $select_comp->show($rule['comparator']);
$mout .= '</div>';
// Date header
if (in_array('date', $this->exts)) {
$mout .= '<div id="rule_date_header_div' .$id. '" class="adv"'. ($rule['test'] != 'date' ? ' style="display:none"' : '') .'>';
$mout .= '<span class="label">' . rcube::Q($this->plugin->gettext('dateheader')) . '</span>';
$mout .= '<input type="text" name="_rule_date_header[]" id="rule_date_header'.$id
. '" value="'. Q($rule['test'] == 'date' ? $rule['header'] : '')
. '" size="15"' . $this->error_class($id, 'test', 'dateheader', 'rule_date_header') .' />';
$mout .= '</div>';
}
// Index
if (in_array('index', $this->exts)) {
$need_index = in_array($rule['test'], array('header', ', address', 'date'));
$mout .= '<div id="rule_index_div' .$id. '" class="adv"'. (!$need_index ? ' style="display:none"' : '') .'>';
$mout .= '<span class="label">' . rcube::Q($this->plugin->gettext('index')) . '</span>';
$mout .= '<input type="text" name="_rule_index[]" id="rule_index'.$id
. '" value="'. ($rule['index'] ? intval($rule['index']) : '')
. '" size="3"' . $this->error_class($id, 'test', 'index', 'rule_index') .' />';
$mout .= '&nbsp;<input type="checkbox" name="_rule_index_last[]" id="rule_index_last'.$id
. '" value="1"' . (!empty($rule['last']) ? ' checked="checked"' : '') . ' />'
. '<label for="rule_index_last'.$id.'">'.rcube::Q($this->plugin->gettext('indexlast')).'</label>';
$mout .= '</div>';
}
// Build output table
$out = $div ? '<div class="rulerow" id="rulerow' .$id .'">'."\n" : '';
$out .= '<table><tr>';
$out .= '<td class="advbutton">';
$out .= '<a href="#" id="ruleadv' . $id .'" title="'. rcube::Q($this->plugin->gettext('advancedopts')). '"
onclick="rule_adv_switch(' . $id .', this)" class="show">&nbsp;&nbsp;</a>';
$out .= '</td>';
$out .= '<td class="rowactions">' . $aout . '</td>';
$out .= '<td class="rowtargets">' . $tout . "\n";
$out .= '<div id="rule_advanced' .$id. '" style="display:none">' . $mout . '</div>';
$out .= '</td>';
// add/del buttons
$out .= '<td class="rowbuttons">';
$out .= '<a href="#" id="ruleadd' . $id .'" title="'. rcube::Q($this->plugin->gettext('add')). '"
onclick="rcmail.managesieve_ruleadd(' . $id .')" class="button add"></a>';
$out .= '<a href="#" id="ruledel' . $id .'" title="'. rcube::Q($this->plugin->gettext('del')). '"
onclick="rcmail.managesieve_ruledel(' . $id .')" class="button del' . ($rows_num<2 ? ' disabled' : '') .'"></a>';
$out .= '</td>';
$out .= '</tr></table>';
$out .= $div ? "</div>\n" : '';
return $out;
}
+ private static function rule_test(&$rule)
+ {
+ // first modify value/count tests with 'not' keyword
+ // we'll revert the meaning of operators
+ if ($rule['not'] && preg_match('/^(count|value)-([gteqnl]{2})/', $rule['type'], $m)) {
+ $rule['not'] = false;
+
+ switch ($m[2]) {
+ case 'gt': $rule['type'] = $m[1] . '-le'; break;
+ case 'ge': $rule['type'] = $m[1] . '-lt'; break;
+ case 'lt': $rule['type'] = $m[1] . '-ge'; break;
+ case 'le': $rule['type'] = $m[1] . '-gt'; break;
+ case 'eq': $rule['type'] = $m[1] . '-ne'; break;
+ case 'ne': $rule['type'] = $m[1] . '-eq'; break;
+ }
+ }
+ else if ($rule['not'] && $rule['test'] == 'size') {
+ $rule['not'] = false;
+ $rule['type'] = $rule['type'] == 'over' ? 'under' : 'over';
+ }
+
+ $set = array('header', 'address', 'envelope', 'body', 'date', 'currentdate');
+
+ // build test string supported by select element
+ if ($rule['size']) {
+ $test = $rule['type'];
+ }
+ else if (in_array($rule['test'], $set)) {
+ $test = ($rule['not'] ? 'not' : '') . ($rule['type'] ? $rule['type'] : 'is');
+ }
+ else {
+ $test = ($rule['not'] ? 'not' : '') . $rule['test'];
+ }
+
+ return $test;
+ }
+
function action_div($fid, $id, $div=true)
{
$action = isset($this->form) ? $this->form['actions'][$id] : $this->script[$fid]['actions'][$id];
$rows_num = isset($this->form) ? sizeof($this->form['actions']) : sizeof($this->script[$fid]['actions']);
$out = $div ? '<div class="actionrow" id="actionrow' .$id .'">'."\n" : '';
$out .= '<table><tr><td class="rowactions">';
// action select
$select_action = new html_select(array('name' => "_action_type[$id]", 'id' => 'action_type'.$id,
'onchange' => 'action_type_select(' .$id .')'));
if (in_array('fileinto', $this->exts))
$select_action->add(rcube::Q($this->plugin->gettext('messagemoveto')), 'fileinto');
if (in_array('fileinto', $this->exts) && in_array('copy', $this->exts))
$select_action->add(rcube::Q($this->plugin->gettext('messagecopyto')), 'fileinto_copy');
$select_action->add(rcube::Q($this->plugin->gettext('messageredirect')), 'redirect');
if (in_array('copy', $this->exts))
$select_action->add(rcube::Q($this->plugin->gettext('messagesendcopy')), 'redirect_copy');
if (in_array('reject', $this->exts))
$select_action->add(rcube::Q($this->plugin->gettext('messagediscard')), 'reject');
else if (in_array('ereject', $this->exts))
$select_action->add(rcube::Q($this->plugin->gettext('messagediscard')), 'ereject');
if (in_array('vacation', $this->exts))
$select_action->add(rcube::Q($this->plugin->gettext('messagereply')), 'vacation');
$select_action->add(rcube::Q($this->plugin->gettext('messagedelete')), 'discard');
if (in_array('imapflags', $this->exts) || in_array('imap4flags', $this->exts)) {
$select_action->add(rcube::Q($this->plugin->gettext('setflags')), 'setflag');
$select_action->add(rcube::Q($this->plugin->gettext('addflags')), 'addflag');
$select_action->add(rcube::Q($this->plugin->gettext('removeflags')), 'removeflag');
}
if (in_array('variables', $this->exts)) {
$select_action->add(rcube::Q($this->plugin->gettext('setvariable')), 'set');
}
if (in_array('enotify', $this->exts) || in_array('notify', $this->exts)) {
$select_action->add(rcube::Q($this->plugin->gettext('notify')), 'notify');
}
$select_action->add(rcube::Q($this->plugin->gettext('messagekeep')), 'keep');
$select_action->add(rcube::Q($this->plugin->gettext('rulestop')), 'stop');
$select_type = $action['type'];
if (in_array($action['type'], array('fileinto', 'redirect')) && $action['copy']) {
$select_type .= '_copy';
}
$out .= $select_action->show($select_type);
$out .= '</td>';
// actions target inputs
$out .= '<td class="rowtargets">';
// force domain selection in redirect email input
$domains = (array) $this->rc->config->get('managesieve_domains');
if (!empty($domains)) {
sort($domains);
$domain_select = new html_select(array('name' => "_action_target_domain[$id]", 'id' => 'action_target_domain'.$id));
$domain_select->add(array_combine($domains, $domains));
if ($action['type'] == 'redirect') {
$parts = explode('@', $action['target']);
if (!empty($parts)) {
$action['domain'] = array_pop($parts);
$action['target'] = implode('@', $parts);
}
}
}
// redirect target
$out .= '<span id="redirect_target' . $id . '" style="white-space:nowrap;'
. ' display:' . ($action['type'] == 'redirect' ? 'inline' : 'none') . '">'
. '<input type="text" name="_action_target['.$id.']" id="action_target' .$id. '"'
. ' value="' .($action['type'] == 'redirect' ? rcube::Q($action['target'], 'strict', false) : '') . '"'
. (!empty($domains) ? ' size="20"' : ' size="35"')
. $this->error_class($id, 'action', 'target', 'action_target') .' />'
. (!empty($domains) ? ' @ ' . $domain_select->show($action['domain']) : '')
. '</span>';
// (e)reject target
$out .= '<textarea name="_action_target_area['.$id.']" id="action_target_area' .$id. '" '
.'rows="3" cols="35" '. $this->error_class($id, 'action', 'targetarea', 'action_target_area')
.'style="display:' .(in_array($action['type'], array('reject', 'ereject')) ? 'inline' : 'none') .'">'
. (in_array($action['type'], array('reject', 'ereject')) ? rcube::Q($action['target'], 'strict', false) : '')
. "</textarea>\n";
// vacation
$vsec = in_array('vacation-seconds', $this->exts);
$out .= '<div id="action_vacation' .$id.'" style="display:' .($action['type']=='vacation' ? 'inline' : 'none') .'">';
$out .= '<span class="label">'. rcube::Q($this->plugin->gettext('vacationreason')) .'</span><br />'
.'<textarea name="_action_reason['.$id.']" id="action_reason' .$id. '" '
.'rows="3" cols="35" '. $this->error_class($id, 'action', 'reason', 'action_reason') . '>'
. Q($action['reason'], 'strict', false) . "</textarea>\n";
$out .= '<br /><span class="label">' .rcube::Q($this->plugin->gettext('vacationsubject')) . '</span><br />'
.'<input type="text" name="_action_subject['.$id.']" id="action_subject'.$id.'" '
.'value="' . (is_array($action['subject']) ? rcube::Q(implode(', ', $action['subject']), 'strict', false) : $action['subject']) . '" size="35" '
. $this->error_class($id, 'action', 'subject', 'action_subject') .' />';
$out .= '<br /><span class="label">' .rcube::Q($this->plugin->gettext('vacationaddr')) . '</span><br />'
. $this->list_input($id, 'action_addresses', $action['addresses'], true,
$this->error_class($id, 'action', 'addresses', 'action_addresses'), 30);
$out .= '<br /><span class="label">' . rcube::Q($this->plugin->gettext($vsec ? 'vacationinterval' : 'vacationdays')) . '</span><br />'
.'<input type="text" name="_action_interval['.$id.']" id="action_interval'.$id.'" '
.'value="' .rcube::Q(isset($action['seconds']) ? $action['seconds'] : $action['days'], 'strict', false) . '" size="2" '
. $this->error_class($id, 'action', 'interval', 'action_interval') .' />';
if ($vsec) {
$out .= '&nbsp;<label><input type="radio" name="_action_interval_type['.$id.']" value="days"'
. (!isset($action['seconds']) ? ' checked="checked"' : '') .' class="radio" />'.$this->plugin->gettext('days').'</label>'
. '&nbsp;<label><input type="radio" name="_action_interval_type['.$id.']" value="seconds"'
. (isset($action['seconds']) ? ' checked="checked"' : '') .' class="radio" />'.$this->plugin->gettext('seconds').'</label>';
}
$out .= '</div>';
// flags
$flags = array(
'read' => '\\Seen',
'answered' => '\\Answered',
'flagged' => '\\Flagged',
'deleted' => '\\Deleted',
'draft' => '\\Draft',
);
$flags_target = (array)$action['target'];
$out .= '<div id="action_flags' .$id.'" style="display:'
. (preg_match('/^(set|add|remove)flag$/', $action['type']) ? 'inline' : 'none') . '"'
. $this->error_class($id, 'action', 'flags', 'action_flags') . '>';
foreach ($flags as $fidx => $flag) {
$out .= '<input type="checkbox" name="_action_flags[' .$id .'][]" value="' . $flag . '"'
. (in_array_nocase($flag, $flags_target) ? 'checked="checked"' : '') . ' />'
. rcube::Q($this->plugin->gettext('flag'.$fidx)) .'<br>';
}
$out .= '</div>';
// set variable
$set_modifiers = array(
'lower',
'upper',
'lowerfirst',
'upperfirst',
'quotewildcard',
'length'
);
$out .= '<div id="action_set' .$id.'" style="display:' .($action['type']=='set' ? 'inline' : 'none') .'">';
$out .= '<span class="label">' .rcube::Q($this->plugin->gettext('setvarname')) . '</span><br />'
.'<input type="text" name="_action_varname['.$id.']" id="action_varname'.$id.'" '
.'value="' . rcube::Q($action['name']) . '" size="35" '
. $this->error_class($id, 'action', 'name', 'action_varname') .' />';
$out .= '<br /><span class="label">' .rcube::Q($this->plugin->gettext('setvarvalue')) . '</span><br />'
.'<input type="text" name="_action_varvalue['.$id.']" id="action_varvalue'.$id.'" '
.'value="' . rcube::Q($action['value']) . '" size="35" '
. $this->error_class($id, 'action', 'value', 'action_varvalue') .' />';
$out .= '<br /><span class="label">' .rcube::Q($this->plugin->gettext('setvarmodifiers')) . '</span><br />';
foreach ($set_modifiers as $s_m) {
$s_m_id = 'action_varmods' . $id . $s_m;
$out .= sprintf('<input type="checkbox" name="_action_varmods[%s][]" value="%s" id="%s"%s />%s<br>',
$id, $s_m, $s_m_id,
(array_key_exists($s_m, (array)$action) && $action[$s_m] ? ' checked="checked"' : ''),
rcube::Q($this->plugin->gettext('var' . $s_m)));
}
$out .= '</div>';
// notify
$notify_methods = (array) $this->rc->config->get('managesieve_notify_methods');
$importance_options = $this->notify_importance_options;
if (empty($notify_methods)) {
$notify_methods = $this->notify_methods;
}
list($method, $target) = explode(':', $action['method'], 2);
$method = strtolower($method);
if ($method && !in_array($method, $notify_methods)) {
$notify_methods[] = $method;
}
$select_method = new html_select(array(
'name' => "_action_notifymethod[$id]",
'id' => "_action_notifymethod$id",
'class' => $this->error_class($id, 'action', 'method', 'action_notifymethod'),
));
foreach ($notify_methods as $m_n) {
$select_method->add(rcube::Q($this->rc->text_exists('managesieve.notifymethod'.$m_n) ? $this->plugin->gettext('managesieve.notifymethod'.$m_n) : $m_n), $m_n);
}
$select_importance = new html_select(array(
'name' => "_action_notifyimportance[$id]",
'id' => "_action_notifyimportance$id",
'class' => $this->error_class($id, 'action', 'importance', 'action_notifyimportance')
));
foreach ($importance_options as $io_v => $io_n) {
$select_importance->add(rcube::Q($this->plugin->gettext($io_n)), $io_v);
}
// @TODO: nice UI for mailto: (other methods too) URI parameters
$out .= '<div id="action_notify' .$id.'" style="display:' .($action['type'] == 'notify' ? 'inline' : 'none') .'">';
$out .= '<span class="label">' .rcube::Q($this->plugin->gettext('notifytarget')) . '</span><br />'
. $select_method->show($method)
.'<input type="text" name="_action_notifytarget['.$id.']" id="action_notifytarget'.$id.'" '
.'value="' . rcube::Q($target) . '" size="25" '
. $this->error_class($id, 'action', 'target', 'action_notifytarget') .' />';
$out .= '<br /><span class="label">'. rcube::Q($this->plugin->gettext('notifymessage')) .'</span><br />'
.'<textarea name="_action_notifymessage['.$id.']" id="action_notifymessage' .$id. '" '
.'rows="3" cols="35" '. $this->error_class($id, 'action', 'message', 'action_notifymessage') . '>'
. rcube::Q($action['message'], 'strict', false) . "</textarea>\n";
if (in_array('enotify', $this->exts)) {
$out .= '<br /><span class="label">' .rcube::Q($this->plugin->gettext('notifyfrom')) . '</span><br />'
.'<input type="text" name="_action_notifyfrom['.$id.']" id="action_notifyfrom'.$id.'" '
.'value="' . rcube::Q($action['from']) . '" size="35" '
. $this->error_class($id, 'action', 'from', 'action_notifyfrom') .' />';
}
$out .= '<br /><span class="label">' . rcube::Q($this->plugin->gettext('notifyimportance')) . '</span><br />';
$out .= $select_importance->show($action['importance'] ? (int) $action['importance'] : 2);
$out .= '<div id="action_notifyoption_div' . $id . '">'
.'<span class="label">' . rcube::Q($this->plugin->gettext('notifyoptions')) . '</span><br />'
.$this->list_input($id, 'action_notifyoption', (array)$action['options'], true,
$this->error_class($id, 'action', 'options', 'action_notifyoption'), 30) . '</div>';
$out .= '</div>';
// mailbox select
if ($action['type'] == 'fileinto') {
$mailbox = $this->mod_mailbox($action['target'], 'out');
// make sure non-existing (or unsubscribed) mailbox is listed (#1489956)
$additional = array($mailbox);
}
else {
$mailbox = '';
}
$select = $this->rc->folder_selector(array(
'realnames' => false,
'maxlength' => 100,
'id' => 'action_mailbox' . $id,
'name' => "_action_mailbox[$id]",
'style' => 'display:'.(empty($action['type']) || $action['type'] == 'fileinto' ? 'inline' : 'none'),
'additional' => $additional,
));
$out .= $select->show($mailbox);
$out .= '</td>';
// add/del buttons
$out .= '<td class="rowbuttons">';
$out .= '<a href="#" id="actionadd' . $id .'" title="'. rcube::Q($this->plugin->gettext('add')). '"
onclick="rcmail.managesieve_actionadd(' . $id .')" class="button add"></a>';
$out .= '<a href="#" id="actiondel' . $id .'" title="'. rcube::Q($this->plugin->gettext('del')). '"
onclick="rcmail.managesieve_actiondel(' . $id .')" class="button del' . ($rows_num<2 ? ' disabled' : '') .'"></a>';
$out .= '</td>';
$out .= '</tr></table>';
$out .= $div ? "</div>\n" : '';
return $out;
}
protected function genid()
{
return preg_replace('/[^0-9]/', '', microtime(true));
}
protected function strip_value($str, $allow_html = false, $trim = true)
{
if (is_array($str)) {
foreach ($str as $idx => $val) {
$val = $this->strip_value($val, $allow_html, $trim);
if ($val === '') {
unset($str[$idx]);
}
}
return $str;
}
if (!$allow_html) {
$str = strip_tags($str);
}
return $trim ? trim($str) : $str;
}
protected function error_class($id, $type, $target, $elem_prefix='')
{
// TODO: tooltips
if (($type == 'test' && ($str = $this->errors['tests'][$id][$target])) ||
($type == 'action' && ($str = $this->errors['actions'][$id][$target]))
) {
$this->add_tip($elem_prefix.$id, $str, true);
return ' class="error"';
}
return '';
}
protected function add_tip($id, $str, $error=false)
{
if ($error)
$str = html::span('sieve error', $str);
$this->tips[] = array($id, $str);
}
protected function print_tips()
{
if (empty($this->tips))
return;
$script = rcmail_output::JS_OBJECT_NAME.'.managesieve_tip_register('.json_encode($this->tips).');';
$this->rc->output->add_script($script, 'foot');
}
protected function list_input($id, $name, $value, $enabled, $class, $size=null)
{
$value = (array) $value;
$value = array_map(array('rcube', 'Q'), $value);
$value = implode("\n", $value);
return '<textarea data-type="list" name="_' . $name . '['.$id.']" id="' . $name.$id . '"'
. ($enabled ? '' : ' disabled="disabled"')
. ($size ? ' data-size="'.$size.'"' : '')
. $class
. ' style="display:none">' . $value . '</textarea>';
}
/**
* Validate input for date part elements
*/
protected function validate_date_part($type, $value)
{
// we do simple validation of date/part format
switch ($type) {
case 'date': // yyyy-mm-dd
return preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/', $value);
case 'iso8601':
return preg_match('/^[0-9: .,ZWT+-]+$/', $value);
case 'std11':
return preg_match('/^((Sun|Mon|Tue|Wed|Thu|Fri|Sat),\s+)?[0-9]{1,2}\s+'
. '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+[0-9]{2,4}\s+'
. '[0-9]{2}:[0-9]{2}(:[0-9]{2})?\s+([+-]*[0-9]{4}|[A-Z]{1,3})$', $value);
case 'julian':
return preg_match('/^[0-9]+$/', $value);
case 'time': // hh:mm:ss
return preg_match('/^[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $value);
case 'year':
return preg_match('/^[0-9]{4}$/', $value);
case 'month':
return preg_match('/^[0-9]{2}$/', $value) && $value > 0 && $value < 13;
case 'day':
return preg_match('/^[0-9]{2}$/', $value) && $value > 0 && $value < 32;
case 'hour':
return preg_match('/^[0-9]{2}$/', $value) && $value < 24;
case 'minute':
return preg_match('/^[0-9]{2}$/', $value) && $value < 60;
case 'second':
// According to RFC5260, seconds can be from 00 to 60
return preg_match('/^[0-9]{2}$/', $value) && $value < 61;
case 'weekday':
return preg_match('/^[0-9]$/', $value) && $value < 7;
case 'zone':
return preg_match('/^[+-][0-9]{4}$/', $value);
}
}
/**
* Converts mailbox name from/to UTF7-IMAP from/to internal Sieve encoding
* with delimiter replacement.
*
* @param string $mailbox Mailbox name
* @param string $mode Conversion direction ('in'|'out')
*
* @return string Mailbox name
*/
protected function mod_mailbox($mailbox, $mode = 'out')
{
$delimiter = $_SESSION['imap_delimiter'];
$replace_delimiter = $this->rc->config->get('managesieve_replace_delimiter');
$mbox_encoding = $this->rc->config->get('managesieve_mbox_encoding', 'UTF7-IMAP');
if ($mode == 'out') {
$mailbox = rcube_charset::convert($mailbox, $mbox_encoding, 'UTF7-IMAP');
if ($replace_delimiter && $replace_delimiter != $delimiter)
$mailbox = str_replace($replace_delimiter, $delimiter, $mailbox);
}
else {
$mailbox = rcube_charset::convert($mailbox, 'UTF7-IMAP', $mbox_encoding);
if ($replace_delimiter && $replace_delimiter != $delimiter)
$mailbox = str_replace($delimiter, $replace_delimiter, $mailbox);
}
return $mailbox;
}
/**
* List sieve scripts
*
* @return array Scripts list
*/
public function list_scripts()
{
if ($this->list !== null) {
return $this->list;
}
$this->list = $this->sieve->get_scripts();
// Handle active script(s) and list of scripts according to Kolab's KEP:14
if ($this->rc->config->get('managesieve_kolab_master')) {
-
// Skip protected names
foreach ((array)$this->list as $idx => $name) {
$_name = strtoupper($name);
if ($_name == 'MASTER')
$master_script = $name;
else if ($_name == 'MANAGEMENT')
$management_script = $name;
else if($_name == 'USER')
$user_script = $name;
else
continue;
unset($this->list[$idx]);
}
// get active script(s), read USER script
if ($user_script) {
$extension = $this->rc->config->get('managesieve_filename_extension', '.sieve');
$filename_regex = '/'.preg_quote($extension, '/').'$/';
$_SESSION['managesieve_user_script'] = $user_script;
$this->sieve->load($user_script);
foreach ($this->sieve->script->as_array() as $rules) {
foreach ($rules['actions'] as $action) {
if ($action['type'] == 'include' && empty($action['global'])) {
$name = preg_replace($filename_regex, '', $action['target']);
- $this->active[] = $name;
+ // make sure the script exist
+ if (in_array($name, $this->list)) {
+ $this->active[] = $name;
+ }
}
}
}
}
// create USER script if it doesn't exist
else {
$content = "# USER Management Script\n"
."#\n"
."# This script includes the various active sieve scripts\n"
."# it is AUTOMATICALLY GENERATED. DO NOT EDIT MANUALLY!\n"
."#\n"
."# For more information, see http://wiki.kolab.org/KEP:14#USER\n"
."#\n";
if ($this->sieve->save_script('USER', $content)) {
$_SESSION['managesieve_user_script'] = 'USER';
if (empty($this->master_file))
$this->sieve->activate('USER');
}
}
}
else if (!empty($this->list)) {
// Get active script name
if ($active = $this->sieve->get_active()) {
$this->active = array($active);
}
// Hide scripts from config
$exceptions = $this->rc->config->get('managesieve_filename_exceptions');
if (!empty($exceptions)) {
$this->list = array_diff($this->list, (array)$exceptions);
}
}
+ // reindex
+ if (!empty($this->list)) {
+ $this->list = array_values($this->list);
+ }
+
return $this->list;
}
/**
* Removes sieve script
*
* @param string $name Script name
*
* @return bool True on success, False on failure
*/
public function remove_script($name)
{
$result = $this->sieve->remove($name);
// Kolab's KEP:14
if ($result && $this->rc->config->get('managesieve_kolab_master')) {
$this->deactivate_script($name);
}
return $result;
}
/**
* Activates sieve script
*
* @param string $name Script name
*
* @return bool True on success, False on failure
*/
public function activate_script($name)
{
// Kolab's KEP:14
if ($this->rc->config->get('managesieve_kolab_master')) {
$extension = $this->rc->config->get('managesieve_filename_extension', '.sieve');
$user_script = $_SESSION['managesieve_user_script'];
// if the script is not active...
if ($user_script && array_search($name, $this->active) === false) {
// ...rewrite USER file adding appropriate include command
if ($this->sieve->load($user_script)) {
$script = $this->sieve->script->as_array();
$list = array();
$regexp = '/' . preg_quote($extension, '/') . '$/';
// Create new include entry
$rule = array(
'actions' => array(
0 => array(
'target' => $name.$extension,
'type' => 'include',
'personal' => true,
)));
// get all active scripts for sorting
foreach ($script as $rid => $rules) {
foreach ($rules['actions'] as $action) {
if ($action['type'] == 'include' && empty($action['global'])) {
$target = $extension ? preg_replace($regexp, '', $action['target']) : $action['target'];
$list[] = $target;
}
}
}
$list[] = $name;
// Sort and find current script position
asort($list, SORT_LOCALE_STRING);
$list = array_values($list);
$index = array_search($name, $list);
// add rule at the end of the script
if ($index === false || $index == count($list)-1) {
$this->sieve->script->add_rule($rule);
}
// add rule at index position
else {
$script2 = array();
foreach ($script as $rid => $rules) {
if ($rid == $index) {
$script2[] = $rule;
}
$script2[] = $rules;
}
$this->sieve->script->content = $script2;
}
$result = $this->sieve->save();
if ($result) {
$this->active[] = $name;
}
}
}
}
else {
$result = $this->sieve->activate($name);
if ($result)
$this->active = array($name);
}
return $result;
}
/**
* Deactivates sieve script
*
* @param string $name Script name
*
* @return bool True on success, False on failure
*/
public function deactivate_script($name)
{
// Kolab's KEP:14
if ($this->rc->config->get('managesieve_kolab_master')) {
$extension = $this->rc->config->get('managesieve_filename_extension', '.sieve');
$user_script = $_SESSION['managesieve_user_script'];
// if the script is active...
if ($user_script && ($key = array_search($name, $this->active)) !== false) {
// ...rewrite USER file removing appropriate include command
if ($this->sieve->load($user_script)) {
$script = $this->sieve->script->as_array();
$name = $name.$extension;
foreach ($script as $rid => $rules) {
foreach ($rules['actions'] as $action) {
if ($action['type'] == 'include' && empty($action['global'])
&& $action['target'] == $name
) {
break 2;
}
}
}
// Entry found
if ($rid < count($script)) {
$this->sieve->script->delete_rule($rid);
$result = $this->sieve->save();
if ($result) {
unset($this->active[$key]);
}
}
}
}
}
else {
$result = $this->sieve->deactivate();
if ($result)
$this->active = array();
}
return $result;
}
/**
* Saves current script (adding some variables)
*/
public function save_script($name = null)
{
// Kolab's KEP:14
if ($this->rc->config->get('managesieve_kolab_master')) {
$this->sieve->script->set_var('EDITOR', self::PROGNAME);
$this->sieve->script->set_var('EDITOR_VERSION', self::VERSION);
}
return $this->sieve->save($name);
}
/**
* Returns list of rules from the current script
*
* @return array List of rules
*/
public function list_rules()
{
$result = array();
$i = 1;
foreach ($this->script as $idx => $filter) {
- if ($filter['type'] != 'if') {
+ if (empty($filter['actions'])) {
continue;
}
$fname = $filter['name'] ? $filter['name'] : "#$i";
$result[] = array(
'id' => $idx,
'name' => $fname,
'class' => $filter['disabled'] ? 'disabled' : '',
);
$i++;
}
return $result;
}
/**
* Initializes internal script data
*/
protected function init_script()
{
$this->script = $this->sieve->script->as_array();
if (!$this->script) {
return;
}
$headers = array();
$exceptions = array('date', 'currentdate', 'size', 'body');
// find common headers used in script, will be added to the list
// of available (predefined) headers (#1489271)
foreach ($this->script as $rule) {
foreach ((array) $rule['tests'] as $test) {
if ($test['test'] == 'header') {
foreach ((array) $test['arg1'] as $header) {
$lc_header = strtolower($header);
// skip special names to not confuse UI
if (in_array($lc_header, $exceptions)) {
continue;
}
if (!isset($this->headers[$lc_header]) && !isset($headers[$lc_header])) {
$headers[$lc_header] = $header;
}
}
}
}
}
ksort($headers);
$this->headers += $headers;
}
}
diff --git a/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php b/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php
index bc62d2f..518d79d 100644
--- a/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php
+++ b/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php
@@ -1,1209 +1,1217 @@
<?php
/**
* Class for operations on Sieve scripts
*
* Copyright (C) 2008-2011, The Roundcube Dev Team
* Copyright (C) 2011, Kolab Systems AG
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
class rcube_sieve_script
{
public $content = array(); // script rules array
private $vars = array(); // "global" variables
private $prefix = ''; // script header (comments)
private $supported = array( // Sieve extensions supported by class
'body', // RFC5173
'copy', // RFC3894
'date', // RFC5260
'enotify', // RFC5435
'envelope', // RFC5228
'ereject', // RFC5429
'fileinto', // RFC5228
'imapflags', // draft-melnikov-sieve-imapflags-06
'imap4flags', // RFC5232
'include', // draft-ietf-sieve-include-12
'index', // RFC5260
'notify', // draft-martin-sieve-notify-01,
'regex', // draft-ietf-sieve-regex-01
'reject', // RFC5429
'relational', // RFC3431
'subaddress', // RFC5233
'vacation', // RFC5230
'vacation-seconds', // RFC6131
'variables', // RFC5229
// @TODO: spamtest+virustest, mailbox
);
/**
* Object constructor
*
* @param string Script's text content
* @param array List of capabilities supported by server
*/
public function __construct($script, $capabilities=array())
{
$capabilities = array_map('strtolower', (array) $capabilities);
// disable features by server capabilities
if (!empty($capabilities)) {
foreach ($this->supported as $idx => $ext) {
if (!in_array($ext, $capabilities)) {
unset($this->supported[$idx]);
}
}
}
// Parse text content of the script
$this->_parse_text($script);
}
/**
* Adds rule to the script (at the end)
*
* @param string Rule name
* @param array Rule content (as array)
*
* @return int The index of the new rule
*/
public function add_rule($content)
{
// TODO: check this->supported
array_push($this->content, $content);
return sizeof($this->content)-1;
}
public function delete_rule($index)
{
if(isset($this->content[$index])) {
unset($this->content[$index]);
return true;
}
return false;
}
public function size()
{
return sizeof($this->content);
}
public function update_rule($index, $content)
{
// TODO: check this->supported
if ($this->content[$index]) {
$this->content[$index] = $content;
return $index;
}
return false;
}
/**
* Sets "global" variable
*
* @param string $name Variable name
* @param string $value Variable value
* @param array $mods Variable modifiers
*/
public function set_var($name, $value, $mods = array())
{
// Check if variable exists
for ($i=0, $len=count($this->vars); $i<$len; $i++) {
if ($this->vars[$i]['name'] == $name) {
break;
}
}
$var = array_merge($mods, array('name' => $name, 'value' => $value));
$this->vars[$i] = $var;
}
/**
* Unsets "global" variable
*
* @param string $name Variable name
*/
public function unset_var($name)
{
// Check if variable exists
foreach ($this->vars as $idx => $var) {
if ($var['name'] == $name) {
unset($this->vars[$idx]);
break;
}
}
}
/**
* Gets the value of "global" variable
*
* @param string $name Variable name
*
* @return string Variable value
*/
public function get_var($name)
{
// Check if variable exists
for ($i=0, $len=count($this->vars); $i<$len; $i++) {
if ($this->vars[$i]['name'] == $name) {
return $this->vars[$i]['name'];
}
}
}
/**
* Sets script header content
*
* @param string $text Header content
*/
public function set_prefix($text)
{
$this->prefix = $text;
}
/**
* Returns script as text
*/
public function as_text()
{
$output = '';
$exts = array();
$idx = 0;
if (!empty($this->vars)) {
if (in_array('variables', (array)$this->supported)) {
$has_vars = true;
array_push($exts, 'variables');
}
foreach ($this->vars as $var) {
if (empty($has_vars)) {
// 'variables' extension not supported, put vars in comments
$output .= sprintf("# %s %s\n", $var['name'], $var['value']);
}
else {
$output .= 'set ';
foreach (array_diff(array_keys($var), array('name', 'value')) as $opt) {
$output .= ":$opt ";
}
$output .= self::escape_string($var['name']) . ' ' . self::escape_string($var['value']) . ";\n";
}
}
}
$imapflags = in_array('imap4flags', $this->supported) ? 'imap4flags' : 'imapflags';
$notify = in_array('enotify', $this->supported) ? 'enotify' : 'notify';
// rules
foreach ($this->content as $rule) {
$script = '';
$tests = array();
$i = 0;
// header
if (!empty($rule['name']) && strlen($rule['name'])) {
$script .= '# rule:[' . $rule['name'] . "]\n";
}
// constraints expressions
if (!empty($rule['tests'])) {
foreach ($rule['tests'] as $test) {
$tests[$i] = '';
switch ($test['test']) {
case 'size':
$tests[$i] .= ($test['not'] ? 'not ' : '');
$tests[$i] .= 'size :' . ($test['type']=='under' ? 'under ' : 'over ') . $test['arg'];
break;
case 'true':
$tests[$i] .= ($test['not'] ? 'false' : 'true');
break;
case 'exists':
$tests[$i] .= ($test['not'] ? 'not ' : '');
$tests[$i] .= 'exists ' . self::escape_string($test['arg']);
break;
case 'header':
$tests[$i] .= ($test['not'] ? 'not ' : '');
$tests[$i] .= 'header';
$this->add_index($test, $tests[$i], $exts);
$this->add_operator($test, $tests[$i], $exts);
$tests[$i] .= ' ' . self::escape_string($test['arg1']);
$tests[$i] .= ' ' . self::escape_string($test['arg2']);
break;
case 'address':
case 'envelope':
if ($test['test'] == 'envelope') {
array_push($exts, 'envelope');
}
$tests[$i] .= ($test['not'] ? 'not ' : '');
$tests[$i] .= $test['test'];
if ($test['test'] != 'envelope') {
$this->add_index($test, $tests[$i], $exts);
}
// :all address-part is optional, skip it
if (!empty($test['part']) && $test['part'] != 'all') {
$tests[$i] .= ' :' . $test['part'];
if ($test['part'] == 'user' || $test['part'] == 'detail') {
array_push($exts, 'subaddress');
}
}
$this->add_operator($test, $tests[$i], $exts);
$tests[$i] .= ' ' . self::escape_string($test['arg1']);
$tests[$i] .= ' ' . self::escape_string($test['arg2']);
break;
case 'body':
array_push($exts, 'body');
$tests[$i] .= ($test['not'] ? 'not ' : '') . 'body';
if (!empty($test['part'])) {
$tests[$i] .= ' :' . $test['part'];
if (!empty($test['content']) && $test['part'] == 'content') {
$tests[$i] .= ' ' . self::escape_string($test['content']);
}
}
$this->add_operator($test, $tests[$i], $exts);
$tests[$i] .= ' ' . self::escape_string($test['arg']);
break;
case 'date':
case 'currentdate':
array_push($exts, 'date');
$tests[$i] .= ($test['not'] ? 'not ' : '') . $test['test'];
$this->add_index($test, $tests[$i], $exts);
if (!empty($test['originalzone']) && $test['test'] == 'date') {
$tests[$i] .= ' :originalzone';
}
else if (!empty($test['zone'])) {
$tests[$i] .= ' :zone ' . self::escape_string($test['zone']);
}
$this->add_operator($test, $tests[$i], $exts);
if ($test['test'] == 'date') {
$tests[$i] .= ' ' . self::escape_string($test['header']);
}
$tests[$i] .= ' ' . self::escape_string($test['part']);
$tests[$i] .= ' ' . self::escape_string($test['arg']);
break;
}
$i++;
}
}
// disabled rule: if false #....
if (!empty($tests)) {
$script .= 'if ' . ($rule['disabled'] ? 'false # ' : '');
if (count($tests) > 1) {
$tests_str = implode(', ', $tests);
}
else {
$tests_str = $tests[0];
}
if ($rule['join'] || count($tests) > 1) {
$script .= sprintf('%s (%s)', $rule['join'] ? 'allof' : 'anyof', $tests_str);
}
else {
$script .= $tests_str;
}
$script .= "\n{\n";
}
// action(s)
if (!empty($rule['actions'])) {
foreach ($rule['actions'] as $action) {
$action_script = '';
switch ($action['type']) {
case 'fileinto':
array_push($exts, 'fileinto');
$action_script .= 'fileinto ';
if ($action['copy']) {
$action_script .= ':copy ';
array_push($exts, 'copy');
}
$action_script .= self::escape_string($action['target']);
break;
case 'redirect':
$action_script .= 'redirect ';
if ($action['copy']) {
$action_script .= ':copy ';
array_push($exts, 'copy');
}
$action_script .= self::escape_string($action['target']);
break;
case 'reject':
case 'ereject':
array_push($exts, $action['type']);
$action_script .= $action['type'].' '
. self::escape_string($action['target']);
break;
case 'addflag':
case 'setflag':
case 'removeflag':
array_push($exts, $imapflags);
$action_script .= $action['type'].' '
. self::escape_string($action['target']);
break;
case 'keep':
case 'discard':
case 'stop':
$action_script .= $action['type'];
break;
case 'include':
array_push($exts, 'include');
$action_script .= 'include ';
foreach (array_diff(array_keys($action), array('target', 'type')) as $opt) {
$action_script .= ":$opt ";
}
$action_script .= self::escape_string($action['target']);
break;
case 'set':
array_push($exts, 'variables');
$action_script .= 'set ';
foreach (array_diff(array_keys($action), array('name', 'value', 'type')) as $opt) {
$action_script .= ":$opt ";
}
$action_script .= self::escape_string($action['name']) . ' ' . self::escape_string($action['value']);
break;
case 'notify':
array_push($exts, $notify);
$action_script .= 'notify';
$method = $action['method'];
unset($action['method']);
$action['options'] = (array) $action['options'];
// Here we support draft-martin-sieve-notify-01 used by Cyrus
if ($notify == 'notify') {
switch ($action['importance']) {
case 1: $action_script .= " :high"; break;
//case 2: $action_script .= " :normal"; break;
case 3: $action_script .= " :low"; break;
}
// Old-draft way: :method "mailto" :options "email@address"
if (!empty($method)) {
$parts = explode(':', $method, 2);
$action['method'] = $parts[0];
array_unshift($action['options'], $parts[1]);
}
unset($action['importance']);
unset($action['from']);
unset($method);
}
foreach (array('id', 'importance', 'method', 'options', 'from', 'message') as $n_tag) {
if (!empty($action[$n_tag])) {
$action_script .= " :$n_tag " . self::escape_string($action[$n_tag]);
}
}
if (!empty($method)) {
$action_script .= ' ' . self::escape_string($method);
}
break;
case 'vacation':
array_push($exts, 'vacation');
$action_script .= 'vacation';
if (isset($action['seconds'])) {
array_push($exts, 'vacation-seconds');
$action_script .= " :seconds " . intval($action['seconds']);
}
else if (!empty($action['days'])) {
$action_script .= " :days " . intval($action['days']);
}
if (!empty($action['addresses']))
$action_script .= " :addresses " . self::escape_string($action['addresses']);
if (!empty($action['subject']))
$action_script .= " :subject " . self::escape_string($action['subject']);
if (!empty($action['handle']))
$action_script .= " :handle " . self::escape_string($action['handle']);
if (!empty($action['from']))
$action_script .= " :from " . self::escape_string($action['from']);
if (!empty($action['mime']))
$action_script .= " :mime";
$action_script .= " " . self::escape_string($action['reason']);
break;
}
if ($action_script) {
$script .= !empty($tests) ? "\t" : '';
$script .= $action_script . ";\n";
}
}
}
if ($script) {
$output .= $script . (!empty($tests) ? "}\n" : '');
$idx++;
}
}
// requires
if (!empty($exts)) {
$exts = array_unique($exts);
if (in_array('vacation-seconds', $exts) && ($key = array_search('vacation', $exts)) !== false) {
unset($exts[$key]);
}
sort($exts); // for convenience use always the same order
$output = 'require ["' . implode('","', $exts) . "\"];\n" . $output;
}
if (!empty($this->prefix)) {
$output = $this->prefix . "\n\n" . $output;
}
return $output;
}
/**
* Returns script object
*
*/
public function as_array()
{
return $this->content;
}
/**
* Returns array of supported extensions
*
*/
public function get_extensions()
{
return array_values($this->supported);
}
/**
* Converts text script to rules array
*
* @param string Text script
*/
private function _parse_text($script)
{
$prefix = '';
$options = array();
while ($script) {
$script = trim($script);
$rule = array();
// Comments
while (!empty($script) && $script[0] == '#') {
$endl = strpos($script, "\n");
$line = $endl ? substr($script, 0, $endl) : $script;
// Roundcube format
if (preg_match('/^# rule:\[(.*)\]/', $line, $matches)) {
$rulename = $matches[1];
}
// KEP:14 variables
else if (preg_match('/^# (EDITOR|EDITOR_VERSION) (.+)$/', $line, $matches)) {
$this->set_var($matches[1], $matches[2]);
}
// Horde-Ingo format
else if (!empty($options['format']) && $options['format'] == 'INGO'
&& preg_match('/^# (.*)/', $line, $matches)
) {
$rulename = $matches[1];
}
else if (empty($options['prefix'])) {
$prefix .= $line . "\n";
}
$script = ltrim(substr($script, strlen($line) + 1));
}
// handle script header
if (empty($options['prefix'])) {
$options['prefix'] = true;
if ($prefix && strpos($prefix, 'horde.org/ingo')) {
$options['format'] = 'INGO';
}
}
// Control structures/blocks
if (preg_match('/^(if|else|elsif)/i', $script)) {
$rule = $this->_tokenize_rule($script);
if (strlen($rulename) && !empty($rule)) {
$rule['name'] = $rulename;
}
}
// Simple commands
else {
$rule = $this->_parse_actions($script, ';');
if (!empty($rule[0]) && is_array($rule)) {
// set "global" variables
if ($rule[0]['type'] == 'set') {
unset($rule[0]['type']);
$this->vars[] = $rule[0];
unset($rule);
}
else {
$rule = array('actions' => $rule);
}
}
}
$rulename = '';
if (!empty($rule)) {
$this->content[] = $rule;
}
}
if (!empty($prefix)) {
$this->prefix = trim($prefix);
}
}
/**
* Convert text script fragment to rule object
*
* @param string Text rule
*
* @return array Rule data
*/
private function _tokenize_rule(&$content)
{
$cond = strtolower(self::tokenize($content, 1));
if ($cond != 'if' && $cond != 'elsif' && $cond != 'else') {
return null;
}
$disabled = false;
$join = false;
+ $join_not = false;
// disabled rule (false + comment): if false # .....
if (preg_match('/^\s*false\s+#/i', $content)) {
$content = preg_replace('/^\s*false\s+#\s*/i', '', $content);
$disabled = true;
}
while (strlen($content)) {
$tokens = self::tokenize($content, true);
$separator = array_pop($tokens);
if (!empty($tokens)) {
$token = array_shift($tokens);
}
else {
$token = $separator;
}
$token = strtolower($token);
if ($token == 'not') {
$not = true;
$token = strtolower(array_shift($tokens));
}
else {
$not = false;
}
+ // we support "not allof" as a negation of allof sub-tests
+ if ($join_not) {
+ $not = !$not;
+ }
+
switch ($token) {
case 'allof':
- $join = true;
+ $join = true;
+ $join_not = $not;
break;
+
case 'anyof':
break;
case 'size':
- $test = array('test' => 'size', 'not' => $not);
+ $test = array('test' => 'size', 'not' => $not);
$test['arg'] = array_pop($tokens);
for ($i=0, $len=count($tokens); $i<$len; $i++) {
if (!is_array($tokens[$i])
&& preg_match('/^:(under|over)$/i', $tokens[$i])
) {
$test['type'] = strtolower(substr($tokens[$i], 1));
}
}
$tests[] = $test;
break;
case 'header':
case 'address':
case 'envelope':
$test = array('test' => $token, 'not' => $not);
$test['arg2'] = array_pop($tokens);
$test['arg1'] = array_pop($tokens);
$test += $this->test_tokens($tokens);
if ($token != 'header' && !empty($tokens)) {
for ($i=0, $len=count($tokens); $i<$len; $i++) {
if (!is_array($tokens[$i]) && preg_match('/^:(localpart|domain|all|user|detail)$/i', $tokens[$i])) {
$test['part'] = strtolower(substr($tokens[$i], 1));
}
}
}
$tests[] = $test;
break;
case 'body':
$test = array('test' => 'body', 'not' => $not);
$test['arg'] = array_pop($tokens);
$test += $this->test_tokens($tokens);
for ($i=0, $len=count($tokens); $i<$len; $i++) {
if (!is_array($tokens[$i]) && preg_match('/^:(raw|content|text)$/i', $tokens[$i])) {
$test['part'] = strtolower(substr($tokens[$i], 1));
if ($test['part'] == 'content') {
$test['content'] = $tokens[++$i];
}
}
}
$tests[] = $test;
break;
case 'date':
case 'currentdate':
$test = array('test' => $token, 'not' => $not);
$test['arg'] = array_pop($tokens);
$test['part'] = array_pop($tokens);
if ($token == 'date') {
$test['header'] = array_pop($tokens);
}
$test += $this->test_tokens($tokens);
for ($i=0, $len=count($tokens); $i<$len; $i++) {
if (!is_array($tokens[$i]) && preg_match('/^:zone$/i', $tokens[$i])) {
$test['zone'] = $tokens[++$i];
}
else if (!is_array($tokens[$i]) && preg_match('/^:originalzone$/i', $tokens[$i])) {
$test['originalzone'] = true;
}
}
$tests[] = $test;
break;
case 'exists':
- $tests[] = array('test' => 'exists', 'not' => $not,
+ $tests[] = array('test' => 'exists', 'not' => $not,
'arg' => array_pop($tokens));
break;
case 'true':
- $tests[] = array('test' => 'true', 'not' => $not);
+ $tests[] = array('test' => 'true', 'not' => $not);
break;
case 'false':
- $tests[] = array('test' => 'true', 'not' => !$not);
+ $tests[] = array('test' => 'true', 'not' => !$not);
break;
}
// goto actions...
if ($separator == '{') {
break;
}
}
// ...and actions block
$actions = $this->_parse_actions($content);
if ($tests && $actions) {
$result = array(
'type' => $cond,
'tests' => $tests,
'actions' => $actions,
'join' => $join,
'disabled' => $disabled,
);
}
return $result;
}
/**
* Parse body of actions section
*
* @param string $content Text body
* @param string $end End of text separator
*
* @return array Array of parsed action type/target pairs
*/
private function _parse_actions(&$content, $end = '}')
{
$result = null;
while (strlen($content)) {
$tokens = self::tokenize($content, true);
$separator = array_pop($tokens);
$token = !empty($tokens) ? array_shift($tokens) : $separator;
switch ($token) {
case 'discard':
case 'keep':
case 'stop':
$result[] = array('type' => $token);
break;
case 'fileinto':
case 'redirect':
$action = array('type' => $token, 'target' => array_pop($tokens));
$args = array('copy');
$action += $this->action_arguments($tokens, $args);
$result[] = $action;
break;
case 'vacation':
$action = array('type' => 'vacation', 'reason' => array_pop($tokens));
$args = array('mime');
$vargs = array('seconds', 'days', 'addresses', 'subject', 'handle', 'from');
$action += $this->action_arguments($tokens, $args, $vargs);
$result[] = $action;
break;
case 'reject':
case 'ereject':
case 'setflag':
case 'addflag':
case 'removeflag':
$result[] = array('type' => $token, 'target' => array_pop($tokens));
break;
case 'include':
$action = array('type' => 'include', 'target' => array_pop($tokens));
$args = array('once', 'optional', 'global', 'personal');
$action += $this->action_arguments($tokens, $args);
$result[] = $action;
break;
case 'set':
$action = array('type' => 'set', 'value' => array_pop($tokens), 'name' => array_pop($tokens));
$args = array('lower', 'upper', 'lowerfirst', 'upperfirst', 'quotewildcard', 'length');
$action += $this->action_arguments($tokens, $args);
$result[] = $action;
break;
case 'require':
// skip, will be build according to used commands
// $result[] = array('type' => 'require', 'target' => array_pop($tokens));
break;
case 'notify':
$action = array('type' => 'notify');
$priorities = array('high' => 1, 'normal' => 2, 'low' => 3);
$vargs = array('from', 'id', 'importance', 'options', 'message', 'method');
$args = array_keys($priorities);
$action += $this->action_arguments($tokens, $args, $vargs);
// Here we'll convert draft-martin-sieve-notify-01 into RFC 5435
if (!isset($action['importance'])) {
foreach ($priorities as $key => $val) {
if (isset($action[$key])) {
$action['importance'] = $val;
unset($action[$key]);
}
}
}
$action['options'] = (array) $action['options'];
// Old-draft way: :method "mailto" :options "email@address"
if (!empty($action['method']) && !empty($action['options'])) {
$action['method'] .= ':' . array_shift($action['options']);
}
// unnamed parameter is a :method in enotify extension
else if (!isset($action['method'])) {
$action['method'] = array_pop($tokens);
}
$result[] = $action;
break;
}
if ($separator == $end)
break;
}
return $result;
}
/**
* Add comparator to the test
*/
private function add_comparator($test, &$out, &$exts)
{
if (empty($test['comparator'])) {
return;
}
if ($test['comparator'] == 'i;ascii-numeric') {
array_push($exts, 'relational');
array_push($exts, 'comparator-i;ascii-numeric');
}
else if (!in_array($test['comparator'], array('i;octet', 'i;ascii-casemap'))) {
array_push($exts, 'comparator-' . $test['comparator']);
}
// skip default comparator
if ($test['comparator'] != 'i;ascii-casemap') {
$out .= ' :comparator ' . self::escape_string($test['comparator']);
}
}
/**
* Add index argument to the test
*/
private function add_index($test, &$out, &$exts)
{
if (!empty($test['index'])) {
array_push($exts, 'index');
$out .= ' :index ' . intval($test['index']) . ($test['last'] ? ' :last' : '');
}
}
/**
* Add operators to the test
*/
private function add_operator($test, &$out, &$exts)
{
if (empty($test['type'])) {
return;
}
// relational operator
if (preg_match('/^(value|count)-([gteqnl]{2})/', $test['type'], $m)) {
array_push($exts, 'relational');
$out .= ' :' . $m[1] . ' "' . $m[2] . '"';
}
else {
if ($test['type'] == 'regex') {
array_push($exts, 'regex');
}
$out .= ' :' . $test['type'];
}
$this->add_comparator($test, $out, $exts);
}
/**
* Extract test tokens
*/
private function test_tokens(&$tokens)
{
$test = array();
$result = array();
for ($i=0, $len=count($tokens); $i<$len; $i++) {
if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) {
$test['comparator'] = $tokens[++$i];
}
else if (!is_array($tokens[$i]) && preg_match('/^:(count|value)$/i', $tokens[$i])) {
$test['type'] = strtolower(substr($tokens[$i], 1)) . '-' . $tokens[++$i];
}
else if (!is_array($tokens[$i]) && preg_match('/^:(is|contains|matches|regex)$/i', $tokens[$i])) {
$test['type'] = strtolower(substr($tokens[$i], 1));
}
else if (!is_array($tokens[$i]) && preg_match('/^:index$/i', $tokens[$i])) {
$test['index'] = intval($tokens[++$i]);
if ($tokens[$i+1] && preg_match('/^:last$/i', $tokens[$i+1])) {
$test['last'] = true;
$i++;
}
}
else {
$result[] = $tokens[$i];
}
}
$tokens = $result;
return $test;
}
/**
* Extract action arguments
*/
private function action_arguments(&$tokens, $bool_args, $val_args = array())
{
$action = array();
$result = array();
for ($i=0, $len=count($tokens); $i<$len; $i++) {
$tok = $tokens[$i];
if (!is_array($tok) && $tok[0] == ':') {
$tok = strtolower(substr($tok, 1));
if (in_array($tok, $bool_args)) {
$action[$tok] = true;
}
else if (in_array($tok, $val_args)) {
$action[$tok] = $tokens[++$i];
}
else {
$result[] = $tok;
}
}
else {
$result[] = $tok;
}
}
$tokens = $result;
return $action;
}
/**
* Escape special chars into quoted string value or multi-line string
* or list of strings
*
* @param string $str Text or array (list) of strings
*
* @return string Result text
*/
static function escape_string($str)
{
if (is_array($str) && count($str) > 1) {
foreach($str as $idx => $val)
$str[$idx] = self::escape_string($val);
return '[' . implode(',', $str) . ']';
}
else if (is_array($str)) {
$str = array_pop($str);
}
// multi-line string
if (preg_match('/[\r\n\0]/', $str) || strlen($str) > 1024) {
return sprintf("text:\n%s\n.\n", self::escape_multiline_string($str));
}
// quoted-string
else {
return '"' . addcslashes($str, '\\"') . '"';
}
}
/**
* Escape special chars in multi-line string value
*
* @param string $str Text
*
* @return string Text
*/
static function escape_multiline_string($str)
{
$str = preg_split('/(\r?\n)/', $str, -1, PREG_SPLIT_DELIM_CAPTURE);
foreach ($str as $idx => $line) {
// dot-stuffing
if (isset($line[0]) && $line[0] == '.') {
$str[$idx] = '.' . $line;
}
}
return implode($str);
}
/**
* Splits script into string tokens
*
* @param string &$str The script
* @param mixed $num Number of tokens to return, 0 for all
* or True for all tokens until separator is found.
* Separator will be returned as last token.
*
* @return mixed Tokens array or string if $num=1
*/
static function tokenize(&$str, $num=0)
{
$result = array();
// remove spaces from the beginning of the string
while (($str = ltrim($str)) !== ''
&& (!$num || $num === true || count($result) < $num)
) {
switch ($str[0]) {
// Quoted string
case '"':
$len = strlen($str);
for ($pos=1; $pos<$len; $pos++) {
if ($str[$pos] == '"') {
break;
}
if ($str[$pos] == "\\") {
if ($str[$pos + 1] == '"' || $str[$pos + 1] == "\\") {
$pos++;
}
}
}
if ($str[$pos] != '"') {
// error
}
// we need to strip slashes for a quoted string
$result[] = stripslashes(substr($str, 1, $pos - 1));
$str = substr($str, $pos + 1);
break;
// Parenthesized list
case '[':
$str = substr($str, 1);
$result[] = self::tokenize($str, 0);
break;
case ']':
$str = substr($str, 1);
return $result;
break;
// list/test separator
case ',':
// command separator
case ';':
// block/tests-list
case '(':
case ')':
case '{':
case '}':
$sep = $str[0];
$str = substr($str, 1);
if ($num === true) {
$result[] = $sep;
break 2;
}
break;
// bracket-comment
case '/':
if ($str[1] == '*') {
if ($end_pos = strpos($str, '*/')) {
$str = substr($str, $end_pos + 2);
}
else {
// error
$str = '';
}
}
break;
// hash-comment
case '#':
if ($lf_pos = strpos($str, "\n")) {
$str = substr($str, $lf_pos);
break;
}
else {
$str = '';
}
// String atom
default:
// empty or one character
if ($str === '' || $str === null) {
break 2;
}
if (strlen($str) < 2) {
$result[] = $str;
$str = '';
break;
}
// tag/identifier/number
if (preg_match('/^([a-z0-9:_]+)/i', $str, $m)) {
$str = substr($str, strlen($m[1]));
if ($m[1] != 'text:') {
$result[] = $m[1];
}
// multiline string
else {
// possible hash-comment after "text:"
if (preg_match('/^( |\t)*(#[^\n]+)?\n/', $str, $m)) {
$str = substr($str, strlen($m[0]));
}
// get text until alone dot in a line
if (preg_match('/^(.*)\r?\n\.\r?\n/sU', $str, $m)) {
$text = $m[1];
// remove dot-stuffing
$text = str_replace("\n..", "\n.", $text);
$str = substr($str, strlen($m[0]));
}
else {
$text = '';
}
$result[] = $text;
}
}
// fallback, skip one character as infinite loop prevention
else {
$str = substr($str, 1);
}
break;
}
}
return $num === 1 ? (isset($result[0]) ? $result[0] : null) : $result;
}
}
diff --git a/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php b/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php
index 10aaea0..28fd801 100644
--- a/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php
+++ b/lib/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php
@@ -1,736 +1,850 @@
<?php
/**
* Managesieve Vacation Engine
*
* Engine part of Managesieve plugin implementing UI and backend access.
*
* Copyright (C) 2011-2014, Kolab Systems AG
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
class rcube_sieve_vacation extends rcube_sieve_engine
{
protected $error;
+ protected $script_name;
+ protected $vacation = array();
function actions()
{
$error = $this->start('vacation');
// find current vacation rule
if (!$error) {
$this->vacation_rule();
$this->vacation_post();
}
+
$this->plugin->add_label('vacation.saving');
$this->rc->output->add_handlers(array(
'vacationform' => array($this, 'vacation_form'),
));
$this->rc->output->set_pagetitle($this->plugin->gettext('vacation'));
$this->rc->output->send('managesieve.vacation');
}
- private function vacation_rule()
+ /**
+ * Find and load sieve script with/for vacation rule
+ *
+ * @return int Connection status: 0 on success, >0 on failure
+ */
+ protected function load_script()
{
- $this->vacation = array();
+ if ($this->script_name !== null) {
+ return 0;
+ }
+
+ $list = $this->list_scripts();
+ $master = $this->rc->config->get('managesieve_kolab_master');
+ $included = array();
+
+ $this->script_name = false;
+
+ // first try the active script(s)...
+ if (!empty($this->active)) {
+ // Note: there can be more than one active script on KEP:14-enabled server
+ foreach ($this->active as $script) {
+ if ($this->sieve->load($script)) {
+ foreach ($this->sieve->script->as_array() as $rule) {
+ if (!empty($rule['actions'])) {
+ if ($rule['actions'][0]['type'] == 'vacation') {
+ $this->script_name = $script;
+ return 0;
+ }
+ else if (empty($master) && $rule['actions'][0]['type'] == 'include') {
+ $included[] = $rule['actions'][0]['target'];
+ }
+ }
+ }
+ }
+ }
+
+ // ...else try scripts included in active script (not for KEP:14)
+ foreach ($included as $script) {
+ if ($this->sieve->load($script)) {
+ foreach ($this->sieve->script->as_array() as $rule) {
+ if (!empty($rule['actions']) && $rule['actions'][0]['type'] == 'vacation') {
+ $this->script_name = $script;
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ // try all other scripts
+ if (!empty($list)) {
+ // else try included scripts
+ foreach (array_diff($list, $included, $this->active) as $script) {
+ if ($this->sieve->load($script)) {
+ foreach ($this->sieve->script->as_array() as $rule) {
+ if (!empty($rule['actions']) && $rule['actions'][0]['type'] == 'vacation') {
+ $this->script_name = $script;
+ return 0;
+ }
+ }
+ }
+ }
+
+ // none of the scripts contains existing vacation rule
+ // use any (first) active or just existing script (in that order)
+ if (!empty($this->active)) {
+ $this->sieve->load($this->script_name = $this->active[0]);
+ }
+ else {
+ $this->sieve->load($this->script_name = $list[0]);
+ }
+ }
+
+ return $this->sieve->error();
+ }
- if (empty($this->active)) {
+ private function vacation_rule()
+ {
+ if ($this->script_name === false || $this->script_name === null || !$this->sieve->load($this->script_name)) {
return;
}
- $list = array();
+ $list = array();
+ $active = in_array($this->script_name, $this->active);
// find (first) vacation rule
foreach ($this->script as $idx => $rule) {
if (empty($this->vacation) && !empty($rule['actions']) && $rule['actions'][0]['type'] == 'vacation') {
foreach ($rule['actions'] as $act) {
if ($act['type'] == 'discard' || $act['type'] == 'keep') {
$action = $act['type'];
}
else if ($act['type'] == 'redirect') {
$action = $act['copy'] ? 'copy' : 'redirect';
$target = $act['target'];
}
}
$this->vacation = array_merge($rule['actions'][0], array(
'idx' => $idx,
- 'disabled' => $rule['disabled'],
+ 'disabled' => $rule['disabled'] || !$active,
'name' => $rule['name'],
'tests' => $rule['tests'],
'action' => $action ?: 'keep',
'target' => $target,
));
}
- else {
+ else if ($active) {
$list[$idx] = $rule['name'];
}
}
$this->vacation['list'] = $list;
}
private function vacation_post()
{
if (empty($_POST)) {
return;
}
$date_extension = in_array('date', $this->exts);
$regex_extension = in_array('regex', $this->exts);
// set user's timezone
try {
$timezone = new DateTimeZone($this->rc->config->get('timezone', 'GMT'));
}
catch (Exception $e) {
$timezone = new DateTimeZone('GMT');
}
$status = rcube_utils::get_input_value('vacation_status', rcube_utils::INPUT_POST);
$subject = rcube_utils::get_input_value('vacation_subject', rcube_utils::INPUT_POST, true);
$reason = rcube_utils::get_input_value('vacation_reason', rcube_utils::INPUT_POST, true);
$addresses = rcube_utils::get_input_value('vacation_addresses', rcube_utils::INPUT_POST, true);
$interval = rcube_utils::get_input_value('vacation_interval', rcube_utils::INPUT_POST);
$interval_type = rcube_utils::get_input_value('vacation_interval_type', rcube_utils::INPUT_POST);
$date_from = rcube_utils::get_input_value('vacation_datefrom', rcube_utils::INPUT_POST);
$date_to = rcube_utils::get_input_value('vacation_dateto', rcube_utils::INPUT_POST);
$time_from = rcube_utils::get_input_value('vacation_timefrom', rcube_utils::INPUT_POST);
$time_to = rcube_utils::get_input_value('vacation_timeto', rcube_utils::INPUT_POST);
$after = rcube_utils::get_input_value('vacation_after', rcube_utils::INPUT_POST);
$action = rcube_utils::get_input_value('vacation_action', rcube_utils::INPUT_POST);
$target = rcube_utils::get_input_value('action_target', rcube_utils::INPUT_POST, true);
$target_domain = rcube_utils::get_input_value('action_domain', rcube_utils::INPUT_POST);
$interval_type = $interval_type == 'seconds' ? 'seconds' : 'days';
$vacation_action['type'] = 'vacation';
$vacation_action['reason'] = $this->strip_value(str_replace("\r\n", "\n", $reason));
$vacation_action['subject'] = $subject;
$vacation_action['addresses'] = $addresses;
$vacation_action[$interval_type] = $interval;
$vacation_tests = (array) $this->vacation['tests'];
foreach ((array) $vacation_action['addresses'] as $aidx => $address) {
$vacation_action['addresses'][$aidx] = $address = trim($address);
if (empty($address)) {
unset($vacation_action['addresses'][$aidx]);
}
else if (!rcube_utils::check_email($address)) {
$error = 'noemailwarning';
break;
}
}
if ($vacation_action['reason'] == '') {
$error = 'managesieve.emptyvacationbody';
}
if ($vacation_action[$interval_type] && !preg_match('/^[0-9]+$/', $vacation_action[$interval_type])) {
$error = 'managesieve.forbiddenchars';
}
// find and remove existing date/regex/true rules
foreach ((array) $vacation_tests as $idx => $t) {
if ($t['test'] == 'currentdate' || $t['test'] == 'true'
|| ($t['test'] == 'header' && $t['type'] == 'regex' && $t['arg1'] == 'received')
) {
unset($vacation_tests[$idx]);
}
}
if ($date_extension) {
foreach (array('date_from', 'date_to') as $var) {
$time = ${str_replace('date', 'time', $var)};
$date = trim($$var . ' ' . $time);
if ($date && ($dt = rcube_utils::anytodatetime($date, $timezone))) {
if ($time) {
$vacation_tests[] = array(
'test' => 'currentdate',
'part' => 'iso8601',
'type' => 'value-' . ($var == 'date_from' ? 'ge' : 'le'),
'zone' => $dt->format('O'),
'arg' => str_replace('+00:00', 'Z', strtoupper($dt->format('c'))),
);
}
else {
$vacation_tests[] = array(
'test' => 'currentdate',
'part' => 'date',
'type' => 'value-' . ($var == 'date_from' ? 'ge' : 'le'),
'zone' => $dt->format('O'),
'arg' => $dt->format('Y-m-d'),
);
}
}
}
}
else if ($regex_extension) {
// Add date range rules if range specified
if ($date_from && $date_to) {
if ($tests = self::build_regexp_tests($date_from, $date_to, $error)) {
$vacation_tests = array_merge($vacation_tests, $tests);
}
}
}
if ($action == 'redirect' || $action == 'copy') {
if ($target_domain) {
$target .= '@' . $target_domain;
}
if (empty($target) || !rcube_utils::check_email($target)) {
$error = 'noemailwarning';
}
}
if (empty($vacation_tests)) {
$vacation_tests = $this->rc->config->get('managesieve_vacation_test', array(array('test' => 'true')));
}
- // @TODO: handle situation when there's no active script
-
if (!$error) {
$rule = $this->vacation;
$rule['type'] = 'if';
$rule['name'] = $rule['name'] ?: $this->plugin->gettext('vacation');
$rule['disabled'] = $status == 'off';
$rule['tests'] = $vacation_tests;
$rule['join'] = $date_extension ? count($vacation_tests) > 1 : false;
$rule['actions'] = array($vacation_action);
+ $rule['after'] = $after;
if ($action && $action != 'keep') {
$rule['actions'][] = array(
'type' => $action == 'discard' ? 'discard' : 'redirect',
'copy' => $action == 'copy',
'target' => $action != 'discard' ? $target : '',
);
}
- // reset original vacation rule
- if (isset($this->vacation['idx'])) {
- $this->script[$this->vacation['idx']] = null;
- }
-
- // re-order rules if needed
- if (isset($after) && $after !== '') {
- // add at target position
- if ($after >= count($this->script) - 1) {
- $this->script[] = $rule;
- }
- else {
- $script = array();
-
- foreach ($this->script as $idx => $r) {
- if ($r) {
- $script[] = $r;
- }
-
- if ($idx == $after) {
- $script[] = $rule;
- }
- }
-
- $this->script = $script;
- }
- }
- else {
- array_unshift($this->script, $rule);
- }
-
- $this->sieve->script->content = array_values(array_filter($this->script));
-
- if ($this->save_script()) {
+ if ($this->save_vacation_script($rule)) {
$this->rc->output->show_message('managesieve.vacationsaved', 'confirmation');
$this->rc->output->send();
}
}
$this->rc->output->show_message($error ? $error : 'managesieve.saveerror', 'error');
$this->rc->output->send();
}
/**
* Independent vacation form
*/
public function vacation_form($attrib)
{
// check supported extensions
$date_extension = in_array('date', $this->exts);
$regex_extension = in_array('regex', $this->exts);
$seconds_extension = in_array('vacation-seconds', $this->exts);
// build FORM tag
$form_id = !empty($attrib['id']) ? $attrib['id'] : 'form';
$out = $this->rc->output->request_form(array(
'id' => $form_id,
'name' => $form_id,
'method' => 'post',
'task' => 'settings',
'action' => 'plugin.managesieve-vacation',
'noclose' => true
) + $attrib);
// form elements
$subject = new html_inputfield(array('name' => 'vacation_subject', 'id' => 'vacation_subject', 'size' => 50));
$reason = new html_textarea(array('name' => 'vacation_reason', 'id' => 'vacation_reason', 'cols' => 60, 'rows' => 8));
$interval = new html_inputfield(array('name' => 'vacation_interval', 'id' => 'vacation_interval', 'size' => 5));
$addresses = '<textarea name="vacation_addresses" id="vacation_addresses" data-type="list" data-size="30" style="display: none">'
. rcube::Q(implode("\n", (array) $this->vacation['addresses']), 'strict', false) . '</textarea>';
$status = new html_select(array('name' => 'vacation_status', 'id' => 'vacation_status'));
$action = new html_select(array('name' => 'vacation_action', 'id' => 'vacation_action', 'onchange' => 'vacation_action_select()'));
$status->add($this->plugin->gettext('vacation.on'), 'on');
$status->add($this->plugin->gettext('vacation.off'), 'off');
$action->add($this->plugin->gettext('vacation.keep'), 'keep');
$action->add($this->plugin->gettext('vacation.discard'), 'discard');
$action->add($this->plugin->gettext('vacation.redirect'), 'redirect');
if (in_array('copy', $this->exts)) {
$action->add($this->plugin->gettext('vacation.copy'), 'copy');
}
if ($this->rc->config->get('managesieve_vacation') != 2 && count($this->vacation['list'])) {
$after = new html_select(array('name' => 'vacation_after', 'id' => 'vacation_after'));
$after->add('', '');
foreach ($this->vacation['list'] as $idx => $rule) {
$after->add($rule, $idx);
}
}
$interval_txt = $interval->show(isset($this->vacation['seconds']) ? $this->vacation['seconds'] : $this->vacation['days']);
if ($seconds_extension) {
$interval_select = new html_select(array('name' => 'vacation_interval_type'));
$interval_select->add($this->plugin->gettext('days'), 'days');
$interval_select->add($this->plugin->gettext('seconds'), 'seconds');
$interval_txt .= '&nbsp;' . $interval_select->show(isset($this->vacation['seconds']) ? 'seconds' : 'days');
}
else {
$interval_txt .= '&nbsp;' . $this->plugin->gettext('days');
}
if ($date_extension || $regex_extension) {
$date_from = new html_inputfield(array('name' => 'vacation_datefrom', 'id' => 'vacation_datefrom', 'class' => 'datepicker', 'size' => 12));
$date_to = new html_inputfield(array('name' => 'vacation_dateto', 'id' => 'vacation_dateto', 'class' => 'datepicker', 'size' => 12));
$date_format = $this->rc->config->get('date_format', 'Y-m-d');
}
if ($date_extension) {
$time_from = new html_inputfield(array('name' => 'vacation_timefrom', 'id' => 'vacation_timefrom', 'size' => 6));
$time_to = new html_inputfield(array('name' => 'vacation_timeto', 'id' => 'vacation_timeto', 'size' => 6));
$time_format = $this->rc->config->get('time_format', 'H:i');
$date_value = array();
foreach ((array) $this->vacation['tests'] as $test) {
if ($test['test'] == 'currentdate') {
$idx = $test['type'] == 'value-ge' ? 'from' : 'to';
if ($test['part'] == 'date') {
$date_value[$idx]['date'] = $test['arg'];
}
else if ($test['part'] == 'iso8601') {
$date_value[$idx]['datetime'] = $test['arg'];
}
}
}
foreach ($date_value as $idx => $value) {
$date = $value['datetime'] ?: $value['date'];
$date_value[$idx] = $this->rc->format_date($date, $date_format, false);
if (!empty($value['datetime'])) {
$date_value['time_' . $idx] = $this->rc->format_date($date, $time_format, true);
}
}
}
else if ($regex_extension) {
// Sieve 'date' extension not available, read start/end from RegEx based rules instead
if ($date_tests = self::parse_regexp_tests($this->vacation['tests'])) {
$date_value['from'] = $this->rc->format_date($date_tests['from'], $date_format, false);
$date_value['to'] = $this->rc->format_date($date_tests['to'], $date_format, false);
}
}
// force domain selection in redirect email input
$domains = (array) $this->rc->config->get('managesieve_domains');
$redirect = $this->vacation['action'] == 'redirect' || $this->vacation['action'] == 'copy';
if (!empty($domains)) {
sort($domains);
$domain_select = new html_select(array('name' => 'action_domain', 'id' => 'action_domain'));
$domain_select->add(array_combine($domains, $domains));
if ($redirect && $this->vacation['target']) {
$parts = explode('@', $this->vacation['target']);
if (!empty($parts)) {
$this->vacation['domain'] = array_pop($parts);
$this->vacation['target'] = implode('@', $parts);
}
}
}
// redirect target
$action_target = ' <span id="action_target_span" style="display:' . ($redirect ? 'inline' : 'none') . '">'
. '<input type="text" name="action_target" id="action_target"'
. ' value="' .($redirect ? rcube::Q($this->vacation['target'], 'strict', false) : '') . '"'
. (!empty($domains) ? ' size="20"' : ' size="35"') . '/>'
. (!empty($domains) ? ' @ ' . $domain_select->show($this->vacation['domain']) : '')
. '</span>';
// Message tab
$table = new html_table(array('cols' => 2));
$table->add('title', html::label('vacation_subject', $this->plugin->gettext('vacation.subject')));
$table->add(null, $subject->show($this->vacation['subject']));
$table->add('title', html::label('vacation_reason', $this->plugin->gettext('vacation.body')));
$table->add(null, $reason->show($this->vacation['reason']));
if ($date_extension || $regex_extension) {
$table->add('title', html::label('vacation_datefrom', $this->plugin->gettext('vacation.start')));
$table->add(null, $date_from->show($date_value['from']) . ($time_from ? ' ' . $time_from->show($date_value['time_from']) : ''));
$table->add('title', html::label('vacation_dateto', $this->plugin->gettext('vacation.end')));
$table->add(null, $date_to->show($date_value['to']) . ($time_to ? ' ' . $time_to->show($date_value['time_to']) : ''));
}
$table->add('title', html::label('vacation_status', $this->plugin->gettext('vacation.status')));
$table->add(null, $status->show(!isset($this->vacation['disabled']) || $this->vacation['disabled'] ? 'off' : 'on'));
$out .= html::tag('fieldset', $class, html::tag('legend', null, $this->plugin->gettext('vacation.reply')) . $table->show($attrib));
// Advanced tab
$table = new html_table(array('cols' => 2));
$table->add('title', html::label('vacation_addresses', $this->plugin->gettext('vacation.addresses')));
$table->add(null, $addresses);
$table->add('title', html::label('vacation_interval', $this->plugin->gettext('vacation.interval')));
$table->add(null, $interval_txt);
if ($after) {
$table->add('title', html::label('vacation_after', $this->plugin->gettext('vacation.after')));
$table->add(null, $after->show($this->vacation['idx'] - 1));
}
$table->add('title', html::label('vacation_action', $this->plugin->gettext('vacation.action')));
$table->add('vacation', $action->show($this->vacation['action']) . $action_target);
$out .= html::tag('fieldset', $class, html::tag('legend', null, $this->plugin->gettext('vacation.advanced')) . $table->show($attrib));
$out .= '</form>';
$this->rc->output->add_gui_object('sieveform', $form_id);
if ($time_format) {
$this->rc->output->set_env('time_format', $time_format);
}
return $out;
}
public static function build_regexp_tests($date_from, $date_to, &$error)
{
$tests = array();
$dt_from = rcube_utils::anytodatetime($date_from);
$dt_to = rcube_utils::anytodatetime($date_to);
$interval = $dt_from->diff($dt_to);
if ($interval->invert || $interval->days > 365) {
$error = 'managesieve.invaliddateformat';
return;
}
$dt_i = $dt_from;
$interval = new DateInterval('P1D');
$matchexp = '';
while (!$dt_i->diff($dt_to)->invert) {
$days = (int) $dt_i->format('d');
$matchexp .= $days < 10 ? "[ 0]$days" : $days;
if ($days == $dt_i->format('t') || $dt_i->diff($dt_to)->days == 0) {
$test = array(
'test' => 'header',
'type' => 'regex',
'arg1' => 'received',
'arg2' => '('.$matchexp.') '.$dt_i->format('M Y')
);
$tests[] = $test;
$matchexp = '';
}
else {
$matchexp .= '|';
}
$dt_i->add($interval);
}
return $tests;
}
public static function parse_regexp_tests($tests)
{
$rx_from = '/^\(([0-9]{2}).*\)\s([A-Za-z]+)\s([0-9]{4})/';
$rx_to = '/^\(.*([0-9]{2})\)\s([A-Za-z]+)\s([0-9]{4})/';
$result = array();
foreach ((array) $tests as $test) {
if ($test['test'] == 'header' && $test['type'] == 'regex' && $test['arg1'] == 'received') {
$textexp = preg_replace('/\[ ([^\]]*)\]/', '0', $test['arg2']);
if (!$result['from'] && preg_match($rx_from, $textexp, $matches)) {
$result['from'] = $matches[1]." ".$matches[2]." ".$matches[3];
}
if (preg_match($rx_to, $textexp, $matches)) {
$result['to'] = $matches[1]." ".$matches[2]." ".$matches[3];
}
}
}
return $result;
}
+ /**
+ * Saves vacation script (adding some variables)
+ */
+ protected function save_vacation_script($rule)
+ {
+ // if script does not exist create a new one
+ if ($this->script_name === null || $this->script_name === false) {
+ $this->script_name = $this->rc->config->get('managesieve_script_name');
+ if (empty($this->script_name)) {
+ $this->script_name = 'roundcube';
+ }
+
+ $this->script = array($rule);
+ $script_active = false;
+ }
+ // if script exists
+ else {
+ $script_active = in_array($this->script_name, $this->active);
+
+ // re-order rules if needed
+ if (isset($rule['after']) && $rule['after'] !== '') {
+ // reset original vacation rule
+ if (isset($this->vacation['idx'])) {
+ $this->script[$this->vacation['idx']] = null;
+ }
+
+ // add at target position
+ if ($rule['after'] >= count($this->script) - 1) {
+ $this->script[] = $rule;
+ }
+ else {
+ $script = array();
+
+ foreach ($this->script as $idx => $r) {
+ if ($r) {
+ $script[] = $r;
+ }
+
+ if ($idx == $rule['after']) {
+ $script[] = $rule;
+ }
+ }
+
+ $this->script = $script;
+ }
+
+ $this->script = array_values(array_filter($this->script));
+ }
+ // update original vacation rule if it exists
+ else if (isset($this->vacation['idx'])) {
+ $this->script[$this->vacation['idx']] = $rule;
+ }
+ // otherwise put vacation rule on top
+ else {
+ array_unshift($this->script, $rule);
+ }
+
+ // if the script was not active, we need to de-activate
+ // all rules except the vacation rule, but only if it is not disabled
+ if (!$script_active && !$rule['disabled']) {
+ foreach ($this->script as $idx => $r) {
+ if (empty($r['actions']) || $r['actions'][0]['type'] != 'vacation') {
+ $this->script[$idx]['disabled'] = true;
+ }
+ }
+ }
+ }
+
+ $this->sieve->script->content = $this->script;
+
+ // save the script
+ $saved = $this->save_script($this->script_name);
+
+ // activate the script
+ if ($saved && !$script_active && !$rule['disabled']) {
+ $this->activate_script($this->script_name);
+ }
+
+ return $saved;
+ }
+
/**
* API: get vacation rule
*
* @return array Vacation rule information
*/
public function get_vacation()
{
$this->exts = $this->sieve->get_extensions();
$this->init_script();
$this->vacation_rule();
// check supported extensions
$date_extension = in_array('date', $this->exts);
$regex_extension = in_array('regex', $this->exts);
$seconds_extension = in_array('vacation-seconds', $this->exts);
// set user's timezone
try {
$timezone = new DateTimeZone($this->rc->config->get('timezone', 'GMT'));
}
catch (Exception $e) {
$timezone = new DateTimeZone('GMT');
}
if ($date_extension) {
$date_value = array();
foreach ((array) $this->vacation['tests'] as $test) {
if ($test['test'] == 'currentdate') {
$idx = $test['type'] == 'value-ge' ? 'start' : 'end';
if ($test['part'] == 'date') {
$date_value[$idx]['date'] = $test['arg'];
}
else if ($test['part'] == 'iso8601') {
$date_value[$idx]['datetime'] = $test['arg'];
}
}
}
foreach ($date_value as $idx => $value) {
$$idx = new DateTime($value['datetime'] ?: $value['date'], $timezone);
}
}
else if ($regex_extension) {
// Sieve 'date' extension not available, read start/end from RegEx based rules instead
if ($date_tests = self::parse_regexp_tests($this->vacation['tests'])) {
$from = new DateTime($date_tests['from'] . ' ' . '00:00:00', $timezone);
$to = new DateTime($date_tests['to'] . ' ' . '23:59:59', $timezone);
}
}
if (isset($this->vacation['seconds'])) {
$interval = $this->vacation['seconds'] . 's';
}
else if (isset($this->vacation['days'])) {
$interval = $this->vacation['days'] . 'd';
}
$vacation = array(
'supported' => $this->exts,
'interval' => $interval,
'start' => $start,
'end' => $end,
'enabled' => $this->vacation['reason'] && empty($this->vacation['disabled']),
'message' => $this->vacation['reason'],
'subject' => $this->vacation['subject'],
'action' => $this->vacation['action'],
'target' => $this->vacation['target'],
'addresses' => $this->vacation['addresses'],
);
return $vacation;
}
/**
* API: set vacation rule
*
* @param array $vacation Vacation rule information (see self::get_vacation())
*
* @return bool True on success, False on failure
*/
public function set_vacation($data)
{
$this->exts = $this->sieve->get_extensions();
$this->error = false;
$this->init_script();
$this->vacation_rule();
// check supported extensions
$date_extension = in_array('date', $this->exts);
$regex_extension = in_array('regex', $this->exts);
$seconds_extension = in_array('vacation-seconds', $this->exts);
$vacation['type'] = 'vacation';
$vacation['reason'] = $this->strip_value(str_replace("\r\n", "\n", $data['message']));
$vacation['addresses'] = $data['addresses'];
$vacation['subject'] = $data['subject'];
$vacation_tests = (array) $this->vacation['tests'];
foreach ((array) $vacation['addresses'] as $aidx => $address) {
$vacation['addresses'][$aidx] = $address = trim($address);
if (empty($address)) {
unset($vacation['addresses'][$aidx]);
}
else if (!rcube_utils::check_email($address)) {
$this->error = "Invalid address in vacation addresses: $address";
return false;
}
}
if ($vacation['reason'] == '') {
$this->error = "No vacation message specified";
return false;
}
if ($data['interval']) {
if (!preg_match('/^([0-9]+)\s*([sd])$/', $data['interval'], $m)) {
$this->error = "Invalid vacation interval value: " . $data['interval'];
return false;
}
else if ($m[1]) {
$vacation[strtolower($m[2]) == 's' ? 'seconds' : 'days'] = $m[1];
}
}
// find and remove existing date/regex/true rules
foreach ((array) $vacation_tests as $idx => $t) {
if ($t['test'] == 'currentdate' || $t['test'] == 'true'
|| ($t['test'] == 'header' && $t['type'] == 'regex' && $t['arg1'] == 'received')
) {
unset($vacation_tests[$idx]);
}
}
if ($date_extension) {
foreach (array('start', 'end') as $var) {
if ($dt = $data[$var]) {
$vacation_tests[] = array(
'test' => 'currentdate',
'part' => 'iso8601',
'type' => 'value-' . ($var == 'start' ? 'ge' : 'le'),
'zone' => $dt->format('O'),
'arg' => str_replace('+00:00', 'Z', strtoupper($dt->format('c'))),
);
}
}
}
else if ($regex_extension) {
// Add date range rules if range specified
if ($data['start'] && $data['end']) {
if ($tests = self::build_regexp_tests($data['start'], $data['end'], $error)) {
$vacation_tests = array_merge($vacation_tests, $tests);
}
if ($error) {
$this->error = "Invalid dates specified or unsupported period length";
return false;
}
}
}
if ($data['action'] == 'redirect' || $data['action'] == 'copy') {
if (empty($data['target']) || !rcube_utils::check_email($data['target'])) {
$this->error = "Invalid address in action taget: " . $data['target'];
return false;
}
}
else if ($data['action'] && $data['action'] != 'keep' && $data['action'] != 'discard') {
$this->error = "Unsupported vacation action: " . $data['action'];
return false;
}
if (empty($vacation_tests)) {
$vacation_tests = $this->rc->config->get('managesieve_vacation_test', array(array('test' => 'true')));
}
- // @TODO: handle situation when there's no active script
-
$rule = $this->vacation;
$rule['type'] = 'if';
$rule['name'] = $rule['name'] ?: 'Out-of-Office';
$rule['disabled'] = isset($data['enabled']) && !$data['enabled'];
$rule['tests'] = $vacation_tests;
$rule['join'] = $date_extension ? count($vacation_tests) > 1 : false;
$rule['actions'] = array($vacation);
if ($data['action'] && $data['action'] != 'keep') {
$rule['actions'][] = array(
'type' => $data['action'] == 'discard' ? 'discard' : 'redirect',
'copy' => $data['action'] == 'copy',
'target' => $data['action'] != 'discard' ? $data['target'] : '',
);
}
- // reset original vacation rule
- if (isset($this->vacation['idx'])) {
- $this->script[$this->vacation['idx']] = null;
- }
-
- array_unshift($this->script, $rule);
-
- $this->sieve->script->content = array_values(array_filter($this->script));
-
- return $this->save_script();
+ return $this->save_vacation_script($rule);
}
/**
* API: connect to managesieve server
*/
public function connect($username, $password)
{
if (!parent::connect($username, $password)) {
return $this->load_script();
}
}
/**
* API: Returns last error
*
* @return string Error message
*/
public function get_error()
{
return $this->error;
}
}
diff --git a/lib/plugins/managesieve/localization/ar_SA.inc b/lib/plugins/managesieve/localization/ar_SA.inc
index 8e6d841..88e8957 100644
--- a/lib/plugins/managesieve/localization/ar_SA.inc
+++ b/lib/plugins/managesieve/localization/ar_SA.inc
@@ -1,33 +1,188 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
+$labels['filters'] = 'تصفية';
+$labels['managefilters'] = 'ادارة تصفية البريد الوارد';
+$labels['filtername'] = 'اسم التصفية';
+$labels['newfilter'] = 'تصفية جديدة';
+$labels['filteradd'] = 'اضافة تصفية';
+$labels['filterdel'] = 'حذف تصفية';
+$labels['moveup'] = 'نقل الى الاعلى ';
+$labels['movedown'] = 'نقل الى الاسفل';
+$labels['filterallof'] = 'مطابقة جميع القواعد التالية';
+$labels['filteranyof'] = 'مطابقة اي القواعد التالية';
+$labels['filterany'] = 'كل الرسائل';
+$labels['filtercontains'] = 'محتوى';
+$labels['filternotcontains'] = 'بدون محتوى';
+$labels['filteris'] = 'مساوي الى';
+$labels['filterisnot'] = 'لا يساوي';
+$labels['filterexists'] = 'موجود';
+$labels['filternotexists'] = 'غير موجود';
+$labels['filtermatches'] = 'يطابق التعبير';
+$labels['filternotmatches'] = 'لا يطابق التعبير';
+$labels['filterregex'] = 'يطابق التعبير العادي';
+$labels['filternotregex'] = 'لا يطابق التعبير العادي';
+$labels['filterunder'] = 'تحت';
+$labels['filterover'] = 'خلال';
+$labels['addrule'] = 'اضافة قاعدة';
+$labels['delrule'] = 'حذف قاعدة';
+$labels['messagemoveto'] = 'نقل الرساله الى ';
+$labels['messageredirect'] = 'إعادة توجيه الرسالة الى ';
+$labels['messagecopyto'] = 'نسخ الرسالة الى ';
+$labels['messagesendcopy'] = 'ارسال نسخة من الرسالة الى ';
+$labels['messagereply'] = 'الرد مع رسالة';
$labels['messagedelete'] = 'حذف الرسالة';
+$labels['messagediscard'] = 'تجاهل مع الرسالة';
+$labels['messagekeep'] = 'إبقاء الرسالة في علبة الوارد';
+$labels['messagesrules'] = 'للبريد الوارد:';
+$labels['messagesactions'] = '...تنفيذ المهام التالية:';
$labels['add'] = 'إضافة';
$labels['del'] = 'حذف';
+$labels['sender'] = 'المرسل';
$labels['recipient'] = 'مستلم';
+$labels['vacationaddr'] = 'عناوين البريد الالكتروني(ـة) الاضافية:';
+$labels['vacationdays'] = 'في الغالب كم رسالة ترسل (بالايام):';
+$labels['vacationinterval'] = 'كم عدد الرسائل المرسلة عادةً:';
+$labels['vacationreason'] = 'نص الرسالة (بسبب الاجازة):';
+$labels['vacationsubject'] = 'موضوع الرسالة:';
+$labels['days'] = 'ايام';
+$labels['seconds'] = 'ثواني';
+$labels['rulestop'] = 'ايقاف تقييم القواعد';
+$labels['enable'] = 'تفعيل/تعطيل';
+$labels['filterset'] = 'مجموعة مصفياة';
+$labels['filtersets'] = 'مجموعة تصفية';
+$labels['filtersetadd'] = 'اضافة مجموعة تصفية';
+$labels['filtersetdel'] = 'حذف مجموعة التصفية الحالية';
+$labels['filtersetact'] = 'تفعيل مجموعة التصفية الحالية ';
+$labels['filtersetdeact'] = 'تعطيل مجموعة التصفية الحالية ';
+$labels['filterdef'] = 'تعريف التصفية ';
+$labels['filtersetname'] = 'اسم مجموعة التصفية';
+$labels['newfilterset'] = 'مجموعة تصفية جديدة';
$labels['active'] = 'نشط';
+$labels['none'] = 'لاشيء';
+$labels['fromset'] = 'من مجموعة';
+$labels['fromfile'] = 'من ملف';
+$labels['filterdisabled'] = 'تعطيل التصفية';
+$labels['countisgreaterthan'] = 'العدد اكبر من ';
+$labels['countisgreaterthanequal'] = 'العدد اكبر من او يساوي ';
+$labels['countislessthan'] = 'العدد اقل من ';
+$labels['countislessthanequal'] = 'العدد اقل من او يساوي';
+$labels['countequals'] = 'العدد يساوي ';
+$labels['countnotequals'] = 'العدد لا يساوي';
+$labels['valueisgreaterthan'] = 'القيمة اكبر من ';
+$labels['valueisgreaterthanequal'] = 'القيمة اكبر من او تساوي';
+$labels['valueislessthan'] = 'القيمة اقل من ';
+$labels['valueislessthanequal'] = 'القيمة اقل من او يساوي';
+$labels['valueequals'] = 'القيمة تساوي';
+$labels['valuenotequals'] = 'القيمة لا تساوي';
+$labels['setflags'] = 'ضع وسم على هذه الرسالة ';
+$labels['addflags'] = 'اضف وسم على هذه الرسالة ';
+$labels['removeflags'] = 'احذف الوسم الموجود على هذه الرسالة ';
+$labels['flagread'] = 'قراءة ';
$labels['flagdeleted'] = 'محذوف';
+$labels['flaganswered'] = 'مجابة';
$labels['flagflagged'] = 'موسوم';
$labels['flagdraft'] = 'مسودة';
+$labels['setvariable'] = 'تعيين متغير';
+$labels['setvarname'] = 'اسم المتغير:';
+$labels['setvarvalue'] = 'قيمة المتغير:';
+$labels['setvarmodifiers'] = 'تعديلات:';
+$labels['varquotewildcard'] = 'أقتبس أحرف خاصة';
+$labels['varlength'] = 'الطول';
+$labels['notify'] = 'ارسل تنبية';
+$labels['notifyimportance'] = 'اهمية:';
$labels['notifyimportancelow'] = 'منخفض';
$labels['notifyimportancenormal'] = 'عادي';
$labels['notifyimportancehigh'] = 'مرتفع';
+$labels['filtercreate'] = 'انشئ تصفية';
+$labels['usedata'] = 'استخدم البيانات التالية في الفلتر:';
+$labels['nextstep'] = 'الخطوة التالية';
+$labels['...'] = '...';
+$labels['currdate'] = 'التاريخ الحالي';
+$labels['datetest'] = 'التاريخ';
+$labels['dateheader'] = 'الراس:';
+$labels['year'] = 'السنة';
+$labels['month'] = 'شهر';
+$labels['day'] = 'يوم';
+$labels['date'] = 'التاريخ (yyyy-mm-dd)';
+$labels['julian'] = 'التاريخ (يوليان)';
+$labels['hour'] = 'ساعات';
+$labels['minute'] = 'دقائق';
+$labels['second'] = 'ثواني';
+$labels['time'] = 'الوقت (hh:mm:ss)';
+$labels['iso8601'] = 'التاريخ (ISO8601)';
+$labels['std11'] = 'التاريخ (RFC2822)';
+$labels['zone'] = 'المنطقة الزمنية';
+$labels['weekday'] = 'ايام العمل (0-6)';
$labels['advancedopts'] = 'خيارات متقدّمة';
+$labels['body'] = 'نص';
$labels['address'] = 'العنوان';
+$labels['modifier'] = 'تعديل:';
+$labels['text'] = 'نص';
+$labels['contenttype'] = 'نوع المحتوى';
+$labels['modtype'] = 'نوع:';
$labels['allparts'] = 'الكل';
$labels['domain'] = 'المجال';
+$labels['localpart'] = 'الجزء المحلي';
+$labels['user'] = 'مستخدم';
+$labels['detail'] = 'تفاصيل';
+$labels['default'] = 'افتراضي';
+$labels['index'] = 'الوارد:';
+$labels['indexlast'] = 'تراجع';
+$labels['vacation'] = 'اجازة ';
+$labels['vacation.advanced'] = 'اعدادات متقدمة';
+$labels['vacation.subject'] = 'موضوع';
+$labels['vacation.body'] = 'محتوى ';
+$labels['vacation.status'] = 'الحالة ';
+$labels['vacation.on'] = 'تشغيل';
+$labels['vacation.off'] = 'ايقاف';
+$labels['vacation.addresses'] = 'عناوينني الاضافية';
+$labels['vacation.saving'] = 'يتم حفظ البيانات...';
+$messages['filterunknownerror'] = 'خطا غير معروف من الخادم.';
+$messages['filterconnerror'] = 'لا يمكن الاتصال بالخادم.';
+$messages['filterdeleteerror'] = 'لا يمكن حذف التصفية.خطا في الخادم.';
+$messages['filterdeleted'] = 'تم حذف التصفية بنجاح.';
+$messages['filtersaved'] = 'تم حفظ التصفية بنجاح.';
+$messages['filtersaveerror'] = 'لا يمكن حفظ التصفية.خطا في الخادم.';
+$messages['filterdeleteconfirm'] = 'هل تريد فعلاً حذف التصفية المحددة؟';
+$messages['ruledeleteconfirm'] = 'هل تريد فعلاً حذف القواعد المحددة؟';
+$messages['actiondeleteconfirm'] = 'هل تريد فعلاً حذف الاجراءات المحددة؟';
+$messages['forbiddenchars'] = 'احرف محظورة في هذا الحقل.';
+$messages['cannotbeempty'] = 'لا يمكن ترك الحقل فارغاً';
+$messages['ruleexist'] = 'اسم هذة التصفية موجود مسبقاً';
+$messages['setactivateerror'] = 'لا يمكن تفعيل مجموعة التصفية المحددة.خطا في الخادم.';
+$messages['setdeactivateerror'] = 'لا يمكن تعطيل مجموعة التصفية المحددة.خطا في الخادم.';
+$messages['setdeleteerror'] = 'لا يمكن حذف مجموعة التصفية المحددة.خطا في الخادم.';
+$messages['setactivated'] = 'تم تفعيل مجموعة التصفية بنجاح.';
+$messages['setdeactivated'] = 'تم تعطيل مجموعة التصفية بنجاح.';
+$messages['setdeleted'] = 'تم حذف مجموعة التصفية بنجاح.';
+$messages['setdeleteconfirm'] = 'هل تريد فعلاً حذف مجموعات التصفية المحددة؟';
+$messages['setcreateerror'] = 'لا يمكن انشاء مجموعة تصفية.خطا في الخادم.';
+$messages['setcreated'] = 'تم انشاء مجموعة التصفية بنجاح.';
+$messages['activateerror'] = 'لا يمكن تمكين التصفية(ـات) المحددة .خطا في الخادم.';
+$messages['deactivateerror'] = 'لا يمكن تعطيل التصفية(ـات) المحددة .خطا في الخادم.';
+$messages['deactivated'] = 'تم تعطيل المصفيـ(ـاة) بنجاح.';
+$messages['activated'] = 'تم تفعيل المصفيـ(ـاة) بنجاح.';
+$messages['moved'] = 'تم نقل التصفية بنجاح.';
+$messages['moveerror'] = 'لا يمكن نقل التصفياة المحددة.خطا في الخادم.';
+$messages['nametoolong'] = 'الإسم طويل جداً';
+$messages['setexist'] = 'المجموعة موجودة مسبقا.';
+$messages['nodata'] = 'يجب تحديد موضع واحد على الأقل!';
+$messages['invaliddateformat'] = 'تاريخ غير صحيح او يوجد خطا في تنسق اجزاء التاريخ';
+$messages['saveerror'] = 'لا يمكن حفظ البيانات. خطا في الخادم.';
+$messages['vacationsaved'] = 'تم حفظ تاريخ الاجازة بنجاح.';
?>
diff --git a/lib/plugins/managesieve/localization/az_AZ.inc b/lib/plugins/managesieve/localization/az_AZ.inc
index d692b96..8aec7e3 100644
--- a/lib/plugins/managesieve/localization/az_AZ.inc
+++ b/lib/plugins/managesieve/localization/az_AZ.inc
@@ -1,192 +1,188 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Süzgəclər';
$labels['managefilters'] = 'Gələn məktub üçün süzgəclərin idarəsi';
$labels['filtername'] = 'Süzgəcin adı';
$labels['newfilter'] = 'Yeni süzgəc';
$labels['filteradd'] = 'Süzgəc əlavə et';
$labels['filterdel'] = 'Süzgəci sil';
$labels['moveup'] = 'Yuxarı apar';
$labels['movedown'] = 'Aşağı apar';
$labels['filterallof'] = 'göstərilən bütün qaydalara uyur';
$labels['filteranyof'] = 'verilmiş istənilən qaydaya uyur';
$labels['filterany'] = 'bütün məktublar';
$labels['filtercontains'] = 'daxildir';
$labels['filternotcontains'] = 'daxil deyil';
$labels['filteris'] = 'uyğundur';
$labels['filterisnot'] = 'uyğun deyil';
$labels['filterexists'] = 'mövcuddur';
$labels['filternotexists'] = 'mövcud deyil';
$labels['filtermatches'] = 'ifadə ilə üst-üstə düşür';
$labels['filternotmatches'] = 'ifadə ilə üst-üstə düşmür';
$labels['filterregex'] = 'daimi ifadənin nəticəsi ilə üst-üstə düşür';
$labels['filternotregex'] = 'daimi ifadə ilə üst-üstə düşmür';
$labels['filterunder'] = 'altında';
$labels['filterover'] = 'yuxarıda';
$labels['addrule'] = 'Qayda əlavə et';
$labels['delrule'] = 'Qaydanı sil';
$labels['messagemoveto'] = 'Məktubu köçür';
$labels['messageredirect'] = 'Məktubu yolla';
$labels['messagecopyto'] = 'Məktubu kopyala';
$labels['messagesendcopy'] = 'Məktubun kopyasını göndər';
$labels['messagereply'] = 'Məktubla cavab ver';
$labels['messagedelete'] = 'Sil';
$labels['messagediscard'] = 'Məktubla rədd et';
$labels['messagekeep'] = 'Məktubu gələnlərdə saxla';
$labels['messagesrules'] = 'Daxil olan məktub üçün:';
$labels['messagesactions'] = '...növbəti hərəkəti yerinə yetir:';
$labels['add'] = 'Əlavə et';
$labels['del'] = 'Sil';
$labels['sender'] = 'Göndərən';
$labels['recipient'] = 'Qəbul edən';
$labels['vacationaddr'] = 'Mənim əlavə e-poçt ünvan(lar)ım: ';
$labels['vacationdays'] = 'Məktub neçə müddətdən bir göndərilsin (gündə):';
$labels['vacationinterval'] = 'Məktublar nə qədər sıx göndərilsin:';
-$labels['days'] = 'günlər';
-$labels['seconds'] = 'saniyələr';
$labels['vacationreason'] = 'Məktubun mətni (səbəb yoxdur):';
$labels['vacationsubject'] = 'Məktubun mövzusu:';
+$labels['days'] = 'günlər';
+$labels['seconds'] = 'saniyələr';
$labels['rulestop'] = 'Yerinə yetirməyi dayandır';
$labels['enable'] = 'Yandır/Söndür';
$labels['filterset'] = 'Süzgəc dəsti';
$labels['filtersets'] = 'Süzgəc dəstləri';
$labels['filtersetadd'] = 'Süzgəc dəsti əlavə et';
$labels['filtersetdel'] = 'İndiki sücgəc dəstini sil';
$labels['filtersetact'] = 'İndiki sücgəc dəstini yandır';
$labels['filtersetdeact'] = 'İndiki süzgəc dəstini söndür';
$labels['filterdef'] = 'Süzgəcin təsviri';
$labels['filtersetname'] = 'Süzgəc dəstinin adı';
$labels['newfilterset'] = 'Yeni süzgəc dəsti';
$labels['active'] = 'aktiv';
$labels['none'] = 'heç biri';
$labels['fromset'] = 'dəstdən';
$labels['fromfile'] = 'fayldan';
$labels['filterdisabled'] = 'Süzgəci söndür';
$labels['countisgreaterthan'] = 'sayı buradan daha çoxdur';
$labels['countisgreaterthanequal'] = 'say çox və ya bərabərdir';
$labels['countislessthan'] = 'say buradan azdır';
$labels['countislessthanequal'] = 'say azdır və ya bərabərdir';
$labels['countequals'] = 'say bərabərdir';
$labels['countnotequals'] = 'say bərabər deyil';
$labels['valueisgreaterthan'] = 'dəyər buradan daha böyükdür';
$labels['valueisgreaterthanequal'] = 'dəyər çoxdur və ya bərabərdir';
$labels['valueislessthan'] = 'dəyər buradan azdır';
$labels['valueislessthanequal'] = 'dəyər azdır və ya bərabərdir';
$labels['valueequals'] = 'dəyər bərabərdir';
$labels['valuenotequals'] = 'dəyər bərabər deyil';
$labels['setflags'] = 'Məktublara flaq quraşdır';
$labels['addflags'] = 'Məktuba flaq əlavə et';
$labels['removeflags'] = 'Məktubdan flaqları sil';
$labels['flagread'] = 'Oxu';
$labels['flagdeleted'] = 'Silindi';
$labels['flaganswered'] = 'Cavab verilmiş';
$labels['flagflagged'] = 'İşarəlilər';
$labels['flagdraft'] = 'Qaralama';
$labels['setvariable'] = 'Dəyişəni təyin et';
$labels['setvarname'] = 'Dəyişənin adı:';
$labels['setvarvalue'] = 'Dəyişənin dəyəri:';
$labels['setvarmodifiers'] = 'Modifikatorlar';
$labels['varlower'] = 'aşağı registr';
$labels['varupper'] = 'yuxarı registr';
$labels['varlowerfirst'] = 'aşağı registrdə birinci simvol';
$labels['varupperfirst'] = 'yuxarı registrdə birinci simvol';
$labels['varquotewildcard'] = 'dırnaq simvolu';
$labels['varlength'] = 'uzunluq';
$labels['notify'] = 'Bildiriş göndər';
-$labels['notifyaddress'] = 'Poçt ünvanı:';
-$labels['notifybody'] = 'Bildiriş mətni';
-$labels['notifysubject'] = 'Bildiriş mövzusu';
-$labels['notifyfrom'] = 'Bildirişi yolla:';
$labels['notifyimportance'] = 'Vaciblik';
$labels['notifyimportancelow'] = 'aşağı';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'yuxarı';
$labels['filtercreate'] = 'Süzgəc yarat';
$labels['usedata'] = 'Süzgəcdə bu məlumatları istifadə et:';
$labels['nextstep'] = 'Sonrakı';
$labels['...'] = '...';
$labels['currdate'] = 'İndiki tarix';
$labels['datetest'] = 'Tarix';
$labels['dateheader'] = 'başlıq:';
$labels['year'] = 'il';
$labels['month'] = 'ay';
$labels['day'] = 'gün';
$labels['date'] = 'tarix (iiii-aa-gg)';
$labels['julian'] = 'tarix (yulian)';
$labels['hour'] = 'saat';
$labels['minute'] = 'dəqiqə';
$labels['second'] = 'saniyə';
$labels['time'] = 'saat (sa:dd:sn)';
$labels['iso8601'] = 'tarix (ISO8601)';
$labels['std11'] = 'tarix (RFC2822)';
$labels['zone'] = 'saat-zona';
$labels['weekday'] = 'həftənin günü (0-6)';
$labels['advancedopts'] = 'Əlavə ayarlar';
$labels['body'] = 'Məzmun';
$labels['address'] = 'ünvan';
$labels['envelope'] = 'zərf';
$labels['modifier'] = 'modifikator:';
$labels['text'] = 'mətn';
$labels['undecoded'] = 'emal olunmamış (xammal)';
$labels['contenttype'] = 'məzmun növü';
$labels['modtype'] = 'növ:';
$labels['allparts'] = 'hamısı';
$labels['domain'] = 'domen';
$labels['localpart'] = 'lokal hissə';
$labels['user'] = 'istifadəçi';
$labels['detail'] = 'təfsilat';
$labels['comparator'] = 'komparator:';
$labels['default'] = 'ön qurğulu';
$labels['octet'] = 'ciddi (oktet)';
$labels['asciicasemap'] = 'qeydiyyat üzrə müstəqil (ascii-casemap)';
$labels['asciinumeric'] = 'ədədi (ascii-numeric)';
$labels['index'] = 'indeks:';
$labels['indexlast'] = 'arxaya';
$messages['filterunknownerror'] = 'Serverin naməlum xətası.';
$messages['filterconnerror'] = 'Serverə qoşulmaq alınmır';
$messages['filterdeleteerror'] = 'Süzgəci silmək mümkün deyil. Server xətası.';
$messages['filterdeleted'] = 'Süzgəc uğurla silindi.';
$messages['filtersaved'] = 'Süzgəc uğurla saxlanıldı.';
$messages['filtersaveerror'] = 'Süzgəci saxlamaq mümkün deyil. Server xətası.';
$messages['filterdeleteconfirm'] = 'Siz həqiqətən süzgəci silmək istəyirsiniz?';
$messages['ruledeleteconfirm'] = 'Bu qaydanı silməkdə əminsiniz?';
$messages['actiondeleteconfirm'] = 'Bu hərəkəti silməkdə əminsiniz?';
$messages['forbiddenchars'] = 'Sahədə qadağan edilən işarələr.';
$messages['cannotbeempty'] = 'Sahə boş ola bilməz.';
$messages['ruleexist'] = 'Bu adla süzgəc artıq mövcuddur.';
$messages['setactivateerror'] = 'Seçilmiş süzgəcləri aktiv etmək mümkün deyil. Server xətası.';
$messages['setdeactivateerror'] = 'Seçilmiş süzgəcləri deaktiv mümkün deyil. Server xətası.';
$messages['setdeleteerror'] = 'Seçilmiş süzgəcləri silmək mümkün deyil. Server xətası.';
$messages['setactivated'] = 'Süzgəc dəsti yandırıldı.';
$messages['setdeactivated'] = 'Süzgəc dəsti söndürüldü.';
$messages['setdeleted'] = 'Süzgəc dəsti silindi.';
$messages['setdeleteconfirm'] = 'Bu süzgəc dəstini silməkdə əminsiniz?';
$messages['setcreateerror'] = 'Süzgəcləri yaratmaq mümkün deyil. Server xətası.';
$messages['setcreated'] = 'Süzgəc dəsti uğurla yaradıldı.';
$messages['activateerror'] = 'Seçilmiş süzgəc(lər)i yandırmaq mümkün deyil. Server xətası.';
$messages['deactivateerror'] = 'Seçilmiş süzgəc(lər)i söndürmək mümkün deyil. Server xətası.';
$messages['deactivated'] = 'Süzgəc(lər) uğurla yandırıldı.';
$messages['activated'] = 'Süzgəc(lər) uğurla söndürüldü.';
$messages['moved'] = 'Süzgəc uğurla köçürüldü.';
$messages['moveerror'] = 'Süzgəci köçürmək mümkün deyil. Server xətası.';
$messages['nametoolong'] = 'Süzgəc dəstini yaratmaq mümkün deyil. Ad çox uzundur.';
$messages['namereserved'] = 'Rezerv edilmiş ad.';
$messages['setexist'] = 'Dəst artıq mövcuddur.';
$messages['nodata'] = 'Heç olmasa bir mövqe tutmaq lazımdır!';
$messages['invaliddateformat'] = 'Tarix və ya tarix formatının bir hissəsi səhvdir';
?>
diff --git a/lib/plugins/managesieve/localization/be_BE.inc b/lib/plugins/managesieve/localization/be_BE.inc
index 29c97ad..5a691cd 100644
--- a/lib/plugins/managesieve/localization/be_BE.inc
+++ b/lib/plugins/managesieve/localization/be_BE.inc
@@ -1,192 +1,188 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Фільтры';
$labels['managefilters'] = 'Парадкаваць фільтры атрыманай пошты';
$labels['filtername'] = 'Назва фільтра';
$labels['newfilter'] = 'Новы фільтр';
$labels['filteradd'] = 'Дадаць фільтр';
$labels['filterdel'] = 'Выдаліць фільтр';
$labels['moveup'] = 'Пасунуць уверх';
$labels['movedown'] = 'Пасунуць уніз';
$labels['filterallof'] = 'выконваюцца ўсе наступныя правілы';
$labels['filteranyof'] = 'выконваецца любое з наступных правіл';
$labels['filterany'] = 'усе паведамленні';
$labels['filtercontains'] = 'змяшчае';
$labels['filternotcontains'] = 'не змяшчае';
$labels['filteris'] = 'роўна';
$labels['filterisnot'] = 'не роўна';
$labels['filterexists'] = 'існуе';
$labels['filternotexists'] = 'не існуе';
$labels['filtermatches'] = 'задавальняе выразу';
$labels['filternotmatches'] = 'не задавальняе выразу';
$labels['filterregex'] = 'задавальняе рэгулярнаму выразу';
$labels['filternotregex'] = 'не задавальняе рэгулярнаму выразу';
$labels['filterunder'] = 'менш';
$labels['filterover'] = 'больш';
$labels['addrule'] = 'Дадаць правіла';
$labels['delrule'] = 'Выдаліць правіла';
$labels['messagemoveto'] = 'Перамясціць паведамленне ў';
$labels['messageredirect'] = 'Перанакіраваць паведамленне на';
$labels['messagecopyto'] = 'Скапіяваць паведамленне ў';
$labels['messagesendcopy'] = 'Даслаць копію на';
$labels['messagereply'] = 'Адказаць наступнае';
$labels['messagedelete'] = 'Выдаліць паведамленне';
$labels['messagediscard'] = 'Скасаваць з паведамленнем';
$labels['messagekeep'] = 'Пакінуць паведамленне ў Атрыманых';
$labels['messagesrules'] = 'Для атрыманай пошты:';
$labels['messagesactions'] = '...выконваць наступныя дзеянні:';
$labels['add'] = 'Дадаць';
$labels['del'] = 'Выдаліць';
$labels['sender'] = 'Ад каго';
$labels['recipient'] = 'Каму';
$labels['vacationaddr'] = 'Дадатковы(я) адрасы эл. пошты:';
$labels['vacationdays'] = 'Як часта дасылаць паведамленні (у днях):';
$labels['vacationinterval'] = 'Як часта дасылаць паведамленні:';
-$labels['days'] = 'дзён';
-$labels['seconds'] = 'секунд';
$labels['vacationreason'] = 'Цела паведамлення (прычына вакацый):';
$labels['vacationsubject'] = 'Тэма паведамлення:';
+$labels['days'] = 'дзён';
+$labels['seconds'] = 'секунд';
$labels['rulestop'] = 'Перастаць выконваць праверку правілаў';
$labels['enable'] = 'Уключыць/Выключыць';
$labels['filterset'] = 'Набор фільтраў';
$labels['filtersets'] = 'Наборы фільтраў';
$labels['filtersetadd'] = 'Дадаць набор фільтраў';
$labels['filtersetdel'] = 'Выдаліць бягучы набор фільтраў';
$labels['filtersetact'] = 'Актываваць бягучы набор фільтраў';
$labels['filtersetdeact'] = 'Дэактываваць бягучы набор фільтраў';
$labels['filterdef'] = 'Вызначэнне фільтра';
$labels['filtersetname'] = 'Назва набору фільтраў';
$labels['newfilterset'] = 'Новы набор фільтраў';
$labels['active'] = 'актыўны';
$labels['none'] = 'няма';
$labels['fromset'] = 'з набору';
$labels['fromfile'] = 'з файла';
$labels['filterdisabled'] = 'Фільтр адключаны';
$labels['countisgreaterthan'] = 'лік большы за';
$labels['countisgreaterthanequal'] = 'лік большы за альбо роўны';
$labels['countislessthan'] = 'лік меншы за';
$labels['countislessthanequal'] = 'лік меншы за альбо роўны';
$labels['countequals'] = 'лік роўны';
$labels['countnotequals'] = 'лік не роўны';
$labels['valueisgreaterthan'] = 'значэнне большае за';
$labels['valueisgreaterthanequal'] = 'значэнне большае за альбо роўнае';
$labels['valueislessthan'] = 'значэнне меншае за';
$labels['valueislessthanequal'] = 'значэнне меншае за альбо роўнае';
$labels['valueequals'] = 'значэнне роўнае';
$labels['valuenotequals'] = 'значэнне не роўнае';
$labels['setflags'] = 'Устанавіць флагі на паведамленне';
$labels['addflags'] = 'Дадаць флагі да паведамлення';
$labels['removeflags'] = 'Выдаліць флагі з паведамлення';
$labels['flagread'] = 'Прачытана';
$labels['flagdeleted'] = 'Выдалена';
$labels['flaganswered'] = 'З адказам';
$labels['flagflagged'] = 'Пазначана';
$labels['flagdraft'] = 'Чарнавік';
$labels['setvariable'] = 'Устанавіць зменную';
$labels['setvarname'] = 'Імя зменнай:';
$labels['setvarvalue'] = 'Значэнне зменнай:';
$labels['setvarmodifiers'] = 'Мадыфікатары:';
$labels['varlower'] = 'ніжні рэгістр';
$labels['varupper'] = 'верхні рэгістр';
$labels['varlowerfirst'] = 'першы знак у ніжнім рэгістры';
$labels['varupperfirst'] = 'першы знак у верхнім рэгістры';
$labels['varquotewildcard'] = 'службовыя знакі забіраць у апострафы';
$labels['varlength'] = 'даўжыня';
$labels['notify'] = 'Паслаць апавяшчэнне';
-$labels['notifyaddress'] = 'На эл. адрас:';
-$labels['notifybody'] = 'Цела апавяшчэння:';
-$labels['notifysubject'] = 'Тэма апавяшчэння:';
-$labels['notifyfrom'] = 'Адпраўнік апавяшчэння:';
$labels['notifyimportance'] = 'Важнасць:';
$labels['notifyimportancelow'] = 'нізкая';
$labels['notifyimportancenormal'] = 'звычайная';
$labels['notifyimportancehigh'] = 'высокая';
$labels['filtercreate'] = 'Стварыць фільтр';
$labels['usedata'] = 'Ужываць наступныя дадзеныя ў фільтры:';
$labels['nextstep'] = 'Наступны крок';
$labels['...'] = '...';
$labels['currdate'] = 'Бягучая дата';
$labels['datetest'] = 'Дата';
$labels['dateheader'] = 'загаловак:';
$labels['year'] = 'год';
$labels['month'] = 'месяц';
$labels['day'] = 'дзень';
$labels['date'] = 'дата (гггг-мм-дд)';
$labels['julian'] = 'дата (юліянская)';
$labels['hour'] = 'гадзіна';
$labels['minute'] = 'мінута';
$labels['second'] = 'секунда';
$labels['time'] = 'час (гг:мм:сс)';
$labels['iso8601'] = 'дата (ISO8601)';
$labels['std11'] = 'дата (RFC2822)';
$labels['zone'] = 'часавая зона';
$labels['weekday'] = 'дзень тыдня (0-6)';
$labels['advancedopts'] = 'Дадатковыя параметры';
$labels['body'] = 'Цела';
$labels['address'] = 'адрас';
$labels['envelope'] = 'канверт';
$labels['modifier'] = 'мадыфікатар:';
$labels['text'] = 'тэкст';
$labels['undecoded'] = 'неапрацаваны (сыры)';
$labels['contenttype'] = 'тып змесціва';
$labels['modtype'] = 'пошук у адрасах:';
$labels['allparts'] = 'усюль';
$labels['domain'] = 'у імені дамена';
$labels['localpart'] = 'толькі ў імені карыстальніка, без дамена';
$labels['user'] = 'у поўным імені карыстальніка';
$labels['detail'] = 'у дадатковых звестках';
$labels['comparator'] = 'спосаб параўнання:';
$labels['default'] = 'стандартны';
$labels['octet'] = 'строгі (octet)';
$labels['asciicasemap'] = 'без уліку рэгістру (ascii-casemap)';
$labels['asciinumeric'] = 'лікавы (ascii-numeric)';
$labels['index'] = 'індэкс:';
$labels['indexlast'] = 'назад';
$messages['filterunknownerror'] = 'Невядомая памылка сервера.';
$messages['filterconnerror'] = 'Не ўдалося злучыцца з серверам.';
$messages['filterdeleteerror'] = 'Не ўдалося выдаліць фільтр. Памылка на серверы.';
$messages['filterdeleted'] = 'Фільтр выдалены.';
$messages['filtersaved'] = 'Фільтр захаваны.';
$messages['filtersaveerror'] = 'Не ўдалося захаваць фільтр. Памылка на серверы.';
$messages['filterdeleteconfirm'] = 'Напраўду выдаліць абраны фільтр?';
$messages['ruledeleteconfirm'] = 'Напраўду выдаліць абранае правіла?';
$messages['actiondeleteconfirm'] = 'Напраўду выдаліць абранае дзеянне?';
$messages['forbiddenchars'] = 'Забароненыя знакі ў полі.';
$messages['cannotbeempty'] = 'Поле не можа быць пустым.';
$messages['ruleexist'] = 'Фільтр з гэтай назвай ужо існуе.';
$messages['setactivateerror'] = 'Не ўдалося ўключыць абраны набор фільтраў. Памылка на серверы.';
$messages['setdeactivateerror'] = 'Не ўдалося адключыць абраны набор фільтраў. Памылка на серверы.';
$messages['setdeleteerror'] = 'Не ўдалося выдаліць абраны набор фільтраў. Памылка на серверы.';
$messages['setactivated'] = 'Набор фільтраў актываваны.';
$messages['setdeactivated'] = 'Набор фільтраў дэактываваны.';
$messages['setdeleted'] = 'Набор фільтраў выдалены.';
$messages['setdeleteconfirm'] = 'Напраўду выдаліць абраны набор фільтраў?';
$messages['setcreateerror'] = 'Не ўдалося стварыць набор фільтраў. Памылка на серверы.';
$messages['setcreated'] = 'Набор фільтраў створаны.';
$messages['activateerror'] = 'Не ўдалося ўключыць абраны(я) фільтры. Памылка на серверы.';
$messages['deactivateerror'] = 'Не ўдалося адключыць абраны(я) фільтры. Памылка на серверы.';
$messages['deactivated'] = 'Фільтр(ы) адключаны.';
$messages['activated'] = 'Фільтр(ы) уключаны.';
$messages['moved'] = 'Фільтр перамешчаны.';
$messages['moveerror'] = 'Не ўдалося перамясціць абраны фільтр. Памылка на серверы.';
$messages['nametoolong'] = 'Задаўгая назва.';
$messages['namereserved'] = 'Зарэзерваваная назва.';
$messages['setexist'] = 'Набор ужо існуе.';
$messages['nodata'] = 'Мінімум адна пазіцыя павінна быць вылучана!';
$messages['invaliddateformat'] = 'Няслушная дата альбо фармат даты';
?>
diff --git a/lib/plugins/managesieve/localization/bg_BG.inc b/lib/plugins/managesieve/localization/bg_BG.inc
index c9d06d2..432cb4f 100644
--- a/lib/plugins/managesieve/localization/bg_BG.inc
+++ b/lib/plugins/managesieve/localization/bg_BG.inc
@@ -1,192 +1,209 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Филтри';
$labels['managefilters'] = 'Управление на филтри за входяща поща';
$labels['filtername'] = 'Име на филтър';
$labels['newfilter'] = 'Нов филтър';
$labels['filteradd'] = 'Нов филтър';
$labels['filterdel'] = 'Изтриване на филтър';
$labels['moveup'] = 'Преместване нагоре';
$labels['movedown'] = 'Преместване надолу';
$labels['filterallof'] = 'която изпълнява всички условия';
$labels['filteranyof'] = 'която изпълнява някое от условията';
$labels['filterany'] = 'за всички писма';
$labels['filtercontains'] = 'съдържа';
$labels['filternotcontains'] = 'не съдържа';
$labels['filteris'] = 'е равно на';
$labels['filterisnot'] = 'не е равно на';
$labels['filterexists'] = 'съществува';
$labels['filternotexists'] = 'не съществува';
$labels['filtermatches'] = 'съвпада с израз';
$labels['filternotmatches'] = 'не съвпада с израз';
$labels['filterregex'] = 'отговаря на регулярен израз';
$labels['filternotregex'] = 'не отговаря на регулярен израз';
$labels['filterunder'] = 'под';
$labels['filterover'] = 'над';
$labels['addrule'] = 'Ново условие';
$labels['delrule'] = 'Изтриване на условие';
$labels['messagemoveto'] = 'Премести писмото във';
$labels['messageredirect'] = 'Пренасочи писмото до';
$labels['messagecopyto'] = 'Копирай писмото във';
$labels['messagesendcopy'] = 'Изпрати копие на писмото до';
$labels['messagereply'] = 'Отговори с писмо';
$labels['messagedelete'] = 'Изтрий писмото';
$labels['messagediscard'] = 'Отхвърли със съобщение';
$labels['messagekeep'] = 'Остави писмото във Вх. поща';
$labels['messagesrules'] = 'При получаване на поща...';
$labels['messagesactions'] = '...изпълни следните действия:';
$labels['add'] = 'Добави';
$labels['del'] = 'Изтрий';
$labels['sender'] = 'Подател';
$labels['recipient'] = 'Получател';
$labels['vacationaddr'] = 'Мои допълнителни e-mail адреси:';
$labels['vacationdays'] = 'Колко често да праща писма (в дни):';
$labels['vacationinterval'] = 'Колко често да праща писма:';
-$labels['days'] = 'дни';
-$labels['seconds'] = 'секунди';
$labels['vacationreason'] = 'Текст на писмото (причина за ваканцията)';
$labels['vacationsubject'] = 'Заглавие на писмото';
+$labels['days'] = 'дни';
+$labels['seconds'] = 'секунди';
$labels['rulestop'] = 'Спри проверка на други условия';
$labels['enable'] = 'Включи/Изключи';
$labels['filterset'] = 'Набор филтри';
$labels['filtersets'] = 'Набори филтри';
$labels['filtersetadd'] = 'Нов набор филтри';
$labels['filtersetdel'] = 'Изтриване на текущ набор филтри';
$labels['filtersetact'] = 'Активиране на текущ набор филтри';
$labels['filtersetdeact'] = 'Деактивиране на текущ набор филтри';
$labels['filterdef'] = 'Дефиниране на филтър';
$labels['filtersetname'] = 'Име на набор филтри';
$labels['newfilterset'] = 'Нов набор филтри';
$labels['active'] = 'активен';
$labels['none'] = 'няма';
$labels['fromset'] = 'от набор';
$labels['fromfile'] = 'от файл';
$labels['filterdisabled'] = 'Изключен филтър';
$labels['countisgreaterthan'] = 'брой е по-голям от';
$labels['countisgreaterthanequal'] = 'брой е по-голям или равен на';
$labels['countislessthan'] = 'брой е по-малък от';
$labels['countislessthanequal'] = 'брой е по-малък или равен на';
$labels['countequals'] = 'брой е равен на';
$labels['countnotequals'] = 'брой не е равен на';
$labels['valueisgreaterthan'] = 'стойност е по-голяма от';
$labels['valueisgreaterthanequal'] = 'стойност е по-голяма или равна на';
$labels['valueislessthan'] = 'стойност е по-малка от';
$labels['valueislessthanequal'] = 'стойност е по-малка или равна на';
$labels['valueequals'] = 'стойност е равна на';
$labels['valuenotequals'] = 'стойност не е равна на';
$labels['setflags'] = 'Установи флагове на писмо';
$labels['addflags'] = 'Добави флагове към писмо';
$labels['removeflags'] = 'Премахни флагове от писмо';
$labels['flagread'] = 'Прочетено';
$labels['flagdeleted'] = 'Изтрито';
$labels['flaganswered'] = 'Отговорено';
$labels['flagflagged'] = 'Отбелязано';
$labels['flagdraft'] = 'Чернова';
$labels['setvariable'] = 'Установи променлива';
$labels['setvarname'] = 'Име на променлива:';
$labels['setvarvalue'] = 'Стойност на променлива:';
$labels['setvarmodifiers'] = 'Модификатори:';
$labels['varlower'] = 'малки букви';
$labels['varupper'] = 'главни букви';
$labels['varlowerfirst'] = 'първи знак с малка буква';
$labels['varupperfirst'] = 'първи знак с главна буква';
$labels['varquotewildcard'] = 'цитиране на специални знаци';
$labels['varlength'] = 'дължина';
$labels['notify'] = 'Изпрати известие';
-$labels['notifyaddress'] = 'До e-mail адрес:';
-$labels['notifybody'] = 'Основен текст на известието:';
-$labels['notifysubject'] = 'Заглавие на известието:';
-$labels['notifyfrom'] = 'Подател на известието:';
+$labels['notifytarget'] = 'Известие към:';
+$labels['notifymessage'] = 'Съдържание на известие (опционално):';
+$labels['notifyoptions'] = 'Опции на известие (опционално):';
+$labels['notifyfrom'] = 'Известие от (опционално):';
$labels['notifyimportance'] = 'Приоритет:';
$labels['notifyimportancelow'] = 'нисък';
$labels['notifyimportancenormal'] = 'нормален';
$labels['notifyimportancehigh'] = 'висок';
+$labels['notifymethodmailto'] = 'Ел. поща';
+$labels['notifymethodtel'] = 'Телефон';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Използвай за нов филтър';
$labels['usedata'] = 'Използвай следните данни във филтъра:';
$labels['nextstep'] = 'Следваща стъпка';
$labels['...'] = '...';
$labels['currdate'] = 'Текуща дата';
$labels['datetest'] = 'Дата';
$labels['dateheader'] = 'заглавен блок:';
$labels['year'] = 'година';
$labels['month'] = 'месец';
$labels['day'] = 'ден';
$labels['date'] = 'дата (гггг-мм-дд)';
$labels['julian'] = 'дата (юлианска)';
$labels['hour'] = 'час';
$labels['minute'] = 'минута';
$labels['second'] = 'секунда';
$labels['time'] = 'време (чч:мм:сс)';
$labels['iso8601'] = 'дата (ISO8601)';
$labels['std11'] = 'дата (RFC2822)';
$labels['zone'] = 'часова зона';
$labels['weekday'] = 'ден от седмицата (0-6)';
$labels['advancedopts'] = 'Разширени настройки';
$labels['body'] = 'Основен текст';
$labels['address'] = 'адрес';
$labels['envelope'] = 'плик';
$labels['modifier'] = 'модификатор:';
$labels['text'] = 'текст';
$labels['undecoded'] = 'недекодиран (суров)';
$labels['contenttype'] = 'тип на съдържанието';
$labels['modtype'] = 'тип:';
$labels['allparts'] = 'всичко';
$labels['domain'] = 'домейн';
$labels['localpart'] = 'локална част';
$labels['user'] = 'потребител';
$labels['detail'] = 'датайли';
$labels['comparator'] = 'сравнител:';
$labels['default'] = 'по подразбиране';
$labels['octet'] = 'стриктно (октет)';
$labels['asciicasemap'] = 'без значение малки/големи букви';
$labels['asciinumeric'] = 'цифрово';
$labels['index'] = 'индекс:';
$labels['indexlast'] = 'наобратно';
+$labels['vacation'] = 'Отпуск';
+$labels['vacation.reply'] = 'Писмо отговор';
+$labels['vacation.advanced'] = 'Разширени настройки';
+$labels['vacation.subject'] = 'Относно';
+$labels['vacation.body'] = 'Съдържание';
+$labels['vacation.status'] = 'Статус';
+$labels['vacation.on'] = 'Вкл.';
+$labels['vacation.off'] = 'Изкл.';
+$labels['vacation.addresses'] = 'Моите допълнителни адреси';
+$labels['vacation.interval'] = 'Интервал на отговор';
+$labels['vacation.after'] = 'Постави правило за отпуск след';
+$labels['vacation.saving'] = 'Запис на данни...';
$messages['filterunknownerror'] = 'Неизвестна сървърна грешка.';
$messages['filterconnerror'] = 'Неуспешно свързване с managesieve сървъра.';
$messages['filterdeleteerror'] = 'Невъзможно изтриване на филтъра. Възникна сървърна грешка.';
$messages['filterdeleted'] = 'Филтърът е изтрит успешно.';
$messages['filtersaved'] = 'Филтърът е записан успешно.';
$messages['filtersaveerror'] = 'Невъзможно записване на филтъра. Възникна сървърна грешка.';
$messages['filterdeleteconfirm'] = 'Наистина ли желаете да изтриете избрания филтър?';
$messages['ruledeleteconfirm'] = 'Сигурни ли сте, че желаете да изтриете избраното условие?';
$messages['actiondeleteconfirm'] = 'Сигурни ли сте, че желаете да изтриете избраното действие?';
$messages['forbiddenchars'] = 'Забранени символи в полето.';
$messages['cannotbeempty'] = 'Полето не може да бъде празно.';
$messages['ruleexist'] = 'Вече има филтър с указаното име.';
$messages['setactivateerror'] = 'Невъзможно активиране на избрания набор от филтри. Възникна сървърна грешка.';
$messages['setdeactivateerror'] = 'Невъзможно деактивиране на избрания набор от филтри. Възникна сървърна грешка.';
$messages['setdeleteerror'] = 'Невъзможно изтриване на избрания набор от филтри. Възникна сървърна грешка.';
$messages['setactivated'] = 'Наборът от филтри е активиран успешно.';
$messages['setdeactivated'] = 'Наборът от филтри е деактивиран успешно.';
$messages['setdeleted'] = 'Наборът от филтри е изтрит успешно.';
$messages['setdeleteconfirm'] = 'Сигурни ли сте, че желаете да изтриете избрания набор от филтри?';
$messages['setcreateerror'] = 'Невъзможно създаване на набор от филтри. Възникна сървърна грешка.';
$messages['setcreated'] = 'Наборът от филтри е създаден успешно.';
$messages['activateerror'] = 'Невъзможно включване на филтъра. Възникна сървърна грешка.';
$messages['deactivateerror'] = 'Невъзможно изключване на филтъра. Възникна сървърна грешка.';
$messages['deactivated'] = 'Филтърът е изключен успешно.';
$messages['activated'] = 'Филтърът е включен успешно.';
$messages['moved'] = 'Филтърът е преместен успешно.';
$messages['moveerror'] = 'Невъзможно преместване на филтъра. Възникна сървърна грешка.';
$messages['nametoolong'] = 'Името е прекалено дълго.';
$messages['namereserved'] = 'Резервирано име.';
$messages['setexist'] = 'Вече има такъв набор филтри.';
$messages['nodata'] = 'Поне една позиция трябва да е избрана!';
$messages['invaliddateformat'] = 'невалидна дата или формат на част от дата';
+$messages['saveerror'] = 'Невъзможен запис на данни. Грешка при достъп до сървър.';
+$messages['vacationsaved'] = 'Данните за отпуск са записани успешно.';
?>
diff --git a/lib/plugins/managesieve/localization/bs_BA.inc b/lib/plugins/managesieve/localization/bs_BA.inc
index bee1aba..84a85a3 100644
--- a/lib/plugins/managesieve/localization/bs_BA.inc
+++ b/lib/plugins/managesieve/localization/bs_BA.inc
@@ -1,225 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filteri';
$labels['managefilters'] = 'Upravljanje dolaznim email filterima';
$labels['filtername'] = 'Naziv filtera';
$labels['newfilter'] = 'Novi filter';
$labels['filteradd'] = 'Dodaj filter';
$labels['filterdel'] = 'Obriši filter';
$labels['moveup'] = 'Pomjeri gore';
$labels['movedown'] = 'Pomjeri dole';
$labels['filterallof'] = 'poklapa se sa svim sljedećim pravilima';
$labels['filteranyof'] = 'poklapa se sa bilo kojim od sljedećih pravila';
$labels['filterany'] = 'sve poruke';
$labels['filtercontains'] = 'sadrži';
$labels['filternotcontains'] = 'ne sadrži';
$labels['filteris'] = 'jednako';
$labels['filterisnot'] = 'nije jednako';
$labels['filterexists'] = 'postoji';
$labels['filternotexists'] = 'ne postoji';
$labels['filtermatches'] = 'poklapa se sa izrazom';
$labels['filternotmatches'] = 'ne poklapa se sa izrazom';
$labels['filterregex'] = 'poklapa se sa regularnim izrazom';
$labels['filternotregex'] = 'ne poklapa se sa regularnim izrazom';
$labels['filterunder'] = 'ispod';
$labels['filterover'] = 'iznad';
$labels['addrule'] = 'Dodaj pravilo';
$labels['delrule'] = 'Obriši pravilo';
$labels['messagemoveto'] = 'Premjesti poruku u';
$labels['messageredirect'] = 'Preusmjeri poruku ka';
$labels['messagecopyto'] = 'Kopiraj poruku u';
$labels['messagesendcopy'] = 'Pošalji kopiju poruke';
$labels['messagereply'] = 'Odgovori';
$labels['messagedelete'] = 'Obriši poruku';
$labels['messagediscard'] = 'Odbaci sa porukom';
$labels['messagekeep'] = 'Zadrži poruku u sandučetu';
$labels['messagesrules'] = 'Za dolazne emailove:';
$labels['messagesactions'] = '...izvrši sljedeće akcije:';
$labels['add'] = 'Dodaj';
$labels['del'] = 'Obriši';
$labels['sender'] = 'Pošiljaoc';
$labels['recipient'] = 'Primaoc';
$labels['vacationaddr'] = 'Moje dodatne email adrese:';
$labels['vacationdays'] = 'Frekvencija slanja poruka (u danima):';
$labels['vacationinterval'] = 'Frekvencija slanja poruka:';
$labels['vacationreason'] = 'Tijelo poruke (razlog za odmor):';
$labels['vacationsubject'] = 'Naslov poruke:';
$labels['days'] = 'dana';
$labels['seconds'] = 'sekundi';
$labels['rulestop'] = 'Prestani procjenjivati pravila';
$labels['enable'] = 'Omogući/Onemogući';
$labels['filterset'] = 'Set filtera';
$labels['filtersets'] = 'Setovi filtera';
$labels['filtersetadd'] = 'Dodaj set filtera';
$labels['filtersetdel'] = 'Obriši trenutni set filtera';
$labels['filtersetact'] = 'Aktiviraj trenutni set filtera';
$labels['filtersetdeact'] = 'Deaktiviraj trenutni set filtera';
$labels['filterdef'] = 'Definicija filtera';
$labels['filtersetname'] = 'Naziv seta filtera';
$labels['newfilterset'] = 'Novi set filtera';
$labels['active'] = 'aktivno';
$labels['none'] = 'ništa';
$labels['fromset'] = 'iz seta';
$labels['fromfile'] = 'iz datoteke';
$labels['filterdisabled'] = 'Filter je onemogućen';
$labels['countisgreaterthan'] = 'brojač je veći od';
$labels['countisgreaterthanequal'] = 'brojač je veći ili jednak';
$labels['countislessthan'] = 'brojač je manji od';
$labels['countislessthanequal'] = 'brojač je manji ili jednak';
$labels['countequals'] = 'brojač je jednak';
$labels['countnotequals'] = 'zbir nije jednak';
$labels['valueisgreaterthan'] = 'vrijednost je veća od';
$labels['valueisgreaterthanequal'] = 'vrijednost je veća ili jednaka';
$labels['valueislessthan'] = 'vrijednost je manja od';
$labels['valueislessthanequal'] = 'vrijednost je manja ili jednaka';
$labels['valueequals'] = 'vrijednost je jednaka';
$labels['valuenotequals'] = 'vrijednost nije jednaka';
$labels['setflags'] = 'Postavi oznake za poruku';
$labels['addflags'] = 'Dodaj oznake u poruku';
$labels['removeflags'] = 'Ukloni oznake iz poruke';
$labels['flagread'] = 'Pročitano';
$labels['flagdeleted'] = 'Obrisano';
$labels['flaganswered'] = 'Odgovoreno';
$labels['flagflagged'] = 'Važno';
$labels['flagdraft'] = 'Skica';
$labels['setvariable'] = 'Postavi promjenjivu';
$labels['setvarname'] = 'Naziv promjenjive:';
$labels['setvarvalue'] = 'Vrijednost promjenjive:';
$labels['setvarmodifiers'] = 'Parametri:';
$labels['varlower'] = 'mala slova';
$labels['varupper'] = 'velika slova';
$labels['varlowerfirst'] = 'prvi znak malim slovom';
$labels['varupperfirst'] = 'prvi znak velikim slovom';
$labels['varquotewildcard'] = 'citiraj specijalne znakove';
$labels['varlength'] = 'dužina';
$labels['notify'] = 'Pošalji napomenu';
$labels['notifytarget'] = 'Odredište napomene:';
$labels['notifymessage'] = 'Poruka napomene (neobavezno):';
$labels['notifyoptions'] = 'Opcije napomene (neobavezno):';
$labels['notifyfrom'] = 'Pošiljalac napomene (neobavezno):';
$labels['notifyimportance'] = 'Prioritet:';
$labels['notifyimportancelow'] = 'mali';
$labels['notifyimportancenormal'] = 'obični';
$labels['notifyimportancehigh'] = 'veliki';
$labels['notifymethodmailto'] = 'Email';
$labels['notifymethodtel'] = 'Telefon';
$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Kreiraj filter';
$labels['usedata'] = 'Koristite sljedeće podatke u filteru:';
$labels['nextstep'] = 'Sljedeći korak';
$labels['...'] = '...';
$labels['currdate'] = 'Trenutni datum';
$labels['datetest'] = 'Datum';
$labels['dateheader'] = 'zaglavlje:';
$labels['year'] = 'godina';
$labels['month'] = 'mjesec';
$labels['day'] = 'dan';
$labels['date'] = 'datum (gggg-mm-dd)';
$labels['julian'] = 'datum (julijanski)';
$labels['hour'] = 'sat';
$labels['minute'] = 'minuta';
$labels['second'] = 'sekunda';
$labels['time'] = 'vrijeme (hh:mm:ss)';
$labels['iso8601'] = 'datum (ISO8601)';
$labels['std11'] = 'datum (RFC2822)';
$labels['zone'] = 'vremenska zona';
$labels['weekday'] = 'sedmica (0-6)';
$labels['advancedopts'] = 'Napredne opcije';
$labels['body'] = 'Tijelo';
$labels['address'] = 'adresa';
$labels['envelope'] = 'koverta';
$labels['modifier'] = 'prilagođavanje:';
$labels['text'] = 'tekst';
$labels['undecoded'] = 'nekodiran (obični)';
$labels['contenttype'] = 'vrsta sadržaja';
$labels['modtype'] = 'vrsta:';
$labels['allparts'] = 'sve';
$labels['domain'] = 'domena';
$labels['localpart'] = 'lokalni dio';
$labels['user'] = 'korisnik';
$labels['detail'] = 'detalji';
$labels['comparator'] = 'upoređivač:';
$labels['default'] = 'početno';
$labels['octet'] = 'striktno (oktet)';
$labels['asciicasemap'] = 'osjetljivo na velika/mala slova (ascii-casemap)';
$labels['asciinumeric'] = 'numerički (ascii-numeric)';
$labels['index'] = 'indeks:';
$labels['indexlast'] = 'unazad';
$labels['vacation'] = 'Odmor';
$labels['vacation.reply'] = 'Poruka sa odgovorom';
$labels['vacation.advanced'] = 'Napredmen postavke';
$labels['vacation.subject'] = 'Naslov';
$labels['vacation.body'] = 'Tijelo';
-$labels['vacation.dates'] = 'Vrijeme odmora';
-$labels['vacation.from'] = 'Od:';
-$labels['vacation.to'] = 'Do:';
+$labels['vacation.start'] = 'Početak odmora';
+$labels['vacation.end'] = 'Kraj odmora';
$labels['vacation.status'] = 'Status';
$labels['vacation.on'] = 'Uključeno';
$labels['vacation.off'] = 'Isključeno';
$labels['vacation.addresses'] = 'Moje dodatne adrese';
$labels['vacation.interval'] = 'Interval odgovora';
$labels['vacation.after'] = 'Pravilo za odmor stavi nakon';
$labels['vacation.saving'] = 'Snimam podatke...';
$labels['vacation.action'] = 'Akcija za dolazne poruke';
$labels['vacation.keep'] = 'Zadrži';
$labels['vacation.discard'] = 'Odbaci';
$labels['vacation.redirect'] = 'Preusmeri ka';
$labels['vacation.copy'] = 'Pošalji kopiju na';
$labels['arialabelfiltersetactions'] = 'Akcije za filterske setove';
$labels['arialabelfilteractions'] = 'Filterske akcije';
$labels['arialabelfilterform'] = 'Svojstva filtera';
$labels['ariasummaryfilterslist'] = 'Lista filtera';
$labels['ariasummaryfiltersetslist'] = 'Lista filterskih setova';
$labels['filterstitle'] = 'Uredi filtere za dolazni email';
$labels['vacationtitle'] = 'Uredi pravila kada nisam na poslu';
$messages['filterunknownerror'] = 'Nepoznata serverska greška.';
$messages['filterconnerror'] = 'Nije se moguće povezati na server.';
$messages['filterdeleteerror'] = 'Nije moguće obrisati filter. Desila se serverska greška.';
$messages['filterdeleted'] = 'Filter je uspješno obrisan.';
$messages['filtersaved'] = 'Filter je uspješno sačuvan.';
$messages['filtersaveerror'] = 'Nije moguće sačuvati filter. Desila se serverska greška.';
$messages['filterdeleteconfirm'] = 'Da li zaista želite obrisati označeni filter?';
$messages['ruledeleteconfirm'] = 'Jeste li sigurni da želite obrisati označeno pravilo?';
$messages['actiondeleteconfirm'] = 'Jeste li sigurni da želite obrisati označenu akciju?';
$messages['forbiddenchars'] = 'U polje su uneseni nedozvoljeni znakovi.';
$messages['cannotbeempty'] = 'Polje ne može biti prazno.';
$messages['ruleexist'] = 'Filter s tim imenom već postoji.';
$messages['setactivateerror'] = 'Nije moguće aktivirati označeni set filtera. Desila se serverska greška.';
$messages['setdeactivateerror'] = 'Nije moguće deaktivirati označeni set filtera. Desila se serverska greška.';
$messages['setdeleteerror'] = 'Nije moguće obrisati označeni set filtera. Desila se serverska greška.';
$messages['setactivated'] = 'Set filtera je uspješno aktiviran.';
$messages['setdeactivated'] = 'Set filtera je uspješno deaktiviran.';
$messages['setdeleted'] = 'Set filtera je uspješno obrisan.';
$messages['setdeleteconfirm'] = 'Jeste li sigurni da želite obrisati označeni set filtera?';
$messages['setcreateerror'] = 'Nije moguće kreirati set filtera. Desila se serverska greška.';
$messages['setcreated'] = 'Set filtera je uspješno kreiran.';
$messages['activateerror'] = 'Nije moguće omogućiti označene filtere. Desila se serverska greška.';
$messages['deactivateerror'] = 'Nije moguće onemogućiti označene filtere. Desila se serverska greška.';
$messages['deactivated'] = 'Filteri su uspješno omogućeni.';
$messages['activated'] = 'Filteri su uspješno onemogućeni.';
$messages['moved'] = 'Filteri su uspješno premješteni.';
$messages['moveerror'] = 'Nije moguće premjestiti označeni filter. Desila se serverska greška.';
$messages['nametoolong'] = 'Ime je predugo.';
$messages['namereserved'] = 'Ime je rezervisano.';
$messages['setexist'] = 'Set već postoji.';
$messages['nodata'] = 'Morate označiti barem jednu poziciju!';
$messages['invaliddateformat'] = 'Netačan datum ili dio formata datuma';
$messages['saveerror'] = 'Nije moguće snimiti podatke. Desila se serverska greška.';
$messages['vacationsaved'] = 'Podaci o odmoru su uspješno snimljeni.';
$messages['emptyvacationbody'] = 'Tijelo poruke za odmor je neophodno!';
?>
diff --git a/lib/plugins/managesieve/localization/ca_ES.inc b/lib/plugins/managesieve/localization/ca_ES.inc
index 5e54c6a..0dc6863 100644
--- a/lib/plugins/managesieve/localization/ca_ES.inc
+++ b/lib/plugins/managesieve/localization/ca_ES.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtres';
$labels['managefilters'] = 'Gestiona els filtres dels missatges d\'entrada';
$labels['filtername'] = 'Nom del filtre';
$labels['newfilter'] = 'Filtre Nou';
$labels['filteradd'] = 'Afegeix un filtre';
$labels['filterdel'] = 'Suprimeix el filtre';
$labels['moveup'] = 'Mou amunt';
$labels['movedown'] = 'Mou avall';
$labels['filterallof'] = 'que coincideixi amb totes les regles següents';
-$labels['filteranyof'] = 'que no coincideixi amb cap de les regles següents';
+$labels['filteranyof'] = 'que coincideixi amb qualsevol de les regles següents';
$labels['filterany'] = 'tots els missatges';
$labels['filtercontains'] = 'conté';
$labels['filternotcontains'] = 'no conté';
$labels['filteris'] = 'és igual a';
$labels['filterisnot'] = 'és diferent de';
$labels['filterexists'] = 'existeix';
$labels['filternotexists'] = 'no existeix';
$labels['filtermatches'] = 'coincideix amb l\'expressió';
$labels['filternotmatches'] = 'no coincideix amb l\'expressió';
$labels['filterregex'] = 'coincideix amb l\'expressió regular';
$labels['filternotregex'] = 'no coincideix amb l\'expressió regular';
$labels['filterunder'] = 'sota';
$labels['filterover'] = 'sobre';
$labels['addrule'] = 'Afegeix una regla';
$labels['delrule'] = 'Suprimeix una regla';
$labels['messagemoveto'] = 'Mou el missatge a';
$labels['messageredirect'] = 'Redirigeix el missatge cap a';
$labels['messagecopyto'] = 'Copia el missatge a';
$labels['messagesendcopy'] = 'Envia una còpia del missatge a';
$labels['messagereply'] = 'Respon amb un missatge';
$labels['messagedelete'] = 'Suprimeix missatge';
$labels['messagediscard'] = 'Descarta amb un missatge';
$labels['messagekeep'] = 'Deixa el missatge a la bústia';
$labels['messagesrules'] = 'Pels missatges entrants:';
$labels['messagesactions'] = '..executa les següents accions:';
$labels['add'] = 'Afegeix';
$labels['del'] = 'Suprimeix';
$labels['sender'] = 'Remitent';
$labels['recipient'] = 'Destinatari';
$labels['vacationaddr'] = 'Les meves adreces de correu addicionals:';
$labels['vacationdays'] = 'Cada quan enviar un missatge (en dies):';
$labels['vacationinterval'] = 'Amb quina freqüència s\'han d\'enviar els missatges:';
-$labels['days'] = 'dies';
-$labels['seconds'] = 'segons';
$labels['vacationreason'] = 'Cos del missatge (raó de l\'absència):';
$labels['vacationsubject'] = 'Assumpte del missatge:';
+$labels['days'] = 'dies';
+$labels['seconds'] = 'segons';
$labels['rulestop'] = 'Deixa d\'avaluar regles';
$labels['enable'] = 'Habilita/Deshabilita';
$labels['filterset'] = 'Conjunt de filtres';
$labels['filtersets'] = 'Conjunts de filtres';
$labels['filtersetadd'] = 'Afegeix un conjunt de filtres';
$labels['filtersetdel'] = 'Suprimeix el conjunt de filtres actual';
$labels['filtersetact'] = 'Activa el conjunt de filtres actual';
$labels['filtersetdeact'] = 'Desactiva el conjunt de filtres actual';
$labels['filterdef'] = 'Definició del filtre';
$labels['filtersetname'] = 'Nom del conjunt de filtres';
$labels['newfilterset'] = 'Nou conjunt de filtres';
$labels['active'] = 'actiu';
$labels['none'] = 'cap';
$labels['fromset'] = 'des del conjunt';
$labels['fromfile'] = 'des del fitxer';
$labels['filterdisabled'] = 'Filtre deshabilitat';
$labels['countisgreaterthan'] = 'el recompte és més gran de';
$labels['countisgreaterthanequal'] = 'el recompte és més gran o igual a';
$labels['countislessthan'] = 'el recompte és menor de';
$labels['countislessthanequal'] = 'el recompte és menor o igual a';
$labels['countequals'] = 'el recompte és igual que';
$labels['countnotequals'] = 'el recompte és diferent de';
$labels['valueisgreaterthan'] = 'el valor és més gran de';
$labels['valueisgreaterthanequal'] = 'el valor és major o igual que';
$labels['valueislessthan'] = 'el valor és menor que';
$labels['valueislessthanequal'] = 'el valor és menor o igual de';
$labels['valueequals'] = 'el valor és igual a';
$labels['valuenotequals'] = 'el valor és diferent de';
$labels['setflags'] = 'Posa indicadors al missatge';
$labels['addflags'] = 'Afegeix indicadors al missatge';
$labels['removeflags'] = 'Suprimeix indicadors del missatge';
$labels['flagread'] = 'Llegit';
$labels['flagdeleted'] = 'Suprimit';
$labels['flaganswered'] = 'Respost';
$labels['flagflagged'] = 'Marcat';
$labels['flagdraft'] = 'Esborrany';
$labels['setvariable'] = 'Ajusta la variable';
$labels['setvarname'] = 'Nom de la variable:';
$labels['setvarvalue'] = 'Valor de la variable:';
$labels['setvarmodifiers'] = 'Modificadors:';
$labels['varlower'] = 'minúscules';
$labels['varupper'] = 'majúscules';
$labels['varlowerfirst'] = 'el primer caràcter en minúscula';
$labels['varupperfirst'] = 'el primer caràcter en majúscula';
$labels['varquotewildcard'] = 'engloba els caràcters especials amb cometes';
$labels['varlength'] = 'llargada';
$labels['notify'] = 'Envia notificació';
-$labels['notifyaddress'] = 'A l\'adreça de correu electrònic:';
-$labels['notifybody'] = 'Cos de la notificació:';
-$labels['notifysubject'] = 'Assumpte de la notificació:';
-$labels['notifyfrom'] = 'Remitent de la notificació:';
+$labels['notifytarget'] = 'Objectiu de la notificació:';
+$labels['notifymessage'] = 'Missatge de notificació (opcional):';
+$labels['notifyoptions'] = 'Opcions de notificació (opcional):';
+$labels['notifyfrom'] = 'Remitent de la notificació (opcional):';
$labels['notifyimportance'] = 'Importància:';
$labels['notifyimportancelow'] = 'baixa';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'alta';
+$labels['notifymethodmailto'] = 'Correu electrònic';
+$labels['notifymethodtel'] = 'Telèfon';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Crea filtre';
$labels['usedata'] = 'Fes servir les següents dades al filtre:';
$labels['nextstep'] = 'Següent pas';
$labels['...'] = '...';
$labels['currdate'] = 'Data actual';
$labels['datetest'] = 'Data';
$labels['dateheader'] = 'capçalera:';
$labels['year'] = 'any';
$labels['month'] = 'mes';
$labels['day'] = 'dia';
$labels['date'] = 'data (aaaa-mm-dd)';
$labels['julian'] = 'data (calendari julià)';
$labels['hour'] = 'hora';
$labels['minute'] = 'minut';
$labels['second'] = 'segon';
$labels['time'] = 'hora (hh:mm:ss)';
$labels['iso8601'] = 'data (ISO8601)';
$labels['std11'] = 'data (RFC2822)';
$labels['zone'] = 'fus horari';
$labels['weekday'] = 'dia de la setmana (0-6)';
$labels['advancedopts'] = 'Opcions avançades';
$labels['body'] = 'Cos';
$labels['address'] = 'adreça';
$labels['envelope'] = 'sobre';
$labels['modifier'] = 'modificador:';
$labels['text'] = 'text';
$labels['undecoded'] = 'descodificat (en brut)';
$labels['contenttype'] = 'tipus de contingut';
$labels['modtype'] = 'tipus:';
$labels['allparts'] = 'tots';
$labels['domain'] = 'domini';
$labels['localpart'] = 'part local';
$labels['user'] = 'usuari';
$labels['detail'] = 'detall';
$labels['comparator'] = 'comparador:';
$labels['default'] = 'per defecte';
$labels['octet'] = 'estricte (octet)';
$labels['asciicasemap'] = 'No distingeix entre majúscules i minúscules (ascii-casemap)';
$labels['asciinumeric'] = 'numèric (ascii-numeric)';
$labels['index'] = 'índex:';
$labels['indexlast'] = 'cap enrere';
+$labels['vacation'] = 'Vacances';
+$labels['vacation.reply'] = 'Missatge de resposta';
+$labels['vacation.advanced'] = 'Paràmetres avançats';
+$labels['vacation.subject'] = 'Assumpte';
+$labels['vacation.body'] = 'Cos';
+$labels['vacation.start'] = 'Inici de vacances';
+$labels['vacation.end'] = 'Finalització de vacances';
+$labels['vacation.status'] = 'Estat';
+$labels['vacation.on'] = 'Activat';
+$labels['vacation.off'] = 'Desactivat';
+$labels['vacation.addresses'] = 'Les meves adreces addicionals:';
+$labels['vacation.interval'] = 'Interval de resposta';
+$labels['vacation.after'] = 'Posa la regla de vacances després';
+$labels['vacation.saving'] = 'S\'estan desant les dades...';
+$labels['vacation.action'] = 'Acció pel missatge entrant';
+$labels['vacation.keep'] = 'Conserva';
+$labels['vacation.discard'] = 'Descarta';
+$labels['vacation.redirect'] = 'Redirigeix cap a';
+$labels['vacation.copy'] = 'Envia còpia a';
+$labels['arialabelfiltersetactions'] = 'Accions pel conjunt de filtres';
+$labels['arialabelfilteractions'] = 'Accions del filtre';
+$labels['arialabelfilterform'] = 'Propietats del filtre';
+$labels['ariasummaryfilterslist'] = 'Llistat de filtres';
+$labels['ariasummaryfiltersetslist'] = 'Llistat de conjunts de filtres';
+$labels['filterstitle'] = 'Edita els filtres pels missatges entrants';
+$labels['vacationtitle'] = 'Edita la norma "fora de l\'oficina"';
$messages['filterunknownerror'] = 'Error desconegut al servidor.';
$messages['filterconnerror'] = 'No s\'ha pogut connectar al servidor.';
$messages['filterdeleteerror'] = 'No s\'ha pogut suprimir el filtre. Hi ha hagut un error al servidor.';
$messages['filterdeleted'] = 'El filtre s\'ha suprimit correctament.';
$messages['filtersaved'] = 'El filtre s\'ha desat correctament.';
$messages['filtersaveerror'] = 'No s\'ha pogut desar el filtre. Hi ha hagut un error al servidor.';
$messages['filterdeleteconfirm'] = 'Esteu segurs de voler suprimir el filtre seleccionat?';
$messages['ruledeleteconfirm'] = 'Esteu segurs que voleu suprimir la regla seleccionada?';
$messages['actiondeleteconfirm'] = 'Esteu segurs que voleu suprimir l\'acció seleccionada?';
$messages['forbiddenchars'] = 'El camp conté caràcters prohibits.';
$messages['cannotbeempty'] = 'El camp no pot estar buit.';
$messages['ruleexist'] = 'Ja existeix un filtre amb aquest nom.';
$messages['setactivateerror'] = 'No s\'ha pogut activar el conjunt de filtres seleccionat. Hi ha hagut un error al servidor.';
$messages['setdeactivateerror'] = 'No s\'ha pogut desactivar el conjunt de filtres seleccionat. Hi ha hagut un error al servidor.';
$messages['setdeleteerror'] = 'No s\'ha pogut suprimir el conjunt de filtres seleccionat. Hi ha hagut un error al servidor.';
$messages['setactivated'] = 'El conjunt de filtres s\'ha activat correctament.';
$messages['setdeactivated'] = 'El conjunt de filtres s\'ha desactivat correctament.';
$messages['setdeleted'] = 'El conjunt de filtres s\'ha suprimit correctament.';
$messages['setdeleteconfirm'] = 'Esteu segurs que voleu suprimir el conjunt de filtres seleccionats?';
$messages['setcreateerror'] = 'No s\'ha pogut crear el conjunt de filtres. Hi ha hagut un error al servidor.';
$messages['setcreated'] = 'S\'ha creat correctament el conjunt de filtres.';
$messages['activateerror'] = 'No s\'ha pogut habilitar el(s) filtre(s) seleccionat(s). Hi ha hagut un error al servidor.';
$messages['deactivateerror'] = 'No s\'ha pogut deshabilitar el(s) filtre(s) seleccionat(s). Hi ha hagut un error al servidor.';
$messages['deactivated'] = 'Filtre(s) deshabilitat(s) correctament.';
$messages['activated'] = 'Filtre(s) habilitat(s) correctament.';
$messages['moved'] = 'S\'ha mogut correctament el filtre.';
$messages['moveerror'] = 'No s\'ha pogut moure el filtre seleccionat. Hi ha hagut un error al servidor.';
$messages['nametoolong'] = 'El nom és massa llarg.';
$messages['namereserved'] = 'Nom reservat.';
$messages['setexist'] = 'El conjunt ja existeix.';
$messages['nodata'] = 'S\'ha de seleccionar com a mínim una posició!';
$messages['invaliddateformat'] = 'data no vàlida o format no vàlid';
+$messages['saveerror'] = 'No s\'han pogut desar les dades. Hi ha hagut un error al servidor.';
+$messages['vacationsaved'] = 'Les dades de les vacances s\'han desat correctament.';
+$messages['emptyvacationbody'] = 'És obligatori definir el cos del missatge de vacances';
?>
diff --git a/lib/plugins/managesieve/localization/cs_CZ.inc b/lib/plugins/managesieve/localization/cs_CZ.inc
index d304da6..6db6bac 100644
--- a/lib/plugins/managesieve/localization/cs_CZ.inc
+++ b/lib/plugins/managesieve/localization/cs_CZ.inc
@@ -1,192 +1,218 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtry';
$labels['managefilters'] = 'Nastavení filtrů';
$labels['filtername'] = 'Název filtru';
$labels['newfilter'] = 'Nový filtr';
$labels['filteradd'] = 'Přidej filtr';
$labels['filterdel'] = 'Smaž filtr';
$labels['moveup'] = 'Posunout nahoru';
$labels['movedown'] = 'Posunout dolů';
$labels['filterallof'] = 'Odpovídají všechny pravidla';
$labels['filteranyof'] = 'Odpovídá kterékoliv pravidlo';
$labels['filterany'] = 'Všechny zprávy';
$labels['filtercontains'] = 'obsahuje';
$labels['filternotcontains'] = 'neobsahuje';
$labels['filteris'] = 'odpovídá';
$labels['filterisnot'] = 'neodpovídá';
$labels['filterexists'] = 'existuje';
$labels['filternotexists'] = 'neexistuje';
$labels['filtermatches'] = 'odpovídá výrazu';
$labels['filternotmatches'] = 'neodpovídá výrazu';
$labels['filterregex'] = 'odpovídá regulárnímu výrazu';
$labels['filternotregex'] = 'neodpovídá regulárnímu výrazu';
$labels['filterunder'] = 'pod';
$labels['filterover'] = 'nad';
$labels['addrule'] = 'Přidej pravidlo';
$labels['delrule'] = 'Smaž pravidlo';
$labels['messagemoveto'] = 'Přesuň zprávu do';
$labels['messageredirect'] = 'Přeposlat zprávu na';
$labels['messagecopyto'] = 'Zkopírovat zprávu do';
$labels['messagesendcopy'] = 'Odeslat kopii zprávy na';
$labels['messagereply'] = 'Odpovědět se zprávou';
$labels['messagedelete'] = 'Smazat zprávu';
$labels['messagediscard'] = 'Smazat se zprávou';
$labels['messagekeep'] = 'Ponechat zprávu v doručené poště';
$labels['messagesrules'] = 'Pravidla pro příchozí zprávu:';
$labels['messagesactions'] = '...vykonej následující akce:';
$labels['add'] = 'Přidej';
$labels['del'] = 'Smaž';
$labels['sender'] = 'Odesílatel';
$labels['recipient'] = 'Příjemce';
$labels['vacationaddr'] = 'Moje další e-mailová adresa(y):';
$labels['vacationdays'] = 'Počet dnů mezi automatickými odpověďmi:';
$labels['vacationinterval'] = 'Prodleva mezi automatickými odpověďmi:';
-$labels['days'] = 'dnů';
-$labels['seconds'] = 'sekund';
$labels['vacationreason'] = 'Zpráva (Důvod nepřítomnosti):';
$labels['vacationsubject'] = 'Předmět zprávy:';
+$labels['days'] = 'dnů';
+$labels['seconds'] = 'sekund';
$labels['rulestop'] = 'Zastavit pravidla';
$labels['enable'] = 'Zapnout/Vypnout';
$labels['filterset'] = 'Sada filtrů';
$labels['filtersets'] = 'Sady filtrů';
$labels['filtersetadd'] = 'Přidat sadu filtrů';
$labels['filtersetdel'] = 'Odebrat tuto sadu filtrů';
$labels['filtersetact'] = 'Activate current filters set';
$labels['filtersetdeact'] = 'Vypnout tuto sadu filtrů';
$labels['filterdef'] = 'Definice filtru';
$labels['filtersetname'] = 'Nastavit název sady filtrů';
$labels['newfilterset'] = 'Nová sada filtrů';
$labels['active'] = 'aktivní';
$labels['none'] = 'nic';
$labels['fromset'] = 'ze sady';
$labels['fromfile'] = 'ze souboru';
$labels['filterdisabled'] = 'Filtr neaktivní';
$labels['countisgreaterthan'] = 'počet je větší než';
$labels['countisgreaterthanequal'] = 'počet je větší nebo roven';
$labels['countislessthan'] = 'počet je nižší než';
$labels['countislessthanequal'] = 'počet je nižší nebo roven';
$labels['countequals'] = 'počet je roven';
$labels['countnotequals'] = 'počet není roven';
$labels['valueisgreaterthan'] = 'hodnota je větší než';
$labels['valueisgreaterthanequal'] = 'hodnota je větší nebo stejná jako';
$labels['valueislessthan'] = 'hodnota je nižší než';
$labels['valueislessthanequal'] = 'hodnota je nižší nebo stejná jako';
$labels['valueequals'] = 'hodnota odpovídá';
$labels['valuenotequals'] = 'hodnota neodpovídá';
$labels['setflags'] = 'Nastavit vlajky u zprávy';
$labels['addflags'] = 'Přidat vlajky ke zprávě';
$labels['removeflags'] = 'Odstranit vlajky ze zprávy';
$labels['flagread'] = 'Přečteno';
$labels['flagdeleted'] = 'Smazáno';
$labels['flaganswered'] = 'Odpovězené';
$labels['flagflagged'] = 'Označeno';
$labels['flagdraft'] = 'Koncept';
$labels['setvariable'] = 'Nastavit proměnnou';
$labels['setvarname'] = 'Název proměnné:';
$labels['setvarvalue'] = 'Hodnota proměnné:';
$labels['setvarmodifiers'] = 'Modifikátory:';
$labels['varlower'] = 'malá písmena';
$labels['varupper'] = 'velká písmena';
$labels['varlowerfirst'] = 'první písmeno malé';
$labels['varupperfirst'] = 'první písmeno velké';
$labels['varquotewildcard'] = 'uvodit speciální znaky uvozovkama';
$labels['varlength'] = 'délka';
$labels['notify'] = 'Odeslat oznámení';
-$labels['notifyaddress'] = 'Na emailovou adresu:';
-$labels['notifybody'] = 'Zpráva oznámení:';
-$labels['notifysubject'] = 'Předmět oznámení:';
-$labels['notifyfrom'] = 'Odesílatel oznámení:';
+$labels['notifytarget'] = 'Cíl oznámení:';
+$labels['notifymessage'] = 'Zpráva oznámení (nepovinné):';
+$labels['notifyoptions'] = 'Možnosti oznámení (nepovinné):';
+$labels['notifyfrom'] = 'Odesílatel oznámení (nepovinné):';
$labels['notifyimportance'] = 'Důležitost:';
$labels['notifyimportancelow'] = 'nízká';
$labels['notifyimportancenormal'] = 'normální';
$labels['notifyimportancehigh'] = 'vysoká';
+$labels['notifymethodmailto'] = 'E-mail';
+$labels['notifymethodtel'] = 'Telefon';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Vytvořit filtr';
$labels['usedata'] = 'Použít následující údaje ve filtru:';
$labels['nextstep'] = 'Další krok';
$labels['...'] = '...';
$labels['currdate'] = 'Aktuální datum';
$labels['datetest'] = 'Datum';
$labels['dateheader'] = 'hlavička:';
$labels['year'] = 'rok';
$labels['month'] = 'měsíc';
$labels['day'] = 'den';
$labels['date'] = 'datum (rrrr-mm-dd)';
$labels['julian'] = 'datum (juliánské)';
$labels['hour'] = 'hodina';
$labels['minute'] = 'minuta';
$labels['second'] = 'sekunda';
$labels['time'] = 'čas (hh:mm:ss)';
$labels['iso8601'] = 'datum (ISO8601)';
$labels['std11'] = 'datum (RFC2822)';
$labels['zone'] = 'časová zóna';
$labels['weekday'] = 'všední den (0-6)';
$labels['advancedopts'] = 'Pokročilá nastavení';
$labels['body'] = 'Tělo';
$labels['address'] = 'adresa';
$labels['envelope'] = 'obálka';
$labels['modifier'] = 'měnič:';
$labels['text'] = 'text';
$labels['undecoded'] = 'nedekódované (surové)';
$labels['contenttype'] = 'typ obsahu';
$labels['modtype'] = 'typ:';
$labels['allparts'] = 'vše';
$labels['domain'] = 'doména';
$labels['localpart'] = 'místní část';
$labels['user'] = 'uživatel';
$labels['detail'] = 'detail';
$labels['comparator'] = 'porovnávač:';
$labels['default'] = 'výchozí';
$labels['octet'] = 'striktní (oktet)';
$labels['asciicasemap'] = 'necitlivé na velikost písmen (ascii-casemap)';
$labels['asciinumeric'] = 'číslené (ascii-numeric)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'pozpátku';
+$labels['vacation'] = 'Dovolená';
+$labels['vacation.reply'] = 'Odpověd';
+$labels['vacation.advanced'] = 'Pokročilá nastavení';
+$labels['vacation.subject'] = 'Předmět';
+$labels['vacation.body'] = 'Tělo';
+$labels['vacation.start'] = 'Začátek dovolené';
+$labels['vacation.end'] = 'Konec dovolené';
+$labels['vacation.status'] = 'Stav';
+$labels['vacation.on'] = 'Zapnuto';
+$labels['vacation.off'] = 'Vypnuto';
+$labels['vacation.addresses'] = 'Moje další adresy:';
+$labels['vacation.interval'] = 'Doba mezi odpověďmi';
+$labels['vacation.after'] = 'Uložit pravidlo o dovolené za';
+$labels['vacation.saving'] = 'Ukládám data...';
+$labels['vacation.keep'] = 'Zachovat';
+$labels['vacation.discard'] = 'Zrušit';
+$labels['vacation.copy'] = 'Odeslat kopii zprávy na';
+$labels['arialabelfilterform'] = 'Vlastnosti filtru';
+$labels['ariasummaryfilterslist'] = 'Seznam filtrů';
+$labels['ariasummaryfiltersetslist'] = 'Seznam sad filtrů';
$messages['filterunknownerror'] = 'Neznámá chyba serveru';
$messages['filterconnerror'] = 'Nebylo možné se připojit k sieve serveru';
$messages['filterdeleteerror'] = 'Nebylo možné smazat filtr. Došlo k chybě serveru.';
$messages['filterdeleted'] = 'Filtr byl smazán';
$messages['filtersaved'] = 'Filtr byl uložen';
$messages['filtersaveerror'] = 'Nebylo možné uložit filtr. Došlo k chybě serveru.';
$messages['filterdeleteconfirm'] = 'Opravdu chcete smazat vybraný filtr?';
$messages['ruledeleteconfirm'] = 'Jste si jisti, že chcete smazat vybrané pravidlo?';
$messages['actiondeleteconfirm'] = 'Jste si jisti, že chcete smazat vybranou akci?';
$messages['forbiddenchars'] = 'Zakázané znaky v poli';
$messages['cannotbeempty'] = 'Pole nemůže být prázdné';
$messages['ruleexist'] = 'Filtr s uvedeným názvem již existuje.';
$messages['setactivateerror'] = 'Nelze zapnout vybranou sadu filtrů. Došlo k chybě serveru.';
$messages['setdeactivateerror'] = 'Nelze vypnout vybranou sadu filtrů. Došlo k chybě serveru.';
$messages['setdeleteerror'] = 'Nelze odstranit vybranou sadu filtrů. Došlo k chybě serveru.';
$messages['setactivated'] = 'Sada filtrů úspěšně zapnuta.';
$messages['setdeactivated'] = 'Sada filtrů úspěšně vypnuta.';
$messages['setdeleted'] = 'Sada filtrů úspěšně odstraněna.';
$messages['setdeleteconfirm'] = 'Opravdu si přejete odebrat vybranou sadu filtrů.';
$messages['setcreateerror'] = 'Nelze vytvořit sadu filtrů. Došlo k chybě serveru.';
$messages['setcreated'] = 'Sada filtrů úspěšně vytvořena.';
$messages['activateerror'] = 'Nelze zapnout vybrané filtr/y. Došlo k chybě serveru.';
$messages['deactivateerror'] = 'Nelze vypnout vybrané filtr/y. Došlo k chybě serveru.';
-$messages['deactivated'] = 'Filtr/y úspěšne vypnuty.';
+$messages['deactivated'] = 'Filtr(y) úspěšně vypnuty.';
$messages['activated'] = 'Filtr/y úspěšně zapnuty.';
$messages['moved'] = 'Filtr byl úspěšně přesunut.';
$messages['moveerror'] = 'Nelze přesunout vybraný filtr. Došlo k chybě serveru.';
$messages['nametoolong'] = 'Příliš dlouhý název.';
$messages['namereserved'] = 'Vyhrazený název.';
$messages['setexist'] = 'Sada již existuje.';
$messages['nodata'] = 'Musí být vybrána minimálně jedna pozice!';
$messages['invaliddateformat'] = 'Neplatné datum nebo část data';
+$messages['saveerror'] = 'Nebylo možné uložit data. Došlo k chybě serveru.';
+$messages['vacationsaved'] = 'Data o dovolené byla uložena.';
+$messages['emptyvacationbody'] = 'Tělo zprávy';
?>
diff --git a/lib/plugins/managesieve/localization/cy_GB.inc b/lib/plugins/managesieve/localization/cy_GB.inc
index a10205d..8d8d1a5 100644
--- a/lib/plugins/managesieve/localization/cy_GB.inc
+++ b/lib/plugins/managesieve/localization/cy_GB.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Hidlyddion';
$labels['managefilters'] = 'Rheoli hidlyddion ebost i fewn';
$labels['filtername'] = 'Enw hidlydd';
$labels['newfilter'] = 'Hidlydd newydd';
$labels['filteradd'] = 'Ychwanegu hidlydd';
$labels['filterdel'] = 'Dileu hidlydd';
$labels['moveup'] = 'Symud i fyny';
$labels['movedown'] = 'Symud i lawr';
$labels['filterallof'] = 'sy\'n cyfateb i\'r holl reolau canlynol';
$labels['filteranyof'] = 'sy\'n cyfateb i unrhyw un i\'r rheolau canlynol';
$labels['filterany'] = 'pob neges';
$labels['filtercontains'] = 'yn cynnwys';
$labels['filternotcontains'] = 'ddim yn cynnwys';
$labels['filteris'] = 'yn hafal i';
$labels['filterisnot'] = 'ddim yn hafal i';
$labels['filterexists'] = 'yn bodoli';
$labels['filternotexists'] = 'ddim yn bodoli';
$labels['filtermatches'] = 'yn cyfateb i\'r mynegiant';
$labels['filternotmatches'] = 'ddim yn cyfateb i\'r mynegiant';
$labels['filterregex'] = 'yn cyfateb i\'r mynegiant rheolaidd';
$labels['filternotregex'] = 'ddim yn cyfateb i\'r mynegiant rheolaidd';
$labels['filterunder'] = 'o dan';
$labels['filterover'] = 'dros';
$labels['addrule'] = 'Ychwanegu rheol';
$labels['delrule'] = 'Dileu rheol';
$labels['messagemoveto'] = 'Symud neges i';
$labels['messageredirect'] = 'Ail-gyfeirio neges i';
$labels['messagecopyto'] = 'Copio neges i';
$labels['messagesendcopy'] = 'Danfon copi o\'r neges i';
$labels['messagereply'] = 'Ymateb gyda\'r neges';
$labels['messagedelete'] = 'Dileu neges';
$labels['messagediscard'] = 'Gwaredu gyda neges';
$labels['messagekeep'] = 'Cadw\'r neges yn y Mewnflwch';
$labels['messagesrules'] = 'Ar gyfer ebost i fewn:';
$labels['messagesactions'] = '...rhedeg y gweithredoedd canlynol:';
$labels['add'] = 'Ychwanegu';
$labels['del'] = 'Dileu';
$labels['sender'] = 'Anfonwr';
$labels['recipient'] = 'Derbynnwr';
$labels['vacationaddr'] = 'Fy nghyfeiriad(au) ebost ychwanegol:';
$labels['vacationdays'] = 'Pa mor aml i ddanfon negeseuon (mewn dyddiau):';
$labels['vacationinterval'] = 'Pa mor aml i ddanfon negeseuon:';
-$labels['days'] = 'dyddiau';
-$labels['seconds'] = 'eiliadau';
$labels['vacationreason'] = 'Corff neges (rheswm ar wyliau):';
$labels['vacationsubject'] = 'Pwnc neges:';
+$labels['days'] = 'dyddiau';
+$labels['seconds'] = 'eiliadau';
$labels['rulestop'] = 'Stopio gwerthuso rheolau';
$labels['enable'] = 'Galluogi/Analluogi';
$labels['filterset'] = 'Set hidlyddion';
$labels['filtersets'] = 'Setiau hidlyddion';
$labels['filtersetadd'] = 'Ychwanegu set hidlyddion';
$labels['filtersetdel'] = 'Dileu set hidlyddion cyfredol';
$labels['filtersetact'] = 'Dileu set hidlyddion gweithredol';
$labels['filtersetdeact'] = 'Analluogi set hidlyddion cyfredol';
$labels['filterdef'] = 'Diffiniad hidlydd';
$labels['filtersetname'] = 'Enw set hidlyddion';
$labels['newfilterset'] = 'Set hidlyddion newydd';
$labels['active'] = 'gweithredol';
$labels['none'] = 'dim';
$labels['fromset'] = 'o set';
$labels['fromfile'] = 'o ffeil';
$labels['filterdisabled'] = 'Analluogwyd hidlydd';
$labels['countisgreaterthan'] = 'rhif yn fwy na';
$labels['countisgreaterthanequal'] = 'rhif yn fwy na neu hafal i';
$labels['countislessthan'] = 'rhif yn llai na';
$labels['countislessthanequal'] = 'rhif yn llai na neu hafal i';
$labels['countequals'] = 'rhif yn hafal i';
$labels['countnotequals'] = 'rhif ddim yn hafal i';
$labels['valueisgreaterthan'] = 'gwerth yn fwy na';
$labels['valueisgreaterthanequal'] = 'gwerth yn fwy na neu hafal i';
$labels['valueislessthan'] = 'gwerth yn llai na';
$labels['valueislessthanequal'] = 'gwerth yn llai neu hafal i';
$labels['valueequals'] = 'gwerth yn hafal i';
$labels['valuenotequals'] = 'gwerth ddim yn hafal i';
$labels['setflags'] = 'Rhoi fflag ar y neges';
$labels['addflags'] = 'Ychwanegu fflag i\'r neges';
$labels['removeflags'] = 'Dileu fflag o\'r neges';
$labels['flagread'] = 'Darllen';
$labels['flagdeleted'] = 'Dilewyd';
$labels['flaganswered'] = 'Atebwyd';
$labels['flagflagged'] = 'Nodwyd';
$labels['flagdraft'] = 'Drafft';
$labels['setvariable'] = 'Gosod newidyn';
$labels['setvarname'] = 'Enw newidyn:';
$labels['setvarvalue'] = 'Gwerth newidyn:';
$labels['setvarmodifiers'] = 'Addasydd:';
$labels['varlower'] = 'llythrennau bychain';
$labels['varupper'] = 'priflythrennau';
$labels['varlowerfirst'] = 'llythyren gyntaf yn fach';
$labels['varupperfirst'] = 'llythyren gyntaf yn briflythyren';
$labels['varquotewildcard'] = 'dyfynnu nodau arbennig';
$labels['varlength'] = 'hyd';
$labels['notify'] = 'Anfon hysbysiad';
-$labels['notifyaddress'] = 'I gyfeiriad ebost:';
-$labels['notifybody'] = 'Corff hysbysiad:';
-$labels['notifysubject'] = 'Pwnc hysbysiad:';
-$labels['notifyfrom'] = 'Anfonwr hysbysiad:';
+$labels['notifytarget'] = 'Target hysbysu:';
+$labels['notifymessage'] = 'Neges hysbysu (dewisol):';
+$labels['notifyoptions'] = 'Dewisiadau hysbysu (dewisol):';
+$labels['notifyfrom'] = 'Anfonwr hysbysiad (dewisol):';
$labels['notifyimportance'] = 'Pwysigrwydd:';
$labels['notifyimportancelow'] = 'isel';
$labels['notifyimportancenormal'] = 'arferol';
$labels['notifyimportancehigh'] = 'uchel';
+$labels['notifymethodmailto'] = 'Ebost';
+$labels['notifymethodtel'] = 'Ffôn';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Creu hidlydd';
$labels['usedata'] = 'Defnyddio\'r wybodaeth ganlynol yn yr hidlydd:';
$labels['nextstep'] = 'Cam nesaf';
$labels['...'] = '...';
$labels['currdate'] = 'Dyddiad cyfredol';
$labels['datetest'] = 'Dyddiad';
$labels['dateheader'] = 'pennawd:';
$labels['year'] = 'blwyddyn';
$labels['month'] = 'mis';
$labels['day'] = 'dydd';
$labels['date'] = 'dyddiad (bbbb-mm-dd)';
$labels['julian'] = 'dyddiad (julian)';
$labels['hour'] = 'awr';
$labels['minute'] = 'munud';
$labels['second'] = 'eiliad';
$labels['time'] = 'amser (aa:mm:ee)';
$labels['iso8601'] = 'dyddiad (ISO8601)';
$labels['std11'] = 'dyddiad (RFC2822)';
$labels['zone'] = 'parth-amser';
$labels['weekday'] = 'dydd yr wythnos (0-6)';
$labels['advancedopts'] = 'Dewisiadau uwch';
$labels['body'] = 'Corff';
$labels['address'] = 'cyfeiriad';
$labels['envelope'] = 'amlen';
$labels['modifier'] = 'newidydd:';
$labels['text'] = 'testun';
$labels['undecoded'] = 'heb ei ddatgodi (amrwd)';
$labels['contenttype'] = 'math cynnwys';
$labels['modtype'] = 'math:';
$labels['allparts'] = 'popeth';
$labels['domain'] = 'parth';
$labels['localpart'] = 'darn lleol';
$labels['user'] = 'defnyddiwr';
$labels['detail'] = 'manylion';
$labels['comparator'] = 'cymharydd';
$labels['default'] = 'rhagosodiad';
$labels['octet'] = 'llym (octet)';
$labels['asciicasemap'] = 'maint llythrennau (ascii-casemap)';
$labels['asciinumeric'] = 'rhifau (ascii-numeric)';
$labels['index'] = 'mynegai:';
$labels['indexlast'] = 'o chwith';
+$labels['vacation'] = 'Gwyliau';
+$labels['vacation.reply'] = 'Neges ymateb';
+$labels['vacation.advanced'] = 'Gosodiadau uwch';
+$labels['vacation.subject'] = 'Pwnc';
+$labels['vacation.body'] = 'Corff';
+$labels['vacation.start'] = 'Dechrau gwyliau';
+$labels['vacation.end'] = 'Diwedd gwyliau';
+$labels['vacation.status'] = 'Statws';
+$labels['vacation.on'] = 'Ymlaen';
+$labels['vacation.off'] = 'I ffwrdd';
+$labels['vacation.addresses'] = 'Fy nghyfeiriadau ychwanegol';
+$labels['vacation.interval'] = 'Cyfnod ymateb';
+$labels['vacation.after'] = 'Rhoi rheol gwyliau ar ôl';
+$labels['vacation.saving'] = 'Yn cadw\'r data...';
+$labels['vacation.action'] = 'Gweithred neges i fewn';
+$labels['vacation.keep'] = 'Cadw';
+$labels['vacation.discard'] = 'Gwaredu';
+$labels['vacation.redirect'] = 'Ailgyfeirio i';
+$labels['vacation.copy'] = 'Danfon copi i';
+$labels['arialabelfiltersetactions'] = 'Gweithrediadau set hidlydd';
+$labels['arialabelfilteractions'] = 'Gweithrediadau hidlydd';
+$labels['arialabelfilterform'] = 'Nodweddion hidlydd';
+$labels['ariasummaryfilterslist'] = 'Rhestr o hidlyddion';
+$labels['ariasummaryfiltersetslist'] = 'Rhestr o setiau hidlyddion';
+$labels['filterstitle'] = 'Golygu hidlyddion ebost i fewn';
+$labels['vacationtitle'] = 'Golygu rheol allan-o\'r-swyddfa';
$messages['filterunknownerror'] = 'Gwall gweinydd anhysbys.';
$messages['filterconnerror'] = 'Methwyd cysylltu a\'r gweinydd.';
$messages['filterdeleteerror'] = 'Methwyd dileu hidlydd. Cafwydd gwall gweinydd.';
$messages['filterdeleted'] = 'Dilëuwyd hidlydd yn llwyddiannus.';
$messages['filtersaved'] = 'Cadwyd hidlydd yn llwyddiannus.';
$messages['filtersaveerror'] = 'Methwyd cadw hidlydd. Cafwyd gwall gweinydd.';
$messages['filterdeleteconfirm'] = 'Ydych chi wir am ddileu yr hidlydd ddewiswyd?';
$messages['ruledeleteconfirm'] = 'Ydych chi\'n siwr eich bod am ddileu\'r rheol ddewiswyd?';
$messages['actiondeleteconfirm'] = 'Ydych chi\'n siwr eich bod am ddileu\'r weithred ddewiswyd?';
$messages['forbiddenchars'] = 'Llythrennau gwaharddedig yn y maes.';
$messages['cannotbeempty'] = 'Ni all y maes fod yn wag.';
$messages['ruleexist'] = 'Mae hidlydd gyda\'r enw yma yn bodoli\'n barod.';
$messages['setactivateerror'] = 'Methwyd galluogi y hidlyddion dewiswyd. Cafwyd gwall gweinydd.';
$messages['setdeactivateerror'] = 'Methwyd analluogi y hidlyddion dewiswyd. Cafwyd gwall gweinydd.';
$messages['setdeleteerror'] = 'Methwyd dileu y set hidlyddion ddewiswyd. Cafwyd gwall gweinydd.';
$messages['setactivated'] = 'Bywiogwyd y set hidlydd yn llwyddiannus.';
$messages['setdeactivated'] = 'Dadfywiogwyd y set hidlydd yn llwyddiannus.';
$messages['setdeleted'] = 'Dilëuwyd y set hidlydd yn llwyddiannus.';
$messages['setdeleteconfirm'] = 'Ydych chi\'n siwr eich bod am ddileu\'r set hidlydd ddewiswyd?';
$messages['setcreateerror'] = 'Methwyd creu set hidlydd. Cafwyd gwall gweinydd.';
$messages['setcreated'] = 'Crëuwyd y set hidlydd yn llwyddiannus.';
$messages['activateerror'] = 'Methwyd galluogi y hidlydd(ion) dewiswyd. Cafwyd gwall gweinydd.';
$messages['deactivateerror'] = 'Methwyd analluogi y hidlydd(ion) dewiswyd. Cafwyd gwall gweinydd.';
$messages['deactivated'] = 'Galluogwyd y hidlydd(ion) yn llwyddiannus.';
$messages['activated'] = 'Analluogwyd y hidlydd(ion) yn llwyddiannus.';
$messages['moved'] = 'Symudwyd y hidlydd yn llwyddiannus.';
$messages['moveerror'] = 'Methwyd symud y hidlydd dewiswyd. Cafwyd gwall gweinydd.';
$messages['nametoolong'] = 'Enw yn rhy hir.';
$messages['namereserved'] = 'Enw neilltuedig.';
$messages['setexist'] = 'Mae\'r set yn bodoli\'n barod.';
$messages['nodata'] = 'Rhaid dewis o leia un safle!';
$messages['invaliddateformat'] = 'Dyddiad neu fformat dyddiad annilys';
+$messages['saveerror'] = 'Methwyd cadw\'r data. Cafwyd gwall gweinydd.';
+$messages['vacationsaved'] = 'Cadwyd y data gwyliau yn llwyddiannus.';
+$messages['emptyvacationbody'] = 'Mae angen rhoi corff y neges wyliau!';
?>
diff --git a/lib/plugins/managesieve/localization/da_DK.inc b/lib/plugins/managesieve/localization/da_DK.inc
index b240741..ebf1cb0 100644
--- a/lib/plugins/managesieve/localization/da_DK.inc
+++ b/lib/plugins/managesieve/localization/da_DK.inc
@@ -1,192 +1,205 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtre';
$labels['managefilters'] = 'Ændre indgående mail filtreing';
$labels['filtername'] = 'Filter navn';
$labels['newfilter'] = 'Nyt filter';
$labels['filteradd'] = 'Tilføj filter';
$labels['filterdel'] = 'Slet filter';
$labels['moveup'] = 'Flyt op';
$labels['movedown'] = 'Flyt ned';
$labels['filterallof'] = 'matcher alle af de følgende regler';
$labels['filteranyof'] = 'matcher en af følgende regler';
$labels['filterany'] = 'alle meddelelser';
$labels['filtercontains'] = 'indeholder';
$labels['filternotcontains'] = 'indeholder ikke';
$labels['filteris'] = 'er ens med';
$labels['filterisnot'] = 'er ikke ens med';
$labels['filterexists'] = 'findes';
$labels['filternotexists'] = 'ikke eksisterer';
$labels['filtermatches'] = 'matcher udtryk';
$labels['filternotmatches'] = 'matcher ikke udtryk';
$labels['filterregex'] = 'matcher regulært udtryk';
$labels['filternotregex'] = 'matcher ikke regulært udtryk';
$labels['filterunder'] = 'under';
$labels['filterover'] = 'over';
$labels['addrule'] = 'Tilføj regel';
$labels['delrule'] = 'Slet regel';
$labels['messagemoveto'] = 'Flyt besked til';
$labels['messageredirect'] = 'Redirriger besked til';
$labels['messagecopyto'] = 'Kopier besked til';
$labels['messagesendcopy'] = 'Send kopi af besked til';
$labels['messagereply'] = 'Svar med besked';
$labels['messagedelete'] = 'Slet besked';
$labels['messagediscard'] = 'Slet med besked';
$labels['messagekeep'] = 'Behold besked i Inbox';
$labels['messagesrules'] = 'For indkomne besked:';
$labels['messagesactions'] = '...udfør følgende aktioner:';
$labels['add'] = 'Tilføje';
$labels['del'] = 'Fjern';
$labels['sender'] = 'Afsender';
$labels['recipient'] = 'Modtager';
$labels['vacationaddr'] = 'Min(e) yderligere email-adresse(r):';
$labels['vacationdays'] = 'Hvor tit skal besked sendes (i dage):';
$labels['vacationinterval'] = 'Hvor tit skal besked sendes:';
-$labels['days'] = 'dage';
-$labels['seconds'] = 'sekunder';
$labels['vacationreason'] = 'Besked (ved ferie):';
$labels['vacationsubject'] = 'Besked emne:';
+$labels['days'] = 'dage';
+$labels['seconds'] = 'sekunder';
$labels['rulestop'] = 'Stop behandling af regler';
$labels['enable'] = 'Aktivér/Deaktivér';
$labels['filterset'] = 'Filter sæt';
$labels['filtersets'] = 'Filtre sæt';
$labels['filtersetadd'] = 'Tilføj filter sæt';
$labels['filtersetdel'] = 'Slet aktuel filter sæt';
$labels['filtersetact'] = 'Aktiver nuværende filter sæt';
$labels['filtersetdeact'] = 'Deaktiver nuværende filter sæt';
$labels['filterdef'] = 'Filter definition';
$labels['filtersetname'] = 'Filter sæt navn';
$labels['newfilterset'] = 'Nyt filter sæt';
$labels['active'] = 'aktiv';
$labels['none'] = 'ingen';
$labels['fromset'] = 'fra sæt';
$labels['fromfile'] = 'fra fil';
$labels['filterdisabled'] = 'Filter deaktiveret';
$labels['countisgreaterthan'] = 'antal er større end';
$labels['countisgreaterthanequal'] = 'antal er større end eller lig med';
$labels['countislessthan'] = 'antal er mindre end';
$labels['countislessthanequal'] = 'antal er mindre end eller lig med';
$labels['countequals'] = 'antal er lig med';
$labels['countnotequals'] = 'antal er ikke lig med';
$labels['valueisgreaterthan'] = 'værdi er større end';
$labels['valueisgreaterthanequal'] = 'værdi er større end eller lig med';
$labels['valueislessthan'] = 'værdi er mindre end';
$labels['valueislessthanequal'] = 'værdi er mindre end eller lig med';
$labels['valueequals'] = 'værdi er lig med';
$labels['valuenotequals'] = 'værdi er ikke lig med';
$labels['setflags'] = 'Sæt flag i beskeden';
$labels['addflags'] = 'Tilføj flag til beskeden';
$labels['removeflags'] = 'Fjern flag fra beskeden';
$labels['flagread'] = 'Læs';
$labels['flagdeleted'] = 'Slettede';
$labels['flaganswered'] = 'Besvaret';
$labels['flagflagged'] = 'Markeret';
$labels['flagdraft'] = 'Kladde';
$labels['setvariable'] = 'Skriv variablen';
$labels['setvarname'] = 'Variabel navn:';
$labels['setvarvalue'] = 'Variabel værdi:';
$labels['setvarmodifiers'] = 'Modifikator';
$labels['varlower'] = 'små bogstaver';
$labels['varupper'] = 'store bogstaver';
$labels['varlowerfirst'] = 'første bogstav lille';
$labels['varupperfirst'] = 'Første bogstav stort';
$labels['varquotewildcard'] = 'Sæt specialle tegn i citationstegn ';
$labels['varlength'] = 'længde';
$labels['notify'] = 'Send meddelelse';
-$labels['notifyaddress'] = 'Til e-mail adresse:';
-$labels['notifybody'] = 'meddelelses indhold:';
-$labels['notifysubject'] = 'Meddelelses emne:';
-$labels['notifyfrom'] = 'Meddelelses afsender:';
$labels['notifyimportance'] = 'Vigtighed:';
$labels['notifyimportancelow'] = 'lav';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'høj';
+$labels['notifymethodmailto'] = 'Email';
+$labels['notifymethodtel'] = 'Telefon';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Opret filter';
$labels['usedata'] = 'Brug følgende data i filteret:';
$labels['nextstep'] = 'Næste trin';
$labels['...'] = '...';
$labels['currdate'] = 'Aktuel dato';
$labels['datetest'] = 'Dato';
$labels['dateheader'] = 'header:';
$labels['year'] = 'år';
$labels['month'] = 'måned';
$labels['day'] = 'dag';
$labels['date'] = 'dato (åååå-mm-dd)';
$labels['julian'] = 'dato (juliansk)';
$labels['hour'] = 'time';
$labels['minute'] = 'minut';
$labels['second'] = 'sekund';
$labels['time'] = 'tid (tt:mm:ss)';
$labels['iso8601'] = 'dato (ISO8601)';
$labels['std11'] = 'dato (RFC2822)';
$labels['zone'] = 'tidszone';
$labels['weekday'] = 'ugedag (0-6)';
$labels['advancedopts'] = 'Advancerede muligheder';
$labels['body'] = 'Brødtekst';
$labels['address'] = 'adresse';
$labels['envelope'] = 'kuvert';
$labels['modifier'] = 'modificerer:';
$labels['text'] = 'tekst';
$labels['undecoded'] = 'udekodet (råt):';
$labels['contenttype'] = 'indholdstype';
$labels['modtype'] = 'type:';
$labels['allparts'] = 'alle';
$labels['domain'] = 'domæne';
$labels['localpart'] = 'lokal del';
$labels['user'] = 'bruger';
$labels['detail'] = 'detalje';
$labels['comparator'] = 'sammenligner:';
$labels['default'] = 'standard';
$labels['octet'] = 'præcis (oktet)';
$labels['asciicasemap'] = 'store og små bogstaver (ascii-bogstaver)';
$labels['asciinumeric'] = 'numerisk (ascii-numerisk)';
$labels['index'] = 'indeks:';
$labels['indexlast'] = 'baglends';
+$labels['vacation'] = 'Ferie';
+$labels['vacation.reply'] = 'Svar besked';
+$labels['vacation.advanced'] = 'Avanceret indstillinger ';
+$labels['vacation.subject'] = 'Emne';
+$labels['vacation.start'] = 'Ferie star';
+$labels['vacation.end'] = 'Ferie slut';
+$labels['vacation.status'] = 'Status';
+$labels['vacation.saving'] = 'Gemmer data...';
+$labels['vacation.keep'] = 'Behold';
+$labels['vacation.discard'] = 'Kasser';
+$labels['vacation.redirect'] = 'Omdiriger til ';
+$labels['vacation.copy'] = 'Send kopi til';
$messages['filterunknownerror'] = 'Ukendt server fejl.';
$messages['filterconnerror'] = 'Kan ikke forbinde til server.';
$messages['filterdeleteerror'] = 'Kunne ikke slette filter. Serverfejl opstod.';
$messages['filterdeleted'] = 'Filter slettet.';
$messages['filtersaved'] = 'Filter gemt.';
$messages['filtersaveerror'] = 'Kunne ikke gemme filter. Serverfejl.';
$messages['filterdeleteconfirm'] = 'Vil du slette det valgte filter?';
$messages['ruledeleteconfirm'] = 'Er du sikker på at du vil slette den valgte regel?';
$messages['actiondeleteconfirm'] = 'Er du sikker på du vil slette den valgte handling?';
$messages['forbiddenchars'] = 'Ulovlige tegn i feltet';
$messages['cannotbeempty'] = 'Feltet kan ikke være tomt.';
$messages['ruleexist'] = 'Filter med dette navn eksisterer allerede.';
$messages['setactivateerror'] = 'Kan ikke aktiverer valgt filter sæt. Server fejl.';
$messages['setdeactivateerror'] = 'Kan ikke deaktivere valgt filter sæt. Server fejl.';
$messages['setdeleteerror'] = 'Kan ikke slette valgt filter sæt. Server fejl.';
$messages['setactivated'] = 'Filter sæt aktiveret.';
$messages['setdeactivated'] = 'Filter sæt deaktiveret.';
$messages['setdeleted'] = 'Filter sæt slettet.';
$messages['setdeleteconfirm'] = 'Er du sikker på du vil slette valgt filter sæt?';
$messages['setcreateerror'] = 'Kan ikke oprette filter sæt. Server fejl.';
$messages['setcreated'] = 'Filter sæt oprettet.';
$messages['activateerror'] = 'Kan ikke aktivere valgt filter sæt. Server fejl.';
$messages['deactivateerror'] = 'Kan ikke deaktivere valgt filter sæt. Server fejl.';
$messages['deactivated'] = 'Filter(filtre) aktiveret.';
$messages['activated'] = 'Filter(filtre) deaktiveret.';
$messages['moved'] = 'Filter flyttet.';
$messages['moveerror'] = 'Kan ikke flytte valgt filter. Server fejl.';
$messages['nametoolong'] = 'Navn er for langt.';
$messages['namereserved'] = 'Reserveret navn.';
$messages['setexist'] = 'Filterv sæt eksisterer allerede';
$messages['nodata'] = 'Mindst en position skal vælges!';
$messages['invaliddateformat'] = 'Ugyldigt dato- eller tidsformat';
+$messages['saveerror'] = 'Kunne ikke gemme data. Server fejl';
+$messages['vacationsaved'] = 'Ferie data gemt';
?>
diff --git a/lib/plugins/managesieve/localization/de_CH.inc b/lib/plugins/managesieve/localization/de_CH.inc
index 234f313..13a4a67 100644
--- a/lib/plugins/managesieve/localization/de_CH.inc
+++ b/lib/plugins/managesieve/localization/de_CH.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filter';
$labels['managefilters'] = 'Verwalte eingehende Nachrichtenfilter';
$labels['filtername'] = 'Filtername';
$labels['newfilter'] = 'Neuer Filter';
$labels['filteradd'] = 'Filter hinzufügen';
$labels['filterdel'] = 'Filter löschen';
$labels['moveup'] = 'Nach oben';
$labels['movedown'] = 'Nach unten';
$labels['filterallof'] = 'UND (alle Regeln müssen zutreffen)';
$labels['filteranyof'] = 'ODER (eine der Regeln muss zutreffen';
$labels['filterany'] = 'Für alle Nachrichten';
$labels['filtercontains'] = 'enthält';
$labels['filternotcontains'] = 'enthält nicht';
$labels['filteris'] = 'ist gleich';
$labels['filterisnot'] = 'ist ungleich';
$labels['filterexists'] = 'ist vorhanden';
$labels['filternotexists'] = 'nicht vorhanden';
$labels['filtermatches'] = 'entspricht Ausdruck';
$labels['filternotmatches'] = 'entspricht nicht Ausdruck';
$labels['filterregex'] = 'trifft regulären Ausdruck';
$labels['filternotregex'] = 'entspricht regulärem Ausdruck';
$labels['filterunder'] = 'unter';
$labels['filterover'] = 'über';
$labels['addrule'] = 'Regel hinzufügen';
$labels['delrule'] = 'Regel löschen';
$labels['messagemoveto'] = 'Verschiebe Nachricht nach';
$labels['messageredirect'] = 'Leite Nachricht um nach';
$labels['messagecopyto'] = 'Kopiere Nachricht nach';
$labels['messagesendcopy'] = 'Sende Kopie an';
$labels['messagereply'] = 'Antworte mit Nachricht';
$labels['messagedelete'] = 'Nachricht löschen';
$labels['messagediscard'] = 'Discard with message';
$labels['messagekeep'] = 'Im Posteingang behalten';
$labels['messagesrules'] = 'Für eingehende Nachrichten:';
$labels['messagesactions'] = 'Führe folgende Aktionen aus:';
$labels['add'] = 'Hinzufügen';
$labels['del'] = 'Löschen';
$labels['sender'] = 'Absender';
$labels['recipient'] = 'Empfänger';
$labels['vacationaddr'] = 'Meine weiteren E-Mail-Adressen:';
$labels['vacationdays'] = 'Antwort wird erneut gesendet nach (in Tagen):';
$labels['vacationinterval'] = 'Wie oft senden:';
-$labels['days'] = 'Tage';
-$labels['seconds'] = 'Sekunden';
$labels['vacationreason'] = 'Inhalt der Nachricht (Abwesenheitsgrund):';
$labels['vacationsubject'] = 'Betreff';
+$labels['days'] = 'Tage';
+$labels['seconds'] = 'Sekunden';
$labels['rulestop'] = 'Regelauswertung anhalten';
$labels['enable'] = 'Aktivieren/Deaktivieren';
$labels['filterset'] = 'Filtersätze';
$labels['filtersets'] = 'Filtersätze';
$labels['filtersetadd'] = 'Filtersatz anlegen';
$labels['filtersetdel'] = 'Aktuellen Filtersatz löschen';
$labels['filtersetact'] = 'Aktuellen Filtersatz aktivieren';
$labels['filtersetdeact'] = 'Aktuellen Filtersatz deaktivieren';
$labels['filterdef'] = 'Filterdefinition';
$labels['filtersetname'] = 'Filtersatzname';
$labels['newfilterset'] = 'Neuer Filtersatz';
$labels['active'] = 'aktiv';
$labels['none'] = 'keine';
$labels['fromset'] = 'aus Filtersatz';
$labels['fromfile'] = 'aus Datei';
$labels['filterdisabled'] = 'Filter deaktiviert';
$labels['countisgreaterthan'] = 'Anzahl ist grösser als';
$labels['countisgreaterthanequal'] = 'Anzahl ist gleich oder grösser als';
$labels['countislessthan'] = 'Anzahl ist kleiner als';
$labels['countislessthanequal'] = 'Anzahl ist gleich oder kleiner als';
$labels['countequals'] = 'Anzahl ist gleich';
$labels['countnotequals'] = 'Anzahl ist ungleich';
$labels['valueisgreaterthan'] = 'Wert ist grösser als';
$labels['valueisgreaterthanequal'] = 'Wert ist gleich oder grösser als';
$labels['valueislessthan'] = 'Wert ist kleiner';
$labels['valueislessthanequal'] = 'Wert ist gleich oder kleiner als';
$labels['valueequals'] = 'Wert ist gleich';
$labels['valuenotequals'] = 'Wert ist ungleich';
$labels['setflags'] = 'Setze Markierungen';
$labels['addflags'] = 'Füge Markierung hinzu';
$labels['removeflags'] = 'Entferne Markierung';
$labels['flagread'] = 'gelesen';
$labels['flagdeleted'] = 'Gelöscht';
$labels['flaganswered'] = 'Beantwortet';
$labels['flagflagged'] = 'Markiert';
$labels['flagdraft'] = 'Entwurf';
$labels['setvariable'] = 'Setze Variable';
$labels['setvarname'] = 'Variablenname:';
$labels['setvarvalue'] = 'Variablenwert:';
$labels['setvarmodifiers'] = 'Umwandler:';
$labels['varlower'] = 'Kleinschreibung';
$labels['varupper'] = 'Grossschreibung';
$labels['varlowerfirst'] = 'Erster Buchstabe klein';
$labels['varupperfirst'] = 'Erster Buchstabe gross';
$labels['varquotewildcard'] = 'Sonderzeichen auszeichnen';
$labels['varlength'] = 'Länge';
$labels['notify'] = 'Mitteilung senden';
-$labels['notifyaddress'] = 'Empfängeradresse:';
-$labels['notifybody'] = 'Mitteilungstext:';
-$labels['notifysubject'] = 'Mitteilungsbetreff:';
-$labels['notifyfrom'] = 'Absender:';
+$labels['notifytarget'] = 'Mitteilungsempfänger:';
+$labels['notifymessage'] = 'Mitteilungstext (optional):';
+$labels['notifyoptions'] = 'Mitteilungsoptionen (optional):';
+$labels['notifyfrom'] = 'Absender (optional):';
$labels['notifyimportance'] = 'Wichtigkeit:';
$labels['notifyimportancelow'] = 'tief';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'hoch';
+$labels['notifymethodmailto'] = 'E-Mail';
+$labels['notifymethodtel'] = 'Telefon';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Filter erstellen';
$labels['usedata'] = 'Die folgenden Daten im Filter benutzen:';
$labels['nextstep'] = 'Nächster Schritt';
$labels['...'] = '...';
$labels['currdate'] = 'Aktuelles Datum';
$labels['datetest'] = 'Datum';
$labels['dateheader'] = 'Kopfzeile:';
$labels['year'] = 'Jahr';
$labels['month'] = 'Monat';
$labels['day'] = 'Tag';
$labels['date'] = 'Datum (JJJJ-MM-TT)';
$labels['julian'] = 'Datum (julianisch)';
$labels['hour'] = 'Stunde';
$labels['minute'] = 'Minute';
$labels['second'] = 'Sekunde';
$labels['time'] = 'Zeit (hh:mm:ss)';
$labels['iso8601'] = 'Datum (ISO-8601)';
$labels['std11'] = 'Datum (RFC 2822)';
$labels['zone'] = 'Zeitzone';
$labels['weekday'] = 'Wochentag (0-6)';
$labels['advancedopts'] = 'Erweiterte Optionen';
$labels['body'] = 'Inhalt';
$labels['address'] = 'Adresse';
$labels['envelope'] = 'Umschlag';
$labels['modifier'] = 'Wandler';
$labels['text'] = 'Text';
$labels['undecoded'] = 'kodiert (roh)';
$labels['contenttype'] = 'Inhaltstyp';
$labels['modtype'] = 'Typ:';
$labels['allparts'] = 'alle';
$labels['domain'] = 'Domain';
$labels['localpart'] = 'lokaler Teil';
$labels['user'] = 'Benutzer';
$labels['detail'] = 'Detail';
$labels['comparator'] = 'Komparator';
$labels['default'] = 'Vorgabewert';
$labels['octet'] = 'strikt (Oktet)';
$labels['asciicasemap'] = 'Gross-/Kleinschreibung ignorieren';
$labels['asciinumeric'] = 'numerisch (ascii-numeric)';
$labels['index'] = 'Index:';
$labels['indexlast'] = 'rückwärts';
+$labels['vacation'] = 'Abwesenheit';
+$labels['vacation.reply'] = 'Antworte mit Nachricht';
+$labels['vacation.advanced'] = 'Erweiterte Einstellungen';
+$labels['vacation.subject'] = 'Betreff';
+$labels['vacation.body'] = 'Inhalt';
+$labels['vacation.start'] = 'Beginn der Abwesenheit';
+$labels['vacation.end'] = 'Ende der Abwesenheit';
+$labels['vacation.status'] = 'Status';
+$labels['vacation.on'] = 'Ein';
+$labels['vacation.off'] = 'Aus';
+$labels['vacation.addresses'] = 'Meine weiteren E-Mail-Adressen';
+$labels['vacation.interval'] = 'Antwort-Intervall';
+$labels['vacation.after'] = 'Abwesenheitsregel einfügen nach';
+$labels['vacation.saving'] = 'Speichere Daten...';
+$labels['vacation.action'] = 'Aktion für eingehende Nachrichten';
+$labels['vacation.keep'] = 'Behalten';
+$labels['vacation.discard'] = 'Verwerfen';
+$labels['vacation.redirect'] = 'Weiterleiten an';
+$labels['vacation.copy'] = 'Kopie an';
+$labels['arialabelfiltersetactions'] = 'Filtersatz-Aktionen';
+$labels['arialabelfilteractions'] = 'Filteraktionen';
+$labels['arialabelfilterform'] = 'Filtereigenschaften';
+$labels['ariasummaryfilterslist'] = 'Filterliste';
+$labels['ariasummaryfiltersetslist'] = 'Filtersatzliste';
+$labels['filterstitle'] = 'Eingehende Nachrichtenfilter bearbeiten';
+$labels['vacationtitle'] = 'Abwesenheitsregel bearbeiten';
$messages['filterunknownerror'] = 'Unbekannter Serverfehler';
$messages['filterconnerror'] = 'Kann nicht zum Sieve-Server verbinden';
$messages['filterdeleteerror'] = 'Serverfehler beim Löschen des Filters.';
$messages['filterdeleted'] = 'Filter erfolgreich gelöscht';
$messages['filtersaved'] = 'Filter gespeichert';
$messages['filtersaveerror'] = 'Serverfehler beim Speichern des Filters.';
$messages['filterdeleteconfirm'] = 'Möchten Sie den Filter löschen ?';
$messages['ruledeleteconfirm'] = 'Sicher, dass Sie die Regel löschen wollen?';
$messages['actiondeleteconfirm'] = 'Sicher, dass Sie die ausgewaehlte Aktion löschen wollen?';
$messages['forbiddenchars'] = 'Unerlaubte Zeichen im Feld';
$messages['cannotbeempty'] = 'Feld darf nicht leer sein';
$messages['ruleexist'] = 'Ein Filter mit dem angegebenen Namen existiert bereits.';
$messages['setactivateerror'] = 'Serverfehler beim Aktivieren des gewählten Filtersatzes.';
$messages['setdeactivateerror'] = 'Serverfehler beim Deaktivieren des gewählten Filtersatzes.';
$messages['setdeleteerror'] = 'Serverfehler beim Löschen des gewählten Filtersatzes.';
$messages['setactivated'] = 'Filtersatz erfolgreich aktiviert.';
$messages['setdeactivated'] = 'Filtersatz erfolgreich deaktiviert.';
$messages['setdeleted'] = 'Filtersatz erfolgreich gelöscht.';
$messages['setdeleteconfirm'] = 'Sind Sie sicher, dass Sie den ausgewählten Filtersatz löschen möchten?';
$messages['setcreateerror'] = 'Serverfehler beim Erstellen des Filtersatzes.';
$messages['setcreated'] = 'Filter erfolgreich erstellt.';
$messages['activateerror'] = 'Serverfehler beim Aktivieren des/der gewählten Filter(s).';
$messages['deactivateerror'] = 'Serverfehler beim Deaktivieren des/der gewählten Filter(s).';
$messages['deactivated'] = 'Filter erfolgreich aktiviert.';
$messages['activated'] = 'Filter erfolgreich deaktiviert.';
$messages['moved'] = 'Filter erfolgreich verschoben.';
$messages['moveerror'] = 'Serverfehler beim Verschieben des gewählten Filters.';
$messages['nametoolong'] = 'Filtersatz kann nicht erstellt werden. Name zu lang.';
$messages['namereserved'] = 'Reservierter Name.';
$messages['setexist'] = 'Filtersatz existiert bereits.';
$messages['nodata'] = 'Mindestens eine Position muss ausgewählt werden!';
$messages['invaliddateformat'] = 'Ungültiges Datumsformat';
+$messages['saveerror'] = 'Fehler beim Speichern (Serverfehler)';
+$messages['vacationsaved'] = 'Abwesenheitsnotiz erfolgreich gespeichert.';
+$messages['emptyvacationbody'] = 'Mitteilungstext ist erforderlich!';
?>
diff --git a/lib/plugins/managesieve/localization/de_DE.inc b/lib/plugins/managesieve/localization/de_DE.inc
index 2eaa215..f57ffa3 100644
--- a/lib/plugins/managesieve/localization/de_DE.inc
+++ b/lib/plugins/managesieve/localization/de_DE.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filter';
$labels['managefilters'] = 'Filter für eingehende Nachrichten verwalten';
$labels['filtername'] = 'Filtername';
$labels['newfilter'] = 'Neuer Filter';
$labels['filteradd'] = 'Filter hinzufügen';
$labels['filterdel'] = 'Filter löschen';
$labels['moveup'] = 'Nach oben';
$labels['movedown'] = 'Nach unten';
$labels['filterallof'] = 'trifft auf alle folgenden Regeln zu';
$labels['filteranyof'] = 'trifft auf eine der folgenden Regeln zu';
$labels['filterany'] = 'alle Nachrichten';
$labels['filtercontains'] = 'enthält';
$labels['filternotcontains'] = 'enthält nicht';
$labels['filteris'] = 'ist gleich';
$labels['filterisnot'] = 'ist ungleich';
$labels['filterexists'] = 'existiert';
$labels['filternotexists'] = 'existiert nicht';
$labels['filtermatches'] = 'trifft auf Ausdruck zu';
$labels['filternotmatches'] = 'trifft nicht auf Ausdruck zu';
$labels['filterregex'] = 'trifft auf regulären Ausdruck zu';
$labels['filternotregex'] = 'trifft nicht auf regulären Ausdruck zu';
$labels['filterunder'] = 'unter';
$labels['filterover'] = 'über';
$labels['addrule'] = 'Regel hinzufügen';
$labels['delrule'] = 'Regel löschen';
$labels['messagemoveto'] = 'Nachricht verschieben nach';
$labels['messageredirect'] = 'Nachricht umleiten an';
$labels['messagecopyto'] = 'Nachricht kopieren nach';
$labels['messagesendcopy'] = 'Kopie senden an';
$labels['messagereply'] = 'Mit Nachricht antworten';
$labels['messagedelete'] = 'Nachricht löschen';
$labels['messagediscard'] = 'Abweisen mit Nachricht';
$labels['messagekeep'] = 'Behalte Nachricht im Posteingang';
$labels['messagesrules'] = 'Für eingehende Nachrichten:';
$labels['messagesactions'] = '...führe folgende Aktionen aus:';
$labels['add'] = 'Hinzufügen';
$labels['del'] = 'Löschen';
$labels['sender'] = 'Absender';
$labels['recipient'] = 'Empfänger';
$labels['vacationaddr'] = 'Meine zusätzliche E-Mail-Adresse(n):';
$labels['vacationdays'] = 'Wie oft sollen Nachrichten gesendet werden (in Tagen):';
$labels['vacationinterval'] = 'Wie oft sollen Nachrichten gesendet werden:';
-$labels['days'] = 'Tage';
-$labels['seconds'] = 'Sekunden';
$labels['vacationreason'] = 'Nachrichteninhalt (Abwesenheitsgrund):';
$labels['vacationsubject'] = 'Nachrichtenbetreff';
+$labels['days'] = 'Tage';
+$labels['seconds'] = 'Sekunden';
$labels['rulestop'] = 'Regelauswertung anhalten';
$labels['enable'] = 'Aktivieren/Deaktivieren';
$labels['filterset'] = 'Filtersätze';
$labels['filtersets'] = 'Filtersätze';
$labels['filtersetadd'] = 'Filtersatz anlegen';
$labels['filtersetdel'] = 'Aktuellen Filtersatz löschen';
$labels['filtersetact'] = 'Aktuellen Filtersatz aktivieren';
$labels['filtersetdeact'] = 'Aktuellen Filtersatz deaktivieren';
$labels['filterdef'] = 'Filterdefinition';
$labels['filtersetname'] = 'Filtersatzname';
$labels['newfilterset'] = 'Neuer Filtersatz';
$labels['active'] = 'aktiv';
$labels['none'] = 'keine';
$labels['fromset'] = 'aus Filtersatz';
$labels['fromfile'] = 'aus Datei';
$labels['filterdisabled'] = 'Filter deaktiviert';
$labels['countisgreaterthan'] = 'Anzahl ist größer als';
$labels['countisgreaterthanequal'] = 'Anzahl ist gleich oder größer als';
$labels['countislessthan'] = 'Anzahl ist kleiner als';
$labels['countislessthanequal'] = 'Anzahl ist gleich oder kleiner als';
$labels['countequals'] = 'Anzahl ist gleich';
$labels['countnotequals'] = 'Anzahl ist nicht gleich';
$labels['valueisgreaterthan'] = 'Wert ist größer als';
$labels['valueisgreaterthanequal'] = 'Wert ist gleich oder größer als';
$labels['valueislessthan'] = 'Wert ist kleiner';
$labels['valueislessthanequal'] = 'Wert ist gleich oder kleiner als';
$labels['valueequals'] = 'Wert ist gleich';
$labels['valuenotequals'] = 'Wert ist nicht gleich';
$labels['setflags'] = 'Markierung an der Nachricht setzen';
$labels['addflags'] = 'Markierung zur Nachricht hinzufügen';
$labels['removeflags'] = 'Markierungen von der Nachricht entfernen';
$labels['flagread'] = 'Gelesen';
$labels['flagdeleted'] = 'Gelöscht';
$labels['flaganswered'] = 'Beantwortet';
$labels['flagflagged'] = 'Markiert';
$labels['flagdraft'] = 'Entwurf';
$labels['setvariable'] = 'Variable setzen';
$labels['setvarname'] = 'Name der Variable:';
$labels['setvarvalue'] = 'Wert der Variable:';
$labels['setvarmodifiers'] = 'Modifikatoren:';
$labels['varlower'] = 'Kleinschreibung';
$labels['varupper'] = 'Großschreibung';
$labels['varlowerfirst'] = 'Erster Buchstabe kleingeschrieben';
$labels['varupperfirst'] = 'Erster Buchstabe großgeschrieben';
$labels['varquotewildcard'] = 'maskiere Sonderzeichen';
$labels['varlength'] = 'Länge';
$labels['notify'] = 'Sende Benachrichtigung';
-$labels['notifyaddress'] = 'An Email Adresse:';
-$labels['notifybody'] = 'Benachrichtigungs-Text:';
-$labels['notifysubject'] = 'Benachrichtigungs-Betreff:';
-$labels['notifyfrom'] = 'Benachrichtigungs-Absender:';
+$labels['notifytarget'] = 'Benachrichtigungs-Empfänger:';
+$labels['notifymessage'] = 'Inhalt der Benachrichtigung (optional):';
+$labels['notifyoptions'] = 'Optionen für die Benachrichtigung (optional)';
+$labels['notifyfrom'] = 'Absender der Benachrichtigung (optional):';
$labels['notifyimportance'] = 'Priorität:';
$labels['notifyimportancelow'] = 'niedrig';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'hoch';
+$labels['notifymethodmailto'] = 'E-Mail';
+$labels['notifymethodtel'] = 'Telefon';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Filter erstellen';
$labels['usedata'] = 'Die folgenden Daten im Filter benutzen:';
$labels['nextstep'] = 'Nächster Schritt';
$labels['...'] = '...';
$labels['currdate'] = 'Aktuelles Datum';
$labels['datetest'] = 'Datum';
$labels['dateheader'] = 'Kopfzeile:';
$labels['year'] = 'jahr';
$labels['month'] = 'monat';
$labels['day'] = 'tag';
$labels['date'] = 'datum (yyyy-mm-dd)';
$labels['julian'] = 'datum (julian)';
$labels['hour'] = 'stunde';
$labels['minute'] = 'minute';
$labels['second'] = 'sekunde';
$labels['time'] = 'zeit (hh:mm:ss)';
$labels['iso8601'] = 'datum (ISO8601)';
$labels['std11'] = 'datum (RFC2822)';
$labels['zone'] = 'Zeitzone';
$labels['weekday'] = 'wochentag (0-6)';
$labels['advancedopts'] = 'Erweiterte Optionen';
$labels['body'] = 'Textkörper';
$labels['address'] = 'Adresse';
$labels['envelope'] = 'Umschlag';
$labels['modifier'] = 'Modifikator:';
$labels['text'] = 'Text';
$labels['undecoded'] = 'Nicht dekodiert';
$labels['contenttype'] = 'Inhaltstyp';
$labels['modtype'] = 'Typ:';
$labels['allparts'] = 'Alle';
$labels['domain'] = 'Domäne';
$labels['localpart'] = 'lokaler Teil';
$labels['user'] = 'Benutzer';
$labels['detail'] = 'Detail';
$labels['comparator'] = 'Komperator:';
$labels['default'] = 'Vorgabewert';
$labels['octet'] = 'strikt (Oktett)';
$labels['asciicasemap'] = 'Groß-/Kleinschreibung ignorieren';
$labels['asciinumeric'] = 'numerisch (ascii-numeric)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'rückwärts';
+$labels['vacation'] = 'Urlaub';
+$labels['vacation.reply'] = 'Antwort';
+$labels['vacation.advanced'] = 'Erweiterte Einstellungen';
+$labels['vacation.subject'] = 'Betreff';
+$labels['vacation.body'] = 'Hauptteil';
+$labels['vacation.start'] = 'Beginn des Urlaubs';
+$labels['vacation.end'] = 'Ende des Urlaubs';
+$labels['vacation.status'] = 'Status';
+$labels['vacation.on'] = 'An';
+$labels['vacation.off'] = 'Aus';
+$labels['vacation.addresses'] = 'Meine weiteren Adressen';
+$labels['vacation.interval'] = 'Antwort Intervall';
+$labels['vacation.after'] = 'Setze Urlaubsregel nach';
+$labels['vacation.saving'] = 'Speichere Daten...';
+$labels['vacation.action'] = 'Eingehende Nachrichtaktion';
+$labels['vacation.keep'] = 'Behalten';
+$labels['vacation.discard'] = 'Verwerfen';
+$labels['vacation.redirect'] = 'Weiterleiten an';
+$labels['vacation.copy'] = 'Kopie senden an';
+$labels['arialabelfiltersetactions'] = 'Aktionen für Filtersätze';
+$labels['arialabelfilteractions'] = 'Aktionen für Filter';
+$labels['arialabelfilterform'] = 'Filtereigenschaften';
+$labels['ariasummaryfilterslist'] = 'Liste von Filtern';
+$labels['ariasummaryfiltersetslist'] = 'Liste von Filtersätzen';
+$labels['filterstitle'] = 'Eingehende Mailfilter bearbeiten';
+$labels['vacationtitle'] = 'Abwesendheitsregel bearbeiten';
$messages['filterunknownerror'] = 'Unbekannter Serverfehler';
$messages['filterconnerror'] = 'Kann keine Verbindung mit Managesieve-Server herstellen';
$messages['filterdeleteerror'] = 'Filter kann nicht gelöscht werden. Ein Serverfehler ist aufgetreten.';
$messages['filterdeleted'] = 'Filter erfolgreich gelöscht';
$messages['filtersaved'] = 'Filter erfolgreich gespeichert';
$messages['filtersaveerror'] = 'Filter kann nicht gespeichert werden. Ein Serverfehler ist aufgetreten.';
$messages['filterdeleteconfirm'] = 'Möchten Sie den ausgewählten Filter wirklich löschen?';
$messages['ruledeleteconfirm'] = 'Sind Sie sicher, dass Sie die ausgewählte Regel löschen möchten?';
$messages['actiondeleteconfirm'] = 'Sind Sie sicher, dass Sie die ausgewählte Aktion löschen möchten?';
$messages['forbiddenchars'] = 'Unzulässige Zeichen im Eingabefeld';
$messages['cannotbeempty'] = 'Eingabefeld darf nicht leer sein';
$messages['ruleexist'] = 'Ein Filter mit dem angegebenen Namen existiert bereits.';
$messages['setactivateerror'] = 'Kann ausgewählten Filtersatz nicht aktivieren. Serverfehler';
$messages['setdeactivateerror'] = 'Kann ausgewählten Filtersatz nicht deaktivieren. Serverfehler';
$messages['setdeleteerror'] = 'Kann ausgewählten Filtersatz nicht löschen. Serverfehler';
$messages['setactivated'] = 'Filtersatz wurde erfolgreich aktiviert';
$messages['setdeactivated'] = 'Filtersatz wurde erfolgreich deaktiviert';
$messages['setdeleted'] = 'Filtersatz wurde erfolgreich gelöscht';
$messages['setdeleteconfirm'] = 'Sind Sie sicher, dass Sie den ausgewählten Filtersatz löschen möchten?';
$messages['setcreateerror'] = 'Erstellen von Filter Sätzen nicht möglich. Es ist ein Serverfehler aufgetreten.';
$messages['setcreated'] = 'Filtersatz wurde erfolgreich erstellt';
$messages['activateerror'] = 'Filter kann nicht aktiviert werden. Serverfehler.';
$messages['deactivateerror'] = 'Filter kann nicht deaktiviert werden. Serverfehler.';
$messages['deactivated'] = 'Filter erfolgreich deaktiviert.';
$messages['activated'] = 'Filter erfolgreich aktiviert.';
$messages['moved'] = 'Filter erfolgreich verschoben.';
$messages['moveerror'] = 'Filter kann nicht verschoben werden. Serverfehler.';
$messages['nametoolong'] = 'Kann Filtersatz nicht erstellen. Name zu lang';
$messages['namereserved'] = 'Reservierter Name.';
$messages['setexist'] = 'Filtersatz existiert bereits.';
$messages['nodata'] = 'Mindestens eine Position muss ausgewählt werden!';
$messages['invaliddateformat'] = 'Ungültiges Datum oder Teil-Format';
+$messages['saveerror'] = 'Ein Serverfehler ist aufgetreten, Speicherung war nicht möglich.';
+$messages['vacationsaved'] = 'Urlaubsdaten erfolgreich gespeichert.';
+$messages['emptyvacationbody'] = 'Inhalt der Abwesenheitsbenachrichtigung wird benötigt!';
?>
diff --git a/lib/plugins/managesieve/localization/el_GR.inc b/lib/plugins/managesieve/localization/el_GR.inc
index 73ec786..d1c7833 100644
--- a/lib/plugins/managesieve/localization/el_GR.inc
+++ b/lib/plugins/managesieve/localization/el_GR.inc
@@ -1,181 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Φίλτρα';
$labels['managefilters'] = 'Διαχείριση φίλτρων εισερχόμενων';
$labels['filtername'] = 'Ονομασία φίλτρου';
$labels['newfilter'] = 'Δημιουργία φίλτρου';
$labels['filteradd'] = 'Προσθήκη φίλτρου';
$labels['filterdel'] = 'Διαγραφή φίλτρου';
$labels['moveup'] = 'Μετακίνηση πάνω';
$labels['movedown'] = 'Μετακίνηση κάτω';
$labels['filterallof'] = 'ταιριάζουν με όλους τους παρακάτω κανόνες';
$labels['filteranyof'] = 'ταιριάζουν με οποιονδήποτε από τους παρακάτω κανόνες';
$labels['filterany'] = 'όλα τα μηνύματα';
$labels['filtercontains'] = 'περιέχει';
$labels['filternotcontains'] = 'δεν περιέχει';
$labels['filteris'] = 'είναι ίσο με';
$labels['filterisnot'] = 'δεν είναι ίσο με';
$labels['filterexists'] = 'υπάρχει';
$labels['filternotexists'] = 'δεν υπάρχει';
$labels['filtermatches'] = 'ταιριάζει με την έκφραση ';
$labels['filternotmatches'] = 'Δεν ταιριάζει με την έκφραση';
$labels['filterregex'] = 'ταιριάζει με κανονική έκφραση';
$labels['filternotregex'] = 'δεν ταιριάζει με κανονική έκφραση';
$labels['filterunder'] = 'κάτω';
$labels['filterover'] = 'πάνω';
$labels['addrule'] = 'Προσθήκη κανόνα';
$labels['delrule'] = 'Διαγραφή κανόνα';
$labels['messagemoveto'] = 'Μετακίνηση μηνύματος στο';
$labels['messageredirect'] = 'Προώθηση μηνύματος στο';
$labels['messagecopyto'] = 'Αντιγραφη μυνηματος σε';
$labels['messagesendcopy'] = 'Αποστολη της αντιγραφης μυνηματος σε';
$labels['messagereply'] = 'Απάντηση με μήνυμα';
$labels['messagedelete'] = 'Διαγραφή μηνύματος';
$labels['messagediscard'] = 'Απόρριψη με μήνυμα';
+$labels['messagekeep'] = 'Διατήρηση μηνύματος στα Εισερχόμενα';
$labels['messagesrules'] = 'Για εισερχόμενα μηνύματα που:';
$labels['messagesactions'] = '...εκτέλεση των παρακάτω ενεργειών:';
$labels['add'] = 'Προσθήκη';
$labels['del'] = 'Διαγραφή';
$labels['sender'] = 'Αποστολέας';
$labels['recipient'] = 'Παραλήπτης';
+$labels['vacationaddr'] = 'Πρόσθετες διευθύνσεις email:';
$labels['vacationdays'] = 'Συχνότητα αποστολής μηνυμάτων (σε ημέρες):';
$labels['vacationinterval'] = 'Συχνότητα αποστολής μηνυμάτων:';
-$labels['days'] = 'ημερες';
-$labels['seconds'] = 'δευτερόλεπτα';
$labels['vacationreason'] = 'Σώμα μηνύματος (λόγος απουσίας):';
$labels['vacationsubject'] = 'Θέμα μηνύματος: ';
+$labels['days'] = 'ημερες';
+$labels['seconds'] = 'δευτερόλεπτα';
$labels['rulestop'] = 'Παύση επαλήθευσης κανόνων';
$labels['enable'] = 'Ενεργοποιηση/Απενεργοποιηση';
$labels['filterset'] = 'Φίλτρα';
$labels['filtersets'] = 'Φίλτρο';
$labels['filtersetadd'] = 'Προσθήκη φίλτρων';
$labels['filtersetdel'] = 'Διαγραφή φίλτρων';
$labels['filtersetact'] = 'Ενεργοποιηση φιλτρων';
$labels['filtersetdeact'] = 'Απενεργοποιηση φιλτρων';
$labels['filterdef'] = 'Ορισμος φιλτρου';
$labels['filtersetname'] = 'Ονομασία φίλτρων';
$labels['newfilterset'] = 'Νεα φιλτρα';
$labels['active'] = 'ενεργο';
$labels['none'] = 'κανένα';
$labels['fromset'] = 'από το σύνολο ';
$labels['fromfile'] = 'απο αρχειο';
$labels['filterdisabled'] = 'Απενεργοποιημενο φιλτρο';
$labels['countisgreaterthan'] = 'αρίθμηση είναι μεγαλύτερη από';
$labels['countisgreaterthanequal'] = 'η μετρηση είναι μεγαλύτερη ή ίση προς';
$labels['countislessthan'] = 'η μετρηση είναι μικρότερη απο';
$labels['countislessthanequal'] = 'η μετρηση είναι μικρότερη ή ίση προς';
$labels['countequals'] = 'η μέτρηση είναι ίση προς ';
$labels['countnotequals'] = 'η μέτρηση δεν είναι ίση προς ';
$labels['valueisgreaterthan'] = 'η τιμη είναι μεγαλύτερη από';
$labels['valueisgreaterthanequal'] = 'η τιμη είναι μεγαλύτερη ή ίση προς';
$labels['valueislessthan'] = 'η τιμη είναι μικρότερη απο';
$labels['valueislessthanequal'] = 'η τιμη είναι μικρότερη ή ίση προς';
$labels['valueequals'] = 'η τιμη είναι ίση με';
$labels['valuenotequals'] = 'η τιμη δεν είναι ίση με';
$labels['setflags'] = 'Ορισμός σημαίων στο μήνυμα';
$labels['addflags'] = 'Προσθήκη σημαίων στο μήνυμα';
$labels['removeflags'] = 'Αφαιρέση σημαίων από το μήνυμα';
$labels['flagread'] = 'Αναγνωση';
$labels['flagdeleted'] = 'Διεγραμμένο';
$labels['flaganswered'] = 'Απαντήθηκε ';
$labels['flagflagged'] = 'Σημειωμένο';
$labels['flagdraft'] = 'Πρόχειρα';
$labels['setvariable'] = 'Ορισμός μεταβλητής';
$labels['setvarname'] = 'Όνομα μεταβλητης:';
$labels['setvarvalue'] = 'Τιμη μεταβλητης:';
$labels['setvarmodifiers'] = 'Τροποποιητές: ';
$labels['varlower'] = 'Μικρογράμματη γραφή';
$labels['varupper'] = 'κεφαλαία γράμματα ';
$labels['varlowerfirst'] = 'πρώτος χαρακτήρας πεζός ';
$labels['varupperfirst'] = 'πρώτος χαρακτήρας κεφαλαία γράμματα';
$labels['varquotewildcard'] = 'παραθέση ειδικων χαρακτήρων';
$labels['varlength'] = 'Μήκος';
$labels['notify'] = 'Αποστολή ειδοποίησης ';
-$labels['notifyaddress'] = 'Σε διεύθυνση email:';
-$labels['notifybody'] = 'Οργανισμός ειδοποιησης:';
-$labels['notifysubject'] = 'Θεμα ειδοποιησης:';
-$labels['notifyfrom'] = 'Αποστολεας ειδοποιησης:';
+$labels['notifytarget'] = 'Παραλήπτης ειδοποίησης:';
+$labels['notifymessage'] = 'Μήνυμα ειδοποίησης (προαιρετικά):';
+$labels['notifyoptions'] = 'Επιλογές ειδοποίησης (προαιρετικά):';
+$labels['notifyfrom'] = 'Αποστολέας ειδοποίησης (προαιρετικά):';
$labels['notifyimportance'] = 'Σημασία: ';
$labels['notifyimportancelow'] = 'Χαμηλή';
$labels['notifyimportancenormal'] = 'Κανονική';
$labels['notifyimportancehigh'] = 'Υψηλή';
+$labels['notifymethodmailto'] = 'Email';
+$labels['notifymethodtel'] = 'Τηλέφωνο';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Δημιουργία φίλτρου';
$labels['usedata'] = 'Χρησιμοποιηση ακολουθων δεδομενων στο φιλτρο:';
$labels['nextstep'] = 'Επομενο βημα';
$labels['...'] = '...';
$labels['currdate'] = 'Τρέχουσα ημερομηνία';
$labels['datetest'] = 'Ημερομηνία';
$labels['dateheader'] = 'επικεφαλίδα:';
$labels['year'] = 'χρονος';
$labels['month'] = 'μηνας';
$labels['day'] = 'ημερα';
$labels['date'] = 'ημερομηνια (yyyy-mm-dd)';
$labels['julian'] = 'ημερομηνια (julian)';
$labels['hour'] = 'ωρα';
$labels['minute'] = 'λεπτο';
$labels['second'] = 'δευτερόλεπτο';
$labels['time'] = 'ωρα (hh:mm:ss)';
$labels['iso8601'] = 'ημερομηνια (ISO8601)';
$labels['std11'] = 'ημερομηνια (RFC2822)';
$labels['zone'] = 'Ζώνη Ώρας';
$labels['weekday'] = 'ημέρα της εβδομάδας (0-6)';
$labels['advancedopts'] = 'Προχωρημένες ρυθμίσεις';
$labels['body'] = 'Σώμα';
$labels['address'] = 'Διεύθυνση';
$labels['envelope'] = 'φάκελος';
$labels['modifier'] = 'Τροποποιηση: ';
$labels['text'] = 'κειμενο';
$labels['undecoded'] = 'αποκωδικοποιημένο (raw)';
$labels['contenttype'] = 'Τύπος περιεχομένου ';
$labels['modtype'] = 'τυπος:';
$labels['allparts'] = 'Όλα';
$labels['domain'] = 'τομέας';
$labels['localpart'] = 'τοπικό τμήμα ';
$labels['user'] = 'χρηστης';
$labels['detail'] = 'λεπτομερειες';
$labels['comparator'] = 'σύγκριση:';
$labels['default'] = 'προεπιλογή';
$labels['octet'] = 'αυστηρή (οκτάδα) ';
$labels['asciicasemap'] = 'πεζά ή κεφαλαία (ascii-casemap)';
$labels['asciinumeric'] = 'αριθμητικό (ascii-αριθμητικο)';
$labels['index'] = 'ευρετήριο:';
$labels['indexlast'] = 'προς τα πίσω';
+$labels['vacation'] = 'Διακοπές';
+$labels['vacation.reply'] = 'Μήνυμα απάντησης';
+$labels['vacation.advanced'] = 'Προηγμένες ρυθμίσεις';
+$labels['vacation.subject'] = 'Θέμα';
+$labels['vacation.body'] = 'Σώμα';
+$labels['vacation.start'] = 'Έναρξη διακοπών';
+$labels['vacation.end'] = 'Λήξη διακοπών';
+$labels['vacation.status'] = 'Κατάσταση';
+$labels['vacation.on'] = 'Ενεργό';
+$labels['vacation.off'] = 'Ανενεργό';
+$labels['vacation.addresses'] = 'Επιπλέον διευθύνσεις';
+$labels['vacation.interval'] = 'Διάστημα απάντησης';
+$labels['vacation.after'] = 'Εισαγωγή κανόνα διακοπών μετά από';
+$labels['vacation.saving'] = 'Αποθήκευση δεδομένων...';
+$labels['vacation.action'] = 'Ενέργεια εισερχόμενου μηνύματος';
+$labels['vacation.keep'] = 'Διατήρηση';
+$labels['vacation.discard'] = 'Διαγραφή';
+$labels['vacation.redirect'] = 'Ανακατεύθυνση σε';
+$labels['vacation.copy'] = 'Αποστολή αντιγράφου σε';
+$labels['arialabelfiltersetactions'] = 'Ενέργειες ομάδας φίλτρων';
+$labels['arialabelfilteractions'] = 'Ενέργειες φίλτρων';
+$labels['arialabelfilterform'] = 'Ιδιότητες φίλτρων';
+$labels['ariasummaryfilterslist'] = 'Λίστα φίλτρων';
+$labels['ariasummaryfiltersetslist'] = 'Λίστα ομάδων φίλτρων';
+$labels['filterstitle'] = 'Επεξεργασία φίλτρων εισερχόμενης αλληλογραφίας';
+$labels['vacationtitle'] = 'Επεξεργασία κανόνα εκτός-γραφείου';
$messages['filterunknownerror'] = 'Άγνωστο σφάλμα διακομιστή';
$messages['filterconnerror'] = 'Αδυναμία σύνδεσης στον διακομιστή managesieve';
+$messages['filterdeleteerror'] = 'Αδυναμία διαγραφής φίλτρου. Προέκυψε σφάλμα στον διακομιστή';
$messages['filterdeleted'] = 'Το φίλτρο διαγράφηκε επιτυχώς';
$messages['filtersaved'] = 'Το φίλτρο αποθηκεύτηκε επιτυχώς';
+$messages['filtersaveerror'] = 'Αδυναμία αποθήκευσης φίλτρου. Προέκυψε σφάλμα στον διακομιστή';
$messages['filterdeleteconfirm'] = 'Είστε σίγουροι ότι θέλετε να διαγράψετε το επιλεγμένο φίλτρο? ';
$messages['ruledeleteconfirm'] = 'Θέλετε όντως να διαγράψετε τον επιλεγμένο κανόνα;';
$messages['actiondeleteconfirm'] = 'Θέλετε όντως να διαγράψετε την επιλεγμένη ενέργεια;';
$messages['forbiddenchars'] = 'Μη επιτρεπτοί χαρακτήρες στο πεδίο';
$messages['cannotbeempty'] = 'Το πεδίο δεν μπορεί να είναι κενό';
$messages['ruleexist'] = 'Φιλτρο με αυτο το όνομα υπάρχει ήδη. ';
+$messages['setactivateerror'] = 'Αδυναμία ενεργοποίησης επιλεγμένων ομάδων φίλτρων. Προέκυψε σφάλμα στον διακομιστή.';
+$messages['setdeactivateerror'] = 'Αδυναμία απενεργοποίησης επιλεγμένων ομάδων φίλτρων. Προέκυψε σφάλμα στον διακομιστή.';
+$messages['setdeleteerror'] = 'Αδυναμία διαγραφής των επιλεγμένων ομάδων φίλτρων. Προέκυψε σφάλμα στον διακομιστή';
$messages['setactivated'] = 'Φίλτρα ενεργοποιήθηκαν με επιτυχία.';
$messages['setdeactivated'] = 'Φίλτρα απενεργοποιήθηκαν με επιτυχία.';
$messages['setdeleted'] = 'Τα φίλτρα διαγράφηκαν επιτυχώς.';
$messages['setdeleteconfirm'] = 'Θέλετε όντως να διαγράψετε τα επιλεγμένα φιλτρα?';
+$messages['setcreateerror'] = 'Αδυναμία δημιουργίας ομάδας φίλτρων. Προέκυψε σφάλμα στον διακομιστή.';
$messages['setcreated'] = 'Τα φιλτρα δημιουργηθηκαν επιτυχως.';
+$messages['activateerror'] = 'Αδυναμία ενεργοποίησης επιλεγμένου φίλτρου(ων). Προέκυψε σφάλμα στον διακομιστή.';
+$messages['deactivateerror'] = 'Αδυναμία απενεργοποίησης επιλεγμένου φίλτρου(ων). Προέκυψε σφάλμα στον διακομιστή.';
$messages['deactivated'] = 'Το φιλτρο(α) απενεργοποιηθηκαν επιτυχως.';
$messages['activated'] = 'Το φίλτρο(α) ενεργοποιηθηκαν επιτυχώς.';
$messages['moved'] = 'Το φίλτρο μετακινηθηκε επιτυχώς.';
+$messages['moveerror'] = 'Αδυναμία μετακίνησης επιλεγμένου φίλτρου. Προέκυψε σφάλμα στον διακομιστή.';
$messages['nametoolong'] = 'Το όνομα είναι πολύ μεγάλο.';
$messages['namereserved'] = 'Δεσμευμένο όνομα. ';
$messages['setexist'] = 'Set υπάρχει ήδη. ';
$messages['nodata'] = 'Τουλάχιστον μία θέση πρέπει να επιλεγεί!';
$messages['invaliddateformat'] = 'Μη έγκυρη ημερομηνία ή η ημερομηνία μορφής τμήμα';
+$messages['saveerror'] = 'Αδύνατη η αποθήκευση δεδομένων. Προέκυψε σφάλμα στον διακομιστή';
+$messages['vacationsaved'] = 'Τα δεδομένα διακοπών αποθηκεύτηκαν επιτυχώς.';
+$messages['emptyvacationbody'] = 'Απαιτείται σώμα για το μήνυμα διακοπών!';
?>
diff --git a/lib/plugins/managesieve/localization/en_CA.inc b/lib/plugins/managesieve/localization/en_CA.inc
index 311e595..400f835 100644
--- a/lib/plugins/managesieve/localization/en_CA.inc
+++ b/lib/plugins/managesieve/localization/en_CA.inc
@@ -1,192 +1,209 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filters';
$labels['managefilters'] = 'Manage incoming mail filters';
$labels['filtername'] = 'Filter name';
$labels['newfilter'] = 'New filter';
$labels['filteradd'] = 'Add filter';
$labels['filterdel'] = 'Delete filter';
$labels['moveup'] = 'Move up';
$labels['movedown'] = 'Move down';
$labels['filterallof'] = 'matching all of the following rules';
$labels['filteranyof'] = 'matching any of the following rules';
$labels['filterany'] = 'all messages';
$labels['filtercontains'] = 'contains';
$labels['filternotcontains'] = 'not contains';
$labels['filteris'] = 'is equal to';
$labels['filterisnot'] = 'is not equal to';
$labels['filterexists'] = 'exists';
$labels['filternotexists'] = 'not exists';
$labels['filtermatches'] = 'matches expression';
$labels['filternotmatches'] = 'not matches expression';
$labels['filterregex'] = 'matches regular expression';
$labels['filternotregex'] = 'not matches regular expression';
$labels['filterunder'] = 'under';
$labels['filterover'] = 'over';
$labels['addrule'] = 'Add rule';
$labels['delrule'] = 'Delete rule';
$labels['messagemoveto'] = 'Move message to';
$labels['messageredirect'] = 'Redirect message to';
$labels['messagecopyto'] = 'Copy message to';
$labels['messagesendcopy'] = 'Send message copy to';
$labels['messagereply'] = 'Reply with message';
$labels['messagedelete'] = 'Delete message';
$labels['messagediscard'] = 'Discard with message';
$labels['messagekeep'] = 'Keep message in Inbox';
$labels['messagesrules'] = 'For incoming mail:';
$labels['messagesactions'] = '...execute the following actions:';
$labels['add'] = 'Add';
$labels['del'] = 'Delete';
$labels['sender'] = 'Sender';
$labels['recipient'] = 'Recipient';
$labels['vacationaddr'] = 'My additional e-mail address(es):';
$labels['vacationdays'] = 'How often send messages (in days):';
$labels['vacationinterval'] = 'How often send messages:';
-$labels['days'] = 'days';
-$labels['seconds'] = 'seconds';
$labels['vacationreason'] = 'Message body (vacation reason):';
$labels['vacationsubject'] = 'Message subject:';
+$labels['days'] = 'days';
+$labels['seconds'] = 'seconds';
$labels['rulestop'] = 'Stop evaluating rules';
$labels['enable'] = 'Enable/Disable';
$labels['filterset'] = 'Filters set';
$labels['filtersets'] = 'Filter sets';
$labels['filtersetadd'] = 'Add filters set';
$labels['filtersetdel'] = 'Delete current filters set';
$labels['filtersetact'] = 'Activate current filters set';
$labels['filtersetdeact'] = 'Deactivate current filters set';
$labels['filterdef'] = 'Filter definition';
$labels['filtersetname'] = 'Filters set name';
$labels['newfilterset'] = 'New filters set';
$labels['active'] = 'active';
$labels['none'] = 'none';
$labels['fromset'] = 'from set';
$labels['fromfile'] = 'from file';
$labels['filterdisabled'] = 'Filter disabled';
$labels['countisgreaterthan'] = 'count is greater than';
$labels['countisgreaterthanequal'] = 'count is greater than or equal to';
$labels['countislessthan'] = 'count is less than';
$labels['countislessthanequal'] = 'count is less than or equal to';
$labels['countequals'] = 'count is equal to';
$labels['countnotequals'] = 'count is not equal to';
$labels['valueisgreaterthan'] = 'value is greater than';
$labels['valueisgreaterthanequal'] = 'value is greater than or equal to';
$labels['valueislessthan'] = 'value is less than';
$labels['valueislessthanequal'] = 'value is less than or equal to';
$labels['valueequals'] = 'value is equal to';
$labels['valuenotequals'] = 'value is not equal to';
$labels['setflags'] = 'Set flags to the message';
$labels['addflags'] = 'Add flags to the message';
$labels['removeflags'] = 'Remove flags from the message';
$labels['flagread'] = 'Read';
$labels['flagdeleted'] = 'Deleted';
$labels['flaganswered'] = 'Answered';
$labels['flagflagged'] = 'Flagged';
$labels['flagdraft'] = 'Draft';
$labels['setvariable'] = 'Set variable';
$labels['setvarname'] = 'Variable name:';
$labels['setvarvalue'] = 'Variable value:';
$labels['setvarmodifiers'] = 'Modifiers:';
$labels['varlower'] = 'lower-case';
$labels['varupper'] = 'upper-case';
$labels['varlowerfirst'] = 'first character lower-case';
$labels['varupperfirst'] = 'first character upper-case';
$labels['varquotewildcard'] = 'quote special characters';
$labels['varlength'] = 'length';
$labels['notify'] = 'Send notification';
-$labels['notifyaddress'] = 'To e-mail address:';
-$labels['notifybody'] = 'Notification body:';
-$labels['notifysubject'] = 'Notification subject:';
-$labels['notifyfrom'] = 'Notification sender:';
+$labels['notifytarget'] = 'Notification target:';
+$labels['notifymessage'] = 'Notification message (optional):';
+$labels['notifyoptions'] = 'Notification options (optional):';
+$labels['notifyfrom'] = 'Notification sender (optional):';
$labels['notifyimportance'] = 'Importance:';
$labels['notifyimportancelow'] = 'low';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'high';
+$labels['notifymethodmailto'] = 'Email';
+$labels['notifymethodtel'] = 'Phone';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Create filter';
$labels['usedata'] = 'Use following data in the filter:';
$labels['nextstep'] = 'Next Step';
$labels['...'] = '...';
$labels['currdate'] = 'Current date';
$labels['datetest'] = 'Date';
$labels['dateheader'] = 'header:';
$labels['year'] = 'year';
$labels['month'] = 'month';
$labels['day'] = 'day';
$labels['date'] = 'date (yyyy-mm-dd)';
$labels['julian'] = 'date (julian)';
$labels['hour'] = 'hour';
$labels['minute'] = 'minute';
$labels['second'] = 'second';
$labels['time'] = 'time (hh:mm:ss)';
$labels['iso8601'] = 'date (ISO8601)';
$labels['std11'] = 'date (RFC2822)';
$labels['zone'] = 'time-zone';
$labels['weekday'] = 'weekday (0-6)';
$labels['advancedopts'] = 'Advanced options';
$labels['body'] = 'Body';
$labels['address'] = 'address';
$labels['envelope'] = 'envelope';
$labels['modifier'] = 'modifier:';
$labels['text'] = 'text';
$labels['undecoded'] = 'undecoded (raw)';
$labels['contenttype'] = 'content type';
$labels['modtype'] = 'type:';
$labels['allparts'] = 'all';
$labels['domain'] = 'domain';
$labels['localpart'] = 'local part';
$labels['user'] = 'user';
$labels['detail'] = 'detail';
$labels['comparator'] = 'comparator:';
$labels['default'] = 'default';
$labels['octet'] = 'strict (octet)';
$labels['asciicasemap'] = 'case insensitive (ascii-casemap)';
$labels['asciinumeric'] = 'numeric (ascii-numeric)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'backwards';
+$labels['vacation'] = 'Vacation';
+$labels['vacation.reply'] = 'Reply message';
+$labels['vacation.advanced'] = 'Advanced settings';
+$labels['vacation.subject'] = 'Subject';
+$labels['vacation.body'] = 'Body';
+$labels['vacation.status'] = 'Status';
+$labels['vacation.on'] = 'On';
+$labels['vacation.off'] = 'Off';
+$labels['vacation.addresses'] = 'My additional addresses';
+$labels['vacation.interval'] = 'Reply interval';
+$labels['vacation.after'] = 'Put vacation rule after';
+$labels['vacation.saving'] = 'Saving data...';
$messages['filterunknownerror'] = 'Unknown server error.';
$messages['filterconnerror'] = 'Unable to connect to server.';
$messages['filterdeleteerror'] = 'Unable to delete filter. Server error occurred.';
$messages['filterdeleted'] = 'Filter deleted successfully.';
$messages['filtersaved'] = 'Filter saved successfully.';
$messages['filtersaveerror'] = 'Unable to save filter. Server error occurred.';
$messages['filterdeleteconfirm'] = 'Do you really want to delete selected filter?';
$messages['ruledeleteconfirm'] = 'Are you sure, you want to delete selected rule?';
$messages['actiondeleteconfirm'] = 'Are you sure, you want to delete selected action?';
$messages['forbiddenchars'] = 'Forbidden characters in field.';
$messages['cannotbeempty'] = 'Field cannot be empty.';
$messages['ruleexist'] = 'Filter with specified name already exists.';
$messages['setactivateerror'] = 'Unable to activate selected filters set. Server error occurred.';
$messages['setdeactivateerror'] = 'Unable to deactivate selected filters set. Server error occurred.';
$messages['setdeleteerror'] = 'Unable to delete selected filters set. Server error occurred.';
$messages['setactivated'] = 'Filters set activated successfully.';
$messages['setdeactivated'] = 'Filters set deactivated successfully.';
$messages['setdeleted'] = 'Filters set deleted successfully.';
$messages['setdeleteconfirm'] = 'Are you sure, you want to delete selected filters set?';
$messages['setcreateerror'] = 'Unable to create filters set. Server error occurred.';
$messages['setcreated'] = 'Filters set created successfully.';
$messages['activateerror'] = 'Unable to enable selected filter(s). Server error occurred.';
$messages['deactivateerror'] = 'Unable to disable selected filter(s). Server error occurred.';
$messages['deactivated'] = 'Filter(s) disabled successfully.';
$messages['activated'] = 'Filter(s) enabled successfully.';
$messages['moved'] = 'Filter moved successfully.';
$messages['moveerror'] = 'Unable to move selected filter. Server error occurred.';
$messages['nametoolong'] = 'Name too long.';
$messages['namereserved'] = 'Reserved name.';
$messages['setexist'] = 'Set already exists.';
$messages['nodata'] = 'At least one position must be selected!';
$messages['invaliddateformat'] = 'Invalid date or date part format';
+$messages['saveerror'] = 'Unable to save data. Server error occurred.';
+$messages['vacationsaved'] = 'Vacation data saved successfully.';
?>
diff --git a/lib/plugins/managesieve/localization/en_GB.inc b/lib/plugins/managesieve/localization/en_GB.inc
index f899e60..0cc8872 100644
--- a/lib/plugins/managesieve/localization/en_GB.inc
+++ b/lib/plugins/managesieve/localization/en_GB.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filters';
$labels['managefilters'] = 'Manage incoming mail filters';
$labels['filtername'] = 'Filter name';
$labels['newfilter'] = 'New filter';
$labels['filteradd'] = 'Add filter';
$labels['filterdel'] = 'Delete filter';
$labels['moveup'] = 'Move up';
$labels['movedown'] = 'Move down';
$labels['filterallof'] = 'matching all of the following rules';
$labels['filteranyof'] = 'matching any of the following rules';
$labels['filterany'] = 'all messages';
$labels['filtercontains'] = 'contains';
$labels['filternotcontains'] = 'not contains';
$labels['filteris'] = 'is equal to';
$labels['filterisnot'] = 'is not equal to';
$labels['filterexists'] = 'exists';
$labels['filternotexists'] = 'not exists';
$labels['filtermatches'] = 'matches expression';
$labels['filternotmatches'] = 'not matches expression';
$labels['filterregex'] = 'matches regular expression';
$labels['filternotregex'] = 'not matches regular expression';
$labels['filterunder'] = 'under';
$labels['filterover'] = 'over';
$labels['addrule'] = 'Add rule';
$labels['delrule'] = 'Delete rule';
$labels['messagemoveto'] = 'Move message to';
$labels['messageredirect'] = 'Redirect message to';
$labels['messagecopyto'] = 'Copy message to';
$labels['messagesendcopy'] = 'Send message copy to';
$labels['messagereply'] = 'Reply with message';
$labels['messagedelete'] = 'Delete message';
$labels['messagediscard'] = 'Discard with message';
$labels['messagekeep'] = 'Keep message in Inbox';
$labels['messagesrules'] = 'For incoming mail:';
$labels['messagesactions'] = '...execute the following actions:';
$labels['add'] = 'Add';
$labels['del'] = 'Delete';
$labels['sender'] = 'Sender';
$labels['recipient'] = 'Recipient';
$labels['vacationaddr'] = 'My additional e-mail address(es):';
$labels['vacationdays'] = 'How often send messages (in days):';
$labels['vacationinterval'] = 'How often send messages:';
-$labels['days'] = 'days';
-$labels['seconds'] = 'seconds';
$labels['vacationreason'] = 'Message body (vacation reason):';
$labels['vacationsubject'] = 'Message subject:';
+$labels['days'] = 'days';
+$labels['seconds'] = 'seconds';
$labels['rulestop'] = 'Stop evaluating rules';
$labels['enable'] = 'Enable/Disable';
$labels['filterset'] = 'Filters set';
$labels['filtersets'] = 'Filter sets';
$labels['filtersetadd'] = 'Add filters set';
$labels['filtersetdel'] = 'Delete current filters set';
$labels['filtersetact'] = 'Activate current filters set';
$labels['filtersetdeact'] = 'Deactivate current filters set';
$labels['filterdef'] = 'Filter definition';
$labels['filtersetname'] = 'Filters set name';
$labels['newfilterset'] = 'New filters set';
$labels['active'] = 'active';
$labels['none'] = 'none';
$labels['fromset'] = 'from set';
$labels['fromfile'] = 'from file';
$labels['filterdisabled'] = 'Filter disabled';
$labels['countisgreaterthan'] = 'count is greater than';
$labels['countisgreaterthanequal'] = 'count is greater than or equal to';
$labels['countislessthan'] = 'count is less than';
$labels['countislessthanequal'] = 'count is less than or equal to';
$labels['countequals'] = 'count is equal to';
$labels['countnotequals'] = 'count is not equal to';
$labels['valueisgreaterthan'] = 'value is greater than';
$labels['valueisgreaterthanequal'] = 'value is greater than or equal to';
$labels['valueislessthan'] = 'value is less than';
$labels['valueislessthanequal'] = 'value is less than or equal to';
$labels['valueequals'] = 'value is equal to';
$labels['valuenotequals'] = 'value is not equal to';
$labels['setflags'] = 'Set flags to the message';
$labels['addflags'] = 'Add flags to the message';
$labels['removeflags'] = 'Remove flags from the message';
$labels['flagread'] = 'Read';
$labels['flagdeleted'] = 'Deleted';
$labels['flaganswered'] = 'Answered';
$labels['flagflagged'] = 'Flagged';
$labels['flagdraft'] = 'Draft';
$labels['setvariable'] = 'Set variable';
$labels['setvarname'] = 'Variable name:';
$labels['setvarvalue'] = 'Variable value:';
$labels['setvarmodifiers'] = 'Modifiers:';
$labels['varlower'] = 'lower-case';
$labels['varupper'] = 'upper-case';
$labels['varlowerfirst'] = 'first character lower-case';
$labels['varupperfirst'] = 'first character upper-case';
$labels['varquotewildcard'] = 'quote special characters';
$labels['varlength'] = 'length';
$labels['notify'] = 'Send notification';
-$labels['notifyaddress'] = 'To e-mail address:';
-$labels['notifybody'] = 'Notification body:';
-$labels['notifysubject'] = 'Notification subject:';
-$labels['notifyfrom'] = 'Notification sender:';
+$labels['notifytarget'] = 'Notification target:';
+$labels['notifymessage'] = 'Notification message (optional):';
+$labels['notifyoptions'] = 'Notification options (optional):';
+$labels['notifyfrom'] = 'Notification sender (optional):';
$labels['notifyimportance'] = 'Importance:';
$labels['notifyimportancelow'] = 'low';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'high';
+$labels['notifymethodmailto'] = 'Email';
+$labels['notifymethodtel'] = 'Phone';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Create filter';
$labels['usedata'] = 'Use following data in the filter:';
$labels['nextstep'] = 'Next Step';
$labels['...'] = '...';
$labels['currdate'] = 'Current date';
$labels['datetest'] = 'Date';
$labels['dateheader'] = 'header:';
$labels['year'] = 'year';
$labels['month'] = 'month';
$labels['day'] = 'day';
$labels['date'] = 'date (yyyy-mm-dd)';
$labels['julian'] = 'date (julian)';
$labels['hour'] = 'hour';
$labels['minute'] = 'minute';
$labels['second'] = 'second';
$labels['time'] = 'time (hh:mm:ss)';
$labels['iso8601'] = 'date (ISO8601)';
$labels['std11'] = 'date (RFC2822)';
$labels['zone'] = 'time-zone';
$labels['weekday'] = 'weekday (0-6)';
$labels['advancedopts'] = 'Advanced options';
$labels['body'] = 'Body';
$labels['address'] = 'address';
$labels['envelope'] = 'envelope';
$labels['modifier'] = 'modifier:';
$labels['text'] = 'text';
$labels['undecoded'] = 'undecoded (raw)';
$labels['contenttype'] = 'content type';
$labels['modtype'] = 'type:';
$labels['allparts'] = 'all';
$labels['domain'] = 'domain';
$labels['localpart'] = 'local part';
$labels['user'] = 'user';
$labels['detail'] = 'detail';
$labels['comparator'] = 'comparator:';
$labels['default'] = 'default';
$labels['octet'] = 'strict (octet)';
$labels['asciicasemap'] = 'case insensitive (ascii-casemap)';
$labels['asciinumeric'] = 'numeric (ascii-numeric)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'backwards';
+$labels['vacation'] = 'Vacation';
+$labels['vacation.reply'] = 'Reply message';
+$labels['vacation.advanced'] = 'Advanced settings';
+$labels['vacation.subject'] = 'Subject';
+$labels['vacation.body'] = 'Body';
+$labels['vacation.start'] = 'Vacation start';
+$labels['vacation.end'] = 'Vacation end';
+$labels['vacation.status'] = 'Status';
+$labels['vacation.on'] = 'On';
+$labels['vacation.off'] = 'Off';
+$labels['vacation.addresses'] = 'My additional addresses';
+$labels['vacation.interval'] = 'Reply interval';
+$labels['vacation.after'] = 'Put vacation rule after';
+$labels['vacation.saving'] = 'Saving data...';
+$labels['vacation.action'] = 'Incoming message action';
+$labels['vacation.keep'] = 'Keep';
+$labels['vacation.discard'] = 'Discard';
+$labels['vacation.redirect'] = 'Redirect to';
+$labels['vacation.copy'] = 'Send copy to';
+$labels['arialabelfiltersetactions'] = 'Filter set actions';
+$labels['arialabelfilteractions'] = 'Filter actions';
+$labels['arialabelfilterform'] = 'Filter properties';
+$labels['ariasummaryfilterslist'] = 'List of filters';
+$labels['ariasummaryfiltersetslist'] = 'List of filter sets';
+$labels['filterstitle'] = 'Edit incoming mail filters';
+$labels['vacationtitle'] = 'Edit out-of-office rule';
$messages['filterunknownerror'] = 'Unknown server error';
$messages['filterconnerror'] = 'Unable to connect to managesieve server';
$messages['filterdeleteerror'] = 'Unable to delete filter. Server error occurred.';
$messages['filterdeleted'] = 'Filter deleted successfully';
$messages['filtersaved'] = 'Filter saved successfully';
$messages['filtersaveerror'] = 'Unable to save filter. Server error occurred.';
$messages['filterdeleteconfirm'] = 'Do you really want to delete selected filter?';
$messages['ruledeleteconfirm'] = 'Are you sure, you want to delete selected rule?';
$messages['actiondeleteconfirm'] = 'Are you sure, you want to delete selected action?';
$messages['forbiddenchars'] = 'Forbidden characters in field';
$messages['cannotbeempty'] = 'Field cannot be empty';
$messages['ruleexist'] = 'Filter with specified name already exists.';
$messages['setactivateerror'] = 'Unable to activate selected filters set. Server error occurred.';
$messages['setdeactivateerror'] = 'Unable to deactivate selected filters set. Server error occurred.';
$messages['setdeleteerror'] = 'Unable to delete selected filters set. Server error occurred.';
$messages['setactivated'] = 'Filters set activated successfully.';
$messages['setdeactivated'] = 'Filters set deactivated successfully.';
$messages['setdeleted'] = 'Filters set deleted successfully.';
$messages['setdeleteconfirm'] = 'Are you sure, you want to delete selected filters set?';
$messages['setcreateerror'] = 'Unable to create filters set. Server error occurred.';
$messages['setcreated'] = 'Filters set created successfully.';
$messages['activateerror'] = 'Unable to enable selected filter(s). Server error occurred.';
$messages['deactivateerror'] = 'Unable to disable selected filter(s). Server error occurred.';
$messages['deactivated'] = 'Filter(s) disabled successfully.';
$messages['activated'] = 'Filter(s) enabled successfully.';
$messages['moved'] = 'Filter moved successfully.';
$messages['moveerror'] = 'Unable to move selected filter. Server error occurred.';
$messages['nametoolong'] = 'Name too long.';
$messages['namereserved'] = 'Reserved name.';
$messages['setexist'] = 'Set already exists.';
$messages['nodata'] = 'At least one position must be selected!';
$messages['invaliddateformat'] = 'Invalid date or date part format';
+$messages['saveerror'] = 'Unable to save data. Server error occurred.';
+$messages['vacationsaved'] = 'Vacation data saved successfully.';
+$messages['emptyvacationbody'] = 'Body of vacation message is required!';
?>
diff --git a/lib/plugins/managesieve/localization/en_US.inc b/lib/plugins/managesieve/localization/en_US.inc
index ac766f9..f455d55 100644
--- a/lib/plugins/managesieve/localization/en_US.inc
+++ b/lib/plugins/managesieve/localization/en_US.inc
@@ -1,230 +1,229 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filters';
$labels['managefilters'] = 'Manage incoming mail filters';
$labels['filtername'] = 'Filter name';
$labels['newfilter'] = 'New filter';
$labels['filteradd'] = 'Add filter';
$labels['filterdel'] = 'Delete filter';
$labels['moveup'] = 'Move up';
$labels['movedown'] = 'Move down';
$labels['filterallof'] = 'matching all of the following rules';
$labels['filteranyof'] = 'matching any of the following rules';
$labels['filterany'] = 'all messages';
$labels['filtercontains'] = 'contains';
$labels['filternotcontains'] = 'not contains';
$labels['filteris'] = 'is equal to';
$labels['filterisnot'] = 'is not equal to';
$labels['filterexists'] = 'exists';
$labels['filternotexists'] = 'not exists';
$labels['filtermatches'] = 'matches expression';
$labels['filternotmatches'] = 'not matches expression';
$labels['filterregex'] = 'matches regular expression';
$labels['filternotregex'] = 'not matches regular expression';
$labels['filterunder'] = 'under';
$labels['filterover'] = 'over';
$labels['addrule'] = 'Add rule';
$labels['delrule'] = 'Delete rule';
$labels['messagemoveto'] = 'Move message to';
$labels['messageredirect'] = 'Redirect message to';
$labels['messagecopyto'] = 'Copy message to';
$labels['messagesendcopy'] = 'Send message copy to';
$labels['messagereply'] = 'Reply with message';
$labels['messagedelete'] = 'Delete message';
$labels['messagediscard'] = 'Discard with message';
$labels['messagekeep'] = 'Keep message in Inbox';
$labels['messagesrules'] = 'For incoming mail:';
$labels['messagesactions'] = '...execute the following actions:';
$labels['add'] = 'Add';
$labels['del'] = 'Delete';
$labels['sender'] = 'Sender';
$labels['recipient'] = 'Recipient';
$labels['vacationaddr'] = 'My additional e-mail address(es):';
$labels['vacationdays'] = 'How often send messages (in days):';
$labels['vacationinterval'] = 'How often send messages:';
$labels['vacationreason'] = 'Message body (vacation reason):';
$labels['vacationsubject'] = 'Message subject:';
$labels['days'] = 'days';
$labels['seconds'] = 'seconds';
$labels['rulestop'] = 'Stop evaluating rules';
$labels['enable'] = 'Enable/Disable';
$labels['filterset'] = 'Filters set';
$labels['filtersets'] = 'Filter sets';
$labels['filtersetadd'] = 'Add filters set';
$labels['filtersetdel'] = 'Delete current filters set';
$labels['filtersetact'] = 'Activate current filters set';
$labels['filtersetdeact'] = 'Deactivate current filters set';
$labels['filterdef'] = 'Filter definition';
$labels['filtersetname'] = 'Filters set name';
$labels['newfilterset'] = 'New filters set';
$labels['active'] = 'active';
$labels['none'] = 'none';
$labels['fromset'] = 'from set';
$labels['fromfile'] = 'from file';
$labels['filterdisabled'] = 'Filter disabled';
$labels['countisgreaterthan'] = 'count is greater than';
$labels['countisgreaterthanequal'] = 'count is greater than or equal to';
$labels['countislessthan'] = 'count is less than';
$labels['countislessthanequal'] = 'count is less than or equal to';
$labels['countequals'] = 'count is equal to';
$labels['countnotequals'] = 'count is not equal to';
$labels['valueisgreaterthan'] = 'value is greater than';
$labels['valueisgreaterthanequal'] = 'value is greater than or equal to';
$labels['valueislessthan'] = 'value is less than';
$labels['valueislessthanequal'] = 'value is less than or equal to';
$labels['valueequals'] = 'value is equal to';
$labels['valuenotequals'] = 'value is not equal to';
$labels['setflags'] = 'Set flags to the message';
$labels['addflags'] = 'Add flags to the message';
$labels['removeflags'] = 'Remove flags from the message';
$labels['flagread'] = 'Read';
$labels['flagdeleted'] = 'Deleted';
$labels['flaganswered'] = 'Answered';
$labels['flagflagged'] = 'Flagged';
$labels['flagdraft'] = 'Draft';
$labels['setvariable'] = 'Set variable';
$labels['setvarname'] = 'Variable name:';
$labels['setvarvalue'] = 'Variable value:';
$labels['setvarmodifiers'] = 'Modifiers:';
$labels['varlower'] = 'lower-case';
$labels['varupper'] = 'upper-case';
$labels['varlowerfirst'] = 'first character lower-case';
$labels['varupperfirst'] = 'first character upper-case';
$labels['varquotewildcard'] = 'quote special characters';
$labels['varlength'] = 'length';
$labels['notify'] = 'Send notification';
$labels['notifytarget'] = 'Notification target:';
$labels['notifymessage'] = 'Notification message (optional):';
$labels['notifyoptions'] = 'Notification options (optional):';
$labels['notifyfrom'] = 'Notification sender (optional):';
$labels['notifyimportance'] = 'Importance:';
$labels['notifyimportancelow'] = 'low';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'high';
$labels['notifymethodmailto'] = 'Email';
$labels['notifymethodtel'] = 'Phone';
$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Create filter';
$labels['usedata'] = 'Use following data in the filter:';
$labels['nextstep'] = 'Next Step';
$labels['...'] = '...';
$labels['currdate'] = 'Current date';
$labels['datetest'] = 'Date';
$labels['dateheader'] = 'header:';
$labels['year'] = 'year';
$labels['month'] = 'month';
$labels['day'] = 'day';
$labels['date'] = 'date (yyyy-mm-dd)';
$labels['julian'] = 'date (julian)';
$labels['hour'] = 'hour';
$labels['minute'] = 'minute';
$labels['second'] = 'second';
$labels['time'] = 'time (hh:mm:ss)';
$labels['iso8601'] = 'date (ISO8601)';
$labels['std11'] = 'date (RFC2822)';
$labels['zone'] = 'time-zone';
$labels['weekday'] = 'weekday (0-6)';
$labels['advancedopts'] = 'Advanced options';
$labels['body'] = 'Body';
$labels['address'] = 'address';
$labels['envelope'] = 'envelope';
$labels['modifier'] = 'modifier:';
$labels['text'] = 'text';
$labels['undecoded'] = 'undecoded (raw)';
$labels['contenttype'] = 'content type';
$labels['modtype'] = 'type:';
$labels['allparts'] = 'all';
$labels['domain'] = 'domain';
$labels['localpart'] = 'local part';
$labels['user'] = 'user';
$labels['detail'] = 'detail';
$labels['comparator'] = 'comparator:';
$labels['default'] = 'default';
$labels['octet'] = 'strict (octet)';
$labels['asciicasemap'] = 'case insensitive (ascii-casemap)';
$labels['asciinumeric'] = 'numeric (ascii-numeric)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'backwards';
$labels['vacation'] = 'Vacation';
$labels['vacation.reply'] = 'Reply message';
$labels['vacation.advanced'] = 'Advanced settings';
$labels['vacation.subject'] = 'Subject';
$labels['vacation.body'] = 'Body';
-$labels['vacation.dates'] = 'Vacation time';
-$labels['vacation.from'] = 'From:';
-$labels['vacation.to'] = 'To:';
+$labels['vacation.start'] = 'Vacation start';
+$labels['vacation.end'] = 'Vacation end';
$labels['vacation.status'] = 'Status';
$labels['vacation.on'] = 'On';
$labels['vacation.off'] = 'Off';
$labels['vacation.addresses'] = 'My additional addresses';
$labels['vacation.interval'] = 'Reply interval';
$labels['vacation.after'] = 'Put vacation rule after';
$labels['vacation.saving'] = 'Saving data...';
$labels['vacation.action'] = 'Incoming message action';
$labels['vacation.keep'] = 'Keep';
$labels['vacation.discard'] = 'Discard';
$labels['vacation.redirect'] = 'Redirect to';
$labels['vacation.copy'] = 'Send copy to';
$labels['arialabelfiltersetactions'] = 'Filter set actions';
$labels['arialabelfilteractions'] = 'Filter actions';
$labels['arialabelfilterform'] = 'Filter properties';
$labels['ariasummaryfilterslist'] = 'List of filters';
$labels['ariasummaryfiltersetslist'] = 'List of filter sets';
$labels['filterstitle'] = 'Edit incoming mail filters';
$labels['vacationtitle'] = 'Edit out-of-office rule';
$messages = array();
$messages['filterunknownerror'] = 'Unknown server error.';
$messages['filterconnerror'] = 'Unable to connect to server.';
$messages['filterdeleteerror'] = 'Unable to delete filter. Server error occurred.';
$messages['filterdeleted'] = 'Filter deleted successfully.';
$messages['filtersaved'] = 'Filter saved successfully.';
$messages['filtersaveerror'] = 'Unable to save filter. Server error occurred.';
$messages['filterdeleteconfirm'] = 'Do you really want to delete selected filter?';
$messages['ruledeleteconfirm'] = 'Are you sure, you want to delete selected rule?';
$messages['actiondeleteconfirm'] = 'Are you sure, you want to delete selected action?';
$messages['forbiddenchars'] = 'Forbidden characters in field.';
$messages['cannotbeempty'] = 'Field cannot be empty.';
$messages['ruleexist'] = 'Filter with specified name already exists.';
$messages['setactivateerror'] = 'Unable to activate selected filters set. Server error occurred.';
$messages['setdeactivateerror'] = 'Unable to deactivate selected filters set. Server error occurred.';
$messages['setdeleteerror'] = 'Unable to delete selected filters set. Server error occurred.';
$messages['setactivated'] = 'Filters set activated successfully.';
$messages['setdeactivated'] = 'Filters set deactivated successfully.';
$messages['setdeleted'] = 'Filters set deleted successfully.';
$messages['setdeleteconfirm'] = 'Are you sure, you want to delete selected filters set?';
$messages['setcreateerror'] = 'Unable to create filters set. Server error occurred.';
$messages['setcreated'] = 'Filters set created successfully.';
$messages['activateerror'] = 'Unable to enable selected filter(s). Server error occurred.';
$messages['deactivateerror'] = 'Unable to disable selected filter(s). Server error occurred.';
$messages['deactivated'] = 'Filter(s) disabled successfully.';
$messages['activated'] = 'Filter(s) enabled successfully.';
$messages['moved'] = 'Filter moved successfully.';
$messages['moveerror'] = 'Unable to move selected filter. Server error occurred.';
$messages['nametoolong'] = 'Name too long.';
$messages['namereserved'] = 'Reserved name.';
$messages['setexist'] = 'Set already exists.';
$messages['nodata'] = 'At least one position must be selected!';
$messages['invaliddateformat'] = 'Invalid date or date part format';
$messages['saveerror'] = 'Unable to save data. Server error occurred.';
$messages['vacationsaved'] = 'Vacation data saved successfully.';
$messages['emptyvacationbody'] = 'Body of vacation message is required!';
?>
diff --git a/lib/plugins/managesieve/localization/es_419.inc b/lib/plugins/managesieve/localization/es_419.inc
index 45b5dfb..6131895 100644
--- a/lib/plugins/managesieve/localization/es_419.inc
+++ b/lib/plugins/managesieve/localization/es_419.inc
@@ -1,225 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtros';
$labels['managefilters'] = 'Administrar filtros de correos entrantes';
$labels['filtername'] = 'Nombre del filtro';
$labels['newfilter'] = 'Filtro nuevo';
$labels['filteradd'] = 'Agregar filtro';
$labels['filterdel'] = 'Eliminar filtro';
$labels['moveup'] = 'Mover hacia arriba';
$labels['movedown'] = 'Mover hacia abajo';
$labels['filterallof'] = 'coincide con todas las reglas siguientes';
$labels['filteranyof'] = 'coincide con cualquiera de las reglas siguientes';
$labels['filterany'] = 'todos los mensajes';
$labels['filtercontains'] = 'contiene';
$labels['filternotcontains'] = 'no contiene';
$labels['filteris'] = 'es igual a';
$labels['filterisnot'] = 'no es igual a';
$labels['filterexists'] = 'existe';
$labels['filternotexists'] = 'no existe';
$labels['filtermatches'] = 'coincide con la expresión';
$labels['filternotmatches'] = 'no coincide con la expresión';
$labels['filterregex'] = 'coincide con la expresión regular';
$labels['filternotregex'] = 'no coincide con la expresión regular';
$labels['filterunder'] = 'bajo';
$labels['filterover'] = 'sobre';
$labels['addrule'] = 'Agregar regla';
$labels['delrule'] = 'Eliminar regla';
$labels['messagemoveto'] = 'Mover mensaje a';
$labels['messageredirect'] = 'Redireccionar mensaje a';
$labels['messagecopyto'] = 'Copiar mensaje a';
$labels['messagesendcopy'] = 'Enviar una copia del mensaje a ';
$labels['messagereply'] = 'Responder con el mensaje';
$labels['messagedelete'] = 'Eliminar mensaje';
$labels['messagediscard'] = 'Descartar el mensaje';
$labels['messagekeep'] = 'Mantener mensaje en la bandeja de entrada';
$labels['messagesrules'] = 'Para correo entrante:';
$labels['messagesactions'] = '... ejecutar las siguientes acciones:';
$labels['add'] = 'Agregar';
$labels['del'] = 'Eliminar';
$labels['sender'] = 'Remitente';
$labels['recipient'] = 'Destinatario';
$labels['vacationaddr'] = 'Mis direccion(es) adiconal(es):';
$labels['vacationdays'] = 'Cuan a menudo enviar mensajes (en días):';
$labels['vacationinterval'] = '¿Con qué frecuencia enviar mensajes?:';
$labels['vacationreason'] = 'Cuerpo del mensaje (motivo de las vacaciones):';
$labels['vacationsubject'] = 'Asunto del mensaje:';
$labels['days'] = 'días';
$labels['seconds'] = 'segundos';
$labels['rulestop'] = 'Detener la evaluación de reglas';
$labels['enable'] = 'Habilitar/Deshabilitar';
$labels['filterset'] = 'Set de filtros';
$labels['filtersets'] = 'Filtro acciona';
$labels['filtersetadd'] = 'Agregar set de filtros';
$labels['filtersetdel'] = 'Eliminar set de filtros actual';
$labels['filtersetact'] = 'Activar set de filtros actual';
$labels['filtersetdeact'] = 'Desactivar set de filtros actual';
$labels['filterdef'] = 'Definición del filtro';
$labels['filtersetname'] = 'Nombre del set de filtros';
$labels['newfilterset'] = 'Nuevo set de filtros';
$labels['active'] = 'activo';
$labels['none'] = 'ninguno';
$labels['fromset'] = 'desde set';
$labels['fromfile'] = 'desde archivo';
$labels['filterdisabled'] = 'filtro deshabilitado';
$labels['countisgreaterthan'] = 'la cuenta es mayor a';
$labels['countisgreaterthanequal'] = 'la cuenta es mayor o igual a ';
$labels['countislessthan'] = 'la cuenta es menor que';
$labels['countislessthanequal'] = 'la cuenta es menor o igual que';
$labels['countequals'] = 'la cuenta es igual a ';
$labels['countnotequals'] = 'la cuenta no es menor a';
$labels['valueisgreaterthan'] = 'el valor es mayor que';
$labels['valueisgreaterthanequal'] = 'el balor es mayor o igual que ';
$labels['valueislessthan'] = 'el valor es menor que ';
$labels['valueislessthanequal'] = 'el valor es menor o igual que ';
$labels['valueequals'] = 'el valor es igual a ';
$labels['valuenotequals'] = 'el valor no es igual a';
$labels['setflags'] = 'Colocar etiquetas al mensaje';
$labels['addflags'] = 'Agrega etiquetas al mensaje';
$labels['removeflags'] = 'Eliminar etiquetas al mensaje';
$labels['flagread'] = 'Leido';
$labels['flagdeleted'] = 'Eliminado';
$labels['flaganswered'] = 'Respondido';
$labels['flagflagged'] = 'Etiquetado';
$labels['flagdraft'] = 'Borrador';
$labels['setvariable'] = 'Establecer variable';
$labels['setvarname'] = 'Nombre de la variable:';
$labels['setvarvalue'] = 'Valor de la variable:';
$labels['setvarmodifiers'] = 'Modificadores:';
$labels['varlower'] = 'minúscula';
$labels['varupper'] = 'mayúscula';
$labels['varlowerfirst'] = 'primer carácter en minúscula';
$labels['varupperfirst'] = 'primer carácter en mayúscula';
$labels['varquotewildcard'] = 'citar carácteres especiales';
$labels['varlength'] = 'largo';
$labels['notify'] = 'Enviar notificación';
$labels['notifytarget'] = 'Destinatario de la notificación:';
$labels['notifymessage'] = 'Mensaje de notificación (opcional):';
$labels['notifyoptions'] = 'Opciones de notificación (opcional):';
$labels['notifyfrom'] = 'Remitente de la notificación (opcional):';
$labels['notifyimportance'] = 'Importancia:';
$labels['notifyimportancelow'] = 'baja';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'alta';
$labels['notifymethodmailto'] = 'Correo electrónico';
$labels['notifymethodtel'] = 'Teléfono';
$labels['notifymethodsms'] = 'Mensaje de texto';
$labels['filtercreate'] = 'Crear filtro';
$labels['usedata'] = 'Usar los datos siguientes en el filtro:';
$labels['nextstep'] = 'Paso siguiente';
$labels['...'] = '...';
$labels['currdate'] = 'Fecha actual';
$labels['datetest'] = 'Fecha';
$labels['dateheader'] = 'encabezado:';
$labels['year'] = 'año';
$labels['month'] = 'mes';
$labels['day'] = 'día';
$labels['date'] = 'fecha(aaaa-mm-dd)';
$labels['julian'] = 'fecha (julian)';
$labels['hour'] = 'hora';
$labels['minute'] = 'minuto';
$labels['second'] = 'segundo';
$labels['time'] = 'hora (hh:mm:ss)';
$labels['iso8601'] = 'fecha (ISO8601)';
$labels['std11'] = 'fecha (RFC2822)';
$labels['zone'] = 'zona horaria';
$labels['weekday'] = 'día de la semana (0-6)';
$labels['advancedopts'] = 'Opciones avanzadas';
$labels['body'] = 'Cuerpo';
$labels['address'] = 'dirección';
$labels['envelope'] = 'sobre';
$labels['modifier'] = 'modificador:';
$labels['text'] = 'texto';
$labels['undecoded'] = 'decodificado (crudo)';
$labels['contenttype'] = 'tipo de contenido';
$labels['modtype'] = 'tipo:';
$labels['allparts'] = 'todo';
$labels['domain'] = 'dominio';
$labels['localpart'] = 'parte local';
$labels['user'] = 'usuario';
$labels['detail'] = 'detalle';
$labels['comparator'] = 'comparador:';
$labels['default'] = 'predeterminado';
$labels['octet'] = 'estricto (octeto)';
$labels['asciicasemap'] = 'no sensible a mayúsculas y minúsculas (mapero-ascii)';
$labels['asciinumeric'] = 'numérico (ascii-numérico)';
$labels['index'] = 'índice:';
$labels['indexlast'] = 'hacia atrás';
$labels['vacation'] = 'Vacaciones';
$labels['vacation.reply'] = 'Responder mensaje';
$labels['vacation.advanced'] = 'Opciones avanzadas';
$labels['vacation.subject'] = 'Asunto';
$labels['vacation.body'] = 'Cuerpo';
-$labels['vacation.dates'] = 'Horario de vacaciones';
-$labels['vacation.from'] = 'De:';
-$labels['vacation.to'] = 'Para:';
+$labels['vacation.start'] = 'Inicio de vacaciones';
+$labels['vacation.end'] = 'Final de vacaciones';
$labels['vacation.status'] = 'Estado';
$labels['vacation.on'] = 'Encendido';
$labels['vacation.off'] = 'Apagado';
$labels['vacation.addresses'] = 'Mis direcciones adicionales';
$labels['vacation.interval'] = 'Intervalo de respuesta';
$labels['vacation.after'] = 'Colocar regla de vacaciones luego';
$labels['vacation.saving'] = 'Guardando información...';
$labels['vacation.action'] = 'Acción para mensaje entrante';
$labels['vacation.keep'] = 'Mantener';
$labels['vacation.discard'] = 'Descartar';
$labels['vacation.redirect'] = 'Redireccionar a';
$labels['vacation.copy'] = 'Enviar una copia a';
$labels['arialabelfiltersetactions'] = 'Acciones del set de filtros';
$labels['arialabelfilteractions'] = 'Acciones de filtros';
$labels['arialabelfilterform'] = 'Propiedades de filtros';
$labels['ariasummaryfilterslist'] = 'Lista de filtros';
$labels['ariasummaryfiltersetslist'] = 'Lista de set de filtros';
$labels['filterstitle'] = 'Administrar filtros de correos entrantes';
$labels['vacationtitle'] = 'Editar regla de fuera de oficina';
$messages['filterunknownerror'] = 'Error de servidor desconocido.';
$messages['filterconnerror'] = 'No se puede conectar al servidor.';
$messages['filterdeleteerror'] = 'No se puede eliminar el filtro. Ocurrió un error de servidor.';
$messages['filterdeleted'] = 'Filtro eliminado exitosamente.';
$messages['filtersaved'] = 'Filtro guardado exitosamente.';
$messages['filtersaveerror'] = 'No es posible guardar el filtro. Ha ocurrido un error de servidor.';
$messages['filterdeleteconfirm'] = '¿Estás seguro que quieres eliminar el filtro seleccionado?';
$messages['ruledeleteconfirm'] = '¿Estás seguro que quieres eliminar la regla seleccionada?';
$messages['actiondeleteconfirm'] = '¿Estás seguro que queires eliminar la acción seleccionada?';
$messages['forbiddenchars'] = 'Carácteres ilegales en el campo.';
$messages['cannotbeempty'] = 'El campo no puede estar vacio.';
$messages['ruleexist'] = 'Ya existe un filtro con el nombre especificado.';
$messages['setactivateerror'] = 'No es posible activar el set de filtros seleccionado. Ha ocurrido un error de servidor.';
$messages['setdeactivateerror'] = 'No es posible desactivar el set de filtros selecciona. Ha ocurrido un error de servidor.';
$messages['setdeleteerror'] = 'No es posible eliminar el set de filtros seleccionado. Ha ocurrido un error de servidor.';
$messages['setactivated'] = 'Set de filtros activado exitosamente.';
$messages['setdeactivated'] = 'Set de filtros desactivado exitosamente.';
$messages['setdeleted'] = 'Set de filtroseliminado exitosamente.';
$messages['setdeleteconfirm'] = '¿Estas seguro que deseas eliminar el set de filtros seleccionado?';
$messages['setcreateerror'] = 'No es posible crear el set de filtros. Ha ocurrido un error de servidor.';
$messages['setcreated'] = 'Set de filtros creado exitosamente.';
$messages['activateerror'] = 'No es posible habilitar los filtros seleccionados. Ha ocurrido un error de servidor.';
$messages['deactivateerror'] = 'No es posible deshabilitar los filtros seleccionados. Ha ocurrido un error de servidor.';
$messages['deactivated'] = 'Filtro(s) deshabilitado(s) exitosamente.';
$messages['activated'] = 'Filtro(s) habilitado(s) exitosamente.';
$messages['moved'] = 'Filtro movido exitosamente.';
$messages['moveerror'] = 'No es posible mover los filtros seleccionados. Ha ocurrido un error de servidor.';
$messages['nametoolong'] = 'Nombre demasiado largo.';
$messages['namereserved'] = 'Nombre reservado.';
$messages['setexist'] = 'Set ya existe.';
$messages['nodata'] = 'Debes seleccionar al menos una posición.';
$messages['invaliddateformat'] = 'Fecha o parte del formato no válido';
$messages['saveerror'] = 'No es posible guardar la información. Ha ocurrido un error de servidor.';
$messages['vacationsaved'] = 'Información de vacaciones guardada exitosamente.';
$messages['emptyvacationbody'] = 'Cuerpo del mensaje de vacaciones es requerido!';
?>
diff --git a/lib/plugins/managesieve/localization/es_AR.inc b/lib/plugins/managesieve/localization/es_AR.inc
index 6b3749d..6ac6533 100644
--- a/lib/plugins/managesieve/localization/es_AR.inc
+++ b/lib/plugins/managesieve/localization/es_AR.inc
@@ -1,225 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtros';
$labels['managefilters'] = 'Administrar filtros de correo entrante';
$labels['filtername'] = 'Nombre del filtro';
$labels['newfilter'] = 'Nuevo filtro';
$labels['filteradd'] = 'Agregar filtro';
$labels['filterdel'] = 'Eliminar filtro';
$labels['moveup'] = 'Mover arriba';
$labels['movedown'] = 'Mover abajo';
$labels['filterallof'] = 'coinidir con todas las reglas siguientes';
$labels['filteranyof'] = 'coincidir con alguna de las reglas siguientes';
$labels['filterany'] = 'todos los mensajes';
$labels['filtercontains'] = 'contiene';
$labels['filternotcontains'] = 'no contiene';
$labels['filteris'] = 'es igual a';
$labels['filterisnot'] = 'no es igual a';
$labels['filterexists'] = 'existe';
$labels['filternotexists'] = 'no existe';
$labels['filtermatches'] = 'coincide con la expresión';
$labels['filternotmatches'] = 'no coindice con la expresión';
$labels['filterregex'] = 'coincide con la expresión regular';
$labels['filternotregex'] = 'no coincide con la expresión regular';
$labels['filterunder'] = 'bajo';
$labels['filterover'] = 'sobre';
$labels['addrule'] = 'Agregar regla';
$labels['delrule'] = 'Eliminar regla';
$labels['messagemoveto'] = 'Mover mensaje a';
$labels['messageredirect'] = 'Redirigir mensaje a';
$labels['messagecopyto'] = 'Copiar mensaje a';
$labels['messagesendcopy'] = 'Enviar copia del mensaje a';
$labels['messagereply'] = 'Responder con un mensaje';
$labels['messagedelete'] = 'Eliminar mensaje';
$labels['messagediscard'] = 'Descartar con un mensaje';
$labels['messagekeep'] = 'Mantener mensajes en bandeja de entrada';
$labels['messagesrules'] = 'Para el correo entrante:';
$labels['messagesactions'] = '... ejecutar las siguientes acciones:';
$labels['add'] = 'Agregar';
$labels['del'] = 'Eliminar';
$labels['sender'] = 'Remitente';
$labels['recipient'] = 'Destinatario';
$labels['vacationaddr'] = 'Mi(s) direccion(es) de e-mail adicional(es):';
$labels['vacationdays'] = 'Cada cuanto enviar mensajes (en días):';
$labels['vacationinterval'] = 'Enviar mensajes cada:';
$labels['vacationreason'] = 'Cuerpo del mensaje (razón de vacaciones):';
$labels['vacationsubject'] = 'Asunto del mensaje:';
$labels['days'] = 'dias';
$labels['seconds'] = 'segundos';
$labels['rulestop'] = 'Parar de evaluar reglas';
$labels['enable'] = 'Habilitar/Deshabilitar';
$labels['filterset'] = 'Conjunto de filtros';
$labels['filtersets'] = 'Filtro activa';
$labels['filtersetadd'] = 'Agregar conjunto de filtros';
$labels['filtersetdel'] = 'Eliminar conjunto de filtros';
$labels['filtersetact'] = 'Activar conjunto de filtros';
$labels['filtersetdeact'] = 'Deactivar conjunto de filtros';
$labels['filterdef'] = 'Definicion del conjunto de filtros';
$labels['filtersetname'] = 'Nombre del conjunto de filtros';
$labels['newfilterset'] = 'Nuevo conjunto de filtros';
$labels['active'] = 'Activar';
$labels['none'] = 'none';
$labels['fromset'] = 'desde conjunto';
$labels['fromfile'] = 'desde archivo';
$labels['filterdisabled'] = 'Filtro deshabilitado';
$labels['countisgreaterthan'] = 'la cuenta es mayor a';
$labels['countisgreaterthanequal'] = 'la cuenta es mayor o igual a';
$labels['countislessthan'] = 'la cuenta es menor a';
$labels['countislessthanequal'] = 'la cuenta es menor o igual a';
$labels['countequals'] = 'la cuenta es igual a';
$labels['countnotequals'] = 'la cuenta no es igual a';
$labels['valueisgreaterthan'] = 'el valor es mayor a';
$labels['valueisgreaterthanequal'] = 'el valor es mayor o igual a';
$labels['valueislessthan'] = 'el valor es menor a';
$labels['valueislessthanequal'] = 'el valor es menor o igual a';
$labels['valueequals'] = 'el valor es igual a';
$labels['valuenotequals'] = 'el valor no es igual a';
$labels['setflags'] = 'Configurar marcas del mensaje';
$labels['addflags'] = 'Agregar marcas al mensaje';
$labels['removeflags'] = 'Eliminar marcas del mensaje';
$labels['flagread'] = 'Leer';
$labels['flagdeleted'] = 'Eliminado';
$labels['flaganswered'] = 'Respondido';
$labels['flagflagged'] = 'Marcado';
$labels['flagdraft'] = 'Borrador';
$labels['setvariable'] = 'Setear variable';
$labels['setvarname'] = 'Nombre de variable:';
$labels['setvarvalue'] = 'Valor de variable:';
$labels['setvarmodifiers'] = 'Modificadores:';
$labels['varlower'] = 'minúscula';
$labels['varupper'] = 'mayúscula';
$labels['varlowerfirst'] = 'primer caracter en minúscula';
$labels['varupperfirst'] = 'primer caracter en mayúscula';
$labels['varquotewildcard'] = 'citar caracteres especiales';
$labels['varlength'] = 'longitud';
$labels['notify'] = 'Enviar notificación';
$labels['notifytarget'] = 'Objetivo de la notificación:';
$labels['notifymessage'] = 'Mensaje de notificación (opcional):';
$labels['notifyoptions'] = 'Opciones de notificación (opcional):';
$labels['notifyfrom'] = 'Remitente de la notificación (opcional):';
$labels['notifyimportance'] = 'Importancia:';
$labels['notifyimportancelow'] = 'baja';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'alta';
$labels['notifymethodmailto'] = 'Email';
$labels['notifymethodtel'] = 'Teléfono';
$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Crear filtro';
$labels['usedata'] = 'Usar la siguiente información en el filtro:';
$labels['nextstep'] = 'Siguiente paso';
$labels['...'] = '...';
$labels['currdate'] = 'Fecha actual';
$labels['datetest'] = 'Fecha';
$labels['dateheader'] = 'encabezado:';
$labels['year'] = 'año';
$labels['month'] = 'mes';
$labels['day'] = 'dia';
$labels['date'] = 'fecha (yyyy-mm-dd)';
$labels['julian'] = 'fecha (juliano)';
$labels['hour'] = 'hora';
$labels['minute'] = 'minuto';
$labels['second'] = 'segundo';
$labels['time'] = 'hora (hh:mm:ss)';
$labels['iso8601'] = 'fecha (ISO8601)';
$labels['std11'] = 'fecha (RFC2822)';
$labels['zone'] = 'zona horaria';
$labels['weekday'] = 'día de la semana (0-6)';
$labels['advancedopts'] = 'Opciones avanzadas';
$labels['body'] = 'Cuerpo';
$labels['address'] = 'dirección';
$labels['envelope'] = 'envoltura';
$labels['modifier'] = 'modificador:';
$labels['text'] = 'texto';
$labels['undecoded'] = 'sin decodificar (crudo)';
$labels['contenttype'] = 'tipo de contenido';
$labels['modtype'] = 'tipo:';
$labels['allparts'] = 'todo';
$labels['domain'] = 'dominio';
$labels['localpart'] = 'parte local';
$labels['user'] = 'usuario';
$labels['detail'] = 'detalle';
$labels['comparator'] = 'comparador:';
$labels['default'] = 'por defecto';
$labels['octet'] = 'estricto (octeto)';
$labels['asciicasemap'] = 'no sensible a minúsculas o mayúsculas (ascii-casemap)';
$labels['asciinumeric'] = 'numérico (ascii-numeric)';
$labels['index'] = 'índice:';
$labels['indexlast'] = 'hacia atrás';
$labels['vacation'] = 'Vacaciones';
$labels['vacation.reply'] = 'Responder mensaje';
$labels['vacation.advanced'] = 'Opciones avanzdas';
$labels['vacation.subject'] = 'Asunto';
$labels['vacation.body'] = 'Cuerpo';
-$labels['vacation.dates'] = 'Período de vacaciones';
-$labels['vacation.from'] = 'De:';
-$labels['vacation.to'] = 'Para:';
+$labels['vacation.start'] = 'Inicio de vacaciones';
+$labels['vacation.end'] = 'Final de vacaciones';
$labels['vacation.status'] = 'Estado';
$labels['vacation.on'] = 'On';
$labels['vacation.off'] = 'Off';
$labels['vacation.addresses'] = 'Mis direcciones adicionales';
$labels['vacation.interval'] = 'Intervalo de respuesta';
$labels['vacation.after'] = 'Colocar luego regla de vacaciones ';
$labels['vacation.saving'] = 'Guardando información...';
$labels['vacation.action'] = 'Acción para mensaje entrante';
$labels['vacation.keep'] = 'Mantener';
$labels['vacation.discard'] = 'Descartar';
$labels['vacation.redirect'] = 'Reenviar a';
$labels['vacation.copy'] = 'Enviar copia a';
$labels['arialabelfiltersetactions'] = 'Acciones de conjunto de filtros';
$labels['arialabelfilteractions'] = 'Filtrar acciones';
$labels['arialabelfilterform'] = 'Filtrar propiedades';
$labels['ariasummaryfilterslist'] = 'Listado de filtros';
$labels['ariasummaryfiltersetslist'] = 'Listado de conjunto de filtros';
$labels['filterstitle'] = 'Editar filtros para mensajes entrantes';
$labels['vacationtitle'] = 'Editar reglas "fuera de la oficina"';
$messages['filterunknownerror'] = 'Error desconocido de servidor';
$messages['filterconnerror'] = 'Imposible conectar con el servidor managesieve';
$messages['filterdeleteerror'] = 'Imposible borrar filtro. Ha ocurrido un error en el servidor';
$messages['filterdeleted'] = 'Filtro borrado satisfactoriamente';
$messages['filtersaved'] = 'Filtro guardado satisfactoriamente';
$messages['filtersaveerror'] = 'Imposible guardar ell filtro. Ha ocurrido un error en el servidor';
$messages['filterdeleteconfirm'] = '¿Realmente desea borrar el filtro seleccionado?';
$messages['ruledeleteconfirm'] = '¿Está seguro de que desea borrar la regla seleccionada?';
$messages['actiondeleteconfirm'] = '¿Está seguro de que desea borrar la acción seleccionada?';
$messages['forbiddenchars'] = 'Caracteres prohibidos en el campo';
$messages['cannotbeempty'] = 'El campo no puede estar vacío';
$messages['ruleexist'] = 'El filtro con el nombre especificado ya existe.';
$messages['setactivateerror'] = 'Imposible activar el conjunto de filtros. Error en el servidor.';
$messages['setdeactivateerror'] = 'Imposible desactivar el conjunto de filtros. Error en el servidor.';
$messages['setdeleteerror'] = 'Imposible eliminar el conjunto de filtros. Error en el servidor.';
$messages['setactivated'] = 'Conjunto de filtros activados correctamente';
$messages['setdeactivated'] = 'Conjunto de filtros desactivados correctamente';
$messages['setdeleted'] = 'Conjunto de filtros eliminados correctamente';
$messages['setdeleteconfirm'] = '¿Esta seguro, que quiere eliminar el conjunto de filtros seleccionado?';
$messages['setcreateerror'] = 'Imposible crear el conjunto de filtros. Error en el servidor.';
$messages['setcreated'] = 'Conjunto de filtros creados correctamente';
$messages['activateerror'] = 'Imposible activar el conjunto de filtros. Error en el servidor.';
$messages['deactivateerror'] = 'Imposible desactivar el conjunto de filtros. Error en el servidor.';
$messages['deactivated'] = 'Filtro deshabilitado satisfactoriamente';
$messages['activated'] = 'Filtro habilitado satisfactoriamente';
$messages['moved'] = 'Filtro movido satisfactoriamente';
$messages['moveerror'] = 'Imposible mover el filtro seleccionado. Ha ocurrido un error en el servidor.';
$messages['nametoolong'] = 'El nombre es demasiado largo.';
$messages['namereserved'] = 'Nombre reservado.';
$messages['setexist'] = 'Conjunto ya existe.';
$messages['nodata'] = 'Al menos una posición debe ser seleccionada!';
$messages['invaliddateformat'] = 'Fecha o formato de fecha inválido';
$messages['saveerror'] = 'Imposible guardar la información. Ha ocurrido un error con el servidor.';
$messages['vacationsaved'] = 'Información de vacaciones guardada satisfactoriamente.';
$messages['emptyvacationbody'] = '¡Se requiere un cuerpo para el mensaje por vacaciones!';
?>
diff --git a/lib/plugins/managesieve/localization/es_ES.inc b/lib/plugins/managesieve/localization/es_ES.inc
index 91602ee..62f357b 100644
--- a/lib/plugins/managesieve/localization/es_ES.inc
+++ b/lib/plugins/managesieve/localization/es_ES.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtros';
$labels['managefilters'] = 'Administrar filtros de correo entrante';
$labels['filtername'] = 'Nombre del filtro';
$labels['newfilter'] = 'Nuevo filtro';
$labels['filteradd'] = 'Agregar filtro';
$labels['filterdel'] = 'Eliminar filtro';
$labels['moveup'] = 'Mover arriba';
$labels['movedown'] = 'Mover abajo';
$labels['filterallof'] = 'coincidir con todas las reglas siguientes';
$labels['filteranyof'] = 'coincidir con alguna de las reglas siguientes';
$labels['filterany'] = 'todos los mensajes';
$labels['filtercontains'] = 'contiene';
$labels['filternotcontains'] = 'no contiene';
$labels['filteris'] = 'es igual a';
$labels['filterisnot'] = 'no es igual a';
$labels['filterexists'] = 'existe';
$labels['filternotexists'] = 'no existe';
$labels['filtermatches'] = 'coincide con la expresión';
$labels['filternotmatches'] = 'no coincide con la expresión';
$labels['filterregex'] = 'coincide con la expresión regular';
$labels['filternotregex'] = 'no coincide con la expresión regular';
$labels['filterunder'] = 'bajo';
$labels['filterover'] = 'sobre';
$labels['addrule'] = 'Agregar regla';
$labels['delrule'] = 'Eliminar regla';
$labels['messagemoveto'] = 'Mover mensaje a';
$labels['messageredirect'] = 'Redirigir mensaje a';
$labels['messagecopyto'] = 'Copiar mensaje a';
$labels['messagesendcopy'] = 'Enviar copia del mensaje a';
$labels['messagereply'] = 'Responder con un mensaje';
$labels['messagedelete'] = 'Eliminar mensaje';
$labels['messagediscard'] = 'Descartar con un mensaje';
$labels['messagekeep'] = 'Mantener el mensaje en la bandeja de entrada';
$labels['messagesrules'] = 'Para el correo entrante:';
$labels['messagesactions'] = '... ejecutar las siguientes acciones:';
$labels['add'] = 'Agregar';
$labels['del'] = 'Eliminar';
$labels['sender'] = 'Remitente';
$labels['recipient'] = 'Destinatario';
$labels['vacationaddr'] = 'Mis direcciones adicionales de correo electrónico:';
$labels['vacationdays'] = 'Cada cuánto enviar mensajes (en días):';
$labels['vacationinterval'] = 'Cada cuánto enviar mensajes:';
-$labels['days'] = 'días';
-$labels['seconds'] = 'segundos';
$labels['vacationreason'] = 'Cuerpo del mensaje (razón de vacaciones):';
$labels['vacationsubject'] = 'Asunto del Mensaje:';
+$labels['days'] = 'días';
+$labels['seconds'] = 'segundos';
$labels['rulestop'] = 'Parar de evaluar reglas';
$labels['enable'] = 'Habilitar/Deshabilitar';
$labels['filterset'] = 'Conjunto de filtros';
$labels['filtersets'] = 'Conjuntos de filtros';
$labels['filtersetadd'] = 'Agregar conjunto de filtros';
$labels['filtersetdel'] = 'Eliminar conjunto de filtros actual';
$labels['filtersetact'] = 'Activar conjunto de filtros actual';
$labels['filtersetdeact'] = 'Desactivar conjunto de filtros actual';
$labels['filterdef'] = 'Definición de filtros';
$labels['filtersetname'] = 'Nombre del conjunto de filtros';
$labels['newfilterset'] = 'Nuevo conjunto de filtros';
$labels['active'] = 'activo';
$labels['none'] = 'ninguno';
$labels['fromset'] = 'de conjunto';
$labels['fromfile'] = 'de archivo';
$labels['filterdisabled'] = 'Filtro desactivado';
$labels['countisgreaterthan'] = 'contiene más que';
$labels['countisgreaterthanequal'] = 'contiene más o igual que';
$labels['countislessthan'] = 'contiene menos que';
$labels['countislessthanequal'] = 'contiene menos o igual que';
$labels['countequals'] = 'contiene igual que';
$labels['countnotequals'] = 'la cuenta no es igual a';
$labels['valueisgreaterthan'] = 'el valor es mayor que';
$labels['valueisgreaterthanequal'] = 'el valor es mayor o igual que';
$labels['valueislessthan'] = 'el valor es menor que';
$labels['valueislessthanequal'] = 'el valor es menor o igual que';
$labels['valueequals'] = 'el valor es igual que';
$labels['valuenotequals'] = 'el valor no es igual a';
$labels['setflags'] = 'Etiquetar el mensaje';
$labels['addflags'] = 'Agregar etiquetas al mensaje';
$labels['removeflags'] = 'Eliminar etiquetas al mensaje';
$labels['flagread'] = 'Leído';
$labels['flagdeleted'] = 'Eliminado';
$labels['flaganswered'] = 'Respondido';
$labels['flagflagged'] = 'Marcado';
$labels['flagdraft'] = 'Borrador';
$labels['setvariable'] = 'Establecer variable';
$labels['setvarname'] = 'Nombre de la variable:';
$labels['setvarvalue'] = 'Valor de la variable:';
$labels['setvarmodifiers'] = 'Modificadores';
$labels['varlower'] = 'minúsculas';
$labels['varupper'] = 'mayúsculas';
$labels['varlowerfirst'] = 'inicial en minúsculas';
$labels['varupperfirst'] = 'inicial en mayúsculas';
$labels['varquotewildcard'] = 'entrecomillar caracteres especiales';
$labels['varlength'] = 'longitud';
$labels['notify'] = 'Enviar notificación';
-$labels['notifyaddress'] = 'A la dirección de correo:';
-$labels['notifybody'] = 'Cuerpo de la notificación:';
-$labels['notifysubject'] = 'Tema de la notificación:';
-$labels['notifyfrom'] = 'Remitente de la notificación:';
+$labels['notifytarget'] = 'Destino de la notificación:';
+$labels['notifymessage'] = 'Mensaje de notificación (opcional):';
+$labels['notifyoptions'] = 'Opciones de notificación (opcional):';
+$labels['notifyfrom'] = 'Remitente de la notificación (opcional):';
$labels['notifyimportance'] = 'Importancia:';
$labels['notifyimportancelow'] = 'baja';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'alta';
+$labels['notifymethodmailto'] = 'Correo electrónico';
+$labels['notifymethodtel'] = 'Teléfono';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Crear Filtro';
$labels['usedata'] = 'Usar los siguientes datos en el filtro:';
$labels['nextstep'] = 'Siguiente paso';
$labels['...'] = '...';
$labels['currdate'] = 'Fecha actual';
$labels['datetest'] = 'Fecha';
$labels['dateheader'] = 'cabecera:';
$labels['year'] = 'año';
$labels['month'] = 'mes';
$labels['day'] = 'día';
$labels['date'] = 'fecha (aaaa-mm-dd)';
$labels['julian'] = 'fecha (juliano)';
$labels['hour'] = 'hora';
$labels['minute'] = 'minuto';
$labels['second'] = 'segundo';
$labels['time'] = 'hora (hh:mm:ss)';
$labels['iso8601'] = 'fecha (ISO8601)';
$labels['std11'] = 'fecha (RFC2822)';
$labels['zone'] = 'zona horaria';
$labels['weekday'] = 'día de la semana (0-6)';
$labels['advancedopts'] = 'Opciones avanzadas';
$labels['body'] = 'Cuerpo del mensaje';
$labels['address'] = 'dirección';
$labels['envelope'] = 'envoltura';
$labels['modifier'] = 'modificador:';
$labels['text'] = 'texto';
$labels['undecoded'] = 'decodificar (en bruto)';
$labels['contenttype'] = 'tipo de contenido';
$labels['modtype'] = 'tipo:';
$labels['allparts'] = 'todo';
$labels['domain'] = 'dominio';
$labels['localpart'] = 'parte local';
$labels['user'] = 'usuario';
$labels['detail'] = 'detalle';
$labels['comparator'] = 'comparador:';
$labels['default'] = 'predeterminado';
$labels['octet'] = 'estricto (octeto)';
$labels['asciicasemap'] = 'no sensible a mayúsculas (ascii-casemap)';
$labels['asciinumeric'] = 'numerico (ascii-numeric)';
$labels['index'] = 'índice:';
$labels['indexlast'] = 'hacia atrás';
+$labels['vacation'] = 'Vacaciones';
+$labels['vacation.reply'] = 'Mensaje de respuesta';
+$labels['vacation.advanced'] = 'Configuración avanzada';
+$labels['vacation.subject'] = 'Asunto';
+$labels['vacation.body'] = 'Cuerpo';
+$labels['vacation.start'] = 'Comienzo de las vacaciones';
+$labels['vacation.end'] = 'Final de las vacaciones';
+$labels['vacation.status'] = 'Estado';
+$labels['vacation.on'] = 'Activado';
+$labels['vacation.off'] = 'Desactivado';
+$labels['vacation.addresses'] = 'Mis direcciones adicionales';
+$labels['vacation.interval'] = 'Intervalo de respuesta';
+$labels['vacation.after'] = 'Poner regla de vacaciones después de';
+$labels['vacation.saving'] = 'Guardando datos...';
+$labels['vacation.action'] = 'Acción de mensaje entrante';
+$labels['vacation.keep'] = 'Mantener';
+$labels['vacation.discard'] = 'Descartar';
+$labels['vacation.redirect'] = 'Redireccionar a';
+$labels['vacation.copy'] = 'Enviar copia a';
+$labels['arialabelfiltersetactions'] = 'Acciones de un paquete de filtros';
+$labels['arialabelfilteractions'] = 'Acciones de filtro';
+$labels['arialabelfilterform'] = 'Propiedades de filtro';
+$labels['ariasummaryfilterslist'] = 'Lista de filtros';
+$labels['ariasummaryfiltersetslist'] = 'Lista de paquetes de filtros';
+$labels['filterstitle'] = 'Editar filtros de mensajes entrantes';
+$labels['vacationtitle'] = 'Editar la regla fuera-de-la-oficina';
$messages['filterunknownerror'] = 'Error desconocido en el servidor.';
$messages['filterconnerror'] = 'No se ha podido conectar con el servidor managesieve.';
$messages['filterdeleteerror'] = 'No se ha podido borrar el filtro. Ha ocurrido un error en el servidor.';
$messages['filterdeleted'] = 'Filtro borrado satisfactoriamente.';
$messages['filtersaved'] = 'Filtro guardado satisfactoriamente.';
$messages['filtersaveerror'] = 'No se ha podido guardar el filtro. Ha ocurrido un error en el servidor.';
$messages['filterdeleteconfirm'] = '¿Realmente desea borrar el filtro seleccionado?';
$messages['ruledeleteconfirm'] = '¿Está seguro de que desea borrar la regla seleccionada?';
$messages['actiondeleteconfirm'] = '¿Está seguro de que desea borrar la acción seleccionada?';
$messages['forbiddenchars'] = 'Caracteres prohibidos en el campo.';
$messages['cannotbeempty'] = 'El campo no puede estar vacío.';
$messages['ruleexist'] = 'Ya existe un filtro con el nombre especificado.';
$messages['setactivateerror'] = 'No se ha podido activar el conjunto de filtros seleccionado. Ha ocurrido un error en el servidor.';
$messages['setdeactivateerror'] = 'No se ha podido desactivar el conjunto de filtros seleccionado. Ha ocurrido un error en el servidor.';
$messages['setdeleteerror'] = 'No se ha podido borrar el conjunto de filtros seleccionado. Ha ocurrido un error en el servidor.';
$messages['setactivated'] = 'Conjunto de filtros activado satisfactoriamente.';
$messages['setdeactivated'] = 'Conjunto de filtros desactivado satisfactoriamente.';
$messages['setdeleted'] = 'Conjunto de filtros borrado satisfactoriamente.';
$messages['setdeleteconfirm'] = '¿Está seguro de que desea borrar el conjunto de filtros seleccionado?';
$messages['setcreateerror'] = 'No se ha podido crear el conjunto de filtros. Ha ocurrido un error en el servidor.';
$messages['setcreated'] = 'Conjunto de filtros creado satisfactoriamente.';
$messages['activateerror'] = 'No se han podido habilitar los filtros seleccionados. Ha ocurrido un error en el servidor.';
$messages['deactivateerror'] = 'No se han podido deshabilitar los filtros seleccionados. Ha ocurrido un error en el servidor.';
$messages['deactivated'] = 'Filtro(s) deshabilitado(s) correctamente.';
$messages['activated'] = 'Filtro(s) habilitado(s) correctamente.';
$messages['moved'] = 'Filtro movido correctamente.';
$messages['moveerror'] = 'No se ha podido mover el filtro seleccionado. Ha ocurrido un error en el servidor.';
$messages['nametoolong'] = 'Nombre demasiado largo.';
$messages['namereserved'] = 'Nombre reservado.';
$messages['setexist'] = 'El conjunto ya existe.';
$messages['nodata'] = '¡Al menos una posición debe ser seleccionada!';
$messages['invaliddateformat'] = 'Fecha o formato de parte de la fecha no válido';
+$messages['saveerror'] = 'No fue posible guardar los datos. Ha ocurrido un error en el servidor.';
+$messages['vacationsaved'] = 'Datos de vacaciones guardados correctamente.';
+$messages['emptyvacationbody'] = '¡Hace falta un texto en el mensaje de vacaciones!';
?>
diff --git a/lib/plugins/managesieve/localization/et_EE.inc b/lib/plugins/managesieve/localization/et_EE.inc
index e8a3bd3..3957dcb 100644
--- a/lib/plugins/managesieve/localization/et_EE.inc
+++ b/lib/plugins/managesieve/localization/et_EE.inc
@@ -1,181 +1,177 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtrid';
$labels['managefilters'] = 'Halda sisenevate kirjade filtreid';
$labels['filtername'] = 'Filtri nimi';
$labels['newfilter'] = 'Uus filter';
$labels['filteradd'] = 'Lisa filter';
$labels['filterdel'] = 'Kustuta filter';
$labels['moveup'] = 'Liiguta üles';
$labels['movedown'] = 'Liiguta alla';
$labels['filterallof'] = 'vastab kõikidele järgnevatele reeglitele';
$labels['filteranyof'] = 'vastab mõnele järgnevatest reeglitest';
$labels['filterany'] = 'kõik kirjad';
$labels['filtercontains'] = 'sisaldab';
$labels['filternotcontains'] = 'ei sisalda';
$labels['filteris'] = 'on võrdne kui';
$labels['filterisnot'] = 'ei ole võrdne kui';
$labels['filterexists'] = 'on olemas';
$labels['filternotexists'] = 'pole olemas';
$labels['filtermatches'] = 'vastab avaldisele';
$labels['filternotmatches'] = 'ei vasta avaldisele';
$labels['filterregex'] = 'vastab regulaaravaldisele';
$labels['filternotregex'] = 'ei vasta regulaaravaldisele';
$labels['filterunder'] = 'alt';
$labels['filterover'] = 'üle';
$labels['addrule'] = 'Lisa reegel';
$labels['delrule'] = 'Kustuta reegel';
$labels['messagemoveto'] = 'Liiguta kiri';
$labels['messageredirect'] = 'Suuna kiri ümber';
$labels['messagecopyto'] = 'Kopeeri kiri';
$labels['messagesendcopy'] = 'Saada kirja koopia';
$labels['messagereply'] = 'Vasta kirjaga';
$labels['messagedelete'] = 'Kustuta kiri';
$labels['messagediscard'] = 'Viska ära teatega';
$labels['messagesrules'] = 'Siseneva kirja puhul, mis:';
$labels['messagesactions'] = '...käivita järgnevad tegevused:';
$labels['add'] = 'Lisa';
$labels['del'] = 'Kustuta';
$labels['sender'] = 'Saatja';
$labels['recipient'] = 'Saaja';
$labels['vacationdays'] = 'Kui tihti kirju saata (päevades):';
$labels['vacationinterval'] = 'Kui tihti kirju saata:';
-$labels['days'] = 'päeva';
-$labels['seconds'] = 'sekundit';
$labels['vacationreason'] = 'Kirja sisu (puhkuse põhjus):';
$labels['vacationsubject'] = 'Kirja teema:';
+$labels['days'] = 'päeva';
+$labels['seconds'] = 'sekundit';
$labels['rulestop'] = 'Peata reeglite otsimine';
$labels['enable'] = 'Luba/keela';
$labels['filterset'] = 'Filtrite kogum';
$labels['filtersets'] = 'Filtri kogum';
$labels['filtersetadd'] = 'Lisa filtrite kogum';
$labels['filtersetdel'] = 'Kustuta praegune filtrite kogum';
$labels['filtersetact'] = 'Aktiveeri praegune filtrite kogum';
$labels['filtersetdeact'] = 'De-aktiveeri praegune filtrite kogum';
$labels['filterdef'] = 'Filtri definitsioon';
$labels['filtersetname'] = 'Filtrite kogumi nimi';
$labels['newfilterset'] = 'Uus filtrite kogum';
$labels['active'] = 'aktiivne';
$labels['none'] = 'puudub';
$labels['fromset'] = 'kogumist';
$labels['fromfile'] = 'failist';
$labels['filterdisabled'] = 'Filter keelatud';
$labels['countisgreaterthan'] = 'koguarv on suurem kui';
$labels['countisgreaterthanequal'] = 'koguarv on suurem kui või võrdne';
$labels['countislessthan'] = 'koguarv on väiksem';
$labels['countislessthanequal'] = 'koguarv on väiksem kui või võrdne';
$labels['countequals'] = 'koguarv on võrdne';
$labels['countnotequals'] = 'summa ei ole võrdne';
$labels['valueisgreaterthan'] = 'väärtus on suurem kui';
$labels['valueisgreaterthanequal'] = 'väärtus on suurem kui või võrdne';
$labels['valueislessthan'] = 'väärtus on väiksem kui';
$labels['valueislessthanequal'] = 'väärtus on väiksem kui või võrdne';
$labels['valueequals'] = 'väärtus on võrdne';
$labels['valuenotequals'] = 'väärtus ei ole võrdne';
$labels['setflags'] = 'Sea kirjale lipik';
$labels['addflags'] = 'Lisa kirjale lipikuid';
$labels['removeflags'] = 'Eemalda kirjalt lipikud';
$labels['flagread'] = 'Loetud';
$labels['flagdeleted'] = 'Kustutatud';
$labels['flaganswered'] = 'Vastatud';
$labels['flagflagged'] = 'Märgistatud';
$labels['flagdraft'] = 'Mustand';
$labels['setvariable'] = 'Määra muutuja';
$labels['setvarname'] = 'Muutuja nimi:';
$labels['setvarvalue'] = 'Muutuja väärtus:';
$labels['setvarmodifiers'] = 'Muutjad:';
$labels['varlower'] = 'väiketähed';
$labels['varupper'] = 'suurtähed';
$labels['varlowerfirst'] = 'esimene märk on väiketäht';
$labels['varupperfirst'] = 'esimene märk on suurtäht';
$labels['varquotewildcard'] = 'tsiteeri erimärke';
$labels['varlength'] = 'pikkus';
$labels['notify'] = 'Saada teavitus';
-$labels['notifyaddress'] = 'Saaja e-posti aadress:';
-$labels['notifybody'] = 'Teavituse sisu:';
-$labels['notifysubject'] = 'Teavituse pealkiri:';
-$labels['notifyfrom'] = 'Teavituse saatja:';
$labels['notifyimportance'] = 'Tähtsus:';
$labels['notifyimportancelow'] = 'madal';
$labels['notifyimportancenormal'] = 'tavaline';
$labels['notifyimportancehigh'] = 'kõrge';
$labels['filtercreate'] = 'Loo filter';
$labels['usedata'] = 'Kasuta filtris järgmisi andmeid:';
$labels['nextstep'] = 'Järgmine samm';
$labels['...'] = '…';
$labels['currdate'] = 'Praegune kuupäev';
$labels['datetest'] = 'Kuupäev';
$labels['dateheader'] = 'päis:';
$labels['year'] = 'aasta';
$labels['month'] = 'kuu';
$labels['day'] = 'päev';
$labels['date'] = 'kuupäev (aaaa-kk-pp)';
$labels['julian'] = 'kuupäev (Juliuse)';
$labels['hour'] = 'tund';
$labels['minute'] = 'minut';
$labels['second'] = 'sekund';
$labels['time'] = 'aeg (tt:mm:ss)';
$labels['iso8601'] = 'kuupäev (ISO8601)';
$labels['std11'] = 'kuupäev (RCF2822)';
$labels['zone'] = 'ajatsoon';
$labels['weekday'] = 'nädalapäev (0-6)';
$labels['advancedopts'] = 'Lisaseadistused';
$labels['body'] = 'Põhitekst';
$labels['address'] = 'aadress';
$labels['envelope'] = 'ümbrik';
$labels['modifier'] = 'muutja:';
$labels['text'] = 'tekst';
$labels['undecoded'] = 'kodeerimata (toor)';
$labels['contenttype'] = 'sisu tüüp';
$labels['modtype'] = 'tüüp:';
$labels['allparts'] = 'kõik';
$labels['domain'] = 'domeen';
$labels['localpart'] = 'kohalik osa';
$labels['user'] = 'kasutaja';
$labels['detail'] = 'detail';
$labels['comparator'] = 'võrdleja:';
$labels['default'] = 'vaikimisi';
$labels['octet'] = 'range (octet)';
$labels['asciicasemap'] = 'tõstutundetu (ascii-casemap)';
$labels['asciinumeric'] = 'numbriline (ascii-numeric)';
$labels['index'] = 'indeks:';
$labels['indexlast'] = 'tagasisuunas';
$messages['filterunknownerror'] = 'Tundmatu serveri tõrge';
$messages['filterconnerror'] = 'Managesieve serveriga ühendumine nurjus';
$messages['filterdeleted'] = 'Filter edukalt kustutatud';
$messages['filtersaved'] = 'Filter edukalt salvestatud';
$messages['filterdeleteconfirm'] = 'Soovid valitud filtri kustutada?';
$messages['ruledeleteconfirm'] = 'Soovid valitud reegli kustutada?';
$messages['actiondeleteconfirm'] = 'Soovid valitud tegevuse kustutada?';
$messages['forbiddenchars'] = 'Väljal on lubamatu märk';
$messages['cannotbeempty'] = 'Väli ei või tühi olla';
$messages['ruleexist'] = 'Määratud nimega filter on juba olemas';
$messages['setactivated'] = 'Filtrite kogumi aktiveerimine õnnestus.';
$messages['setdeactivated'] = 'Filtrite kogumi deaktiveerimine õnnestus.';
$messages['setdeleted'] = 'Filtrite kogumi kustutamine õnnestus.';
$messages['setdeleteconfirm'] = 'Oled kindel, et soovid valitud filtrite kogumi kustutada?';
$messages['setcreated'] = 'Filtrite kogumi loomine õnnestus.';
$messages['deactivated'] = 'Filter edukalt lubatud.';
$messages['activated'] = 'Filter edukalt keelatud.';
$messages['moved'] = 'Filter edukalt liigutatud.';
$messages['nametoolong'] = 'Nimi on liiga pikk.';
$messages['namereserved'] = 'Nimi on reserveeritud.';
$messages['setexist'] = 'Kogum on juba olemas.';
$messages['nodata'] = 'Valitud peab olema vähemalt üks asukoht!';
$messages['invaliddateformat'] = 'Vigane kuupäev või kuupäeva formaat';
?>
diff --git a/lib/plugins/managesieve/localization/eu_ES.inc b/lib/plugins/managesieve/localization/eu_ES.inc
index fe29e58..c9a39dc 100644
--- a/lib/plugins/managesieve/localization/eu_ES.inc
+++ b/lib/plugins/managesieve/localization/eu_ES.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Iragazkiak';
$labels['managefilters'] = 'Kudeatu sarrerako posta-iragazkiak';
$labels['filtername'] = 'Iragazkiaren izena';
$labels['newfilter'] = 'Iragazki berria';
$labels['filteradd'] = 'Gehitu iragazkia';
$labels['filterdel'] = 'Ezabatu iragazkia';
$labels['moveup'] = 'Mugitu gora';
$labels['movedown'] = 'Mugitu behera';
$labels['filterallof'] = 'datozen arau guztiak parekatzen';
$labels['filteranyof'] = 'datozen arauetako batzuk parekatzen';
$labels['filterany'] = 'mezu guztiak';
$labels['filtercontains'] = 'badu';
$labels['filternotcontains'] = 'ez du';
$labels['filteris'] = 'honen berdina da';
$labels['filterisnot'] = 'ez da honen berdina';
$labels['filterexists'] = 'badago';
$labels['filternotexists'] = 'ez dago';
-$labels['filtermatches'] = 'bat datorren espresio';
-$labels['filternotmatches'] = 'bat ez datorren espresio';
-$labels['filterregex'] = 'bat datozen adierazpen erregularrak';
-$labels['filternotregex'] = 'bat ez datorren espresio erregularrak';
+$labels['filtermatches'] = 'bat datorren espresioa';
+$labels['filternotmatches'] = 'bat ez datorren espresioa';
+$labels['filterregex'] = 'bat datozen adierazpen erregularra';
+$labels['filternotregex'] = 'bat ez datorren espresio erregularra';
$labels['filterunder'] = 'azpian';
$labels['filterover'] = 'gainean';
$labels['addrule'] = 'Gehitu araua';
$labels['delrule'] = 'Ezabatu araua';
$labels['messagemoveto'] = 'Mugitu mezua hona';
$labels['messageredirect'] = 'Birbideratu mezua hona ';
$labels['messagecopyto'] = 'Kopiatu mezua hona';
$labels['messagesendcopy'] = 'Bidali mezuaren kopia hona';
$labels['messagereply'] = 'Erantzun mezuarekin';
$labels['messagedelete'] = 'Ezabatu mezua';
$labels['messagediscard'] = 'Baztertu mezuarekin';
$labels['messagekeep'] = 'Mantendu mezua Sarrera-ontzian';
$labels['messagesrules'] = 'Sarrerako postarako:';
$labels['messagesactions'] = '...exekutatu datozen ekintzak:';
$labels['add'] = 'Gehitu';
$labels['del'] = 'Ezabatu';
$labels['sender'] = 'Bidaltzailea';
$labels['recipient'] = 'Hartzailea';
$labels['vacationaddr'] = 'Nire helbide elektroniko osagarria(k):';
-$labels['vacationdays'] = 'Zer maiztasunarekin bidaltzen ditu mezuak (egunak)';
+$labels['vacationdays'] = 'Zenbatero bidali mezuak (egunak)';
$labels['vacationinterval'] = 'Zenbatero bidali mezuak:';
+$labels['vacationreason'] = 'Mezuaren gorputza (oporrak direla medio):';
+$labels['vacationsubject'] = 'Mezuaren gaia:';
$labels['days'] = 'egun';
$labels['seconds'] = 'segundo';
-$labels['vacationreason'] = 'Mezuaren gorputza (oporrak direla medio):';
-$labels['vacationsubject'] = 'Mezuaren izenburua:';
$labels['rulestop'] = 'Gelditu arauak ebaluatzen';
$labels['enable'] = 'Gaitu/Ezgaitu';
$labels['filterset'] = 'Iragazki-paketea';
$labels['filtersets'] = 'Iragazki-paketeak';
$labels['filtersetadd'] = 'Gehitu iragazki-paketea';
$labels['filtersetdel'] = 'Ezabatu uneko iragazki-paketea';
$labels['filtersetact'] = 'Gaitu uneko iragazki-paketea';
$labels['filtersetdeact'] = 'Ezgaitu uneko iragazki-paketea';
$labels['filterdef'] = 'Iragazkiaren definizioa';
$labels['filtersetname'] = 'Iragazki-paketearen izena';
$labels['newfilterset'] = 'Iragazki-pakete berria';
$labels['active'] = 'aktiboa';
$labels['none'] = 'Bat ere ez';
$labels['fromset'] = 'paketetik';
$labels['fromfile'] = 'fitxategitik';
$labels['filterdisabled'] = 'Iragazki ezgaitua';
$labels['countisgreaterthan'] = 'kopurua handiagoa da hau baino';
$labels['countisgreaterthanequal'] = 'kopurua hau baino handiagoa edo berdina da';
$labels['countislessthan'] = 'kopurua hau baino txikiagoa da';
$labels['countislessthanequal'] = 'kopurua hau baino txikiagoa edo berdina da';
$labels['countequals'] = 'kopurua honen berdina da';
$labels['countnotequals'] = 'kopurua ez da honen berdina';
$labels['valueisgreaterthan'] = 'balioa hau baino handiagoa da';
$labels['valueisgreaterthanequal'] = 'balioa hau baino handiagoa edo berdina da';
$labels['valueislessthan'] = 'balioa hau baino txikiagoa da';
$labels['valueislessthanequal'] = 'balioa hau baino txikiagoa edo berdina da';
$labels['valueequals'] = 'balioa honen berdina da';
$labels['valuenotequals'] = 'balioa ez da honen berdina';
$labels['setflags'] = 'Jarri banderak mezuarik';
$labels['addflags'] = 'Gehitu banderak mezuari';
$labels['removeflags'] = 'Ezabatu banderak mezutik';
$labels['flagread'] = 'Irakurri';
$labels['flagdeleted'] = 'Ezabatuta';
$labels['flaganswered'] = 'Erantzunda';
$labels['flagflagged'] = 'Bandera jarrita';
$labels['flagdraft'] = 'Ziriborroa';
$labels['setvariable'] = 'Ezarri aldagaia';
$labels['setvarname'] = 'Aldagaiaren izena:';
$labels['setvarvalue'] = 'Aldagaiaren balioa:';
$labels['setvarmodifiers'] = 'Modifikatzaileak:';
$labels['varlower'] = 'minuskulan';
$labels['varupper'] = 'maiuskulan';
$labels['varlowerfirst'] = 'lehenengo karakterea minuskulan';
$labels['varupperfirst'] = 'lehenengo karakterea maiuskulan';
$labels['varquotewildcard'] = 'aipatu karaktere bereziak';
$labels['varlength'] = 'luzera';
$labels['notify'] = 'Bidali jakinarazpena';
-$labels['notifyaddress'] = 'e-posta helbidera:';
-$labels['notifybody'] = 'Jakinarazpenaren gorputza:';
-$labels['notifysubject'] = 'Jakinarazpenaren subjektua:';
-$labels['notifyfrom'] = 'Jakinarazpenaren bidaltzailea:';
+$labels['notifytarget'] = 'Jakinarazpenaren xedea:';
+$labels['notifymessage'] = 'Jakinarazpenaren mezua (aukerakoa):';
+$labels['notifyoptions'] = 'Jakinarazpenaren aukerak (aukerakoa):';
+$labels['notifyfrom'] = 'Jakinarazpenaren bidaltzailea (aukerakoa):';
$labels['notifyimportance'] = 'Garrantzia:';
$labels['notifyimportancelow'] = 'baxua';
$labels['notifyimportancenormal'] = 'normala';
$labels['notifyimportancehigh'] = 'altua';
+$labels['notifymethodmailto'] = 'Helbide elektronikoa';
+$labels['notifymethodtel'] = 'Telefonoa';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Sortu iragazkia';
$labels['usedata'] = 'Erabili datorren data iragazkian:';
$labels['nextstep'] = 'Hurrengo urratsa';
$labels['...'] = '...';
$labels['currdate'] = 'Uneko data';
$labels['datetest'] = 'Data';
$labels['dateheader'] = 'goiburua:';
$labels['year'] = 'urte';
$labels['month'] = 'hilabete';
$labels['day'] = 'egun';
$labels['date'] = 'data (yyyy-mm-dd)';
$labels['julian'] = 'data (juliarra)';
$labels['hour'] = 'ordu';
$labels['minute'] = 'minutu';
$labels['second'] = 'segundo';
$labels['time'] = 'ordua (hh:mm:ss)';
$labels['iso8601'] = 'data (ISO8601)';
$labels['std11'] = 'data (RFC2822)';
$labels['zone'] = 'ordu-zona';
$labels['weekday'] = 'asteguna (0-6)';
$labels['advancedopts'] = 'Aukera aurreratuak';
$labels['body'] = 'Gorputza';
$labels['address'] = 'helbidea';
$labels['envelope'] = 'gutun-azala';
$labels['modifier'] = 'modifikatzailea:';
$labels['text'] = 'testua';
$labels['undecoded'] = 'kodetu gabe (gordina)';
$labels['contenttype'] = 'eduki mota';
$labels['modtype'] = 'mota:';
$labels['allparts'] = 'denak';
$labels['domain'] = 'domeinua';
$labels['localpart'] = 'zati lokala';
$labels['user'] = 'erabiltzailea';
$labels['detail'] = 'xehetasuna';
$labels['comparator'] = 'alderatzailea:';
$labels['default'] = 'lehenetsia';
$labels['octet'] = 'zorrotza (zortzikotea)';
$labels['asciicasemap'] = 'minuskulak eta maiuskulak (ascii-casemap)';
$labels['asciinumeric'] = 'numerikoa (ascii-numeric)';
$labels['index'] = 'indexatu:';
$labels['indexlast'] = 'atzeraka';
+$labels['vacation'] = 'Oporraldia';
+$labels['vacation.reply'] = 'Erantzun mezua';
+$labels['vacation.advanced'] = 'Ezarpen aurreratuak';
+$labels['vacation.subject'] = 'Gaia';
+$labels['vacation.body'] = 'Gorputza';
+$labels['vacation.start'] = 'Oporraldiaren hasiera';
+$labels['vacation.end'] = 'Oporraldiaren bukaera';
+$labels['vacation.status'] = 'Egoera';
+$labels['vacation.on'] = 'Piztuta';
+$labels['vacation.off'] = 'Itzalita';
+$labels['vacation.addresses'] = 'Nire helbide osagarriak';
+$labels['vacation.interval'] = 'Erantzun tartea';
+$labels['vacation.after'] = 'Jarri oporren erregela honen ondoren';
+$labels['vacation.saving'] = 'Datuak gordetzen...';
+$labels['vacation.action'] = 'Sarrerako mezuaren ekintza';
+$labels['vacation.keep'] = 'Mantendu';
+$labels['vacation.discard'] = 'Baztertu';
+$labels['vacation.redirect'] = 'Birbideratu hona';
+$labels['vacation.copy'] = 'Bidali kopia hona';
+$labels['arialabelfiltersetactions'] = 'Iragazki-paketearen ekintzak';
+$labels['arialabelfilteractions'] = 'Iragazki-ekintzak';
+$labels['arialabelfilterform'] = 'Iragazkiaren ezaugarriak';
+$labels['ariasummaryfilterslist'] = 'Iragazkien zerrenda';
+$labels['ariasummaryfiltersetslist'] = 'Iragazki-paketeen zerrenda';
+$labels['filterstitle'] = 'Editatu postaren sarrera-iragazkiak';
+$labels['vacationtitle'] = 'Bulegotik-kanpo -erantzun automatiko- araua';
$messages['filterunknownerror'] = 'Zerbitzari ezezaguna errorea';
$messages['filterconnerror'] = 'Ezin da konektatu zerbitzariarekin.';
$messages['filterdeleteerror'] = 'Ezin da ezabatu iragazkia. Errore bat gertatu da zerbitzarian.';
$messages['filterdeleted'] = 'Iragazkia ongi ezabatu da.';
$messages['filtersaved'] = 'Iragazkia ongi ezabatu da.';
$messages['filtersaveerror'] = 'Ezin da gorde iragazkia. Zerbitzarian errore bat gertatu da.';
$messages['filterdeleteconfirm'] = 'Seguru zaude hautatutako iragazkiak ezabatu nahi dituzula?';
$messages['ruledeleteconfirm'] = 'Seguru zaude hautatutako arauak ezabatu nahi dituzula?';
$messages['actiondeleteconfirm'] = 'Seguru zaude hautatutako ekintzak ezabatu nahi dituzula?';
$messages['forbiddenchars'] = 'Debekatutako karaktereak eremuan.';
$messages['cannotbeempty'] = 'Eremua ezin da hutsik egon.';
$messages['ruleexist'] = 'Lehendik badago izen hori duen iragazki bat.';
$messages['setactivateerror'] = 'Ezin da aktibatu hautatutako iragazki paketea. Zerbitzarian errore bat gertatu da.';
$messages['setdeactivateerror'] = 'Ezin da ezgaitu hautatutako iragazki-paketea. Zerbitzarian errore bat gertatu da.';
$messages['setdeleteerror'] = 'Ezin da ezabatu hautatutako iragazki-paketea. Zerbitzarian errore bat gertatu da.';
$messages['setactivated'] = 'Iragazki paketea ongi aktibatu da.';
$messages['setdeactivated'] = 'Iragazki paketea ongi desaktibatu da.';
$messages['setdeleted'] = 'Iragazki paketea ongi ezabatu da.';
$messages['setdeleteconfirm'] = 'Seguru zaude hautatutako iragazki paketea ezabatu nahi duzula?';
$messages['setcreateerror'] = 'Ezin da iragazki-paketea sortu. Zerbitzarian errore bat gertatu da.';
$messages['setcreated'] = 'Iragazki paketea ongi sortu da.';
$messages['activateerror'] = 'Ezin da gaitu hautatutako iragazkia(k). Zerbitzarian errore bat gertatu da.';
$messages['deactivateerror'] = 'Ezin da ezgaitu hautatutako iragazkia(k). Zerbitzarian errore bat gertatu da.';
$messages['deactivated'] = 'Iragazkia(k) ongi ezgaitu da.';
$messages['activated'] = 'Iragazkia(k) ongi gaitu da.';
$messages['moved'] = 'Iragazkia ongi mugitu da.';
$messages['moveerror'] = 'Ezin da mugitu hautatutako iragazkia. Zerbitzarian errore bat gertatu da.';
$messages['nametoolong'] = 'Izen luzeegia.';
$messages['namereserved'] = 'Izen erreserbatua.';
$messages['setexist'] = 'Lehendik badago pakete hori.';
$messages['nodata'] = 'Gutxienez posizio bat hautatu behar da!';
$messages['invaliddateformat'] = 'Dataren edo dataren zati baten formatua ez da baliozkoa ';
+$messages['saveerror'] = 'Ezin dira datuak gorde. Errorea gertatu da zerbitzarian.';
+$messages['vacationsaved'] = 'Oporren data ongi gorde da.';
+$messages['emptyvacationbody'] = 'Beharrezkoa da oporraldiko mezua jartzea!';
?>
diff --git a/lib/plugins/managesieve/localization/fa_IR.inc b/lib/plugins/managesieve/localization/fa_IR.inc
index b938c58..65f2d0d 100644
--- a/lib/plugins/managesieve/localization/fa_IR.inc
+++ b/lib/plugins/managesieve/localization/fa_IR.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
-$labels['filters'] = 'صافی‌ها';
-$labels['managefilters'] = 'مدیریت صافی‌های نامه ورودی';
-$labels['filtername'] = 'نام صافی';
-$labels['newfilter'] = 'صافی جدید';
-$labels['filteradd'] = 'افزودن صافی';
-$labels['filterdel'] = 'حذف صافی';
+$labels['filters'] = 'پالایه‌ها';
+$labels['managefilters'] = 'مدیریت پالایه‌های نامه ورودی';
+$labels['filtername'] = 'نام پالایه';
+$labels['newfilter'] = 'پالایه جدید';
+$labels['filteradd'] = 'افزودن پالایه';
+$labels['filterdel'] = 'حذف پالایه';
$labels['moveup'] = 'انتقال به بالا';
$labels['movedown'] = 'انتقال به پایین';
$labels['filterallof'] = 'مطابقت همه قوانین ذیل';
$labels['filteranyof'] = 'مطابقت هر کدام از قوانین ذیل';
$labels['filterany'] = 'همه پیغام ها';
-$labels['filtercontains'] = 'شامل‌';
+$labels['filtercontains'] = 'دربرگیرنده';
$labels['filternotcontains'] = 'بدون';
$labels['filteris'] = 'برابر است با';
$labels['filterisnot'] = 'برابر نیست با';
$labels['filterexists'] = 'وجود دارد';
$labels['filternotexists'] = 'وجود ندارد';
$labels['filtermatches'] = 'با عبارت تطابق دارد';
$labels['filternotmatches'] = 'با عبارت تطابق ندارد';
$labels['filterregex'] = 'با عبارت عمومی تطابق دارد';
$labels['filternotregex'] = 'با عبارت عمومی تطابق ندارد';
$labels['filterunder'] = 'زیر';
$labels['filterover'] = 'بالا';
$labels['addrule'] = 'افزودن قانون';
$labels['delrule'] = 'حذف قانون';
$labels['messagemoveto'] = 'انتقال پیغام به';
$labels['messageredirect'] = 'بازگردانی پیغام به';
$labels['messagecopyto'] = 'رونوشت پیغام به';
$labels['messagesendcopy'] = 'ارسال رونوشت پیغام به';
$labels['messagereply'] = 'پاسخ همراه پیغام';
$labels['messagedelete'] = 'حذف پیغام';
$labels['messagediscard'] = 'دور ریختن با پیغام';
$labels['messagekeep'] = 'پیغام را در صندوق ورودی نگه دار';
$labels['messagesrules'] = 'برای صندوق ورودی:';
$labels['messagesactions'] = '...انجام اعمال ذیل:';
$labels['add'] = 'افزودن';
$labels['del'] = 'حذف';
$labels['sender'] = 'فرستنده';
$labels['recipient'] = 'گیرنده';
-$labels['vacationaddr'] = 'آدرس(های) ایمیل اضافی من:';
+$labels['vacationaddr'] = 'نشانی(های) رایانامه دیگر من:';
$labels['vacationdays'] = 'پیغام ها در چه مواقعی فرستاده شدند (در روزهای):';
$labels['vacationinterval'] = 'مواقعی که پیغام‌ها ارسال می‌شوند:';
-$labels['days'] = 'روزها';
-$labels['seconds'] = 'ثانیه‌ها';
$labels['vacationreason'] = 'بدنه پیغام (علت مسافرت):';
$labels['vacationsubject'] = 'موضوع پیغام:';
+$labels['days'] = 'روزها';
+$labels['seconds'] = 'ثانیه‌ها';
$labels['rulestop'] = 'توقف قوانین ارزیابی';
$labels['enable'] = 'فعال/غیرفعال‌سازی';
-$labels['filterset'] = 'مجموعه صافی‌ها';
-$labels['filtersets'] = 'مجموعه‌های صافی‌ها';
-$labels['filtersetadd'] = 'افزودن مجموعه صافی‌ها';
-$labels['filtersetdel'] = 'حذف مجموعه صافی‌های جاری';
-$labels['filtersetact'] = 'فعال کردن مجموعه صافی‌های جاری';
-$labels['filtersetdeact'] = 'غیرفعال کردن مجموعه صافی‌های جاری';
-$labels['filterdef'] = 'تعریف صافی';
-$labels['filtersetname'] = 'نام مجموعه صافی‌ها';
-$labels['newfilterset'] = 'مجموعه صافی‌های جدید';
+$labels['filterset'] = 'مجموعه پالایه‌ها';
+$labels['filtersets'] = 'مجموعه‌های پالایه‌ها';
+$labels['filtersetadd'] = 'افزودن مجموعه پالایه‌ها';
+$labels['filtersetdel'] = 'حذف مجموعه پالایه‌های جاری';
+$labels['filtersetact'] = 'فعال کردن مجموعه پالایه‌های جاری';
+$labels['filtersetdeact'] = 'غیرفعال کردن مجموعه پالایه‌های جاری';
+$labels['filterdef'] = 'تعریف پالایه';
+$labels['filtersetname'] = 'نام مجموعه پالایه‌ها';
+$labels['newfilterset'] = 'مجموعه پالایه‌های جدید';
$labels['active'] = 'فعال';
$labels['none'] = 'هیچ‌کدام';
$labels['fromset'] = 'از مجموعه';
$labels['fromfile'] = 'از پرونده';
-$labels['filterdisabled'] = 'صافی غیرفعال شد';
+$labels['filterdisabled'] = 'پالایه غیرفعال شد';
$labels['countisgreaterthan'] = 'تعداد بیشتر است از';
$labels['countisgreaterthanequal'] = 'تعداد بیشتر یا مساوی است با';
$labels['countislessthan'] = 'تعداد کمتر است از';
$labels['countislessthanequal'] = 'تعداد کمتر یا مساوی است با';
$labels['countequals'] = 'تعداد مساوی است با';
$labels['countnotequals'] = 'تعداد برابر نیست با';
$labels['valueisgreaterthan'] = 'مقدار بیشتر است از';
$labels['valueisgreaterthanequal'] = 'مقدار بیشتر یا مساوی است با';
$labels['valueislessthan'] = 'مقدار کمتر است از';
$labels['valueislessthanequal'] = 'مقدار کمتر یا مساوی است با';
$labels['valueequals'] = 'مقدار مساوی است با';
$labels['valuenotequals'] = 'مقدار برابر نیست با';
$labels['setflags'] = 'انتخاب پرچم‌ها برای پیغام';
$labels['addflags'] = 'افزودن پرچم‌ها برای پیغام';
$labels['removeflags'] = 'حذف پرچم‌ها از پیغام';
$labels['flagread'] = 'خوانده‌‌شده';
$labels['flagdeleted'] = 'حذف شده';
$labels['flaganswered'] = 'جواب داده شده';
$labels['flagflagged'] = 'پرچم‌دار';
$labels['flagdraft'] = 'پیش‌نویس';
$labels['setvariable'] = 'تنظیم متغیر';
$labels['setvarname'] = 'نام متغییر';
$labels['setvarvalue'] = 'مقدار متغیر:';
$labels['setvarmodifiers'] = 'اصلاح:';
$labels['varlower'] = 'حروف کوچک';
$labels['varupper'] = 'حروف بزرگ';
$labels['varlowerfirst'] = 'حرف اول کوچک';
$labels['varupperfirst'] = 'حرف اول بزرگ';
$labels['varquotewildcard'] = 'نقل قول کاراکترهای خاص';
$labels['varlength'] = 'طول';
$labels['notify'] = 'ارسال تذکر';
-$labels['notifyaddress'] = 'به آدرس پست الکترونیکی:';
-$labels['notifybody'] = 'بدنه تذکر:';
-$labels['notifysubject'] = 'موضوع تذکر:';
-$labels['notifyfrom'] = 'فرستنده تذکر:';
+$labels['notifytarget'] = 'مقصد آگاه‌سازی:';
+$labels['notifymessage'] = 'متن آگاه‌سازی (تختیاری):';
+$labels['notifyoptions'] = 'گزینه‌های آگاه‌سازی (اختیاری):';
+$labels['notifyfrom'] = 'فرستنده آگاه‌سازی (اختیاری):';
$labels['notifyimportance'] = 'اهمیت:';
$labels['notifyimportancelow'] = 'کم';
$labels['notifyimportancenormal'] = 'معمولی';
$labels['notifyimportancehigh'] = 'زیاد';
-$labels['filtercreate'] = 'ایجاد صافی';
-$labels['usedata'] = 'استفاده از داده ذیل در صافی:';
+$labels['notifymethodmailto'] = 'رایانامه';
+$labels['notifymethodtel'] = 'تلفن';
+$labels['notifymethodsms'] = 'پیامک';
+$labels['filtercreate'] = 'ایجاد پالایه';
+$labels['usedata'] = 'استفاده از داده ذیل در پالایه:';
$labels['nextstep'] = 'مرحله بعدی';
$labels['...'] = '...';
$labels['currdate'] = 'تاریخ جاری';
$labels['datetest'] = 'تاریخ';
$labels['dateheader'] = 'سربرگ:';
$labels['year'] = 'سال';
$labels['month'] = 'ماه';
$labels['day'] = 'روز';
$labels['date'] = 'تاریخ (yyyy-mm-dd)';
$labels['julian'] = 'تاریخ (میلادی)';
$labels['hour'] = 'ساعت';
$labels['minute'] = 'دقیقه';
$labels['second'] = 'ثانیه';
$labels['time'] = 'ساعت (hh:mm:ss)';
$labels['iso8601'] = 'تاریخ (ISO8601)';
$labels['std11'] = 'تاریخ (RFC2822)';
$labels['zone'] = 'منطقه زمانی';
$labels['weekday'] = 'روز هفته (0-6)';
$labels['advancedopts'] = 'گزینه‌های پیشرفته';
$labels['body'] = 'بدنه';
$labels['address'] = 'نشانی';
$labels['envelope'] = 'پاکت';
$labels['modifier'] = 'تغییر دهنده:';
$labels['text'] = 'متن‌';
$labels['undecoded'] = 'کد نشده (خام)';
$labels['contenttype'] = 'نوع محتوا';
$labels['modtype'] = 'نوع';
$labels['allparts'] = 'همه';
$labels['domain'] = 'دامنه';
$labels['localpart'] = 'قسمت محلی';
$labels['user'] = 'کاربر';
$labels['detail'] = 'جزئیات';
$labels['comparator'] = 'مقایسه:';
$labels['default'] = 'پیش‌فرض';
$labels['octet'] = 'سخت (octet)';
$labels['asciicasemap'] = 'حساس به حروه کوچک و بزرگ (ascii-casemap)';
$labels['asciinumeric'] = 'عددی (ascii-numeric)';
$labels['index'] = 'فهرست:';
$labels['indexlast'] = 'به عقب';
+$labels['vacation'] = 'مسافرت';
+$labels['vacation.reply'] = 'پاسخ به یغام';
+$labels['vacation.advanced'] = 'تنظیمات پیشرفته';
+$labels['vacation.subject'] = 'موضوع';
+$labels['vacation.body'] = 'بدنه';
+$labels['vacation.start'] = 'شروع تعطیلی';
+$labels['vacation.end'] = 'پایان تعطیلی';
+$labels['vacation.status'] = 'وضعیت';
+$labels['vacation.on'] = 'روشن';
+$labels['vacation.off'] = 'خاموش';
+$labels['vacation.addresses'] = 'نشانی‌های دیگر من';
+$labels['vacation.interval'] = 'فاصله پاسخ';
+$labels['vacation.after'] = 'قرار دادن قانون مسافرت بعد از';
+$labels['vacation.saving'] = 'ذخیره داده‌ها...';
+$labels['vacation.action'] = 'کنش عملکرد ورودی';
+$labels['vacation.keep'] = 'نگه داشتن';
+$labels['vacation.discard'] = 'دور انداختن';
+$labels['vacation.redirect'] = 'بازگردانی به';
+$labels['vacation.copy'] = 'ارسال رونوشت به';
+$labels['arialabelfiltersetactions'] = 'کنش‌های مجموعه پالایه';
+$labels['arialabelfilteractions'] = 'کنش‌های پالایه';
+$labels['arialabelfilterform'] = 'خصوصیات پالایه';
+$labels['ariasummaryfilterslist'] = 'فهرست پالایه‌ها';
+$labels['ariasummaryfiltersetslist'] = 'فهرست مجموعه پالایه‌ها';
+$labels['filterstitle'] = 'ویرایش پالایه‌های پست ورودی';
+$labels['vacationtitle'] = 'ویرایش نقش بیرون از دفتر';
$messages['filterunknownerror'] = 'خطای سرور نامعلوم.';
$messages['filterconnerror'] = 'ناتوانی در اتصال به سرور.';
-$messages['filterdeleteerror'] = 'ناتوانی در حذف صافی. خطای سرور رخ داد.';
-$messages['filterdeleted'] = 'صافی با موفقیت حذف شد.';
-$messages['filtersaved'] = 'صافی با موفقیت ذخیره شد.';
+$messages['filterdeleteerror'] = 'ناتوانی در حذف پالایه. خطای سرور رخ داد.';
+$messages['filterdeleted'] = 'پالایه با کام‌یابی حذف شد.';
+$messages['filtersaved'] = 'پالایه با کام‌یابی ذخیره شد.';
$messages['filtersaveerror'] = 'ناتوانی در ذخیره فیلتر. خطای سرور رخ داد.';
-$messages['filterdeleteconfirm'] = 'آیا مطمئن به حذف صافی انتخاب شده هستید؟';
+$messages['filterdeleteconfirm'] = 'آیا مطمئن به حذف پالایه انتخاب شده هستید؟';
$messages['ruledeleteconfirm'] = 'آیا مطمئن هستید که می خواهید قانون انتخاب شده را حذف کنید؟';
$messages['actiondeleteconfirm'] = 'آیا مطمئن هستید که می خواهید عمل انتخاب شده را حذف کنید.';
$messages['forbiddenchars'] = 'حروف ممنوعه در فیلد.';
$messages['cannotbeempty'] = 'فیلد نمی تواند خالی باشد.';
-$messages['ruleexist'] = 'صافی با این نام مشخص وجود دارد.';
-$messages['setactivateerror'] = 'ناتوان در فعال کردن مجموعه صافی‌ها انتخاب شده. خطای سرور رخ داد.';
-$messages['setdeactivateerror'] = 'ناتوان در غیرفعال کردن مجموعه صافی‌ها انتخاب شده. خطای سرور رخ داد.';
-$messages['setdeleteerror'] = 'ناتوان در حذف مجموعه صافی‌ها انتخاب شده. خطای سرور رخ داد.';
-$messages['setactivated'] = 'مجموعه صافی‌ها با موفقیت فعال شد.';
-$messages['setdeactivated'] = 'مجموعه صافی‌ها با موفقیت غیرفعال شد.';
-$messages['setdeleted'] = 'مجموعه صافی‌ها با موفقیت حذف شد.';
-$messages['setdeleteconfirm'] = 'آیا مطمئن هستید که می‌خواهید مجموعه صافی‌ها انتخاب شده را حذف کنید؟';
-$messages['setcreateerror'] = 'ناتوانی در ایجاد مجموعه صافی‌ها. خطای سرور رخ داد.';
-$messages['setcreated'] = 'مجموعه صافی‌ها با موفقیت ایجاد شد.';
-$messages['activateerror'] = 'ناتوانی در فعال کردن صافی(های) انتخاب شده. خطای سرور رخ داد.';
-$messages['deactivateerror'] = 'ناتوانی در غیرفعال کردن صافی(های) انتخاب شده. خطای سرور رخ داد.';
-$messages['deactivated'] = 'صافی(ها) با موفقیت فعال شدند.';
-$messages['activated'] = 'صافی(ها) با موفقیت غیرفعال شدند.';
-$messages['moved'] = 'صافی با موفقیت منتقل شد.';
-$messages['moveerror'] = 'ناتوانی در انتقال صافی انتخاب شده. خطای سرور رخ داد.';
+$messages['ruleexist'] = 'پالایه با این نام مشخص وجود دارد.';
+$messages['setactivateerror'] = 'ناتوان در فعال کردن مجموعه پالایه‌ها انتخاب شده. خطای سرور رخ داد.';
+$messages['setdeactivateerror'] = 'ناتوان در غیرفعال کردن مجموعه پالایه‌ها انتخاب شده. خطای سرور رخ داد.';
+$messages['setdeleteerror'] = 'ناتوان در حذف مجموعه پالایه‌ها انتخاب شده. خطای سرور رخ داد.';
+$messages['setactivated'] = 'مجموعه پالایه‌ها با کام‌یابی فعال شد.';
+$messages['setdeactivated'] = 'مجموعه پالایه‌ها با کام‌یابی غیرفعال شد.';
+$messages['setdeleted'] = 'مجموعه پالایه‌ها با کام‌یابی حذف شد.';
+$messages['setdeleteconfirm'] = 'آیا مطمئن هستید که می‌خواهید مجموعه پالایه‌ها انتخاب شده را حذف کنید؟';
+$messages['setcreateerror'] = 'ناتوانی در ایجاد مجموعه پالایه‌ها. خطای سرور رخ داد.';
+$messages['setcreated'] = 'مجموعه پالایه‌ها با کام‌یابی ایجاد شد.';
+$messages['activateerror'] = 'ناتوانی در فعال کردن پالایه(های) انتخاب شده. خطای سرور رخ داد.';
+$messages['deactivateerror'] = 'ناتوانی در غیرفعال کردن پالایه(های) انتخاب شده. خطای سرور رخ داد.';
+$messages['deactivated'] = 'پالایه(ها) با کام‌یابی فعال شدند.';
+$messages['activated'] = 'پالایه(ها) با کام‌یابی غیرفعال شدند.';
+$messages['moved'] = 'پالایه با کام‌یابی منتقل شد.';
+$messages['moveerror'] = 'ناتوانی در انتقال پالایه انتخاب شده. خطای سرور رخ داد.';
$messages['nametoolong'] = 'نام خیلی بلند.';
$messages['namereserved'] = 'نام رزرو شده.';
$messages['setexist'] = 'مجموعه در حال حاضر موجود است.';
$messages['nodata'] = 'حداقل باید یک موقعیت باید انتخاب شود.';
$messages['invaliddateformat'] = 'قالب تاریخ اشتباه';
+$messages['saveerror'] = 'ناتوانی در ذخیره اطلاعات. خطای سرور رخ داد.';
+$messages['vacationsaved'] = 'اطلاعات مسافرت با کام‌یابی ذخیره شد.';
+$messages['emptyvacationbody'] = 'متن پیغام تعطیلی لازم است!';
?>
diff --git a/lib/plugins/managesieve/localization/fi_FI.inc b/lib/plugins/managesieve/localization/fi_FI.inc
index 1bec7a3..ba10b1f 100644
--- a/lib/plugins/managesieve/localization/fi_FI.inc
+++ b/lib/plugins/managesieve/localization/fi_FI.inc
@@ -1,110 +1,183 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Suodattimet';
$labels['managefilters'] = 'Hallitse saapuvan sähköpostin suodattimia';
$labels['filtername'] = 'Suodattimen nimi';
$labels['newfilter'] = 'Uusi suodatin';
$labels['filteradd'] = 'Lisää suodatin';
$labels['filterdel'] = 'Poista suodatin';
$labels['moveup'] = 'Siirrä ylös';
$labels['movedown'] = 'Siirrä alas';
$labels['filterallof'] = 'Täsmää kaikkiin seuraaviin sääntöihin';
$labels['filteranyof'] = 'Täsmää mihin tahansa seuraavista säännöistä';
$labels['filterany'] = 'Kaikki viestit';
$labels['filtercontains'] = 'Sisältää';
$labels['filternotcontains'] = 'Ei sisällä';
$labels['filteris'] = 'on samanlainen kuin';
$labels['filterisnot'] = 'ei ole samanlainen kuin';
$labels['filterexists'] = 'on olemassa';
$labels['filternotexists'] = 'ei ole olemassa';
$labels['filtermatches'] = 'vastaa lauseketta';
$labels['filternotmatches'] = 'ei vastaa lauseketta';
$labels['filterregex'] = 'vastaa säännöllistä lauseketta';
$labels['filternotregex'] = 'ei vastaa säännöllistä lauseketta';
$labels['filterunder'] = 'alle';
$labels['filterover'] = 'yli';
$labels['addrule'] = 'Lisää sääntö';
$labels['delrule'] = 'Poista sääntö';
$labels['messagemoveto'] = 'Siirrä viesti';
$labels['messageredirect'] = 'Lähetä viesti edelleen';
$labels['messagecopyto'] = 'Kopioi viesti';
$labels['messagesendcopy'] = 'Lähetä kopio viestistä';
$labels['messagereply'] = 'Vastaa viestillä';
$labels['messagedelete'] = 'Poista viesti';
$labels['messagediscard'] = 'Hylkää viestillä';
$labels['messagekeep'] = 'Säilytä viesti saapuneissa';
+$labels['messagesrules'] = 'Saapuville viesteille:';
$labels['messagesactions'] = '...suorita seuraavat toiminnot:';
$labels['add'] = 'Lisää';
$labels['del'] = 'Poista';
$labels['sender'] = 'Lähettäjä';
$labels['recipient'] = 'Vastaanottaja';
+$labels['vacationaddr'] = 'Muut sähköpostiosoitteeni:';
+$labels['vacationreason'] = 'Viestin runko (loman syy):';
$labels['vacationsubject'] = 'Viestin aihe:';
$labels['days'] = 'päivää';
$labels['seconds'] = 'sekuntia';
+$labels['rulestop'] = 'Lopeta sääntöjen arviointi';
+$labels['enable'] = 'Ota käyttöön/poista käytöstä';
+$labels['filterset'] = 'Suodattimien asetus';
+$labels['filtersets'] = 'Suodattimen asetus';
+$labels['filtersetadd'] = 'Lisää suodatinasetus';
+$labels['filtersetdel'] = 'Poista nykyiset suodatinasetukset';
+$labels['filtersetact'] = 'Aktivoi nykyinen suodattimien asetus';
+$labels['filtersetdeact'] = 'Poista käytöstä nykyinen suodattimien asetus';
+$labels['filterdef'] = 'Suodattimen määrittely';
+$labels['filtersetname'] = 'Suodattimien asetuksen nimi';
+$labels['active'] = 'aktiivinen';
+$labels['none'] = 'Ei mikään';
+$labels['fromset'] = 'sarjasta';
+$labels['fromfile'] = 'tiedostosta';
+$labels['filterdisabled'] = 'Suodatin poistettu käytöstä';
+$labels['countisgreaterthan'] = 'määrä on suurempi kuin';
+$labels['countisgreaterthanequal'] = 'määrä on suurempi tai yhtä suuri kuin';
+$labels['countislessthan'] = 'määrä on vähemmän kuin';
+$labels['countislessthanequal'] = 'määrä on vähemmän tai yhtä suuri kuin';
+$labels['countequals'] = 'määrä on yhtä suuri kuin';
+$labels['countnotequals'] = 'määrä ei ole yhtä suuri kuin';
+$labels['valueisgreaterthan'] = 'arvo on suurempi kuin';
+$labels['valueisgreaterthanequal'] = 'arvo on suurempi kuin tai yhtä suuri kuin';
+$labels['valueislessthan'] = 'arvo on vähemmän kuin';
+$labels['valueislessthanequal'] = 'määrä on vähemmän tai yhtä suuri kuin';
+$labels['valueequals'] = 'arvo on yhtä suuri kuin';
+$labels['valuenotequals'] = 'arvo ei ole yhtä suuri kuin';
$labels['setflags'] = 'Aseta liput viestiin';
$labels['addflags'] = 'Lisää liput viestiin';
$labels['removeflags'] = 'Poista liput viestistä';
+$labels['flagread'] = 'Lue';
$labels['flagdeleted'] = 'Poistettu';
$labels['flaganswered'] = 'Vastattu';
$labels['flagflagged'] = 'Liputettu';
$labels['flagdraft'] = 'Luonnos';
$labels['setvariable'] = 'Aseta muuttuja';
$labels['setvarname'] = 'Muuttujan nimi:';
$labels['setvarvalue'] = 'Muuttujan arvo:';
+$labels['setvarmodifiers'] = 'Muuntimet:';
+$labels['varlower'] = 'pienellä kirjoitettu';
+$labels['varupper'] = 'isolla kirjoitettu';
+$labels['varlowerfirst'] = 'ensimmäinen merkki pienellä kirjoitettuna';
+$labels['varupperfirst'] = 'ensimmäinen merkki isolla kirjoitettuna';
+$labels['varquotewildcard'] = 'lainaa erikoismerkit';
+$labels['varlength'] = 'pituus';
+$labels['notify'] = 'Lähetä ilmoitus';
+$labels['notifytarget'] = 'Ilmoituksen kohde:';
+$labels['notifymessage'] = 'Ilmoituksen viesti (valinnainen):';
+$labels['notifyoptions'] = 'Ilmoituksen valinnat (valinnainen)';
+$labels['notifyfrom'] = 'Ilmoituksen lähettäjä (valinnainen):';
$labels['notifyimportance'] = 'Tärkeysaste:';
+$labels['notifyimportancelow'] = 'matala';
+$labels['notifyimportancenormal'] = 'normaali';
+$labels['notifyimportancehigh'] = 'korkea';
$labels['notifymethodmailto'] = 'Sähköposti';
$labels['notifymethodtel'] = 'Puhelin';
$labels['notifymethodsms'] = 'Tekstiviesti';
$labels['filtercreate'] = 'Luo suodatin';
+$labels['usedata'] = 'Käytä seuraavaa dataa suodattimessa:';
+$labels['nextstep'] = 'Seuraava vaihe';
$labels['...'] = '...';
+$labels['currdate'] = 'Nykyinen päivämäärä';
+$labels['datetest'] = 'Päivämäärä';
+$labels['dateheader'] = 'otsikko:';
$labels['year'] = 'vuosi';
$labels['month'] = 'kuukausi';
$labels['day'] = 'päivä';
+$labels['date'] = 'päivämäärä (vvvv-kk-pp)';
+$labels['julian'] = 'päivämäärä (juliaaninen)';
$labels['hour'] = 'tunti';
$labels['minute'] = 'minuutti';
$labels['second'] = 'sekunti';
$labels['time'] = 'aika (hh:mm:ss)';
+$labels['iso8601'] = 'päivämäärä (ISO8601)';
+$labels['std11'] = 'päivämäärä (RFC2882)';
$labels['zone'] = 'aikavyöhyke';
+$labels['weekday'] = 'viikonpäivä (0-6)';
$labels['advancedopts'] = 'Lisävalinnat';
+$labels['body'] = 'Runko';
$labels['address'] = 'osoite';
+$labels['envelope'] = 'kirjekuori';
+$labels['modifier'] = 'muuntaja:';
+$labels['text'] = 'teksti';
+$labels['undecoded'] = 'dekoodaamaton (raaka)';
+$labels['contenttype'] = 'sisällön tyyppi';
+$labels['modtype'] = 'tyyppi:';
$labels['allparts'] = 'kaikki';
+$labels['domain'] = 'domain';
+$labels['localpart'] = 'paikallinen osa';
$labels['user'] = 'käyttäjä';
+$labels['detail'] = 'yksityiskohta';
+$labels['comparator'] = 'vertailija:';
$labels['default'] = 'oletus';
$labels['vacation'] = 'Loma';
$labels['vacation.reply'] = 'Vastausviesti';
$labels['vacation.advanced'] = 'Lisäasetukset';
$labels['vacation.subject'] = 'Aihe';
$labels['vacation.body'] = 'Sisältö';
-$labels['vacation.dates'] = 'Loma-aika';
-$labels['vacation.from'] = 'Lähettäjä:';
-$labels['vacation.to'] = 'Vastaanottaja:';
$labels['vacation.status'] = 'Tila';
$labels['vacation.on'] = 'Päällä';
$labels['vacation.off'] = 'Pois';
$labels['vacation.saving'] = 'Tallennetaan tietoja...';
+$labels['vacation.action'] = 'Toiminto saapuvalle viestille';
+$labels['vacation.keep'] = 'Säilytä';
+$labels['vacation.discard'] = 'Hylkää';
+$labels['vacation.redirect'] = 'Ohjaa uudelleen osoitteeseen';
+$labels['vacation.copy'] = 'Lähetä kopio osoitteeseen';
$messages['filterunknownerror'] = 'Tuntematon palvelinvirhe.';
$messages['filterconnerror'] = 'Yhteys palvelimeen epäonnistui.';
$messages['filterdeleted'] = 'Suodatin poistettu onnistuneesti.';
+$messages['filtersaved'] = 'Suodatin tallennettu onnistuneesti.';
+$messages['filtersaveerror'] = 'Suodattimen tallennus epäonnistui palvelinvirheen vuoksi.';
$messages['filterdeleteconfirm'] = 'Haluatko varmasti poistaa valitun suodattimen?';
+$messages['forbiddenchars'] = 'Virheellisiä merkkejä kentässä.';
$messages['cannotbeempty'] = 'Kenttä ei voi olla tyhjä.';
+$messages['ruleexist'] = 'Suodatin samalla nimellä on jo olemassa.';
$messages['moved'] = 'Suodatin siirretty onnistuneesti.';
$messages['nametoolong'] = 'Nimi on liian pitkä.';
$messages['saveerror'] = 'Tietojen tallennus epäonnistui palvelinvirheen vuoksi.';
$messages['vacationsaved'] = 'Lomatiedot tallennettu onnistuneesti.';
$messages['emptyvacationbody'] = 'Lomaviestin sisältö vaaditaan!';
?>
diff --git a/lib/plugins/managesieve/localization/fr_FR.inc b/lib/plugins/managesieve/localization/fr_FR.inc
index 67a73a9..6377d12 100644
--- a/lib/plugins/managesieve/localization/fr_FR.inc
+++ b/lib/plugins/managesieve/localization/fr_FR.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtres';
-$labels['managefilters'] = 'Gérer les filtres sur les courriels entrants';
+$labels['managefilters'] = 'Gérer les filtres de courriels entrants';
$labels['filtername'] = 'Nom du filtre';
$labels['newfilter'] = 'Nouveau filtre';
$labels['filteradd'] = 'Ajouter un filtre';
$labels['filterdel'] = 'Supprimer le filtre';
$labels['moveup'] = 'Monter';
$labels['movedown'] = 'Descendre';
-$labels['filterallof'] = 'valident toutes les conditions suivantes';
+$labels['filterallof'] = 'correspondant à toutes les règles suivantes';
$labels['filteranyof'] = 'valident au moins une des conditions suivantes';
$labels['filterany'] = 'tous les messages';
$labels['filtercontains'] = 'contient';
$labels['filternotcontains'] = 'ne contient pas';
$labels['filteris'] = 'est égal à';
$labels['filterisnot'] = 'est différent de';
$labels['filterexists'] = 'existe';
$labels['filternotexists'] = 'n\'existe pas';
-$labels['filtermatches'] = 'concorde avec l\'expression';
-$labels['filternotmatches'] = 'ne concorde pas avec l\'expression';
-$labels['filterregex'] = 'concorde avec l\'expression régulière';
-$labels['filternotregex'] = 'ne concorde pas avec l\'expression régulière';
-$labels['filterunder'] = 'est plus petit que';
-$labels['filterover'] = 'est plus grand que';
+$labels['filtermatches'] = 'correspond à l\'expression';
+$labels['filternotmatches'] = 'ne correspond pas à l\'expression';
+$labels['filterregex'] = 'correspond à l\'expression rationnelle';
+$labels['filternotregex'] = 'ne correspond pas à l\'expression rationnelle';
+$labels['filterunder'] = 'plus petit que';
+$labels['filterover'] = 'plus grand que';
$labels['addrule'] = 'Ajouter une règle';
$labels['delrule'] = 'Supprimer une règle';
$labels['messagemoveto'] = 'Déplacer le message vers';
-$labels['messageredirect'] = 'Transférer le message à';
+$labels['messageredirect'] = 'Rediriger le message à';
$labels['messagecopyto'] = 'Copier le message vers';
$labels['messagesendcopy'] = 'Envoyer une copie du message à';
$labels['messagereply'] = 'Répondre avec le message';
$labels['messagedelete'] = 'Supprimer le message';
$labels['messagediscard'] = 'Rejeter avec le message';
$labels['messagekeep'] = 'Conserver le message dans la boîte de réception';
$labels['messagesrules'] = 'Pour les courriels entrants :';
-$labels['messagesactions'] = '...exécuter les actions suivantes:';
+$labels['messagesactions'] = '...exécuter les actions suivantes :';
$labels['add'] = 'Ajouter';
$labels['del'] = 'Supprimer';
$labels['sender'] = 'Expéditeur';
$labels['recipient'] = 'Destinataire';
-$labels['vacationaddr'] = 'Adresse(s) e-mail additionnelle(s):';
-$labels['vacationdays'] = 'Ne pas renvoyer un message avant (jours) :';
-$labels['vacationinterval'] = 'Comment envoyer les messages :';
+$labels['vacationaddr'] = 'Mes adresses courriel additionnelles :';
+$labels['vacationdays'] = 'Fréquence d\'envoi des messages (en jours) :';
+$labels['vacationinterval'] = 'Fréquence d\'envoi des messages :';
+$labels['vacationreason'] = 'Corps du message (raison de l\'absence) :';
+$labels['vacationsubject'] = 'Objet du message :';
$labels['days'] = 'jours';
$labels['seconds'] = 'secondes';
-$labels['vacationreason'] = 'Corps du message (raison de l\'absence) :';
-$labels['vacationsubject'] = 'Sujet du message:';
-$labels['rulestop'] = 'Arrêter d\'évaluer les prochaines règles';
-$labels['enable'] = 'Activer/Désactiver';
-$labels['filterset'] = 'Groupe de filtres';
-$labels['filtersets'] = 'Groupes de filtres';
-$labels['filtersetadd'] = 'Ajouter un groupe de filtres';
-$labels['filtersetdel'] = 'Supprimer le groupe de filtres actuel';
-$labels['filtersetact'] = 'Activer le groupe de filtres actuel';
-$labels['filtersetdeact'] = 'Désactiver le groupe de filtres actuel';
+$labels['rulestop'] = 'Arrêter l\'évaluation des règles';
+$labels['enable'] = 'Activer/désactiver';
+$labels['filterset'] = 'Jeu de filtres';
+$labels['filtersets'] = 'Jeux de filtres';
+$labels['filtersetadd'] = 'Ajouter un jeu de filtres';
+$labels['filtersetdel'] = 'Supprimer le jeu de filtres actuel';
+$labels['filtersetact'] = 'Activer le jeu de filtres actuel';
+$labels['filtersetdeact'] = 'Désactiver le jeu de filtres actuel';
$labels['filterdef'] = 'Définition du filtre';
-$labels['filtersetname'] = 'Nom du groupe de filtres';
-$labels['newfilterset'] = 'Nouveau groupe de filtres';
-$labels['active'] = 'actif';
+$labels['filtersetname'] = 'Nom du jeu de filtres';
+$labels['newfilterset'] = 'Nouveau jeu de filtres';
+$labels['active'] = 'activer';
$labels['none'] = 'aucun';
-$labels['fromset'] = 'à partir du filtre';
+$labels['fromset'] = 'à partir du jeu';
$labels['fromfile'] = 'à partir du fichier';
$labels['filterdisabled'] = 'Filtre désactivé';
$labels['countisgreaterthan'] = 'total supérieur à';
$labels['countisgreaterthanequal'] = 'total supérieur ou égal à';
$labels['countislessthan'] = 'total inférieur à';
-$labels['countislessthanequal'] = 'total inférieur à';
+$labels['countislessthanequal'] = 'total inférieur ou égal à';
$labels['countequals'] = 'total égal à';
-$labels['countnotequals'] = 'le comptage n\'est pas égal à';
+$labels['countnotequals'] = 'le nombre n\'est pas égal à';
$labels['valueisgreaterthan'] = 'valeur supérieure à';
$labels['valueisgreaterthanequal'] = 'valeur supérieure ou égale à';
$labels['valueislessthan'] = 'valeur inférieure à';
$labels['valueislessthanequal'] = 'valeur inférieure ou égale à';
$labels['valueequals'] = 'valeur égale à';
$labels['valuenotequals'] = 'la valeur n\'est pas égale à';
-$labels['setflags'] = 'Mettre les marqueurs au message';
-$labels['addflags'] = 'Ajouter les marqueurs au message';
-$labels['removeflags'] = 'Supprimer les marqueurs du message';
+$labels['setflags'] = 'Définir les drapeaux pour le message';
+$labels['addflags'] = 'Ajouter les drapeaux au message';
+$labels['removeflags'] = 'Supprimer les drapeaux du message';
$labels['flagread'] = 'Lu';
$labels['flagdeleted'] = 'Supprimé';
-$labels['flaganswered'] = 'Répondu';
-$labels['flagflagged'] = 'Marqué';
+$labels['flaganswered'] = 'Réponse envoyée';
+$labels['flagflagged'] = 'Signalé';
$labels['flagdraft'] = 'Brouillon';
$labels['setvariable'] = 'Définir une variable';
$labels['setvarname'] = 'Nom de la variable :';
$labels['setvarvalue'] = 'Valeur de la variable :';
-$labels['setvarmodifiers'] = 'Modifications :';
+$labels['setvarmodifiers'] = 'Modificateurs :';
$labels['varlower'] = 'minuscule';
$labels['varupper'] = 'majuscule';
-$labels['varlowerfirst'] = 'premier caractère minuscule';
-$labels['varupperfirst'] = 'premier caractère majuscule';
-$labels['varquotewildcard'] = 'Échapper les caractères spéciaux';
+$labels['varlowerfirst'] = 'premier caractère en minuscule';
+$labels['varupperfirst'] = 'premier caractère en majuscule';
+$labels['varquotewildcard'] = 'citer les caractères spéciaux';
$labels['varlength'] = 'longueur';
$labels['notify'] = 'Envoyer la notification';
-$labels['notifyaddress'] = 'A l\'adresse e-mail :';
-$labels['notifybody'] = 'Corps de la notification :';
-$labels['notifysubject'] = 'Objet de la notification :';
-$labels['notifyfrom'] = 'Expéditeur de la notification :';
+$labels['notifytarget'] = 'Cible de la notification :';
+$labels['notifymessage'] = 'Message de notification (optionnel) :';
+$labels['notifyoptions'] = 'Options de notification (optionnel) :';
+$labels['notifyfrom'] = 'Expéditeur de la notification (optionnel) :';
$labels['notifyimportance'] = 'Importance :';
$labels['notifyimportancelow'] = 'faible';
-$labels['notifyimportancenormal'] = 'normal';
+$labels['notifyimportancenormal'] = 'normale';
$labels['notifyimportancehigh'] = 'haute';
+$labels['notifymethodmailto'] = 'Courriel';
+$labels['notifymethodtel'] = 'Téléphone';
+$labels['notifymethodsms'] = 'Message texte';
$labels['filtercreate'] = 'Créer un filtre';
-$labels['usedata'] = 'Utiliser les informations suivantes dans le filtre';
+$labels['usedata'] = 'Utiliser les données suivantes dans le filtre :';
$labels['nextstep'] = 'Étape suivante';
$labels['...'] = '...';
$labels['currdate'] = 'Date actuelle';
$labels['datetest'] = 'Date';
-$labels['dateheader'] = 'header:';
+$labels['dateheader'] = 'en-tête :';
$labels['year'] = 'année';
$labels['month'] = 'mois';
$labels['day'] = 'jour';
-$labels['date'] = 'date (yyyy-mm-dd)';
+$labels['date'] = 'date (aaaa-mm-jj)';
$labels['julian'] = 'date (julien)';
$labels['hour'] = 'heure';
$labels['minute'] = 'minute';
$labels['second'] = 'seconde';
$labels['time'] = 'heure (hh:mm:ss)';
$labels['iso8601'] = 'date (ISO8601)';
$labels['std11'] = 'date (RFC2822)';
$labels['zone'] = 'fuseau horaire';
$labels['weekday'] = 'jour de la semaine (0-6)';
$labels['advancedopts'] = 'Options avancées';
-$labels['body'] = 'Corps du message';
+$labels['body'] = 'Corps';
$labels['address'] = 'adresse';
$labels['envelope'] = 'enveloppe';
-$labels['modifier'] = 'modificateur:';
+$labels['modifier'] = 'modificateur :';
$labels['text'] = 'texte';
$labels['undecoded'] = 'non décodé (brut)';
$labels['contenttype'] = 'type de contenu';
-$labels['modtype'] = 'type:';
+$labels['modtype'] = 'type :';
$labels['allparts'] = 'tout';
$labels['domain'] = 'domaine';
$labels['localpart'] = 'partie locale';
$labels['user'] = 'utilisateur';
$labels['detail'] = 'détail';
-$labels['comparator'] = 'comparateur';
+$labels['comparator'] = 'comparateur :';
$labels['default'] = 'par défaut';
$labels['octet'] = 'strict (octet)';
$labels['asciicasemap'] = 'insensible à la casse (ascii-casemap)';
$labels['asciinumeric'] = 'numérique (ascii-numeric)';
-$labels['index'] = 'index:';
-$labels['indexlast'] = 'retour arrière';
-$messages['filterunknownerror'] = 'Erreur du serveur inconnue';
-$messages['filterconnerror'] = 'Connexion au serveur Managesieve impossible';
-$messages['filterdeleteerror'] = 'Impossible de supprimer le filtre. Une erreur serveur est survenue.';
-$messages['filterdeleted'] = 'Le filtre a bien été supprimé';
-$messages['filtersaved'] = 'Le filtre a bien été enregistré';
-$messages['filtersaveerror'] = 'Impossible de sauvegarder le filtre. Une erreur serveur est survenue.';
-$messages['filterdeleteconfirm'] = 'Voulez-vous vraiment supprimer le filtre sélectionné?';
-$messages['ruledeleteconfirm'] = 'Voulez-vous vraiment supprimer la règle sélectionnée?';
-$messages['actiondeleteconfirm'] = 'Voulez-vous vraiment supprimer l\'action sélectionnée?';
+$labels['index'] = 'index :';
+$labels['indexlast'] = 'à l\'envers';
+$labels['vacation'] = 'Vacances';
+$labels['vacation.reply'] = 'Message de réponse';
+$labels['vacation.advanced'] = 'Paramètres avancés';
+$labels['vacation.subject'] = 'Objet';
+$labels['vacation.body'] = 'Corps';
+$labels['vacation.start'] = 'Début de vacances';
+$labels['vacation.end'] = 'Fin de vacances';
+$labels['vacation.status'] = 'État';
+$labels['vacation.on'] = 'Arrêt';
+$labels['vacation.off'] = 'Marche';
+$labels['vacation.addresses'] = 'Mes adresses supplémentaires';
+$labels['vacation.interval'] = 'Plage de réponse';
+$labels['vacation.after'] = 'Mettre en place la règle de vacances après';
+$labels['vacation.saving'] = 'Enregistrement des données...';
+$labels['vacation.action'] = 'Action pour message entrant';
+$labels['vacation.keep'] = 'Garder';
+$labels['vacation.discard'] = 'Rejeter';
+$labels['vacation.redirect'] = 'Réacheminer à';
+$labels['vacation.copy'] = 'Envoyer une copie à';
+$labels['arialabelfiltersetactions'] = 'Actions des jeux de filtrage';
+$labels['arialabelfilteractions'] = 'Actions de filtrage';
+$labels['arialabelfilterform'] = 'Propriété du filtrage';
+$labels['ariasummaryfilterslist'] = 'Liste des filtres';
+$labels['ariasummaryfiltersetslist'] = 'Liste des jeux de filtrage';
+$labels['filterstitle'] = 'Modifier les filtres de courriels entrants';
+$labels['vacationtitle'] = 'Modifier la règle d\'absence du bureau';
+$messages['filterunknownerror'] = 'Erreur de serveur inconnue';
+$messages['filterconnerror'] = 'Connexion au serveur impossible.';
+$messages['filterdeleteerror'] = 'Impossible de supprimer le filtre. Une erreur de serveur est survenue.';
+$messages['filterdeleted'] = 'Le filtre a été supprimé avec succès.';
+$messages['filtersaved'] = 'Le filtre a été enregistré avec succès.';
+$messages['filtersaveerror'] = 'Impossible d\'enregistrer le filtre. Une erreur de serveur est survenue.';
+$messages['filterdeleteconfirm'] = 'Voulez-vous vraiment supprimer le filtre sélectionné ?';
+$messages['ruledeleteconfirm'] = 'Voulez-vous vraiment supprimer la règle sélectionnée ?';
+$messages['actiondeleteconfirm'] = 'Voulez-vous vraiment supprimer l\'action sélectionnée ?';
$messages['forbiddenchars'] = 'Caractères interdits dans le champ';
$messages['cannotbeempty'] = 'Le champ ne peut pas être vide';
$messages['ruleexist'] = 'Un filtre existe déjà avec ce nom.';
-$messages['setactivateerror'] = 'Impossible d\'activer le set de filtres sélectionné. Une erreur serveur est survenue.';
-$messages['setdeactivateerror'] = 'Impossible de désactiver le set de filtres sélectionné. Une erreur serveur est survenue.';
-$messages['setdeleteerror'] = 'Impossible de supprimer les set de filtres sélectionné. Une erreur serveur est survenue.';
-$messages['setactivated'] = 'Le groupe de filtres a bien été activé.';
-$messages['setdeactivated'] = 'Le groupe de filtres a bien été désactivé.';
-$messages['setdeleted'] = 'Le groupe de filtres a bien été supprimé.';
-$messages['setdeleteconfirm'] = 'Voulez vous vraiment supprimer le groupe de filtres sélectionné ?';
-$messages['setcreateerror'] = 'Impossible de créer un set de filtres. Une erreur serveur est survenue.';
-$messages['setcreated'] = 'Le groupe de filtres a bien été créé.';
-$messages['activateerror'] = 'Impossible d\'activer le ou les filtres sélectionné(s). Une erreur serveur est survenue.';
-$messages['deactivateerror'] = 'Impossible de désactiver le ou les filtres sélectionné(s). Une erreur serveur est survenue.';
+$messages['setactivateerror'] = 'Impossible d\'activer le jeu de filtres sélectionné. Une erreur de serveur est survenue.';
+$messages['setdeactivateerror'] = 'Impossible de désactiver le jeu de filtres sélectionné. Une erreur de serveur est survenue.';
+$messages['setdeleteerror'] = 'Impossible de supprimer le jeu de filtres sélectionné. Une erreur de serveur est survenue.';
+$messages['setactivated'] = 'Le jeu de filtres a été activé avec succès.';
+$messages['setdeactivated'] = 'Le jeu de filtres a été désactivé avec succès.';
+$messages['setdeleted'] = 'Le jeu de filtres a été supprimé avec succès.';
+$messages['setdeleteconfirm'] = 'Voulez vous vraiment supprimer le jeu de filtres sélectionné ?';
+$messages['setcreateerror'] = 'Impossible de créer un jeu de filtres. Une erreur de serveur est survenue.';
+$messages['setcreated'] = 'Le jeu de filtres a été créé avec succès.';
+$messages['activateerror'] = 'Impossible d\'activer le/les filtre(s) sélectionné(s). Une erreur de serveur est survenue.';
+$messages['deactivateerror'] = 'Impossible de désactiver le/les filtre(s) sélectionné(s). Une erreur de serveur est survenue.';
$messages['deactivated'] = 'Filtre(s) désactivé(s) avec succès.';
$messages['activated'] = 'Filtre(s) activé(s) avec succès.';
$messages['moved'] = 'Filtre déplacé avec succès.';
-$messages['moveerror'] = 'Impossible de déplacer le filtre sélectionné. Une erreur serveur est survenue.';
+$messages['moveerror'] = 'Impossible de déplacer le filtre sélectionné. Une erreur de serveur est survenue.';
$messages['nametoolong'] = 'Nom trop long.';
$messages['namereserved'] = 'Nom réservé.';
-$messages['setexist'] = 'Ce groupe existe déjà.';
-$messages['nodata'] = 'Au moins un élément doit être selectionné !';
-$messages['invaliddateformat'] = 'Date non valide ou format d\'une partie de la date';
+$messages['setexist'] = 'Le jeu existe déjà.';
+$messages['nodata'] = 'Au moins un élément doit être sélectionné !';
+$messages['invaliddateformat'] = 'Format de date ou d\'une partie de la date invalide';
+$messages['saveerror'] = 'Impossible d\'enregistrer les données. Une erreur du serveur est survenue.';
+$messages['vacationsaved'] = 'Les données de vacances ont été enregistrées avec succès.';
+$messages['emptyvacationbody'] = 'Le corps du message de vacances est nécessaire !';
?>
diff --git a/lib/plugins/managesieve/localization/gl_ES.inc b/lib/plugins/managesieve/localization/gl_ES.inc
index f1f9d2f..cbe45ca 100644
--- a/lib/plugins/managesieve/localization/gl_ES.inc
+++ b/lib/plugins/managesieve/localization/gl_ES.inc
@@ -1,192 +1,206 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtros';
$labels['managefilters'] = 'Xestionar os filtros de correo entrante';
$labels['filtername'] = 'Nome do filtro';
$labels['newfilter'] = 'Novo filtro';
$labels['filteradd'] = 'Engadir filtro';
$labels['filterdel'] = 'Eliminar filtro';
$labels['moveup'] = 'Mover arriba';
$labels['movedown'] = 'Mover abaixo';
$labels['filterallof'] = 'coincidir con todas as regras seguintes';
$labels['filteranyof'] = 'coincidir con algunha das regras seguintes';
$labels['filterany'] = 'todas as mensaxes';
$labels['filtercontains'] = 'contén';
$labels['filternotcontains'] = 'non contén';
$labels['filteris'] = 'é igual a';
$labels['filterisnot'] = 'non é igual a';
$labels['filterexists'] = 'existe';
$labels['filternotexists'] = 'non existe';
$labels['filtermatches'] = 'casa coa expresión';
$labels['filternotmatches'] = 'non casa coa expresión';
$labels['filterregex'] = 'casa coa expresión regular';
$labels['filternotregex'] = 'non casa coa expresión regular';
$labels['filterunder'] = 'baixo';
$labels['filterover'] = 'sobre';
$labels['addrule'] = 'Engadir regra';
$labels['delrule'] = 'Eliminar regra';
$labels['messagemoveto'] = 'Mover a mensaxe a';
$labels['messageredirect'] = 'Redirixir a mensaxe a';
$labels['messagecopyto'] = 'Copiar a mensaxe a';
$labels['messagesendcopy'] = 'Enviar copia da mensaxe a';
$labels['messagereply'] = 'Respostar con unha mensaxe';
$labels['messagedelete'] = 'Eliminar a mensaxe';
$labels['messagediscard'] = 'Descartar con unha mensaxe';
$labels['messagekeep'] = 'Manter mensaxe na caixa de entrada';
$labels['messagesrules'] = 'Para o correo entrante:';
$labels['messagesactions'] = '... executar as seguintes accións:';
$labels['add'] = 'Engadir';
$labels['del'] = 'Eliminar';
$labels['sender'] = 'Remite';
$labels['recipient'] = 'Persoa destinataria';
$labels['vacationaddr'] = 'O(s) meu(s) outro (s) enderezo(s) de correo:';
$labels['vacationdays'] = 'Cada canto enviar mensaxes (en días):';
$labels['vacationinterval'] = 'Con que frecuencia se van enviar mensaxes:';
-$labels['days'] = 'días';
-$labels['seconds'] = 'segundos';
$labels['vacationreason'] = 'Corpo da mensaxe (por vacacións):';
$labels['vacationsubject'] = 'Asunto da mensaxe:';
+$labels['days'] = 'días';
+$labels['seconds'] = 'segundos';
$labels['rulestop'] = 'Parar de avaliar regras';
$labels['enable'] = 'Activar/Desactivar';
$labels['filterset'] = 'Conxunto de filtros';
$labels['filtersets'] = 'Conxunto de filtros';
$labels['filtersetadd'] = 'Engadir un conxunto de filtros';
$labels['filtersetdel'] = 'Eliminar o conxunto de filtros actual';
$labels['filtersetact'] = 'Activar o conxunto de filtros actual';
$labels['filtersetdeact'] = 'Desactivar o conxunto de filtros actual';
$labels['filterdef'] = 'Definición de filtros';
$labels['filtersetname'] = 'Nome do conxunto de filtros';
$labels['newfilterset'] = 'Novo conxunto de filtros';
$labels['active'] = 'activo';
$labels['none'] = 'ningún';
$labels['fromset'] = 'de conxunto';
$labels['fromfile'] = 'de arquivo';
$labels['filterdisabled'] = 'Filtro desactivado';
$labels['countisgreaterthan'] = 'a conta é maior que';
$labels['countisgreaterthanequal'] = 'a conta é maior ou igual a';
$labels['countislessthan'] = 'a conta é menor que';
$labels['countislessthanequal'] = 'a conta é menor ou igual a';
$labels['countequals'] = 'a conta é igual a';
$labels['countnotequals'] = 'a conta non é igual a';
$labels['valueisgreaterthan'] = 'o valor é meirande que ';
$labels['valueisgreaterthanequal'] = 'o valor é maior ou igual a';
$labels['valueislessthan'] = 'o valor é menor que';
$labels['valueislessthanequal'] = 'o valor é menor ou igual a';
$labels['valueequals'] = 'o valor é igual a';
$labels['valuenotequals'] = 'o valor non é igual a';
$labels['setflags'] = 'Marcar a mensaxe';
$labels['addflags'] = 'Engadir marcas á mensaxe';
$labels['removeflags'] = 'Desmarcar as mensaxes';
$labels['flagread'] = 'Lidas';
$labels['flagdeleted'] = 'Eliminadas';
$labels['flaganswered'] = 'Respostadas';
$labels['flagflagged'] = 'Marcadas';
$labels['flagdraft'] = 'Borrador';
$labels['setvariable'] = 'Estabelecer variábel';
$labels['setvarname'] = 'Nome da variábel:';
$labels['setvarvalue'] = 'Valor da variábel:';
$labels['setvarmodifiers'] = 'Modificadores:';
$labels['varlower'] = 'minúscula';
$labels['varupper'] = 'maiúscula';
$labels['varlowerfirst'] = 'primeira letra minúscula';
$labels['varupperfirst'] = 'primeira letra maiúscula';
$labels['varquotewildcard'] = 'poñer entre aspas caracteres especiais';
$labels['varlength'] = 'lonxitude';
$labels['notify'] = 'Enviar notificación';
-$labels['notifyaddress'] = 'A este enderezo de correo:';
-$labels['notifybody'] = 'Corpo da notificación:';
-$labels['notifysubject'] = 'Asunto da notificación:';
-$labels['notifyfrom'] = 'Remite da notificación:';
$labels['notifyimportance'] = 'Importancia:';
$labels['notifyimportancelow'] = 'baixa';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'alta';
+$labels['notifymethodmailto'] = 'Correo electrónico';
+$labels['notifymethodtel'] = 'Teléfono';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Crear filtro';
$labels['usedata'] = 'Usar os seguintes datos no filtro:';
$labels['nextstep'] = 'Seguinte paso';
$labels['...'] = '...';
$labels['currdate'] = 'Data actual';
$labels['datetest'] = 'Data';
$labels['dateheader'] = 'cabeceira:';
$labels['year'] = 'ano';
$labels['month'] = 'mes';
$labels['day'] = 'día';
$labels['date'] = 'data (aaaa-mm-dd)';
$labels['julian'] = 'data (xuliano)';
$labels['hour'] = 'hora';
$labels['minute'] = 'minuto';
$labels['second'] = 'segundo';
$labels['time'] = 'tempo (hh:mm:ss)';
$labels['iso8601'] = 'data (ISO8601)';
$labels['std11'] = 'data (RFC2822)';
$labels['zone'] = 'fuso-horario';
$labels['weekday'] = 'día da semana (0-6)';
$labels['advancedopts'] = 'Opcións avanzadas';
$labels['body'] = 'Corpo';
$labels['address'] = 'enderezo';
$labels['envelope'] = 'sobre';
$labels['modifier'] = 'modificador:';
$labels['text'] = 'texto';
$labels['undecoded'] = 'sen codificar (en bruto)';
$labels['contenttype'] = 'tipo de contido';
$labels['modtype'] = 'tipo:';
$labels['allparts'] = 'todos';
$labels['domain'] = 'dominio';
$labels['localpart'] = 'parte local';
$labels['user'] = 'utente';
$labels['detail'] = 'detalle';
$labels['comparator'] = 'comparador:';
$labels['default'] = 'predeterminado';
$labels['octet'] = 'estricto (octeto)';
$labels['asciicasemap'] = 'non sensíbel a maiúsculas/minúsculas (ascii-casemap)';
$labels['asciinumeric'] = 'numérico (ascii-numerico)';
$labels['index'] = 'índice:';
$labels['indexlast'] = 'atrás';
+$labels['vacation.reply'] = 'Respostar á mensaxe';
+$labels['vacation.advanced'] = 'Opcións avanzadas';
+$labels['vacation.subject'] = 'Asunto';
+$labels['vacation.body'] = 'Corpo';
+$labels['vacation.status'] = 'Estado';
+$labels['vacation.on'] = 'Activar';
+$labels['vacation.off'] = 'Desactivar';
+$labels['vacation.saving'] = 'Gardando datos...';
+$labels['vacation.keep'] = 'Manter';
+$labels['vacation.discard'] = 'Descartar';
+$labels['vacation.redirect'] = 'Redirixir a';
+$labels['vacation.copy'] = 'Enviar copia a';
+$labels['arialabelfilteractions'] = 'Accións de filtrado';
+$labels['arialabelfilterform'] = 'Propiedades dos filtros';
+$labels['ariasummaryfilterslist'] = 'Lista de filtros';
$messages['filterunknownerror'] = 'Erro descoñecido do servidor';
$messages['filterconnerror'] = 'Imposíbel conectar co servidor.';
$messages['filterdeleteerror'] = 'Non se pode eliminar filtro. Produciuse un erro de servidor.';
$messages['filterdeleted'] = 'Filtro borrado con éxito';
$messages['filtersaved'] = 'Filtro gardado con éxito';
$messages['filtersaveerror'] = 'Non se puido gardar filtro. Produciuse un erro de servidor.';
$messages['filterdeleteconfirm'] = 'Realmente queres eliminar o filtro seleccionado?';
$messages['ruledeleteconfirm'] = 'Seguro que queres eliminar a regra seleccionada?';
$messages['actiondeleteconfirm'] = 'Seguro que queres eliminar a acción seleccionada?';
$messages['forbiddenchars'] = 'Caracteres non permitidos no campo';
$messages['cannotbeempty'] = 'O campo non pode estar baleiro';
$messages['ruleexist'] = 'Xa existe un filtro co nome especificado.';
$messages['setactivateerror'] = 'Non se poden activar os filtros seleccionados. Produciuse un erro de servidor.';
$messages['setdeactivateerror'] = 'Non foi posíbel desactivar os filtros seleccionados. Produciuse un erro de servidor.';
$messages['setdeleteerror'] = 'Non é posíbel eliminar os filtros seleccionados. Produciuse un erro de servidor.';
$messages['setactivated'] = 'O conxunto de filtros activouse con éxito';
$messages['setdeactivated'] = 'O conxunto de filtros desactivouse con éxito';
$messages['setdeleted'] = 'O Conxunto de filtros borrouse con éxito';
$messages['setdeleteconfirm'] = 'Seguro que queres eliminar o conxunto de filtros seleccionado?';
$messages['setcreateerror'] = 'Non é posíbel crear filtros. Produciuse un erro de servidor.';
$messages['setcreated'] = 'Conxunto de filtros creado con éxito';
$messages['activateerror'] = 'Non é posíbel activar o(s) filtro(s) seleccionado(s). Produciuse un erro de servidor.';
$messages['deactivateerror'] = 'Incapaz de desactivar filtro(s) seleccionado(s). Produciuse un erro de servidor.';
$messages['deactivated'] = 'Desactiváronse os filtros correctamente.';
$messages['activated'] = 'Activáronse os filtros correctamente';
$messages['moved'] = 'Moveuse correctamente o filtro.';
$messages['moveerror'] = 'Non se pode mover o filtro seleccionado. Produciuse un erro de servidor.';
$messages['nametoolong'] = 'Imposíbel crear o conxunto de filtros. O nome é longo de máis';
$messages['namereserved'] = 'Nome reservado';
$messages['setexist'] = 'Xa existe o conxunto';
$messages['nodata'] = 'É preciso seleccionar polo menos unha posición!';
$messages['invaliddateformat'] = 'Formato de data ou parte dos datos non válidos';
?>
diff --git a/lib/plugins/managesieve/localization/he_IL.inc b/lib/plugins/managesieve/localization/he_IL.inc
index f347d94..4e7b597 100644
--- a/lib/plugins/managesieve/localization/he_IL.inc
+++ b/lib/plugins/managesieve/localization/he_IL.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'מסננים';
$labels['managefilters'] = 'ניהול מסננים לדואר נכנס';
$labels['filtername'] = 'שם המסנן';
$labels['newfilter'] = 'מסנן חדש';
$labels['filteradd'] = 'הוספת מסנן';
$labels['filterdel'] = 'מחיקת מסנן';
$labels['moveup'] = 'הזזה מעלה';
$labels['movedown'] = 'הזזה מטה';
$labels['filterallof'] = 'תאימות לכל הכללים שלהלן';
$labels['filteranyof'] = 'תאימות לחלק מהכללים שלהלן';
$labels['filterany'] = 'כל ההודעות';
$labels['filtercontains'] = 'מכיל';
$labels['filternotcontains'] = 'לא מכיל';
$labels['filteris'] = 'שווה ערך ל-';
$labels['filterisnot'] = 'אינו שווה ערך ל-';
$labels['filterexists'] = 'קיים';
$labels['filternotexists'] = 'לא קיים';
$labels['filtermatches'] = 'תואם ביטוי';
$labels['filternotmatches'] = 'לא תואם ביטוי';
$labels['filterregex'] = 'תואם ביטוי מורכב';
$labels['filternotregex'] = 'לא תואם ביטוי מורכב';
$labels['filterunder'] = 'תחת';
$labels['filterover'] = 'מעל';
$labels['addrule'] = 'הוספת כלל';
$labels['delrule'] = 'מחיקת כלל';
$labels['messagemoveto'] = 'העברת הודעה אל';
$labels['messageredirect'] = 'השמה חדשה של ההודעה אל';
$labels['messagecopyto'] = 'העתקת ההודעה אל';
$labels['messagesendcopy'] = 'משלוח העתק מההודעה אל';
$labels['messagereply'] = 'מענה עם הודעה';
$labels['messagedelete'] = 'מחיקת הודעה';
$labels['messagediscard'] = 'ביטול ההודעה';
$labels['messagekeep'] = 'שמירת הודעה בדואר נכנס';
$labels['messagesrules'] = 'עבור דואר נכנס:';
$labels['messagesactions'] = '...מבצע הפעולות הבאות:';
$labels['add'] = 'הוספה';
$labels['del'] = 'מחיקה';
$labels['sender'] = 'השולח';
$labels['recipient'] = 'הנמען';
$labels['vacationaddr'] = 'כתובות דוא"ל נוספות:';
$labels['vacationdays'] = 'באיזו תדירות ( בימים ) לשלוח הודעות:';
$labels['vacationinterval'] = 'באיזו תדירות לשלוח ההודעה';
-$labels['days'] = 'ימים';
-$labels['seconds'] = 'שניות';
$labels['vacationreason'] = 'גוף ההודעה (סיבת החופשה):';
$labels['vacationsubject'] = 'נושא ההודעה:';
+$labels['days'] = 'ימים';
+$labels['seconds'] = 'שניות';
$labels['rulestop'] = 'עצירה של בחינת הכללים';
$labels['enable'] = 'אפשור/ניטרול';
$labels['filterset'] = 'קבוצת מסננים';
$labels['filtersets'] = 'קבוצות מסננים';
$labels['filtersetadd'] = 'הוספה של קבוצת מסננים';
$labels['filtersetdel'] = 'מחיקה של מסננים נוכחיים';
$labels['filtersetact'] = 'הפעלה של מסננים נוכחיים';
$labels['filtersetdeact'] = 'השבתה של מסננים נוכחיים';
$labels['filterdef'] = 'הגדרת מסנן';
$labels['filtersetname'] = 'שם של קבוצת מסננים';
$labels['newfilterset'] = 'קבוצת מסננים חדשה';
$labels['active'] = 'פעיל';
$labels['none'] = 'אף אחד מאלה';
$labels['fromset'] = 'מקבוצה';
$labels['fromfile'] = 'מקובץ';
$labels['filterdisabled'] = 'מסנן מושבת';
$labels['countisgreaterthan'] = 'המספר גדול מ-';
$labels['countisgreaterthanequal'] = 'המספר גדול או שווה ל-';
$labels['countislessthan'] = 'המספר קטן מ-';
$labels['countislessthanequal'] = 'המספר קטן או שווה ל-';
$labels['countequals'] = 'המספר שווה ל-';
$labels['countnotequals'] = 'המספר אינו שווה ל ';
$labels['valueisgreaterthan'] = 'הערך גדול מ-';
$labels['valueisgreaterthanequal'] = 'הערך גדול או שווה ל-';
$labels['valueislessthan'] = 'הערך קטן מ-';
$labels['valueislessthanequal'] = 'הערך קטן או שווה ל-';
$labels['valueequals'] = 'הערך שווה ל-';
$labels['valuenotequals'] = 'הערך אינו שוה ל ';
$labels['setflags'] = 'סימון דגלים להודעה';
$labels['addflags'] = 'הוספת דגלים להודעה';
$labels['removeflags'] = 'הסרת דגלים מההודעה';
$labels['flagread'] = 'נקרא';
$labels['flagdeleted'] = 'נמחק';
$labels['flaganswered'] = 'נענה';
$labels['flagflagged'] = 'סומן בדגל';
$labels['flagdraft'] = 'טיוטה';
$labels['setvariable'] = 'הגדרת משתנה';
$labels['setvarname'] = 'שם המשתנה:';
$labels['setvarvalue'] = 'ערך המשתנה:';
$labels['setvarmodifiers'] = 'גורם משנה:';
$labels['varlower'] = 'אותיות קטנות';
$labels['varupper'] = 'אותיות גדולות';
$labels['varlowerfirst'] = 'התו הראשון אות קטנה';
$labels['varupperfirst'] = 'התו הראשון אות גדולה';
$labels['varquotewildcard'] = 'תו מיוחד יש לשים בין מרכאות';
$labels['varlength'] = 'אורך';
$labels['notify'] = 'משלוח התראה';
-$labels['notifyaddress'] = 'אל כתובת דו"אל:';
-$labels['notifybody'] = 'גוף ההתראה:';
-$labels['notifysubject'] = 'נושא ההתראה:';
-$labels['notifyfrom'] = 'שולח ההתראה:';
+$labels['notifytarget'] = 'יעד התראה:';
+$labels['notifymessage'] = 'הודעת התראה (רשות):';
+$labels['notifyoptions'] = 'אפשרויות התראה (רשות):';
+$labels['notifyfrom'] = 'שולח התראה (רשות):';
$labels['notifyimportance'] = 'חשיובת:';
$labels['notifyimportancelow'] = 'נמוכה';
$labels['notifyimportancenormal'] = 'רגילה';
$labels['notifyimportancehigh'] = 'גבוהה';
+$labels['notifymethodmailto'] = 'דוא״ל';
+$labels['notifymethodtel'] = 'טלפון';
+$labels['notifymethodsms'] = 'מסרון';
$labels['filtercreate'] = 'יצירת מסנן';
$labels['usedata'] = 'שימוש במידע שלהלן ליצירת המסנן:';
$labels['nextstep'] = 'הצעד הבא';
$labels['...'] = '...';
$labels['currdate'] = 'תאריך נוכחי';
$labels['datetest'] = 'תאריך';
$labels['dateheader'] = 'כותרת:';
$labels['year'] = 'שנה';
$labels['month'] = 'חודש';
$labels['day'] = 'יום';
$labels['date'] = 'תאריך (שנה-חודש-יום)';
$labels['julian'] = 'תאריך (יוליאני)';
$labels['hour'] = 'שעה';
$labels['minute'] = 'דקה';
$labels['second'] = 'שניה';
$labels['time'] = 'זמן (שעה:דקה:שניה)';
$labels['iso8601'] = 'תאריך (ISO8601)';
$labels['std11'] = 'תאריך (RFC2822)';
$labels['zone'] = 'איזור זמן';
$labels['weekday'] = 'יום בשבוע (0-6)';
$labels['advancedopts'] = 'אפשרויות מתקדמות';
$labels['body'] = 'גוף ההודעה';
$labels['address'] = 'כתובת';
$labels['envelope'] = 'מעטפה';
$labels['modifier'] = 'גורם שינוי:';
$labels['text'] = 'תמליל';
$labels['undecoded'] = 'לא מקודד ( גולמי )';
$labels['contenttype'] = 'סוג התוכן';
$labels['modtype'] = 'סוג:';
$labels['allparts'] = 'הכל';
$labels['domain'] = 'מתחם';
$labels['localpart'] = 'חלק מקומי';
$labels['user'] = 'משתמש';
$labels['detail'] = 'פרטים';
$labels['comparator'] = 'משווה:';
$labels['default'] = 'ברירת מחדל';
$labels['octet'] = 'strict (octet)';
$labels['asciicasemap'] = 'case insensitive (ascii-casemap)';
$labels['asciinumeric'] = 'numeric (ascii-numeric)';
$labels['index'] = 'אינדקס:';
$labels['indexlast'] = 'בחזרה';
+$labels['vacation'] = 'חופשה';
+$labels['vacation.reply'] = 'הודעת תשובה';
+$labels['vacation.advanced'] = 'הגדרות מתקדמות';
+$labels['vacation.subject'] = 'נושא';
+$labels['vacation.body'] = 'גוף ההודעה';
+$labels['vacation.start'] = 'תאריך התחלה';
+$labels['vacation.end'] = 'תאריך סיום';
+$labels['vacation.status'] = 'מצב';
+$labels['vacation.on'] = 'מופעל';
+$labels['vacation.off'] = 'כבוי';
+$labels['vacation.addresses'] = 'כתובות נוספות שלי';
+$labels['vacation.interval'] = 'מרווח בין תשובות';
+$labels['vacation.after'] = 'העתקת סרגל החופשה אחרי';
+$labels['vacation.saving'] = 'שמירת מידע...';
+$labels['vacation.action'] = 'פעולה על הודעה נכנסת';
+$labels['vacation.keep'] = 'להשאיר';
+$labels['vacation.discard'] = 'להפטר';
+$labels['vacation.redirect'] = 'הפניה אל';
+$labels['vacation.copy'] = 'שליחת העתק אל';
+$labels['arialabelfiltersetactions'] = 'פעולות על קבוצה של חוקי סינון';
+$labels['arialabelfilteractions'] = 'פעולות מסנן';
+$labels['arialabelfilterform'] = 'מאפייני מסנן';
+$labels['ariasummaryfilterslist'] = 'רשימה של מסננים';
+$labels['ariasummaryfiltersetslist'] = 'רשימת קבוצות של חוקי סינון';
+$labels['filterstitle'] = 'ערוך מסנני דואר נכנס';
+$labels['vacationtitle'] = 'ערוך כלל מחוץ-אל-משרדי';
$messages['filterunknownerror'] = 'שגיאת שרת בלתי מוכרת.';
$messages['filterconnerror'] = 'לא ניתן להתחבר לשרת.';
$messages['filterdeleteerror'] = 'לא ניתן למחוק סינון. שגיאת שרת.';
$messages['filterdeleted'] = 'המסנן נמחק בהצלחה.';
$messages['filtersaved'] = 'המסנן נשמר בהצלחה.';
$messages['filtersaveerror'] = 'לא ניתן לשמור סינון. שגיאת שרת.';
$messages['filterdeleteconfirm'] = 'האם אכן ברצונך למחוק את המסנן הנבחר?';
$messages['ruledeleteconfirm'] = 'האם אכן ברצונך למחוק את הכלל הנבחר?';
$messages['actiondeleteconfirm'] = 'האם אכן ברצונך למחוק את הפעולה הנבחרת?';
$messages['forbiddenchars'] = 'תווים אסורים בשדה.';
$messages['cannotbeempty'] = 'השדה לא יכול להישאר ריק.';
$messages['ruleexist'] = 'כבר קיים מסנן בשם כזה.';
$messages['setactivateerror'] = 'לא ניתן להפעיל את ערכת המסננים הנבחרת. אירעה שגיאה בצד השרת.';
$messages['setdeactivateerror'] = 'לא ניתן להשבית רשימת מסננים שנבחרה. שגיאת שרת.';
$messages['setdeleteerror'] = 'לא ניתן למחוק רשימת מסננים שנבחרה. שגיאת שרת.';
$messages['setactivated'] = 'ערכת המסננים הופעלה בהצלחה.';
$messages['setdeactivated'] = 'ערכת המסננים נוטרלה בהצלחה.';
$messages['setdeleted'] = 'ערכת המסננים נמחקה בהצלחה.';
$messages['setdeleteconfirm'] = 'האם אכן ברצונך למחוק את ערכת המסננים הנבחרת?';
$messages['setcreateerror'] = 'לא ניתן ליצור ערכת מסננים. אירעה שגיאה בצד השרת.';
$messages['setcreated'] = 'ערכת המסננים נוצרה בהצלחה.';
$messages['activateerror'] = 'לא ניתן להפעיל את המסננים הנבחרים. אירעה שגיאה בצד השרת.';
$messages['deactivateerror'] = 'לא ניתן לנטרל את המסננים הנבחרים. אירעה שגיאה בצד השרת.';
$messages['deactivated'] = 'המסננים הופעלו בהצלחה.';
$messages['activated'] = 'המסננים נוטרלו בהצלחה.';
$messages['moved'] = 'המסנן הועבר בהצלחה.';
$messages['moveerror'] = 'לא ניתן להעביר את המסנן הנבחר. אירעה שגיאה בצד השרת.';
$messages['nametoolong'] = 'השם ארוך מדי.';
$messages['namereserved'] = 'השם הזה שמור.';
$messages['setexist'] = 'הערכה כבר קיימת.';
$messages['nodata'] = 'חובה לבחור במיקום אחד לפחות!';
$messages['invaliddateformat'] = 'תאריך לא חוקי אן פורמט לא תקין';
+$messages['saveerror'] = 'לא ניתן לשמור המידע בשל שגיאה של השרת';
+$messages['vacationsaved'] = 'הודעת החופשה נשמרה בהצלחה';
+$messages['emptyvacationbody'] = 'גוף של הודעת חופשה נדרש!';
?>
diff --git a/lib/plugins/managesieve/localization/hr_HR.inc b/lib/plugins/managesieve/localization/hr_HR.inc
index b3192ab..efcd4da 100644
--- a/lib/plugins/managesieve/localization/hr_HR.inc
+++ b/lib/plugins/managesieve/localization/hr_HR.inc
@@ -1,192 +1,194 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filteri';
$labels['managefilters'] = 'Uredi filtere za pristiglu poštu';
$labels['filtername'] = 'Naziv filtera';
$labels['newfilter'] = 'Novi filter';
$labels['filteradd'] = 'Dodaj filter';
$labels['filterdel'] = 'Obriši filter';
$labels['moveup'] = 'Pomakni gore';
$labels['movedown'] = 'Pomakni dolje';
$labels['filterallof'] = 'koje odgovaraju svim sljedećim pravilima';
$labels['filteranyof'] = 'koje odgovaraju bilo kojem od sljedećih pravila';
$labels['filterany'] = 'sve poruke';
$labels['filtercontains'] = 'sadrži';
$labels['filternotcontains'] = 'ne sadrži';
$labels['filteris'] = 'jednako je';
$labels['filterisnot'] = 'nije jednako';
$labels['filterexists'] = 'postoji';
$labels['filternotexists'] = 'ne postoji';
$labels['filtermatches'] = 'odgovara izrazu';
$labels['filternotmatches'] = 'ne odgovara izrazu';
$labels['filterregex'] = 'odgovara regularnom izrazu';
$labels['filternotregex'] = 'ne odgovara regularnom izrazu';
$labels['filterunder'] = 'ispod';
$labels['filterover'] = 'iznad';
$labels['addrule'] = 'Dodaj pravilo';
$labels['delrule'] = 'Obriši pravilo';
$labels['messagemoveto'] = 'Premjesti poruku u';
$labels['messageredirect'] = 'Preusmjeri poruku na';
$labels['messagecopyto'] = 'Kopiraju poruku u';
$labels['messagesendcopy'] = 'Pošalji kopiju poruke na';
$labels['messagereply'] = 'Odgovori sa porukom';
$labels['messagedelete'] = 'Obriši poruku';
$labels['messagediscard'] = 'Otkaži sa porukom';
$labels['messagekeep'] = 'Zadrži poruku u mapi Inbox';
$labels['messagesrules'] = 'Za pristigle poruke:';
$labels['messagesactions'] = '...primijeni sljedeće akcije:';
$labels['add'] = 'Dodaj';
$labels['del'] = 'Obriši';
$labels['sender'] = 'Pošiljatelj';
$labels['recipient'] = 'Primatelj';
$labels['vacationaddr'] = 'Dodatna e-mail adresa(e):';
$labels['vacationdays'] = 'Koliko često slati poruku (u danima):';
$labels['vacationinterval'] = 'Koliko često slati poruku:';
-$labels['days'] = 'dana';
-$labels['seconds'] = 'sekundi';
$labels['vacationreason'] = 'Tijelo poruke (razlog odmora):';
$labels['vacationsubject'] = 'Naslov poruke:';
+$labels['days'] = 'dana';
+$labels['seconds'] = 'sekundi';
$labels['rulestop'] = 'Prekini izvođenje filtera';
$labels['enable'] = 'Omogući/Onemogući';
$labels['filterset'] = 'Grupa filtera';
$labels['filtersets'] = 'Filteri';
$labels['filtersetadd'] = 'Dodaj grupu filtera';
$labels['filtersetdel'] = 'Obriši odabranu grupu filtera';
$labels['filtersetact'] = 'Aktiviraj odabranu grupu filtera';
$labels['filtersetdeact'] = 'Deaktiviraj odabranu grupu filtera';
$labels['filterdef'] = 'Definicije filtera';
$labels['filtersetname'] = 'Naziv grupe filtera';
$labels['newfilterset'] = 'Nova grupa filtera';
$labels['active'] = 'aktivan';
$labels['none'] = 'nijedan';
$labels['fromset'] = 'iz grupe';
$labels['fromfile'] = 'iz datoteke';
$labels['filterdisabled'] = 'Deaktiviraj filter';
$labels['countisgreaterthan'] = 'brojač je veći od';
$labels['countisgreaterthanequal'] = 'brojač je veći ili jednak od';
$labels['countislessthan'] = 'brojač je manji od';
$labels['countislessthanequal'] = 'brojač je manji ili jednak od';
$labels['countequals'] = 'brojač je jednak';
$labels['countnotequals'] = 'brojač nije jednak';
$labels['valueisgreaterthan'] = 'vrijednost je veća od';
$labels['valueisgreaterthanequal'] = 'vrijednost je veća ili jednaka od';
$labels['valueislessthan'] = 'vrijednost je manja od';
$labels['valueislessthanequal'] = 'vrijednost je manja ili jednaka od';
$labels['valueequals'] = 'vrijednost je jednaka';
$labels['valuenotequals'] = 'vrijednost nije jednaka';
$labels['setflags'] = 'Postavi oznake na poruku';
$labels['addflags'] = 'Dodaj oznake na poruku';
$labels['removeflags'] = 'Ukloni oznake sa poruke';
$labels['flagread'] = 'Pročitana';
$labels['flagdeleted'] = 'Obrisana';
$labels['flaganswered'] = 'Odgovorena';
$labels['flagflagged'] = 'Označena';
$labels['flagdraft'] = 'Predložak';
$labels['setvariable'] = 'Postavi varijablu';
$labels['setvarname'] = 'Ime varijable:';
$labels['setvarvalue'] = 'Vrijednost varijable:';
$labels['setvarmodifiers'] = 'Modifikatori:';
$labels['varlower'] = 'mala slova';
$labels['varupper'] = 'velika slova';
$labels['varlowerfirst'] = 'prvo slovo malo';
$labels['varupperfirst'] = 'prvo slovo veliko';
$labels['varquotewildcard'] = 'Citiraj specijalne znakove';
$labels['varlength'] = 'duljina';
$labels['notify'] = 'Pošalji obavijest';
-$labels['notifyaddress'] = 'Na e-mail adresu:';
-$labels['notifybody'] = 'Tekst obavijesti:';
-$labels['notifysubject'] = 'Naslov obavijesti:';
-$labels['notifyfrom'] = 'Pošiljatelj obavijesti:';
$labels['notifyimportance'] = 'Važnost:';
$labels['notifyimportancelow'] = 'niska';
$labels['notifyimportancenormal'] = 'normalna';
$labels['notifyimportancehigh'] = 'visoka';
$labels['filtercreate'] = 'Stvori filter';
$labels['usedata'] = 'Koristi podatke za filter:';
$labels['nextstep'] = 'Idući korak';
$labels['...'] = '…';
$labels['currdate'] = 'Današnji datum';
$labels['datetest'] = 'Datum';
$labels['dateheader'] = 'zaglavlje:';
$labels['year'] = 'godina';
$labels['month'] = 'mjesec';
$labels['day'] = 'dan';
$labels['date'] = 'datum (yyyy-mm-dd)';
$labels['julian'] = 'datum (julijanski)';
$labels['hour'] = 'sat';
$labels['minute'] = 'minute';
$labels['second'] = 'sekunde';
$labels['time'] = 'vrijeme (hh:mm:ss)';
$labels['iso8601'] = 'datum (ISO8601)';
$labels['std11'] = 'datum (RFC2822)';
$labels['zone'] = 'vremenska zona';
$labels['weekday'] = 'dan u tjednu (0-6)';
$labels['advancedopts'] = 'Napredne postavke';
$labels['body'] = 'Tijelo poruke';
$labels['address'] = 'adresa';
$labels['envelope'] = 'omotnica';
$labels['modifier'] = 'modificirao:';
$labels['text'] = 'tekst';
$labels['undecoded'] = 'nedekodirano (raw)';
$labels['contenttype'] = 'tip sadržaja';
$labels['modtype'] = 'tip:';
$labels['allparts'] = 'sve';
$labels['domain'] = 'domena';
$labels['localpart'] = 'lokalni dio';
$labels['user'] = 'korisnik';
$labels['detail'] = 'detalj';
$labels['comparator'] = 'usporedio:';
$labels['default'] = 'preddefinirano';
$labels['octet'] = 'strogo (oktet)';
$labels['asciicasemap'] = 'neosjetljivo na veličinu slova (ascii-casemap)';
$labels['asciinumeric'] = 'numerički (ascii-numeric)';
$labels['index'] = 'indeks:';
$labels['indexlast'] = 'unatrag';
+$labels['vacation.advanced'] = 'Napredne postavke';
+$labels['vacation.subject'] = 'Naslov';
+$labels['vacation.body'] = 'Tijelo poruke';
+$labels['vacation.status'] = 'Status';
+$labels['vacation.saving'] = 'Spremanje podataka...';
$messages['filterunknownerror'] = 'Nepoznata greška na poslužitelju';
$messages['filterconnerror'] = 'Nemoguće spajanje na poslužitelj (managesieve)';
$messages['filterdeleteerror'] = 'Nemoguće brisanje filtera. Greška na poslužitelju.';
$messages['filterdeleted'] = 'Filter je uspješno obrisan';
$messages['filtersaved'] = 'Filter je uspješno spremljen';
$messages['filtersaveerror'] = 'Nemoguće spremiti filter. Greška na poslužitelju.';
$messages['filterdeleteconfirm'] = 'Sigurno želite obrisati odabrani filter?';
$messages['ruledeleteconfirm'] = 'Jeste li sigurni da želite obrisati odabrana pravila?';
$messages['actiondeleteconfirm'] = 'Jeste li sigurni da želite obrisati odabrane akcije?';
$messages['forbiddenchars'] = 'Nedozvoljeni znakovi u polju';
$messages['cannotbeempty'] = 'Polje nesmije biti prazno';
$messages['ruleexist'] = 'Filter sa zadanim imenom već postoji.';
$messages['setactivateerror'] = 'Nemoguće aktivirati odabranu grupu filtera. Greška na poslužitelju.';
$messages['setdeactivateerror'] = 'Nemoguće deaktivirati odabranu grupu filtera. Greška na poslužitelju.';
$messages['setdeleteerror'] = 'Nemoguće obrisati odabranu grupu filtera. Greška na poslužitelju.';
$messages['setactivated'] = 'Grupa filtera je uspješno aktivirana';
$messages['setdeactivated'] = 'Grupa filtera je uspješno deaktivirana';
$messages['setdeleted'] = 'Grupa filtera je uspješno obrisana';
$messages['setdeleteconfirm'] = 'Jeste li sigurni da želite obrisati odabranu grupu filtera?';
$messages['setcreateerror'] = 'Nemoguće stvoriti grupu filtera. Greška na poslužitelju.';
$messages['setcreated'] = 'Grupa filtera je uspješno stvorena';
$messages['activateerror'] = 'Nije moguće omogućiti odabrani filter(e). Greška poslužitelja.';
$messages['deactivateerror'] = 'Nije moguće onemogućiti odabrane filter(e). Greška poslužitelja.';
$messages['deactivated'] = 'Filter(i) omogućen(i) uspješno.';
$messages['activated'] = 'Filter(i) onemogućen(i) uspješno.';
$messages['moved'] = 'Filter uspješno premješten.';
$messages['moveerror'] = 'Nije moguće premjestiti odabrani filter. Greška poslužitelja.';
$messages['nametoolong'] = 'Nemoguće napraviti grupu filtera. Naziv je predugačak';
$messages['namereserved'] = 'Rezervirano ime.';
$messages['setexist'] = 'Skup već postoji.';
$messages['nodata'] = 'Barem jedan pozicija mora biti odabrana!';
$messages['invaliddateformat'] = 'Neispravan datum ili dio datuma';
+$messages['saveerror'] = 'Nemoguće spremiti podatke. Greška na poslužitelju.';
?>
diff --git a/lib/plugins/managesieve/localization/hu_HU.inc b/lib/plugins/managesieve/localization/hu_HU.inc
index afab3f3..eae7650 100644
--- a/lib/plugins/managesieve/localization/hu_HU.inc
+++ b/lib/plugins/managesieve/localization/hu_HU.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Üzenetszűrők';
$labels['managefilters'] = 'Bejövő üzenetek szűrői';
$labels['filtername'] = 'Szűrő neve';
$labels['newfilter'] = 'Új szűrő';
$labels['filteradd'] = 'Szűrő hozzáadása';
$labels['filterdel'] = 'Szűrő törlése';
$labels['moveup'] = 'Mozgatás felfelé';
$labels['movedown'] = 'Mozgatás lefelé';
$labels['filterallof'] = 'A következők szabályok mind illeszkedjenek';
$labels['filteranyof'] = 'A következő szabályok bármelyike illeszkedjen';
$labels['filterany'] = 'Minden üzenet illeszkedjen';
$labels['filtercontains'] = 'tartalmazza';
$labels['filternotcontains'] = 'nem tartalmazza';
$labels['filteris'] = 'megegyezik';
$labels['filterisnot'] = 'nem egyezik meg';
$labels['filterexists'] = 'létezik';
$labels['filternotexists'] = 'nem létezik';
$labels['filtermatches'] = 'kifejezéssel egyezők';
$labels['filternotmatches'] = 'kifejezéssel nem egyezők';
$labels['filterregex'] = 'reguláris kifejezéssel egyezők';
$labels['filternotregex'] = 'reguláris kifejezéssel nem egyezők';
$labels['filterunder'] = 'alatta';
$labels['filterover'] = 'felette';
$labels['addrule'] = 'Szabály hozzáadása';
$labels['delrule'] = 'Szabály törlése';
$labels['messagemoveto'] = 'Üzenet áthelyezése ide:';
$labels['messageredirect'] = 'Üzenet továbbítása ide:';
$labels['messagecopyto'] = 'Üzenet másolása';
$labels['messagesendcopy'] = 'Másolat kűldése az üzenetből';
$labels['messagereply'] = 'Válaszüzenet küldése (autoreply)';
$labels['messagedelete'] = 'Üzenet törlése';
$labels['messagediscard'] = 'Válaszüzenet küldése, a levél törlése';
$labels['messagekeep'] = 'Tartsa az üzenetet a beérkező leveleknél';
$labels['messagesrules'] = 'Az adott tulajdonságú beérkezett üzenetekre:';
$labels['messagesactions'] = '... a következő műveletek végrehajtása:';
$labels['add'] = 'Hozzáadás';
$labels['del'] = 'Törlés';
$labels['sender'] = 'Feladó';
$labels['recipient'] = 'Címzett';
$labels['vacationaddr'] = 'További e-mail címeim:';
$labels['vacationdays'] = 'Válaszüzenet küldése ennyi naponként:';
$labels['vacationinterval'] = 'Milyen gyakran küld üzeneteket:';
-$labels['days'] = 'napok';
-$labels['seconds'] = 'másodpercek';
$labels['vacationreason'] = 'Levél szövege (automatikus válasz):';
$labels['vacationsubject'] = 'Üzenet tárgya:';
+$labels['days'] = 'napok';
+$labels['seconds'] = 'másodpercek';
$labels['rulestop'] = 'Műveletek végrehajtásának befejezése';
$labels['enable'] = 'Bekapcsol/Kikapcsol';
$labels['filterset'] = 'Szűrök készlet';
$labels['filtersets'] = 'Szűrő készletek';
$labels['filtersetadd'] = 'Szűrő hozzáadása a készlethez';
$labels['filtersetdel'] = 'Az aktuális szűrő készlet törlése';
$labels['filtersetact'] = 'Az aktuális szűrő készlet engedélyezése';
$labels['filtersetdeact'] = 'Az aktuális szűrő készlet tiltása';
$labels['filterdef'] = 'Szűrő definíció';
$labels['filtersetname'] = 'Szűrő készlet neve';
$labels['newfilterset'] = 'Új szűrő készlet';
$labels['active'] = 'aktív';
$labels['none'] = 'nincs';
$labels['fromset'] = 'készletből';
$labels['fromfile'] = 'fájlból';
$labels['filterdisabled'] = 'Szűrő kikapcsolása';
$labels['countisgreaterthan'] = 'a számláló nagyobb mint';
$labels['countisgreaterthanequal'] = 'a számláló nagyobb vagy egyenlő';
$labels['countislessthan'] = 'a számláló kissebb mint';
$labels['countislessthanequal'] = 'a számláló kissebb vagy egyenlő';
$labels['countequals'] = 'a számláló egyenlő';
$labels['countnotequals'] = 'össze számolva nem egyenlő';
$labels['valueisgreaterthan'] = 'az érték nagyobb mint';
$labels['valueisgreaterthanequal'] = 'az érték nagyobb vagy egyenlő';
$labels['valueislessthan'] = 'az érték kisebb mint';
$labels['valueislessthanequal'] = 'az érték kisebb vagy egyenlő';
$labels['valueequals'] = 'az érték megegyzik';
$labels['valuenotequals'] = 'az értéke nem azonos';
$labels['setflags'] = 'Jelzők beállítása az üzeneten';
$labels['addflags'] = 'Jelző hozzáadása az üzenethez';
$labels['removeflags'] = 'Jelzők eltávolítása az üzenetből';
$labels['flagread'] = 'Olvasás';
$labels['flagdeleted'] = 'Törölt';
$labels['flaganswered'] = 'Megválaszolt';
$labels['flagflagged'] = 'Megjelölt';
$labels['flagdraft'] = 'Vázlat';
$labels['setvariable'] = 'Változó beállítása';
$labels['setvarname'] = 'Változó neve:';
$labels['setvarvalue'] = 'Változó értéke:';
$labels['setvarmodifiers'] = 'Módosítók';
$labels['varlower'] = 'kisbetű';
$labels['varupper'] = 'nagybetű';
$labels['varlowerfirst'] = 'első karakter kisbetű';
$labels['varupperfirst'] = 'első karakter nagybetű';
$labels['varquotewildcard'] = 'speciális karakterek idézése';
$labels['varlength'] = 'hossz';
$labels['notify'] = 'Értesítés küldése';
-$labels['notifyaddress'] = 'Címzett e-mail címe:';
-$labels['notifybody'] = 'Értesítés levéltörzse:';
-$labels['notifysubject'] = 'Értesítés tárgya:';
-$labels['notifyfrom'] = 'Értesítés feladója:';
+$labels['notifytarget'] = 'Értesítés célja:';
+$labels['notifymessage'] = 'Értesítési üzenet (opcionális):';
+$labels['notifyoptions'] = 'Értesítés opcióik (opcionális):';
+$labels['notifyfrom'] = 'Értesítés feladója (opcionális):';
$labels['notifyimportance'] = 'Fontosság:';
$labels['notifyimportancelow'] = 'alacsony';
$labels['notifyimportancenormal'] = 'normál';
$labels['notifyimportancehigh'] = 'magas';
+$labels['notifymethodmailto'] = 'Email';
+$labels['notifymethodtel'] = 'Telefonszám';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Szűrő létrehozása';
$labels['usedata'] = 'A következő adatok használata a szűrőben';
$labels['nextstep'] = 'Következő lépés';
$labels['...'] = '…';
$labels['currdate'] = 'Mai dátum';
$labels['datetest'] = 'Dátum';
$labels['dateheader'] = 'fejléc:';
$labels['year'] = 'év';
$labels['month'] = 'hónap';
$labels['day'] = 'nap';
$labels['date'] = 'dátum (éééé-hh-nn)';
$labels['julian'] = 'dátum ( julián)';
$labels['hour'] = 'óra';
$labels['minute'] = 'perc';
$labels['second'] = 'másodperc';
$labels['time'] = 'idő (óó:pp:ms)';
$labels['iso8601'] = 'dátum (ISO8601)';
$labels['std11'] = 'dátum (RFC2822)';
$labels['zone'] = 'idő-zóna';
$labels['weekday'] = 'hét napjai (0-6)';
$labels['advancedopts'] = 'Haladó beállítások';
$labels['body'] = 'Levéltörzs';
$labels['address'] = 'cím';
$labels['envelope'] = 'boriték';
$labels['modifier'] = 'módosító:';
$labels['text'] = 'szöveg';
$labels['undecoded'] = 'kódolatlan(nyers)';
$labels['contenttype'] = 'tartalom tipusa';
$labels['modtype'] = 'típus:';
$labels['allparts'] = 'összes';
$labels['domain'] = 'domain';
$labels['localpart'] = 'név rész';
$labels['user'] = 'felhasználó';
$labels['detail'] = 'részlet';
$labels['comparator'] = 'összehasonlító';
$labels['default'] = 'alapértelmezett';
$labels['octet'] = 'strict (octet)';
$labels['asciicasemap'] = 'kis-nagybetüre nem érzékeny (ascii-casemap)';
$labels['asciinumeric'] = 'számszerü (ascii-numeric)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'visszafelé';
+$labels['vacation'] = 'Vakáció';
+$labels['vacation.reply'] = 'Válasz az üzenetre';
+$labels['vacation.advanced'] = 'Haladó beállítások';
+$labels['vacation.subject'] = 'Tárgy';
+$labels['vacation.body'] = 'Törzs';
+$labels['vacation.start'] = 'Szünidő kezdete';
+$labels['vacation.end'] = 'Szünidő vége';
+$labels['vacation.status'] = 'Állapot';
+$labels['vacation.on'] = 'Be';
+$labels['vacation.off'] = 'Ki';
+$labels['vacation.addresses'] = 'További címeim';
+$labels['vacation.interval'] = 'Válasz intervallum';
+$labels['vacation.after'] = 'Rakd a szabadság szabályt ez után ';
+$labels['vacation.saving'] = 'Adatok mentése...';
+$labels['vacation.action'] = 'Beérkező üzenet akció';
+$labels['vacation.keep'] = 'Megtartás';
+$labels['vacation.discard'] = 'Érvénytelenít';
+$labels['vacation.redirect'] = 'Átírányítás ide';
+$labels['vacation.copy'] = 'Másolat kűldése ide';
+$labels['arialabelfiltersetactions'] = 'Szűrő készlet müveletek';
+$labels['arialabelfilteractions'] = 'Szűrő müveletek';
+$labels['arialabelfilterform'] = 'Szűrő tulajdonságai';
+$labels['ariasummaryfilterslist'] = 'Szűrők listája';
+$labels['ariasummaryfiltersetslist'] = 'Szűrő készletek listája';
+$labels['filterstitle'] = 'Bejövő üzenetek szűrőinek szerkesztése';
+$labels['vacationtitle'] = 'Irodán kiívül szabász szerkesztése';
$messages['filterunknownerror'] = 'Ismeretlen szerverhiba';
$messages['filterconnerror'] = 'Nem tudok a szűrőszerverhez kapcsolódni';
$messages['filterdeleteerror'] = 'A szűrőt nem lehet törölni. Szerverhiba történt';
$messages['filterdeleted'] = 'A szűrő törlése sikeres';
$messages['filtersaved'] = 'A szűrő mentése sikeres';
$messages['filtersaveerror'] = 'A szűrő mentése sikertelen. Szerverhiba történt';
$messages['filterdeleteconfirm'] = 'Biztosan törli ezt a szűrőt?';
$messages['ruledeleteconfirm'] = 'Biztosan törli ezt a szabályt?';
$messages['actiondeleteconfirm'] = 'Biztosan törli ezt a műveletet?';
$messages['forbiddenchars'] = 'Érvénytelen karakter a mezőben';
$messages['cannotbeempty'] = 'A mező nem lehet üres';
$messages['ruleexist'] = 'Már van ilyen névvel elmentett szűrő.';
$messages['setactivateerror'] = 'A kiválasztott szűrő készletet nem sikerült engedélyezni. Szerver hiba történt.';
$messages['setdeactivateerror'] = 'A kiválasztott szűrő készletet nem sikerült tiltani. Szerver hiba történt.';
$messages['setdeleteerror'] = 'Nem sikerült a kiválasztott szűrő készletet törölni. Szerver hiba történt.';
$messages['setactivated'] = 'A filter készlet engedélyezése sikeresen végrehajtódott.';
$messages['setdeactivated'] = 'A filter készlet tiltása sikeresen végrehajtódott.';
$messages['setdeleted'] = 'A filter készlet törlése sikeresen végrehajtódott.';
$messages['setdeleteconfirm'] = 'Biztosan törölni szeretnéd a kiválasztott szűrő készleteket?';
$messages['setcreateerror'] = 'Nem sikerült létrehozni a szűrő készletet. Szerver hiba történt.';
$messages['setcreated'] = 'A szűrő készlet sikeresen létrejött.';
$messages['activateerror'] = 'Nem sikerült engedélyezni a kiválasztott szűrö(k)et. Szerver hiba történt.';
$messages['deactivateerror'] = 'Nem sikerült kikapcsolni a kiválasztott szűrő(ke)t. Szerver hiba történt.';
$messages['deactivated'] = 'Szűrő(k) sikeresen bekapcsolva.';
$messages['activated'] = 'Szűrő(k) sikeresen kikapcsolva.';
$messages['moved'] = 'A szűrő sikeresen áthelyezve.';
$messages['moveerror'] = 'Az áthelyezés nem sikerült. Szerver hiba történt.';
$messages['nametoolong'] = 'Túll hosszu név';
$messages['namereserved'] = 'Nem használható (foglalt) név-';
$messages['setexist'] = 'A készlet már létezik.';
$messages['nodata'] = 'Legalább egyet ki kell választani.';
$messages['invaliddateformat'] = 'hibás dátum formátum';
+$messages['saveerror'] = 'Az adat mentése sikertelen. Szerverhiba történt';
+$messages['vacationsaved'] = 'Vakáció adatai sikeresen elmentve.';
+$messages['emptyvacationbody'] = 'A vakácíó üzenet szövegtörzse kötelező!';
?>
diff --git a/lib/plugins/managesieve/localization/id_ID.inc b/lib/plugins/managesieve/localization/id_ID.inc
index 59dadc7..b445ef6 100644
--- a/lib/plugins/managesieve/localization/id_ID.inc
+++ b/lib/plugins/managesieve/localization/id_ID.inc
@@ -1,224 +1,221 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filter';
$labels['managefilters'] = 'Atur filter email masuk';
$labels['filtername'] = 'Nama filter';
$labels['newfilter'] = 'Filter baru';
$labels['filteradd'] = 'Tambah filter';
$labels['filterdel'] = 'Hapus filter';
$labels['moveup'] = 'Pindah ke atas';
$labels['movedown'] = 'Pindah ke bawah';
$labels['filterallof'] = 'cocok dengan semua aturan berikut ini';
$labels['filteranyof'] = 'cocok dengan aturan manapun';
$labels['filterany'] = 'semua pesan';
$labels['filtercontains'] = 'berisi';
$labels['filternotcontains'] = 'tidak berisi';
$labels['filteris'] = 'sama dengan';
$labels['filterisnot'] = 'tidak sama dengan';
$labels['filterexists'] = 'ada';
$labels['filternotexists'] = 'tidak ada';
$labels['filtermatches'] = 'ekspresi yg cocok';
$labels['filternotmatches'] = 'ekspresi yg tidak cocok';
$labels['filterregex'] = 'cocok dengan ekspresi reguler';
$labels['filternotregex'] = 'tidak cocok dengan ekspresi reguler';
$labels['filterunder'] = 'di bawah';
$labels['filterover'] = 'di atas';
$labels['addrule'] = 'Tambah aturan';
$labels['delrule'] = 'Hapus aturan';
$labels['messagemoveto'] = 'Pindah pesan ke';
$labels['messageredirect'] = 'Alihkan pesan ke';
$labels['messagecopyto'] = 'Salin pesan ke';
$labels['messagesendcopy'] = 'Kirim salinan pesan ke';
$labels['messagereply'] = 'balas dengan pesan';
$labels['messagedelete'] = 'Hapus pesan';
$labels['messagediscard'] = 'Buang dengan pesan';
$labels['messagekeep'] = 'Biarkan pesan tetap didalam kotak surat';
$labels['messagesrules'] = 'Untuk email masuk:';
$labels['messagesactions'] = '...lakukan tindakan berikut';
$labels['add'] = 'Tambah';
$labels['del'] = 'Hapus';
$labels['sender'] = 'Pengirim';
$labels['recipient'] = 'Penerima';
$labels['vacationaddr'] = 'Alamat email tambahan saya:';
$labels['vacationdays'] = 'Seberapa sering mengirim pesan (dalam hari):';
$labels['vacationinterval'] = 'Seberapa sering untuk pengiriman pesan:';
$labels['vacationreason'] = 'Isi pesan (alasan liburan):';
$labels['vacationsubject'] = 'Judul pesan:';
$labels['days'] = 'hari';
$labels['seconds'] = 'detik';
$labels['rulestop'] = 'Berhenti mengevaluasi aturan';
$labels['enable'] = 'Aktifkan/Non-Aktifkan';
$labels['filterset'] = 'Himpunan filter';
$labels['filtersets'] = 'Himpunan banyak filter';
$labels['filtersetadd'] = 'Tambahkan himpunan filter';
$labels['filtersetdel'] = 'Hapus himpunan filter yang sekarang';
$labels['filtersetact'] = 'Aktifkan himpunan filter ayng sekarang';
$labels['filtersetdeact'] = 'Matikan himpunan filter ayng sekarang';
$labels['filterdef'] = 'Definisi filter';
$labels['filtersetname'] = 'Nama himpunan filter';
$labels['newfilterset'] = 'Himpunan filter baru';
$labels['active'] = 'aktif';
$labels['none'] = 'nihil';
$labels['fromset'] = 'dari himpunan';
$labels['fromfile'] = 'dari berkas';
$labels['filterdisabled'] = 'Filter dimatikan';
$labels['countisgreaterthan'] = 'penghitungan lebih besar dari';
$labels['countisgreaterthanequal'] = 'penghitungan lebih besa dari atau sama dengan';
$labels['countislessthan'] = 'penghitungan lebih kecil dari';
$labels['countislessthanequal'] = 'penghitungan lebih kecil dari atau sama dengan';
$labels['countequals'] = 'penghitungan sama dengan';
$labels['countnotequals'] = 'penghitungan tidak sama dengan';
$labels['valueisgreaterthan'] = 'nilai lebih besar dari';
$labels['valueisgreaterthanequal'] = 'nilai lebih besar dari atau sama dengan';
$labels['valueislessthan'] = 'nilai lebih kecil dari';
$labels['valueislessthanequal'] = 'nilai lebih kecil dari atau sama dengan';
$labels['valueequals'] = 'nilai sama dengan';
$labels['valuenotequals'] = 'nilai tidak sadengan';
$labels['setflags'] = 'Atur tanda pada pesan';
$labels['addflags'] = 'Berikan tanda pada pesan';
$labels['removeflags'] = 'Cabut tanda dari pesan';
$labels['flagread'] = 'Baca';
$labels['flagdeleted'] = 'Terhapus';
$labels['flaganswered'] = 'Terjawab';
$labels['flagflagged'] = 'Ditandai';
$labels['flagdraft'] = 'Konsep';
$labels['setvariable'] = 'Set variabel';
$labels['setvarname'] = 'Nama variabel:';
$labels['setvarvalue'] = 'Nilai variabel';
$labels['setvarmodifiers'] = 'Pengubah';
$labels['varlower'] = 'huruf kecil';
$labels['varupper'] = 'huruf besar';
$labels['varlowerfirst'] = 'karakter pertama huruf kecil';
$labels['varupperfirst'] = 'karakter pertama huruf besar';
$labels['varquotewildcard'] = 'kutip karakter khusus';
$labels['varlength'] = 'panjang';
$labels['notify'] = 'Kirim pemberitahuan';
$labels['notifytarget'] = 'Pemberitahuan yang dituju:';
$labels['notifymessage'] = 'Pemberitahuan pesan (pilihan):';
$labels['notifyoptions'] = 'Pemberitahuan untuk beberapa pilihan (pilihan):';
$labels['notifyfrom'] = 'Pemberitahuan ke pengirim (tidak harus):';
$labels['notifyimportance'] = 'Tingkat kepentingan:';
$labels['notifyimportancelow'] = 'rendah';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'tinggi';
$labels['notifymethodmailto'] = 'Surat Elektronik / Email';
$labels['notifymethodtel'] = 'Telepon';
$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Buat filter';
$labels['usedata'] = 'Gunakan data berikut dalam filter:';
$labels['nextstep'] = 'Langkah Selanjutnya';
$labels['...'] = '...';
$labels['currdate'] = 'Tanggal sekarang';
$labels['datetest'] = 'Tanggal';
$labels['dateheader'] = 'header / tajuk:';
$labels['year'] = 'tahun';
$labels['month'] = 'bulan';
$labels['day'] = 'hari';
$labels['date'] = 'tanggal (yyyy-mm-dd)';
$labels['julian'] = 'tanggal (kalender julian)';
$labels['hour'] = 'jam';
$labels['minute'] = 'menit';
$labels['second'] = 'detik';
$labels['time'] = 'waktu :(hh:mm:ss)';
$labels['iso8601'] = 'tanggal (ISO8601)';
$labels['std11'] = 'tanggal (RFC2822)';
$labels['zone'] = 'zona-waktu';
$labels['weekday'] = 'hari kerja (0-6)';
$labels['advancedopts'] = 'Pilihan lanjutan';
$labels['body'] = 'Isi';
$labels['address'] = 'alamat';
$labels['envelope'] = 'amplop';
$labels['modifier'] = 'peubah:';
$labels['text'] = 'teks';
$labels['undecoded'] = 'praterjemahan (mentah)';
$labels['contenttype'] = 'tipe isi';
$labels['modtype'] = 'tipe:';
$labels['allparts'] = 'semua';
$labels['domain'] = 'domain';
$labels['localpart'] = 'bagian lokal';
$labels['user'] = 'pengguna';
$labels['detail'] = 'rinci';
$labels['comparator'] = 'pembanding:';
$labels['default'] = 'standar';
$labels['octet'] = 'ketat (oktet)';
$labels['asciicasemap'] = 'case insensitive (ascii-casemap)';
$labels['asciinumeric'] = 'numeric (ascii-numeric)';
$labels['index'] = 'indeks:';
$labels['indexlast'] = 'mundur:';
$labels['vacation'] = 'Liburan';
$labels['vacation.reply'] = 'Balas pesan';
$labels['vacation.advanced'] = 'Pengaturan Lanjutan';
$labels['vacation.subject'] = 'Judul';
$labels['vacation.body'] = 'Isi';
-$labels['vacation.dates'] = 'Waktu Liburan';
-$labels['vacation.from'] = 'Pengirim:';
-$labels['vacation.to'] = 'Kepada:';
$labels['vacation.status'] = 'Status';
$labels['vacation.on'] = 'Nyala';
$labels['vacation.off'] = 'Mati';
$labels['vacation.addresses'] = 'Alamat email tambahan saya';
$labels['vacation.interval'] = 'Balas secara interval';
$labels['vacation.after'] = 'Atur untuk pengaturan cuti setelah';
$labels['vacation.saving'] = 'Menyimpan data...';
$labels['vacation.action'] = 'Tindakan untuk pesan masuk';
$labels['vacation.keep'] = 'Simpan';
$labels['vacation.discard'] = 'Buang';
$labels['vacation.redirect'] = 'Alihkan ke';
$labels['vacation.copy'] = 'Kirim salinan ke';
$labels['arialabelfiltersetactions'] = 'Tindakan untuk penyaringan';
$labels['arialabelfilteractions'] = 'Tindakan penyaringan';
$labels['arialabelfilterform'] = 'Properti untuk penyaringan';
$labels['ariasummaryfilterslist'] = 'Daftar penyaringan';
$labels['ariasummaryfiltersetslist'] = 'Daftar penyaringan yang telah di set';
$labels['filterstitle'] = 'Ubah penyaringan untuk email masuk';
$labels['vacationtitle'] = 'Ubah aturan untuk sedang-diluar-kantor';
$messages['filterunknownerror'] = 'Error pada server tak dikenali.';
$messages['filterconnerror'] = 'Tidak dapat menyambung ke server.';
$messages['filterdeleteerror'] = 'Tidak dapat menghapus penyaringan. Terjadi kesalahan pada server.';
$messages['filterdeleted'] = 'Penyaringan berhasil dihapus.';
$messages['filtersaved'] = 'Penyaringan berhasil disimpan.';
$messages['filtersaveerror'] = 'Tidak dapat menyimpan penyaringan. Terjadi kesalahan pada server.';
$messages['filterdeleteconfirm'] = 'Yakin untuk menghapus penyaringan terpilih?';
$messages['ruledeleteconfirm'] = 'Yakin untuk menghapus aturan terpilih?';
$messages['actiondeleteconfirm'] = 'Yakin untuk menghapus tindakan terpilih?';
$messages['forbiddenchars'] = 'Karakter terlarang pada isian.';
$messages['cannotbeempty'] = 'Isian tidak bisa kosong.';
$messages['ruleexist'] = 'Penyaringan dengan nama tersebut sudah ada.';
$messages['setactivateerror'] = 'Tidak dapat mengaktivkan kumpulan penyaringan terpilih. Terjadi kesalahan pada server.';
$messages['setdeactivateerror'] = 'Tidak bisa mematikan kumpulan penyaringan terpilih. Terjadi kesalahan pada server.';
$messages['setdeleteerror'] = 'Tidak dapat menghapus kumpulan penyaringan terpilih. Terjadi kesalahan pada server.';
$messages['setactivated'] = 'Kumpulan penyaringan berhasil dihidupkan.';
$messages['setdeactivated'] = 'Kumpulan penyaringan berhasil dimatikan.';
$messages['setdeleted'] = 'Kumpulan penyaringan berhasil dihapus.';
$messages['setdeleteconfirm'] = 'Yakin ingin menghapus kumpulan penyaringan terpilih?';
$messages['setcreateerror'] = 'Tidak bisa membuat kumpulan penyaringan. Terjadi kesalahan pada server';
$messages['setcreated'] = 'Kumpulan penyaringan berhasul dibuat.';
$messages['activateerror'] = 'Tidak dapat mengaktifkan penyaringan terpilih. Terjadi kesalahan pada server';
$messages['deactivateerror'] = 'Tidak dapat mematikan penyaringan terpilih. Terjadi kesalahan pada server';
$messages['deactivated'] = 'Berhasil menghidupkan penyaringan.';
$messages['activated'] = 'Berhasil mematikan penyaringan.';
$messages['moved'] = 'Berhasil memindahkan penyaringan.';
$messages['moveerror'] = 'Tidak bisa memindahkan penyaringan terpilih. Ada kesalahan di server.';
$messages['nametoolong'] = 'Nama terlalu panjang.';
$messages['namereserved'] = 'Nama sudah terpesan.';
$messages['setexist'] = 'Kumpulan sudah ada.';
$messages['nodata'] = 'Setidaknya satu posisi harus dipilih!';
$messages['invaliddateformat'] = 'Format tanggal atau bagian dari tanggal salah';
$messages['saveerror'] = 'Tidak dapat menyimpan data. Terjadi kesalahan pada server.';
$messages['vacationsaved'] = 'Data untuk cuti berhasil disimpan.';
?>
diff --git a/lib/plugins/managesieve/localization/it_IT.inc b/lib/plugins/managesieve/localization/it_IT.inc
index b7fc970..b97fde8 100644
--- a/lib/plugins/managesieve/localization/it_IT.inc
+++ b/lib/plugins/managesieve/localization/it_IT.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtri';
$labels['managefilters'] = 'Gestione dei filtri per la posta in arrivo';
$labels['filtername'] = 'Nome del filtro';
$labels['newfilter'] = 'Nuovo filtro';
$labels['filteradd'] = 'Aggiungi filtro';
$labels['filterdel'] = 'Elimina filtro';
$labels['moveup'] = 'Sposta sopra';
$labels['movedown'] = 'Sposta sotto';
$labels['filterallof'] = 'che soddisfa tutte le regole seguenti';
$labels['filteranyof'] = 'che soddisfa una qualsiasi delle regole seguenti';
$labels['filterany'] = 'tutti i messaggi';
$labels['filtercontains'] = 'contiene';
$labels['filternotcontains'] = 'non contiene';
$labels['filteris'] = 'è uguale a';
$labels['filterisnot'] = 'è diverso da';
$labels['filterexists'] = 'esiste';
$labels['filternotexists'] = 'non esiste';
$labels['filtermatches'] = 'matcha l\'espressione';
$labels['filternotmatches'] = 'non matcha l\'espressione';
$labels['filterregex'] = 'matcha l\'espressione regolare';
$labels['filternotregex'] = 'non matcha l\'espressione regolare';
$labels['filterunder'] = 'sotto';
$labels['filterover'] = 'sopra';
$labels['addrule'] = 'Aggiungi regola';
$labels['delrule'] = 'Elimina regola';
$labels['messagemoveto'] = 'Sposta il messaggio in';
$labels['messageredirect'] = 'Inoltra il messaggio a';
$labels['messagecopyto'] = 'copia a';
$labels['messagesendcopy'] = 'Invia copia a';
$labels['messagereply'] = 'Rispondi con il messaggio';
$labels['messagedelete'] = 'Elimina il messaggio';
$labels['messagediscard'] = 'Rifiuta con messaggio';
$labels['messagekeep'] = 'Mantieni il messaggio in Posta ricevuta';
$labels['messagesrules'] = 'Per la posta in arrivo';
$labels['messagesactions'] = '...esegui le seguenti azioni:';
$labels['add'] = 'Aggiungi';
$labels['del'] = 'Elimina';
$labels['sender'] = 'Mittente';
$labels['recipient'] = 'Destinatario';
$labels['vacationaddr'] = 'Account email aggiuntivo(i):';
$labels['vacationdays'] = 'Ogni quanti giorni ribadire il messaggio allo stesso mittente';
$labels['vacationinterval'] = 'Ogni quanto tempo inviare i messaggi:';
-$labels['days'] = 'giorni';
-$labels['seconds'] = 'secondi';
$labels['vacationreason'] = 'Corpo del messaggio (dettagli relativi all\'assenza):';
$labels['vacationsubject'] = 'Oggetto del messaggio';
+$labels['days'] = 'giorni';
+$labels['seconds'] = 'secondi';
$labels['rulestop'] = 'Non valutare le regole successive';
$labels['enable'] = 'Abilita/disabilita';
$labels['filterset'] = 'Gruppi di filtri';
$labels['filtersets'] = 'gruppo di filtri';
$labels['filtersetadd'] = 'Aggiungi gruppo';
$labels['filtersetdel'] = 'Cancella gruppo selezionato';
$labels['filtersetact'] = 'Attiva gruppo selezionato';
$labels['filtersetdeact'] = 'Disattiva gruppo selezionato';
$labels['filterdef'] = 'Definizione del filtro';
$labels['filtersetname'] = 'Nome del Gruppo di filtri';
$labels['newfilterset'] = 'Nuovo gruppo di filri';
$labels['active'] = 'attivo';
$labels['none'] = 'nessuno';
$labels['fromset'] = 'dal set';
$labels['fromfile'] = 'dal file';
$labels['filterdisabled'] = 'Filtro disabilitato';
$labels['countisgreaterthan'] = 'somma maggiore di';
$labels['countisgreaterthanequal'] = 'somma maggiore uguale a';
$labels['countislessthan'] = 'somma minore di';
$labels['countislessthanequal'] = 'somma minore o uguale a';
$labels['countequals'] = 'somma uguale a';
$labels['countnotequals'] = 'il conteggio non è uguale a';
$labels['valueisgreaterthan'] = 'valore maggiore di';
$labels['valueisgreaterthanequal'] = 'valore maggiore uguale a';
$labels['valueislessthan'] = 'valore minore di';
$labels['valueislessthanequal'] = 'valore minore uguale di';
$labels['valueequals'] = 'valore uguale a';
$labels['valuenotequals'] = 'il valore non è uguale a';
$labels['setflags'] = 'Contrassegna il messaggio';
$labels['addflags'] = 'aggiungi flag al messaggio';
$labels['removeflags'] = 'togli flag dal messaggio';
$labels['flagread'] = 'Letto';
$labels['flagdeleted'] = 'Cancellato';
$labels['flaganswered'] = 'Risposto';
$labels['flagflagged'] = 'Contrassegna';
$labels['flagdraft'] = 'Bozza';
$labels['setvariable'] = 'Imposta variabile';
$labels['setvarname'] = 'Nome variabile:';
$labels['setvarvalue'] = 'Valore variabile:';
$labels['setvarmodifiers'] = 'Modificatori:';
$labels['varlower'] = 'minuscole';
$labels['varupper'] = 'maiuscole';
$labels['varlowerfirst'] = 'primo carattere minuscolo';
$labels['varupperfirst'] = 'primo carattere maiuscolo';
$labels['varquotewildcard'] = 'caratteri speciali di quoting';
$labels['varlength'] = 'lunghezza';
$labels['notify'] = 'Invia notifica';
-$labels['notifyaddress'] = 'All\'indirizzo email:';
-$labels['notifybody'] = 'Corpo della notifica:';
-$labels['notifysubject'] = 'Oggetto della notifica:';
-$labels['notifyfrom'] = 'Mittente della notifica:';
+$labels['notifytarget'] = 'Destinatario della notifica';
+$labels['notifymessage'] = 'Messaggio di notifica (opzionale):';
+$labels['notifyoptions'] = 'Opzioni di notifica (opzionale):';
+$labels['notifyfrom'] = 'Mittente della notifica (opzionale):';
$labels['notifyimportance'] = 'Importanza:';
$labels['notifyimportancelow'] = 'bassa';
$labels['notifyimportancenormal'] = 'normale';
$labels['notifyimportancehigh'] = 'alta';
+$labels['notifymethodmailto'] = 'Email';
+$labels['notifymethodtel'] = 'Telefono';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Crea filtro';
$labels['usedata'] = 'utilizza i seguenti dati nel filtro';
$labels['nextstep'] = 'passo successivo';
$labels['...'] = '...';
$labels['currdate'] = 'Data attuale';
$labels['datetest'] = 'Data';
$labels['dateheader'] = 'intestazione:';
$labels['year'] = 'anno';
$labels['month'] = 'mese';
$labels['day'] = 'giorno';
$labels['date'] = 'data (aaaa-mm-gg)';
$labels['julian'] = 'data (Giuliana)';
$labels['hour'] = 'ora';
$labels['minute'] = 'minuto';
$labels['second'] = 'secondo';
$labels['time'] = 'tempo (hh:mm:ss)';
$labels['iso8601'] = 'data (ISO8601)';
$labels['std11'] = 'data (RFC2822)';
$labels['zone'] = 'fuso orario';
$labels['weekday'] = 'giorno della settimana (0-6)';
$labels['advancedopts'] = 'Opzioni avanzate';
$labels['body'] = 'Corpo';
$labels['address'] = 'indirizzo';
$labels['envelope'] = 'busta';
$labels['modifier'] = 'modificatore:';
$labels['text'] = 'testo';
$labels['undecoded'] = 'non decodificato (raw)';
$labels['contenttype'] = 'content type';
$labels['modtype'] = 'tipo:';
$labels['allparts'] = 'tutto';
$labels['domain'] = 'dominio';
$labels['localpart'] = 'parte locale';
$labels['user'] = 'user';
$labels['detail'] = 'dettaglio';
$labels['comparator'] = 'comparatore';
$labels['default'] = 'predefinito';
$labels['octet'] = 'strict (octet)';
$labels['asciicasemap'] = 'non differenziare maiuscole/minuscole (ascii-casemap)';
$labels['asciinumeric'] = 'numerico';
$labels['index'] = 'indice:';
$labels['indexlast'] = 'indietro';
+$labels['vacation'] = 'Vacanza';
+$labels['vacation.reply'] = 'Messaggio di risposta';
+$labels['vacation.advanced'] = 'Impostazioni avanzate';
+$labels['vacation.subject'] = 'Oggetto';
+$labels['vacation.body'] = 'Testo';
+$labels['vacation.start'] = 'Inizio vacanza';
+$labels['vacation.end'] = 'Fine vacanza';
+$labels['vacation.status'] = 'Stato';
+$labels['vacation.on'] = 'Attivato';
+$labels['vacation.off'] = 'Disattivato';
+$labels['vacation.addresses'] = 'I miei indirizzi aggiuntivi';
+$labels['vacation.interval'] = 'Intervallo di risposta';
+$labels['vacation.after'] = 'Imposta regola di vacanza dopo';
+$labels['vacation.saving'] = 'Salvataggio...';
+$labels['vacation.action'] = 'Azione messaggio in arrivo';
+$labels['vacation.keep'] = 'Mantieni';
+$labels['vacation.discard'] = 'Elimina';
+$labels['vacation.redirect'] = 'Ridireziona a';
+$labels['vacation.copy'] = 'Invia copia a';
+$labels['arialabelfiltersetactions'] = 'Azione settaggio dei filtri ';
+$labels['arialabelfilteractions'] = 'Azione Filtri';
+$labels['arialabelfilterform'] = 'Proprietà filtri';
+$labels['ariasummaryfilterslist'] = 'Lista dei filtri';
+$labels['ariasummaryfiltersetslist'] = 'Lista settaggio dei filtri';
+$labels['filterstitle'] = 'Modifica filtri dei messaggio in arrivo';
+$labels['vacationtitle'] = 'Modifica le regole del Risponditore automatico';
$messages['filterunknownerror'] = 'Errore sconosciuto del server';
$messages['filterconnerror'] = 'Collegamento al server managesieve fallito';
$messages['filterdeleteerror'] = 'Eliminazione del filtro fallita. Si è verificato un errore nel server.';
$messages['filterdeleted'] = 'Filtro eliminato con successo';
$messages['filtersaved'] = 'Filtro salvato con successo';
$messages['filtersaveerror'] = 'Salvataggio del filtro fallito. Si è verificato un errore nel server.';
$messages['filterdeleteconfirm'] = 'Vuoi veramente eliminare il filtro selezionato?';
$messages['ruledeleteconfirm'] = 'Sei sicuro di voler eliminare la regola selezionata?';
$messages['actiondeleteconfirm'] = 'Sei sicuro di voler eliminare l\'azione selezionata?';
$messages['forbiddenchars'] = 'Caratteri non consentiti nel campo';
$messages['cannotbeempty'] = 'Il campo non può essere vuoto';
$messages['ruleexist'] = 'Esiste già un filtro con questo nome';
$messages['setactivateerror'] = 'Impossibile attivare il filtro. Errore del server.';
$messages['setdeactivateerror'] = 'Impossibile disattivare i filtri selezionati. Errore del server.';
$messages['setdeleteerror'] = 'Impossibile cancellare i filtri selezionati. Errore del server.';
$messages['setactivated'] = 'Filtro attivato';
$messages['setdeactivated'] = 'Filtro disattivato';
$messages['setdeleted'] = 'Filtro cancellato';
$messages['setdeleteconfirm'] = 'Sei sicuro di voler cancellare il gruppo di filtri';
$messages['setcreateerror'] = 'Impossibile creare il gruppo di filtri. Errore del server.';
$messages['setcreated'] = 'Gruppo di filtri creato';
$messages['activateerror'] = 'Impossibile abilitare i filtri selzionati. Errore del server.';
$messages['deactivateerror'] = 'impossibile disabilitare i filtri selezionati. Errore del server.';
$messages['deactivated'] = 'filtro abilitato';
$messages['activated'] = 'filtro disabilitato';
$messages['moved'] = 'filtro spostato';
$messages['moveerror'] = 'impossibile spostare il filtro selezionato. Errore del server.';
$messages['nametoolong'] = 'Impossibile creare il gruppo: Nome troppo lungo';
$messages['namereserved'] = 'nome riservato';
$messages['setexist'] = 'Il gruppo esiste già';
$messages['nodata'] = 'selezionare almeno una posizione';
$messages['invaliddateformat'] = 'Formato della data non valido';
+$messages['saveerror'] = 'Impossibile salvare i dati. Errore del server.';
+$messages['vacationsaved'] = 'Dati di vacanza salvati correttamente.';
+$messages['emptyvacationbody'] = 'Il testo del messaggio non puo\' essere vuoto!';
?>
diff --git a/lib/plugins/managesieve/localization/ja_JP.inc b/lib/plugins/managesieve/localization/ja_JP.inc
index 1fff7e3..db04084 100644
--- a/lib/plugins/managesieve/localization/ja_JP.inc
+++ b/lib/plugins/managesieve/localization/ja_JP.inc
@@ -1,192 +1,214 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'フィルター';
$labels['managefilters'] = '受信メールのフィルターを管理';
$labels['filtername'] = 'フィルター名';
$labels['newfilter'] = '新しいフィルター';
$labels['filteradd'] = 'フィルターを追加';
$labels['filterdel'] = 'フィルターを削除';
$labels['moveup'] = '上に移動';
$labels['movedown'] = '下に移動';
$labels['filterallof'] = '次のルールのすべてに一致';
$labels['filteranyof'] = '次のルールのいずれかに一致';
$labels['filterany'] = 'すべてのメッセージ';
$labels['filtercontains'] = '含む';
$labels['filternotcontains'] = '含まない';
$labels['filteris'] = '次に等しい';
$labels['filterisnot'] = '次に等しくない';
$labels['filterexists'] = 'が存在';
$labels['filternotexists'] = 'が存在しない';
$labels['filtermatches'] = '次の式に一致';
$labels['filternotmatches'] = '次の式に一致しない';
$labels['filterregex'] = '次の正規表現に一致';
$labels['filternotregex'] = '次の正規表現に一致しない';
$labels['filterunder'] = 'より下';
$labels['filterover'] = 'より上';
$labels['addrule'] = 'ルールを追加';
$labels['delrule'] = 'ルールを削除';
$labels['messagemoveto'] = '次にメッセージを移動';
$labels['messageredirect'] = '次のメールアドレスに転送';
$labels['messagecopyto'] = '次にメッセージをコピー';
$labels['messagesendcopy'] = '次にメッセージのコピーを送信';
$labels['messagereply'] = 'メッセージを返信';
$labels['messagedelete'] = 'メッセージを削除';
$labels['messagediscard'] = 'メッセージを破棄';
$labels['messagekeep'] = 'Keep message in Inbox';
$labels['messagesrules'] = '受信したメールの処理:';
$labels['messagesactions'] = '以下の操作を実行:';
$labels['add'] = '追加';
$labels['del'] = '削除';
$labels['sender'] = '送信者';
$labels['recipient'] = '宛先';
$labels['vacationaddr'] = 'My additional e-mail address(es):';
$labels['vacationdays'] = 'メッセージを(1日に)送信する頻度:';
$labels['vacationinterval'] = 'メッセージを送信する頻度:';
-$labels['days'] = '日';
-$labels['seconds'] = '秒';
$labels['vacationreason'] = 'メッセージ本体(休暇の理由):';
$labels['vacationsubject'] = 'メッセージの件名:';
+$labels['days'] = '日';
+$labels['seconds'] = '秒';
$labels['rulestop'] = 'ルールの評価を停止';
$labels['enable'] = '有効/無効';
$labels['filterset'] = 'フィルターセット';
$labels['filtersets'] = 'フィルターセット';
$labels['filtersetadd'] = 'フィルターセットを追加';
$labels['filtersetdel'] = '現在のフィルターセットを削除';
$labels['filtersetact'] = '現在のフィルター セットを有効';
$labels['filtersetdeact'] = '現在のフィルター セットを無効';
$labels['filterdef'] = 'フィルターの定義';
$labels['filtersetname'] = 'フィルターセットの名前';
$labels['newfilterset'] = '新しいフィルターセット';
$labels['active'] = '有効';
$labels['none'] = 'なし';
$labels['fromset'] = 'セットから';
$labels['fromfile'] = 'ファイルから';
$labels['filterdisabled'] = 'フィルターを無効にしました。';
$labels['countisgreaterthan'] = 'より大きい回数';
$labels['countisgreaterthanequal'] = '以上の回数';
$labels['countislessthan'] = '未満の回数';
$labels['countislessthanequal'] = '以下の回数';
$labels['countequals'] = '次と等しい回数';
$labels['countnotequals'] = 'count is not equal to';
$labels['valueisgreaterthan'] = 'より大きい値';
$labels['valueisgreaterthanequal'] = '以上の値';
$labels['valueislessthan'] = '未満の値';
$labels['valueislessthanequal'] = '以下の値';
$labels['valueequals'] = '次と等しい値';
$labels['valuenotequals'] = 'value is not equal to';
$labels['setflags'] = 'メッセージにフラグを設定';
$labels['addflags'] = 'メッセージにフラグを追加';
$labels['removeflags'] = 'メッセージからフラグを削除';
$labels['flagread'] = '既読';
$labels['flagdeleted'] = '削除済み';
$labels['flaganswered'] = '返信済み';
$labels['flagflagged'] = 'フラグ付き';
$labels['flagdraft'] = '下書き';
$labels['setvariable'] = '変数を設定';
$labels['setvarname'] = '変数の名前:';
$labels['setvarvalue'] = '変数の値:';
$labels['setvarmodifiers'] = '修飾子:';
$labels['varlower'] = '小文字';
$labels['varupper'] = '大文字';
$labels['varlowerfirst'] = '最初の文字を小文字';
$labels['varupperfirst'] = '最初の文字を大文字';
$labels['varquotewildcard'] = '特殊文字を引用処理';
$labels['varlength'] = '長さ';
$labels['notify'] = '通知を送信';
-$labels['notifyaddress'] = '送信先の電子メールアドレス:';
-$labels['notifybody'] = '通知の本文:';
-$labels['notifysubject'] = '通知の件名:';
-$labels['notifyfrom'] = '通知の送信者:';
+$labels['notifytarget'] = '通知の対象:';
+$labels['notifymessage'] = '通知のメッセージ(任意):';
+$labels['notifyoptions'] = '通知のオプション(任意):';
+$labels['notifyfrom'] = '通知の送信者(任意):';
$labels['notifyimportance'] = '重要度:';
$labels['notifyimportancelow'] = '低';
$labels['notifyimportancenormal'] = '通常';
$labels['notifyimportancehigh'] = '高';
+$labels['notifymethodmailto'] = '電子メール';
+$labels['notifymethodtel'] = '電話';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'フィルターを作成';
$labels['usedata'] = 'フィルターで次のデータを使用';
$labels['nextstep'] = '次のステップ';
$labels['...'] = '...';
$labels['currdate'] = 'Current date';
$labels['datetest'] = 'Date';
$labels['dateheader'] = 'header:';
$labels['year'] = 'year';
$labels['month'] = 'month';
$labels['day'] = 'day';
$labels['date'] = 'date (yyyy-mm-dd)';
$labels['julian'] = 'date (julian)';
$labels['hour'] = 'hour';
$labels['minute'] = 'minute';
$labels['second'] = 'second';
$labels['time'] = 'time (hh:mm:ss)';
$labels['iso8601'] = 'date (ISO8601)';
$labels['std11'] = 'date (RFC2822)';
$labels['zone'] = 'time-zone';
$labels['weekday'] = 'weekday (0-6)';
$labels['advancedopts'] = '高度なオプション';
$labels['body'] = '本文';
$labels['address'] = 'メールアドレス';
$labels['envelope'] = 'エンベロープ';
$labels['modifier'] = '修正:';
$labels['text'] = 'テキスト';
$labels['undecoded'] = '未デコード(そのまま)';
$labels['contenttype'] = 'Content Type';
$labels['modtype'] = '種類:';
$labels['allparts'] = 'すべて';
$labels['domain'] = 'ドメイン';
$labels['localpart'] = 'ローカルパート';
$labels['user'] = 'ユーザー';
$labels['detail'] = '詳細';
$labels['comparator'] = '比較器:';
$labels['default'] = '初期値';
$labels['octet'] = '厳密(オクテット)';
$labels['asciicasemap'] = '大文字小文字を区別しない(ascii-casemap)';
$labels['asciinumeric'] = '数値(ascii-numeric)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'backwards';
+$labels['vacation'] = '休暇';
+$labels['vacation.reply'] = '返信のメッセージ';
+$labels['vacation.advanced'] = '詳細な設定';
+$labels['vacation.subject'] = '件名';
+$labels['vacation.body'] = '本文';
+$labels['vacation.status'] = '状態';
+$labels['vacation.on'] = 'オン';
+$labels['vacation.off'] = 'オフ';
+$labels['vacation.addresses'] = '追加のアドレス';
+$labels['vacation.interval'] = '返信の間隔';
+$labels['vacation.after'] = '後に休暇のルールを記入';
+$labels['vacation.saving'] = 'データを保存中...';
+$labels['arialabelfiltersetactions'] = 'フィルターセットの動作';
+$labels['arialabelfilteractions'] = 'フィルターの動作';
+$labels['arialabelfilterform'] = 'フィルターの特性';
+$labels['ariasummaryfilterslist'] = 'フィルターの一覧';
+$labels['ariasummaryfiltersetslist'] = 'フィルターセットの一覧';
$messages['filterunknownerror'] = '不明なサーバーのエラーです。';
$messages['filterconnerror'] = 'サーバに接続できません。';
$messages['filterdeleteerror'] = 'フィルターを削除できません。サーバーでエラーが発生しました。';
$messages['filterdeleted'] = 'フィルターを削除しました。';
$messages['filtersaved'] = 'フィルターを保存しました。';
$messages['filtersaveerror'] = 'フィルターの保存できません。サーバーでエラーが発生しました。';
$messages['filterdeleteconfirm'] = '本当に選択したフィルターを削除しますか?';
$messages['ruledeleteconfirm'] = '本当に選択したルールを削除しますか?';
$messages['actiondeleteconfirm'] = '本当に選択した操作を削除しますか?';
$messages['forbiddenchars'] = '項目に禁止している文字が含まれています。';
$messages['cannotbeempty'] = '項目は空欄にできません。';
$messages['ruleexist'] = '指定した名前のフィルターが既に存在します。';
$messages['setactivateerror'] = '選択したフィルターセットを有効にできません。サーバーでエラーが発生しました。';
$messages['setdeactivateerror'] = '選択したフィルターセットを無効にできません。サーバーでエラーが発生しました。';
$messages['setdeleteerror'] = '選択したフィルターセットを削除できません。サーバーでエラーが発生しました。';
$messages['setactivated'] = 'フィルターセットを有効にしました。';
$messages['setdeactivated'] = 'フィルターセットを無効にしました。';
$messages['setdeleted'] = 'フィルターセットを削除しました。';
$messages['setdeleteconfirm'] = '本当に選択したフィルターセットを削除しますか?';
$messages['setcreateerror'] = 'フィルターセットを作成できません。サーバーでエラーが発生しました。';
$messages['setcreated'] = 'フィルターセットを作成しました。';
$messages['activateerror'] = '選択したフィルターを有効にできません。サーバーでエラーが発生しました。';
$messages['deactivateerror'] = '選択したフィルターを無効にできません。サーバーでエラーが発生しました。';
$messages['deactivated'] = 'フィルターを有効にしました。';
$messages['activated'] = 'フィルターを無効にしました。';
$messages['moved'] = 'フィルターを移動しました。';
$messages['moveerror'] = 'Unable to move selected filter. Server error occurred.';
$messages['nametoolong'] = '名前が長すぎます。';
$messages['namereserved'] = '予約されている名前です。';
$messages['setexist'] = 'フィルターセットが既に存在します。';
$messages['nodata'] = '少なくとも1つの場所を選択しなければなりません!';
$messages['invaliddateformat'] = '無効な日付または日付部分の書式';
+$messages['saveerror'] = 'フィルターの保存できません。サーバーでエラーが発生しました。';
+$messages['vacationsaved'] = '休暇のデータを保存しました。';
?>
diff --git a/lib/plugins/managesieve/localization/km_KH.inc b/lib/plugins/managesieve/localization/km_KH.inc
index a6094be..9d3de70 100644
--- a/lib/plugins/managesieve/localization/km_KH.inc
+++ b/lib/plugins/managesieve/localization/km_KH.inc
@@ -1,119 +1,116 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'តម្រង';
$labels['managefilters'] = 'គ្រប់គ្រង​តម្រប​សំបុត្រ​ចូល';
$labels['filtername'] = 'ឈ្មោះ​តម្រង';
$labels['newfilter'] = 'តម្រង​ថ្មី';
$labels['filteradd'] = 'បន្ថែម​តម្រង';
$labels['filterdel'] = 'លុប​តម្រង';
$labels['moveup'] = 'រុញ​ទៅ​លើ';
$labels['movedown'] = 'រុញ​ចុះ​ក្រោម';
$labels['filterallof'] = 'ត្រូវ​គ្នា​ទៅ​នឹង​លក្ខខណ្ឌ​ទាំង​នេះ';
$labels['filteranyof'] = 'ត្រូវ​គ្នា​ទៅ​នឹង​លក្ខខណ្ឌ​ណាមួយ';
$labels['filterany'] = 'សារ​ទាំង​អស់';
$labels['filtercontains'] = 'មាន​ផ្ទុក';
$labels['filternotcontains'] = 'មិន​មាន';
$labels['filteris'] = 'ស្មើ​នឹង';
$labels['filterisnot'] = 'មិន​ស្មើ​នឹង';
$labels['filterexists'] = 'មាន';
$labels['filternotexists'] = 'មិន​មាន';
$labels['filtermatches'] = 'សញ្ញាណ​ដែល​ត្រូវ';
$labels['filterunder'] = 'ក្រោម';
$labels['filterover'] = 'លើ';
$labels['addrule'] = 'បន្ថែម​លក្ខខណ្ឌ';
$labels['delrule'] = 'លុប​លក្ខខណ្ឌ';
$labels['messagemoveto'] = 'ផ្លាស់​ទី​សារ​ទៅ';
$labels['messageredirect'] = 'ប្ដូរ​ទិសដៅ​សារ​ទៅ';
$labels['messagecopyto'] = 'ចម្លង​សារ​ទៅ';
$labels['messagesendcopy'] = 'ផ្ញើ​ការ​ចម្លង​សារ​ទៅ';
$labels['messagereply'] = 'ឆ្លើយ​តប​ជាមួយ​សារ';
$labels['messagedelete'] = 'លុប​សារ';
$labels['messagediscard'] = 'បោះបង់​ជាមួយ​នឹង​សារ';
$labels['messagesrules'] = 'សម្រាប់​សំបុត្រ​ចូល៖';
$labels['messagesactions'] = '...ប្រតិបត្តិ​សកម្មភាព​ទាំង​នេះ៖';
$labels['add'] = 'បន្ថែម';
$labels['del'] = 'លុប';
$labels['sender'] = 'អ្នក​ផ្ញើ';
$labels['recipient'] = 'អ្នក​ទទួល';
$labels['vacationdays'] = 'តើ​ផ្ញើ​សារ​ញឹកញាប់​ប៉ុណ្ណា (ក្នុង​មួយ​ថ្ងៃ)៖';
-$labels['days'] = 'ថ្ងៃ';
-$labels['seconds'] = 'វិនាទី';
$labels['vacationreason'] = 'តួ​សារ (ហេតុផល​វិស្សមកាល)៖';
$labels['vacationsubject'] = 'ប្រធានបទ​សារ៖';
+$labels['days'] = 'ថ្ងៃ';
+$labels['seconds'] = 'វិនាទី';
$labels['rulestop'] = 'ឈប់​គិត​ទៅ​លើ​លក្ខខណ្ឌ';
$labels['enable'] = 'បើក/បិទ';
$labels['filterdef'] = 'អត្ថន័យ​តម្រង';
$labels['active'] = 'សកម្ម';
$labels['none'] = 'គ្មាន';
$labels['fromfile'] = 'ពី​ឯកសារ';
+$labels['filterdisabled'] = 'បាន​បិទ​តម្រង';
$labels['valuenotequals'] = 'តម្លៃ​មិន​ស្មើ​នឹង';
$labels['flagread'] = 'បាន​អាន';
$labels['flagdeleted'] = 'បាន​លុប';
$labels['flaganswered'] = 'បាន​ឆ្លើយ';
$labels['flagflagged'] = 'បាន​ដាក់​ទង់';
$labels['flagdraft'] = 'ការ​ព្រាង';
$labels['setvariable'] = 'កំណត់​អថេរ';
$labels['setvarname'] = 'ឈ្មោះ​អថេរ៖';
$labels['setvarvalue'] = 'តម្លៃ​អថេរ៖';
$labels['varlower'] = 'អក្សរ​តូច';
$labels['varupper'] = 'អក្សរ​ធំ';
$labels['varlength'] = 'ប្រវែង';
$labels['notify'] = 'ផ្ញើ​ការ​ជូន​ដំណឹង';
-$labels['notifyaddress'] = 'ទៅ​អាសយដ្ឋាន​អ៊ីមែល៖';
-$labels['notifybody'] = 'តួ​ការ​ជូន​ដំណឹង៖';
-$labels['notifysubject'] = 'ប្រធានបទ​ការ​ជូន​ដំណឹង៖';
-$labels['notifyfrom'] = 'អ្នក​ផ្ញើ​ការ​ជូន​ដំណឹង៖';
$labels['notifyimportance'] = 'សំខាន់៖';
$labels['notifyimportancelow'] = 'ទាប';
$labels['notifyimportancenormal'] = 'ធម្មតា';
$labels['notifyimportancehigh'] = 'ខ្ពស់';
$labels['filtercreate'] = 'បង្កើត​តម្រង';
$labels['usedata'] = 'ប្រើ​ទិន្នន័យ​ទាំង​នេះ​ក្នុង​តម្រង៖';
$labels['nextstep'] = 'ជំហាន​បន្ទាប់';
$labels['...'] = '...';
$labels['currdate'] = 'កាលបរិច្ឆេទ​បច្ចុប្បន្ន';
$labels['datetest'] = 'កាលបរិច្ឆេទ';
$labels['dateheader'] = 'ក្បាល៖';
$labels['year'] = 'ឆ្នាំ';
$labels['month'] = 'ខែ';
$labels['day'] = 'ថ្ងៃ';
$labels['date'] = 'កាល​បរិច្ឆេទ (yyyy-mm-dd)';
$labels['julian'] = 'កាល​បរិច្ឆេទ (julian)';
$labels['hour'] = 'ម៉ោង';
$labels['minute'] = 'នាទី';
$labels['second'] = 'វិនាទី';
$labels['time'] = 'ម៉ោង (hh:mm:ss)';
$labels['iso8601'] = 'កាល​បរិច្ឆេទ (ISO8601)';
$labels['std11'] = 'កាល​បរិច្ឆេទ (RFC2822)';
$labels['zone'] = 'តំបន់​ម៉ោង';
$labels['weekday'] = 'ថ្ងៃ​សប្ដាហ៍ (0-6)';
$labels['advancedopts'] = 'ជម្រើស​កម្រិត​ខ្ពស់';
$labels['body'] = 'តួ';
$labels['address'] = 'អាសយដ្ឋាន';
$labels['envelope'] = 'ស្រោម​សំបុត្រ';
$labels['text'] = 'អត្ថបទ';
$labels['contenttype'] = 'ប្រភេទ​មាតិកា';
$labels['modtype'] = 'ប្រភេទ៖';
$labels['allparts'] = 'ទាំងអស់';
$labels['domain'] = 'ដូមេន';
$labels['localpart'] = 'ផ្នែក​មូលដ្ឋាន';
$labels['user'] = 'អ្នកប្រើ';
$labels['detail'] = 'លម្អិត';
$labels['index'] = 'លិបិក្រម៖';
$labels['indexlast'] = 'បកក្រោយ';
?>
diff --git a/lib/plugins/managesieve/localization/ko_KR.inc b/lib/plugins/managesieve/localization/ko_KR.inc
index b552fa9..e9497e7 100644
--- a/lib/plugins/managesieve/localization/ko_KR.inc
+++ b/lib/plugins/managesieve/localization/ko_KR.inc
@@ -1,182 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = '필터';
$labels['managefilters'] = '수신 메일 필터 관리';
$labels['filtername'] = '필터명';
-$labels['newfilter'] = '새 필터';
+$labels['newfilter'] = '새로운 필터';
$labels['filteradd'] = '필터 추가';
$labels['filterdel'] = '필터 삭제';
$labels['moveup'] = '위로 이동';
$labels['movedown'] = '아래로 이동';
-$labels['filterallof'] = '다음의 모든 규칙과 일치함';
+$labels['filterallof'] = '다음 모든 규칙과 일치함';
$labels['filteranyof'] = '다음 규칙 중 하나라도 일치함';
$labels['filterany'] = '모든 메시지';
$labels['filtercontains'] = '다음을 포함함';
$labels['filternotcontains'] = '다음을 포함하지 않음';
-$labels['filteris'] = '다음과 같음';
-$labels['filterisnot'] = '다음과 같지 않음';
+$labels['filteris'] = '다음과 일치함';
+$labels['filterisnot'] = '다음과 일치하지 않음';
$labels['filterexists'] = '다음이 존재함';
$labels['filternotexists'] = '다음이 존재하지 않음';
$labels['filtermatches'] = '다음 표현식과 일치함';
$labels['filternotmatches'] = '다음 표현식과 일치하지 않음';
$labels['filterregex'] = '다음 정규 표현식과 일치함';
$labels['filternotregex'] = '다음 정규 표현식과 일치하지 않음';
$labels['filterunder'] = '다음보다 아래임';
$labels['filterover'] = '다음보다 위임';
$labels['addrule'] = '규칙 추가';
$labels['delrule'] = '규칙 삭제';
-$labels['messagemoveto'] = '메시지를 다음 위치로 이동함';
-$labels['messageredirect'] = '메시지를 다음 주소로 전송함';
-$labels['messagecopyto'] = '메시지를 다음 위치로 복사함';
-$labels['messagesendcopy'] = '메시지의 사본을 다음 위치로 보냄';
+$labels['messagemoveto'] = '메시지를 다음 위치로 이동';
+$labels['messageredirect'] = '메시지를 다음 주소로 재전송';
+$labels['messagecopyto'] = '메시지를 다음 위치로 복사';
+$labels['messagesendcopy'] = '메시지 사본을 다음 대상에게 보내기';
$labels['messagereply'] = '다음 메시지로 회신';
-$labels['messagedelete'] = '메시지를 삭제';
+$labels['messagedelete'] = '메시지 삭제';
$labels['messagediscard'] = '다음 메시지와 함께 폐기';
$labels['messagekeep'] = '메시지를 받은 편지함에 보관';
$labels['messagesrules'] = '해당 받은 메일:';
$labels['messagesactions'] = '...다음 동작을 실행:';
$labels['add'] = '추가';
$labels['del'] = '삭제';
-$labels['sender'] = '발신인';
-$labels['recipient'] = '수신인';
+$labels['sender'] = '발송자';
+$labels['recipient'] = '수신자';
+$labels['vacationaddr'] = '나의 추가적인 이메일 주소:';
$labels['vacationdays'] = '메시지 발신 주기 (일):';
$labels['vacationinterval'] = '메시지 발신 주기:';
-$labels['days'] = '일';
-$labels['seconds'] = '초';
$labels['vacationreason'] = '메시지 본문 (휴가 사유):';
$labels['vacationsubject'] = '메시지 제목:';
+$labels['days'] = '일';
+$labels['seconds'] = '초';
$labels['rulestop'] = '규칙 평가를 중단';
$labels['enable'] = '활성화/비활성화';
$labels['filterset'] = '필터 세트';
$labels['filtersets'] = '필터 세트';
$labels['filtersetadd'] = '필터 세트 추가';
$labels['filtersetdel'] = '현재 필터 세트를 삭제';
-$labels['filtersetact'] = '현재 필터 세트를 활성화';
-$labels['filtersetdeact'] = '현재 필터 세트를 비활성화';
+$labels['filtersetact'] = '현재 필터 세트 활성화';
+$labels['filtersetdeact'] = '현재 필터 세트 비활성화';
$labels['filterdef'] = '필터 정의';
$labels['filtersetname'] = '필터 세트명';
$labels['newfilterset'] = '새 필터 세트';
-$labels['active'] = '활성';
+$labels['active'] = '활성화됨';
$labels['none'] = '없음';
$labels['fromset'] = '세트로부터';
$labels['fromfile'] = '파일로부터';
$labels['filterdisabled'] = '필터가 비활성화됨';
$labels['countisgreaterthan'] = '개수가 다음보다 큼';
$labels['countisgreaterthanequal'] = '개수가 다음보다 크거나 같음';
$labels['countislessthan'] = '개수가 다음보다 작음';
$labels['countislessthanequal'] = '개수가 작거나 같음';
$labels['countequals'] = '개수가 다음과 같음';
-$labels['countnotequals'] = '갯수가 다음과 같지 않음';
+$labels['countnotequals'] = '개수가 다음과 일치하지 않음';
$labels['valueisgreaterthan'] = '값이 다음보다 큼';
$labels['valueisgreaterthanequal'] = '값이 다음보다 크거나 같음';
$labels['valueislessthan'] = '값이 다음보다 작음';
$labels['valueislessthanequal'] = '값이 다음보다 작거나 같음';
$labels['valueequals'] = '값이 다음과 같음';
-$labels['valuenotequals'] = '값이 다음과 같지 않음';
-$labels['setflags'] = '메시지에 깃발을 설정';
+$labels['valuenotequals'] = '값이 다음과 일치하지 않음';
+$labels['setflags'] = '메시지를 깃발로 표시';
$labels['addflags'] = '메시지에 깃발을 추가';
$labels['removeflags'] = '메시지에서 깃발을 제거';
$labels['flagread'] = '읽음';
$labels['flagdeleted'] = '삭제됨';
$labels['flaganswered'] = '응답함';
-$labels['flagflagged'] = '깃발을 추가함';
+$labels['flagflagged'] = '깃발로 표시함';
$labels['flagdraft'] = '임시 보관함';
$labels['setvariable'] = '변수 설정';
$labels['setvarname'] = '변수명:';
$labels['setvarvalue'] = '변수 값:';
$labels['setvarmodifiers'] = '수식자:';
$labels['varlower'] = '소문자';
$labels['varupper'] = '대문자';
$labels['varlowerfirst'] = '첫 문자를 소문자로';
$labels['varupperfirst'] = '첫 문자를 대문자로';
$labels['varquotewildcard'] = '특수 기호를 인용';
$labels['varlength'] = '길이';
$labels['notify'] = '알림 메시지 보내기';
-$labels['notifyaddress'] = '대상 이메일 주소:';
-$labels['notifybody'] = '알림 메시지 본문:';
-$labels['notifysubject'] = '알림 메시지 제목:';
-$labels['notifyfrom'] = '알림 메시지 발신인:';
+$labels['notifytarget'] = '알림 대상:';
+$labels['notifymessage'] = '알림 메시지(옵션):';
+$labels['notifyoptions'] = '알림 옵션(옵션):';
+$labels['notifyfrom'] = '알림 발송자(옵션):';
$labels['notifyimportance'] = '중요도:';
$labels['notifyimportancelow'] = '낮음';
$labels['notifyimportancenormal'] = '보통';
$labels['notifyimportancehigh'] = '높음';
+$labels['notifymethodmailto'] = '이메일';
+$labels['notifymethodtel'] = '전화';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = '필터 생성';
$labels['usedata'] = '필터에서 다음 데이터를 사용:';
$labels['nextstep'] = '다음 단계';
$labels['...'] = '...';
$labels['currdate'] = '오늘 날짜';
$labels['datetest'] = '날짜';
$labels['dateheader'] = '머리글:';
$labels['year'] = '년';
$labels['month'] = '월';
$labels['day'] = '일';
$labels['date'] = '날짜 (yyyy-mm-dd)';
$labels['julian'] = '날짜 (율리우스력)';
$labels['hour'] = '시';
$labels['minute'] = '분';
$labels['second'] = '초';
$labels['time'] = '시간 (hh:mm:ss)';
$labels['iso8601'] = '날짜 (ISO8601)';
$labels['std11'] = '날짜 (RFC2822)';
$labels['zone'] = '시간대';
$labels['weekday'] = '주중 (0-6)';
$labels['advancedopts'] = '고급 설정';
$labels['body'] = '본문';
$labels['address'] = '주소';
$labels['envelope'] = '봉투';
$labels['modifier'] = '수식자:';
$labels['text'] = '텍스트';
-$labels['undecoded'] = '암호화되지 않음 (원상태)';
+$labels['undecoded'] = '암호화되지 않음(원상태)';
$labels['contenttype'] = '내용 유형';
$labels['modtype'] = '유형:';
$labels['allparts'] = '모두';
$labels['domain'] = '도메인';
$labels['localpart'] = '로컬 부분';
$labels['user'] = '사용자';
$labels['detail'] = '세부사항';
$labels['comparator'] = '비교기:';
$labels['default'] = '기본';
$labels['octet'] = '엄격 (8진수)';
$labels['asciicasemap'] = '대/소문자 구분 (ascii-casemap)';
$labels['asciinumeric'] = '숫자 (ascii-numeric)';
$labels['index'] = '색인:';
$labels['indexlast'] = '역방향';
+$labels['vacation'] = '휴가';
+$labels['vacation.reply'] = '메시지 회신';
+$labels['vacation.advanced'] = '고급 설정';
+$labels['vacation.subject'] = '제목';
+$labels['vacation.body'] = '본문';
+$labels['vacation.start'] = '휴가 시작';
+$labels['vacation.end'] = '휴가 끝';
+$labels['vacation.status'] = '상태';
+$labels['vacation.on'] = '켬';
+$labels['vacation.off'] = '끔';
+$labels['vacation.addresses'] = '내 추가적인 주소';
+$labels['vacation.interval'] = '회신 주기';
+$labels['vacation.after'] = '다음 이후에 휴가 규칙을 위치함';
+$labels['vacation.saving'] = '데이터를 저장하는 중...';
+$labels['vacation.action'] = '수신 메시지 동작';
+$labels['vacation.keep'] = '보관';
+$labels['vacation.discard'] = '폐기';
+$labels['vacation.redirect'] = '재전송';
+$labels['vacation.copy'] = '사본을 다음 대상에게 전송';
+$labels['arialabelfiltersetactions'] = '필터 세트 동작';
+$labels['arialabelfilteractions'] = '필터 동작';
+$labels['arialabelfilterform'] = '필터 속성';
+$labels['ariasummaryfilterslist'] = '필터 목록';
+$labels['ariasummaryfiltersetslist'] = '필터 세트 목록';
+$labels['filterstitle'] = '수신 메일 필터 편집';
+$labels['vacationtitle'] = '자리비움 규칙 편집';
$messages['filterunknownerror'] = '알수 없는 서버 오류.';
-$messages['filterconnerror'] = '서버에 연결할 수 없음.';
+$messages['filterconnerror'] = '서버에 연결할 수 없습니다.';
+$messages['filterdeleteerror'] = '필터를 삭제할 수 없습니다. 서버 오류가 발생했습니다.';
$messages['filterdeleted'] = '필터가 성공적으로 삭제됨.';
$messages['filtersaved'] = '필터가 성공적으로 저장됨.';
+$messages['filtersaveerror'] = '필터를 저장할 수 없습니다. 서버 오류가 발생했습니다.';
$messages['filterdeleteconfirm'] = '정말로 선택한 필터를 삭제하시겠습니까?';
$messages['ruledeleteconfirm'] = '정말로 선택한 규칙을 삭제하시겠습니까?';
$messages['actiondeleteconfirm'] = '정말로 선택한 동작을 삭제하시겠습니까?';
-$messages['forbiddenchars'] = '필드에 금지된 문자가 존재함.';
-$messages['cannotbeempty'] = '필드는 비워둘 수 없음.';
-$messages['ruleexist'] = '지정한 이름의 필터가 이미 존재함.';
+$messages['forbiddenchars'] = '필드에 금지된 문자가 존재합니다.';
+$messages['cannotbeempty'] = '필드는 비어둘 수 없습니다.';
+$messages['ruleexist'] = '지정한 이름의 필터가 이미 존재합니다.';
+$messages['setactivateerror'] = '선택한 필터 세트를 활성화할 수 없습니다. 서버 오류가 발생했습니다.';
+$messages['setdeactivateerror'] = '선택한 필터 세트를 비활성화할 수 없습니다. 서버 오류가 발생했습니다.';
+$messages['setdeleteerror'] = '선택한 필터 세트를 삭제할 수 없습니다. 서버 오류가 발생했습니다.';
$messages['setactivated'] = '필터 세트가 성공적으로 활성화됨.';
$messages['setdeactivated'] = '필터 세트가 성공적으로 비활성화됨.';
$messages['setdeleted'] = '필터 세트가 성공적으로 삭제됨.';
$messages['setdeleteconfirm'] = '정말로 선택한 필터 세트를 삭제하시겠습니까?';
+$messages['setcreateerror'] = '선택한 필터 세트를 생성할 수 없습니다. 서버 오류가 발생했습니다.';
$messages['setcreated'] = '필터 세트가 성공적으로 생성됨.';
+$messages['activateerror'] = '선택한 필터를 활성화할 수 없습니다. 서버 오류가 발생했습니다.';
+$messages['deactivateerror'] = '선택한 필터를 비활성화할 수 없습니다. 서버 오류가 발생했습니다.';
$messages['deactivated'] = '필터가 성공적으로 비활성화됨.';
$messages['activated'] = '필터가 성공적으로 활성화됨.';
-$messages['moved'] = '필터가 성공적으로 이동함.';
-$messages['nametoolong'] = '이름이 너무 김.';
-$messages['namereserved'] = '예약된 이름.';
-$messages['setexist'] = '세트가 이미 존재함.';
+$messages['moved'] = '필터가 성공적으로 이동되었습니다.';
+$messages['moveerror'] = '선택한 필터를 이동할 수 없습니다. 서버 오류가 발생했습니다.';
+$messages['nametoolong'] = '이름이 너무 깁니다.';
+$messages['namereserved'] = '예약된 이름입니다.';
+$messages['setexist'] = '세트가 이미 존재합니다.';
$messages['nodata'] = '최소 하나의 위치가 선택되어야 합니다!';
$messages['invaliddateformat'] = '유효하지 않은 날짜 또는 날짜 일부 형식';
+$messages['saveerror'] = '데이터를 저장할 수 없습니다.. 서버 오류가 발생했습니다.';
+$messages['vacationsaved'] = '휴가 데이터가 성공적으로 저장됨.';
+$messages['emptyvacationbody'] = '휴가 메시지의 본문이 필요합니다!';
?>
diff --git a/lib/plugins/managesieve/localization/lt_LT.inc b/lib/plugins/managesieve/localization/lt_LT.inc
index 5dc81af..575e43c 100644
--- a/lib/plugins/managesieve/localization/lt_LT.inc
+++ b/lib/plugins/managesieve/localization/lt_LT.inc
@@ -1,181 +1,221 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtrai';
$labels['managefilters'] = 'Tvarkyti gaunamų laiškų filtrus';
$labels['filtername'] = 'Filtro pavadinimas';
$labels['newfilter'] = 'Naujas filtras';
$labels['filteradd'] = 'Pridėti filtrą';
$labels['filterdel'] = 'Pašalinti filtrą';
$labels['moveup'] = 'Pakelti aukštyn';
$labels['movedown'] = 'Nuleisti žemyn';
$labels['filterallof'] = 'atitinka visas šias taisykles';
$labels['filteranyof'] = 'atitinka bet kurią šių taisyklių';
$labels['filterany'] = 'visi laiškai';
$labels['filtercontains'] = 'savyje turi';
$labels['filternotcontains'] = 'savyje neturi';
$labels['filteris'] = 'yra lygus';
$labels['filterisnot'] = 'nėra lygus';
$labels['filterexists'] = 'egzistuoja';
$labels['filternotexists'] = 'neegzistuoja';
$labels['filtermatches'] = 'atitinka šabloną';
$labels['filternotmatches'] = 'neatitinka šablono';
$labels['filterregex'] = 'atitinka reguliarųjį reiškinį';
$labels['filternotregex'] = 'neatitinka reguliariojo reiškinio';
$labels['filterunder'] = 'nesiekia';
$labels['filterover'] = 'viršija';
$labels['addrule'] = 'Pridėti taisyklę';
$labels['delrule'] = 'Pašalinti taisyklę';
$labels['messagemoveto'] = 'Perkelti laišką į';
$labels['messageredirect'] = 'Peradresuoti laišką';
$labels['messagecopyto'] = 'Kopijuoti laišką į';
$labels['messagesendcopy'] = 'Nusiųsti laiško kopiją';
$labels['messagereply'] = 'Atsakyti laišku';
$labels['messagedelete'] = 'Pašalinti laišką';
+$labels['messagediscard'] = 'Panaikinti su laišku';
+$labels['messagekeep'] = 'Palikti laišką gautųjų aplanke';
$labels['messagesrules'] = 'Gaunamiems laiškams:';
$labels['messagesactions'] = '…vykdyti šiuos veiksmus:';
$labels['add'] = 'Pridėti';
$labels['del'] = 'Pašalinti';
$labels['sender'] = 'Siuntėjas';
$labels['recipient'] = 'Gavėjas';
$labels['vacationaddr'] = 'Papildomas gavėjų adresų sąrašas:';
$labels['vacationdays'] = 'Kaip dažnai išsiųsti laiškus (dienomis):';
$labels['vacationinterval'] = 'Kaip dažnai siųsti laiškus:';
-$labels['days'] = 'd.';
-$labels['seconds'] = 'sek.';
$labels['vacationreason'] = 'Laiško tekstas';
$labels['vacationsubject'] = 'Laiško tema:';
+$labels['days'] = 'd.';
+$labels['seconds'] = 'sek.';
$labels['rulestop'] = 'Nutraukti taisyklių vykdymą';
$labels['enable'] = 'Įjungti / išjungti';
$labels['filterset'] = 'Filtrų rinkinys';
$labels['filtersets'] = 'Filtrų rinkiniai';
$labels['filtersetadd'] = 'Pridėti filtrų rinkinį';
$labels['filtersetdel'] = 'Pašalinti šį filtrų rinkinį';
$labels['filtersetact'] = 'Įgalinti šį filtrų rinkinį';
$labels['filtersetdeact'] = 'Išjungti šį filtrų rinkinį';
$labels['filterdef'] = 'Filtro aprašas';
$labels['filtersetname'] = 'Filtrų rinkinio pavadinimas';
$labels['newfilterset'] = 'Naujas filtrų rinkinys';
$labels['active'] = 'aktyvus';
$labels['none'] = 'joks';
$labels['fromset'] = 'iš rinkinio';
$labels['fromfile'] = 'iš failo';
$labels['filterdisabled'] = 'Filtras išjungtas';
$labels['countisgreaterthan'] = 'kiekis didesnis nei';
$labels['countisgreaterthanequal'] = 'kiekis didesnis arba lygus';
$labels['countislessthan'] = 'kiekis mažesnis nei';
$labels['countislessthanequal'] = 'kiekis mažesnis arba lygus';
$labels['countequals'] = 'kiekis lygus';
$labels['countnotequals'] = 'kiekis nėra lygus';
$labels['valueisgreaterthan'] = 'reikšmė didesnė nei';
$labels['valueisgreaterthanequal'] = 'reikšmė didesnė arba lygi';
$labels['valueislessthan'] = 'reikšmė mažesnė nei';
$labels['valueislessthanequal'] = 'reikšmė mažesnė arba lygi';
$labels['valueequals'] = 'reikšmė lygi';
$labels['valuenotequals'] = 'reikšmė nėra lygi';
$labels['setflags'] = 'Nustatyti laiško požymius';
$labels['addflags'] = 'Pridėti laiško požymius';
$labels['removeflags'] = 'Pašalinti laiško požymius';
$labels['flagread'] = 'Skaitytas';
$labels['flagdeleted'] = 'Pašalintas';
$labels['flaganswered'] = 'Atsakytas';
$labels['flagflagged'] = 'Pažymėtas gairele';
$labels['flagdraft'] = 'Juodraštis';
$labels['setvariable'] = 'Nustatyti kintamąjį';
$labels['setvarname'] = 'Kintamojo vardas:';
$labels['setvarvalue'] = 'Kintamojo vertė:';
$labels['setvarmodifiers'] = 'Modifikatoriai:';
$labels['varlower'] = 'mažosios raidės';
$labels['varupper'] = 'didžiosios raidės';
$labels['varlowerfirst'] = 'pirmoji raidė mažoji';
$labels['varupperfirst'] = 'pirmoji raidė didžioji';
$labels['varquotewildcard'] = 'cituoti specialius simbolius';
$labels['varlength'] = 'ilgis';
$labels['notify'] = 'Siųsti priminimą';
-$labels['notifyaddress'] = 'Kam, el. pašto adresas:';
-$labels['notifybody'] = 'Priminimo tekstas';
-$labels['notifysubject'] = 'Priminimo pavadinimas';
-$labels['notifyfrom'] = 'Priminimo siuntėjas';
+$labels['notifytarget'] = 'Priminimo gavėjas:';
+$labels['notifymessage'] = 'Priminimo laiškas (nebūtina):';
+$labels['notifyoptions'] = 'Priminimo nustatymai (nebūtina):';
+$labels['notifyfrom'] = 'Priminimo siuntėjas (nebūtina):';
$labels['notifyimportance'] = 'Svarbumas';
$labels['notifyimportancelow'] = 'žemas';
$labels['notifyimportancenormal'] = 'normalus';
$labels['notifyimportancehigh'] = 'aukštas';
+$labels['notifymethodmailto'] = 'El. paštas';
+$labels['notifymethodtel'] = 'Telefono numeris';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Kurti filtrą';
$labels['usedata'] = 'Filtrui naudoti šiuos duomenis:';
$labels['nextstep'] = 'Kitas žingsnis';
$labels['...'] = '…';
$labels['currdate'] = 'Šiandienos data';
$labels['datetest'] = 'Data';
$labels['dateheader'] = 'antraštė:';
$labels['year'] = 'metai';
$labels['month'] = 'mėnuo';
$labels['day'] = 'diena';
$labels['date'] = 'data (yyyy-mm-dd)';
$labels['julian'] = 'data (Julijaus)';
$labels['hour'] = 'valanda';
$labels['minute'] = 'minutė';
$labels['second'] = 'sekundė';
$labels['time'] = 'laikas (hh:mm:ss)';
$labels['iso8601'] = 'data (ISO8601)';
$labels['std11'] = 'data (RFC2822)';
$labels['zone'] = 'laiko-zona';
$labels['weekday'] = 'savaitės diena (0-6)';
$labels['advancedopts'] = 'Papildomi nustatymai';
$labels['body'] = 'Laiško tekstas';
$labels['address'] = 'adresas';
$labels['envelope'] = 'vokas';
$labels['modifier'] = 'midifikatorius:';
$labels['text'] = 'tekstas';
$labels['undecoded'] = 'neiškoduotas (pirminis) tekstas';
$labels['contenttype'] = 'turinio tipas';
$labels['modtype'] = 'tipas:';
$labels['allparts'] = 'visi';
$labels['domain'] = 'sritis';
$labels['localpart'] = 'vietinė adreso dalis';
$labels['user'] = 'naudotojas';
$labels['detail'] = 'detalė';
$labels['comparator'] = 'palyginimo algoritmas:';
$labels['default'] = 'numatytasis';
$labels['octet'] = 'griežtas („octet“)';
$labels['asciicasemap'] = 'nepaisantis raidžių registro („ascii-casemap“)';
$labels['asciinumeric'] = 'skaitinis („ascii-numeric“)';
$labels['index'] = 'turinys:';
$labels['indexlast'] = 'atbulai';
+$labels['vacation'] = 'Atostogos';
+$labels['vacation.reply'] = 'Atsakyti laišku';
+$labels['vacation.advanced'] = 'Papildomos nuostatos';
+$labels['vacation.subject'] = 'Tema';
+$labels['vacation.body'] = 'Laiško tekstas';
+$labels['vacation.status'] = 'Būsena';
+$labels['vacation.on'] = 'Įjungta';
+$labels['vacation.off'] = 'Išjungta';
+$labels['vacation.addresses'] = 'Mano papildomi adresai';
+$labels['vacation.interval'] = 'Atsakymo intervalas';
+$labels['vacation.after'] = 'Atostogų taisyklę pastatyti po';
+$labels['vacation.saving'] = 'Išsaugomi duomenys...';
+$labels['vacation.action'] = 'Veiksmas su gaunamais laiškais';
+$labels['vacation.keep'] = 'Palikti';
+$labels['vacation.discard'] = 'Panaikinti';
+$labels['vacation.redirect'] = 'Peradresuoti kam';
+$labels['vacation.copy'] = 'Siųsti kopiją kam';
+$labels['arialabelfiltersetactions'] = 'Filtrų rinkinio veiksmai';
+$labels['arialabelfilteractions'] = 'Filtro veiksmai';
+$labels['arialabelfilterform'] = 'Filtro nustatymai';
+$labels['ariasummaryfilterslist'] = 'Filtrų sąrašas';
+$labels['ariasummaryfiltersetslist'] = 'Filtrų rinkinių sąrašas';
+$labels['filterstitle'] = 'Tvarkyti gaunamų laiškų filtrus';
+$labels['vacationtitle'] = 'Redaguoti ne-biure taisyklę';
$messages['filterunknownerror'] = 'Nežinoma serverio klaida.';
$messages['filterconnerror'] = 'Neįmanoma užmegzti ryšio su serveriu.';
+$messages['filterdeleteerror'] = 'Nepavyksta ištrinti filtro. Įvyko serverio klaida.';
$messages['filterdeleted'] = 'Filtras panaikintas sėkmingai.';
$messages['filtersaved'] = 'Filtras sėkmingai išsaugotas';
+$messages['filtersaveerror'] = 'Nepavyksta išsaugoti filtro. Įvyko serverio klaida.';
$messages['filterdeleteconfirm'] = 'Ar jūs esate įsitikinęs, jog norite panaikinti pasirinktus filtrus(-ą)?';
$messages['ruledeleteconfirm'] = 'Ar jūs įsitikinęs, jog norite panaikinti pasirinktą taisyklę?';
$messages['actiondeleteconfirm'] = 'Ar jūs įsitikinęs, jog norite panaikinti pasirinktą veiksmą?';
$messages['forbiddenchars'] = 'Laukelyje yra draudžiamų simbolių.';
$messages['cannotbeempty'] = 'Laukelis negali būti tuščias';
$messages['ruleexist'] = 'Filtras tokiu vardu jau yra.';
+$messages['setactivateerror'] = 'Neįmanoma aktyvuoti pasirinkto filtrų rinkinio. Įvyko serverio klaida.';
+$messages['setdeactivateerror'] = 'Neįmanoma išjungti pasirinkto filtrų rinkinio. Įvyko serverio klaida.';
+$messages['setdeleteerror'] = 'Neįmanoma panaikinti pasirinkto filtrų rinkinio. Įvyko serverio klaida.';
$messages['setactivated'] = 'Filtrų rinkinys sėkmingai aktyvuotas.';
$messages['setdeactivated'] = 'Filtrų rinkinys sėkmingai deaktyvuotas.';
$messages['setdeleted'] = 'Filtrų rinkinys sėkmingai panaikintas.';
$messages['setdeleteconfirm'] = 'Ar jūs esate tikri, jog norite panaikinti pasirinktą filtrų rinkinį?';
+$messages['setcreateerror'] = 'Neįmanoma sukurti filtrų rinkinio. Įvyko serverio klaida.';
$messages['setcreated'] = 'Filtrų rinkinys sėkmingai sukurtas.';
+$messages['activateerror'] = 'Neįmanoma įjungti pasirinktų filtrų(-o). Įvyko serverio klaida.';
+$messages['deactivateerror'] = 'Neįmanoma išjungti pasirinktų filtrų(-o). Įvyko serverio klaida.';
$messages['deactivated'] = 'Filtras(-as) sėkmingai išjungti.';
$messages['activated'] = 'Filtras(-as) sėkmingai įjungti.';
$messages['moved'] = 'Filtrai perkelti sėkmingai.';
+$messages['moveerror'] = 'Pasirinkto filtro perkelti neįmanoma. Įvyko serverio klaida.';
$messages['nametoolong'] = 'Vardas per ilgas.';
$messages['namereserved'] = 'Rezervuotas vardas.';
$messages['setexist'] = 'Rinkinys jau yra sukurtas.';
$messages['nodata'] = 'Būtina pasirinkti bent vieną poziciją!';
$messages['invaliddateformat'] = 'Neteisingas datos ar jos dalies formatas';
+$messages['saveerror'] = 'Nepavyksta išsaugoti duomenų. Įvyko serverio klaida.';
+$messages['vacationsaved'] = 'Sėkmingai išsaugoti atostogų duomenys.';
?>
diff --git a/lib/plugins/managesieve/localization/lv_LV.inc b/lib/plugins/managesieve/localization/lv_LV.inc
index 111f3b4..33c000e 100644
--- a/lib/plugins/managesieve/localization/lv_LV.inc
+++ b/lib/plugins/managesieve/localization/lv_LV.inc
@@ -1,192 +1,188 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Vēstuļu filtri';
$labels['managefilters'] = 'Pārvaldīt ienākošo vēstuļu filtrus';
$labels['filtername'] = 'Filtra nosaukums';
$labels['newfilter'] = 'Jauns filtrs';
$labels['filteradd'] = 'Pievienot filtru';
$labels['filterdel'] = 'Dzēst filtru';
$labels['moveup'] = 'Pārvietot augšup';
$labels['movedown'] = 'Pārvietot lejup';
$labels['filterallof'] = 'jāatbilst visiem sekojošajiem nosacījumiem';
$labels['filteranyof'] = 'jāatbilst jebkuram no sekojošajiem nosacījumiem';
$labels['filterany'] = 'visām vēstulēm';
$labels['filtercontains'] = 'satur';
$labels['filternotcontains'] = 'nesatur';
$labels['filteris'] = 'ir vienāds ar';
$labels['filterisnot'] = 'nav vienāds ar';
$labels['filterexists'] = 'eksistē';
$labels['filternotexists'] = 'neeksistē';
$labels['filtermatches'] = 'jāatbilst izteiksmei';
$labels['filternotmatches'] = 'neatbilst izteiksmei';
$labels['filterregex'] = 'jāatbilst regulārai izteiksmei';
$labels['filternotregex'] = 'neatbilst regulārai izteiksmei';
$labels['filterunder'] = 'zem';
$labels['filterover'] = 'virs';
$labels['addrule'] = 'Pievienot nosacījumu';
$labels['delrule'] = 'Dzēst nosacījumu';
$labels['messagemoveto'] = 'Pārvietot vēstuli uz';
$labels['messageredirect'] = 'Pāradresēt vēstuli uz';
$labels['messagecopyto'] = 'Kopēt vēstuli uz';
$labels['messagesendcopy'] = 'Pārsūtīt vēstules kopiju uz';
$labels['messagereply'] = 'Atbildēt ar vēstuli';
$labels['messagedelete'] = 'Dzēst vēstuli';
$labels['messagediscard'] = 'Dzēst vēstuli un atbildēt';
$labels['messagekeep'] = 'Paturēt ziņu Iesūtnē';
$labels['messagesrules'] = 'Ienākošajām vēstulēm:';
$labels['messagesactions'] = '...izpildīt sekojošās darbības:';
$labels['add'] = 'Pievienot';
$labels['del'] = 'Dzēst';
$labels['sender'] = 'Sūtītājs';
$labels['recipient'] = 'Saņēmējs';
$labels['vacationaddr'] = 'Mana(s) papildus e-pasta adrese(s):';
$labels['vacationdays'] = 'Cik bieži sūtī ziņojumus (dienās):';
$labels['vacationinterval'] = 'Cik bieži sūtīt vēstules:';
-$labels['days'] = 'dienas';
-$labels['seconds'] = 'sekundes';
$labels['vacationreason'] = 'Atvaļinājuma paziņojuma teksts:';
$labels['vacationsubject'] = 'Vēstules tēma:';
+$labels['days'] = 'dienas';
+$labels['seconds'] = 'sekundes';
$labels['rulestop'] = 'Apturēt nosacījumu pārbaudi';
$labels['enable'] = 'Ieslēgt/Izslēgt';
$labels['filterset'] = 'Filtru kopa';
$labels['filtersets'] = 'Filtru kopas';
$labels['filtersetadd'] = 'Pievienot filtru kopu';
$labels['filtersetdel'] = 'Dzēst pašreizējo filtru kopu';
$labels['filtersetact'] = 'Aktivizēt pašreizējo filtru kopu';
$labels['filtersetdeact'] = 'Deaktivizēt pašreizējo filtru kopu';
$labels['filterdef'] = 'Filtra apraksts';
$labels['filtersetname'] = 'Filtru kopas nosaukums';
$labels['newfilterset'] = 'Jauna filtru kopa';
$labels['active'] = 'aktīvs';
$labels['none'] = 'nav';
$labels['fromset'] = 'no kopas';
$labels['fromfile'] = 'no faila';
$labels['filterdisabled'] = 'Filtrs atslēgts';
$labels['countisgreaterthan'] = 'skaits ir lielāks kā';
$labels['countisgreaterthanequal'] = 'skaits ir vienāds vai lielāks kā';
$labels['countislessthan'] = 'skaits ir mazāks kā';
$labels['countislessthanequal'] = 'skaits ir vienāds vai mazāks kā';
$labels['countequals'] = 'skaits ir vienāds ar';
$labels['countnotequals'] = 'skaits nav vienāds ar';
$labels['valueisgreaterthan'] = 'vērtība ir lielāka kā';
$labels['valueisgreaterthanequal'] = 'vērtība ir vienāda vai lielāka kā';
$labels['valueislessthan'] = 'vērtība ir mazāka kā';
$labels['valueislessthanequal'] = 'vērtība ir vienāda vai mazāka kā';
$labels['valueequals'] = 'vērtība ir vienāda ar';
$labels['valuenotequals'] = 'vērtība nav vienāda ar';
$labels['setflags'] = 'Marķēt vēstuli';
$labels['addflags'] = 'Pievienot vēstulei marķierus';
$labels['removeflags'] = 'Noņemt vēstulei marķierus';
$labels['flagread'] = 'Lasītas';
$labels['flagdeleted'] = 'Dzēstas';
$labels['flaganswered'] = 'Atbildētas';
$labels['flagflagged'] = 'Marķētas';
$labels['flagdraft'] = 'Melnraksts';
$labels['setvariable'] = 'Iestatīt mainīgo';
$labels['setvarname'] = 'Mainīgā nosaukums:';
$labels['setvarvalue'] = 'Mainīgā vērtība:';
$labels['setvarmodifiers'] = 'Modifikatori:';
$labels['varlower'] = 'mazie burti';
$labels['varupper'] = 'lielie burti';
$labels['varlowerfirst'] = 'pirmais burts kā mazais burts';
$labels['varupperfirst'] = 'pirmais burts kā lielais burts';
$labels['varquotewildcard'] = '"citēt" speciālās rakstzīmes';
$labels['varlength'] = 'garums';
$labels['notify'] = 'Sūtīt paziņojumus';
-$labels['notifyaddress'] = 'Uz e-pasta adresi:';
-$labels['notifybody'] = 'Paziņojuma teksts:';
-$labels['notifysubject'] = 'Paziņojuma tēma:';
-$labels['notifyfrom'] = 'Paziņojuma sūtītājs:';
$labels['notifyimportance'] = 'Svarīgums:';
$labels['notifyimportancelow'] = 'zems';
$labels['notifyimportancenormal'] = 'parasts';
$labels['notifyimportancehigh'] = 'augsts';
$labels['filtercreate'] = 'Izveidot filtru';
$labels['usedata'] = 'Filtrā izmantot sekojošus datus';
$labels['nextstep'] = 'Nākamais solis';
$labels['...'] = '...';
$labels['currdate'] = 'Pašreizējais datums';
$labels['datetest'] = 'Datums';
$labels['dateheader'] = 'galvene:';
$labels['year'] = 'gads';
$labels['month'] = 'mēnesis';
$labels['day'] = 'diena';
$labels['date'] = 'datums (gggg-mm-dd)';
$labels['julian'] = 'datums (Jūlija kalendārs)';
$labels['hour'] = 'stunda';
$labels['minute'] = 'minūte';
$labels['second'] = 'sekunde';
$labels['time'] = 'laiks (hh:mm:ss)';
$labels['iso8601'] = 'datums (ISO8601)';
$labels['std11'] = 'datums (RFC2822)';
$labels['zone'] = 'laikajosla';
$labels['weekday'] = 'nedēļas diena (0-6)';
$labels['advancedopts'] = 'Paplašinātie iestatījumi';
$labels['body'] = 'Pamatteksts';
$labels['address'] = 'adresāts';
$labels['envelope'] = 'aploksne';
$labels['modifier'] = 'modifikators:';
$labels['text'] = 'teksts';
$labels['undecoded'] = 'neatkodēts (neapstrādāti dati)';
$labels['contenttype'] = 'satura tips';
$labels['modtype'] = 'tips:';
$labels['allparts'] = 'viss';
$labels['domain'] = 'domēns';
$labels['localpart'] = 'lokālā daļa';
$labels['user'] = 'lietotājs';
$labels['detail'] = 'detaļas';
$labels['comparator'] = 'salīdzinātājs';
$labels['default'] = 'noklusētā vērtība';
$labels['octet'] = 'precīzs (oktets)';
$labels['asciicasemap'] = 'reģistrnejutīgs (ascii tabula)';
$labels['asciinumeric'] = 'skaitļu (ascii skaitļu)';
$labels['index'] = 'indekss:';
$labels['indexlast'] = 'atpakaļ';
$messages['filterunknownerror'] = 'Nezināma servera kļūda.';
$messages['filterconnerror'] = 'Neizdevās pieslēgties ManageSieve serverim.';
$messages['filterdeleteerror'] = 'Neizdevās izdzēst filtru - atgadījās servera iekšējā kļūda.';
$messages['filterdeleted'] = 'Filtrs veiksmīgi izdzēsts.';
$messages['filtersaved'] = 'Filtrs veiksmīgi saglabāts.';
$messages['filtersaveerror'] = 'Neizdevās saglabāt filtru - atgadījās servera iekšējā kļūda.';
$messages['filterdeleteconfirm'] = 'Vai Jūs tiešām vēlaties dzēst atzīmēto filtru?';
$messages['ruledeleteconfirm'] = 'Vai Jūs tiešām vēlaties dzēst atzīmēto nosacījumu?';
$messages['actiondeleteconfirm'] = 'Vai Jūs tiešām vēlaties dzēst atzīmēto darbību?';
$messages['forbiddenchars'] = 'Lauks satur aizliegtus simbolus.';
$messages['cannotbeempty'] = 'Lauks nedrīkst būt tukšs.';
$messages['ruleexist'] = 'Filtrs ar tādu nosaukumu jau pastāv.';
$messages['setactivateerror'] = 'Neizdevās aktivizēt atzīmēto filtru kopu - atgadījās servera iekšējā kļūda.';
$messages['setdeactivateerror'] = 'Neizdevās deaktivizēt atzīmēto filtru kopu - atgadījās servera iekšējā kļūda.';
$messages['setdeleteerror'] = 'Neizdevās izdzēst atzīmēto filtru kopu - atgadījās servera ieksējā kļūda.';
$messages['setactivated'] = 'Filtru kopa veiksmīgi aktivizēta.';
$messages['setdeactivated'] = 'Filtru kopa veiksmīgi deaktivizēta.';
$messages['setdeleted'] = 'Filtru kopa veiksmīgi izdzēsta.';
$messages['setdeleteconfirm'] = 'Vai tiešām Jūs vēlaties dzēst atzīmēto filtru kopu?';
$messages['setcreateerror'] = 'Neizdevās izveidot filtru kopu - atgadījās servera iekšējā kļūda.';
$messages['setcreated'] = 'Filtru kopa veiksmīgi izveidota.';
$messages['activateerror'] = 'Nav iespējams ieslēgt izvēlēto(s) filtru(s) - atgadījās servera iekšējā kļūda.';
$messages['deactivateerror'] = 'Nav iespējams atslēgt izvēlēto(s) filtru(s) - atgadījās servera iekšējā kļūda.';
$messages['deactivated'] = 'Filtrs(i) veiksmīgi atslēgts(i).';
$messages['activated'] = 'Filtrs(i) veiksmīgi ieslēgts(i).';
$messages['moved'] = 'Filtrs veiksmīgi pārvietots.';
$messages['moveerror'] = 'Nav iespējams pārvietot izvēlēto filtru - atgadījās servera iekšējā kļūda.';
$messages['nametoolong'] = 'Neizdevās izveidot filtru kopu. Pārāk garš kopas nosaukums.';
$messages['namereserved'] = 'Rezervētais nosaukums.';
$messages['setexist'] = 'Kopa jau eksistē.';
$messages['nodata'] = 'Ir jābūt atzīmētai vismaz vienai pozīcijai!';
$messages['invaliddateformat'] = 'Nederīgs datums vai datuma formāts';
?>
diff --git a/lib/plugins/managesieve/localization/nb_NO.inc b/lib/plugins/managesieve/localization/nb_NO.inc
index e0a3448..c9224ae 100644
--- a/lib/plugins/managesieve/localization/nb_NO.inc
+++ b/lib/plugins/managesieve/localization/nb_NO.inc
@@ -1,181 +1,188 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtre';
$labels['managefilters'] = 'Rediger filter for innkommende e-post';
$labels['filtername'] = 'Filternavn';
$labels['newfilter'] = 'Nytt filter';
$labels['filteradd'] = 'Legg til filter';
$labels['filterdel'] = 'Slett filter';
$labels['moveup'] = 'Flytt opp';
$labels['movedown'] = 'Flytt ned';
$labels['filterallof'] = 'som treffer alle følgende regler';
$labels['filteranyof'] = 'som treffer en av følgende regler';
$labels['filterany'] = 'alle meldinger';
$labels['filtercontains'] = 'inneholder';
$labels['filternotcontains'] = 'ikke inneholder';
$labels['filteris'] = 'er lik';
$labels['filterisnot'] = 'er ulik';
$labels['filterexists'] = 'eksisterer';
$labels['filternotexists'] = 'ikke eksisterer';
$labels['filtermatches'] = 'treffer uttrykk';
$labels['filternotmatches'] = 'ikke treffer uttrykk';
$labels['filterregex'] = 'treffer regulært uttrykk';
$labels['filternotregex'] = 'ikke treffer regulært uttrykk';
$labels['filterunder'] = 'under';
$labels['filterover'] = 'over';
$labels['addrule'] = 'Legg til regel';
$labels['delrule'] = 'Slett regel';
$labels['messagemoveto'] = 'Flytt meldingen til';
$labels['messageredirect'] = 'Videresend meldingen til';
$labels['messagecopyto'] = 'Kopier meldingen til';
$labels['messagesendcopy'] = 'Send en kopi av meldingen til';
$labels['messagereply'] = 'Svar med melding';
$labels['messagedelete'] = 'Slett melding';
$labels['messagediscard'] = 'Avvis med melding';
+$labels['messagekeep'] = 'Behold melding i innboks';
$labels['messagesrules'] = 'For innkommende e-post';
$labels['messagesactions'] = '... gjør følgende:';
$labels['add'] = 'Legg til';
$labels['del'] = 'Slett';
$labels['sender'] = 'Avsender';
$labels['recipient'] = 'Mottaker';
+$labels['vacationaddr'] = 'Tilleggs epost-adresse(r):';
$labels['vacationdays'] = 'Periode mellom meldinger (i dager):';
$labels['vacationinterval'] = 'Periode mellom meldinger:';
-$labels['days'] = 'dager';
-$labels['seconds'] = 'sekunder';
$labels['vacationreason'] = 'Innhold (begrunnelse for fravær)';
$labels['vacationsubject'] = 'Meldingsemne:';
+$labels['days'] = 'dager';
+$labels['seconds'] = 'sekunder';
$labels['rulestop'] = 'Stopp evaluering av regler';
$labels['enable'] = 'Aktiver/Deaktiver';
$labels['filterset'] = 'Filtersett';
$labels['filtersets'] = 'Filtersett';
$labels['filtersetadd'] = 'Nytt filtersett';
$labels['filtersetdel'] = 'Slett gjeldende filtersett';
$labels['filtersetact'] = 'Aktiver gjeldende filtersett';
$labels['filtersetdeact'] = 'Deaktiver gjeldende filtersett';
$labels['filterdef'] = 'Filterdefinisjon';
$labels['filtersetname'] = 'Navn på filtersett';
$labels['newfilterset'] = 'Nytt filtersett';
$labels['active'] = 'aktiv';
$labels['none'] = 'ingen';
$labels['fromset'] = 'fra sett';
$labels['fromfile'] = 'fra fil';
$labels['filterdisabled'] = 'Filter deaktivert';
$labels['countisgreaterthan'] = 'antall er flere enn';
$labels['countisgreaterthanequal'] = 'antall er flere enn eller lik';
$labels['countislessthan'] = 'antall er færre enn';
$labels['countislessthanequal'] = 'antall er færre enn eller lik';
$labels['countequals'] = 'antall er lik';
$labels['countnotequals'] = 'tallet er ikke det samme som';
$labels['valueisgreaterthan'] = 'verdien er høyrere enn';
$labels['valueisgreaterthanequal'] = 'verdien er høyere eller lik';
$labels['valueislessthan'] = 'verdien er lavere enn';
$labels['valueislessthanequal'] = 'verdien er lavere eller lik';
$labels['valueequals'] = 'verdien er lik';
$labels['valuenotequals'] = 'verdien er ikke den samme som';
$labels['setflags'] = 'Sett meldingsflagg';
$labels['addflags'] = 'Legg til flagg på meldingen';
$labels['removeflags'] = 'Fjern flagg fra meldingen';
$labels['flagread'] = 'Lese';
$labels['flagdeleted'] = 'Slettet';
$labels['flaganswered'] = 'Besvart';
$labels['flagflagged'] = 'Flagget';
$labels['flagdraft'] = 'Utkast';
$labels['setvariable'] = 'Set variabel';
$labels['setvarname'] = 'Variabelnavn:';
$labels['setvarvalue'] = 'Variabel verdi:';
$labels['setvarmodifiers'] = 'Modifikator:';
$labels['varlower'] = 'med små bokstaver';
$labels['varupper'] = 'med store bokstaver';
$labels['varlowerfirst'] = 'første tegn liten bokstav';
$labels['varupperfirst'] = 'første tegn stor bokstav';
$labels['varquotewildcard'] = 'sitér spesialtegn';
$labels['varlength'] = 'lengde';
$labels['notify'] = 'Send melding';
-$labels['notifyaddress'] = 'Til e-postadresse:';
-$labels['notifybody'] = 'Varseltekst:';
-$labels['notifysubject'] = 'Varselemne:';
-$labels['notifyfrom'] = 'Varselavsender:';
$labels['notifyimportance'] = 'Viktighet:';
$labels['notifyimportancelow'] = 'lav';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'høy';
$labels['filtercreate'] = 'Opprett filter';
$labels['usedata'] = 'Bruk følgende data i filteret:';
$labels['nextstep'] = 'Neste steg';
$labels['...'] = '…';
$labels['currdate'] = 'Nåværende dato';
$labels['datetest'] = 'Dato';
$labels['dateheader'] = 'header:';
$labels['year'] = 'år';
$labels['month'] = 'måned';
$labels['day'] = 'dag';
$labels['date'] = 'dato (yyyy-mm-dd)';
$labels['julian'] = 'dato (juliansk)';
$labels['hour'] = 'time';
$labels['minute'] = 'minutt';
$labels['second'] = 'sekund';
$labels['time'] = 'tid (hh:mm:ss)';
$labels['iso8601'] = 'dato (ISO8601)';
$labels['std11'] = 'dato (RFC2822)';
$labels['zone'] = 'tidssone';
$labels['weekday'] = 'ukedag (0-6)';
$labels['advancedopts'] = 'Avanserte alternativer';
$labels['body'] = 'Meldingstekst';
$labels['address'] = 'adresse';
$labels['envelope'] = 'konvolutt';
$labels['modifier'] = 'modifikator:';
$labels['text'] = 'tekst';
$labels['undecoded'] = 'ikke dekodet (rå)';
$labels['contenttype'] = 'innholdstype';
$labels['modtype'] = 'type:';
$labels['allparts'] = 'alle';
$labels['domain'] = 'domene';
$labels['localpart'] = 'lokal del (local part)';
$labels['user'] = 'bruker';
$labels['detail'] = 'detalj';
$labels['comparator'] = 'sammenligning:';
$labels['default'] = 'standard';
$labels['octet'] = 'streng (oktett)';
$labels['asciicasemap'] = 'ikke skill store og små bokstaver (ascii-casemap)';
$labels['asciinumeric'] = 'numerisk (ascii-numeric)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'baklengs';
$messages['filterunknownerror'] = 'Ukjent problem med tjener.';
$messages['filterconnerror'] = 'Kunne ikke koble til tjeneren.';
+$messages['filterdeleteerror'] = 'Kunne ikke slette filter. Fikk feilmelding fra server.';
$messages['filterdeleted'] = 'Filteret er blitt slettet.';
$messages['filtersaved'] = 'Filteret er blitt lagret.';
+$messages['filtersaveerror'] = 'Kunne ikke lagre filter. Fikk feilmelding fra server.';
$messages['filterdeleteconfirm'] = 'Vil du virkelig slette det valgte filteret?';
$messages['ruledeleteconfirm'] = 'Er du sikker på at du vil slette valgte regel?';
$messages['actiondeleteconfirm'] = 'Er du sikker på at du vil slette valgte hendelse?';
$messages['forbiddenchars'] = 'Ugyldige tegn i felt.';
$messages['cannotbeempty'] = 'Feltet kan ikke stå tomt.';
$messages['ruleexist'] = 'Det finnes allerede et filter med dette navnet.';
+$messages['setactivateerror'] = 'Kunne ikke aktivere valgte filtersett. Fikk feilmelding fra server.';
+$messages['setdeactivateerror'] = 'Kunne ikke deaktivere valgte filtersett. Fikk feilmelding fra server.';
+$messages['setdeleteerror'] = 'Kunne ikke slette valgte filtersett. Fikk feilmelding fra server.';
$messages['setactivated'] = 'Filtersett aktivert.';
$messages['setdeactivated'] = 'Filtersett deaktivert.';
$messages['setdeleted'] = 'Filtersett slettet.';
$messages['setdeleteconfirm'] = 'Er du sikker på at du vil slette det valgte filtersettet?';
+$messages['setcreateerror'] = 'Kunne ikke opprette filtersett. Fikk feilmelding fra server.';
$messages['setcreated'] = 'Filtersett opprettet.';
+$messages['activateerror'] = 'Kunne ikke aktivere valgte filter(e). Fikk feilmelding fra server.';
+$messages['deactivateerror'] = 'Kunne ikke deaktivere valgte filter(e). Fikk feilmelding fra server.';
$messages['deactivated'] = 'Filter skrudd på.';
$messages['activated'] = 'Filter skrudd av.';
$messages['moved'] = 'Filter ble flyttet.';
+$messages['moveerror'] = 'Kunne ikke flytte valgte filter. Fikk feilmelding fra server.';
$messages['nametoolong'] = 'Navnet er for langt.';
$messages['namereserved'] = 'Navnet er reservert.';
$messages['setexist'] = 'Settet eksisterer allerede.';
$messages['nodata'] = 'Du må velge minst én posisjon!';
$messages['invaliddateformat'] = 'Ugyldig dato eller datoformat';
?>
diff --git a/lib/plugins/managesieve/localization/nl_NL.inc b/lib/plugins/managesieve/localization/nl_NL.inc
index 97a4e16..b84b87e 100644
--- a/lib/plugins/managesieve/localization/nl_NL.inc
+++ b/lib/plugins/managesieve/localization/nl_NL.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filters';
$labels['managefilters'] = 'Beheer filters voor inkomende e-mail';
$labels['filtername'] = 'Filternaam';
$labels['newfilter'] = 'Nieuw filter';
$labels['filteradd'] = 'Filter toevoegen';
$labels['filterdel'] = 'Verwijder filter';
$labels['moveup'] = 'Verplaats omhoog';
$labels['movedown'] = 'Verplaats omlaag';
$labels['filterallof'] = 'die voldoet aan alle volgende regels';
$labels['filteranyof'] = 'die voldoet aan één van de volgende regels';
$labels['filterany'] = 'alle berichten';
$labels['filtercontains'] = 'bevat';
$labels['filternotcontains'] = 'bevat niet';
$labels['filteris'] = 'is gelijk aan';
$labels['filterisnot'] = 'is niet gelijk aan';
$labels['filterexists'] = 'bestaat';
$labels['filternotexists'] = 'bestaat niet';
$labels['filtermatches'] = 'komt overeen met expressie';
$labels['filternotmatches'] = 'komt niet overeen met expressie';
$labels['filterregex'] = 'komt overeen met de reguliere expressie';
$labels['filternotregex'] = 'komt niet overeen met de reguliere expressie';
$labels['filterunder'] = 'onder';
$labels['filterover'] = 'over';
$labels['addrule'] = 'Regel toevoegen';
$labels['delrule'] = 'Regel verwijderen';
$labels['messagemoveto'] = 'Verplaats bericht naar';
$labels['messageredirect'] = 'Bericht doorsturen naar';
$labels['messagecopyto'] = 'Kopieer bericht naar';
$labels['messagesendcopy'] = 'Verstuur een kopie naar';
$labels['messagereply'] = 'Beantwoord met bericht';
$labels['messagedelete'] = 'Verwijder bericht';
$labels['messagediscard'] = 'Met bericht negeren';
$labels['messagekeep'] = 'Bewaar bericht in Postvak IN';
$labels['messagesrules'] = 'Voor binnenkomende e-mail:';
$labels['messagesactions'] = '...voer de volgende acties uit';
$labels['add'] = 'Toevoegen';
$labels['del'] = 'Verwijderen';
$labels['sender'] = 'Afzender';
$labels['recipient'] = 'Ontvanger';
$labels['vacationaddr'] = 'Mijn extra e-mailadres(sen):';
$labels['vacationdays'] = 'Hoe vaak moet een bericht verstuurd worden (in dagen):';
$labels['vacationinterval'] = 'Hoe vaak moet een bericht verstuurd worden:';
-$labels['days'] = 'dagen';
-$labels['seconds'] = 'seconden';
$labels['vacationreason'] = 'Bericht (vakantiereden):';
$labels['vacationsubject'] = 'Onderwerp:';
+$labels['days'] = 'dagen';
+$labels['seconds'] = 'seconden';
$labels['rulestop'] = 'Stop met regels uitvoeren';
$labels['enable'] = 'In-/uitschakelen';
$labels['filterset'] = 'Filterset';
$labels['filtersets'] = 'Filtersets';
$labels['filtersetadd'] = 'Nieuwe filterset';
$labels['filtersetdel'] = 'Verwijder huidige filterset';
$labels['filtersetact'] = 'Huidige filterset activeren';
$labels['filtersetdeact'] = 'Huidige filterset uitschakelen';
$labels['filterdef'] = 'Filterdefinitie';
$labels['filtersetname'] = 'Filtersetnaam';
$labels['newfilterset'] = 'Nieuwe filterset';
$labels['active'] = 'actief';
$labels['none'] = 'geen';
$labels['fromset'] = 'van set';
$labels['fromfile'] = 'van bestand';
$labels['filterdisabled'] = 'Filter uitgeschakeld';
$labels['countisgreaterthan'] = 'aantal is groter dan';
$labels['countisgreaterthanequal'] = 'aantal is groter dan of gelijk aan';
$labels['countislessthan'] = 'aantal is kleiner dan';
$labels['countislessthanequal'] = 'aantal is kleiner dan of gelijk aan';
$labels['countequals'] = 'aantal is gelijk aan';
$labels['countnotequals'] = 'aantal is niet gelijk aan';
$labels['valueisgreaterthan'] = 'waarde is groter dan';
$labels['valueisgreaterthanequal'] = 'waarde is groter dan of gelijk aan';
$labels['valueislessthan'] = 'waarde is minder dan';
$labels['valueislessthanequal'] = 'waarde is minder dan of gelijk aan';
$labels['valueequals'] = 'waarde is gelijk aan';
$labels['valuenotequals'] = 'waarde is niet gelijk aan';
$labels['setflags'] = 'Stel markeringen in op bericht';
$labels['addflags'] = 'Voeg markeringen toe aan bericht';
$labels['removeflags'] = 'Verwijder markeringen van bericht';
$labels['flagread'] = 'Lezen';
$labels['flagdeleted'] = 'Verwijderd';
$labels['flaganswered'] = 'Beantwoord';
$labels['flagflagged'] = 'Gemarkeerd';
$labels['flagdraft'] = 'Concept';
$labels['setvariable'] = 'Variabele instellen';
$labels['setvarname'] = 'Naam variabele:';
$labels['setvarvalue'] = 'Waarde:';
$labels['setvarmodifiers'] = 'Waarde wijzigen:';
$labels['varlower'] = 'kleine letters';
$labels['varupper'] = 'hoofdletters';
$labels['varlowerfirst'] = 'eerste karakter als kleine letter';
$labels['varupperfirst'] = 'eerste karakter als hoofdletter';
$labels['varquotewildcard'] = 'speciale karakters quoten';
$labels['varlength'] = 'lengte';
$labels['notify'] = 'Stuur melding';
-$labels['notifyaddress'] = 'Naar e-mailadres:';
-$labels['notifybody'] = 'Meldingsbericht:';
-$labels['notifysubject'] = 'Onderwerp van melding:';
-$labels['notifyfrom'] = 'Afzender:';
+$labels['notifytarget'] = 'Meldingsdoel:';
+$labels['notifymessage'] = 'Meldingsbericht (optioneel):';
+$labels['notifyoptions'] = 'Meldingsopties (optioneel):';
+$labels['notifyfrom'] = 'Meldingsafzender (optioneel):';
$labels['notifyimportance'] = 'Prioriteit:';
$labels['notifyimportancelow'] = 'laag';
$labels['notifyimportancenormal'] = 'normaal';
$labels['notifyimportancehigh'] = 'hoog';
+$labels['notifymethodmailto'] = 'E-mail';
+$labels['notifymethodtel'] = 'Telefoon';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Filter aanmaken';
$labels['usedata'] = 'Gebruik de volgende gegevens in het filter:';
$labels['nextstep'] = 'Volgende stap';
$labels['...'] = '...';
$labels['currdate'] = 'Huidige datum';
$labels['datetest'] = 'Datum';
$labels['dateheader'] = 'header:';
$labels['year'] = 'jaar';
$labels['month'] = 'maand';
$labels['day'] = 'dag';
$labels['date'] = 'datum (jjjj-mm-dd)';
$labels['julian'] = 'datum (juliaanse kalender)';
$labels['hour'] = 'uur';
$labels['minute'] = 'minuut';
$labels['second'] = 'seconde';
$labels['time'] = 'tijd (uu:mm:ss)';
$labels['iso8601'] = 'datum (ISO-8601)';
$labels['std11'] = 'datum (RFC 2822)';
$labels['zone'] = 'tijdzone';
$labels['weekday'] = 'weekdag (0-6)';
$labels['advancedopts'] = 'Geavanceerde opties';
$labels['body'] = 'Inhoud';
$labels['address'] = 'adres';
$labels['envelope'] = 'envelope';
$labels['modifier'] = 'toets op:';
$labels['text'] = 'tekst';
$labels['undecoded'] = 'undecoded (raw)';
$labels['contenttype'] = 'content type';
$labels['modtype'] = 'type:';
$labels['allparts'] = 'Alle';
$labels['domain'] = 'domein';
$labels['localpart'] = 'lokaal gedeelte';
$labels['user'] = 'gebruiker';
$labels['detail'] = 'detail';
$labels['comparator'] = 'vergelijkingswijze:';
$labels['default'] = 'standaard';
$labels['octet'] = 'strikt (octet)';
$labels['asciicasemap'] = 'hoofdletterongevoelig (ascii-casemap)';
$labels['asciinumeric'] = 'numeriek (ascii-numeriek)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'terugwaarts';
+$labels['vacation'] = 'Vakantie';
+$labels['vacation.reply'] = 'Antwoordbericht';
+$labels['vacation.advanced'] = 'Geavanceerde instellingen';
+$labels['vacation.subject'] = 'Onderwerp';
+$labels['vacation.body'] = 'Inhoud';
+$labels['vacation.start'] = 'Begin van vakantie';
+$labels['vacation.end'] = 'Einde van vakantie';
+$labels['vacation.status'] = 'Status';
+$labels['vacation.on'] = 'Aan';
+$labels['vacation.off'] = 'Uit';
+$labels['vacation.addresses'] = 'Mijn extra e-mailadressen';
+$labels['vacation.interval'] = 'Antwoordinterval';
+$labels['vacation.after'] = 'Voeg een vakantieregel toe na';
+$labels['vacation.saving'] = 'Gegevens worden opgeslagen...';
+$labels['vacation.action'] = 'Actie voor inkomend bericht';
+$labels['vacation.keep'] = 'Bewaren';
+$labels['vacation.discard'] = 'Weggooien';
+$labels['vacation.redirect'] = 'Doorsturen naar';
+$labels['vacation.copy'] = 'Kopie sturen naar';
+$labels['arialabelfiltersetactions'] = 'Filtersetacties';
+$labels['arialabelfilteractions'] = 'Filteracties';
+$labels['arialabelfilterform'] = 'Filtereigenschappen';
+$labels['ariasummaryfilterslist'] = 'Filterlijst';
+$labels['ariasummaryfiltersetslist'] = 'Lijst met filtersets';
+$labels['filterstitle'] = 'Bewerk filters voor inkomende berichten';
+$labels['vacationtitle'] = 'Bewerk vakantieregel';
$messages['filterunknownerror'] = 'Onbekende fout';
$messages['filterconnerror'] = 'Kan geen verbinding maken met de managesieve server';
$messages['filterdeleteerror'] = 'Kan filter niet verwijderen. Er trad een serverfout op.';
$messages['filterdeleted'] = 'Filter succesvol verwijderd';
$messages['filtersaved'] = 'Filter succesvol opgeslagen';
$messages['filtersaveerror'] = 'Kan filter niet opslaan. Er trad een serverfout op.';
$messages['filterdeleteconfirm'] = 'Weet je zeker dat je het geselecteerde filter wilt verwijderen?';
$messages['ruledeleteconfirm'] = 'Weet je zeker dat je de geselecteerde regel wilt verwijderen?';
$messages['actiondeleteconfirm'] = 'Weet je zeker dat je de geselecteerde actie wilt verwijderen?';
$messages['forbiddenchars'] = 'Verboden karakters in het veld';
$messages['cannotbeempty'] = 'Veld mag niet leeg zijn';
$messages['ruleexist'] = 'Er bestaat al een filter met deze naam.';
$messages['setactivateerror'] = 'Filterset kon niet geactiveerd worden. Er trad een serverfout op.';
$messages['setdeactivateerror'] = 'Filterset kon niet gedeactiveerd worden. Er trad een serverfout op.';
$messages['setdeleteerror'] = 'Filterset kon niet verwijderd worden. Er trad een serverfout op.';
$messages['setactivated'] = 'Filterset succesvol geactiveerd.';
$messages['setdeactivated'] = 'Filterset succesvol gedeactiveerd.';
$messages['setdeleted'] = 'Filterset succesvol verwijderd.';
$messages['setdeleteconfirm'] = 'Weet u zeker dat u de geselecteerde filterset wilt verwijderen?';
$messages['setcreateerror'] = 'Filterset kon niet aangemaakt worden. Er trad een serverfout op.';
$messages['setcreated'] = 'Filterset succesvol aangemaakt.';
$messages['activateerror'] = 'Geselecteerde filter(s) konden niet ingeschakeld worden. Er trad een serverfout op.';
$messages['deactivateerror'] = 'Geselecteerde filter(s) konden niet uitgeschakeld worden. Er trad een serverfout op.';
$messages['deactivated'] = 'Filter(s) succesvol ingeschakeld.';
$messages['activated'] = 'Filter(s) succesvol uitgeschakeld.';
$messages['moved'] = 'Filter succesvol verplaatst.';
$messages['moveerror'] = 'Het geselecteerde filter kon niet verplaatst worden. Er trad een serverfout op.';
$messages['nametoolong'] = 'Naam is te lang.';
$messages['namereserved'] = 'Gereserveerde naam.';
$messages['setexist'] = 'Filterset bestaat al.';
$messages['nodata'] = 'Tenminste één positie moet geselecteerd worden!';
$messages['invaliddateformat'] = 'Ongeldige datum of datumformaat';
+$messages['saveerror'] = 'Opslaan van de gegevens is mislukt. Er trad een serverfout op.';
+$messages['vacationsaved'] = 'Vakantiegegevens succesvol opgeslagen.';
+$messages['emptyvacationbody'] = 'Inhoud van vakantiebericht is verplicht!';
?>
diff --git a/lib/plugins/managesieve/localization/pl_PL.inc b/lib/plugins/managesieve/localization/pl_PL.inc
index 2759309..06c0a79 100644
--- a/lib/plugins/managesieve/localization/pl_PL.inc
+++ b/lib/plugins/managesieve/localization/pl_PL.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtry';
$labels['managefilters'] = 'Zarządzanie filtrami poczty przychodzącej';
$labels['filtername'] = 'Nazwa filtru';
$labels['newfilter'] = 'Nowy filtr';
$labels['filteradd'] = 'Dodaj filtr';
$labels['filterdel'] = 'Usuń filtr';
$labels['moveup'] = 'W górę';
$labels['movedown'] = 'W dół';
$labels['filterallof'] = 'spełniających wszystkie poniższe kryteria';
$labels['filteranyof'] = 'spełniających dowolne z poniższych kryteriów';
$labels['filterany'] = 'wszystkich';
$labels['filtercontains'] = 'zawiera';
$labels['filternotcontains'] = 'nie zawiera';
$labels['filteris'] = 'jest równe';
$labels['filterisnot'] = 'nie jest równe';
$labels['filterexists'] = 'istnieje';
$labels['filternotexists'] = 'nie istnieje';
$labels['filtermatches'] = 'pasuje do wyrażenia';
$labels['filternotmatches'] = 'nie pasuje do wyrażenia';
$labels['filterregex'] = 'pasuje do wyrażenia regularnego';
$labels['filternotregex'] = 'nie pasuje do wyrażenia regularnego';
$labels['filterunder'] = 'poniżej';
$labels['filterover'] = 'ponad';
$labels['addrule'] = 'Dodaj regułę';
$labels['delrule'] = 'Usuń regułę';
$labels['messagemoveto'] = 'Przenieś wiadomość do';
$labels['messageredirect'] = 'Przekaż wiadomość na konto';
$labels['messagecopyto'] = 'Skopiuj wiadomość do';
$labels['messagesendcopy'] = 'Wyślij kopię do';
$labels['messagereply'] = 'Odpowiedz wiadomością o treści';
$labels['messagedelete'] = 'Usuń wiadomość';
$labels['messagediscard'] = 'Odrzuć z komunikatem';
$labels['messagekeep'] = 'Zachowaj wiadomość w Odebranych';
$labels['messagesrules'] = 'W stosunku do przychodzącej poczty:';
$labels['messagesactions'] = '...wykonaj następujące czynności:';
$labels['add'] = 'Dodaj';
$labels['del'] = 'Usuń';
$labels['sender'] = 'Nadawca';
$labels['recipient'] = 'Odbiorca';
$labels['vacationaddr'] = 'Moje dodatkowe adresy email:';
$labels['vacationdays'] = 'Częstotliwość wysyłania wiadomości (w dniach):';
$labels['vacationinterval'] = 'Jak często wysyłać wiadomości:';
-$labels['days'] = 'dni';
-$labels['seconds'] = 'sekundy';
$labels['vacationreason'] = 'Treść (przyczyna nieobecności):';
$labels['vacationsubject'] = 'Temat wiadomości:';
+$labels['days'] = 'dni';
+$labels['seconds'] = 'sekundy';
$labels['rulestop'] = 'Przerwij przetwarzanie reguł';
$labels['enable'] = 'Włącz/Wyłącz';
$labels['filterset'] = 'Zbiór filtrów';
$labels['filtersets'] = 'Zbiory fitrów';
$labels['filtersetadd'] = 'Dodaj zbiór filtrów';
$labels['filtersetdel'] = 'Usuń bieżący zbiór filtrów';
$labels['filtersetact'] = 'Aktywuj bieżący zbiór filtrów';
$labels['filtersetdeact'] = 'Deaktywuj bieżący zbiór filtrów';
$labels['filterdef'] = 'Definicja filtra';
$labels['filtersetname'] = 'Nazwa zbioru';
$labels['newfilterset'] = 'Nowy zbiór filtrów';
$labels['active'] = 'aktywny';
$labels['none'] = 'brak';
$labels['fromset'] = 'ze zbioru';
$labels['fromfile'] = 'z pliku';
$labels['filterdisabled'] = 'Filtr wyłączony';
$labels['countisgreaterthan'] = 'ilość jest większa od';
$labels['countisgreaterthanequal'] = 'ilość jest równa lub większa od';
$labels['countislessthan'] = 'ilość jest mniejsza od';
$labels['countislessthanequal'] = 'ilość jest równa lub mniejsza od';
$labels['countequals'] = 'ilość jest równa';
$labels['countnotequals'] = 'ilość nie jest równa';
$labels['valueisgreaterthan'] = 'wartość jest większa od';
$labels['valueisgreaterthanequal'] = 'wartość jest równa lub większa od';
$labels['valueislessthan'] = 'wartość jest mniejsza od';
$labels['valueislessthanequal'] = 'wartość jest równa lub mniejsza od';
$labels['valueequals'] = 'wartość jest równa';
$labels['valuenotequals'] = 'wartość nie jest równa';
$labels['setflags'] = 'Ustaw flagi wiadomości';
$labels['addflags'] = 'Dodaj flagi do wiadomości';
$labels['removeflags'] = 'Usuń flagi wiadomości';
$labels['flagread'] = 'Przeczytana';
$labels['flagdeleted'] = 'Usunięta';
$labels['flaganswered'] = 'Z odpowiedzią';
$labels['flagflagged'] = 'Oflagowana';
$labels['flagdraft'] = 'Szkic';
$labels['setvariable'] = 'Ustaw zmienną';
$labels['setvarname'] = 'Nazwa zmiennej:';
$labels['setvarvalue'] = 'Wartość zmiennej:';
$labels['setvarmodifiers'] = 'Modyfikatory:';
$labels['varlower'] = 'małe litery';
$labels['varupper'] = 'wielkie litery';
$labels['varlowerfirst'] = 'pierwsza mała litera';
$labels['varupperfirst'] = 'pierwsza wielka litera';
$labels['varquotewildcard'] = 'zamień znaki specjalne';
$labels['varlength'] = 'długość';
$labels['notify'] = 'Wyślij powiadomienie';
-$labels['notifyaddress'] = 'Na adres e-mail:';
-$labels['notifybody'] = 'Treść powiadomienia:';
-$labels['notifysubject'] = 'Tytuł powiadomienia:';
-$labels['notifyfrom'] = 'Nadawca powiadomienia:';
+$labels['notifytarget'] = 'Odbiorca powiadomienia:';
+$labels['notifymessage'] = 'Wiadomość powiadomienia (opcjonalne):';
+$labels['notifyoptions'] = 'Opcje powiadomienia (opcjonalne):';
+$labels['notifyfrom'] = 'Nadawca powiadomienia (opcjonalne):';
$labels['notifyimportance'] = 'Priorytet:';
$labels['notifyimportancelow'] = 'niski';
$labels['notifyimportancenormal'] = 'normalny';
$labels['notifyimportancehigh'] = 'wysoki';
+$labels['notifymethodmailto'] = 'E-Mail';
+$labels['notifymethodtel'] = 'Telefon';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Utwórz filtr';
$labels['usedata'] = 'Użyj następujących danych do utworzenia filtra:';
$labels['nextstep'] = 'Następny krok';
$labels['...'] = '...';
$labels['currdate'] = 'Bieżąca data';
$labels['datetest'] = 'Data';
$labels['dateheader'] = 'nagłówek:';
$labels['year'] = 'rok';
$labels['month'] = 'miesiąc';
$labels['day'] = 'dzień';
$labels['date'] = 'data (rrrr-mm-dd)';
$labels['julian'] = 'data (kalendarz juliański)';
$labels['hour'] = 'godzina';
$labels['minute'] = 'minuta';
$labels['second'] = 'sekunda';
$labels['time'] = 'czas (gg:mm:ss)';
$labels['iso8601'] = 'data (ISO8601)';
$labels['std11'] = 'data (RFC2822)';
$labels['zone'] = 'Strefa czasowa';
$labels['weekday'] = 'dzień tygodnia (0-6)';
$labels['advancedopts'] = 'Zaawansowane opcje';
$labels['body'] = 'Treść';
$labels['address'] = 'adres';
$labels['envelope'] = 'koperta (envelope)';
$labels['modifier'] = 'modyfikator:';
$labels['text'] = 'tekst';
$labels['undecoded'] = 'nie (raw)';
$labels['contenttype'] = 'typ części (content type)';
$labels['modtype'] = 'typ:';
$labels['allparts'] = 'wszystkie';
$labels['domain'] = 'domena';
$labels['localpart'] = 'część lokalna';
$labels['user'] = 'użytkownik';
$labels['detail'] = 'detal';
$labels['comparator'] = 'komparator:';
$labels['default'] = 'domyślny';
$labels['octet'] = 'dokładny (octet)';
$labels['asciicasemap'] = 'nierozróżniający wielkości liter (ascii-casemap)';
$labels['asciinumeric'] = 'numeryczny (ascii-numeric)';
$labels['index'] = 'indeks:';
$labels['indexlast'] = 'wstecz';
+$labels['vacation'] = 'Nieobecność';
+$labels['vacation.reply'] = 'Odpowiedź';
+$labels['vacation.advanced'] = 'Ustawienia zaawansowane';
+$labels['vacation.subject'] = 'Temat';
+$labels['vacation.body'] = 'Treść';
+$labels['vacation.start'] = 'Początek nieobecności';
+$labels['vacation.end'] = 'Koniec nieobecności';
+$labels['vacation.status'] = 'Status';
+$labels['vacation.on'] = 'włączone';
+$labels['vacation.off'] = 'wyłączone';
+$labels['vacation.addresses'] = 'Moje dodatkowe adresy';
+$labels['vacation.interval'] = 'Częstotliwość odpowiedzi';
+$labels['vacation.after'] = 'Umieść regułę odpowiedzi po';
+$labels['vacation.saving'] = 'Zapisywanie danych...';
+$labels['vacation.action'] = 'Akcje wiadomości przychodzących';
+$labels['vacation.keep'] = 'Zachowaj';
+$labels['vacation.discard'] = 'Odrzuć';
+$labels['vacation.redirect'] = 'Przekaż do';
+$labels['vacation.copy'] = 'Wyślij kopię do';
+$labels['arialabelfiltersetactions'] = 'Zbiór filtrów akcji';
+$labels['arialabelfilteractions'] = 'Akcje filtrów';
+$labels['arialabelfilterform'] = 'Ustawienia filtrów';
+$labels['ariasummaryfilterslist'] = 'Spis filtrów';
+$labels['ariasummaryfiltersetslist'] = 'Lista zbiorów filtrów';
+$labels['filterstitle'] = 'Zarządzaj filtrami wiadomości przychodzących';
+$labels['vacationtitle'] = 'Zarządzaj asystentem nieobecności';
$messages['filterunknownerror'] = 'Nieznany błąd serwera.';
$messages['filterconnerror'] = 'Nie można nawiązać połączenia z serwerem.';
$messages['filterdeleteerror'] = 'Nie można usunąć filtra. Błąd serwera.';
$messages['filterdeleted'] = 'Filtr został usunięty pomyślnie.';
$messages['filtersaved'] = 'Filtr został zapisany pomyślnie.';
$messages['filtersaveerror'] = 'Nie można zapisać filtra. Wystąpił błąd serwera.';
$messages['filterdeleteconfirm'] = 'Czy na pewno chcesz usunąć wybrany filtr?';
$messages['ruledeleteconfirm'] = 'Czy na pewno chcesz usunąć wybraną regułę?';
$messages['actiondeleteconfirm'] = 'Czy na pewno usunąć wybraną akcję?';
$messages['forbiddenchars'] = 'Pole zawiera niedozwolone znaki.';
$messages['cannotbeempty'] = 'Pole nie może być puste.';
$messages['ruleexist'] = 'Filtr o podanej nazwie już istnieje.';
$messages['setactivateerror'] = 'Nie można aktywować wybranego zbioru filtrów. Błąd serwera.';
$messages['setdeactivateerror'] = 'Nie można deaktywować wybranego zbioru filtrów. Błąd serwera.';
$messages['setdeleteerror'] = 'Nie można usunąć wybranego zbioru filtrów. Błąd serwera.';
$messages['setactivated'] = 'Zbiór filtrów został aktywowany pomyślnie.';
$messages['setdeactivated'] = 'Zbiór filtrów został deaktywowany pomyślnie.';
$messages['setdeleted'] = 'Zbiór filtrów został usunięty pomyślnie.';
$messages['setdeleteconfirm'] = 'Czy na pewno chcesz usunąć wybrany zbiór filtrów?';
$messages['setcreateerror'] = 'Nie można utworzyć zbioru filtrów. Błąd serwera.';
$messages['setcreated'] = 'Zbiór filtrów został utworzony pomyślnie.';
$messages['activateerror'] = 'Nie można włączyć wybranych filtrów. Błąd serwera.';
$messages['deactivateerror'] = 'Nie można wyłączyć wybranych filtrów. Błąd serwera.';
$messages['deactivated'] = 'Filtr(y) włączono pomyślnie.';
$messages['activated'] = 'Filtr(y) wyłączono pomyślnie.';
$messages['moved'] = 'Filter został przeniesiony pomyślnie.';
$messages['moveerror'] = 'Nie można przenieść wybranego filtra. Błąd serwera.';
$messages['nametoolong'] = 'Zbyt długa nazwa.';
$messages['namereserved'] = 'Nazwa zarezerwowana.';
$messages['setexist'] = 'Zbiór już istnieje.';
$messages['nodata'] = 'Należy wybrać co najmniej jedną pozycję!';
$messages['invaliddateformat'] = 'Nieprawidłowy format daty lub fragmentu daty';
+$messages['saveerror'] = 'Nie można zapisać danych. Wystąpił błąd serwera.';
+$messages['vacationsaved'] = 'Dane nieobecności zapisano pomyślnie.';
+$messages['emptyvacationbody'] = 'Treść wiadomości jest wymagana!';
?>
diff --git a/lib/plugins/managesieve/localization/pt_BR.inc b/lib/plugins/managesieve/localization/pt_BR.inc
index 063c60c..b0ccaf6 100644
--- a/lib/plugins/managesieve/localization/pt_BR.inc
+++ b/lib/plugins/managesieve/localization/pt_BR.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtros';
$labels['managefilters'] = 'Gerenciar filtros de entrada de e-mail';
$labels['filtername'] = 'Nome do filtro';
$labels['newfilter'] = 'Novo filtro';
$labels['filteradd'] = 'Adicionar filtro';
$labels['filterdel'] = 'Excluir filtro';
$labels['moveup'] = 'Mover para cima';
$labels['movedown'] = 'Mover para baixo';
$labels['filterallof'] = 'casando todas as seguintes regras';
$labels['filteranyof'] = 'casando qualquer das seguintes regras';
$labels['filterany'] = 'todas as mensagens';
$labels['filtercontains'] = 'contem';
$labels['filternotcontains'] = 'não contem';
$labels['filteris'] = 'é igual a';
$labels['filterisnot'] = 'não é igual a';
$labels['filterexists'] = 'existe';
$labels['filternotexists'] = 'não existe';
$labels['filtermatches'] = 'expressão combina';
$labels['filternotmatches'] = 'expressão não combina';
$labels['filterregex'] = 'combina com expressão regular';
$labels['filternotregex'] = 'não combina com a expressão regular';
$labels['filterunder'] = 'inferior a';
$labels['filterover'] = 'superior a';
$labels['addrule'] = 'Adicionar regra';
$labels['delrule'] = 'Excluir regra';
$labels['messagemoveto'] = 'Mover mensagem para';
$labels['messageredirect'] = 'Redirecionar mensagem para';
$labels['messagecopyto'] = 'Copiar mensagem para';
$labels['messagesendcopy'] = 'Enviar cópia da mensagem para';
$labels['messagereply'] = 'Responder com mensagem';
$labels['messagedelete'] = 'Excluir mensagem';
$labels['messagediscard'] = 'Descartar com mensagem';
$labels['messagekeep'] = 'Manter mensagens na caixa';
$labels['messagesrules'] = 'Para e-mails recebidos:';
$labels['messagesactions'] = '...execute as seguintes ações:';
$labels['add'] = 'Adicionar';
$labels['del'] = 'Excluir';
$labels['sender'] = 'Remetente';
$labels['recipient'] = 'Destinatário';
$labels['vacationaddr'] = 'Meu endereço de e-mail adicional:';
$labels['vacationdays'] = 'Enviar mensagens com que frequência (em dias):';
$labels['vacationinterval'] = 'Como geralmente enviam mensagens:';
-$labels['days'] = 'dias';
-$labels['seconds'] = 'segundos';
$labels['vacationreason'] = 'Corpo da mensagem (motivo de férias):';
$labels['vacationsubject'] = 'Título da mensagem:';
+$labels['days'] = 'dias';
+$labels['seconds'] = 'segundos';
$labels['rulestop'] = 'Parar de avaliar regras';
$labels['enable'] = 'Habilitar/Desabilitar';
$labels['filterset'] = 'Conjunto de filtros';
$labels['filtersets'] = 'Conjuntos de filtro';
$labels['filtersetadd'] = 'Adicionar conjunto de filtros';
$labels['filtersetdel'] = 'Excluir conjunto de filtros atual';
$labels['filtersetact'] = 'Ativar conjunto de filtros atual';
$labels['filtersetdeact'] = 'Desativar conjunto de filtros atual';
$labels['filterdef'] = 'Definição de filtro';
$labels['filtersetname'] = 'Nome do conjunto de filtros';
$labels['newfilterset'] = 'Novo conjunto de filtros';
$labels['active'] = 'ativo';
$labels['none'] = 'nenhum';
$labels['fromset'] = 'Do conjunto';
$labels['fromfile'] = 'Do arquivo';
$labels['filterdisabled'] = 'Filtro desativado';
$labels['countisgreaterthan'] = 'contagem é maior que';
$labels['countisgreaterthanequal'] = 'contagem é maior ou igual a';
$labels['countislessthan'] = 'contagem é menor que';
$labels['countislessthanequal'] = 'contagem é menor ou igual a';
$labels['countequals'] = 'contagem é igual a';
$labels['countnotequals'] = 'contagem não é igual a';
$labels['valueisgreaterthan'] = 'valor é maior que';
$labels['valueisgreaterthanequal'] = 'valor é maior ou igual a';
$labels['valueislessthan'] = 'valor é menor que';
$labels['valueislessthanequal'] = 'valor é menor ou igual a';
$labels['valueequals'] = 'valor é igual a';
$labels['valuenotequals'] = 'valor não é igual a';
$labels['setflags'] = 'Definir marcadores à mensagem';
$labels['addflags'] = 'Adicionar marcadores à mensagem';
$labels['removeflags'] = 'Remover marcadores da mensagem';
$labels['flagread'] = 'Lida';
$labels['flagdeleted'] = 'Excluída';
$labels['flaganswered'] = 'Respondida';
$labels['flagflagged'] = 'Marcada';
$labels['flagdraft'] = 'Rascunho';
$labels['setvariable'] = 'Definir variável';
$labels['setvarname'] = 'Nome da variável:';
$labels['setvarvalue'] = 'Valor da variável:';
$labels['setvarmodifiers'] = 'Modificadores:';
$labels['varlower'] = 'minúsculas';
$labels['varupper'] = 'maiúsculas';
$labels['varlowerfirst'] = 'primeiro caractere minúsculo';
$labels['varupperfirst'] = 'primeiro caractere maiúsculo';
$labels['varquotewildcard'] = 'caracteres especiais de citação';
$labels['varlength'] = 'tamanho';
$labels['notify'] = 'Enviar notificação';
-$labels['notifyaddress'] = 'Para endereço de e-mail:';
-$labels['notifybody'] = 'Corpo da notificação:';
-$labels['notifysubject'] = 'Título da notificação:';
-$labels['notifyfrom'] = 'Remetente da notificação:';
+$labels['notifytarget'] = 'Destino da notificação:';
+$labels['notifymessage'] = 'Mensagem de notificação (opcional):';
+$labels['notifyoptions'] = 'Opções de notificação (opcional):';
+$labels['notifyfrom'] = 'Remetente da notificação (opcional):';
$labels['notifyimportance'] = 'Importância';
$labels['notifyimportancelow'] = 'baixa';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'alta';
+$labels['notifymethodmailto'] = 'Email';
+$labels['notifymethodtel'] = 'Telefone';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Criar filtro';
$labels['usedata'] = 'Usar os seguintes dados no filtro:';
$labels['nextstep'] = 'Próximo Passo';
$labels['...'] = '...';
$labels['currdate'] = 'Data atual';
$labels['datetest'] = 'Data';
$labels['dateheader'] = 'cabeçalho:';
$labels['year'] = 'ano';
$labels['month'] = 'mês';
$labels['day'] = 'dia';
$labels['date'] = 'data (aaaa-mm-dd)';
$labels['julian'] = 'data (calendário juliano)';
$labels['hour'] = 'hora';
$labels['minute'] = 'minuto';
$labels['second'] = 'segundo';
$labels['time'] = 'hora (hh:mm:ss)';
$labels['iso8601'] = 'data (ISO8601)';
$labels['std11'] = 'data (RFC2822)';
$labels['zone'] = 'fuso horário';
$labels['weekday'] = 'dia da semana (0-6)';
$labels['advancedopts'] = 'Opções avançadas';
$labels['body'] = 'Corpo';
$labels['address'] = 'endereço';
$labels['envelope'] = 'envelope';
$labels['modifier'] = 'modificador:';
$labels['text'] = 'texto';
$labels['undecoded'] = 'decodificado (bruto)';
$labels['contenttype'] = 'tipo de conteúdo';
$labels['modtype'] = 'tipo:';
$labels['allparts'] = 'todas';
$labels['domain'] = 'domínio';
$labels['localpart'] = 'parte local';
$labels['user'] = 'usuário';
$labels['detail'] = 'detalhes';
$labels['comparator'] = 'comparador:';
$labels['default'] = 'padrão';
$labels['octet'] = 'estrito (octeto)';
$labels['asciicasemap'] = 'caso insensível (mapa de caracteres ascii)';
$labels['asciinumeric'] = 'numérico (ascii-numeric)';
$labels['index'] = 'índice:';
$labels['indexlast'] = 'retroceder';
+$labels['vacation'] = 'Férias';
+$labels['vacation.reply'] = 'Responder mensagem';
+$labels['vacation.advanced'] = 'Opções avançadas';
+$labels['vacation.subject'] = 'Assunto';
+$labels['vacation.body'] = 'Conteúdo';
+$labels['vacation.start'] = 'Início das férias';
+$labels['vacation.end'] = 'Término das férias';
+$labels['vacation.status'] = 'Estado';
+$labels['vacation.on'] = 'Ligado';
+$labels['vacation.off'] = 'Desligado';
+$labels['vacation.addresses'] = 'Meus endereços de e-mail adicionais';
+$labels['vacation.interval'] = 'Intervalo de resposta';
+$labels['vacation.after'] = 'Colocar regra de férias após';
+$labels['vacation.saving'] = 'Salvando dados...';
+$labels['vacation.action'] = 'Ações para mensagens recebidas';
+$labels['vacation.keep'] = 'Manter';
+$labels['vacation.discard'] = 'Descartar';
+$labels['vacation.redirect'] = 'Redirecionar para';
+$labels['vacation.copy'] = 'Enviar cópia para';
+$labels['arialabelfiltersetactions'] = 'Ações do grupo de filtros';
+$labels['arialabelfilteractions'] = 'Ações do filtro';
+$labels['arialabelfilterform'] = 'Propriedades do filtro';
+$labels['ariasummaryfilterslist'] = 'Lista dos filtros';
+$labels['ariasummaryfiltersetslist'] = 'Lista de grupo de filtros';
+$labels['filterstitle'] = 'Editar filtro dos e-mails recebidos';
+$labels['vacationtitle'] = 'Editar regra de ausência';
$messages['filterunknownerror'] = 'Erro desconhecido de servidor';
$messages['filterconnerror'] = 'Não foi possível conectar ao servidor managesieve';
$messages['filterdeleteerror'] = 'Impossível excluir o filtro. Ocorreu um erro no servidor.';
$messages['filterdeleted'] = 'Filtro excluído com sucesso';
$messages['filtersaved'] = 'Filtro gravado com sucesso';
$messages['filtersaveerror'] = 'Impossível salvar o filtro. Ocorreu um erro no servidor.';
$messages['filterdeleteconfirm'] = 'Deseja realmente excluir o filtro selecionado?';
$messages['ruledeleteconfirm'] = 'Deseja realmente excluir a regra selecionada?';
$messages['actiondeleteconfirm'] = 'Deseja realmente excluir a ação selecionada?';
$messages['forbiddenchars'] = 'Caracteres não permitidos no campo';
$messages['cannotbeempty'] = 'Campo não pode ficar em branco';
$messages['ruleexist'] = 'O filtro com o nome especificado já existe.';
$messages['setactivateerror'] = 'Impossível ativar o conjunto de filtros selecionados. Ocorreu um erro no servidor.';
$messages['setdeactivateerror'] = 'Impossível desativar o conjunto de filtros selecionados. Ocorreu um erro no servidor.';
$messages['setdeleteerror'] = 'Impossível excluir o conjunto de filtros selecionados. Ocorreu um erro no servidor.';
$messages['setactivated'] = 'Conjunto de filtros ativados com sucesso.';
$messages['setdeactivated'] = 'Conjunto de filtros desativados com sucesso.';
$messages['setdeleted'] = 'Conjunto de filtros excluídos com sucesso.';
$messages['setdeleteconfirm'] = 'Você está certo que deseja excluir o conjunto de filtros selecionados?';
$messages['setcreateerror'] = 'Impossível criar o conjunto de filtros. Ocorreu um erro no servidor.';
$messages['setcreated'] = 'Conjunto de filtros criado com sucesso.';
$messages['activateerror'] = 'Impossível habilitar o(s) filtro(s) selecionado(s). Ocorreu um erro no servidor.';
$messages['deactivateerror'] = 'Impossível desabilitar o(s) filtro(s) selecionado(s). Ocorreu um erro no servidor.';
$messages['deactivated'] = 'Filtro(s) habilitado(s) com sucesso.';
$messages['activated'] = 'Filtro(s) desabilitado(s) com sucesso.';
$messages['moved'] = 'Filtro movido com sucesso.';
$messages['moveerror'] = 'Impossível mover o filtro selecionado. Ocorreu um erro no servidor.';
$messages['nametoolong'] = 'Nome muito longo.';
$messages['namereserved'] = 'Nome reservado.';
$messages['setexist'] = 'Conjunto já existe.';
$messages['nodata'] = 'Pelo menos uma posição precisa ser selecionada!';
$messages['invaliddateformat'] = 'Data inválida';
+$messages['saveerror'] = 'Impossível salvar dados. Ocorreu um erro no servidor.';
+$messages['vacationsaved'] = 'Dados de férias salvos com sucesso.';
+$messages['emptyvacationbody'] = 'Conteúdo da mensagem de férias necessário!';
?>
diff --git a/lib/plugins/managesieve/localization/pt_PT.inc b/lib/plugins/managesieve/localization/pt_PT.inc
index 4e93076..ec39542 100644
--- a/lib/plugins/managesieve/localization/pt_PT.inc
+++ b/lib/plugins/managesieve/localization/pt_PT.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtros';
$labels['managefilters'] = 'Gerir filtros';
$labels['filtername'] = 'Nome do filtro';
$labels['newfilter'] = 'Novo filtro';
$labels['filteradd'] = 'Adicionar filtro';
$labels['filterdel'] = 'Eliminar filtro';
$labels['moveup'] = 'Mover para cima';
$labels['movedown'] = 'Mover para baixo';
$labels['filterallof'] = 'corresponde a todas as seguintes regras';
$labels['filteranyof'] = 'corresponde a uma das seguintes regras';
$labels['filterany'] = 'todas as mensagens';
$labels['filtercontains'] = 'contém';
$labels['filternotcontains'] = 'não contém';
$labels['filteris'] = 'é igual a';
$labels['filterisnot'] = 'é diferente de';
$labels['filterexists'] = 'existe';
$labels['filternotexists'] = 'não existe';
$labels['filtermatches'] = 'expressão corresponde';
$labels['filternotmatches'] = 'expressão não corresponde';
$labels['filterregex'] = 'corresponde à expressão';
$labels['filternotregex'] = 'não corresponde à expressão';
$labels['filterunder'] = 'é inferior a';
$labels['filterover'] = 'é superior a';
$labels['addrule'] = 'Adicionar regra';
$labels['delrule'] = 'Eliminar regra';
$labels['messagemoveto'] = 'Mover mensagem para';
$labels['messageredirect'] = 'Redirecionar mensagem para';
$labels['messagecopyto'] = 'Copiar mensagem para';
$labels['messagesendcopy'] = 'Enviar cópia da mensagem para';
$labels['messagereply'] = 'Responder com a mensagem';
$labels['messagedelete'] = 'Eliminar mensagem';
$labels['messagediscard'] = 'Rejeitar mensagem';
$labels['messagekeep'] = 'Manter mensagem na Caixa de entrada';
$labels['messagesrules'] = 'Regras para Filtros';
$labels['messagesactions'] = 'Acções para Filtros';
$labels['add'] = 'Adicionar';
$labels['del'] = 'Eliminar';
$labels['sender'] = 'Remetente';
$labels['recipient'] = 'Destinatário';
$labels['vacationaddr'] = 'Os meus endereços de e-mail adicionais:';
$labels['vacationdays'] = 'Enviar mensagens com que frequência (em dias):';
$labels['vacationinterval'] = 'Com que frequência envia mensagens:';
-$labels['days'] = 'dias';
-$labels['seconds'] = 'segundos';
$labels['vacationreason'] = 'Conteúdo da mensagem (motivo da ausência):';
$labels['vacationsubject'] = 'Assunto da mensagem:';
+$labels['days'] = 'dias';
+$labels['seconds'] = 'segundos';
$labels['rulestop'] = 'Parar de avaliar regras';
$labels['enable'] = 'Activar/Desactivar';
$labels['filterset'] = 'Filtros definidos';
$labels['filtersets'] = 'Filtros definidos';
$labels['filtersetadd'] = 'Adicionar definição de filtros';
$labels['filtersetdel'] = 'Eliminar definição de filtros actuais';
$labels['filtersetact'] = 'Activar definição de filtros actuais';
$labels['filtersetdeact'] = 'Desactivar definição de filtros actuais';
$labels['filterdef'] = 'Definição de filtros';
$labels['filtersetname'] = 'Nome da definição de filtros';
$labels['newfilterset'] = 'Nova definição de filtros';
$labels['active'] = 'activo';
$labels['none'] = 'nehnum';
$labels['fromset'] = 'definição de';
$labels['fromfile'] = 'a partir do ficheiro';
$labels['filterdisabled'] = 'Filtro desactivado';
$labels['countisgreaterthan'] = 'contagem é maior que';
$labels['countisgreaterthanequal'] = 'contagem é maior ou igual a';
$labels['countislessthan'] = 'contagem é menor que';
$labels['countislessthanequal'] = 'contagem é menor ou igual a';
$labels['countequals'] = 'contagem é igual a';
$labels['countnotequals'] = 'a contagem não é igual a';
$labels['valueisgreaterthan'] = 'valor é maior que';
$labels['valueisgreaterthanequal'] = 'valor é maior ou igual a';
$labels['valueislessthan'] = 'valor é menor que';
$labels['valueislessthanequal'] = 'valor é menor ou igual a';
$labels['valueequals'] = 'valor é igual a';
$labels['valuenotequals'] = 'o valor não é igual a';
$labels['setflags'] = 'Definir indicadores para a mensagem';
$labels['addflags'] = 'Adicionar indicadores para a mensagem';
$labels['removeflags'] = 'Eliminar indicadores da mensagem';
$labels['flagread'] = 'Lida';
$labels['flagdeleted'] = 'Eliminada';
$labels['flaganswered'] = 'Respondida';
$labels['flagflagged'] = 'Marcada';
$labels['flagdraft'] = 'Rascunho';
$labels['setvariable'] = 'Definir variável';
$labels['setvarname'] = 'Nome da Variável:';
$labels['setvarvalue'] = 'Valor da Variável:';
$labels['setvarmodifiers'] = 'Modificadores:';
$labels['varlower'] = 'minúscula';
$labels['varupper'] = 'maiúscula';
$labels['varlowerfirst'] = 'primeira letra em minúscula';
$labels['varupperfirst'] = 'primeira letra em maiúscula';
$labels['varquotewildcard'] = 'citar caracteres especiais';
$labels['varlength'] = 'tamanho';
$labels['notify'] = 'Enviar notificação';
-$labels['notifyaddress'] = 'Endereço de E-mail to:';
-$labels['notifybody'] = 'Corpo de Notificação:';
-$labels['notifysubject'] = 'Assunto Notificação:';
-$labels['notifyfrom'] = 'Remetente Notificação:';
+$labels['notifytarget'] = 'Destino da notificação:';
+$labels['notifymessage'] = 'Mensagem de notificação (opcional):';
+$labels['notifyoptions'] = 'Opções de notificação (opcional):';
+$labels['notifyfrom'] = 'Remetente da notificação (opcional):';
$labels['notifyimportance'] = 'Importância:';
$labels['notifyimportancelow'] = 'baixa';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'alta';
+$labels['notifymethodmailto'] = 'Email';
+$labels['notifymethodtel'] = 'Telefone';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Criar filtro';
$labels['usedata'] = 'Usar os seguintes dados no filtro:';
$labels['nextstep'] = 'Próximo passo';
$labels['...'] = '...';
$labels['currdate'] = 'Data atual';
$labels['datetest'] = 'Data';
$labels['dateheader'] = 'cabeçalho:';
$labels['year'] = 'ano';
$labels['month'] = 'mês';
$labels['day'] = 'dia';
$labels['date'] = 'data (yyyy-mm-dd)';
$labels['julian'] = 'data (juliano)';
$labels['hour'] = 'hora';
$labels['minute'] = 'minuto';
$labels['second'] = 'segundo';
$labels['time'] = 'hora (hh:mm:ss)';
$labels['iso8601'] = 'data (ISO8601)';
$labels['std11'] = 'data (RFC2822)';
$labels['zone'] = 'fuso horário';
$labels['weekday'] = 'dia da semana (0-6)';
$labels['advancedopts'] = 'Opções avançadas';
$labels['body'] = 'Corpo';
$labels['address'] = 'endereço';
$labels['envelope'] = 'envelope';
$labels['modifier'] = 'modificador:';
$labels['text'] = 'Texto';
$labels['undecoded'] = 'não descodificado (raw)';
$labels['contenttype'] = 'tipo de conteúdo';
$labels['modtype'] = 'tipo:';
$labels['allparts'] = 'todos';
$labels['domain'] = 'domínio';
$labels['localpart'] = 'parte local';
$labels['user'] = 'utilizador';
$labels['detail'] = 'detalhe';
$labels['comparator'] = 'Comparador';
$labels['default'] = 'predefinido';
$labels['octet'] = 'estrito (octeto)';
$labels['asciicasemap'] = 'não sensível a maiúsculas/minúsculas (caracteres ascii)';
$labels['asciinumeric'] = 'numérico (numérico ascii)';
$labels['index'] = 'índice:';
$labels['indexlast'] = 'retroceder';
+$labels['vacation'] = 'Férias';
+$labels['vacation.reply'] = 'Mensagem de resposta';
+$labels['vacation.advanced'] = 'Definições avançadas';
+$labels['vacation.subject'] = 'Assunto';
+$labels['vacation.body'] = 'Corpo da mensagem';
+$labels['vacation.start'] = 'Início de férias';
+$labels['vacation.end'] = 'Fim de férias';
+$labels['vacation.status'] = 'Estado';
+$labels['vacation.on'] = 'Ligar';
+$labels['vacation.off'] = 'Desligar';
+$labels['vacation.addresses'] = 'Meus endereços adicionais';
+$labels['vacation.interval'] = 'Intervalo de resposta';
+$labels['vacation.after'] = 'Coloque regra de férias depois';
+$labels['vacation.saving'] = 'A guardar dados...';
+$labels['vacation.action'] = 'Acção para mensagem recebida';
+$labels['vacation.keep'] = 'Manter';
+$labels['vacation.discard'] = 'Rejeitar';
+$labels['vacation.redirect'] = 'Redireccionar para';
+$labels['vacation.copy'] = 'Enviar cópia para';
+$labels['arialabelfiltersetactions'] = 'Acções do conjunto de filtros';
+$labels['arialabelfilteractions'] = 'Acções dos filtros';
+$labels['arialabelfilterform'] = 'Propriedades dos filtro';
+$labels['ariasummaryfilterslist'] = 'Lista de filtros';
+$labels['ariasummaryfiltersetslist'] = 'Lista de conjuntos de filtros';
+$labels['filterstitle'] = 'Editar filtros de mensagens recebidas';
+$labels['vacationtitle'] = 'Editar regra de ausência do escritório';
$messages['filterunknownerror'] = 'Erro de servidor desconhecido';
$messages['filterconnerror'] = 'Não é possível ligar ao servidor Sieve';
$messages['filterdeleteerror'] = 'Não foi possível eliminar o filtro. Ocorreu um erro no servidor.';
$messages['filterdeleted'] = 'Filtro eliminado com sucesso';
$messages['filtersaved'] = 'Filtro guardado com sucesso';
$messages['filtersaveerror'] = 'Não foi possível guardar o filtro. Ocorreu um erro no servidor.';
$messages['filterdeleteconfirm'] = 'Tem a certeza que pretende eliminar este filtro?';
$messages['ruledeleteconfirm'] = 'Tem a certeza que pretende eliminar esta regra?';
$messages['actiondeleteconfirm'] = 'Tem a certeza que pretende eliminar esta acção?';
$messages['forbiddenchars'] = 'Caracteres inválidos no campo.';
$messages['cannotbeempty'] = 'Este campo não pode estar vazio.';
$messages['ruleexist'] = 'Já existe um Filtro com o nome especificado.';
$messages['setactivateerror'] = 'Não foi possível ativar os filtros selecionados. Ocorreu um erro no servidor.';
$messages['setdeactivateerror'] = 'Não foi possível desativar os filtros selecionados. Ocorreu um erro no servidor.';
$messages['setdeleteerror'] = 'Não foi possível eliminar os filtros selecionados. Ocorreu um erro no servidor.';
$messages['setactivated'] = 'Filtros ativados com sucesso.';
$messages['setdeactivated'] = 'Filtros desativados com sucesso.';
$messages['setdeleted'] = 'Filtros eliminados com sucesso.';
$messages['setdeleteconfirm'] = 'Tem a certeza que pretende eliminar os filtros selecionados?';
$messages['setcreateerror'] = 'Não foi possível criar o conjunto de filtros. Ocorreu um erro no servidor.';
$messages['setcreated'] = 'Conjunto de filtros criado com sucesso.';
$messages['activateerror'] = 'Não foi possível ativar os filtros selecionados. Ocorreu um erro no servidor.';
$messages['deactivateerror'] = 'Não foi possível desativar os filtros selecionados. Ocorreu um erro no servidor.';
$messages['deactivated'] = 'Filtro(s) ativado(s) com sucesso.';
$messages['activated'] = 'Filtro(s) desativado(s) com sucesso.';
$messages['moved'] = 'Filtro movido com sucesso.';
$messages['moveerror'] = 'Não foi possível mover o filtro selecionado. Ocorreu um erro no servidor.';
$messages['nametoolong'] = 'Nome demasiado longo.';
$messages['namereserved'] = 'Nome invertido.';
$messages['setexist'] = 'O conjunto já existe.';
$messages['nodata'] = 'Deve selecionar pelo menos uma posição.';
$messages['invaliddateformat'] = 'Data ou formato de data inválido.';
+$messages['saveerror'] = 'Não foi possível guardar os dados. Ocorreu um erro no servidor.';
+$messages['vacationsaved'] = 'Dados de férias guardados com sucesso.';
+$messages['emptyvacationbody'] = 'É necessário o corpo da mensagem de férias!';
?>
diff --git a/lib/plugins/managesieve/localization/ro_RO.inc b/lib/plugins/managesieve/localization/ro_RO.inc
index 2cddf36..017320e 100644
--- a/lib/plugins/managesieve/localization/ro_RO.inc
+++ b/lib/plugins/managesieve/localization/ro_RO.inc
@@ -1,183 +1,202 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtre';
$labels['managefilters'] = 'Administreaza filtrele pentru mesaje primite.';
$labels['filtername'] = 'Nume filtru';
$labels['newfilter'] = 'Filtru nou';
$labels['filteradd'] = 'Adauga un filtru';
$labels['filterdel'] = 'Sterge filtru.';
$labels['moveup'] = 'Muta mai sus';
$labels['movedown'] = 'Muta mai jos';
$labels['filterallof'] = 'se potriveste cu toate regulile urmatoare';
$labels['filteranyof'] = 'se potriveste cu oricare din regulile urmatoare';
$labels['filterany'] = 'toate mesajele';
$labels['filtercontains'] = 'contine';
$labels['filternotcontains'] = 'nu contine';
$labels['filteris'] = 'este egal cu';
$labels['filterisnot'] = 'este diferit de';
$labels['filterexists'] = 'exista';
$labels['filternotexists'] = 'nu exista';
$labels['filtermatches'] = 'se potriveste cu expresia';
$labels['filternotmatches'] = 'nu se potriveste cu expresia';
$labels['filterregex'] = 'se potriveste cu expresia regulata';
$labels['filternotregex'] = 'nu se potriveste cu expresia regulata';
$labels['filterunder'] = 'sub';
$labels['filterover'] = 'peste';
$labels['addrule'] = 'Adauga regula';
$labels['delrule'] = 'Sterge regula';
$labels['messagemoveto'] = 'Muta mesajul in';
$labels['messageredirect'] = 'Redirectioneaza mesajul catre';
$labels['messagecopyto'] = 'Copiaza mesajul in';
$labels['messagesendcopy'] = 'Trimite o copie a mesajului catre';
$labels['messagereply'] = 'Raspunde cu mesajul';
$labels['messagedelete'] = 'Sterge mesajul';
$labels['messagediscard'] = 'Respinge cu mesajul';
$labels['messagekeep'] = 'Pastreaza mesajele in Inbox';
$labels['messagesrules'] = 'Pentru e-mail primit:';
$labels['messagesactions'] = '...executa urmatoarele actiuni:';
$labels['add'] = 'Adauga';
$labels['del'] = 'Sterge';
$labels['sender'] = 'Expeditor';
$labels['recipient'] = 'Destinatar';
-$labels['vacationaddr'] = 'Adrese(le) mele de email suplimentare:';
+$labels['vacationaddr'] = 'Adresele mele de e-mail (adiţionale)';
$labels['vacationdays'] = 'Cat de des sa trimit mesajele (in zile):';
$labels['vacationinterval'] = 'Cât de des să trimit mesaje:';
-$labels['days'] = 'zile';
-$labels['seconds'] = 'secunde';
$labels['vacationreason'] = 'Corpul mesajului (motivul vacantei):';
$labels['vacationsubject'] = 'Subiectul mesajului:';
+$labels['days'] = 'zile';
+$labels['seconds'] = 'secunde';
$labels['rulestop'] = 'Nu mai evalua reguli';
$labels['enable'] = 'Activeaza/Dezactiveaza';
$labels['filterset'] = 'Filtre setate';
$labels['filtersets'] = 'Filtrul seteaza';
$labels['filtersetadd'] = 'Adauga set de filtre';
$labels['filtersetdel'] = 'Sterge setul curent de filtre';
$labels['filtersetact'] = 'Activeaza setul curent de filtre';
$labels['filtersetdeact'] = 'Dezactiveaza setul curent de filtre';
$labels['filterdef'] = 'Definiţie filtru';
$labels['filtersetname'] = 'Nume set filtre';
$labels['newfilterset'] = 'Set filtre nou';
$labels['active'] = 'activ';
$labels['none'] = 'niciunul';
$labels['fromset'] = 'din setul';
$labels['fromfile'] = 'din fişier';
$labels['filterdisabled'] = 'Filtru dezactivat';
$labels['countisgreaterthan'] = 'numărul este mai mare ca';
$labels['countisgreaterthanequal'] = 'numărul este mai mare sau egal cu';
$labels['countislessthan'] = 'numărul este mai mic decât';
$labels['countislessthanequal'] = 'numărul este mai mic sau egal cu';
$labels['countequals'] = 'numărul este egal cu';
$labels['countnotequals'] = 'numaratoarea nu este egala cu';
$labels['valueisgreaterthan'] = 'valoarea este egală cu';
$labels['valueisgreaterthanequal'] = 'valoarea este mai mare sau egala cu';
$labels['valueislessthan'] = 'valoarea este mai mică decât';
$labels['valueislessthanequal'] = 'valoarea este mai mică sau egală cu';
$labels['valueequals'] = 'valoarea este egală cu';
$labels['valuenotequals'] = 'valoarea nu este egala cu';
$labels['setflags'] = 'Pune marcaje mesajului';
$labels['addflags'] = 'Adaugă marcaje mesajului';
$labels['removeflags'] = 'Şterge marcajele mesajului';
$labels['flagread'] = 'Citit';
$labels['flagdeleted'] = 'Șters';
$labels['flaganswered'] = 'Răspuns';
$labels['flagflagged'] = 'Marcat';
$labels['flagdraft'] = 'Schiță';
$labels['setvariable'] = 'Setare variabilă';
$labels['setvarname'] = 'Nume variabilă:';
$labels['setvarvalue'] = 'Valoare variabilă:';
$labels['setvarmodifiers'] = 'Modificatori:';
$labels['varlower'] = 'cu litere mici';
$labels['varupper'] = 'cu litere mari';
$labels['varlowerfirst'] = 'primul caracter cu litre mici';
$labels['varupperfirst'] = 'primul caracter cu litre mari';
$labels['varquotewildcard'] = 'caracterele speciale in citat';
$labels['varlength'] = 'lungime';
$labels['notify'] = 'Notificare trimitere';
-$labels['notifyaddress'] = 'La adresa de e-mail';
-$labels['notifybody'] = 'Mesajul de notificare:';
-$labels['notifysubject'] = 'Subiectul notificării:';
-$labels['notifyfrom'] = 'Expeditorul notificării:';
$labels['notifyimportance'] = 'Importanța:';
$labels['notifyimportancelow'] = 'mică';
$labels['notifyimportancenormal'] = 'normală';
$labels['notifyimportancehigh'] = 'mare';
$labels['filtercreate'] = 'Crează filtru';
$labels['usedata'] = 'Foloseşte următoarele date în filtru:';
$labels['nextstep'] = 'Următorul Pas';
$labels['...'] = '...';
$labels['currdate'] = 'Data curenta';
$labels['datetest'] = 'Data';
$labels['dateheader'] = 'header:';
$labels['year'] = 'an';
$labels['month'] = 'luna';
$labels['day'] = 'zi';
$labels['date'] = 'data (AAAA-LL-ZZ)';
$labels['julian'] = 'data (calendar iulian)';
$labels['hour'] = 'ora';
$labels['minute'] = 'minut';
$labels['second'] = 'secunda';
$labels['time'] = 'ora (hh:mm:ss)';
$labels['iso8601'] = 'data (ISO8601)';
$labels['std11'] = 'data (RFC2822)';
$labels['zone'] = 'fus orar';
$labels['weekday'] = 'zi saptamana (0-6)';
$labels['advancedopts'] = 'Opţiuni avansate';
$labels['body'] = 'Corp';
$labels['address'] = 'adresă';
$labels['envelope'] = 'plic';
$labels['modifier'] = 'modificator:';
$labels['text'] = 'text';
$labels['undecoded'] = 'nedecodat (brut)';
$labels['contenttype'] = 'tip conţinut';
$labels['modtype'] = 'tip:';
$labels['allparts'] = 'toate';
$labels['domain'] = 'domeniu';
$labels['localpart'] = 'partea locală';
$labels['user'] = 'utilizator';
$labels['detail'] = 'detaliu';
$labels['comparator'] = 'comparator:';
$labels['default'] = 'implicit';
$labels['octet'] = 'strict (octet)';
$labels['asciicasemap'] = 'ignoră majusculele (ascii-casemap)';
$labels['asciinumeric'] = 'numeric (ascii-numeric)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'invers';
+$labels['vacation'] = 'Vacanta';
+$labels['vacation.reply'] = 'Raspunde mesajului';
+$labels['vacation.advanced'] = 'Setari avansate';
+$labels['vacation.subject'] = 'Subiect';
+$labels['vacation.body'] = 'Corp';
+$labels['vacation.status'] = 'Statut';
+$labels['vacation.on'] = 'Pe';
+$labels['vacation.off'] = 'De pe';
+$labels['vacation.addresses'] = 'Adresa mea aditionala';
+$labels['vacation.interval'] = 'Interval de raspundere';
+$labels['vacation.after'] = 'Pune regula de vacanta dupa';
+$labels['vacation.saving'] = 'Salvez datele...';
$messages['filterunknownerror'] = 'Eroare necunoscută la server:';
$messages['filterconnerror'] = 'Nu mă pot conecta la server.';
+$messages['filterdeleteerror'] = 'Nu pot şterge filtrul. S-a produs o eroare la server.';
$messages['filterdeleted'] = 'Filtrul a fost şters cu succes.';
$messages['filtersaved'] = 'Filtrul a fost salvat cu succes.';
+$messages['filtersaveerror'] = 'Nu am putut salva filtrul. S-a produs o eroare la server.';
$messages['filterdeleteconfirm'] = 'Chiar vrei să ştergi filtrul selectat?';
$messages['ruledeleteconfirm'] = 'Eşti sigur că vrei să ştergi regula selectată?';
$messages['actiondeleteconfirm'] = 'Eşti sigur că vrei să ştergi acţiunea selectată?';
$messages['forbiddenchars'] = 'Caractere nepermise în câmp.';
$messages['cannotbeempty'] = 'Câmpul nu poate fi gol.';
$messages['ruleexist'] = 'Filtrul cu numele specificat există deja.';
+$messages['setactivateerror'] = 'Nu pot activa setul de filtre selectat. S-a produs o eroare la server.';
+$messages['setdeactivateerror'] = 'Nu pot dezactiva setul de filtre selectat. S-a produs o eroare la server.';
+$messages['setdeleteerror'] = 'Nu pot şterge setul de filtre selectat. S-a produs o eroare la server.';
$messages['setactivated'] = 'Setul de filtre activat cu succes.';
$messages['setdeactivated'] = 'Setul de filtre dezactivat cu succes.';
$messages['setdeleted'] = 'Setul de filtre şters cu succes.';
$messages['setdeleteconfirm'] = 'Eşti sigur(ă) că vrei să ştergi setul de filtre selectat?';
+$messages['setcreateerror'] = 'Nu am putut crea setul de filtre. S-a produs o eroare la server.';
$messages['setcreated'] = 'Setul de filtre creat cu succes.';
+$messages['activateerror'] = 'Nu am putut activa filtrul (filtrele) selectate. S-a produs o eroare la server.';
+$messages['deactivateerror'] = 'Nu am putut dezactiva filtrele (filtrele) selectate. S-a produs o eroare la server.';
$messages['deactivated'] = 'Filtrele au fost activate cu succes.';
$messages['activated'] = 'Filtrele au fost dezactivate cu succes.';
$messages['moved'] = 'Filtrele au fost mutate cu succes.';
+$messages['moveerror'] = 'Nu am putut muta filtrul selectat. S-a produs o eroare la server.';
$messages['nametoolong'] = 'Numele este prea lung.';
$messages['namereserved'] = 'Nume rezervat.';
$messages['setexist'] = 'Setul există deja.';
$messages['nodata'] = 'Trebuie selectată cel putin o poziţie!';
$messages['invaliddateformat'] = 'Data sau parte din data in format invalid';
+$messages['saveerror'] = 'Nu am putut salva datele. A aparut o eroare de server.';
+$messages['vacationsaved'] = 'Data de vacanta salvata cu succes';
?>
diff --git a/lib/plugins/managesieve/localization/ru_RU.inc b/lib/plugins/managesieve/localization/ru_RU.inc
index ea0ebd2..6714c4c 100644
--- a/lib/plugins/managesieve/localization/ru_RU.inc
+++ b/lib/plugins/managesieve/localization/ru_RU.inc
@@ -1,225 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Фильтры';
$labels['managefilters'] = 'Управление фильтрами для входящей почты';
$labels['filtername'] = 'Название фильтра';
$labels['newfilter'] = 'Новый фильтр';
$labels['filteradd'] = 'Добавить фильтр';
$labels['filterdel'] = 'Удалить фильтр';
$labels['moveup'] = 'Сдвинуть вверх';
$labels['movedown'] = 'Сдвинуть вниз';
$labels['filterallof'] = 'соответствует всем указанным правилам';
$labels['filteranyof'] = 'соответствует любому из указанных правил';
$labels['filterany'] = 'все сообщения';
$labels['filtercontains'] = 'содержит';
$labels['filternotcontains'] = 'не содержит';
$labels['filteris'] = 'соответствует';
$labels['filterisnot'] = 'не соответствует';
$labels['filterexists'] = 'существует';
$labels['filternotexists'] = 'не существует';
$labels['filtermatches'] = 'совпадает с выражением';
$labels['filternotmatches'] = 'не совпадает с выражением';
$labels['filterregex'] = 'совпадает с регулярным выражением';
$labels['filternotregex'] = 'не совпадает с регулярным выражением';
$labels['filterunder'] = 'меньше';
$labels['filterover'] = 'больше';
$labels['addrule'] = 'Добавить правило';
$labels['delrule'] = 'Удалить правило';
$labels['messagemoveto'] = 'Переместить сообщение в';
$labels['messageredirect'] = 'Перенаправить сообщение на';
$labels['messagecopyto'] = 'Скопировать сообщение в';
$labels['messagesendcopy'] = 'Отправить копию сообщения на';
$labels['messagereply'] = 'Ответить с сообщением';
$labels['messagedelete'] = 'Удалить сообщение';
$labels['messagediscard'] = 'Отбросить с сообщением';
$labels['messagekeep'] = 'Оставить сообщение во Входящих';
$labels['messagesrules'] = 'Для входящей почты:';
$labels['messagesactions'] = '...выполнить следующие действия:';
$labels['add'] = 'Добавить';
$labels['del'] = 'Удалить';
$labels['sender'] = 'Отправитель';
$labels['recipient'] = 'Получатель';
$labels['vacationaddr'] = 'Мой дополнительный адрес(а):';
$labels['vacationdays'] = 'Как часто отправлять сообщения (в днях):';
$labels['vacationinterval'] = 'Как часто отправлять сообщения:';
$labels['vacationreason'] = 'Текст сообщения (причина отсутствия):';
$labels['vacationsubject'] = 'Тема сообщения:';
$labels['days'] = 'дней';
$labels['seconds'] = 'секунд';
$labels['rulestop'] = 'Закончить выполнение';
$labels['enable'] = 'Включить/Выключить';
$labels['filterset'] = 'Набор фильтров';
$labels['filtersets'] = 'Наборы фильтров';
$labels['filtersetadd'] = 'Добавить набор фильтров';
$labels['filtersetdel'] = 'Удалить текущий набор фильтров';
$labels['filtersetact'] = 'Включить текущий набор фильтров';
$labels['filtersetdeact'] = 'Отключить текущий набор фильтров';
$labels['filterdef'] = 'Описание фильтра';
$labels['filtersetname'] = 'Название набора фильтров';
$labels['newfilterset'] = 'Новый набор фильтров';
$labels['active'] = 'используется';
$labels['none'] = 'нет';
$labels['fromset'] = 'из набора';
$labels['fromfile'] = 'из файла';
$labels['filterdisabled'] = 'Отключить фильтр';
$labels['countisgreaterthan'] = 'количество больше, чем';
$labels['countisgreaterthanequal'] = 'количество больше или равно';
$labels['countislessthan'] = 'количество меньше, чем';
$labels['countislessthanequal'] = 'количество меньше или равно';
$labels['countequals'] = 'количество равно';
$labels['countnotequals'] = 'количество не равно';
$labels['valueisgreaterthan'] = 'значение больше, чем';
$labels['valueisgreaterthanequal'] = 'значение больше или равно';
$labels['valueislessthan'] = 'значение меньше, чем';
$labels['valueislessthanequal'] = 'значение меньше или равно';
$labels['valueequals'] = 'значение равно';
$labels['valuenotequals'] = 'значение не равно';
$labels['setflags'] = 'Установить флаги на сообщение';
$labels['addflags'] = 'Добавить флаги к сообщению';
$labels['removeflags'] = 'Убрать флаги из сообщения';
$labels['flagread'] = 'Прочитано';
$labels['flagdeleted'] = 'Удалено';
$labels['flaganswered'] = 'Отвечено';
$labels['flagflagged'] = 'Помечено';
$labels['flagdraft'] = 'Черновик';
$labels['setvariable'] = 'Задать переменную';
$labels['setvarname'] = 'Имя переменной:';
$labels['setvarvalue'] = 'Значение переменной:';
$labels['setvarmodifiers'] = 'Модификаторы:';
$labels['varlower'] = 'нижний регистр';
$labels['varupper'] = 'верхний регистр';
$labels['varlowerfirst'] = 'первый символ в нижнем регистре';
$labels['varupperfirst'] = 'первый символ в верхнем регистре';
$labels['varquotewildcard'] = 'символ кавычек';
$labels['varlength'] = 'длина';
$labels['notify'] = 'Отправить уведомление';
$labels['notifytarget'] = 'Объект уведомления:';
$labels['notifymessage'] = 'Сообщение уведомления (не обязательно):';
$labels['notifyoptions'] = 'Параметры уведомления (не обязательно):';
$labels['notifyfrom'] = 'Отправитель уведомления (не обязательно):';
$labels['notifyimportance'] = 'Важность:';
$labels['notifyimportancelow'] = 'низкая';
$labels['notifyimportancenormal'] = 'нормальная';
$labels['notifyimportancehigh'] = 'высокая';
$labels['notifymethodmailto'] = 'Email';
$labels['notifymethodtel'] = 'Телефон';
$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Создать фильтр';
$labels['usedata'] = 'Использовать следующие данные в фильтре:';
$labels['nextstep'] = 'Далее';
$labels['...'] = '...';
$labels['currdate'] = 'Текущая дата';
$labels['datetest'] = 'Дата';
$labels['dateheader'] = 'заголовок:';
$labels['year'] = 'год';
$labels['month'] = 'месяц';
$labels['day'] = 'день';
$labels['date'] = 'дата (гггг-мм-дд)';
$labels['julian'] = 'дата (юлианская)';
$labels['hour'] = 'час';
$labels['minute'] = 'минута';
$labels['second'] = 'секунда';
$labels['time'] = 'время (чч:мм:сс)';
$labels['iso8601'] = 'дата (ISO8601)';
$labels['std11'] = 'дата (RFC2822)';
$labels['zone'] = 'часовой пояс';
$labels['weekday'] = 'день недели (0-6)';
$labels['advancedopts'] = 'Дополнительные параметры';
$labels['body'] = 'Тело письма';
$labels['address'] = 'адрес';
$labels['envelope'] = 'конверт';
$labels['modifier'] = 'модификатор области поиска:';
$labels['text'] = 'текст';
$labels['undecoded'] = 'необработанный (сырой)';
$labels['contenttype'] = 'тип содержимого';
$labels['modtype'] = 'поиск в адресах:';
$labels['allparts'] = 'везде';
$labels['domain'] = 'в имени домена';
$labels['localpart'] = 'только в имени пользователя, без домена';
$labels['user'] = 'в полном имени пользователя';
$labels['detail'] = 'в дополнительных сведениях';
$labels['comparator'] = 'способ сравнения:';
$labels['default'] = 'по умолчанию';
$labels['octet'] = 'Строгий (octet)';
$labels['asciicasemap'] = 'Регистронезависимый (ascii-casemap)';
$labels['asciinumeric'] = 'Числовой (ascii-numeric)';
$labels['index'] = 'индекс:';
$labels['indexlast'] = 'наоборот';
$labels['vacation'] = 'Отпуск';
$labels['vacation.reply'] = 'Ответное сообщение';
$labels['vacation.advanced'] = 'Дополнительные настройки';
$labels['vacation.subject'] = 'Тема';
$labels['vacation.body'] = 'Тело письма';
-$labels['vacation.dates'] = 'Время отпуска';
-$labels['vacation.from'] = 'От:';
-$labels['vacation.to'] = 'Кому:';
+$labels['vacation.start'] = 'Начало отпуска';
+$labels['vacation.end'] = 'Конец отпуска';
$labels['vacation.status'] = 'Состояние';
$labels['vacation.on'] = 'Вкл.';
$labels['vacation.off'] = 'Выкл.';
$labels['vacation.addresses'] = 'Мои дополнительные адреса';
$labels['vacation.interval'] = 'Интервал ответа';
$labels['vacation.after'] = 'Поместить правило отпуска после';
$labels['vacation.saving'] = 'Сохранение данных...';
$labels['vacation.action'] = 'Действия с входящим сообщением';
$labels['vacation.keep'] = 'Оставить';
$labels['vacation.discard'] = 'Отменить';
$labels['vacation.redirect'] = 'Перенаправить на';
$labels['vacation.copy'] = 'Отправить копию на';
$labels['arialabelfiltersetactions'] = 'Действия набора фильтров';
$labels['arialabelfilteractions'] = 'Действия фильтра';
$labels['arialabelfilterform'] = 'Свойства фильтра';
$labels['ariasummaryfilterslist'] = 'Список фильтров';
$labels['ariasummaryfiltersetslist'] = 'Список набора фильтров';
$labels['filterstitle'] = 'Редактировать фильтры для входящей почты';
$labels['vacationtitle'] = 'Изменить правило "Не в офисе"';
$messages['filterunknownerror'] = 'Неизвестная ошибка сервера.';
$messages['filterconnerror'] = 'Невозможно подключиться к серверу.';
$messages['filterdeleteerror'] = 'Невозможно удалить фильтр. Ошибка сервера.';
$messages['filterdeleted'] = 'Фильтр успешно удалён.';
$messages['filtersaved'] = 'Фильтр успешно сохранён.';
$messages['filtersaveerror'] = 'Невозможно сохранить фильтр. Ошибка сервера.';
$messages['filterdeleteconfirm'] = 'Вы действительно хотите удалить выделенный фильтр?';
$messages['ruledeleteconfirm'] = 'Вы уверенны, что хотите удалить выделенное правило?';
$messages['actiondeleteconfirm'] = 'Вы уверенны, что хотите удалить выделенное действие?';
$messages['forbiddenchars'] = 'Недопустимые символы в поле.';
$messages['cannotbeempty'] = 'Поле не может быть пустым.';
$messages['ruleexist'] = 'Фильтр с таким именем уже существует.';
$messages['setactivateerror'] = 'Невозможно включить выбранный набор фильтров. Ошибка сервера.';
$messages['setdeactivateerror'] = 'Невозможно отключить выбранный набор фильтров. Ошибка сервера.';
$messages['setdeleteerror'] = 'Невозможно удалить выбранный набор фильтров. Ошибка сервера.';
$messages['setactivated'] = 'Набор фильтров успешно включён.';
$messages['setdeactivated'] = 'Набор фильтров успешно отключён.';
$messages['setdeleted'] = 'Набор фильтров успешно удалён.';
$messages['setdeleteconfirm'] = 'Вы уверены в том, что хотите удалить выбранный набор фильтров?';
$messages['setcreateerror'] = 'Невозможно создать набор фильтров. Ошибка сервера.';
$messages['setcreated'] = 'Набор фильтров успешно создан.';
$messages['activateerror'] = 'Невозможно включить выбранный(е) фильтр(ы). Ошибка сервера.';
$messages['deactivateerror'] = 'Невозможно выключить выбранный(е) фильтр(ы). Ошибка сервера.';
$messages['deactivated'] = 'Фильтр(ы) успешно отключен(ы).';
$messages['activated'] = 'Фильтр(ы) успешно включен(ы).';
$messages['moved'] = 'Фильтр успешно перемещён.';
$messages['moveerror'] = 'Невозможно переместить фильтр. Ошибка сервера.';
$messages['nametoolong'] = 'Слишком длинное имя.';
$messages['namereserved'] = 'Зарезервированное имя.';
$messages['setexist'] = 'Набор уже существует.';
$messages['nodata'] = 'Нужно выбрать хотя бы одну позицию!';
$messages['invaliddateformat'] = 'Неверная дата или формат части даты';
$messages['saveerror'] = 'Невозможно сохранить данные. Ошибка сервера.';
$messages['vacationsaved'] = 'Данные об отпуске успешно сохранены.';
$messages['emptyvacationbody'] = 'Сообщение о причине отсутствия не может быть пустым!';
?>
diff --git a/lib/plugins/managesieve/localization/sk_SK.inc b/lib/plugins/managesieve/localization/sk_SK.inc
index 9a75586..4cad13f 100644
--- a/lib/plugins/managesieve/localization/sk_SK.inc
+++ b/lib/plugins/managesieve/localization/sk_SK.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtre';
$labels['managefilters'] = 'Správa filtrov prichádzajúcej pošty';
$labels['filtername'] = 'Názov filtra';
$labels['newfilter'] = 'Nový filter';
$labels['filteradd'] = 'Pridať filter';
$labels['filterdel'] = 'Vymazať filter';
$labels['moveup'] = 'Presunúť nahor';
$labels['movedown'] = 'Presunúť nadol';
$labels['filterallof'] = 'vyhovujúca všetkým z nasledujúcich pravidiel';
$labels['filteranyof'] = 'vyhovujúca ľubovoľnému z nasledujúcich pravidiel';
$labels['filterany'] = 'všetky správy';
$labels['filtercontains'] = 'obsahuje';
$labels['filternotcontains'] = 'neobsahuje';
$labels['filteris'] = 'sa rovná';
$labels['filterisnot'] = 'sa nerovná';
$labels['filterexists'] = 'existuje';
$labels['filternotexists'] = 'neexistuje';
$labels['filtermatches'] = 'vyhovuje výrazu';
$labels['filternotmatches'] = 'nevyhovuje výrazu';
$labels['filterregex'] = 'vyhovuje regulárnemu výrazu';
$labels['filternotregex'] = 'nevyhovuje regulárnemu výrazu';
$labels['filterunder'] = 'pod';
$labels['filterover'] = 'nad';
$labels['addrule'] = 'Pridať pravidlo';
$labels['delrule'] = 'Vymazať pravidlo';
$labels['messagemoveto'] = 'Presunúť správu do';
$labels['messageredirect'] = 'Presmerovať správu na';
$labels['messagecopyto'] = 'Kopírovať správu do';
$labels['messagesendcopy'] = 'Poslať kópiu správy na adresu';
$labels['messagereply'] = 'Odpovedať správou';
$labels['messagedelete'] = 'Vymazať správu';
$labels['messagediscard'] = 'Vymazať a poslať správu na';
$labels['messagekeep'] = 'Ponechať správu v Doručenej pošte';
$labels['messagesrules'] = 'Pre prichádzajúcu poštu:';
$labels['messagesactions'] = '...vykonať tieto akcie:';
$labels['add'] = 'Pridať';
$labels['del'] = 'Vymazať';
$labels['sender'] = 'Odosielateľ';
$labels['recipient'] = 'Príjemca';
$labels['vacationaddr'] = 'Iná moja e-mailová adresa (adresy):';
$labels['vacationdays'] = 'Ako často odosielať správy (v dňoch):';
$labels['vacationinterval'] = 'Ako často odosielať správy:';
-$labels['days'] = 'dní';
-$labels['seconds'] = 'sekúnd';
$labels['vacationreason'] = 'Telo správy (dôvod neprítomnosti):';
$labels['vacationsubject'] = 'Predmet správy:';
+$labels['days'] = 'dní';
+$labels['seconds'] = 'sekúnd';
$labels['rulestop'] = 'Koniec pravidiel';
$labels['enable'] = 'Zapnúť/vypnúť';
$labels['filterset'] = 'Súprava filtrov';
$labels['filtersets'] = 'Súpravy filtrov';
$labels['filtersetadd'] = 'Pridať súpravu filtrov';
$labels['filtersetdel'] = 'Vymazať aktuálnu súpravu filtrov';
$labels['filtersetact'] = 'Aktivovať aktuálnu súpravu filtrov';
$labels['filtersetdeact'] = 'Deaktivovať aktuálnu súpravu filtrov';
$labels['filterdef'] = 'Definícia filtra';
$labels['filtersetname'] = 'Názov súpravy filtrov';
$labels['newfilterset'] = 'Nová súprava filtrov';
$labels['active'] = 'aktívna';
$labels['none'] = 'žiadne';
$labels['fromset'] = 'zo súpravy';
$labels['fromfile'] = 'zo súboru';
$labels['filterdisabled'] = 'Filter vypnutý';
$labels['countisgreaterthan'] = 'počet je väčší ako';
$labels['countisgreaterthanequal'] = 'počet je väčší alebo rovný ako';
$labels['countislessthan'] = 'počet je menší ako';
$labels['countislessthanequal'] = 'počet je menší alebo rovný ako';
$labels['countequals'] = 'počet je rovný';
$labels['countnotequals'] = 'počet sa nerovná';
$labels['valueisgreaterthan'] = 'hodnota je väčšia ako';
$labels['valueisgreaterthanequal'] = 'hodnota je väčšia alebo rovná ako';
$labels['valueislessthan'] = 'hodnota je menšia ako';
$labels['valueislessthanequal'] = 'hodnota je menšia alebo rovná ako';
$labels['valueequals'] = 'hodnota je rovná ako';
$labels['valuenotequals'] = 'hodnota sa nerovná';
$labels['setflags'] = 'Nastaviť príznaky správy';
$labels['addflags'] = 'Pridať príznaky správy';
$labels['removeflags'] = 'Odstrániť príznaky zo správy';
$labels['flagread'] = 'Prečítané';
$labels['flagdeleted'] = 'Vymazané';
$labels['flaganswered'] = 'Odpovedané';
$labels['flagflagged'] = 'Označené príznakom';
$labels['flagdraft'] = 'Koncept';
$labels['setvariable'] = 'Nastaviť premennú';
$labels['setvarname'] = 'Názov premennej:';
$labels['setvarvalue'] = 'Hodnota premennej:';
$labels['setvarmodifiers'] = 'Modifikátory:';
$labels['varlower'] = 'malé písmená';
$labels['varupper'] = 'VEĽKÉ PÍSMENÁ';
$labels['varlowerfirst'] = 'prvé písmeno malé';
$labels['varupperfirst'] = 'prvé písmeno veľké';
$labels['varquotewildcard'] = 'k špeciálnym znakom pridať úvodzovky';
$labels['varlength'] = 'dĺžka';
$labels['notify'] = 'Odoslať oznámenie';
-$labels['notifyaddress'] = 'Na e-mailovú adresu:';
-$labels['notifybody'] = 'Telo oznámenia:';
-$labels['notifysubject'] = 'Predmet oznámenia:';
-$labels['notifyfrom'] = 'Odosielateľ oznámenia:';
+$labels['notifytarget'] = 'Cieľ notifikácie:';
+$labels['notifymessage'] = 'Notifikačná správa (voliteľne):';
+$labels['notifyoptions'] = 'Nastavenia notifikácie (voliteľné):';
+$labels['notifyfrom'] = 'Odosielateľ notifikácie (voliteľne):';
$labels['notifyimportance'] = 'Priorita:';
$labels['notifyimportancelow'] = 'nízka';
$labels['notifyimportancenormal'] = 'normálna';
$labels['notifyimportancehigh'] = 'vysoká';
+$labels['notifymethodmailto'] = 'E-mail';
+$labels['notifymethodtel'] = 'Telefón';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Vytvoriť filter';
$labels['usedata'] = 'Použiť tieto údaje vo filtri:';
$labels['nextstep'] = 'Ďalší krok';
$labels['...'] = '...';
$labels['currdate'] = 'Aktuálny dátum';
$labels['datetest'] = 'Dátum';
$labels['dateheader'] = 'záhlavie:';
$labels['year'] = 'rok';
$labels['month'] = 'mesiac';
$labels['day'] = 'deň';
$labels['date'] = 'dátum (rrrr-mm-dd)';
$labels['julian'] = 'dátum (podľa Juliánskeho kalendára)';
$labels['hour'] = 'hod.';
$labels['minute'] = 'min.';
$labels['second'] = 'sek.';
$labels['time'] = 'čas (hh:mm:ss)';
$labels['iso8601'] = 'dátum (ISO8601)';
$labels['std11'] = 'dátum (RFC2822)';
$labels['zone'] = 'časové pásmo';
$labels['weekday'] = 'deň v týždni (0-6)';
$labels['advancedopts'] = 'Rozšírené nastavenia';
$labels['body'] = 'Telo';
$labels['address'] = 'adresa';
$labels['envelope'] = 'obálka';
$labels['modifier'] = 'modifikátor:';
$labels['text'] = 'text';
$labels['undecoded'] = 'nedekódované (raw)';
$labels['contenttype'] = 'typ obsahu';
$labels['modtype'] = 'typ:';
$labels['allparts'] = 'všetko';
$labels['domain'] = 'doména';
$labels['localpart'] = 'lokálna časť';
$labels['user'] = 'používateľ';
$labels['detail'] = 'detail';
$labels['comparator'] = 'porovnávač:';
$labels['default'] = 'predvolené';
$labels['octet'] = 'striktný (osmičkový)';
$labels['asciicasemap'] = 'nerozlišuje veľké a malé písmená (ascii tabuľka znakov)';
$labels['asciinumeric'] = 'numerické (ascii čísla)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'dozadu';
+$labels['vacation'] = 'Dovolenka';
+$labels['vacation.reply'] = 'Odpoveď na správu';
+$labels['vacation.advanced'] = 'Pokročilé nastavenia';
+$labels['vacation.subject'] = 'Predmet';
+$labels['vacation.body'] = 'Telo';
+$labels['vacation.start'] = 'Začiatok dovolenky';
+$labels['vacation.end'] = 'Koniec dovolenky';
+$labels['vacation.status'] = 'Stav';
+$labels['vacation.on'] = 'Zap.';
+$labels['vacation.off'] = 'Vyp.';
+$labels['vacation.addresses'] = 'Moje ďalšie adresy';
+$labels['vacation.interval'] = 'Interval odpovedania';
+$labels['vacation.after'] = 'Nastaviť pravidlo pre dovolenku po';
+$labels['vacation.saving'] = 'Ukladanie údajov...';
+$labels['vacation.action'] = 'Akcia pre prichádzajúcu správu';
+$labels['vacation.keep'] = 'Zachovať';
+$labels['vacation.discard'] = 'Vyhodiť do koša';
+$labels['vacation.redirect'] = 'Presmerovať na';
+$labels['vacation.copy'] = 'Poslať kópiu na';
+$labels['arialabelfiltersetactions'] = 'Akcie zo súpravy filtrov';
+$labels['arialabelfilteractions'] = 'Akcie filtra';
+$labels['arialabelfilterform'] = 'Nastavenia filtra';
+$labels['ariasummaryfilterslist'] = 'Zoznam filtrov';
+$labels['ariasummaryfiltersetslist'] = 'Zoznam súprav s filtrami';
+$labels['filterstitle'] = 'Upraviť filtre prichádzajúcich e-mailov';
+$labels['vacationtitle'] = 'Upraviť pravidlo pre čas mimo kancelárie';
$messages['filterunknownerror'] = 'Neznáma chyba servera.';
$messages['filterconnerror'] = 'Nepodarilo sa pripojiť k serveru.';
$messages['filterdeleteerror'] = 'Nemožno vymazať filter. Nastala chyba servera.';
$messages['filterdeleted'] = 'Filter bol úspešne vymazaný.';
$messages['filtersaved'] = 'Filter bol úspešne uložený.';
$messages['filtersaveerror'] = 'Nemožno uložiť filter. Nastala chyba servera.';
$messages['filterdeleteconfirm'] = 'Naozaj chcete vymazať vybraný filter?';
$messages['ruledeleteconfirm'] = 'Naozaj chcete vymazať vybrané pravidlo?';
$messages['actiondeleteconfirm'] = 'Naozaj chcete vymazať vybranú akciu?';
$messages['forbiddenchars'] = 'Pole obsahuje nepovolené znaky.';
$messages['cannotbeempty'] = 'Pole nemôže byť prázdne.';
$messages['ruleexist'] = 'Filter so zadaným názvom už existuje.';
$messages['setactivateerror'] = 'Nemožno aktivovať vybranú súpravu filtrov. Nastala chyba servera.';
$messages['setdeactivateerror'] = 'Nemožno deaktivovať vybranú súpravu filtrov. Nastala chyba servera.';
$messages['setdeleteerror'] = 'Nemožno vymazať vybranú súpravu filtrov. Nastala chyba servera.';
$messages['setactivated'] = 'Súprava filtrov bola úspešne aktivovaná.';
$messages['setdeactivated'] = 'Súprava filtrov bola úspešne deaktivovaná.';
$messages['setdeleted'] = 'Súprava filtrov bola úspešne vymazaná.';
$messages['setdeleteconfirm'] = 'Naozaj chcete vymazať vybranú súpravu filtrov?';
$messages['setcreateerror'] = 'Nemožno vytvoriť súpravu filtrov. Nastala chyba servera.';
$messages['setcreated'] = 'Súprava filtrov bola úspešne vytvorená.';
$messages['activateerror'] = 'Nemožno aktivovať vybraný filter (vybrané filtre). Nastala chyba servera.';
$messages['deactivateerror'] = 'Nemožno vypnúť vybraný filter (vybrané filtre). Nastala chyba servera.';
$messages['deactivated'] = 'Filtre boli úspešne vypnuté.';
$messages['activated'] = 'Filtre boli úspešne zapnuté.';
$messages['moved'] = 'Filter bol úspešne presunutý.';
$messages['moveerror'] = 'Nemožno presunúť vybraný filter. Nastala chyba servera.';
$messages['nametoolong'] = 'Názov je príliš dlhý.';
$messages['namereserved'] = 'Rezervovaný názov.';
$messages['setexist'] = 'Súprava už existuje.';
$messages['nodata'] = 'Aspoň jedna pozícia musí byť zvolená!';
$messages['invaliddateformat'] = 'Neplatný formát dátumu alebo časti dátumu';
+$messages['saveerror'] = 'Údaje nemožno uložiť. Nastala chyba servera.';
+$messages['vacationsaved'] = 'Údaje o dovolenke boli úspešne uložené.';
+$messages['emptyvacationbody'] = 'Musíte zadať telo správy, zobrazovanej v čase neprítomnosti!';
?>
diff --git a/lib/plugins/managesieve/localization/sl_SI.inc b/lib/plugins/managesieve/localization/sl_SI.inc
index 2915684..f0e5159 100644
--- a/lib/plugins/managesieve/localization/sl_SI.inc
+++ b/lib/plugins/managesieve/localization/sl_SI.inc
@@ -1,192 +1,188 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtri';
$labels['managefilters'] = 'Uredi filtre za dohodno pošto';
$labels['filtername'] = 'Ime filtra';
$labels['newfilter'] = 'Nov filter';
$labels['filteradd'] = 'Dodaj filter';
$labels['filterdel'] = 'Izbriši filter';
$labels['moveup'] = 'Pomakni se navzgor';
$labels['movedown'] = 'Pomakni se navzdol';
$labels['filterallof'] = 'izpolnjeni morajo biti vsi pogoji';
$labels['filteranyof'] = 'izpolnjen mora biti vsaj eden od navedenih pogojev';
$labels['filterany'] = 'pogoj velja za vsa sporočila';
$labels['filtercontains'] = 'vsebuje';
$labels['filternotcontains'] = 'ne vsebuje';
$labels['filteris'] = 'je enak/a';
$labels['filterisnot'] = 'ni enak/a';
$labels['filterexists'] = 'obstaja';
$labels['filternotexists'] = 'ne obstaja';
$labels['filtermatches'] = 'ustreza izrazu';
$labels['filternotmatches'] = 'ne ustreza izrazu';
$labels['filterregex'] = 'ustreza regularnemu izrazu';
$labels['filternotregex'] = 'ne ustreza regularnemu izrazu';
$labels['filterunder'] = 'pod';
$labels['filterover'] = 'nad';
$labels['addrule'] = 'Dodaj pravilo';
$labels['delrule'] = 'Izbriši pravilo';
$labels['messagemoveto'] = 'Premakni sporočilo v';
$labels['messageredirect'] = 'Preusmeri sporočilo v';
$labels['messagecopyto'] = 'Kopiraj sporočila na';
$labels['messagesendcopy'] = 'Pošlji kopijo sporočila na';
$labels['messagereply'] = 'Odgovori s sporočilom';
$labels['messagedelete'] = 'Izbriši sporočilo';
$labels['messagediscard'] = 'Zavrži s sporočilom';
$labels['messagekeep'] = 'Ohrani sporočila v mapi Prejeto';
$labels['messagesrules'] = 'Določi pravila za dohodno pošto:';
$labels['messagesactions'] = '...izvrši naslednja dejanja:';
$labels['add'] = 'Dodaj';
$labels['del'] = 'Izbriši';
$labels['sender'] = 'Pošiljatelj';
$labels['recipient'] = 'Prejemnik';
$labels['vacationaddr'] = 'Moji dodatni e-naslovi';
$labels['vacationdays'] = 'Kako pogosto naj bodo sporočila poslana (v dnevih):';
$labels['vacationinterval'] = 'Sporočila pošlji na:';
-$labels['days'] = 'dni';
-$labels['seconds'] = 'sekund';
$labels['vacationreason'] = 'Vsebina sporočila (vzrok za odsotnost):';
$labels['vacationsubject'] = 'Zadeva sporočila';
+$labels['days'] = 'dni';
+$labels['seconds'] = 'sekund';
$labels['rulestop'] = 'Prekini z izvajanjem pravil';
$labels['enable'] = 'Omogoči/Onemogoči';
$labels['filterset'] = 'Nastavitev filtrov';
$labels['filtersets'] = 'Nastavitve filtrov';
$labels['filtersetadd'] = 'Dodaj nastavitev filtrov';
$labels['filtersetdel'] = 'Izbriši trenutne nastavitve filtriranja';
$labels['filtersetact'] = 'Vključi trenutno nastavitev filtriranja';
$labels['filtersetdeact'] = 'Onemogoči trenutno nastavitev filtriranja';
$labels['filterdef'] = 'Opis filtra';
$labels['filtersetname'] = 'Ime filtra';
$labels['newfilterset'] = 'Nov filter';
$labels['active'] = 'aktiven';
$labels['none'] = 'brez';
$labels['fromset'] = 'iz nastavitve';
$labels['fromfile'] = 'iz dokumenta';
$labels['filterdisabled'] = 'Filter onemogočen';
$labels['countisgreaterthan'] = 'seštevek je večji od';
$labels['countisgreaterthanequal'] = 'seštevek je večji ali enak';
$labels['countislessthan'] = 'seštevek je manjši od';
$labels['countislessthanequal'] = 'seštevel je manjši ali enak';
$labels['countequals'] = 'seštevek je enak';
$labels['countnotequals'] = 'vsota ne ustreza';
$labels['valueisgreaterthan'] = 'vrednost je večja od';
$labels['valueisgreaterthanequal'] = 'vrednost je večja ali enaka';
$labels['valueislessthan'] = 'vrednost je manjša od';
$labels['valueislessthanequal'] = 'vrednost je manjša ali enaka';
$labels['valueequals'] = 'vrednost je enaka';
$labels['valuenotequals'] = 'vrednost ni enaka';
$labels['setflags'] = 'Označi sporočilo';
$labels['addflags'] = 'Označi sporočilo';
$labels['removeflags'] = 'Odstrani zaznamke s sporočil';
$labels['flagread'] = 'Prebrano';
$labels['flagdeleted'] = 'Izbrisano';
$labels['flaganswered'] = 'Odgovorjeno';
$labels['flagflagged'] = 'Označeno';
$labels['flagdraft'] = 'Osnutek';
$labels['setvariable'] = 'Nastavi spremenljivko';
$labels['setvarname'] = 'Ime spremenljivke:';
$labels['setvarvalue'] = 'Vrednost spremenljivke:';
$labels['setvarmodifiers'] = 'Modifikator:';
$labels['varlower'] = 'majhne črke';
$labels['varupper'] = 'velike črke';
$labels['varlowerfirst'] = 'prvi znak velika začetnica';
$labels['varupperfirst'] = 'prvi znak velika začetnica';
$labels['varquotewildcard'] = 'citiraj posebne znake';
$labels['varlength'] = 'dolžina';
$labels['notify'] = 'Poštlji obvestilo';
-$labels['notifyaddress'] = 'Na elektronski naslov:';
-$labels['notifybody'] = 'Telo obvestila:';
-$labels['notifysubject'] = 'Zadeva obvestila:';
-$labels['notifyfrom'] = 'Pošiljatelj obvestila:';
$labels['notifyimportance'] = 'Pomembnost:';
$labels['notifyimportancelow'] = 'nizko';
$labels['notifyimportancenormal'] = 'običajno';
$labels['notifyimportancehigh'] = 'visoko';
$labels['filtercreate'] = 'Ustvari filter';
$labels['usedata'] = 'Pri stvarjanju filtra uporabi naslednje podatke';
$labels['nextstep'] = 'Naslednji korak';
$labels['...'] = '...';
$labels['currdate'] = 'Današnji datum';
$labels['datetest'] = 'Datum';
$labels['dateheader'] = 'glava:';
$labels['year'] = 'leto';
$labels['month'] = 'mesec';
$labels['day'] = 'dan';
$labels['date'] = 'datum(yyyy-mm-dd)';
$labels['julian'] = 'datum (julijanski)';
$labels['hour'] = 'ura';
$labels['minute'] = 'minuta';
$labels['second'] = 'sekunda';
$labels['time'] = 'čas';
$labels['iso8601'] = 'datum (ISO8601)';
$labels['std11'] = 'datum (RFC2822)';
$labels['zone'] = 'časovni pas';
$labels['weekday'] = 'dan v tednu (0-6)';
$labels['advancedopts'] = 'Dodatne možnosti';
$labels['body'] = 'Vsebina';
$labels['address'] = 'naslov';
$labels['envelope'] = 'ovojnica';
$labels['modifier'] = 'modifikator';
$labels['text'] = 'besedilo';
$labels['undecoded'] = 'neobdelano';
$labels['contenttype'] = 'tip vsebine';
$labels['modtype'] = 'tip';
$labels['allparts'] = 'vse';
$labels['domain'] = 'domena';
$labels['localpart'] = 'lokalni del';
$labels['user'] = 'uporabnik';
$labels['detail'] = 'podrobnosti';
$labels['comparator'] = 'primerjalnik';
$labels['default'] = 'privzeto';
$labels['octet'] = 'strict (octet)';
$labels['asciicasemap'] = 'ni občutljiv na velike/male črke (ascii-casemap)';
$labels['asciinumeric'] = 'numerično (ascii-numeric)';
$labels['index'] = 'indeks:';
$labels['indexlast'] = 'obraten vrstni red';
$messages['filterunknownerror'] = 'Prišlo je do neznane napake.';
$messages['filterconnerror'] = 'Povezave s strežnikom (managesieve) ni bilo mogoče vzpostaviti';
$messages['filterdeleteerror'] = 'Pravila ni bilo mogoče izbrisati. Prišlo je do napake.';
$messages['filterdeleted'] = 'Pravilo je bilo uspešno izbrisano.';
$messages['filtersaved'] = 'Pravilo je bilo uspešno shranjeno';
$messages['filtersaveerror'] = 'Pravila ni bilo mogoče shraniti. Prišlo je do napake.';
$messages['filterdeleteconfirm'] = 'Ste prepričani, da želite izbrisati izbrano pravilo?';
$messages['ruledeleteconfirm'] = 'Ste prepričani, da želite izbrisati izbrano pravilo?';
$messages['actiondeleteconfirm'] = 'Ste prepričani, da želite izbrisati izbrano dejanje?';
$messages['forbiddenchars'] = 'V polju so neveljavni znaki';
$messages['cannotbeempty'] = 'Polje ne sme biti prazno';
$messages['ruleexist'] = 'Filer s tem imenom že obstaja';
$messages['setactivateerror'] = 'Izbranih filtrov ni bilo mogoče vključiti. Prišlo je do napake na strežniku.';
$messages['setdeactivateerror'] = 'Izbranih filtrov ni bilo mogoče izključiti. Prišlo je do napake na strežniku.';
$messages['setdeleteerror'] = 'Izbranih filtrov ni bilo mogoče izbrisati. Prišlo je do napake na strežniku.';
$messages['setactivated'] = 'Filter je bil uspešno vključen.';
$messages['setdeactivated'] = 'Filter je bil uspešno onemogočen.';
$messages['setdeleted'] = 'Filter je bil uspešno izbrisan.';
$messages['setdeleteconfirm'] = 'Ste prepričani, da želite izbrisati ta filter?';
$messages['setcreateerror'] = 'Nabora filtrov ni bilo mogoče ustvariti. Prišlo je do napake na strežniku.';
$messages['setcreated'] = 'Filter je bil uspešno ustvarjen.';
$messages['activateerror'] = 'Izbranega/ih filtra/ov ni bilo mogoče vključiti. Prišlo je do napake na strežniku.';
$messages['deactivateerror'] = 'Izbranega/ih fitra/ov ni bilo mogoče izključiti. Prišlo je do napake na strežniku.';
$messages['deactivated'] = 'Filtri so bili uspešno omogočeni.';
$messages['activated'] = 'Filtri so bili uspešno onemogočeni.';
$messages['moved'] = 'Filter je bil uspešno premaknjen.';
$messages['moveerror'] = 'Izbranega filtra ni bilo mogoče premakniti. Prišlo je do napake na strežniku.';
$messages['nametoolong'] = 'Ime je predolgo.';
$messages['namereserved'] = 'Rezervirano ime.';
$messages['setexist'] = 'Nastavitev filtra že obstaja.';
$messages['nodata'] = 'Izbrana mora biti vsaj ena nastavitev!';
$messages['invaliddateformat'] = 'Neveljaven datum ali oblika zapisa datuma';
?>
diff --git a/lib/plugins/managesieve/localization/sv_SE.inc b/lib/plugins/managesieve/localization/sv_SE.inc
index 131d46c..ede13cb 100644
--- a/lib/plugins/managesieve/localization/sv_SE.inc
+++ b/lib/plugins/managesieve/localization/sv_SE.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filter';
$labels['managefilters'] = 'Administrera filter';
$labels['filtername'] = 'Filternamn';
$labels['newfilter'] = 'Nytt filter';
-$labels['filteradd'] = 'Lägg till filter';
+$labels['filteradd'] = 'Nytt filter';
$labels['filterdel'] = 'Ta bort filter';
$labels['moveup'] = 'Flytta upp filter';
$labels['movedown'] = 'Flytta ner filter';
$labels['filterallof'] = 'Filtrera på alla följande regler';
$labels['filteranyof'] = 'Filtrera på någon av följande regler';
$labels['filterany'] = 'Filtrera alla meddelanden';
$labels['filtercontains'] = 'innehåller';
$labels['filternotcontains'] = 'inte innehåller';
$labels['filteris'] = 'är lika med';
$labels['filterisnot'] = 'är inte lika med';
$labels['filterexists'] = 'finns';
$labels['filternotexists'] = 'inte finns';
$labels['filtermatches'] = 'matchar uttryck';
$labels['filternotmatches'] = 'inte matchar uttryck';
$labels['filterregex'] = 'matchar reguljärt uttryck';
$labels['filternotregex'] = 'inte matchar reguljärt uttryck';
$labels['filterunder'] = 'under';
$labels['filterover'] = 'över';
$labels['addrule'] = 'Lägg till regel';
$labels['delrule'] = 'Ta bort regel';
$labels['messagemoveto'] = 'Flytta meddelande till';
$labels['messageredirect'] = 'Ändra mottagare till';
$labels['messagecopyto'] = 'Kopiera meddelande till';
$labels['messagesendcopy'] = 'Skicka kopia av meddelande till';
$labels['messagereply'] = 'Besvara meddelande';
$labels['messagedelete'] = 'Ta bort meddelande';
$labels['messagediscard'] = 'Avböj med felmeddelande';
$labels['messagekeep'] = 'Behåll meddelande i Inkorg';
$labels['messagesrules'] = 'För inkommande meddelande';
$labels['messagesactions'] = 'Utför följande åtgärd';
$labels['add'] = 'Lägg till';
$labels['del'] = 'Ta bort';
$labels['sender'] = 'Avsändare';
$labels['recipient'] = 'Mottagare';
$labels['vacationaddr'] = 'Ytterligare mottagaradresser:';
$labels['vacationdays'] = 'Antal dagar mellan auto-svar:';
$labels['vacationinterval'] = 'Tid mellan auto-svar:';
+$labels['vacationreason'] = 'Meddelandetext (frånvaroanledning):';
+$labels['vacationsubject'] = 'Meddelandeämne:';
$labels['days'] = 'Dagar';
$labels['seconds'] = 'Sekunder';
-$labels['vacationreason'] = 'Meddelande i auto-svar:';
-$labels['vacationsubject'] = 'Meddelandeämne:';
$labels['rulestop'] = 'Avsluta filtrering';
$labels['enable'] = 'Aktivera/deaktivera';
$labels['filterset'] = 'Filtergrupp';
$labels['filtersets'] = 'Filtergrupper';
-$labels['filtersetadd'] = 'Lägg till filtergrupp';
+$labels['filtersetadd'] = 'Ny filtergrupp';
$labels['filtersetdel'] = 'Ta bort filtergrupp';
$labels['filtersetact'] = 'Aktivera filtergrupp';
$labels['filtersetdeact'] = 'Deaktivera filtergrupp';
$labels['filterdef'] = 'Filterdefinition';
$labels['filtersetname'] = 'Filtergruppsnamn';
$labels['newfilterset'] = 'Ny filtergrupp';
$labels['active'] = 'aktiv';
$labels['none'] = 'ingen';
$labels['fromset'] = 'från grupp';
$labels['fromfile'] = 'från fil';
$labels['filterdisabled'] = 'Filter deaktiverat';
$labels['countisgreaterthan'] = 'antal är större än';
$labels['countisgreaterthanequal'] = 'antal är större än eller lika med';
$labels['countislessthan'] = 'antal är mindre än';
$labels['countislessthanequal'] = 'antal är mindre än eller lika med';
$labels['countequals'] = 'antal är lika med';
$labels['countnotequals'] = 'antal är inte lika med';
$labels['valueisgreaterthan'] = 'värde är större än';
$labels['valueisgreaterthanequal'] = 'värde är större än eller lika med';
$labels['valueislessthan'] = 'värde är mindre än';
$labels['valueislessthanequal'] = 'värde är mindre än eller lika med';
$labels['valueequals'] = 'värde är lika med';
$labels['valuenotequals'] = 'värde är inte lika med';
$labels['setflags'] = 'Flagga meddelande';
$labels['addflags'] = 'Lägg till meddelandeflaggor';
$labels['removeflags'] = 'Ta bort meddelandeflaggor';
$labels['flagread'] = 'Läst';
$labels['flagdeleted'] = 'Borttaget';
$labels['flaganswered'] = 'Besvarat';
$labels['flagflagged'] = 'Flaggat';
$labels['flagdraft'] = 'Utkast';
$labels['setvariable'] = 'Sätt variabel';
$labels['setvarname'] = 'Variabelnamn:';
$labels['setvarvalue'] = 'Variabelvärde:';
$labels['setvarmodifiers'] = 'Modifierare:';
$labels['varlower'] = 'Gemener';
$labels['varupper'] = 'Versaler';
$labels['varlowerfirst'] = 'Första tecken gement';
$labels['varupperfirst'] = 'Första tecken versalt';
$labels['varquotewildcard'] = 'Koda specialtecken';
$labels['varlength'] = 'Längd';
$labels['notify'] = 'Skicka avisering';
-$labels['notifyaddress'] = 'Mottagaradress:';
-$labels['notifybody'] = 'Aviseringsmeddelande:';
-$labels['notifysubject'] = 'Aviseringsämne:';
-$labels['notifyfrom'] = 'Aviseringsavsändare:';
+$labels['notifytarget'] = 'Aviseringsmål:';
+$labels['notifymessage'] = 'Aviseringsmeddelande (valfritt):';
+$labels['notifyoptions'] = 'Aviseringstillval (valfritt):';
+$labels['notifyfrom'] = 'Aviseringsavsändare (valfri):';
$labels['notifyimportance'] = 'Prioritet:';
$labels['notifyimportancelow'] = 'Låg';
$labels['notifyimportancenormal'] = 'Normal';
$labels['notifyimportancehigh'] = 'Hög';
+$labels['notifymethodmailto'] = 'E-post';
+$labels['notifymethodtel'] = 'Telefon';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Skapa filter';
$labels['usedata'] = 'Använd följande information i filtret:';
$labels['nextstep'] = 'Nästa steg';
$labels['...'] = '...';
$labels['currdate'] = 'Dagens datum';
$labels['datetest'] = 'Datum';
$labels['dateheader'] = 'huvud:';
$labels['year'] = 'år';
$labels['month'] = 'månad';
$labels['day'] = 'dag';
$labels['date'] = 'datum (åååå-mm-dd)';
$labels['julian'] = 'datum (Julianskt)';
$labels['hour'] = 'timme';
$labels['minute'] = 'minut';
$labels['second'] = 'sekund';
$labels['time'] = 'tid (hh:mm:ss)';
$labels['iso8601'] = 'datum (ISO 8601)';
$labels['std11'] = 'datum (RFC 2822)';
$labels['zone'] = 'tidszon';
$labels['weekday'] = 'veckodag (0-6)';
$labels['advancedopts'] = 'Avancerade inställningar';
$labels['body'] = 'Meddelandeinnehåll';
$labels['address'] = 'adress';
$labels['envelope'] = 'kuvert';
$labels['modifier'] = 'modifierare:';
$labels['text'] = 'text';
$labels['undecoded'] = 'obearbetat (rå)';
$labels['contenttype'] = 'innehållstyp';
$labels['modtype'] = 'typ:';
$labels['allparts'] = 'allt';
$labels['domain'] = 'domän';
$labels['localpart'] = 'lokal del';
$labels['user'] = 'användare';
$labels['detail'] = 'detalj';
$labels['comparator'] = 'jämförelse:';
$labels['default'] = 'standard';
$labels['octet'] = 'strikt (oktalt)';
$labels['asciicasemap'] = 'teckenlägesokänslig (ascii-casemap)';
$labels['asciinumeric'] = 'numerisk (ascii-numeric)';
$labels['index'] = 'index:';
$labels['indexlast'] = 'omvänd';
+$labels['vacation'] = 'Frånvaro';
+$labels['vacation.reply'] = 'Besvara meddelande';
+$labels['vacation.advanced'] = 'Avancerade inställningar';
+$labels['vacation.subject'] = 'Ämne';
+$labels['vacation.body'] = 'Innehåll';
+$labels['vacation.start'] = 'Frånvaron börjar';
+$labels['vacation.end'] = 'Frånvaron slutar';
+$labels['vacation.status'] = 'Status';
+$labels['vacation.on'] = 'På';
+$labels['vacation.off'] = 'Av';
+$labels['vacation.addresses'] = 'Ytterligare mottagaradresser';
+$labels['vacation.interval'] = 'Svarsintervall';
+$labels['vacation.after'] = 'Placera frånvaroregel efter';
+$labels['vacation.saving'] = 'Sparar data...';
+$labels['vacation.action'] = 'Hantering av inkommande meddelanden';
+$labels['vacation.keep'] = 'Behåll';
+$labels['vacation.discard'] = 'Förkasta';
+$labels['vacation.redirect'] = 'Ändra mottagare till';
+$labels['vacation.copy'] = 'Skicka kopia till';
+$labels['arialabelfiltersetactions'] = 'Hantera filtergrupper';
+$labels['arialabelfilteractions'] = 'Hantera filter';
+$labels['arialabelfilterform'] = 'Filteregenskaper';
+$labels['ariasummaryfilterslist'] = 'Lista med filter';
+$labels['ariasummaryfiltersetslist'] = 'Lista med filtergrupper';
+$labels['filterstitle'] = 'Ändra filter för inkommande meddelanden';
+$labels['vacationtitle'] = 'Ändra regel för frånvaromeddelande';
$messages['filterunknownerror'] = 'Okänt serverfel';
$messages['filterconnerror'] = 'Anslutning till serverns filtertjänst misslyckades';
$messages['filterdeleteerror'] = 'Filtret kunde inte tas bort på grund av serverfel';
$messages['filterdeleted'] = 'Filtret är borttaget';
$messages['filtersaved'] = 'Filtret har sparats';
$messages['filtersaveerror'] = 'Filtret kunde inte sparas på grund av serverfel';
$messages['filterdeleteconfirm'] = 'Vill du ta bort det markerade filtret?';
$messages['ruledeleteconfirm'] = 'Vill du ta bort filterregeln?';
$messages['actiondeleteconfirm'] = 'Vill du ta bort filteråtgärden?';
$messages['forbiddenchars'] = 'Otillåtet tecken i fältet';
$messages['cannotbeempty'] = 'Fältet kan inte lämnas tomt';
$messages['ruleexist'] = 'Ett filter med angivet namn finns redan.';
$messages['setactivateerror'] = 'Filtergruppen kunde inte aktiveras på grund av serverfel';
$messages['setdeactivateerror'] = 'Filtergruppen kunde inte deaktiveras på grund av serverfel';
$messages['setdeleteerror'] = 'Filtergruppen kunde inte tas bort på grund av serverfel';
$messages['setactivated'] = 'Filtergruppen är aktiverad';
$messages['setdeactivated'] = 'Filtergruppen är deaktiverad';
$messages['setdeleted'] = 'Filtergruppen är borttagen';
$messages['setdeleteconfirm'] = 'Vill du ta bort filtergruppen?';
$messages['setcreateerror'] = 'Filtergruppen kunde inte läggas till på grund av serverfel';
$messages['setcreated'] = 'Filtergruppen har lagts till';
$messages['activateerror'] = 'Kunde inte aktivera filter på grund av serverfel.';
$messages['deactivateerror'] = 'Kunde inte deaktivera filter på grund av serverfel.';
$messages['deactivated'] = 'Filter aktiverat.';
$messages['activated'] = 'Filter deaktiverat.';
$messages['moved'] = 'Filter flyttat.';
$messages['moveerror'] = 'Kunde inte flytta filter på grund av serverfel.';
$messages['nametoolong'] = 'För långt namn.';
$messages['namereserved'] = 'Reserverat namn.';
$messages['setexist'] = 'Filtergrupp finns redan.';
$messages['nodata'] = 'Minst en position måste väljas!';
$messages['invaliddateformat'] = 'Ogiltigt datum eller del av datumformat';
+$messages['saveerror'] = 'Datan kunde inte sparas på grund av serverfel.';
+$messages['vacationsaved'] = 'Frånvarodatan har sparats.';
+$messages['emptyvacationbody'] = 'Text för frånvaromeddelande saknas!';
?>
diff --git a/lib/plugins/managesieve/localization/tr_TR.inc b/lib/plugins/managesieve/localization/tr_TR.inc
index b0b09e1..c618386 100644
--- a/lib/plugins/managesieve/localization/tr_TR.inc
+++ b/lib/plugins/managesieve/localization/tr_TR.inc
@@ -1,192 +1,224 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filtreler';
$labels['managefilters'] = 'Gelen e-posta filtrelerini yönet';
$labels['filtername'] = 'Filtre adı';
$labels['newfilter'] = 'Yeni filtre';
$labels['filteradd'] = 'Filtre ekle';
$labels['filterdel'] = 'Filtre Sil';
$labels['moveup'] = 'Yukarı taşı';
$labels['movedown'] = 'Aşağı taşı';
$labels['filterallof'] = 'Aşağıdaki kuralların hepsine uyan';
$labels['filteranyof'] = 'Aşağıdaki kuralların herhangi birine uyan';
$labels['filterany'] = 'Tüm mesajlar';
$labels['filtercontains'] = 'içeren';
$labels['filternotcontains'] = 'içermeyen';
$labels['filteris'] = 'eşittir';
$labels['filterisnot'] = 'eşit değildir';
$labels['filterexists'] = 'mevcut';
$labels['filternotexists'] = 'mevcut değil';
$labels['filtermatches'] = 'ifadeye uyan';
$labels['filternotmatches'] = 'ifadeye uymayan';
$labels['filterregex'] = 'düzenli ifadeye uyan';
$labels['filternotregex'] = 'düzenli ifadeye uymayan';
$labels['filterunder'] = 'altında';
$labels['filterover'] = 'üzerinde';
$labels['addrule'] = 'Kural ekle';
$labels['delrule'] = 'Kuralı sil';
$labels['messagemoveto'] = 'mesajı taşı';
$labels['messageredirect'] = 'mesajı yönlendir';
$labels['messagecopyto'] = 'Mesajı kopyala';
$labels['messagesendcopy'] = 'mesajın kopyasını gönder';
$labels['messagereply'] = 'mesajla birlikte cevap ver';
$labels['messagedelete'] = 'Mesajı sil';
$labels['messagediscard'] = 'mesajı yok say';
$labels['messagekeep'] = 'Mesajı Gelen Kutusunda tut.';
$labels['messagesrules'] = 'Gelen e-postalar için:';
$labels['messagesactions'] = '... aşağıdaki aksiyonları çalıştır:';
$labels['add'] = 'Ekle';
$labels['del'] = 'Sil';
$labels['sender'] = 'Gönderici';
$labels['recipient'] = 'Alıcı';
$labels['vacationaddr'] = 'Ek e-posta adres(ler)im:';
$labels['vacationdays'] = 'Ne sıklıkla mesajlar gönderilir(gün)';
$labels['vacationinterval'] = 'Ne kadar sıklıkla mesaj gönderirsiniz:';
-$labels['days'] = 'günler';
-$labels['seconds'] = 'saniyeler';
$labels['vacationreason'] = 'Mesaj gövdesi(tatil sebebi):';
$labels['vacationsubject'] = 'Mesaj konusu:';
+$labels['days'] = 'günler';
+$labels['seconds'] = 'saniyeler';
$labels['rulestop'] = 'Kuralları değerlendirmeyi bitir';
$labels['enable'] = 'Etkinleştir/Etkisiz Kıl';
$labels['filterset'] = 'Filtre seti';
$labels['filtersets'] = 'Filtre setleri';
$labels['filtersetadd'] = 'Filtre seti ekle';
$labels['filtersetdel'] = 'Mevcut filtre setini sil';
$labels['filtersetact'] = 'Mevcut filtre setini etkinleştir';
$labels['filtersetdeact'] = 'Mevcut filtre setini etkinsizleştir';
$labels['filterdef'] = 'Filtre tanımı';
$labels['filtersetname'] = 'Filtre seti adı';
$labels['newfilterset'] = 'Yeni filtre seti';
$labels['active'] = 'etkin';
$labels['none'] = 'hiçbiri';
$labels['fromset'] = 'gönderici seti';
$labels['fromfile'] = 'gönderici dosya';
$labels['filterdisabled'] = 'Filtre iptal edildi';
$labels['countisgreaterthan'] = 'toplamı büyük';
$labels['countisgreaterthanequal'] = 'toplamı büyük veya eşit';
$labels['countislessthan'] = 'toplamı az';
$labels['countislessthanequal'] = 'toplamı daha az veya eşit';
$labels['countequals'] = 'toplamı eşit';
$labels['countnotequals'] = 'toplamı eşit değil';
$labels['valueisgreaterthan'] = 'değeri büyük';
$labels['valueisgreaterthanequal'] = 'değeri büyük veya eşit';
$labels['valueislessthan'] = 'değer az';
$labels['valueislessthanequal'] = 'değer daha az veya eşit';
$labels['valueequals'] = 'değer eşit';
$labels['valuenotequals'] = 'değer eşit değil';
$labels['setflags'] = 'bayrakları mesaja set et';
$labels['addflags'] = 'Bayrakları mesaja ekle';
$labels['removeflags'] = 'Bayrakları mesajdan sil';
$labels['flagread'] = 'Oku';
$labels['flagdeleted'] = 'Silindi';
$labels['flaganswered'] = 'Cevaplanmış';
$labels['flagflagged'] = 'İşaretli';
$labels['flagdraft'] = 'Taslak';
$labels['setvariable'] = 'Değişken tanımla';
-$labels['setvarname'] = 'Değişken adı';
+$labels['setvarname'] = 'Değişken adı:';
$labels['setvarvalue'] = 'Değişken değeri:';
$labels['setvarmodifiers'] = 'Değiştiriciler:';
$labels['varlower'] = 'küçük harf';
$labels['varupper'] = 'büyük harf';
$labels['varlowerfirst'] = 'İlk karakter küçük harf';
$labels['varupperfirst'] = 'İlk karakter büyük harf';
$labels['varquotewildcard'] = 'özel karakterleri tırnak içine al';
$labels['varlength'] = 'uzunluk';
$labels['notify'] = 'Bildirim gönder';
-$labels['notifyaddress'] = 'Alıcı e-posta adresi';
-$labels['notifybody'] = 'Bildirim gövdesi:';
-$labels['notifysubject'] = 'Bildirim konusu:';
-$labels['notifyfrom'] = 'Bildirim göndericisi:';
-$labels['notifyimportance'] = 'Önem derecesi';
+$labels['notifytarget'] = 'Bildirim hedefi:';
+$labels['notifymessage'] = 'Bildirim mesajı (tercihe bağlı):';
+$labels['notifyoptions'] = 'Bildirim tercihleri (tercihe bağlı):';
+$labels['notifyfrom'] = 'Bildirim göndericisi (tercihe bağlı):';
+$labels['notifyimportance'] = 'Önem derecesi:';
$labels['notifyimportancelow'] = 'düşük';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'yüksek';
+$labels['notifymethodmailto'] = 'E-posta';
+$labels['notifymethodtel'] = 'Telefon';
+$labels['notifymethodsms'] = 'SMS';
$labels['filtercreate'] = 'Süzgeç oluştur';
$labels['usedata'] = 'Aşağıdaki verileri süzgeçte kullan';
$labels['nextstep'] = 'Sonraki adım';
$labels['...'] = '...';
$labels['currdate'] = 'Mevcut tarih';
$labels['datetest'] = 'Tarih';
$labels['dateheader'] = 'Başlık';
$labels['year'] = 'yıl';
$labels['month'] = 'ay';
$labels['day'] = 'gün';
$labels['date'] = 'tarih (yyyy-aa-gg)';
$labels['julian'] = 'tarih (julian)';
$labels['hour'] = 'saat';
$labels['minute'] = 'dakika';
$labels['second'] = 'saniye';
$labels['time'] = 'saat (ss:dd:ss)';
$labels['iso8601'] = 'tarih (ISO8601)';
$labels['std11'] = 'tarih (RFC2822)';
$labels['zone'] = 'saat-dilimi';
$labels['weekday'] = 'Hafta günleri (0-6)';
$labels['advancedopts'] = 'Gelişmiş seçenekler';
$labels['body'] = 'Gövde';
$labels['address'] = 'adres';
$labels['envelope'] = 'zarf';
$labels['modifier'] = 'değiştirici';
$labels['text'] = 'metin';
$labels['undecoded'] = 'çözülmemiş(ham)';
$labels['contenttype'] = 'içerik türü';
$labels['modtype'] = 'tip:';
$labels['allparts'] = 'hepsi';
$labels['domain'] = 'alan adı';
$labels['localpart'] = 'yerel parça';
$labels['user'] = 'kullanıcı';
$labels['detail'] = 'detay';
$labels['comparator'] = 'karşılaştırıcı';
$labels['default'] = 'öntanımlı';
$labels['octet'] = 'sıkı(oktet)';
$labels['asciicasemap'] = 'büyük küçük harf duyarsız(ascii-casemap)';
$labels['asciinumeric'] = 'sayı (ascii-numeric)';
$labels['index'] = 'indeks:';
$labels['indexlast'] = 'geriye yönelik';
+$labels['vacation'] = 'Tatil';
+$labels['vacation.reply'] = 'Cevap mesajı';
+$labels['vacation.advanced'] = 'Gelişmiş seçenekler';
+$labels['vacation.subject'] = 'Konu';
+$labels['vacation.body'] = 'Gövde';
+$labels['vacation.start'] = 'Tatil başlangıcı';
+$labels['vacation.end'] = 'Tatil bitişi';
+$labels['vacation.status'] = 'Durum';
+$labels['vacation.on'] = 'Etkin';
+$labels['vacation.off'] = 'Devre dışı';
+$labels['vacation.addresses'] = 'Ek adresler';
+$labels['vacation.interval'] = 'Cevap aralığı';
+$labels['vacation.after'] = 'Şundan sonra tatil kuralı koy';
+$labels['vacation.saving'] = 'Veri kaydediliyor...';
+$labels['vacation.action'] = 'Gelen mesaj aksiyonu';
+$labels['vacation.keep'] = 'Koru';
+$labels['vacation.discard'] = 'Yoksay';
+$labels['vacation.redirect'] = 'Şuraya yönlendir';
+$labels['vacation.copy'] = 'Şuraya kopya gönder';
+$labels['arialabelfiltersetactions'] = 'Filtre seti aksiyonları';
+$labels['arialabelfilteractions'] = 'Filtre aksiyonları';
+$labels['arialabelfilterform'] = 'Filtre özellikleri';
+$labels['ariasummaryfilterslist'] = 'Filtre listesi';
+$labels['ariasummaryfiltersetslist'] = 'Filtre seti listesi';
+$labels['filterstitle'] = 'Gelen e-posta filtrelerini düzenle';
+$labels['vacationtitle'] = 'Ofis dışında kuralını düzenle';
$messages['filterunknownerror'] = 'Bilinmeyen sunucu hatası.';
$messages['filterconnerror'] = 'Sunucuya bağlanamıyor.';
$messages['filterdeleteerror'] = 'Filtre silinemedi. Sunucuda hata oluştu.';
$messages['filterdeleted'] = 'Filtre başarıyla silindi.';
-$messages['filtersaved'] = 'Filter başarıyla kaydedildi.';
+$messages['filtersaved'] = 'Filtre başarıyla kaydedildi.';
$messages['filtersaveerror'] = 'Filtre kaydedilemedi. Sunucuda hata oluştu.';
$messages['filterdeleteconfirm'] = 'Seçilen filtreleri gerçekten silmek istiyor musun?';
$messages['ruledeleteconfirm'] = 'Seçili kuralları silmek istediğinizden emin misiniz?';
$messages['actiondeleteconfirm'] = 'Seçili aksiyonları silmek istediğinizden emin misiniz?';
$messages['forbiddenchars'] = 'Alanda izin verilmeyen karakterler var.';
$messages['cannotbeempty'] = 'Alan boş olmaz';
$messages['ruleexist'] = 'Belirtilen isimde bir filtre zaten var.';
$messages['setactivateerror'] = 'Seçilen filtreler etkinleştirilemedi. Sunucuda hata oluştu.';
$messages['setdeactivateerror'] = 'Seçilen filtreler pasifleştirilemedi. Sunucuda hata oluştu.';
$messages['setdeleteerror'] = 'Seçilen filtreler silinemedi. Sunucuda hata oluştu.';
$messages['setactivated'] = 'Filtreler başarıyla etkinleştirilemedi.';
$messages['setdeactivated'] = 'Filtreler başarıyla pasifleştirildi.';
$messages['setdeleted'] = 'Filtre seti başarıyla silindi.';
$messages['setdeleteconfirm'] = 'Seçilen filtre setlerini silmek istediğinizden emin misiniz?';
$messages['setcreateerror'] = 'Filtre setleri oluşturulamadı. Sunucuda hata oluştu.';
$messages['setcreated'] = 'Filtre setleri başarıyla oluşturuldu.';
$messages['activateerror'] = 'Seçilen filtre(ler) etkinleştirilemedi. Sunucuda hata oluştu.';
$messages['deactivateerror'] = 'Seçilen filtre(ler) pasifleştirilemedi. Sunucuda hata oluştu.';
$messages['deactivated'] = 'Filtre(ler) başarıyla etkinleştirildi.';
$messages['activated'] = 'Filtre(ler) başarıyla iptal edildi.';
$messages['moved'] = 'Filtre başarıyla taşındı.';
$messages['moveerror'] = 'Seçilen filtre taşınamadı. Sunucuda hata oluştu.';
$messages['nametoolong'] = 'İsim çok uzun.';
$messages['namereserved'] = 'rezerve edilmiş isim.';
$messages['setexist'] = 'Set zaten var.';
$messages['nodata'] = 'En az bir pozisyon seçilmelidir.';
$messages['invaliddateformat'] = 'geçersiz tarih veya tarih biçimi';
+$messages['saveerror'] = 'Veri kaydedilemedi. Sunucuda hata oluştu.';
+$messages['vacationsaved'] = 'Tatil verisi başarıyla kaydedildi.';
+$messages['emptyvacationbody'] = 'Tatil mesajı metni gerekmektedir.';
?>
diff --git a/lib/plugins/managesieve/localization/uk_UA.inc b/lib/plugins/managesieve/localization/uk_UA.inc
index d85b03b..fce7867 100644
--- a/lib/plugins/managesieve/localization/uk_UA.inc
+++ b/lib/plugins/managesieve/localization/uk_UA.inc
@@ -1,144 +1,140 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Фільтри';
$labels['managefilters'] = 'Керування фільтрами вхідної пошти';
$labels['filtername'] = 'Назва фільтру';
$labels['newfilter'] = 'Новий фільтр';
$labels['filteradd'] = 'Додати фільтр';
$labels['filterdel'] = 'Видалити фільтр';
$labels['moveup'] = 'Пересунути вгору';
$labels['movedown'] = 'Пересунути вниз';
$labels['filterallof'] = 'задовольняє усім наступним умовам';
$labels['filteranyof'] = 'задовольняє будь-якій з умов';
$labels['filterany'] = 'всі повідомлення';
$labels['filtercontains'] = 'містить';
$labels['filternotcontains'] = 'не містить';
$labels['filteris'] = 'ідентичний до';
$labels['filterisnot'] = 'не ідентичний до';
$labels['filterexists'] = 'існує';
$labels['filternotexists'] = 'не існує';
$labels['filterunder'] = 'менше, ніж';
$labels['filterover'] = 'більше, ніж';
$labels['addrule'] = 'Додати правило';
$labels['delrule'] = 'Видалити правило';
$labels['messagemoveto'] = 'Пересунути повідомлення до';
$labels['messageredirect'] = 'Перенаправити повідомлення до';
$labels['messagecopyto'] = 'Копіювати листа до';
$labels['messagesendcopy'] = 'Надсилати копію листа на';
$labels['messagereply'] = 'Автовідповідач';
$labels['messagedelete'] = 'Видалити повідомлення';
$labels['messagediscard'] = 'Відхилити з повідомленням';
$labels['messagekeep'] = 'Залишити лист у Вхідних';
$labels['messagesrules'] = 'Для вхідної пошти';
$labels['messagesactions'] = '... виконати дію:';
$labels['add'] = 'Додати';
$labels['del'] = 'Видалити';
$labels['sender'] = 'Відправник';
$labels['recipient'] = 'Отримувач';
$labels['vacationaddr'] = 'Додаткова адреса(и):';
$labels['vacationdays'] = 'Як часто повторювати (у днях):';
-$labels['days'] = 'днів';
-$labels['seconds'] = 'секунд';
$labels['vacationreason'] = 'Текст повідомлення:';
$labels['vacationsubject'] = 'Тема листа:';
+$labels['days'] = 'днів';
+$labels['seconds'] = 'секунд';
$labels['rulestop'] = 'Зупинити перевірку правил';
$labels['enable'] = 'Увімкнути/Вимкнуни';
$labels['filterset'] = 'Набір фільтрів';
$labels['filtersetadd'] = 'Додати набір фільтрів';
$labels['filtersetdel'] = 'Видалити поточний набір';
$labels['filtersetact'] = 'Активувати поточний набір';
$labels['filterdef'] = 'Параметри фільтру';
$labels['filtersetname'] = 'Назва набору фільтрів';
$labels['newfilterset'] = 'Новий набір фільтрів';
$labels['active'] = 'активний';
$labels['none'] = 'нічого';
$labels['fromset'] = 'з набору';
$labels['fromfile'] = 'з файлу';
$labels['filterdisabled'] = 'Фільтр вимкнено';
$labels['countisgreaterthan'] = 'лічильник більший за';
$labels['countisgreaterthanequal'] = 'лічильник більший або рівний ';
$labels['countislessthan'] = 'лічильник менший';
$labels['countislessthanequal'] = 'льчильник менший або рівний';
$labels['countequals'] = 'лічильник рівний';
$labels['countnotequals'] = 'лічильник рівний';
$labels['valueisgreaterthan'] = 'значення більше за';
$labels['valueisgreaterthanequal'] = 'значення більше або рівне';
$labels['valueislessthan'] = 'значення менше за';
$labels['valueislessthanequal'] = 'значення менше або рівне';
$labels['valueequals'] = 'значення рівне';
$labels['valuenotequals'] = 'значення не рівне';
$labels['flagdraft'] = 'Чернетка';
$labels['setvariable'] = 'Встановити змінну';
$labels['setvarname'] = 'Назва змінної:';
$labels['setvarvalue'] = 'Значення змінної:';
$labels['setvarmodifiers'] = 'Модифікатори:';
$labels['varlower'] = 'нижній регістр';
$labels['varupper'] = 'верхній регістр';
$labels['varlowerfirst'] = 'перший символ в нижньому регістрі';
$labels['varupperfirst'] = 'перший символ в верхньому регістрі';
$labels['varlength'] = 'довжина';
$labels['notify'] = 'Надсилати сповіщення';
-$labels['notifyaddress'] = 'На електронну адресу:';
-$labels['notifybody'] = 'Тіло сповіщення:';
-$labels['notifysubject'] = 'Тема сповіщення:';
-$labels['notifyfrom'] = 'Відправник сповіщення:';
$labels['filtercreate'] = 'Створити фільтр';
$labels['nextstep'] = 'Наступний крок';
$labels['...'] = '...';
$labels['currdate'] = 'Поточна дата';
$labels['datetest'] = 'Дата';
$labels['dateheader'] = 'шапка:';
$labels['year'] = 'рік';
$labels['month'] = 'місяць';
$labels['day'] = 'день';
$labels['date'] = 'дата (рррр-мм-дд)';
$labels['hour'] = 'година';
$labels['minute'] = 'хвилина';
$labels['second'] = 'секунда';
$labels['time'] = 'час (гг:хх:сс)';
$labels['iso8601'] = 'дата (ISO8601)';
$labels['std11'] = 'дата (RFC2822)';
$labels['zone'] = 'часовий пояс';
$labels['advancedopts'] = 'Розширені параметри';
$labels['body'] = 'Тіло';
$labels['address'] = 'адреса';
$labels['text'] = 'текст';
$labels['modtype'] = 'тип:';
$labels['allparts'] = 'все';
$labels['domain'] = 'домен';
$labels['localpart'] = 'локальна частина';
$labels['user'] = 'користувач';
$labels['detail'] = 'деталь';
$labels['default'] = 'типово';
$labels['index'] = 'індекс:';
$messages['filterunknownerror'] = 'Невідома помилка сервера';
$messages['filterconnerror'] = 'Неможливо з\'єднатися з сервером';
$messages['filterdeleted'] = 'Фільтр успішно видалено';
$messages['filtersaved'] = 'Фільтр успішно збережено';
$messages['filterdeleteconfirm'] = 'Ви дійсно хочете видалити обраний фільтр?';
$messages['ruledeleteconfirm'] = 'Ви дійсно хочете видалити обране правило?';
$messages['actiondeleteconfirm'] = 'Ви дійсно хочете видалити обрану дію?';
$messages['forbiddenchars'] = 'Введено заборонений символ';
$messages['cannotbeempty'] = 'Поле не може бути пустим';
$messages['setactivated'] = 'Набір фільтрів активовано успішно';
$messages['setdeleted'] = 'Набір фільтрів видалено успішно';
$messages['setdeleteconfirm'] = 'Ви впевнені, що хочете видалити обраний набір?';
$messages['setcreated'] = 'Набір фільтрів створено успішно';
$messages['moveerror'] = 'Неможливо перемістити обраний фільтр. Помилка сервера.';
$messages['nametoolong'] = 'Не вдалося створити набір. Занадто довга назва';
?>
diff --git a/lib/plugins/managesieve/localization/vi_VN.inc b/lib/plugins/managesieve/localization/vi_VN.inc
index c791381..d22ff7e 100644
--- a/lib/plugins/managesieve/localization/vi_VN.inc
+++ b/lib/plugins/managesieve/localization/vi_VN.inc
@@ -1,192 +1,209 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Bộ lọc';
$labels['managefilters'] = 'Quản lý bộ lọc thư đến';
$labels['filtername'] = 'Lọc tên';
$labels['newfilter'] = 'Bộ lọc mới';
$labels['filteradd'] = 'Thêm bộ lọc';
$labels['filterdel'] = 'Xóa bộ lọc';
$labels['moveup'] = 'Chuyển lên';
$labels['movedown'] = 'Chuyển xuống';
$labels['filterallof'] = 'Phù hợp với tất cả các qui luật sau đây';
$labels['filteranyof'] = 'Phù hợp với bất kỳ qui luật nào sau đây';
$labels['filterany'] = 'Tất cả tin nhắn';
$labels['filtercontains'] = 'Bao gồm';
$labels['filternotcontains'] = 'Không bao gồm';
$labels['filteris'] = 'Bằng với';
$labels['filterisnot'] = 'Không bằng với';
$labels['filterexists'] = 'Tồn tại';
$labels['filternotexists'] = 'Không tồn tại';
$labels['filtermatches'] = 'Tương ứng với cách diễn đạt';
$labels['filternotmatches'] = 'Không tương ứng với cách diễn đạt';
$labels['filterregex'] = 'Tương ứng với cách diễn đạt thông thường';
$labels['filternotregex'] = 'Không phù hợp với cách diễn đạt thông thường';
$labels['filterunder'] = 'Dưới';
$labels['filterover'] = 'Hơn';
$labels['addrule'] = 'Thêm qui luật';
$labels['delrule'] = 'Xóa qui luật';
$labels['messagemoveto'] = 'Chuyển tin nhắn tới';
$labels['messageredirect'] = 'Gửi lại tin nhắn tới';
$labels['messagecopyto'] = 'Sao chép tin nhắn tới';
$labels['messagesendcopy'] = 'Gửi bản sao chép tin nhắn tới';
$labels['messagereply'] = 'Trả lời tin nhắn';
$labels['messagedelete'] = 'Xóa thư';
$labels['messagediscard'] = 'Loại bỏ với tin nhắn';
$labels['messagekeep'] = 'Giữ thư ở Hộp thư chính';
$labels['messagesrules'] = 'Với thư đến';
$labels['messagesactions'] = 'Thực hiện các hành động sau:';
$labels['add'] = 'Thêm';
$labels['del'] = 'Xoá';
$labels['sender'] = 'Người gửi';
$labels['recipient'] = 'Người nhận';
$labels['vacationaddr'] = '(Các) Địa chỉ email bổ sung của tôi:';
$labels['vacationdays'] = 'Số lần gửi thư (trong ngày)';
$labels['vacationinterval'] = 'Tần suất gửi thư:';
-$labels['days'] = 'ngày';
-$labels['seconds'] = 'giây';
$labels['vacationreason'] = 'Nội dung chính';
$labels['vacationsubject'] = 'Tiêu đề thư';
+$labels['days'] = 'ngày';
+$labels['seconds'] = 'giây';
$labels['rulestop'] = 'Ngừng đánh giá qui luật';
$labels['enable'] = 'Kích hoạt/Không kích hoạt';
$labels['filterset'] = 'Đặt các bộ lọc';
$labels['filtersets'] = 'Thiết lập bộ lọc';
$labels['filtersetadd'] = 'Thêm bộ lọc';
$labels['filtersetdel'] = 'Xóa bộ lọc hiện tại';
$labels['filtersetact'] = 'Kích hoạt bộ lọc hiện tại';
$labels['filtersetdeact'] = 'Ngừng kích hoạt bộ lọc hiện tai';
$labels['filterdef'] = 'Định nghĩa bộ lọc';
$labels['filtersetname'] = 'Tên bộ lọc';
$labels['newfilterset'] = 'Thiết lập bộ lọc mới';
$labels['active'] = 'Kích hoạt';
$labels['none'] = 'Không có';
$labels['fromset'] = 'Từ thiết lập';
$labels['fromfile'] = 'Từ hồ sơ';
$labels['filterdisabled'] = 'Bộ lọc được ngừng hoạt động';
$labels['countisgreaterthan'] = 'Đếm lớn hơn';
$labels['countisgreaterthanequal'] = 'Đếm lớn hơn hoặc bằng';
$labels['countislessthan'] = 'Đếm ít hơn';
$labels['countislessthanequal'] = 'Đếm ít hơn hoặc bằng';
$labels['countequals'] = 'Đếm bằng';
$labels['countnotequals'] = 'đếm không bằng với';
$labels['valueisgreaterthan'] = 'Giá trị lớn hơn';
$labels['valueisgreaterthanequal'] = 'Giá trị lớn hơn hoặc bằng';
$labels['valueislessthan'] = 'Giá trị nhỏ hơn';
$labels['valueislessthanequal'] = 'Giá trị nhỏ hơn hoặc bằng';
$labels['valueequals'] = 'Giá trị bằng';
$labels['valuenotequals'] = 'giá trị không bằng với';
$labels['setflags'] = 'Thiết lập đánh dấu cho thư';
$labels['addflags'] = 'Thêm đánh dấu cho thư';
$labels['removeflags'] = 'Bỏ đánh dấu khỏi thư';
$labels['flagread'] = 'Đọc';
$labels['flagdeleted'] = 'Đã được xóa';
$labels['flaganswered'] = 'Đã trả lời';
$labels['flagflagged'] = 'Đã đánh dấu';
$labels['flagdraft'] = 'Nháp';
$labels['setvariable'] = 'Đặt biến';
$labels['setvarname'] = 'Tên biến:';
$labels['setvarvalue'] = 'Giá trị biến:';
$labels['setvarmodifiers'] = 'Bộ chia:';
$labels['varlower'] = 'viết thường';
$labels['varupper'] = 'viết hoa';
$labels['varlowerfirst'] = 'chữ cái đầu viết thường';
$labels['varupperfirst'] = 'chữ cái đầu viết hoa';
$labels['varquotewildcard'] = 'trích dẫn ký tự đặc biệt';
$labels['varlength'] = 'độ dài';
$labels['notify'] = 'Gửi thông báo';
-$labels['notifyaddress'] = 'Gửi đến địa chỉ email:';
-$labels['notifybody'] = 'Nội dung thông báo:';
-$labels['notifysubject'] = 'Tiêu đề thông báo:';
-$labels['notifyfrom'] = 'Người gửi thông báo:';
+$labels['notifytarget'] = 'Mục tiêu thông báo:';
+$labels['notifymessage'] = 'Nội dung thông báo (tuỳ chọn):';
+$labels['notifyoptions'] = 'Lựa chọn thông báo (tuỳ chọn):';
+$labels['notifyfrom'] = 'Người gửi thông báo (tuỳ chọn):';
$labels['notifyimportance'] = 'Mức độ quan trọng:';
$labels['notifyimportancelow'] = 'thấp';
$labels['notifyimportancenormal'] = 'vừa phải';
$labels['notifyimportancehigh'] = 'cao';
+$labels['notifymethodmailto'] = 'Thư điện tử';
+$labels['notifymethodtel'] = 'Điện thoại';
+$labels['notifymethodsms'] = 'Tin nhắn';
$labels['filtercreate'] = 'Tạo bộ lọc';
$labels['usedata'] = 'Dùng dữ liệu trong bộ lọc sau:';
$labels['nextstep'] = 'Bước tiếp theo';
$labels['...'] = '…';
$labels['currdate'] = 'Ngày hiện tại';
$labels['datetest'] = 'Ngày';
$labels['dateheader'] = 'tiêu đề:';
$labels['year'] = 'năm';
$labels['month'] = 'tháng';
$labels['day'] = 'ngày';
$labels['date'] = 'ngày (cú pháp: năm-tháng-ngày)';
$labels['julian'] = 'ngày (theo kiểu Julian)';
$labels['hour'] = 'giờ';
$labels['minute'] = 'phút';
$labels['second'] = 'giây';
$labels['time'] = 'giờ (cú pháp: giờ:phút:giây)';
$labels['iso8601'] = 'ngày (theo chuẩn ISO 8601)';
$labels['std11'] = 'ngày (theo chuẩn RFC 2822)';
$labels['zone'] = 'vùng thời gian';
$labels['weekday'] = 'ngày trog tuần (0-6)';
$labels['advancedopts'] = 'Tùy chọn tính năng cao hơn';
$labels['body'] = 'Nội dung';
$labels['address'] = 'Địa chỉ';
$labels['envelope'] = 'Phong bì';
$labels['modifier'] = 'Bổ nghĩa';
$labels['text'] = 'Văn bản';
$labels['undecoded'] = 'Chưa được giải mã (nguyên bản)';
$labels['contenttype'] = 'Kiểu mẫu nội dung';
$labels['modtype'] = 'Kiểu:';
$labels['allparts'] = 'Tất cả';
$labels['domain'] = 'Phạm vi';
$labels['localpart'] = 'Phần nội bộ';
$labels['user'] = 'Người dùng';
$labels['detail'] = 'Chi tiết';
$labels['comparator'] = 'Vật so sánh';
$labels['default'] = 'Mặc định';
$labels['octet'] = 'Khắt khe';
$labels['asciicasemap'] = 'Không phân biệt chữ hoa chữ thường';
$labels['asciinumeric'] = 'Bảng mã ASCII';
$labels['index'] = 'chỉ mục:';
$labels['indexlast'] = 'ngược';
+$labels['vacation'] = 'Thiết lập tự động trả lời trong kỳ nghỉ';
+$labels['vacation.reply'] = 'Trả lời thư';
+$labels['vacation.advanced'] = 'Tùy chọn tính năng cao hơn';
+$labels['vacation.subject'] = 'Tiêu đề';
+$labels['vacation.body'] = 'Nội dung thư';
+$labels['vacation.status'] = 'Trạng thái';
+$labels['vacation.on'] = 'Bật';
+$labels['vacation.off'] = 'Tắt';
+$labels['vacation.addresses'] = 'Các địa chỉ bổ sung của tôi';
+$labels['vacation.interval'] = 'Khoảng thời gian trả lời';
+$labels['vacation.after'] = 'Đặt quy định kỳ nghỉ sau';
+$labels['vacation.saving'] = 'Lưu lại dữ liệu...';
$messages['filterunknownerror'] = 'Không tìm được lỗi máy chủ';
$messages['filterconnerror'] = 'Không kết nối được với máy chủ.';
$messages['filterdeleteerror'] = 'Không thể xóa bộ lọc. Xuất hiện lỗi ở máy chủ';
$messages['filterdeleted'] = 'Xóa bộ lọc thành công';
$messages['filtersaved'] = 'Lưu bộ lọc thành công';
$messages['filtersaveerror'] = 'Không thể lưu bộ lọc. Xuất hiện lỗi ở máy chủ';
$messages['filterdeleteconfirm'] = 'Bạn có thực sự muốn xóa bộ lọc được chọn?';
$messages['ruledeleteconfirm'] = 'Bạn có chắc chắn muốn xóa qui luật được chọn?';
$messages['actiondeleteconfirm'] = 'Bạn có chắc chắn muốn xóa hành động được chọn?';
$messages['forbiddenchars'] = 'Ký tự bị cấm trong ô';
$messages['cannotbeempty'] = 'Ô không thể bị bỏ trống';
$messages['ruleexist'] = 'Đã tồn tại bộ lọc với tên cụ thế';
$messages['setactivateerror'] = 'Không thể kích hoạt bộ lọc được lựa chọn. Xuất hiện lỗi ở máy chủ';
$messages['setdeactivateerror'] = 'Không thể tắt bộ lọc được lựa chọn. Xuất hiện lỗi ở máy chủ';
$messages['setdeleteerror'] = 'Không thể xóa bộ lọc được lựa chọn. Xuất hiện lỗi ở máy chủ.';
$messages['setactivated'] = 'Bộ lọc được khởi động thành công';
$messages['setdeactivated'] = 'Ngừng kích hoạt bộ lọc thành công';
$messages['setdeleted'] = 'Xóa bộ lọc thành công';
$messages['setdeleteconfirm'] = 'Bạn có chắc bạn muốn xóa thiết lập bộ lọc được chọn?';
$messages['setcreateerror'] = 'Không thể tạo thiết lập bộ lọc. Có lỗi xuất hiện ở máy chủ';
$messages['setcreated'] = 'Thiết lập bộ lọc được tạo thành công';
$messages['activateerror'] = 'Không thể khởi động (các) bộ lọc được chọn. Có lỗi xuất hiện ở máy chủ';
$messages['deactivateerror'] = 'Không thể tắt (các) bộ lọc đã chọn. Có lỗi xuất hiện ở máy chủ';
$messages['deactivated'] = 'Bộ lọc được khởi động thành công';
$messages['activated'] = 'Bộ lọc được tắt thành công';
$messages['moved'] = 'Bộ lọc được chuyển đi thành công';
$messages['moveerror'] = 'Không thể chuyển bộ lọc đã chọn. Có lỗi xuất hiện ở máy chủ.';
$messages['nametoolong'] = 'Tên quá dài';
$messages['namereserved'] = 'Tên đã được bảo vệ';
$messages['setexist'] = 'Thiết lập đã tồn tại';
$messages['nodata'] = 'Ít nhất một vị trí phải được chọn';
$messages['invaliddateformat'] = 'Lỗi không đúng cú pháp ngày hoặc nhập ngày sai';
+$messages['saveerror'] = 'Không thể lưu trữ dữ liệu. Xuất hiện lỗi ở máy chủ.';
+$messages['vacationsaved'] = 'Thiết lập kỳ nghỉ đã được lưu lại thành công.';
?>
diff --git a/lib/plugins/managesieve/localization/zh_CN.inc b/lib/plugins/managesieve/localization/zh_CN.inc
index 5c38cfe..a39b312 100644
--- a/lib/plugins/managesieve/localization/zh_CN.inc
+++ b/lib/plugins/managesieve/localization/zh_CN.inc
@@ -1,170 +1,166 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = '过滤器';
$labels['managefilters'] = '管理邮件过滤规则';
$labels['filtername'] = '过滤规则名称';
$labels['newfilter'] = '新建过滤规则';
$labels['filteradd'] = '添加过滤规则';
$labels['filterdel'] = '删除过滤规则';
$labels['moveup'] = '上移';
$labels['movedown'] = '下移';
$labels['filterallof'] = '匹配所有规则';
$labels['filteranyof'] = '匹配任意一条规则';
$labels['filterany'] = '所有邮件';
$labels['filtercontains'] = '包含';
$labels['filternotcontains'] = '不包含';
$labels['filteris'] = '等于';
$labels['filterisnot'] = '不等于';
$labels['filterexists'] = '存在';
$labels['filternotexists'] = '不存在';
$labels['filtermatches'] = '匹配表达式';
$labels['filternotmatches'] = '不匹配表达式';
$labels['filterregex'] = '匹配正则表达式';
$labels['filternotregex'] = '不匹配正则表达式';
$labels['filterunder'] = '小于';
$labels['filterover'] = '大于';
$labels['addrule'] = '新建规则';
$labels['delrule'] = '删除规则';
$labels['messagemoveto'] = '将邮件移至';
$labels['messageredirect'] = '将邮件转发至';
$labels['messagecopyto'] = '复制邮件至';
$labels['messagesendcopy'] = '发送复制邮件至';
$labels['messagereply'] = '回复以下内容';
$labels['messagedelete'] = '删除邮件';
$labels['messagediscard'] = '舍弃邮件并回复以下内容';
$labels['messagesrules'] = '对新收取的邮件应用规则:';
$labels['messagesactions'] = '执行以下操作:';
$labels['add'] = '添加';
$labels['del'] = '删除';
$labels['sender'] = '发件人';
$labels['recipient'] = '收件人';
$labels['vacationdays'] = '发送邮件频率(单位:天):';
$labels['vacationinterval'] = '发送邮件频率:';
-$labels['days'] = '天';
-$labels['seconds'] = '秒';
$labels['vacationreason'] = '邮件正文(假期原因)';
$labels['vacationsubject'] = '邮件主题';
+$labels['days'] = '天';
+$labels['seconds'] = '秒';
$labels['rulestop'] = '停止评价规则';
$labels['enable'] = '启用/禁用';
$labels['filterset'] = '过滤器设置';
$labels['filtersets'] = '过滤器设置集';
$labels['filtersetadd'] = '增加过滤器设置集';
$labels['filtersetdel'] = '删除当前的过滤器设置集';
$labels['filtersetact'] = '激活当前的过滤器设置集';
$labels['filtersetdeact'] = '停用当前的过滤器设置集';
$labels['filterdef'] = '过滤器定义';
$labels['filtersetname'] = '过滤器集的名称';
$labels['newfilterset'] = '新的过滤器集';
$labels['active'] = '启用';
$labels['none'] = '无';
$labels['fromset'] = '从设置集';
$labels['fromfile'] = '从文件';
$labels['filterdisabled'] = '过滤器已禁用';
$labels['countisgreaterthan'] = '计数大于';
$labels['countisgreaterthanequal'] = '计数大于或等于';
$labels['countislessthan'] = '计数小于';
$labels['countislessthanequal'] = '计数小于或等于';
$labels['countequals'] = '计数等于';
$labels['valueisgreaterthan'] = '值大于';
$labels['valueisgreaterthanequal'] = '值大于或等于';
$labels['valueislessthan'] = '值小于';
$labels['valueislessthanequal'] = '值小于或等于';
$labels['valueequals'] = '值等于';
$labels['setflags'] = '设定邮件的标识';
$labels['addflags'] = '增加邮件的标识';
$labels['removeflags'] = '删除邮件的标识';
$labels['flagread'] = '读取';
$labels['flagdeleted'] = '删除';
$labels['flaganswered'] = '已答复';
$labels['flagflagged'] = '已标记';
$labels['flagdraft'] = '草稿';
$labels['setvariable'] = '设置变量';
$labels['setvarname'] = '变量名:';
$labels['setvarvalue'] = '值:';
$labels['setvarmodifiers'] = '修改:';
$labels['varlower'] = '小写';
$labels['varupper'] = '大写';
$labels['varlowerfirst'] = '首字母小写';
$labels['varupperfirst'] = '首字母大写';
$labels['varquotewildcard'] = '引用特殊字符';
$labels['varlength'] = '长度';
$labels['notify'] = '发送通知';
-$labels['notifyaddress'] = '收件地址:';
-$labels['notifybody'] = '通知正文:';
-$labels['notifysubject'] = '通知主题';
-$labels['notifyfrom'] = '通知的发送人:';
$labels['notifyimportance'] = '优先级:';
$labels['notifyimportancelow'] = '低';
$labels['notifyimportancenormal'] = '中';
$labels['notifyimportancehigh'] = '高';
$labels['filtercreate'] = '创建过滤规则';
$labels['usedata'] = '在过滤器中使用下列数据';
$labels['nextstep'] = '下一步';
$labels['...'] = '...';
$labels['currdate'] = '当前日期';
$labels['datetest'] = '日期';
$labels['year'] = '年';
$labels['month'] = '月';
$labels['day'] = '天';
$labels['date'] = '日期 (年-月-日)';
$labels['hour'] = '小时';
$labels['minute'] = '分钟';
$labels['second'] = '秒';
$labels['zone'] = '时区';
$labels['advancedopts'] = '高级选项';
$labels['body'] = '正文';
$labels['address'] = '地址';
$labels['envelope'] = '信封';
$labels['modifier'] = '修饰符:';
$labels['text'] = '文本';
$labels['undecoded'] = '未解码(RAW)';
$labels['contenttype'] = '内容类型';
$labels['modtype'] = '类型:';
$labels['allparts'] = '全部';
$labels['domain'] = '域';
$labels['localpart'] = '本地部份';
$labels['user'] = '用户';
$labels['detail'] = '细节';
$labels['comparator'] = '比较:';
$labels['default'] = '默认';
$labels['octet'] = '严格模式(字节)';
$labels['asciicasemap'] = '不区分大小写(ascii 字符)';
$labels['asciinumeric'] = '数字类型(ascii 数字)';
$messages['filterunknownerror'] = '未知的服务器错误';
$messages['filterconnerror'] = '无法连接至服务器';
$messages['filterdeleted'] = '过滤器已成功删除';
$messages['filtersaved'] = '过滤器已成功保存。';
$messages['filterdeleteconfirm'] = '您确定要删除所选择的过滤器吗?';
$messages['ruledeleteconfirm'] = '您确定要删除所选择的规则吗?';
$messages['actiondeleteconfirm'] = '您确定要删除所选择的操作吗?';
$messages['forbiddenchars'] = '内容包含禁用字符';
$messages['cannotbeempty'] = '内容不能为空';
$messages['ruleexist'] = '指定过滤器名称已存在。';
$messages['setactivated'] = '启用过滤器集成功。';
$messages['setdeactivated'] = '禁用过滤器集成功。';
$messages['setdeleted'] = '删除过滤器成功。';
$messages['setdeleteconfirm'] = '您确定要删除指定的过滤器吗?';
$messages['setcreated'] = '过滤器成功创建。';
$messages['deactivated'] = '启用过滤器成功。';
$messages['activated'] = '禁用过滤器成功。';
$messages['moved'] = '移动过滤器成功。';
$messages['nametoolong'] = '无法创建过滤器集,名称太长。';
$messages['namereserved'] = '保留名称。';
$messages['setexist'] = '设置已存在。';
$messages['nodata'] = '至少选择一个位置!';
?>
diff --git a/lib/plugins/managesieve/managesieve.js b/lib/plugins/managesieve/managesieve.js
index cd0d5f3..4d60833 100644
--- a/lib/plugins/managesieve/managesieve.js
+++ b/lib/plugins/managesieve/managesieve.js
@@ -1,1028 +1,1031 @@
/**
* (Manage)Sieve Filters plugin
*
* @licstart The following is the entire license notice for the
* JavaScript code in this file.
*
* Copyright (c) 2012-2014, The Roundcube Dev Team
*
* The JavaScript code in this page is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* @licend The above is the entire license notice
* for the JavaScript code in this file.
*/
if (window.rcmail) {
rcmail.addEventListener('init', function(evt) {
// add managesieve-create command to message_commands array,
// so it's state will be updated on message selection/unselection
if (rcmail.env.task == 'mail') {
if (rcmail.env.action != 'show')
rcmail.env.message_commands.push('managesieve-create');
else
rcmail.enable_command('managesieve-create', true);
}
if (rcmail.env.task == 'mail' || rcmail.env.action.startsWith('plugin.managesieve')) {
// Create layer for form tips
if (!rcmail.env.framed) {
rcmail.env.ms_tip_layer = $('<div id="managesieve-tip" class="popupmenu"></div>');
rcmail.env.ms_tip_layer.appendTo(document.body);
}
}
// register commands
rcmail.register_command('plugin.managesieve-save', function() { rcmail.managesieve_save() });
rcmail.register_command('plugin.managesieve-act', function() { rcmail.managesieve_act() });
rcmail.register_command('plugin.managesieve-add', function() { rcmail.managesieve_add() });
rcmail.register_command('plugin.managesieve-del', function() { rcmail.managesieve_del() });
rcmail.register_command('plugin.managesieve-move', function() { rcmail.managesieve_move() });
rcmail.register_command('plugin.managesieve-setadd', function() { rcmail.managesieve_setadd() });
rcmail.register_command('plugin.managesieve-setdel', function() { rcmail.managesieve_setdel() });
rcmail.register_command('plugin.managesieve-setact', function() { rcmail.managesieve_setact() });
rcmail.register_command('plugin.managesieve-setget', function() { rcmail.managesieve_setget() });
if (rcmail.env.action.startsWith('plugin.managesieve')) {
if (rcmail.gui_objects.sieveform) {
rcmail.enable_command('plugin.managesieve-save', true);
sieve_form_init();
}
else {
rcmail.enable_command('plugin.managesieve-add', 'plugin.managesieve-setadd', !rcmail.env.sieveconnerror);
}
var setcnt, set = rcmail.env.currentset;
if (rcmail.gui_objects.filterslist) {
rcmail.filters_list = new rcube_list_widget(rcmail.gui_objects.filterslist,
{multiselect:false, draggable:true, keyboard:true});
rcmail.filters_list
.addEventListener('select', function(e) { rcmail.managesieve_select(e); })
.addEventListener('dragstart', function(e) { rcmail.managesieve_dragstart(e); })
.addEventListener('dragend', function(e) { rcmail.managesieve_dragend(e); })
.addEventListener('initrow', function(row) {
row.obj.onmouseover = function() { rcmail.managesieve_focus_filter(row); };
row.obj.onmouseout = function() { rcmail.managesieve_unfocus_filter(row); };
})
.init();
}
if (rcmail.gui_objects.filtersetslist) {
rcmail.filtersets_list = new rcube_list_widget(rcmail.gui_objects.filtersetslist,
{multiselect:false, draggable:false, keyboard:true});
rcmail.filtersets_list.init().focus();
if (set != null) {
set = rcmail.managesieve_setid(set);
rcmail.filtersets_list.select(set);
}
// attach select event after initial record was selected
rcmail.filtersets_list.addEventListener('select', function(e) { rcmail.managesieve_setselect(e); });
setcnt = rcmail.filtersets_list.rowcount;
rcmail.enable_command('plugin.managesieve-set', true);
rcmail.enable_command('plugin.managesieve-setact', 'plugin.managesieve-setget', setcnt);
rcmail.enable_command('plugin.managesieve-setdel', setcnt > 1);
// Fix dragging filters over sets list
$('tr', rcmail.gui_objects.filtersetslist).each(function (i, e) { rcmail.managesieve_fixdragend(e); });
}
}
if (rcmail.gui_objects.sieveform && rcmail.env.rule_disabled)
$('#disabled').attr('checked', true);
});
};
/*********************************************************/
/********* Managesieve UI methods *********/
/*********************************************************/
rcube_webmail.prototype.managesieve_add = function()
{
this.load_managesieveframe();
this.filters_list.clear_selection();
};
rcube_webmail.prototype.managesieve_del = function()
{
var id = this.filters_list.get_single_selection();
if (confirm(this.get_label('managesieve.filterdeleteconfirm'))) {
var lock = this.set_busy(true, 'loading');
this.http_post('plugin.managesieve-action',
'_act=delete&_fid='+this.filters_list.rows[id].uid, lock);
}
};
rcube_webmail.prototype.managesieve_act = function()
{
var id = this.filters_list.get_single_selection(),
lock = this.set_busy(true, 'loading');
this.http_post('plugin.managesieve-action',
'_act=act&_fid='+this.filters_list.rows[id].uid, lock);
};
// Filter selection
rcube_webmail.prototype.managesieve_select = function(list)
{
var id = list.get_single_selection();
if (id != null)
this.load_managesieveframe(list.rows[id].uid);
};
// Set selection
rcube_webmail.prototype.managesieve_setselect = function(list)
{
this.show_contentframe(false);
this.filters_list.clear(true);
this.enable_command('plugin.managesieve-setdel', list.rowcount > 1);
this.enable_command('plugin.managesieve-setact', 'plugin.managesieve-setget', true);
var id = list.get_single_selection();
if (id != null)
this.managesieve_list(this.env.filtersets[id]);
};
rcube_webmail.prototype.managesieve_rowid = function(id)
{
var i, rows = this.filters_list.rows;
for (i in rows)
if (rows[i] != null && rows[i].uid == id)
return i;
};
// Returns set's identifier
rcube_webmail.prototype.managesieve_setid = function(name)
{
for (var i in this.env.filtersets)
if (this.env.filtersets[i] == name)
return i;
};
// Filters listing request
rcube_webmail.prototype.managesieve_list = function(script)
{
var lock = this.set_busy(true, 'loading');
this.http_post('plugin.managesieve-action', '_act=list&_set='+urlencode(script), lock);
};
// Script download request
rcube_webmail.prototype.managesieve_setget = function()
{
var id = this.filtersets_list.get_single_selection(),
script = this.env.filtersets[id];
location.href = this.env.comm_path+'&_action=plugin.managesieve-action&_act=setget&_set='+urlencode(script);
};
// Set activate/deactivate request
rcube_webmail.prototype.managesieve_setact = function()
{
var id = this.filtersets_list.get_single_selection(),
lock = this.set_busy(true, 'loading'),
script = this.env.filtersets[id],
action = $('#rcmrow'+id).hasClass('disabled') ? 'setact' : 'deact';
this.http_post('plugin.managesieve-action', '_act='+action+'&_set='+urlencode(script), lock);
};
// Set delete request
rcube_webmail.prototype.managesieve_setdel = function()
{
if (!confirm(this.get_label('managesieve.setdeleteconfirm')))
return false;
var id = this.filtersets_list.get_single_selection(),
lock = this.set_busy(true, 'loading'),
script = this.env.filtersets[id];
this.http_post('plugin.managesieve-action', '_act=setdel&_set='+urlencode(script), lock);
};
// Set add request
rcube_webmail.prototype.managesieve_setadd = function()
{
this.filters_list.clear_selection();
this.enable_command('plugin.managesieve-act', 'plugin.managesieve-del', false);
if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
var lock = this.set_busy(true, 'loading');
target = window.frames[this.env.contentframe];
target.location.href = this.env.comm_path+'&_action=plugin.managesieve-action&_framed=1&_newset=1&_unlock='+lock;
}
};
rcube_webmail.prototype.managesieve_updatelist = function(action, o)
{
this.set_busy(true);
switch (action) {
// Delete filter row
case 'del':
var id = o.id, list = this.filters_list;
list.remove_row(this.managesieve_rowid(o.id));
list.clear_selection();
this.show_contentframe(false);
this.enable_command('plugin.managesieve-del', 'plugin.managesieve-act', false);
// filter identifiers changed, fix the list
$('tr', this.filters_list.list).each(function() {
// remove hidden (deleted) rows
if (this.style.display == 'none') {
$(this).detach();
return;
}
var rowid = this.id.substr(6);
// remove all attached events
$(this).unbind();
// update row id
if (rowid > id)
$(this).attr('id', 'rcmrow' + (rowid-1));
});
list.init();
break;
// Update filter row
case 'update':
var i, row = $('#rcmrow'+this.managesieve_rowid(o.id));
if (o.name)
$('td', row).text(o.name);
if (o.disabled)
row.addClass('disabled');
else
row.removeClass('disabled');
$('#disabled', $('iframe').contents()).prop('checked', o.disabled);
break;
// Add filter row to the list
case 'add':
var list = this.filters_list,
row = $('<tr><td class="name"></td></tr>');
$('td', row).text(o.name);
row.attr('id', 'rcmrow'+o.id);
if (o.disabled)
row.addClass('disabled');
list.insert_row(row.get(0));
list.highlight_row(o.id);
this.enable_command('plugin.managesieve-del', 'plugin.managesieve-act', true);
break;
// Filling rules list
case 'list':
var i, tr, td, el, list = this.filters_list;
if (o.clear)
list.clear();
for (i in o.list) {
el = o.list[i];
tr = document.createElement('TR');
td = document.createElement('TD');
$(td).text(el.name);
td.className = 'name';
tr.id = 'rcmrow' + el.id;
if (el['class'])
tr.className = el['class'];
tr.appendChild(td);
list.insert_row(tr);
}
if (o.set)
list.highlight_row(o.set);
else
this.enable_command('plugin.managesieve-del', 'plugin.managesieve-act', false);
break;
// Sactivate/deactivate set
case 'setact':
var id = this.managesieve_setid(o.name), row = $('#rcmrow' + id);
if (o.active) {
if (o.all)
$('tr', this.gui_objects.filtersetslist).addClass('disabled');
row.removeClass('disabled');
}
else
row.addClass('disabled');
break;
// Delete set row
case 'setdel':
var id = this.managesieve_setid(o.name);
this.filtersets_list.remove_row(id);
this.filters_list.clear();
this.show_contentframe(false);
this.enable_command('plugin.managesieve-setdel', 'plugin.managesieve-setact', 'plugin.managesieve-setget', false);
delete this.env.filtersets[id];
break;
// Create set row
case 'setadd':
var id = 'S' + new Date().getTime(),
list = this.filtersets_list,
row = $('<tr class="disabled"><td class="name"></td></tr>');
$('td', row).text(o.name);
row.attr('id', 'rcmrow'+id);
this.env.filtersets[id] = o.name;
list.insert_row(row.get(0));
// move row into its position on the list
if (o.index != list.rowcount-1) {
row.detach();
var elem = $('tr:visible', list.list).get(o.index);
row.insertBefore(elem);
}
list.select(id);
// Fix dragging filters over sets list
this.managesieve_fixdragend(row);
break;
}
this.set_busy(false);
};
// load filter frame
rcube_webmail.prototype.load_managesieveframe = function(id)
{
var has_id = typeof(id) != 'undefined' && id != null;
this.enable_command('plugin.managesieve-act', 'plugin.managesieve-del', has_id);
if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
target = window.frames[this.env.contentframe];
var msgid = this.set_busy(true, 'loading');
target.location.href = this.env.comm_path+'&_action=plugin.managesieve-action&_framed=1'
+(has_id ? '&_fid='+id : '')+'&_unlock='+msgid;
}
};
// load filter frame
rcube_webmail.prototype.managesieve_dragstart = function(list)
{
var id = this.filters_list.get_single_selection();
this.drag_active = true;
this.drag_filter = id;
};
rcube_webmail.prototype.managesieve_dragend = function(e)
{
if (this.drag_active) {
if (this.drag_filter_target) {
var lock = this.set_busy(true, 'loading');
this.show_contentframe(false);
this.http_post('plugin.managesieve-action', '_act=move&_fid='+this.drag_filter
+'&_to='+this.drag_filter_target, lock);
}
this.drag_active = false;
}
};
// Fixes filters dragging over sets list
// @TODO: to be removed after implementing copying filters
rcube_webmail.prototype.managesieve_fixdragend = function(elem)
{
var p = this;
$(elem).bind('mouseup' + ((bw.iphone || bw.ipad) ? ' touchend' : ''), function(e) {
if (p.drag_active)
p.filters_list.drag_mouse_up(e);
});
};
rcube_webmail.prototype.managesieve_focus_filter = function(row)
{
var id = row.id.replace(/^rcmrow/, '');
if (this.drag_active && id != this.drag_filter) {
this.drag_filter_target = id;
$(row.obj).addClass(id < this.drag_filter ? 'filtermoveup' : 'filtermovedown');
}
};
rcube_webmail.prototype.managesieve_unfocus_filter = function(row)
{
if (this.drag_active) {
$(row.obj).removeClass('filtermoveup filtermovedown');
this.drag_filter_target = null;
}
};
/*********************************************************/
/********* Filter Form methods *********/
/*********************************************************/
// Form submition
rcube_webmail.prototype.managesieve_save = function()
{
if (this.env.action == 'plugin.managesieve-vacation') {
var data = $(this.gui_objects.sieveform).serialize();
this.http_post('plugin.managesieve-vacation', data, this.display_message(this.get_label('managesieve.vacation.saving'), 'loading'));
return;
}
if (parent.rcmail && parent.rcmail.filters_list && this.gui_objects.sieveform.name != 'filtersetform') {
var id = parent.rcmail.filters_list.get_single_selection();
if (id != null)
this.gui_objects.sieveform.elements['_fid'].value = parent.rcmail.filters_list.rows[id].uid;
}
this.gui_objects.sieveform.submit();
};
// Operations on filters form
rcube_webmail.prototype.managesieve_ruleadd = function(id)
{
this.http_post('plugin.managesieve-action', '_act=ruleadd&_rid='+id);
};
rcube_webmail.prototype.managesieve_rulefill = function(content, id, after)
{
if (content != '') {
// create new element
var div = document.getElementById('rules'),
row = document.createElement('div');
this.managesieve_insertrow(div, row, after);
// fill row after inserting (for IE)
row.setAttribute('id', 'rulerow'+id);
row.className = 'rulerow';
row.innerHTML = content;
// initialize smart list inputs
$('textarea[data-type="list"]', row).each(function() {
smart_field_init(this);
});
this.managesieve_formbuttons(div);
}
};
rcube_webmail.prototype.managesieve_ruledel = function(id)
{
if ($('#ruledel'+id).hasClass('disabled'))
return;
if (confirm(this.get_label('managesieve.ruledeleteconfirm'))) {
var row = document.getElementById('rulerow'+id);
row.parentNode.removeChild(row);
this.managesieve_formbuttons(document.getElementById('rules'));
}
};
rcube_webmail.prototype.managesieve_actionadd = function(id)
{
this.http_post('plugin.managesieve-action', '_act=actionadd&_aid='+id);
};
rcube_webmail.prototype.managesieve_actionfill = function(content, id, after)
{
if (content != '') {
var div = document.getElementById('actions'),
row = document.createElement('div');
this.managesieve_insertrow(div, row, after);
// fill row after inserting (for IE)
row.className = 'actionrow';
row.setAttribute('id', 'actionrow'+id);
row.innerHTML = content;
// initialize smart list inputs
$('textarea[data-type="list"]', row).each(function() {
smart_field_init(this);
});
this.managesieve_formbuttons(div);
}
};
rcube_webmail.prototype.managesieve_actiondel = function(id)
{
if ($('#actiondel'+id).hasClass('disabled'))
return;
if (confirm(this.get_label('managesieve.actiondeleteconfirm'))) {
var row = document.getElementById('actionrow'+id);
row.parentNode.removeChild(row);
this.managesieve_formbuttons(document.getElementById('actions'));
}
};
// insert rule/action row in specified place on the list
rcube_webmail.prototype.managesieve_insertrow = function(div, row, after)
{
for (var i=0; i<div.childNodes.length; i++) {
if (div.childNodes[i].id == (div.id == 'rules' ? 'rulerow' : 'actionrow') + after)
break;
}
if (div.childNodes[i+1])
div.insertBefore(row, div.childNodes[i+1]);
else
div.appendChild(row);
};
// update Delete buttons status
rcube_webmail.prototype.managesieve_formbuttons = function(div)
{
var i, button, buttons = [];
// count and get buttons
for (i=0; i<div.childNodes.length; i++) {
if (div.id == 'rules' && div.childNodes[i].id) {
if (/rulerow/.test(div.childNodes[i].id))
buttons.push('ruledel' + div.childNodes[i].id.replace(/rulerow/, ''));
}
else if (div.childNodes[i].id) {
if (/actionrow/.test(div.childNodes[i].id))
buttons.push( 'actiondel' + div.childNodes[i].id.replace(/actionrow/, ''));
}
}
for (i=0; i<buttons.length; i++) {
button = document.getElementById(buttons[i]);
if (i>0 || buttons.length>1) {
$(button).removeClass('disabled');
}
else {
$(button).addClass('disabled');
}
}
};
function rule_header_select(id)
{
var obj = document.getElementById('header' + id),
size = document.getElementById('rule_size' + id),
op = document.getElementById('rule_op' + id),
header = document.getElementById('custom_header' + id + '_list'),
mod = document.getElementById('rule_mod' + id),
trans = document.getElementById('rule_trans' + id),
comp = document.getElementById('rule_comp' + id),
datepart = document.getElementById('rule_date_part' + id),
dateheader = document.getElementById('rule_date_header_div' + id),
h = obj.value;
if (h == 'size') {
size.style.display = 'inline';
$.each([op, header, mod, trans, comp], function() { this.style.display = 'none'; });
}
else {
header.style.display = h != '...' ? 'none' : 'inline-block';
size.style.display = 'none';
op.style.display = 'inline';
comp.style.display = '';
mod.style.display = h == 'body' || h == 'currentdate' || h == 'date' ? 'none' : 'block';
trans.style.display = h == 'body' ? 'block' : 'none';
}
if (datepart)
datepart.style.display = h == 'currentdate' || h == 'date' ? 'inline' : 'none';
if (dateheader)
dateheader.style.display = h == 'date' ? '' : 'none';
rule_op_select(op, id, h);
rule_mod_select(id, h);
obj.style.width = h == '...' ? '40px' : '';
};
function rule_op_select(obj, id, header)
{
var target = document.getElementById('rule_target' + id + '_list');
if (!header)
header = document.getElementById('header' + id).value;
target.style.display = obj.value == 'exists' || obj.value == 'notexists' || header == 'size' ? 'none' : 'inline-block';
};
function rule_trans_select(id)
{
var obj = document.getElementById('rule_trans_op' + id),
target = document.getElementById('rule_trans_type' + id);
target.style.display = obj.value != 'content' ? 'none' : 'inline';
};
function rule_mod_select(id, header)
{
var obj = document.getElementById('rule_mod_op' + id),
target = document.getElementById('rule_mod_type' + id),
index = document.getElementById('rule_index_div' + id);
if (!header)
header = document.getElementById('header' + id).value;
target.style.display = obj.value != 'address' && obj.value != 'envelope' ? 'none' : 'inline';
if (index)
index.style.display = header != 'body' && header != 'currentdate' && header != 'size' && obj.value != 'envelope' ? '' : 'none';
};
function rule_join_radio(value)
{
$('#rules').css('display', value == 'any' ? 'none' : 'block');
};
function rule_adv_switch(id, elem)
{
var elem = $(elem), enabled = elem.hasClass('hide'), adv = $('#rule_advanced'+id);
if (enabled) {
adv.hide();
elem.removeClass('hide').addClass('show');
}
else {
adv.show();
elem.removeClass('show').addClass('hide');
}
}
function action_type_select(id)
{
var obj = document.getElementById('action_type' + id),
v = obj.value, enabled = {},
elems = {
mailbox: document.getElementById('action_mailbox' + id),
target: document.getElementById('redirect_target' + id),
target_area: document.getElementById('action_target_area' + id),
flags: document.getElementById('action_flags' + id),
vacation: document.getElementById('action_vacation' + id),
set: document.getElementById('action_set' + id),
notify: document.getElementById('action_notify' + id)
};
if (v == 'fileinto' || v == 'fileinto_copy') {
enabled.mailbox = 1;
}
else if (v == 'redirect' || v == 'redirect_copy') {
enabled.target = 1;
}
else if (v.match(/^reject|ereject$/)) {
enabled.target_area = 1;
}
else if (v.match(/^(add|set|remove)flag$/)) {
enabled.flags = 1;
}
else if (v == 'vacation') {
enabled.vacation = 1;
}
else if (v == 'set') {
enabled.set = 1;
}
else if (v == 'notify') {
enabled.notify = 1;
}
for (var x in elems) {
elems[x].style.display = !enabled[x] ? 'none' : 'inline';
}
};
function vacation_action_select()
{
var selected = $('#vacation_action').val();
$('#action_target_span')[selected == 'discard' || selected == 'keep' ? 'hide' : 'show']();
};
// Inititalizes smart list input
function smart_field_init(field)
{
var id = field.id + '_list',
area = $('<span class="listarea"></span>'),
list = field.value ? field.value.split("\n") : [''];
if ($('#'+id).length)
return;
// add input rows
$.each(list, function(i, v) {
area.append(smart_field_row(v, field.name, i, $(field).data('size')));
});
area.attr('id', id);
field = $(field);
if (field.attr('disabled'))
area.hide();
+ // disable the original field anyway, we don't want it in POST
+ else
+ field.prop('disabled', true);
field.after(area);
if (field.hasClass('error')) {
area.addClass('error');
rcmail.managesieve_tip_register([[id, field.data('tip')]]);
}
};
function smart_field_row(value, name, idx, size)
{
// build row element content
var input, content = '<span class="listelement">'
+ '<span class="reset"></span><input type="text"></span>',
elem = $(content),
attrs = {value: value, name: name + '[]'};
if (size)
attrs.size = size;
input = $('input', elem).attr(attrs).keydown(function(e) {
var input = $(this);
// element creation event (on Enter)
if (e.which == 13) {
var name = input.attr('name').replace(/\[\]$/, ''),
dt = (new Date()).getTime(),
elem = smart_field_row('', name, dt, size);
input.parent().after(elem);
$('input', elem).focus();
}
// backspace or delete: remove input, focus previous one
else if ((e.which == 8 || e.which == 46) && input.val() == '') {
var parent = input.parent(), siblings = parent.parent().children();
if (siblings.length > 1) {
if (parent.prev().length)
parent.prev().children('input').focus();
else
parent.next().children('input').focus();
parent.remove();
return false;
}
}
});
// element deletion event
$('span[class="reset"]', elem).click(function() {
var span = $(this.parentNode);
if (span.parent().children().length > 1)
span.remove();
else
$('input', span).val('').focus();
});
return elem;
}
// Register onmouse(leave/enter) events for tips on specified form element
rcube_webmail.prototype.managesieve_tip_register = function(tips)
{
var n, framed = parent.rcmail,
tip = framed ? parent.rcmail.env.ms_tip_layer : rcmail.env.ms_tip_layer;
for (var n in tips) {
$('#'+tips[n][0])
.data('tip', tips[n][1])
.bind('mouseenter', function(e) {
var elem = $(this),
offset = elem.offset(),
left = offset.left,
top = offset.top - 12,
minwidth = elem.width();
if (framed) {
offset = $((rcmail.env.task == 'mail' ? '#sievefilterform > iframe' : '#filter-box'), parent.document).offset();
top += offset.top;
left += offset.left;
}
tip.html(elem.data('tip'));
top -= tip.height();
tip.css({left: left, top: top, minWidth: (minwidth-2) + 'px'}).show();
})
.bind('mouseleave', function(e) { tip.hide(); });
}
};
// format time string
function sieve_formattime(hour, minutes)
{
var i, c, h, time = '', format = rcmail.env.time_format || 'H:i';
for (i=0; i<format.length; i++) {
c = format.charAt(i);
switch (c) {
case 'a': time += hour > 12 ? 'am' : 'pm'; break;
case 'A': time += hour > 12 ? 'AM' : 'PM'; break;
case 'g':
case 'h':
h = hour == 0 ? 12 : hour > 12 ? hour - 12 : hour;
time += (c == 'h' && hour < 10 ? '0' : '') + hour;
break;
case 'G': time += hour; break;
case 'H': time += (hour < 10 ? '0' : '') + hour; break;
case 'i': time += (minutes < 10 ? '0' : '') + minutes; break;
case 's': time += '00';
default: time += c;
}
}
return time;
}
function sieve_form_init()
{
// small resize for header element
$('select[name="_header[]"]', rcmail.gui_objects.sieveform).each(function() {
if (this.value == '...') this.style.width = '40px';
});
// resize dialog window
if (rcmail.env.action == 'plugin.managesieve' && rcmail.env.task == 'mail') {
parent.rcmail.managesieve_dialog_resize(rcmail.gui_objects.sieveform);
}
$('input[type="text"]:first', rcmail.gui_objects.sieveform).focus();
// initialize smart list inputs
$('textarea[data-type="list"]', rcmail.gui_objects.sieveform).each(function() {
smart_field_init(this);
});
// enable date pickers on date fields
if ($.datepicker && rcmail.env.date_format) {
$.datepicker.setDefaults({
dateFormat: rcmail.env.date_format,
changeMonth: true,
showOtherMonths: true,
selectOtherMonths: true,
onSelect: function(dateText) { $(this).focus().val(dateText); }
});
$('input.datepicker').datepicker();
}
// configure drop-down menu on time input fields based on jquery UI autocomplete
$('#vacation_timefrom, #vacation_timeto')
.attr('autocomplete', "off")
.autocomplete({
delay: 100,
minLength: 1,
source: function(p, callback) {
var h, result = [];
for (h = 0; h < 24; h++)
result.push(sieve_formattime(h, 0));
result.push(sieve_formattime(23, 59));
return callback(result);
},
open: function(event, ui) {
// scroll to current time
var $this = $(this), val = $this.val(),
widget = $this.autocomplete('widget').css('width', '10em'),
menu = $this.data('ui-autocomplete').menu;
if (val && val.length)
widget.children().each(function() {
var li = $(this);
if (li.text().indexOf(val) == 0)
menu._scrollIntoView(li);
});
},
select: function(event, ui) {
$(this).val(ui.item.value);
return false;
}
})
.click(function() { // show drop-down upon clicks
$(this).autocomplete('search', $(this).val() || ' ');
})
}
/*********************************************************/
/********* Mail UI methods *********/
/*********************************************************/
rcube_webmail.prototype.managesieve_create = function(force)
{
- if (!force && this.env.action != 'show' && !$('#'+this.env.contentframe).is(':visible')) {
+ if (!force && this.env.action != 'show') {
var uid = this.message_list.get_single_selection(),
lock = this.set_busy(true, 'loading');
this.http_post('plugin.managesieve-action', {_uid: uid}, lock);
return;
}
if (!this.env.sieve_headers || !this.env.sieve_headers.length)
return;
var i, html, buttons = {}, dialog = $("#sievefilterform");
// create dialog window
if (!dialog.length) {
dialog = $('<div id="sievefilterform"></div>');
$('body').append(dialog);
}
// build dialog window content
html = '<fieldset><legend>'+this.gettext('managesieve.usedata')+'</legend><ul>';
for (i in this.env.sieve_headers)
html += '<li><input type="checkbox" name="headers[]" id="sievehdr'+i+'" value="'+i+'" checked="checked" />'
+'<label for="sievehdr'+i+'">'+this.env.sieve_headers[i][0]+':</label> '+this.env.sieve_headers[i][1]+'</li>';
html += '</ul></fieldset>';
dialog.html(html);
// [Next Step] button action
buttons[this.gettext('managesieve.nextstep')] = function () {
// check if there's at least one checkbox checked
var hdrs = $('input[name="headers[]"]:checked', dialog);
if (!hdrs.length) {
alert(rcmail.gettext('managesieve.nodata'));
return;
}
// build frame URL
var url = rcmail.get_task_url('mail');
url = rcmail.add_url(url, '_action', 'plugin.managesieve');
url = rcmail.add_url(url, '_framed', 1);
hdrs.map(function() {
var val = rcmail.env.sieve_headers[this.value];
url = rcmail.add_url(url, 'r['+this.value+']', val[0]+':'+val[1]);
});
// load form in the iframe
var frame = $('<iframe>').attr({src: url, frameborder: 0})
dialog.empty().append(frame).dialog('widget').resize();
// Change [Next Step] button with [Save] button
buttons = {};
buttons[rcmail.gettext('save')] = function() {
var win = $('iframe', dialog).get(0).contentWindow;
win.rcmail.managesieve_save();
};
dialog.dialog('option', 'buttons', buttons);
};
// show dialog window
dialog.dialog({
modal: false,
resizable: true,
closeOnEscape: !bw.ie7, // disable for performance reasons
title: this.gettext('managesieve.newfilter'),
close: function() { rcmail.managesieve_dialog_close(); },
buttons: buttons,
minWidth: 600,
minHeight: 300,
height: 250
}).show();
this.env.managesieve_dialog = dialog;
}
rcube_webmail.prototype.managesieve_dialog_close = function()
{
var dialog = this.env.managesieve_dialog;
// BUG(?): if we don't remove the iframe first, it will be reloaded
dialog.html('');
dialog.dialog('destroy').hide();
}
rcube_webmail.prototype.managesieve_dialog_resize = function(o)
{
var dialog = this.env.managesieve_dialog,
win = $(window), form = $(o);
width = $('fieldset:first', o).width(), // fieldset width is more appropriate here
height = form.height(),
w = win.width(), h = win.height();
dialog.dialog('option', { height: Math.min(h-20, height+120), width: Math.min(w-20, width+65) })
.dialog('option', 'position', ['center', 'center']); // works in a separate call only (!?)
}
diff --git a/lib/plugins/managesieve/managesieve.php b/lib/plugins/managesieve/managesieve.php
index 478f26b..f41394e 100644
--- a/lib/plugins/managesieve/managesieve.php
+++ b/lib/plugins/managesieve/managesieve.php
@@ -1,275 +1,274 @@
<?php
/**
* Managesieve (Sieve Filters)
*
* Plugin that adds a possibility to manage Sieve filters in Thunderbird's style.
* It's clickable interface which operates on text scripts and communicates
* with server using managesieve protocol. Adds Filters tab in Settings.
*
* @version @package_version@
* @author Aleksander Machniak <alec@alec.pl>
*
* Configuration (see config.inc.php.dist)
*
* Copyright (C) 2008-2013, The Roundcube Dev Team
* Copyright (C) 2011-2013, Kolab Systems AG
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
class managesieve extends rcube_plugin
{
public $task = 'mail|settings';
private $rc;
private $engine;
function init()
{
$this->rc = rcube::get_instance();
// register actions
$this->register_action('plugin.managesieve', array($this, 'managesieve_actions'));
$this->register_action('plugin.managesieve-action', array($this, 'managesieve_actions'));
$this->register_action('plugin.managesieve-vacation', array($this, 'managesieve_actions'));
$this->register_action('plugin.managesieve-save', array($this, 'managesieve_save'));
if ($this->rc->task == 'settings') {
$this->add_hook('settings_actions', array($this, 'settings_actions'));
$this->init_ui();
}
else if ($this->rc->task == 'mail') {
// register message hook
- $this->add_hook('message_headers_output', array($this, 'mail_headers'));
+ if ($this->rc->action == 'show') {
+ $this->add_hook('message_headers_output', array($this, 'mail_headers'));
+ }
// inject Create Filter popup stuff
if (empty($this->rc->action) || $this->rc->action == 'show'
|| strpos($this->rc->action, 'plugin.managesieve') === 0
) {
$this->mail_task_handler();
}
}
}
/**
* Initializes plugin's UI (localization, js script)
*/
function init_ui()
{
if ($this->ui_initialized) {
return;
}
// load localization
$this->add_texts('localization/');
$sieve_action = strpos($this->rc->action, 'plugin.managesieve') === 0;
if ($this->rc->task == 'mail' || $sieve_action) {
$this->include_script('managesieve.js');
}
// include styles
$skin_path = $this->local_skin_path();
if ($this->rc->task == 'settings' || $sieve_action) {
- if (is_file($this->home . "/$skin_path/managesieve.css")) {
- $this->include_stylesheet("$skin_path/managesieve.css");
- }
+ $this->include_stylesheet("$skin_path/managesieve.css");
}
else {
- if (is_file($this->home . "/$skin_path/managesieve_mail.css")) {
- $this->include_stylesheet("$skin_path/managesieve_mail.css");
- }
+ $this->include_stylesheet("$skin_path/managesieve_mail.css");
}
$this->ui_initialized = true;
}
/**
* Adds Filters section in Settings
*/
function settings_actions($args)
{
$this->load_config();
$vacation_mode = (int) $this->rc->config->get('managesieve_vacation');
// register Filters action
if ($vacation_mode != 2) {
$args['actions'][] = array(
'action' => 'plugin.managesieve',
'class' => 'filter',
'label' => 'filters',
'domain' => 'managesieve',
'title' => 'filterstitle',
);
}
// register Vacation action
if ($vacation_mode > 0) {
$args['actions'][] = array(
'action' => 'plugin.managesieve-vacation',
'class' => 'vacation',
'label' => 'vacation',
'domain' => 'managesieve',
'title' => 'vacationtitle',
);
}
return $args;
}
/**
* Add UI elements to the 'mailbox view' and 'show message' UI.
*/
function mail_task_handler()
{
// make sure we're not in ajax request
if ($this->rc->output->type != 'html') {
return;
}
// use jQuery for popup window
$this->require_plugin('jqueryui');
// include js script and localization
$this->init_ui();
// add 'Create filter' item to message menu
$this->api->add_content(html::tag('li', null,
$this->api->output->button(array(
'command' => 'managesieve-create',
'label' => 'managesieve.filtercreate',
'type' => 'link',
'classact' => 'icon filterlink active',
'class' => 'icon filterlink',
'innerclass' => 'icon filterlink',
))), 'messagemenu');
// register some labels/messages
$this->rc->output->add_label('managesieve.newfilter', 'managesieve.usedata',
'managesieve.nodata', 'managesieve.nextstep', 'save');
$this->rc->session->remove('managesieve_current');
}
/**
* Get message headers for popup window
*/
function mail_headers($args)
{
// this hook can be executed many times
if ($this->mail_headers_done) {
return $args;
}
$this->mail_headers_done = true;
$headers = $this->parse_headers($args['headers']);
if ($this->rc->action == 'preview')
$this->rc->output->command('parent.set_env', array('sieve_headers' => $headers));
else
$this->rc->output->set_env('sieve_headers', $headers);
return $args;
}
/**
* Plugin action handler
*/
function managesieve_actions()
{
// handle fetching email headers for the new filter form
- if ($uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_GPC)) {
- $mailbox = $this->rc->get_storage()->get_folder();
- $message = new rcube_message($uid, $mailbox);
+ if ($uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST)) {
+ $uids = rcmail::get_uids();
+ $mailbox = key($uids);
+ $message = new rcube_message($uids[$mailbox][0], $mailbox);
$headers = $this->parse_headers($message->headers);
$this->rc->output->set_env('sieve_headers', $headers);
$this->rc->output->command('managesieve_create', true);
$this->rc->output->send();
}
// handle other actions
$engine_type = $this->rc->action == 'plugin.managesieve-vacation' ? 'vacation' : '';
$engine = $this->get_engine($engine_type);
$this->init_ui();
$engine->actions();
}
/**
* Forms save action handler
*/
function managesieve_save()
{
// load localization
$this->add_texts('localization/', array('filters','managefilters'));
// include main js script
if ($this->api->output->type == 'html') {
$this->include_script('managesieve.js');
}
$engine = $this->get_engine();
$engine->save();
}
/**
* Initializes engine object
*/
public function get_engine($type = null)
{
if (!$this->engine) {
$this->load_config();
// Add include path for internal classes
$include_path = $this->home . '/lib' . PATH_SEPARATOR;
$include_path .= ini_get('include_path');
set_include_path($include_path);
$class_name = 'rcube_sieve_' . ($type ? $type : 'engine');
$this->engine = new $class_name($this);
}
return $this->engine;
}
/**
* Extract mail headers for new filter form
*/
private function parse_headers($headers)
{
$result = array();
if ($headers->subject)
$result[] = array('Subject', rcube_mime::decode_header($headers->subject));
// @TODO: List-Id, others?
foreach (array('From', 'To') as $h) {
$hl = strtolower($h);
if ($headers->$hl) {
$list = rcube_mime::decode_address_list($headers->$hl);
foreach ($list as $item) {
if ($item['mailto']) {
$result[] = array($h, $item['mailto']);
}
}
}
}
return $result;
}
}
diff --git a/lib/plugins/managesieve/package.xml b/lib/plugins/managesieve/package.xml
deleted file mode 100644
index f4c4bb9..0000000
--- a/lib/plugins/managesieve/package.xml
+++ /dev/null
@@ -1,128 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
- http://pear.php.net/dtd/tasks-1.0.xsd
- http://pear.php.net/dtd/package-2.0
- http://pear.php.net/dtd/package-2.0.xsd">
- <name>managesieve</name>
- <channel>pear.roundcube.net</channel>
- <summary>Sieve filters manager for Roundcube</summary>
- <description>
- Adds a possibility to manage Sieve scripts (incoming mail filters).
- It's clickable interface which operates on text scripts and communicates
- with server using managesieve protocol. Adds Filters tab in Settings.
- </description>
- <lead>
- <name>Aleksander Machniak</name>
- <user>alec</user>
- <email>alec@alec.pl</email>
- <active>yes</active>
- </lead>
- <date>2014-02-14</date>
- <version>
- <release>7.2</release>
- <api>7.0</api>
- </version>
- <stability>
- <release>stable</release>
- <api>stable</api>
- </stability>
- <license uri="http://www.gnu.org/licenses/gpl.html">GNU GPLv3+</license>
- <notes>-</notes>
- <contents>
- <dir baseinstalldir="/" name="/">
- <file name="managesieve.php" role="php">
- <tasks:replace from="@name@" to="name" type="package-info"/>
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="managesieve.js" role="data">
- <tasks:replace from="@name@" to="name" type="package-info"/>
- <tasks:replace from="@package_version@" to="version" type="package-info"/>
- </file>
- <file name="lib/Roundcube/rcube_sieve.php" role="php"></file>
- <file name="lib/Roundcube/rcube_sieve_engine.php" role="php"></file>
- <file name="lib/Roundcube/rcube_sieve_script.php" role="php"></file>
- <file name="lib/Net/Sieve.php" role="php"></file>
- <file name="localization/be_BE.inc" role="data"></file>
- <file name="localization/bg_BG.inc" role="data"></file>
- <file name="localization/bs_BA.inc" role="data"></file>
- <file name="localization/ca_ES_BA.inc" role="data"></file>
- <file name="localization/cs_CZ.inc" role="data"></file>
- <file name="localization/cy_GB.inc" role="data"></file>
- <file name="localization/da_DK.inc" role="data"></file>
- <file name="localization/de_CH.inc" role="data"></file>
- <file name="localization/de_DE.inc" role="data"></file>
- <file name="localization/el_GR.inc" role="data"></file>
- <file name="localization/en_GB.inc" role="data"></file>
- <file name="localization/en_US.inc" role="data"></file>
- <file name="localization/eo.inc" role="data"></file>
- <file name="localization/es_AR.inc" role="data"></file>
- <file name="localization/es_ES.inc" role="data"></file>
- <file name="localization/et_EE.inc" role="data"></file>
- <file name="localization/fa_IR.inc" role="data"></file>
- <file name="localization/fi_FI.inc" role="data"></file>
- <file name="localization/fr_FR.inc" role="data"></file>
- <file name="localization/gl_ES.inc" role="data"></file>
- <file name="localization/he_IL.inc" role="data"></file>
- <file name="localization/hr_HR.inc" role="data"></file>
- <file name="localization/hu_HU.inc" role="data"></file>
- <file name="localization/ia_IA.inc" role="data"></file>
- <file name="localization/id_ID.inc" role="data"></file>
- <file name="localization/it_IT.inc" role="data"></file>
- <file name="localization/ja_JP.inc" role="data"></file>
- <file name="localization/lt_LT.inc" role="data"></file>
- <file name="localization/lv_LV.inc" role="data"></file>
- <file name="localization/ml_ML.inc" role="data"></file>
- <file name="localization/mr_IN.inc" role="data"></file>
- <file name="localization/nb_NO.inc" role="data"></file>
- <file name="localization/nl_NL.inc" role="data"></file>
- <file name="localization/pl_PL.inc" role="data"></file>
- <file name="localization/pt_BR.inc" role="data"></file>
- <file name="localization/pt_PT.inc" role="data"></file>
- <file name="localization/ro_RO.inc" role="data"></file>
- <file name="localization/ru_RU.inc" role="data"></file>
- <file name="localization/si_LK.inc" role="data"></file>
- <file name="localization/sk_SK.inc" role="data"></file>
- <file name="localization/sl_SI.inc" role="data"></file>
- <file name="localization/sv_SE.inc" role="data"></file>
- <file name="localization/tr_TR.inc" role="data"></file>
- <file name="localization/uk_UA.inc" role="data"></file>
- <file name="localization/vi_VN.inc" role="data"></file>
- <file name="localization/zh_CN.inc" role="data"></file>
- <file name="localization/zh_TW.inc" role="data"></file>
- <file name="skins/classic/managesieve.css" role="data"></file>
- <file name="skins/classic/managesieve_mail.css" role="data"></file>
- <file name="skins/classic/templates/filteredit.html" role="data"></file>
- <file name="skins/classic/templates/managesieve.html" role="data"></file>
- <file name="skins/classic/templates/setedit.html" role="data"></file>
- <file name="skins/classic/images/add.png" role="data"></file>
- <file name="skins/classic/images/del.png" role="data"></file>
- <file name="skins/classic/images/down_small.gif" role="data"></file>
- <file name="skins/classic/images/erase.png" role="data"></file>
- <file name="skins/classic/images/filter.png" role="data"></file>
- <file name="skins/classic/images/up_small.gif" role="data"></file>
- <file name="skins/larry/managesieve.css" role="data"></file>
- <file name="skins/larry/managesieve_mail.css" role="data"></file>
- <file name="skins/larry/templates/filteredit.html" role="data"></file>
- <file name="skins/larry/templates/managesieve.html" role="data"></file>
- <file name="skins/larry/templates/setedit.html" role="data"></file>
- <file name="skins/larry/images/add.png" role="data"></file>
- <file name="skins/larry/images/del.png" role="data"></file>
- <file name="skins/larry/images/down_small.gif" role="data"></file>
- <file name="skins/larry/images/erase.png" role="data"></file>
- <file name="skins/larry/images/up_small.gif" role="data"></file>
- <file name="config.inc.php.dist" role="data"></file>
- </dir>
- <!-- / -->
- </contents>
- <dependencies>
- <required>
- <php>
- <min>5.2.1</min>
- </php>
- <pearinstaller>
- <min>1.7.0</min>
- </pearinstaller>
- </required>
- </dependencies>
- <phprelease/>
-</package>
diff --git a/lib/plugins/managesieve/skins/larry/managesieve.css b/lib/plugins/managesieve/skins/larry/managesieve.css
index 61d1c89..47e992c 100644
--- a/lib/plugins/managesieve/skins/larry/managesieve.css
+++ b/lib/plugins/managesieve/skins/larry/managesieve.css
@@ -1,459 +1,450 @@
#filtersetslistbox
{
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 150px;
}
#filtersscreen
{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 162px;
}
#filterslistbox
{
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 180px;
}
#filter-box
{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 192px;
}
#filter-frame
{
border-radius: 4px;
}
#filterslist,
#filtersetslist
{
width: 100%;
table-layout: fixed;
}
#filterslist tbody td,
#filtersetslist tbody td
{
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
#filterslist tbody tr.disabled td,
#filtersetslist tbody tr.disabled td
{
color: #87A3AA;
}
#filtersetslist tbody td
{
font-weight: bold;
}
#filterslist tbody tr.filtermoveup td
{
border-top: 2px dotted #555;
padding-top: 5px;
}
#filterslist tbody tr.filtermovedown td
{
border-bottom: 2px dotted #555;
padding-bottom: 4px;
}
body.iframe
{
min-width: 620px;
}
#filter-form
{
min-width: 550px;
white-space: nowrap;
padding: 20px 10px 10px 10px;
}
#filter-form legend, #filter-form label
{
color: #666666;
}
#rules, #actions
{
margin-top: 5px;
padding: 0;
border-collapse: collapse;
}
div.rulerow, div.actionrow
{
width: auto;
padding: 2px;
white-space: nowrap;
border: 1px solid white;
}
div.rulerow:hover, div.actionrow:hover
{
padding: 2px;
white-space: nowrap;
background-color: #D9ECF4;
border: 1px solid #BBD3DA;
border-radius: 4px;
}
div.rulerow table, div.actionrow table
{
padding: 0px;
min-width: 600px;
}
#filter-form td
{
vertical-align: top;
}
td.advbutton
{
width: 1%;
}
td.advbutton a
{
display: block;
padding-top: 14px;
height: 6px;
width: 12px;
text-decoration: none;
}
td.advbutton a.show
{
background: url(images/down_small.gif) center no-repeat;
}
td.advbutton a.hide
{
background: url(images/up_small.gif) center no-repeat;
}
td.rowbuttons
{
text-align: right;
white-space: nowrap;
width: 1%;
}
td.rowactions
{
white-space: nowrap;
width: 1%;
padding-top: 2px;
}
td.rowtargets
{
white-space: nowrap;
width: 98%;
padding-left: 3px;
padding-top: 2px;
}
td.rowtargets > div
{
vertical-align: top;
margin-top: 2px;
}
td.rowtargets div.adv
{
padding-top: 3px;
font-size: 10px;
}
td.rowtargets div.adv span.label
{
display: inline-block;
padding-right: 5px;
min-width: 70px;
}
input.disabled, input.disabled:hover
{
color: #999999;
}
input.error, textarea.error
{
background-color: #FFFFC4;
}
input.box,
input.radio
{
border: 0;
margin-top: 0;
}
input.radio
{
vertical-align: middle;
}
select.operator_selector
{
width: 200px;
vertical-align: top;
}
td.rowtargets span,
span.label
{
color: #666666;
font-size: 10px;
white-space: nowrap;
}
td.rowtargets label
{
color: black;
}
#footer
{
padding-top: 5px;
width: 100%;
}
#footer .footerleft label
{
margin-left: 40px;
white-space: nowrap;
}
.itemlist
{
line-height: 25px;
}
.itemlist input
{
vertical-align: middle;
}
span.sieve.error
{
color: red;
white-space: nowrap;
}
#managesieve-tip
{
padding: 3px;
background-color: #eee;
}
a.button
{
margin: 0;
padding: 0;
}
a.button.add
{
background: url(images/add.png) no-repeat;
width: 30px;
height: 20px;
margin-right: 4px;
display: inline-block;
}
a.button.del
{
background: url(images/del.png) no-repeat;
width: 30px;
height: 20px;
display: inline-block;
}
a.button.disabled
{
opacity: 0.35;
filter: alpha(opacity=35);
cursor: default;
}
#filter-form select,
#filter-form input,
#filter-form textarea
{
font-size: 11px;
padding: 1px;
vertical-align: middle;
max-width: 280px;
}
-html.mozilla #filter-form select
-{
- padding-top: 3px;
- padding-bottom: 3px;
-}
-
/* revert larry style button */
#filter-form input.button
{
- padding-bottom: 2px;
- padding-left: 5px;
- padding-right: 5px;
- padding-top: 2px;
+ padding: inherit;
}
fieldset
{
border-radius: 4px;
}
/* smart multi-row input field */
.listarea
{
border: 1px solid #B2B2B2;
border-radius: 4px;
box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.1);
-webkit-box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.1);
margin: 0;
padding: 2px;
display: inline-block;
max-height: 59px;
overflow-y: auto;
}
td.rowtargets > span.listarea
{
vertical-align: top;
margin-top: 2px;
}
.listelement
{
display: block;
white-space: nowrap;
background-color: #fff;
border-top: 1px solid #e2e2e2;
height: 14px;
padding: 0;
margin: 0;
overflow: hidden;
line-height: 14px;
}
.listarea.error .listelement
{
background-color: #FFFFC4;
}
.listelement:first-child
{
border-top: none;
}
#filter-form .listelement input
{
border: none;
border-radius: 0;
box-shadow: none;
outline: none;
vertical-align: top;
height: 14px;
padding-top: 0;
padding-bottom: 0;
line-height: 14px;
background-color: transparent;
}
.listelement input:focus
{
box-shadow: none;
}
.listelement .reset
{
display: inline-block;
width: 16px;
height: 16px;
background: url(images/erase.png) -1px -1px no-repeat #eee;
cursor: pointer;
}
/* fixes for popup window */
body.iframe.mail
{
margin: 0;
padding: 0;
}
body.iframe.mail #filter-form
{
padding: 10px 5px 5px 5px;
}
/* vacation form */
#settings-sections .vacation a {
background-image: url(images/vacation_icons.png);
background-repeat: no-repeat;
background-position: 7px 1px;
}
#settings-sections .vacation.selected a {
background-position: 7px -23px;
}
#managesieve-vacation {
position: absolute;
top: 0;
left: 212px;
right: 0;
bottom: 0;
overflow: auto;
}
#vacationform .listarea {
max-height: 91px;
}
#vacationform .listelement,
#vacationform .listelement .reset {
height: 22px;
}
#vacationform .listelement .reset {
background-position: -1px 3px;
}
#vacationform .listelement input {
vertical-align: top;
border: 0;
box-shadow: none;
}
#vacationform td.vacation {
white-space: nowrap;
}
diff --git a/lib/plugins/managesieve/tests/Managesieve.php b/lib/plugins/managesieve/tests/Managesieve.php
index d802f56..6e930b8 100644
--- a/lib/plugins/managesieve/tests/Managesieve.php
+++ b/lib/plugins/managesieve/tests/Managesieve.php
@@ -1,23 +1,23 @@
<?php
class Managesieve_Plugin extends PHPUnit_Framework_TestCase
{
function setUp()
{
- include_once dirname(__FILE__) . '/../managesieve.php';
+ include_once __DIR__ . '/../managesieve.php';
}
/**
* Plugin object construction test
*/
function test_constructor()
{
$rcube = rcube::get_instance();
$plugin = new managesieve($rcube->api);
$this->assertInstanceOf('managesieve', $plugin);
$this->assertInstanceOf('rcube_plugin', $plugin);
}
}
diff --git a/lib/plugins/managesieve/tests/Parser.php b/lib/plugins/managesieve/tests/Parser.php
index 9050f09..33edce0 100644
--- a/lib/plugins/managesieve/tests/Parser.php
+++ b/lib/plugins/managesieve/tests/Parser.php
@@ -1,62 +1,62 @@
<?php
class Parser extends PHPUnit_Framework_TestCase
{
function setUp()
{
- include_once dirname(__FILE__) . '/../lib/Roundcube/rcube_sieve_script.php';
+ include_once __DIR__ . '/../lib/Roundcube/rcube_sieve_script.php';
}
/**
* Sieve script parsing
*
* @dataProvider data_parser
*/
function test_parser($input, $output, $message)
{
// get capabilities list from the script
$caps = array();
if (preg_match('/require \[([a-z0-9", ]+)\]/', $input, $m)) {
foreach (explode(',', $m[1]) as $cap) {
$caps[] = trim($cap, '" ');
}
}
$script = new rcube_sieve_script($input, $caps);
$result = $script->as_text();
$this->assertEquals(trim($result), trim($output), $message);
}
/**
* Data provider for test_parser()
*/
function data_parser()
{
- $dir_path = realpath(dirname(__FILE__) . '/src');
+ $dir_path = realpath(__DIR__ . '/src');
$dir = opendir($dir_path);
$result = array();
while ($file = readdir($dir)) {
if (preg_match('/^[a-z0-9_]+$/', $file)) {
$input = file_get_contents($dir_path . '/' . $file);
if (file_exists($dir_path . '/' . $file . '.out')) {
$output = file_get_contents($dir_path . '/' . $file . '.out');
}
else {
$output = $input;
}
$result[] = array(
'input' => $input,
'output' => $output,
'message' => "Error in parsing '$file' file",
);
}
}
return $result;
}
}
diff --git a/lib/plugins/managesieve/tests/Tokenizer.php b/lib/plugins/managesieve/tests/Tokenizer.php
index e71bae0..f50ed75 100644
--- a/lib/plugins/managesieve/tests/Tokenizer.php
+++ b/lib/plugins/managesieve/tests/Tokenizer.php
@@ -1,33 +1,33 @@
<?php
class Tokenizer extends PHPUnit_Framework_TestCase
{
function setUp()
{
- include_once dirname(__FILE__) . '/../lib/Roundcube/rcube_sieve_script.php';
+ include_once __DIR__ . '/../lib/Roundcube/rcube_sieve_script.php';
}
function data_tokenizer()
{
return array(
array(1, "text: #test\nThis is test ; message;\nMulti line\n.\n;\n", '"This is test ; message;\nMulti line"'),
array(0, '["test1","test2"]', '[["test1","test2"]]'),
array(1, '["test"]', '["test"]'),
array(1, '"te\\"st"', '"te\\"st"'),
array(0, 'test #comment', '["test"]'),
array(0, "text:\ntest\n.\ntext:\ntest\n.\n", '["test","test"]'),
array(1, '"\\a\\\\\\"a"', '"a\\\\\\"a"'),
);
}
/**
* @dataProvider data_tokenizer
*/
function test_tokenizer($num, $input, $output)
{
$res = json_encode(rcube_sieve_script::tokenize($input, $num));
$this->assertEquals(trim($res), trim($output));
}
}
diff --git a/lib/plugins/managesieve/tests/Vacation.php b/lib/plugins/managesieve/tests/Vacation.php
index e34eb7a..942525c 100644
--- a/lib/plugins/managesieve/tests/Vacation.php
+++ b/lib/plugins/managesieve/tests/Vacation.php
@@ -1,66 +1,66 @@
<?php
class Managesieve_Vacation extends PHPUnit_Framework_TestCase
{
function setUp()
{
- include_once dirname(__FILE__) . '/../lib/Roundcube/rcube_sieve_engine.php';
- include_once dirname(__FILE__) . '/../lib/Roundcube/rcube_sieve_vacation.php';
+ include_once __DIR__ . '/../lib/Roundcube/rcube_sieve_engine.php';
+ include_once __DIR__ . '/../lib/Roundcube/rcube_sieve_vacation.php';
}
/**
* Plugin object construction test
*/
function test_constructor()
{
$vacation = new rcube_sieve_vacation(true);
$this->assertInstanceOf('rcube_sieve_vacation', $vacation);
}
function test_build_regexp_tests()
{
$tests = rcube_sieve_vacation::build_regexp_tests('2014-02-20', '2014-03-05', $error);
$this->assertCount(2, $tests);
$this->assertSame('header', $tests[0]['test']);
$this->assertSame('regex', $tests[0]['type']);
$this->assertSame('received', $tests[0]['arg1']);
$this->assertSame('(20|21|22|23|24|25|26|27|28) Feb 2014', $tests[0]['arg2']);
$this->assertSame('header', $tests[1]['test']);
$this->assertSame('regex', $tests[1]['type']);
$this->assertSame('received', $tests[1]['arg1']);
$this->assertSame('([ 0]1|[ 0]2|[ 0]3|[ 0]4|[ 0]5) Mar 2014', $tests[1]['arg2']);
$tests = rcube_sieve_vacation::build_regexp_tests('2014-02-20', '2014-01-05', $error);
$this->assertSame(null, $tests);
$this->assertSame('managesieve.invaliddateformat', $error);
}
function test_parse_regexp_tests()
{
$tests = array(
array(
'test' => 'header',
'type' => 'regex',
'arg1' => 'received',
'arg2' => '(20|21|22|23|24|25|26|27|28) Feb 2014',
),
array(
'test' => 'header',
'type' => 'regex',
'arg1' => 'received',
'arg2' => '([ 0]1|[ 0]2|[ 0]3|[ 0]4|[ 0]5) Mar 2014',
)
);
$result = rcube_sieve_vacation::parse_regexp_tests($tests);
$this->assertCount(2, $result);
$this->assertSame('20 Feb 2014', $result['from']);
$this->assertSame('05 Mar 2014', $result['to']);
}
}
diff --git a/tests/data_calendar.php b/tests/data_calendar.php
new file mode 100644
index 0000000..6435eb8
--- /dev/null
+++ b/tests/data_calendar.php
@@ -0,0 +1,88 @@
+<?php
+
+class data_calendar extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Test for kolab_sync_data_calendar::from_kolab_alarm()
+ */
+ function test_from_kolab_alarm()
+ {
+ $obj = new kolab_sync_data_calendar_test;
+
+ $result = $obj->from_kolab_alarm(array());
+ $this->assertSame(null, $result);
+
+ $event = array('valarms' => array(array(
+ 'action' => 'DISPLAY',
+ 'trigger' => 'PT5M',
+ )));
+ $result = $obj->from_kolab_alarm($event);
+ $this->assertSame(null, $result);
+
+ $event = array('valarms' => array(array(
+ 'action' => 'DISPLAY',
+ 'trigger' => '-PT5M',
+ )));
+ $result = $obj->from_kolab_alarm($event);
+ $this->assertSame(5, $result);
+
+ $event = array('valarms' => array(array(
+ 'action' => 'DISPLAY',
+ 'trigger' => 'PT0M',
+ )));
+ $result = $obj->from_kolab_alarm($event);
+ $this->assertSame(0, $result);
+
+ $event = array('valarms' => array(array(
+ 'action' => 'DISPLAY',
+ 'trigger' => '-PT0M',
+ )));
+ $result = $obj->from_kolab_alarm($event);
+ $this->assertSame(0, $result);
+
+ $event = array('valarms' => array(array(
+ 'action' => 'DISPLAY',
+ 'trigger' => 'PT0S',
+ )));
+ $result = $obj->from_kolab_alarm($event);
+ $this->assertSame(0, $result);
+ }
+
+ /**
+ * Test for kolab_sync_data_calendar::to_kolab_alarm()
+ */
+ function test_to_kolab_alarm()
+ {
+ $obj = new kolab_sync_data_calendar_test;
+
+ $result = $obj->to_kolab_alarm(null, array());
+ $this->assertSame(array(), $result);
+
+ $result = $obj->to_kolab_alarm(0, array());
+ $this->assertSame('-PT0M', $result[0]['trigger']);
+
+ $result = $obj->to_kolab_alarm(15, array());
+ $this->assertSame('-PT15M', $result[0]['trigger']);
+ $this->assertSame('DISPLAY', $result[0]['action']);
+ }
+}
+
+/**
+ * kolab_sync_data_calendar wrapper, so we can test protected methods too
+ */
+class kolab_sync_data_calendar_test extends kolab_sync_data_calendar
+{
+ function __construct()
+ {
+ }
+
+ public function from_kolab_alarm($value)
+ {
+ return parent::from_kolab_alarm($value);
+ }
+
+ public function to_kolab_alarm($value, $event)
+ {
+ return parent::to_kolab_alarm($value, $event);
+ }
+}
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
index ba94a37..fdd6461 100644
--- a/tests/phpunit.xml
+++ b/tests/phpunit.xml
@@ -1,13 +1,14 @@
<phpunit backupGlobals="false"
bootstrap="bootstrap.php"
colors="true">
<testsuites>
<testsuite name="All Tests">
<file>body_converter.php</file>
<file>data.php</file>
+ <file>data_calendar.php</file>
<file>data_tasks.php</file>
<file>message.php</file>
<file>timezone_converter.php</file>
</testsuite>
</testsuites>
</phpunit>

File Metadata

Mime Type
text/x-diff
Expires
Tue, Feb 3, 7:44 AM (1 d, 8 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
05/76/d2f5c26748ccedb5ae9fcc361895
Default Alt Text
(1 MB)

Event Timeline