diff --git a/api/js/jsapi/egw_app.js b/api/js/jsapi/egw_app.js index e2ad66c0e6..e4caf79833 100644 --- a/api/js/jsapi/egw_app.js +++ b/api/js/jsapi/egw_app.js @@ -199,9 +199,12 @@ var EgwApp = /** @class */ (function () { * * @param pushData * @param grant_fields List of fields in pushData.acl with account IDs that might grant access eg: info_responsible + * @param appname Optional, to check against the grants for a different application. Defaults to this.appname. + * + * @return boolean Entry has ACL access */ - EgwApp.prototype._push_grant_check = function (pushData, grant_fields) { - var grants = egw.grants(this.appname); + EgwApp.prototype._push_grant_check = function (pushData, grant_fields, appname) { + var grants = egw.grants(appname || this.appname); // No grants known if (!grants) return true; diff --git a/api/js/jsapi/egw_app.ts b/api/js/jsapi/egw_app.ts index de729a71b2..34c79a246a 100644 --- a/api/js/jsapi/egw_app.ts +++ b/api/js/jsapi/egw_app.ts @@ -324,10 +324,13 @@ export abstract class EgwApp * * @param pushData * @param grant_fields List of fields in pushData.acl with account IDs that might grant access eg: info_responsible + * @param appname Optional, to check against the grants for a different application. Defaults to this.appname. + * + * @return boolean Entry has ACL access */ - _push_grant_check(pushData : PushData, grant_fields : string[]) : boolean + _push_grant_check(pushData : PushData, grant_fields : string[], appname? : string) : boolean { - let grants = egw.grants(this.appname); + let grants = egw.grants(appname || this.appname); // No grants known if(!grants) return true; diff --git a/calendar/js/app.js b/calendar/js/app.js index 433bdade52..73d433af39 100644 --- a/calendar/js/app.js +++ b/calendar/js/app.js @@ -534,17 +534,21 @@ var CalendarApp = /** @class */ (function (_super) { * @param {number} pushData.account_id User that caused the notification */ CalendarApp.prototype.push = function (pushData) { - // Calendar cares about calendar & infolog - if (pushData.app !== this.appname && pushData.app !== 'infolog') - return; switch (pushData.app) { case "calendar": if (pushData.type === 'delete') { return _super.prototype.push.call(this, pushData); } return this.push_calendar(pushData); - case "infolog": - return this.push_infolog(pushData); + default: + if (jQuery.extend([], egw.preference("integration_toggle", "calendar")).indexOf(pushData.app) >= 0) { + if (pushData.app == "infolog") { + return this.push_infolog(pushData); + } + // Other integration here + // TODO + debugger; + } } }; /** @@ -555,17 +559,19 @@ var CalendarApp = /** @class */ (function (_super) { CalendarApp.prototype.push_infolog = function (pushData) { var _this = this; var _a; + // Check if we have access + if (!this._push_grant_check(pushData, ["info_owner", "info_responsible"], "infolog")) { + return; + } // check visibility - grants is ID => permission of people we're allowed to see - var owners = []; var infolog_grants = egw.grants(pushData.app); // Filter what's allowed down to those we care about var filtered = Object.keys(infolog_grants).filter(function (account) { return _this.state.owner.indexOf(account) >= 0; }); - // Check if we're interested in displaying by owner / responsible var owner_check = filtered.filter(function (value) { return pushData.acl.info_owner == value || pushData.acl.info_responsible.indexOf(value) >= 0; }); if (!owner_check || owner_check.length == 0) { - // The owner is not in the list of what we're allowed / care about + // The owner is not in the list of what we care about return; } // Only need to update the list if we're on that view diff --git a/calendar/js/app.ts b/calendar/js/app.ts index c7ba09d0d0..6729db7d99 100644 --- a/calendar/js/app.ts +++ b/calendar/js/app.ts @@ -465,9 +465,6 @@ class CalendarApp extends EgwApp */ push(pushData) { - // Calendar cares about calendar & infolog - if(pushData.app !== this.appname && pushData.app !== 'infolog') return; - switch (pushData.app) { case "calendar": @@ -476,8 +473,17 @@ class CalendarApp extends EgwApp return super.push(pushData); } return this.push_calendar(pushData); - case "infolog": - return this.push_infolog(pushData); + default: + if(jQuery.extend([],egw.preference("integration_toggle","calendar")).indexOf(pushData.app) >= 0) + { + if(pushData.app == "infolog") + { + return this.push_infolog(pushData); + } + // Other integration here + // TODO + debugger; + } } } @@ -488,20 +494,24 @@ class CalendarApp extends EgwApp */ private push_infolog(pushData : PushData) { + // Check if we have access + if(!this._push_grant_check(pushData, ["info_owner","info_responsible"],"infolog")) + { + return; + } + // check visibility - grants is ID => permission of people we're allowed to see - let owners = []; let infolog_grants = egw.grants(pushData.app); // Filter what's allowed down to those we care about let filtered = Object.keys(infolog_grants).filter(account => this.state.owner.indexOf(account) >= 0); - // Check if we're interested in displaying by owner / responsible let owner_check = filtered.filter(function(value) { return pushData.acl.info_owner == value || pushData.acl.info_responsible.indexOf(value) >= 0; }) if(!owner_check || owner_check.length == 0) { - // The owner is not in the list of what we're allowed / care about + // The owner is not in the list of what we care about return; } diff --git a/infolog/js/app.js b/infolog/js/app.js index 21a3a0f6f6..2bf6a16da9 100644 --- a/infolog/js/app.js +++ b/infolog/js/app.js @@ -31,7 +31,6 @@ require("../jsapi/egw_global"); require("../etemplate/et2_types"); var egw_app_1 = require("../../api/js/jsapi/egw_app"); var etemplate2_1 = require("../../api/js/etemplate/etemplate2"); -var et2_extension_nextmatch_1 = require("../../api/js/etemplate/et2_extension_nextmatch"); var CRM_1 = require("../../addressbook/js/CRM"); /** * UI for Infolog @@ -49,6 +48,9 @@ var InfologApp = /** @class */ (function (_super) { var _this = // call parent _super.call(this, 'infolog') || this; + // These fields help with push filtering & access control to see if we care about a push message + _this.push_grant_fields = ["info_owner", "info_responsible"]; + _this.push_filter_fields = ["info_owner", "info_responsible"]; _this._action_ids = []; _this._action_all = false; return _this; @@ -154,96 +156,6 @@ var InfologApp = /** @class */ (function (_super) { this.et2._inst.refresh(_msg, _app, _id, _type); } }; - /** - * 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 - */ - InfologApp.prototype.push = function (pushData) { - var _this = this; - if (pushData.app !== this.appname) - return; - // pushData does not contain everything, just the minimum. - var event = pushData.acl || {}; - if (pushData.type === 'delete') { - return _super.prototype.push.call(this, pushData); - } - // 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 disapear) - if (pushData.type !== "add" && this.egw.dataHasUID(this.uid(pushData))) { - return this.et2.getInstanceManager().refresh("", pushData.app, pushData.id, pushData.type); - } - // check visibility - grants is ID => permission of people we're allowed to see - if (typeof this._grants === 'undefined') { - this._grants = egw.grants(this.appname); - } - // check user has a grant from owner or a responsible - if (this._grants && typeof this._grants[pushData.acl.info_owner] === 'undefined' && - // responsible gets implicit access, so we need to check them too - !pushData.acl.info_responsible.filter(function (res) { return typeof _this._grants[res] !== 'undefined'; }).length) { - // No ACL access - return; - } - // no responsible means, owner is responsible - if (!pushData.acl.info_responsible || !pushData.acl.info_responsible.length) { - pushData.acl.info_responsible = [pushData.acl.info_owner]; - } - // Filter what's allowed down to those we care about - var filters = { - owner: { col: "info_owner", filter_values: [] }, - responsible: { col: "info_responsible", filter_values: [] } - }; - if (this.et2) { - this.et2.iterateOver(function (nm) { - var value = nm.getValue(); - if (!value || !value.col_filter) - return; - 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]); - } - } - }, this, et2_extension_nextmatch_1.et2_nextmatch); - } - 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: void 0 }; - } - return "continue"; - } - // acl value is an array (eg. info_responsible) --> check intersection with filter - if (!field_filter.filter_values.filter(function (account) { return pushData.acl[field_filter.col].indexOf(account) >= 0; }).length) { - return { value: void 0 }; - } - }; - // check filters against ACL data - for (var _i = 0, _a = Object.values(filters); _i < _a.length; _i++) { - var field_filter = _a[_i]; - var state_1 = _loop_1(field_filter); - if (typeof state_1 === "object") - return state_1.value; - } - // Pass actual refresh on to just nextmatch - var nm = this.et2.getDOMWidgetById('nm'); - nm.refresh(pushData.id, pushData.type); - }; /** * Retrieve the current state of the application for future restoration * diff --git a/infolog/js/app.ts b/infolog/js/app.ts index 48bdbd6bef..c1a2f071ad 100644 --- a/infolog/js/app.ts +++ b/infolog/js/app.ts @@ -31,8 +31,9 @@ import {CRMView} from "../../addressbook/js/CRM"; class InfologApp extends EgwApp { - // Hold on to ACL grants - private _grants : any; + // These fields help with push filtering & access control to see if we care about a push message + protected push_grant_fields = ["info_owner","info_responsible"]; + protected push_filter_fields = ["info_owner","info_responsible"] /** * Constructor @@ -163,108 +164,6 @@ class InfologApp extends EgwApp this.et2._inst.refresh(_msg, _app, _id, _type); } } - /** - * 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.appname) return; - - // pushData does not contain everything, just the minimum. - let event = pushData.acl || {}; - - if(pushData.type === 'delete') - { - return super.push(pushData); - } - - // 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 disapear) - if (pushData.type !== "add" && this.egw.dataHasUID(this.uid(pushData))) - { - return this.et2.getInstanceManager().refresh("", pushData.app, pushData.id, pushData.type); - } - - // check visibility - grants is ID => permission of people we're allowed to see - if (typeof this._grants === 'undefined') - { - this._grants = egw.grants(this.appname); - } - // check user has a grant from owner or a responsible - if (this._grants && typeof this._grants[pushData.acl.info_owner] === 'undefined' && - // responsible gets implicit access, so we need to check them too - !pushData.acl.info_responsible.filter(res => typeof this._grants[res] !== 'undefined').length) - { - // No ACL access - return; - } - - // no responsible means, owner is responsible - if (!pushData.acl.info_responsible || !pushData.acl.info_responsible.length) - { - pushData.acl.info_responsible = [pushData.acl.info_owner]; - } - - // Filter what's allowed down to those we care about - let filters = { - owner: {col: "info_owner", filter_values: []}, - responsible: {col: "info_responsible", filter_values: []} - }; - if(this.et2) - { - this.et2.iterateOver( function(nm) { - let value = nm.getValue(); - if(!value || !value.col_filter) return; - - 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]); - } - } - },this, et2_nextmatch); - } - - // check filters against 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; - } - continue; - } - // acl value is an array (eg. info_responsible) --> check intersection with filter - if(!field_filter.filter_values.filter(account => pushData.acl[field_filter.col].indexOf(account) >= 0).length) - { - return; - } - } - - // Pass actual refresh on to just nextmatch - let nm = this.et2.getDOMWidgetById('nm'); - nm.refresh(pushData.id, pushData.type); - } /** * Retrieve the current state of the application for future restoration diff --git a/timesheet/js/app.js b/timesheet/js/app.js index 0ff3f55d75..fb75162ffb 100644 --- a/timesheet/js/app.js +++ b/timesheet/js/app.js @@ -30,7 +30,6 @@ require("jqueryui"); require("../jsapi/egw_global"); require("../etemplate/et2_types"); var egw_app_1 = require("../../api/js/jsapi/egw_app"); -var etemplate2_1 = require("../../api/js/etemplate/etemplate2"); /** * UI for timesheet * @@ -39,7 +38,11 @@ var etemplate2_1 = require("../../api/js/etemplate/etemplate2"); var TimesheetApp = /** @class */ (function (_super) { __extends(TimesheetApp, _super); function TimesheetApp() { - return _super.call(this, 'timesheet') || this; + var _this = _super.call(this, 'timesheet') || this; + // These fields help with push filtering & access control to see if we care about a push message + _this.push_grant_fields = ["ts_owner"]; + _this.push_filter_fields = ["ts_owner"]; + return _this; } /** * This function is called when the etemplate2 object is loaded @@ -174,49 +177,6 @@ var TimesheetApp = /** @class */ (function (_super) { if (widget) return widget.options.value; }; - /** - * 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: 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 - */ - TimesheetApp.prototype.push = function (pushData) { - var _a, _b, _c; - // timesheed does NOT care about other apps data - if (pushData.app !== this.appname) - return; - if (pushData.type === 'delete') { - return _super.prototype.push.call(this, pushData); - } - // This must be before all ACL checks, as owner might have changed and entry need to be removed - // (server responds then with null / no entry causing the entry to disapear) - if (pushData.type !== "add" && this.egw.dataHasUID(this.uid(pushData))) { - return etemplate2_1.etemplate2.app_refresh("", pushData.app, pushData.id, pushData.type); - } - // all other cases (add, edit, update) are handled identical - // check visibility - if (typeof this._grants === 'undefined') { - this._grants = egw.grants(this.appname); - } - if (typeof this._grants[pushData.acl.ts_owner] === 'undefined') - return; - // check if we might not see it because of an owner filter - var nm = (_a = this.et2) === null || _a === void 0 ? void 0 : _a.getWidgetById('nm'); - var nm_value = (_b = nm) === null || _b === void 0 ? void 0 : _b.getValue(); - if (nm && nm_value && ((_c = nm_value.col_filter) === null || _c === void 0 ? void 0 : _c.ts_owner) && nm_value.col_filter.ts_owner != pushData.acl.ts_owner) { - return; - } - etemplate2_1.etemplate2.app_refresh("", pushData.app, pushData.id, pushData.type); - }; /** * Run action via ajax * diff --git a/timesheet/js/app.ts b/timesheet/js/app.ts index cda87f13da..0b2e88f348 100644 --- a/timesheet/js/app.ts +++ b/timesheet/js/app.ts @@ -29,6 +29,10 @@ import {etemplate2} from "../../api/js/etemplate/etemplate2"; class TimesheetApp extends EgwApp { + // These fields help with push filtering & access control to see if we care about a push message + protected push_grant_fields = ["ts_owner"]; + protected push_filter_fields = ["ts_owner"] + constructor() { super('timesheet'); @@ -199,58 +203,6 @@ class TimesheetApp extends EgwApp if(widget) return widget.options.value; } - private _grants : any; - - /** - * 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: 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(pushData) - { - // timesheed does NOT care about other apps data - if (pushData.app !== this.appname) return; - - if (pushData.type === 'delete') - { - return super.push(pushData); - } - - // This must be before all ACL checks, as owner might have changed and entry need to be removed - // (server responds then with null / no entry causing the entry to disapear) - if (pushData.type !== "add" && this.egw.dataHasUID(this.uid(pushData))) - { - return etemplate2.app_refresh("", pushData.app, pushData.id, pushData.type); - } - - // all other cases (add, edit, update) are handled identical - // check visibility - if (typeof this._grants === 'undefined') - { - this._grants = egw.grants(this.appname); - } - if (typeof this._grants[pushData.acl.ts_owner] === 'undefined') return; - - // check if we might not see it because of an owner filter - let nm = this.et2?.getWidgetById('nm'); - let nm_value = nm?.getValue(); - if (nm && nm_value && nm_value.col_filter?.ts_owner && nm_value.col_filter.ts_owner != pushData.acl.ts_owner) - { - return; - } - etemplate2.app_refresh("",pushData.app, pushData.id, pushData.type); - } - /** * Run action via ajax *