Enable push for addressbook

Refactor EgwApp.push() to cover more common stuff for simpler app code
This commit is contained in:
nathangray 2021-02-24 09:18:42 -07:00
parent 2a2f0bf390
commit 3c72462b5e
7 changed files with 252 additions and 10 deletions

View File

@ -408,7 +408,8 @@ class addressbook_hooks
'owner' => array(
'key' => 'egw_addressbook.contact_id',
'column' => 'egw_addressbook.contact_owner'
)
),
'push_data' => ['owner','tid','cat_id']
);
return $links;
}

View File

@ -44,8 +44,13 @@ var AddressbookApp = /** @class */ (function (_super) {
* @memberOf app.addressbook
*/
function AddressbookApp() {
var _this =
// call parent
return _super.call(this, 'addressbook') || this;
_super.call(this, 'addressbook') || this;
// These fields help with push
_this.push_grant_fields = ["owner"];
_this.push_filter_fields = ["tid", "owner", "cat_id"];
return _this;
}
/**
* Destructor
@ -1231,9 +1236,8 @@ var AddressbookApp = /** @class */ (function (_super) {
* Remove the entry again, if user is not allowed
*/
AddressbookApp.prototype.shared_changed = function () {
var _a;
var shared = this.et2.getInputWidgetById('shared_values');
var value = (_a = shared) === null || _a === void 0 ? void 0 : _a.get_value();
var value = shared === null || shared === void 0 ? void 0 : shared.get_value();
if (value) {
this.egw.json('addressbook.addressbook_ui.ajax_check_shared', [{
contact: this.et2.getInstanceManager().getValues(this.et2),

View File

@ -27,6 +27,10 @@ import {etemplate2} from "../../api/js/etemplate/etemplate2";
*/
class AddressbookApp extends EgwApp
{
// These fields help with push
protected push_grant_fields = ["owner"];
protected push_filter_fields = ["tid","owner","cat_id"]
/**
* Constructor
*

View File

@ -55,6 +55,7 @@ var __extends = (this && this.__extends) || (function () {
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.et2_nextmatch_accountfilterheader = exports.et2_nextmatch_filterheader = exports.et2_nextmatch_sortheader = exports.et2_nextmatch_customfields = exports.et2_nextmatch_header = exports.et2_nextmatch = void 0;
require("./et2_core_common");
require("./et2_core_interfaces");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
@ -1921,11 +1922,10 @@ var et2_nextmatch = /** @class */ (function (_super) {
* @param {object} target
*/
et2_nextmatch.prototype.handle_drop = function (event, target) {
var _a;
// Check to see if we can handle the link
// First, find the UID
var row = this.controller.getRowByNode(target);
var uid = ((_a = row) === null || _a === void 0 ? void 0 : _a.uid) || null;
var uid = (row === null || row === void 0 ? void 0 : row.uid) || null;
// Get the file information
var files = [];
if (event.originalEvent && event.originalEvent.dataTransfer &&
@ -2014,7 +2014,8 @@ var et2_nextmatch = /** @class */ (function (_super) {
idsArr[i] = idsArr[i].split("::").pop();
}
var value = {
"selected": idsArr
"selected": idsArr,
col_filter: {}
};
jQuery.extend(value, this.activeFilters, this.value);
return value;

View File

@ -2707,7 +2707,8 @@ export class et2_nextmatch extends et2_DOMWidget implements et2_IResizeable, et2
idsArr[i] = idsArr[i].split("::").pop();
}
const value = {
"selected": idsArr
"selected": idsArr,
col_filter: {}
};
jQuery.extend(value, this.activeFilters, this.value);
return value;

View File

@ -163,10 +163,107 @@ var EgwApp = /** @class */ (function () {
// 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"
// handle delete, for simple case of uid === "$app::$id"
if (pushData.type === 'delete' && egw.dataHasUID(this.uid(pushData))) {
egw.refresh('', pushData.app, pushData.id, 'delete');
return;
}
// If we know about it and it's an update, just update.
// This must be before all ACL checks, as responsible might have changed and entry need to be removed
// (server responds then with null / no entry causing the entry to disappear)
if (pushData.type !== "add" && this.egw.dataHasUID(this.uid(pushData))) {
return this.et2.getInstanceManager().refresh("", pushData.app, pushData.id, pushData.type);
}
// Check grants to see if we know we aren't supposed to show it
if (typeof this.push_grant_fields !== "undefined" && this.push_grant_fields.length > 0
&& !this._push_grant_check(pushData, this.push_grant_fields)) {
return;
}
// Nextmatch does the hard part of updating. Try to find one.
var nm = this.et2.getDOMWidgetById('nm');
if (!nm) {
return;
}
// Filter what's allowed down to those we can see / care about based on nm filters
if (typeof this.push_filter_fields !== "undefined" && this.push_filter_fields.length > 0 &&
!this._push_field_filter(pushData, nm, this.push_filter_fields)) {
return;
}
// Pass actual refresh on to just nextmatch
nm.refresh(pushData.id, pushData.type);
};
/**
* Check grants to see if we can quickly tell if this entry is not for us
*
* Override this method if the app has non-standard access control.
*
* @param pushData
* @param grant_fields List of fields in pushData.acl with account IDs that might grant access eg: info_responsible
*/
EgwApp.prototype._push_grant_check = function (pushData, grant_fields) {
var grants = egw.grants(this.appname);
// check user has a grant from owner or something
for (var i = 0; i < grant_fields.length; i++) {
if (grants && typeof grants[pushData.acl[grant_fields[i]]] !== 'undefined') {
// ACL access
return true;
}
}
return false;
};
/**
* Check pushData.acl values against a list of fields to see if we care about this entry based on current nextmatch
* filter values. This is not a definitive yes or no (the server will tell us when we ask), we just want to cheaply
* avoid a server call if we know it won't be in the list.
*
* @param pushData
* @param filter_fields List of filter field names eg: [owner, cat_id]
* @return boolean True if the nextmatch filters might include the entry, false if not
*/
EgwApp.prototype._push_field_filter = function (pushData, nm, filter_fields) {
var filters = {};
for (var i = 0; i < filter_fields.length; i++) {
filters[filter_fields[i]] = {
col: filter_fields[i],
filter_values: []
};
}
// Get current filter values
if (this.et2) {
var value = nm.getValue();
if (!value || !value.col_filter)
return false;
for (var _i = 0, _a = Object.values(filters); _i < _a.length; _i++) {
var field_filter = _a[_i];
if (value.col_filter[field_filter.col]) {
field_filter.filter_values.push(value.col_filter[field_filter.col]);
}
}
}
var _loop_1 = function (field_filter) {
// no filter set
if (field_filter.filter_values.length == 0)
return "continue";
// acl value is a scalar (not array) --> check contained in filter
if (pushData.acl && typeof pushData.acl[field_filter.col] !== 'object') {
if (field_filter.filter_values.indexOf(pushData.acl[field_filter.col]) < 0) {
return { value: false };
}
return "continue";
}
// acl value is an array (eg. tr_assigned) --> check intersection with filter
if (!field_filter.filter_values.filter(function (account) { return pushData.acl[field_filter.col].indexOf(account) >= 0; }).length) {
return { value: false };
}
};
// check filters against pushData.acl data
for (var _b = 0, _c = Object.values(filters); _b < _c.length; _b++) {
var field_filter = _c[_b];
var state_1 = _loop_1(field_filter);
if (typeof state_1 === "object")
return state_1.value;
}
return true;
};
/**
* Get (possible) app-specific uid

View File

@ -131,6 +131,22 @@ export abstract class EgwApp
*/
static _instances: EgwApp[] = [];
/**
* If pushData.acl has fields that can help filter based on ACL grants, list them
* here and we can check them and ignore push messages if there is no ACL for that entry
*
* @protected
*/
protected push_grant_fields: string[];
/**
* If pushData.acl has fields that can help filter based on current nextmatch filters,
* list them here and we can check and ignore push messages if the nextmatch filters do not exclude them
*
* @protected
*/
protected push_filter_fields: string[];
/**
* Initialization and setup goes here, but the etemplate2 object
* is not yet ready.
@ -259,11 +275,129 @@ export abstract class EgwApp
// 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"
// handle delete, for simple case of uid === "$app::$id"
if (pushData.type === 'delete' && egw.dataHasUID(this.uid(pushData)))
{
egw.refresh('', pushData.app, pushData.id, 'delete');
return;
}
// If we know about it and it's an update, just update.
// This must be before all ACL checks, as responsible might have changed and entry need to be removed
// (server responds then with null / no entry causing the entry to disappear)
if (pushData.type !== "add" && this.egw.dataHasUID(this.uid(pushData)))
{
return this.et2.getInstanceManager().refresh("", pushData.app, pushData.id, pushData.type);
}
// Check grants to see if we know we aren't supposed to show it
if(typeof this.push_grant_fields !== "undefined" && this.push_grant_fields.length > 0
&& !this._push_grant_check(pushData, this.push_grant_fields)
)
{
return;
}
// Nextmatch does the hard part of updating. Try to find one.
let nm = <et2_nextmatch>this.et2.getDOMWidgetById('nm');
if(!nm)
{
return;
}
// Filter what's allowed down to those we can see / care about based on nm filters
if(typeof this.push_filter_fields !== "undefined" && this.push_filter_fields.length > 0 &&
!this._push_field_filter(pushData, nm, this.push_filter_fields)
)
{
return;
}
// Pass actual refresh on to just nextmatch
nm.refresh(pushData.id, pushData.type);
}
/**
* Check grants to see if we can quickly tell if this entry is not for us
*
* Override this method if the app has non-standard access control.
*
* @param pushData
* @param grant_fields List of fields in pushData.acl with account IDs that might grant access eg: info_responsible
*/
_push_grant_check(pushData : PushData, grant_fields : string[]) : boolean
{
let grants = egw.grants(this.appname);
// check user has a grant from owner or something
for(let i = 0; i < grant_fields.length; i++)
{
if(grants && typeof grants[pushData.acl[grant_fields[i]]] !== 'undefined')
{
// ACL access
return true;
}
}
return false;
}
/**
* Check pushData.acl values against a list of fields to see if we care about this entry based on current nextmatch
* filter values. This is not a definitive yes or no (the server will tell us when we ask), we just want to cheaply
* avoid a server call if we know it won't be in the list.
*
* @param pushData
* @param filter_fields List of filter field names eg: [owner, cat_id]
* @return boolean True if the nextmatch filters might include the entry, false if not
*/
_push_field_filter(pushData : PushData, nm : et2_nextmatch, filter_fields: string[]) : boolean
{
let filters = {};
for(let i = 0; i < filter_fields.length; i++)
{
filters[filter_fields[i]] = {
col: filter_fields[i],
filter_values: []
};
}
// Get current filter values
if(this.et2)
{
let value = nm.getValue();
if(!value || !value.col_filter) return false;
for(let field_filter of Object.values(filters))
{
if(value.col_filter[field_filter.col])
{
field_filter.filter_values.push(value.col_filter[field_filter.col]);
}
}
}
// check filters against pushData.acl data
for(let field_filter of Object.values(filters))
{
// no filter set
if (field_filter.filter_values.length == 0) continue;
// acl value is a scalar (not array) --> check contained in filter
if (pushData.acl && typeof pushData.acl[field_filter.col] !== 'object')
{
if (field_filter.filter_values.indexOf(pushData.acl[field_filter.col]) < 0)
{
return false;
}
continue;
}
// acl value is an array (eg. tr_assigned) --> check intersection with filter
if(!field_filter.filter_values.filter(account => pushData.acl[field_filter.col].indexOf(account) >= 0).length)
{
return false;
}
}
return true;
}
/**