mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-26 00:29:38 +01:00
* Calendar: New context menu action to manually [re]send notifications
This commit is contained in:
parent
df2a426e08
commit
8dedc3392f
@ -851,9 +851,10 @@ class calendar_boupdate extends calendar_bo
|
||||
* @param array $new_event =null Event after the change
|
||||
* @param int|string $user =0 User/participant who started the notify, default current user
|
||||
* @param array $alarm =null values for "offset", "start", etc.
|
||||
* @parqm boolean $ignore_prefs Ignore the user's preferences about when they want to be notified and send it
|
||||
* @return bool true/false
|
||||
*/
|
||||
function _send_update($msg_type, $to_notify, $old_event, $new_event=null, $user=0, array $alarm=null)
|
||||
function _send_update($msg_type, $to_notify, $old_event, $new_event=null, $user=0, array $alarm=null, $ignore_prefs = false)
|
||||
{
|
||||
//error_log(__METHOD__."($msg_type,".array2string($to_notify).",...) ".array2string($new_event));
|
||||
if (!is_array($to_notify))
|
||||
@ -1035,7 +1036,7 @@ class calendar_boupdate extends calendar_bo
|
||||
$fullname = $res_info && !empty($res_info['name']) ? $res_info['name'] : $userid;
|
||||
}
|
||||
$m_type = $msg_type;
|
||||
if (!self::update_requested($userid, $part_prefs, $m_type, $old_event, $new_event, $role,
|
||||
if (!$ignore_prefs && !self::update_requested($userid, $part_prefs, $m_type, $old_event, $new_event, $role,
|
||||
$event['participants'][$GLOBALS['egw_info']['user']['account_id']]))
|
||||
{
|
||||
continue;
|
||||
|
@ -42,6 +42,7 @@ class calendar_uiforms extends calendar_ui
|
||||
'cat_acl' => true,
|
||||
'meeting' => true,
|
||||
'mail_import' => true,
|
||||
'notify' => true
|
||||
);
|
||||
|
||||
/**
|
||||
@ -1773,90 +1774,14 @@ class calendar_uiforms extends calendar_ui
|
||||
$content['duration'] = $content['end'] - $content['start'];
|
||||
if (isset($this->durations[$content['duration']])) $content['end'] = '';
|
||||
|
||||
$row = 3;
|
||||
$readonlys = $content['participants'] = $preserv['participants'] = array();
|
||||
// preserve some ui elements, if set eg. under error-conditions
|
||||
foreach(array('quantity','resource','role') as $n)
|
||||
{
|
||||
if (isset($event['participants'][$n])) $content['participants'][$n] = $event['participants'][$n];
|
||||
}
|
||||
foreach($event['participant_types'] as $type => $participants)
|
||||
{
|
||||
$name = 'accounts';
|
||||
if (isset($this->bo->resources[$type]))
|
||||
{
|
||||
$name = $this->bo->resources[$type]['app'];
|
||||
}
|
||||
// sort participants (in there group/app) by title
|
||||
uksort($participants, array($this, 'uid_title_cmp'));
|
||||
foreach($participants as $id => $status)
|
||||
{
|
||||
$uid = $type == 'u' ? $id : $type.$id;
|
||||
$quantity = $role = null;
|
||||
calendar_so::split_status($status,$quantity,$role);
|
||||
$preserv['participants'][$row] = $content['participants'][$row] = array(
|
||||
'app' => $name == 'accounts' ? ($GLOBALS['egw']->accounts->get_type($id) == 'g' ? 'Group' : 'User') : $name,
|
||||
'uid' => $uid,
|
||||
'status' => $status,
|
||||
'old_status' => $status,
|
||||
'quantity' => $quantity > 1 || $uid[0] == 'r' ? $quantity : '', // only display quantity for resources or if > 1
|
||||
'role' => $role,
|
||||
);
|
||||
// replace iCal roles with a nicer label and remove regular REQ-PARTICIPANT
|
||||
if (isset($this->bo->roles[$role]))
|
||||
{
|
||||
$content['participants'][$row]['role_label'] = lang($this->bo->roles[$role]);
|
||||
}
|
||||
// allow third party apps to use categories for roles
|
||||
elseif(substr($role,0,6) == 'X-CAT-')
|
||||
{
|
||||
$content['participants'][$row]['role_label'] = $GLOBALS['egw']->categories->id2name(substr($role,6));
|
||||
}
|
||||
else
|
||||
{
|
||||
$content['participants'][$row]['role_label'] = lang(str_replace('X-','',$role));
|
||||
}
|
||||
$content['participants'][$row]['delete_id'] = strpbrk($uid,'"\'<>') !== false ? md5($uid) : $uid;
|
||||
//echo "<p>$uid ($quantity): $role --> {$content['participants'][$row]['role']}</p>\n";
|
||||
$this->setup_participants($event,$content,$readonlys,$preserv,$view);
|
||||
|
||||
if (($no_status = !$this->bo->check_status_perms($uid,$event)) || $view)
|
||||
$readonlys['participants'][$row]['status'] = $no_status;
|
||||
if ($preserv['hide_delete'] || !$this->bo->check_perms(Acl::EDIT,$event))
|
||||
$readonlys['participants']['delete'][$uid] = true;
|
||||
// todo: make the participants available as links with email as title
|
||||
$content['participants'][$row++]['title'] = $this->get_title($uid);
|
||||
// enumerate group-invitations, so people can accept/reject them
|
||||
if ($name == 'accounts' && $GLOBALS['egw']->accounts->get_type($id) == 'g' &&
|
||||
($members = $GLOBALS['egw']->accounts->members($id,true)))
|
||||
{
|
||||
$sel_options['status']['G'] = lang('Select one');
|
||||
// sort members by title
|
||||
usort($members, array($this, 'uid_title_cmp'));
|
||||
foreach($members as $member)
|
||||
{
|
||||
if (!isset($participants[$member]) && $this->bo->check_perms(Acl::READ,0,$member))
|
||||
{
|
||||
$preserv['participants'][$row] = $content['participants'][$row] = array(
|
||||
'app' => 'Group invitation',
|
||||
'uid' => $member,
|
||||
'status' => 'G',
|
||||
);
|
||||
$readonlys['participants'][$row]['quantity'] = $readonlys['participants']['delete'][$member] = true;
|
||||
// read access is enough to invite participants, but you need edit rights to change status
|
||||
$readonlys['participants'][$row]['status'] = !$this->bo->check_perms(Acl::EDIT,0,$member);
|
||||
$content['participants'][$row++]['title'] = Api\Accounts::username($member);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// resouces / apps we shedule, atm. resources and addressbook
|
||||
$content['participants']['cal_resources'] = '';
|
||||
foreach($this->bo->resources as $data)
|
||||
{
|
||||
if ($data['app'] == 'email') continue; // make no sense, as we cant search for email
|
||||
$content['participants']['cal_resources'] .= ','.$data['app'];
|
||||
}
|
||||
}
|
||||
$content['participants']['status_date'] = $preserv['actual_date'];
|
||||
// set notify_externals in participants from cfs
|
||||
if (!empty($event['##notify_externals']))
|
||||
@ -2045,6 +1970,98 @@ class calendar_uiforms extends calendar_ui
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the participants for display in edit dialog
|
||||
*
|
||||
* @param $event
|
||||
* @param $content
|
||||
* @param $readonlys
|
||||
* @param $preserv
|
||||
* @param $view
|
||||
*/
|
||||
protected function setup_participants($event, &$content, &$readonlys, &$preserv, $view)
|
||||
{
|
||||
$row = 3;
|
||||
foreach($event['participant_types'] as $type => $participants)
|
||||
{
|
||||
$name = 'accounts';
|
||||
if (isset($this->bo->resources[$type]))
|
||||
{
|
||||
$name = $this->bo->resources[$type]['app'];
|
||||
}
|
||||
// sort participants (in there group/app) by title
|
||||
uksort($participants, array($this, 'uid_title_cmp'));
|
||||
foreach($participants as $id => $status)
|
||||
{
|
||||
$uid = $type == 'u' ? $id : $type.$id;
|
||||
$quantity = $role = null;
|
||||
calendar_so::split_status($status,$quantity,$role);
|
||||
$preserv['participants'][$row] = $content['participants'][$row] = array(
|
||||
'app' => $name == 'accounts' ? ($GLOBALS['egw']->accounts->get_type($id) == 'g' ? 'Group' : 'User') : $name,
|
||||
'uid' => $uid,
|
||||
'status' => $status,
|
||||
'old_status' => $status,
|
||||
'quantity' => $quantity > 1 || $uid[0] == 'r' ? $quantity : '', // only display quantity for resources or if > 1
|
||||
'role' => $role,
|
||||
);
|
||||
// replace iCal roles with a nicer label and remove regular REQ-PARTICIPANT
|
||||
if (isset($this->bo->roles[$role]))
|
||||
{
|
||||
$content['participants'][$row]['role_label'] = lang($this->bo->roles[$role]);
|
||||
}
|
||||
// allow third party apps to use categories for roles
|
||||
elseif(substr($role,0,6) == 'X-CAT-')
|
||||
{
|
||||
$content['participants'][$row]['role_label'] = $GLOBALS['egw']->categories->id2name(substr($role,6));
|
||||
}
|
||||
else
|
||||
{
|
||||
$content['participants'][$row]['role_label'] = lang(str_replace('X-','',$role));
|
||||
}
|
||||
$content['participants'][$row]['delete_id'] = strpbrk($uid,'"\'<>') !== false ? md5($uid) : $uid;
|
||||
//echo "<p>$uid ($quantity): $role --> {$content['participants'][$row]['role']}</p>\n";
|
||||
|
||||
if (($no_status = !$this->bo->check_status_perms($uid,$event)) || $view)
|
||||
$readonlys['participants'][$row]['status'] = $no_status;
|
||||
if ($preserv['hide_delete'] || !$this->bo->check_perms(Acl::EDIT,$event))
|
||||
$readonlys['participants']['delete'][$uid] = true;
|
||||
// todo: make the participants available as links with email as title
|
||||
$content['participants'][$row++]['title'] = $this->get_title($uid);
|
||||
// enumerate group-invitations, so people can accept/reject them
|
||||
if ($name == 'accounts' && $GLOBALS['egw']->accounts->get_type($id) == 'g' &&
|
||||
($members = $GLOBALS['egw']->accounts->members($id,true)))
|
||||
{
|
||||
$sel_options['status']['G'] = lang('Select one');
|
||||
// sort members by title
|
||||
usort($members, array($this, 'uid_title_cmp'));
|
||||
foreach($members as $member)
|
||||
{
|
||||
if (!isset($participants[$member]) && $this->bo->check_perms(Acl::READ,0,$member))
|
||||
{
|
||||
$preserv['participants'][$row] = $content['participants'][$row] = array(
|
||||
'app' => 'Group invitation',
|
||||
'uid' => $member,
|
||||
'status' => 'G',
|
||||
);
|
||||
$readonlys['participants'][$row]['quantity'] = $readonlys['participants']['delete'][$member] = true;
|
||||
// read access is enough to invite participants, but you need edit rights to change status
|
||||
$readonlys['participants'][$row]['status'] = !$this->bo->check_perms(Acl::EDIT,0,$member);
|
||||
$content['participants'][$row++]['title'] = Api\Accounts::username($member);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// resouces / apps we shedule, atm. resources and addressbook
|
||||
$content['participants']['cal_resources'] = '';
|
||||
foreach($this->bo->resources as $data)
|
||||
{
|
||||
if ($data['app'] == 'email') continue; // make no sense, as we cant search for email
|
||||
$content['participants']['cal_resources'] .= ','.$data['app'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove (shared) lock via ajax, when edit popup get's closed
|
||||
*
|
||||
@ -3451,4 +3468,35 @@ class calendar_uiforms extends calendar_ui
|
||||
|
||||
return $this->process_edit($event);
|
||||
}
|
||||
|
||||
public function notify($content=array())
|
||||
{
|
||||
if(is_array($content) && $content['button'])
|
||||
{
|
||||
$participants = array_filter($content['participants']['notify']);
|
||||
$this->bo->_send_update(MSG_ALARM,$participants,$content,null,0,null,true);
|
||||
Framework::window_close();
|
||||
}
|
||||
|
||||
list($id, $date) = explode(':',$_GET['id']);
|
||||
$content = array();
|
||||
$event = $this->bo->read($id, $date);
|
||||
$this->setup_participants($event, $content, $readonlys,$preserve,true);
|
||||
$content = array_merge($event, $content);
|
||||
|
||||
$sel_options = array(
|
||||
'recur_type' => &$this->bo->recur_types,
|
||||
'status' => $this->bo->verbose_status,
|
||||
'duration' => $this->durations,
|
||||
'role' => $this->bo->roles
|
||||
);
|
||||
$readonlys = [];
|
||||
|
||||
|
||||
|
||||
$etpl = new Etemplate('calendar.notify_dialog');
|
||||
$preserve = $content;
|
||||
|
||||
$etpl->exec('calendar.calendar_uiforms.notify', $content, $sel_options, $readonlys, $preserve,2);
|
||||
}
|
||||
}
|
||||
|
@ -963,6 +963,15 @@ class calendar_uilist extends calendar_ui
|
||||
'hint' => 'Do not notify of these changes',
|
||||
'group' => $group,
|
||||
),
|
||||
'notifications' => array(
|
||||
'caption' => 'Send notifications',
|
||||
'hint' => 'Send notifications to users right now',
|
||||
'url' => 'calendar.calendar_uiforms.notify&id=$app_id',
|
||||
'popup' => Link::get_registry('calendar', 'view_popup'),
|
||||
'allowOnMultiple' => false,
|
||||
'group' => $group,
|
||||
'disableClass' => 'rowNoView',
|
||||
)
|
||||
);
|
||||
$status = array_map('lang',$this->bo->verbose_status);
|
||||
unset($status['G']);
|
||||
|
@ -911,6 +911,7 @@ class calendar_uiviews extends calendar_ui
|
||||
$actions['copy']['onExecute'] = 'javaScript:app.calendar.action_open';
|
||||
$actions['print']['open'] = '{"app": "calendar", "type": "edit", "extra": "cal_id=$id&print=1"}';
|
||||
$actions['print']['onExecute'] = 'javaScript:app.calendar.action_open';
|
||||
$actions['notifications']['onExecute'] = 'javaScript:app.calendar.action_open';
|
||||
|
||||
foreach($actions['status']['children'] as $id => &$status)
|
||||
{
|
||||
|
@ -13,6 +13,7 @@ var __extends = (this && this.__extends) || (function () {
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.listview = exports.planner = exports.month = exports.weekN = exports.week = exports.day4 = exports.day = exports.View = void 0;
|
||||
var View = /** @class */ (function () {
|
||||
function View() {
|
||||
}
|
||||
|
@ -1650,9 +1650,10 @@ var CalendarApp = /** @class */ (function (_super) {
|
||||
}
|
||||
else if (_action.data.url) {
|
||||
var url = _action.data.url;
|
||||
url = url.replace(/(\$|%24)app/, app).replace(/(\$|%24)app_id/, app_id)
|
||||
url = url.replace(/(\$|%24)app_id/, app_id)
|
||||
.replace(/(\$|%24)app/, app)
|
||||
.replace(/(\$|%24)id/, id);
|
||||
this.egw.open_link(url);
|
||||
this.egw.open_link(url, _action.data.target, _action.data.popup);
|
||||
}
|
||||
};
|
||||
/**
|
||||
@ -3572,8 +3573,7 @@ var CalendarApp = /** @class */ (function (_super) {
|
||||
// Dates are user time, but we told javascript it was UTC.
|
||||
// Send just the timestamp (as a string) with no timezone
|
||||
(typeof _data.start != "string" ? _data.start.toJSON() : _data.start).slice(0, -1),
|
||||
(typeof _data.end != "string" ? _data.end.toJSON() : _data.end).slice(0, -1),
|
||||
{
|
||||
(typeof _data.end != "string" ? _data.end.toJSON() : _data.end).slice(0, -1), {
|
||||
participants: Object.keys(_data.participants).filter(function (v) { return v.match(/^[0-9]/); })
|
||||
}], function (_value) {
|
||||
if (_value) {
|
||||
|
@ -1795,9 +1795,10 @@ class CalendarApp extends EgwApp
|
||||
else if (_action.data.url)
|
||||
{
|
||||
var url = _action.data.url;
|
||||
url = url.replace(/(\$|%24)app/,app).replace(/(\$|%24)app_id/,app_id)
|
||||
.replace(/(\$|%24)id/,id);
|
||||
this.egw.open_link(url);
|
||||
url = url.replace(/(\$|%24)app_id/,app_id)
|
||||
.replace(/(\$|%24)app/,app)
|
||||
.replace(/(\$|%24)id/,id);
|
||||
this.egw.open_link(url, _action.data.target, _action.data.popup);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ var __extends = (this && this.__extends) || (function () {
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.et2_calendar_event = void 0;
|
||||
/*egw:uses
|
||||
/etemplate/js/et2_core_valueWidget;
|
||||
*/
|
||||
@ -722,7 +723,6 @@ var et2_calendar_event = /** @class */ (function (_super) {
|
||||
* @private
|
||||
*/
|
||||
et2_calendar_event.prototype._status_check = function (event, filter, owner) {
|
||||
var _a;
|
||||
if (!owner || !event) {
|
||||
return false;
|
||||
}
|
||||
@ -756,7 +756,7 @@ var et2_calendar_event = /** @class */ (function (_super) {
|
||||
return element.id == owner;
|
||||
}) || {};
|
||||
var matching_participant = typeof resource.resources == "undefined" ?
|
||||
resource : (_a = resource) === null || _a === void 0 ? void 0 : _a.resources.filter(function (id) { return typeof event.participants[id] != "undefined"; });
|
||||
resource : resource === null || resource === void 0 ? void 0 : resource.resources.filter(function (id) { return typeof event.participants[id] != "undefined"; });
|
||||
if (matching_participant.length > 0) {
|
||||
return this._status_check(event, filter, matching_participant);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ var __extends = (this && this.__extends) || (function () {
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.et2_calendar_owner = void 0;
|
||||
/*egw:uses
|
||||
et2_widget_taglist;
|
||||
*/
|
||||
|
@ -22,6 +22,7 @@ var __extends = (this && this.__extends) || (function () {
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.et2_calendar_planner = void 0;
|
||||
/*egw:uses
|
||||
/calendar/js/et2_widget_view.js;
|
||||
/calendar/js/et2_widget_planner_row.js;
|
||||
|
@ -22,6 +22,7 @@ var __extends = (this && this.__extends) || (function () {
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.et2_calendar_planner_row = void 0;
|
||||
/*egw:uses
|
||||
/calendar/js/et2_widget_view.js;
|
||||
/calendar/js/et2_widget_daycol.js;
|
||||
@ -372,7 +373,7 @@ var et2_calendar_planner_row = /** @class */ (function (_super) {
|
||||
get: function () {
|
||||
return this._date_helper;
|
||||
},
|
||||
enumerable: true,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
/**
|
||||
|
@ -22,6 +22,7 @@ var __extends = (this && this.__extends) || (function () {
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.et2_calendar_timegrid = void 0;
|
||||
/*egw:uses
|
||||
/calendar/js/et2_widget_view.js;
|
||||
*/
|
||||
|
@ -22,6 +22,7 @@ var __extends = (this && this.__extends) || (function () {
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.et2_calendar_view = void 0;
|
||||
var et2_core_valueWidget_1 = require("../../api/js/etemplate/et2_core_valueWidget");
|
||||
var et2_core_inheritance_1 = require("../../api/js/etemplate/et2_core_inheritance");
|
||||
/**
|
||||
@ -278,7 +279,7 @@ var et2_calendar_view = /** @class */ (function (_super) {
|
||||
get: function () {
|
||||
return this._date_helper;
|
||||
},
|
||||
enumerable: true,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
et2_calendar_view.prototype._createNamespace = function () {
|
||||
|
65
calendar/templates/default/notify_dialog.xet
Normal file
65
calendar/templates/default/notify_dialog.xet
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE overlay PUBLIC "-//EGroupware GmbH//eTemplate 2//EN" "http://www.egroupware.org/etemplate2.dtd">
|
||||
<overlay>
|
||||
<template id="calendar.notify_dialog" template="" lang="" group="0" version="1.9.001">
|
||||
<grid width="100%">
|
||||
<columns>
|
||||
<column width="88"/>
|
||||
<column width="130"/>
|
||||
<column width="88"/>
|
||||
<column width="130"/>
|
||||
<column width="130"/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row class="dialogHeader" height="28">
|
||||
<description id="title" class="et2_fullWidth" span="4"/>
|
||||
<hbox>
|
||||
<description font_style="n" id="id"/>
|
||||
<appicon/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row class="dialogHeader2" height="28" >
|
||||
<description for="start" value="Start" width="88"/>
|
||||
<date-time id="start" readonly="true"/>
|
||||
<description for="duration" value="Duration" id="calendar_edit_duration" />
|
||||
<select statustext="Duration of the meeting" class="et2_fullWidth" id="duration" no_lang="1" readonly="true" options="Use end date,,,,,,,false"/>
|
||||
<date-time id="end" readonly="true"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<grid width="100%" id="participants">
|
||||
<columns>
|
||||
<column width="85"/>
|
||||
<column width="350"/>
|
||||
<column width="70"/>
|
||||
<column width="100"/>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row></row>
|
||||
<row></row>
|
||||
<row class="th thb">
|
||||
<description value="Type"/>
|
||||
<description value="Participants"/>
|
||||
<description value="Role"/>
|
||||
<description value="Status"/>
|
||||
<description/>
|
||||
</row>
|
||||
<row valign="top">
|
||||
<description id="${row}[app]"/>
|
||||
<description id="${row}[title]" no_lang="1"/>
|
||||
<description id="${row}[role_label]"/>
|
||||
<select id="${row}[status]" readonly="true"/>
|
||||
<checkbox align="center" label="Notify" id="notify[$row_cont[delete_id]]"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<hbox class="dialogFooterToolbar">
|
||||
<button label="OK" id="button[save]" image="mail" background_image="1"/>
|
||||
<button statustext="Close the window" label="Cancel" id="button[cancel]" onclick="window.close();" image="cancel" background_image="1"/>
|
||||
</hbox>
|
||||
<styles>
|
||||
.selectRole select { width: 100%; }
|
||||
</styles>
|
||||
</template>
|
||||
</overlay>
|
Loading…
Reference in New Issue
Block a user