WIP push for mail (currently only Dovecot with further configuration!)

This commit is contained in:
Ralf Becker 2020-07-20 12:08:53 +02:00
parent 6d9dfc6364
commit bf44ee753a
6 changed files with 198 additions and 17 deletions

View File

@ -210,25 +210,67 @@ var AppJS = (function(){ "use strict"; return Class.extend(
},
/**
* Push method receives push notification about updates to entries from the application
* Handle a push notification about entry changes from the websocket
*
* It can use the extra _data parameter to determine if the client has read access to
* the entry - if an update of the list is necessary.
* Get's called for data of all apps, but should only handle data of apps it displays,
* which is by default only it's own, but can be for multiple apps eg. for calendar.
*
* @param {string} _type either 'update', 'edit', 'delete', 'add' or null
* @param pushData
* @param {string} pushData.app application name
* @param {(string|number)} pushData.id id of entry to refresh or null
* @param {string} pushData.type either 'update', 'edit', 'delete', 'add' or null
* - update: request just modified data from given rows. Sorting is not considered,
* so if the sort field is changed, the row will not be moved.
* - edit: rows changed, but sorting may be affected. Requires full reload.
* - delete: just delete the given rows clientside (no server interaction neccessary)
* - add: requires full reload for proper sorting
* @param {string} _app application name
* @param {(string|number)} _id id of entry to refresh or null
* @param {mixed} _data eg. owner or responsible to decide if update is necessary
* @returns {undefined}
* @param {object|null} pushData.acl Extra data for determining relevance. eg: owner or responsible to decide if update is necessary
* @param {number} pushData.account_id User that caused the notification
*/
push: function(_type, _app, _id, _data)
push: function(pushData)
{
// don't care about other apps data, reimplement if your app does care eg. calendar
if (pushData.app !== this.appname) return;
// only handle delete by default, for simple case of uid === "$app::$id"
if (pushData.type === 'delete')
{
egw.dataStoreUID(this.uid(pushData), null);
}
},
/**
* Get (possible) app-specific uid
*
* @param {object} pushData see push method for individual attributes
*/
uid(pushData)
{
return pushData.app + '::' + pushData.id;
},
/**
* Method called after apps push implementation checked visibility
*
* @param {et2_nextmatch} nm
* @param pushData see push method for individual attributes
* @todo implement better way to update nextmatch widget without disturbing the user / state
* @todo show indicator that an update has happend
* @todo rate-limit update frequency
*/
updateList: function(nm, pushData)
{
switch (pushData.type)
{
case 'add':
case 'unknown':
nm.applyFilters();
break;
default:
egw.dataRefreshUID(this.uid(pushData));
break;
}
},
/**
@ -243,7 +285,7 @@ var AppJS = (function(){ "use strict"; return Class.extend(
open: function(_action, _senders) {
var id_app = _senders[0].id.split('::');
egw.open(id_app[1], this.appname);
},
},
/**
* A generic method to action to server asynchronously

View File

@ -14,6 +14,7 @@ namespace EGroupware\Api\Mail\Imap;
use EGroupware\Api;
use EGroupware\Api\Mail;
use EGroupware\SwoolePush\Tokens;
/**
* Manages connection to Dovecot IMAP server
@ -24,7 +25,7 @@ use EGroupware\Api\Mail;
* --> require by webserver writable user_home to be configured, otherwise deleting get ignored like with defaultimap
* - quota can be read, but not set
*/
class Dovecot extends Mail\Imap
class Dovecot extends Mail\Imap implements Mail\Imap\PushIface
{
/**
* Label shown in EMailAdmin
@ -281,4 +282,49 @@ class Dovecot extends Mail\Imap
// mailbox get's automatic created with full rights for user
return true;
}
/**
* Metadata name to enable push notifications
*/
const METADATA_NAME = '/private/vendor/vendor.dovecot/http-notify';
const METADATA_MAILBOX = '';
const METADATA_PREFIX = 'user=';
/**
* Enable push notifictions for current connection and given account_id
*
* @param int $account_id =null 0=everyone on the instance
* @return bool true on success, false on failure
*/
function enablePush($account_id=null)
{
if (!class_exists(Tokens::class))
{
return false;
}
try {
$this->setMetadata(self::METADATA_MAILBOX, [
self::METADATA_NAME => self::METADATA_PREFIX.$GLOBALS['egw_info']['user']['account_id'].'::'.$this->acc_id.';'.
$this->getMailBoxUserName($GLOBALS['egw_info']['user']['account_lid']) . ';' .
((string)$account_id === '0' ? Tokens::instance() : Tokens::user($account_id)) . '@' .
Api\Header\Http::host(),
]);
}
catch (Horde_Imap_Client_Exception $e) {
_egw_log_exception($e);
return false;
}
return true;
}
/**
* Check if push is available / konfigured for given server
*
* @return bool
*/
function pushAvailable()
{
return in_array($this->acc_imap_host, ['imap.egroupware.org', 'mail.egroupware.org']) ||
$this->acc_imap_host === 'mail' && $this->acc_imap_port == 10143;
}
}

View File

@ -5,10 +5,9 @@
* @link http://www.stylite.de
* @package api
* @subpackage mail
* @author Ralf Becker <rb@stylite.de>
* @author Stylite AG <info@stylite.de>
* @author Ralf Becker <rb@egroupware.org>
* @author EGroupware GmbH <info@egroupware.org>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
namespace EGroupware\Api\Mail\Imap;

View File

@ -0,0 +1,38 @@
<?php
/**
* EGroupware Api: Push Interface for IMAP
*
* @link http://www.stylite.de
* @package api
* @subpackage mail
* @author Ralf Becker <rb@egroupware.org>
* @author EGroupware GmbH <info@egroupware.org>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Mail\Imap;
/**
* This class holds all information about the imap connection.
* This is the base class for all other imap classes.
*
* Also proxies Sieve calls to Mail\Sieve (eg. it behaves like the former felamimail bosieve),
* to allow IMAP plugins to also manage Sieve connection.
*/
interface PushIface
{
/**
* Check if push is available / konfigured for given server
*
* @return bool
*/
function pushAvailable();
/**
* Enable push notifictions for current connection and given account_id
*
* @param int $account_id =null 0=everyone on the instance
* @return bool true on success, false on failure
*/
function enablePush($account_id=null);
}

View File

@ -248,7 +248,16 @@ class mail_ui
// save session varchar
$oldicServerID =& Api\Cache::getSession('mail','activeProfileID');
if ($oldicServerID <> self::$icServerID) $this->mail_bo->openConnection(self::$icServerID);
if ($oldicServerID != self::$icServerID)
{
$this->mail_bo->openConnection(self::$icServerID);
// enable push notifications, if supported (and konfigured) by the server
if ($this->mail_bo->icServer instanceof Api\Mail\Imap\PushIface &&
$this->mail_bo->icServer->pushAvailable())
{
$this->mail_bo->icServer->enablePush();
}
}
if (true) $oldicServerID = self::$icServerID;
if (!Mail::storeActiveProfileIDToPref($this->mail_bo->icServer, self::$icServerID, true ))
{

View File

@ -5,10 +5,9 @@
*
* @link http://www.egroupware.org
* @author EGroupware GmbH [info@egroupware.org]
* @copyright (c) 2013-2014 by EGroupware GmbH <info-AT-egroupware.org>
* @copyright (c) 2013-2020 by EGroupware GmbH <info-AT-egroupware.org>
* @package mail
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
/*egw:uses
@ -367,6 +366,54 @@ app.classes.mail = AppJS.extend(
this.preSetToggledOnActions ();
},
/**
* Handle a push notification about entry changes from the websocket
*
* Get's called for data of all apps, but should only handle data of apps it displays,
* which is by default only it's own, but can be for multiple apps eg. for calendar.
*
* @param pushData
* @param {string} pushData.app application name
* @param {(string|number)} pushData.id id of entry to refresh or null
* @param {string} pushData.type either 'update', 'edit', 'delete', 'add' or null
* - update: request just modified data from given rows. Sorting is not considered,
* so if the sort field is changed, the row will not be moved.
* - edit: rows changed, but sorting may be affected. Requires full reload.
* - delete: just delete the given rows clientside (no server interaction neccessary)
* - add: requires full reload for proper sorting
* @param {object|null} pushData.acl Extra data for determining relevance. eg: owner or responsible to decide if update is necessary
* @param {number} pushData.account_id User that caused the notification
*/
push: function(pushData)
{
// don't care about other apps data, reimplement if your app does care eg. calendar
if (pushData.app !== this.appname) return;
// only handle delete by default, for simple case of uid === "$app::$id"
if (pushData.type === 'delete')
{
return this._super.call(this, pushData);
}
// notify user a new mail arrived
if (pushData.type === 'add')
{
this.egw.message(this.egw.lang('New mail from %1', pushData.acl.from)+'\n'+pushData.acl.subject+'\n'+pushData.acl.snippet, 'success');
}
// check if we might not see it because we are on a different mail account or folder
let nm = this.et2 ? this.et2.getWidgetById('nm') : null;
let nm_value = nm ? nm.getValue() : null;
if (nm_value && nm_value.col_filter)
{
this.updateList(nm, pushData);
}
// update unseen counter in folder-tree
if (pushData.type === 'add' && pushData.acl.folder && pushData.acl.unseen)
{
// todo: pushData.id contains acc_id
}
},
/**
* Observer method receives update notifications from all applications
*