2020-09-03 17:58:12 +02:00
|
|
|
/**
|
|
|
|
* EGroupware - Addressbook - Javascript UI
|
|
|
|
*
|
|
|
|
* @link: https://www.egroupware.org
|
|
|
|
* @package addressbook
|
|
|
|
* @author Hadi Nategh <hn-AT-stylite.de>
|
2021-06-09 11:11:34 +02:00
|
|
|
* @author Ralf Becker <rb-AT-egroupware.org>
|
|
|
|
* @copyright (c) 2008-21 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
2020-09-03 17:58:12 +02:00
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*egw:uses
|
|
|
|
/api/js/jsapi/egw_app.js
|
|
|
|
*/
|
|
|
|
|
|
|
|
import {EgwApp, PushData} from '../../api/js/jsapi/egw_app';
|
|
|
|
import {etemplate2} from "../../api/js/etemplate/etemplate2";
|
|
|
|
import {et2_nextmatch} from "../../api/js/etemplate/et2_extension_nextmatch";
|
2021-06-25 22:49:24 +02:00
|
|
|
import {egw} from "../../api/js/jsapi/egw_global.js";
|
2020-09-03 17:58:12 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* UI for Addressbook CRM view
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
export class CRMView extends EgwApp
|
|
|
|
{
|
2020-10-08 19:53:17 +02:00
|
|
|
// List ID
|
|
|
|
list_id: string = "";
|
|
|
|
|
2020-09-03 17:58:12 +02:00
|
|
|
// Reference to the list
|
|
|
|
nm: et2_nextmatch = null;
|
|
|
|
|
|
|
|
// Which addressbook contact id(s) we are showing entries for
|
|
|
|
contact_ids: string[] = [];
|
|
|
|
|
|
|
|
// Private js for the list
|
|
|
|
app_obj: EgwApp = null;
|
|
|
|
|
|
|
|
// Hold on to the original push handler
|
|
|
|
private _app_obj_push: (pushData: PushData) => void;
|
|
|
|
|
2020-09-04 17:37:07 +02:00
|
|
|
// Push data key(s) to check for our contact ID in the entry's ACL data
|
2020-09-03 17:58:12 +02:00
|
|
|
private push_contact_ids = ["contact_id"];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* CRM is part of addressbook
|
|
|
|
*/
|
|
|
|
constructor()
|
|
|
|
{
|
|
|
|
// call parent
|
|
|
|
super('addressbook');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destructor
|
|
|
|
*/
|
|
|
|
destroy(_app)
|
|
|
|
{
|
|
|
|
this.nm = null;
|
|
|
|
if(this.app_obj != null)
|
|
|
|
{
|
|
|
|
this.app_obj.destroy(_app);
|
|
|
|
}
|
|
|
|
|
|
|
|
// call parent
|
|
|
|
super.destroy(_app);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A template from an app is ready, looks like it might be a CRM view.
|
|
|
|
* Check it, get CRM ready, and bind accordingly
|
|
|
|
*
|
|
|
|
* @param et2
|
|
|
|
* @param appname
|
|
|
|
*/
|
|
|
|
static view_ready(et2: etemplate2, app_obj: EgwApp)
|
|
|
|
{
|
|
|
|
// Check to see if the template is for a CRM view
|
|
|
|
if(et2.app == app_obj.appname)
|
|
|
|
{
|
2020-10-08 19:53:17 +02:00
|
|
|
return CRMView.reconnect(app_obj);
|
2020-09-03 17:58:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure object is there, etemplate2 will pick it up and call our et2_ready
|
2020-10-08 19:53:17 +02:00
|
|
|
let crm : CRMView = undefined;
|
|
|
|
// @ts-ignore
|
2020-09-03 17:58:12 +02:00
|
|
|
if(typeof et2.app_obj.crm == "undefined" && app.classes.crm)
|
|
|
|
{
|
2020-10-08 19:53:17 +02:00
|
|
|
// @ts-ignore
|
|
|
|
crm = et2.app_obj.crm = new app.classes.crm();
|
2020-09-03 17:58:12 +02:00
|
|
|
}
|
2020-10-08 19:53:17 +02:00
|
|
|
if(typeof crm == "undefined")
|
2020-09-03 17:58:12 +02:00
|
|
|
{
|
|
|
|
egw.debug("error", "CRMView object is missing");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// We can set this now
|
|
|
|
crm.set_view_obj(app_obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function is called when the etemplate2 object is loaded
|
|
|
|
* and ready. The associated app [is supposed to have] already called its own et2_ready(),
|
|
|
|
* so any changes done here will override the app.
|
|
|
|
*
|
|
|
|
* @param {etemplate2} et2 newly ready object
|
|
|
|
* @param {string} name Template name
|
|
|
|
*/
|
|
|
|
et2_ready(et2, name)
|
|
|
|
{
|
|
|
|
// call parent
|
|
|
|
super.et2_ready(et2, name);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-10-08 19:53:17 +02:00
|
|
|
/**
|
|
|
|
* Our CRM has become disconnected from its list, probably because something submitted.
|
|
|
|
* Find it, and get things working again.
|
|
|
|
*
|
|
|
|
* @param app_obj
|
|
|
|
*/
|
|
|
|
static reconnect(app_obj : EgwApp)
|
|
|
|
{
|
|
|
|
// Check
|
|
|
|
let contact_ids = app_obj.et2.getArrayMgr("content").getEntry("action_id") || "";
|
|
|
|
if(!contact_ids) return;
|
|
|
|
|
|
|
|
for (let existing_app of EgwApp._instances)
|
|
|
|
{
|
|
|
|
if(existing_app instanceof CRMView && existing_app.list_id == app_obj.et2.getInstanceManager().uniqueId)
|
|
|
|
{
|
|
|
|
// List was reloaded. Rebind.
|
|
|
|
existing_app.app_obj.destroy(existing_app.app_obj.appname);
|
|
|
|
if(!existing_app.nm?.getParent())
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// This will probably not die cleanly, we had a reference when it was destroyed
|
|
|
|
existing_app.nm.destroy();
|
|
|
|
} catch (e) {}
|
|
|
|
}
|
|
|
|
return existing_app.set_view_obj(app_obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-03 17:58:12 +02:00
|
|
|
/**
|
|
|
|
* Set the associated private app JS
|
|
|
|
* We try and pull the needed info here
|
|
|
|
*/
|
|
|
|
set_view_obj(app_obj: EgwApp)
|
|
|
|
{
|
|
|
|
this.app_obj = app_obj;
|
|
|
|
|
2020-10-08 19:53:17 +02:00
|
|
|
// Make sure object is there, etemplate2 will pick it up and call our et2_ready
|
|
|
|
app_obj.et2.getInstanceManager().app_obj.crm = this
|
|
|
|
|
|
|
|
// Make _sure_ we get notified if the list is removed (actions, refresh) - this is not always a full
|
|
|
|
// destruction
|
|
|
|
jQuery(app_obj.et2.getDOMNode()).on('clear', function() {
|
|
|
|
this.nm = null;
|
|
|
|
}.bind(this));
|
|
|
|
|
2020-09-03 17:58:12 +02:00
|
|
|
// For easy reference later
|
2020-10-08 19:53:17 +02:00
|
|
|
this.list_id = app_obj.et2.getInstanceManager().uniqueId;
|
2020-09-03 17:58:12 +02:00
|
|
|
this.nm = <et2_nextmatch>app_obj.et2.getDOMWidgetById('nm');
|
|
|
|
|
|
|
|
let contact_ids = app_obj.et2.getArrayMgr("content").getEntry("action_id") || "";
|
|
|
|
if(typeof contact_ids == "string")
|
|
|
|
{
|
|
|
|
contact_ids = contact_ids.split(",");
|
|
|
|
}
|
|
|
|
this.set_contact_ids(contact_ids);
|
|
|
|
|
|
|
|
// Override the push handler
|
|
|
|
this._override_push(app_obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set or change which contact IDs we are showing entries for
|
|
|
|
*/
|
|
|
|
set_contact_ids(ids: string[])
|
|
|
|
{
|
|
|
|
this.contact_ids = ids;
|
|
|
|
let filter = {action_id: this.contact_ids};
|
|
|
|
|
|
|
|
if(this.nm !== null)
|
|
|
|
{
|
|
|
|
this.nm.applyFilters(filter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a push notification about entry changes from the websocket
|
|
|
|
*
|
|
|
|
* @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: ask server for data, add in intelligently
|
|
|
|
* @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(pushData)
|
|
|
|
{
|
|
|
|
if(pushData.app !== this.app_obj.appname || !this.nm) return;
|
|
|
|
|
|
|
|
// If we know about it and it's an update, just update.
|
|
|
|
// This must be before all ACL checks, as contact might have changed and entry needs to be removed
|
2020-09-04 17:37:07 +02:00
|
|
|
// (server responds then with null / no entry causing the entry to disappear)
|
2020-09-03 17:58:12 +02:00
|
|
|
if (pushData.type !== "add" && this.egw.dataHasUID(this.uid(pushData)))
|
|
|
|
{
|
2020-09-04 17:37:07 +02:00
|
|
|
// Check to see if it's in OUR nextmatch
|
|
|
|
let uid = this.uid(pushData);
|
|
|
|
let known = Object.values(this.nm.controller._indexMap).filter(function(row) {return row.uid ==uid;});
|
|
|
|
let type = pushData.type;
|
|
|
|
if(known && known.length > 0)
|
|
|
|
{
|
|
|
|
if(!this.id_check(pushData.acl))
|
|
|
|
{
|
|
|
|
// Was ours, not anymore, and we know this now - no server needed. Just remove from nm.
|
|
|
|
type = et2_nextmatch.DELETE;
|
|
|
|
}
|
|
|
|
return this.nm.refresh(pushData.id, type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.id_check(pushData.acl))
|
|
|
|
{
|
|
|
|
return this._app_obj_push(pushData);
|
2020-09-03 17:58:12 +02:00
|
|
|
}
|
|
|
|
|
2020-09-04 17:37:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check to see if the given entry is "ours"
|
|
|
|
*
|
|
|
|
* @param entry
|
|
|
|
*/
|
|
|
|
id_check(entry) : boolean
|
|
|
|
{
|
2020-09-03 17:58:12 +02:00
|
|
|
// Check if it's for one of our contacts
|
|
|
|
for(let field of this.push_contact_ids)
|
|
|
|
{
|
2020-09-04 17:37:07 +02:00
|
|
|
if(entry && entry[field])
|
2020-09-03 17:58:12 +02:00
|
|
|
{
|
2020-09-04 17:37:07 +02:00
|
|
|
let val = typeof entry[field] == "string" ? [entry[field]] : entry[field];
|
2020-09-03 17:58:12 +02:00
|
|
|
if(val.filter(v => this.contact_ids.indexOf(v) >= 0).length > 0)
|
|
|
|
{
|
2020-09-04 17:37:07 +02:00
|
|
|
return true;
|
2020-09-03 17:58:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-04 17:37:07 +02:00
|
|
|
return false;
|
2020-09-03 17:58:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Override the list's push handler to do nothing, we'll call it if we want it.
|
|
|
|
*
|
|
|
|
* @param app_obj
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_override_push(app_obj : EgwApp)
|
|
|
|
{
|
|
|
|
this._app_obj_push = app_obj.push.bind(app_obj);
|
|
|
|
app_obj.push = function(pushData) {return false;};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
app.classes.crm = CRMView;
|