mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-27 09:09:04 +01:00
df5f5a0ff4
- Resources could cause error for some owner checks
1175 lines
55 KiB
JavaScript
1175 lines
55 KiB
JavaScript
"use strict";
|
|
/*
|
|
* Egroupware Calendar event widget
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
* @package etemplate
|
|
* @subpackage api
|
|
* @link http://www.egroupware.org
|
|
* @author Nathan Gray
|
|
* @version $Id$
|
|
*/
|
|
var __extends = (this && this.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
/*egw:uses
|
|
/etemplate/js/et2_core_valueWidget;
|
|
*/
|
|
var et2_core_widget_1 = require("../../api/js/etemplate/et2_core_widget");
|
|
var et2_core_valueWidget_1 = require("../../api/js/etemplate/et2_core_valueWidget");
|
|
var et2_core_inheritance_1 = require("../../api/js/etemplate/et2_core_inheritance");
|
|
var et2_core_DOMWidget_1 = require("../../api/js/etemplate/et2_core_DOMWidget");
|
|
var et2_widget_daycol_1 = require("./et2_widget_daycol");
|
|
var et2_widget_planner_row_1 = require("./et2_widget_planner_row");
|
|
/**
|
|
* Class for a single event, displayed in either the timegrid or planner view
|
|
*
|
|
* It is possible to directly provide all information directly, but calendar
|
|
* uses egw.data for caching, so ID is all that is needed.
|
|
*
|
|
* Note that there are several pieces of information that have 'ID' in them:
|
|
* - row_id - used by both et2_calendar_event and the nextmatch to uniquely
|
|
* identify a particular entry or entry ocurrence
|
|
* - id - Recurring events may have their recurrence as a timestamp after their ID,
|
|
* such as '194:1453318200', or not. It's usually (always?) the same as row ID.
|
|
* - app_id - the ID according to the source application. For calendar, this
|
|
* is the same as ID (but always with the recurrence), for other apps this is
|
|
* usually just an integer. With app_id and app, you should be able to call
|
|
* egw.open() and get the specific entry.
|
|
* - Events from other apps will have their app name prepended to their ID, such
|
|
* as 'infolog123', so app_id and id will be different for these events
|
|
* - Cache ID is the same as other apps, and looks like 'calendar::<row_id>'
|
|
* - The DOM ID for the containing div is event_<row_id>
|
|
*
|
|
* Events are expected to be added to either et2_calendar_daycol or
|
|
* et2_calendar_planner_row rather than either et2_calendar_timegrid or
|
|
* et2_calendar_planner directly.
|
|
*
|
|
*/
|
|
var et2_calendar_event = /** @class */ (function (_super) {
|
|
__extends(et2_calendar_event, _super);
|
|
/**
|
|
* Constructor
|
|
*/
|
|
function et2_calendar_event(_parent, _attrs, _child) {
|
|
var _this =
|
|
// Call the inherited constructor
|
|
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_calendar_event._attributes, _child || {})) || this;
|
|
_this._need_actions_linked = false;
|
|
var event = _this;
|
|
// Main container
|
|
_this.div = jQuery(document.createElement("div"))
|
|
.addClass("calendar_calEvent")
|
|
.addClass(_this.options.class)
|
|
.css('width', _this.options.width)
|
|
.on('mouseenter', function () {
|
|
// Bind actions on first mouseover for faster creation
|
|
if (event._need_actions_linked) {
|
|
event._copy_parent_actions();
|
|
}
|
|
// Tooltip
|
|
if (!event._tooltipElem) {
|
|
event.options.statustext_html = true;
|
|
event.set_statustext(event._tooltip());
|
|
if (event.statustext) {
|
|
return event.div.trigger('mouseenter');
|
|
}
|
|
}
|
|
// Hacky to remove egw's tooltip border and let the mouse in
|
|
window.setTimeout(function () {
|
|
jQuery('body .egw_tooltip')
|
|
.css('border', 'none')
|
|
.on('mouseenter', function () {
|
|
event.div.off('mouseleave.tooltip');
|
|
jQuery('body.egw_tooltip').remove();
|
|
jQuery('body').append(this);
|
|
jQuery(this).stop(true).fadeTo(400, 1)
|
|
.on('mouseleave', function () {
|
|
jQuery(this).fadeOut('400', function () {
|
|
jQuery(this).remove();
|
|
// Set up to work again
|
|
event.set_statustext(event._tooltip());
|
|
});
|
|
});
|
|
});
|
|
}, 105);
|
|
});
|
|
_this.title = jQuery(document.createElement('div'))
|
|
.addClass("calendar_calEventHeader")
|
|
.appendTo(_this.div);
|
|
_this.body = jQuery(document.createElement('div'))
|
|
.addClass("calendar_calEventBody")
|
|
.appendTo(_this.div);
|
|
_this.icons = jQuery(document.createElement('div'))
|
|
.addClass("calendar_calEventIcons")
|
|
.appendTo(_this.title);
|
|
_this.setDOMNode(_this.div[0]);
|
|
return _this;
|
|
}
|
|
et2_calendar_event.prototype.doLoadingFinished = function () {
|
|
_super.prototype.doLoadingFinished.call(this);
|
|
// Already know what is needed to hook to cache
|
|
if (this.options.value && this.options.value.row_id) {
|
|
egw.dataRegisterUID('calendar::' + this.options.value.row_id, this._UID_callback, this, this.getInstanceManager().execId, this.id);
|
|
}
|
|
return true;
|
|
};
|
|
et2_calendar_event.prototype.destroy = function () {
|
|
_super.prototype.destroy.call(this);
|
|
if (this._actionObject) {
|
|
this._actionObject.remove();
|
|
this._actionObject = null;
|
|
}
|
|
this.div.off();
|
|
this.title.remove();
|
|
this.title = null;
|
|
this.body.remove();
|
|
this.body = null;
|
|
this.icons = null;
|
|
this.div.remove();
|
|
this.div = null;
|
|
jQuery('body.egw_tooltip').remove();
|
|
// Unregister, or we'll continue to be notified...
|
|
if (this.options.value) {
|
|
var old_app_id = this.options.value.row_id;
|
|
egw.dataUnregisterUID('calendar::' + old_app_id, null, this);
|
|
}
|
|
};
|
|
et2_calendar_event.prototype.set_value = function (_value) {
|
|
// Un-register for updates
|
|
if (this.options.value) {
|
|
var old_id = this.options.value.row_id;
|
|
if (!_value || !_value.row_id || old_id !== _value.row_id) {
|
|
egw.dataUnregisterUID('calendar::' + old_id, null, this);
|
|
}
|
|
}
|
|
this.options.value = _value;
|
|
// Register for updates
|
|
var id = this.options.value.row_id;
|
|
if (!old_id || old_id !== id) {
|
|
egw.dataRegisterUID('calendar::' + id, this._UID_callback, this, this.getInstanceManager().execId, this.id);
|
|
}
|
|
if (_value && !egw.dataHasUID('calendar::' + id)) {
|
|
egw.dataStoreUID('calendar::' + id, _value);
|
|
}
|
|
};
|
|
/**
|
|
* Callback for changes in cached data
|
|
*/
|
|
et2_calendar_event.prototype._UID_callback = function (event) {
|
|
// Copy to avoid changes, which may cause nm problems
|
|
var value = event === null ? null : jQuery.extend({}, event);
|
|
var parent = this.getParent();
|
|
var parent_owner = parent.getDOMNode(parent).dataset['owner'] || parent.getParent().options.owner;
|
|
if (parent_owner.indexOf(',')) {
|
|
parent_owner = parent_owner.split(',');
|
|
}
|
|
// Make sure id is a string, check values
|
|
if (value) {
|
|
this._values_check(value);
|
|
}
|
|
// Check for changing days in the grid view
|
|
if (!this._sameday_check(value) || !this._status_check(value, app.calendar.getState().status_filter, parent_owner)) {
|
|
// May need to update parent to remove out-of-view events
|
|
parent.removeChild(this);
|
|
if (event === null && parent && parent.instanceOf(et2_widget_daycol_1.et2_calendar_daycol)) {
|
|
parent._out_of_view();
|
|
}
|
|
// This should now cease to exist, as new events have been created
|
|
this.destroy();
|
|
return;
|
|
}
|
|
// Copy to avoid changes, which may cause nm problems
|
|
this.options.value = jQuery.extend({}, value);
|
|
if (this.getParent().options.date) {
|
|
this.options.value.date = this.getParent().options.date;
|
|
}
|
|
// Let parent position - could also be et2_calendar_planner_row
|
|
this.getParent().position_event(this);
|
|
// Parent may remove this if the date isn't the same
|
|
if (this.getParent()) {
|
|
this._update();
|
|
}
|
|
};
|
|
/**
|
|
* Draw the event
|
|
*/
|
|
et2_calendar_event.prototype._update = function () {
|
|
// Update to reflect new information
|
|
var event = this.options.value;
|
|
var id = event.row_id ? event.row_id : event.id + (event.recur_type ? ':' + event.recur_date : '');
|
|
var formatted_start = event.start.toJSON();
|
|
this.set_id('event_' + id);
|
|
if (this._actionObject) {
|
|
this._actionObject.id = 'calendar::' + id;
|
|
}
|
|
this._need_actions_linked = !this.options.readonly;
|
|
// Make sure category stuff is there
|
|
// Fake it to use the cache / call - if already there, these will return
|
|
// immediately.
|
|
var im = this.getInstanceManager();
|
|
et2_selectbox.cat_options({
|
|
_type: 'select-cat',
|
|
getInstanceManager: function () { return im; }
|
|
}, { application: event.app || 'calendar' });
|
|
// Need cleaning? (DnD helper removes content)
|
|
// @ts-ignore
|
|
if (!this.div.has(this.title).length) {
|
|
this.div
|
|
.empty()
|
|
.append(this.title)
|
|
.append(this.body);
|
|
}
|
|
if (!this.getParent().options.readonly && !this.options.readonly && this.div.droppable('instance')) {
|
|
this.div
|
|
// Let timegrid always get the drag
|
|
.droppable('option', 'greedy', false);
|
|
}
|
|
// DOM nodes
|
|
this.div
|
|
// Set full day flag
|
|
.attr('data-full_day', event.whole_day)
|
|
// Put everything we need for basic interaction here, so it's available immediately
|
|
.attr('data-id', event.id)
|
|
.attr('data-app', event.app || 'calendar')
|
|
.attr('data-app_id', event.app_id)
|
|
.attr('data-start', formatted_start)
|
|
.attr('data-owner', event.owner)
|
|
.attr('data-recur_type', event.recur_type)
|
|
.attr('data-resize', event.whole_day ? 'WD' : '' + (event.recur_type ? 'S' : ''))
|
|
.attr('data-priority', event.priority)
|
|
// Remove any category classes
|
|
.removeClass(function (index, css) {
|
|
return (css.match(/(^|\s)cat_\S+/g) || []).join(' ');
|
|
})
|
|
// Remove any status classes
|
|
.removeClass(function (index, css) {
|
|
return (css.match(/calendar_calEvent\S+/g) || []).join(' ');
|
|
})
|
|
.removeClass('calendar_calEventSmall')
|
|
.addClass(event.class)
|
|
.toggleClass('calendar_calEventPrivate', typeof event.private !== 'undefined' && event.private);
|
|
this.options.class = event.class;
|
|
var status_class = this._status_class();
|
|
// Add category classes, if real categories are set
|
|
if (event.category && event.category != '0') {
|
|
var cats = event.category.split(',');
|
|
for (var i = 0; i < cats.length; i++) {
|
|
this.div.addClass('cat_' + cats[i]);
|
|
}
|
|
}
|
|
this.div.toggleClass('calendar_calEventUnknown', event.participants[egw.user('account_id')] ? event.participants[egw.user('account_id')][0] === 'U' : false);
|
|
this.div.addClass(status_class);
|
|
this.body.toggleClass('calendar_calEventBodySmall', event.whole_day_on_top || false);
|
|
// Header
|
|
var title = !event.is_private ? egw.htmlspecialchars(event['title']) : egw.lang('private');
|
|
this.title
|
|
.html('<span class="calendar_calTimespan">' + this._get_timespan(event) + '<br /></span>')
|
|
.append('<span class="calendar_calEventTitle">' + title + '</span>');
|
|
// Colors - don't make them transparent if there is no color
|
|
// @ts-ignore
|
|
if (jQuery.Color("rgba(0,0,0,0)").toRgbaString() != jQuery.Color(this.div, 'background-color').toRgbaString()) {
|
|
// Most statuses use colored borders
|
|
this.div.css('border-color', this.div.css('background-color'));
|
|
}
|
|
this.icons.appendTo(this.title)
|
|
.html(this._icons().join(''));
|
|
// Body
|
|
if (event.whole_day_on_top) {
|
|
this.body.html(title);
|
|
}
|
|
else {
|
|
// @ts-ignore
|
|
var start_time = jQuery.datepicker.formatTime(egw.preference("timeformat") === "12" ? "h:mmtt" : "HH:mm", {
|
|
hour: event.start_m / 60,
|
|
minute: event.start_m % 60,
|
|
seconds: 0,
|
|
timezone: 0
|
|
}, { "ampm": (egw.preference("timeformat") === "12") }).trim();
|
|
this.body
|
|
.html('<span class="calendar_calEventTitle">' + title + '</span>')
|
|
.append('<span class="calendar_calTimespan">' + start_time + '</span>');
|
|
if (this.options.value.description.trim()) {
|
|
this.body
|
|
.append('<p>' + egw.htmlspecialchars(this.options.value.description) + '</p>');
|
|
}
|
|
}
|
|
// Clear tooltip for regeneration
|
|
this.set_statustext('');
|
|
// Height specific section
|
|
// This can take an unreasonable amount of time if parent is hidden
|
|
if (jQuery(this.getParent().getDOMNode(this)).is(':visible')) {
|
|
this._small_size();
|
|
}
|
|
};
|
|
/**
|
|
* Calculate display variants for when event is too short for full display
|
|
*
|
|
* Display is based on the number of visible lines, calculated off the header
|
|
* height:
|
|
* 1 - show just the event title, with ellipsis
|
|
* 2 - Show timespan and title, with ellipsis
|
|
* > 4 - Show description as well, truncated to fit
|
|
*/
|
|
et2_calendar_event.prototype._small_size = function () {
|
|
if (this.options.value.whole_day_on_top)
|
|
return;
|
|
// Skip for planner view, it's always small
|
|
if (this.getParent() && this.getParent().instanceOf(et2_widget_planner_row_1.et2_calendar_planner_row))
|
|
return;
|
|
// Pre-calculation reset
|
|
this.div.removeClass('calendar_calEventSmall');
|
|
this.body.css('height', 'auto');
|
|
var line_height = parseFloat(this.div.css('line-height'));
|
|
var visible_lines = Math.floor(this.div.innerHeight() / line_height);
|
|
if (!this.title.height()) {
|
|
// Handle sizing while hidden, such as when calendar is not the active tab
|
|
visible_lines = Math.floor(egw.getHiddenDimensions(this.div).h / egw.getHiddenDimensions(this.title).h);
|
|
}
|
|
visible_lines = Math.max(1, visible_lines);
|
|
if (this.getParent() && this.getParent().instanceOf(et2_widget_daycol_1.et2_calendar_daycol)) {
|
|
this.div.toggleClass('calendar_calEventSmall', visible_lines < 4);
|
|
this.div
|
|
.attr('data-visible_lines', visible_lines);
|
|
}
|
|
else if (this.getParent() && this.getParent().instanceOf(et2_widget_planner_row_1.et2_calendar_planner_row)) {
|
|
// Less than 8 hours is small
|
|
this.div.toggleClass('calendar_calEventSmall', this.options.value.end.valueOf() - this.options.value.start.valueOf() < 28800000);
|
|
}
|
|
if (this.body.height() > this.div.height() - this.title.height() && visible_lines >= 4) {
|
|
this.body.css('height', Math.floor((visible_lines - 1) * line_height - this.title.height()) + 'px');
|
|
}
|
|
else {
|
|
this.body.css('height', '');
|
|
}
|
|
};
|
|
/**
|
|
* Examines the participants & returns CSS classname for status
|
|
*
|
|
* @returns {String}
|
|
*/
|
|
et2_calendar_event.prototype._status_class = function () {
|
|
var status_class = 'calendar_calEventAllAccepted';
|
|
for (var id in this.options.value.participants) {
|
|
var status_1 = this.options.value.participants[id];
|
|
status_1 = et2_calendar_event.split_status(status_1);
|
|
switch (status_1) {
|
|
case 'A':
|
|
case '': // app without status
|
|
break;
|
|
case 'U':
|
|
status_class = 'calendar_calEventSomeUnknown';
|
|
return status_class; // break for
|
|
default:
|
|
status_class = 'calendar_calEventAllAnswered';
|
|
break;
|
|
}
|
|
}
|
|
return status_class;
|
|
};
|
|
/**
|
|
* Create tooltip shown on hover
|
|
*
|
|
* @return {String}
|
|
*/
|
|
et2_calendar_event.prototype._tooltip = function () {
|
|
if (!this.div || !this.options.value || !this.options.value.app_id)
|
|
return '';
|
|
var border = this.div.css('borderTopColor');
|
|
var bg_color = this.div.css('background-color');
|
|
var header_color = this.title.css('color');
|
|
var timespan = this._get_timespan(this.options.value);
|
|
var parent = this.getParent() instanceof et2_widget_daycol_1.et2_calendar_daycol ? this.getParent() : this.getParent();
|
|
parent.date_helper.set_value(this.options.value.start.valueOf ? new Date(this.options.value.start) : this.options.value.start);
|
|
var start = parent.date_helper.input_date.val();
|
|
parent.date_helper.set_value(this.options.value.end.valueOf ? new Date(this.options.value.end) : this.options.value.end);
|
|
var end = parent.date_helper.input_date.val();
|
|
var times = !this.options.value.multiday ?
|
|
'<span class="calendar_calEventLabel">' + this.egw().lang('Time') + '</span>:' + timespan :
|
|
'<span class="calendar_calEventLabel">' + this.egw().lang('Start') + '</span>:' + start + ' ' +
|
|
'<span class="calendar_calEventLabel">' + this.egw().lang('End') + '</span>:' + end;
|
|
var cat_label = '';
|
|
if (this.options.value.category) {
|
|
var cat = et2_createWidget('select-cat', { 'readonly': true }, this);
|
|
cat.set_value(this.options.value.category);
|
|
var cat_label_1 = this.options.value.category.indexOf(',') <= 0 ? cat.span.text() : [];
|
|
if (typeof cat_label_1 != 'string') {
|
|
cat.span.children().each(function () {
|
|
cat_label_1.push(jQuery(this).text());
|
|
});
|
|
cat_label_1 = cat_label_1.join(', ');
|
|
}
|
|
cat.destroy();
|
|
}
|
|
// Location + Videoconference
|
|
var location = '';
|
|
if (this.options.value.location || this.options.value['##videoconference']) {
|
|
location += '<p><span class="calendar_calEventLabel">' + this.egw().lang('Location') + '</span>:' +
|
|
egw.htmlspecialchars(this.options.value.location);
|
|
if (this.options.value['##videoconference']) {
|
|
// Click handler is set in _bind_videoconference()
|
|
location += (this.options.value.location.trim() ? '<br />' : '') +
|
|
'<span data-videoconference="' + this.options.value['##videoconference'] + '">' + this.egw().lang('Videoconference') +
|
|
'<img src="' + this.egw().image('videoconference', 'calendar') + '"/></span>';
|
|
this._bind_videoconference();
|
|
}
|
|
location += '</p>';
|
|
}
|
|
// Participants
|
|
var participants = '';
|
|
if (this.options.value.participant_types['']) {
|
|
participants += this.options.value.participant_types[''].join("<br />");
|
|
}
|
|
for (var type_name in this.options.value.participant_types) {
|
|
if (type_name) {
|
|
participants += '</p><p><span class="calendar_calEventLabel">' + type_name + '</span>:<br />';
|
|
participants += this.options.value.participant_types[type_name].join("<br />");
|
|
}
|
|
}
|
|
return '<div class="calendar_calEventTooltip ' + this._status_class() + ' ' + this.options.class +
|
|
'" style="border-color: ' + border + '; background-color: ' + bg_color + ';">' +
|
|
'<div class="calendar_calEventHeaderSmall">' +
|
|
'<font style="color:' + header_color + '">' + timespan + '</font>' +
|
|
this.icons[0].outerHTML +
|
|
'</div>' +
|
|
'<div class="calendar_calEventBody">' +
|
|
'<p style="margin: 0px;">' +
|
|
'<span class="calendar_calEventTitle">' + egw.htmlspecialchars(this.options.value.title) + '</span><br>' +
|
|
egw.htmlspecialchars(this.options.value.description) + '</p>' +
|
|
'<p style="margin: 2px 0px;">' + times + '</p>' +
|
|
location +
|
|
(cat_label ? '<p><span class="calendar_calEventLabel">' + this.egw().lang('Category') + '</span>:' + cat_label + '</p>' : '') +
|
|
'<p><span class="calendar_calEventLabel">' + this.egw().lang('Participants') + '</span>:<br />' +
|
|
participants + '</p>' + this._participant_summary(this.options.value.participants) +
|
|
'</div>' +
|
|
'</div>';
|
|
};
|
|
/**
|
|
* Generate participant summary line
|
|
*
|
|
* @returns {String}
|
|
*/
|
|
et2_calendar_event.prototype._participant_summary = function (participants) {
|
|
if (Object.keys(this.options.value.participants).length < 2) {
|
|
return '';
|
|
}
|
|
var participant_status = { A: 0, R: 0, T: 0, U: 0, D: 0 };
|
|
var status_label = { A: 'accepted', R: 'rejected', T: 'tentative', U: 'unknown', D: 'delegated' };
|
|
var participant_summary = Object.keys(this.options.value.participants).length + ' ' + this.egw().lang('Participants') + ': ';
|
|
var status_totals = [];
|
|
for (var id in this.options.value.participants) {
|
|
var status = this.options.value.participants[id].substr(0, 1);
|
|
participant_status[status]++;
|
|
}
|
|
for (var status_2 in participant_status) {
|
|
if (participant_status[status_2] > 0) {
|
|
status_totals.push(participant_status[status_2] + ' ' + this.egw().lang(status_label[status_2]));
|
|
}
|
|
}
|
|
return participant_summary + status_totals.join(', ');
|
|
};
|
|
/**
|
|
* Get actual icons from list
|
|
*/
|
|
et2_calendar_event.prototype._icons = function () {
|
|
var icons = [];
|
|
if (this.options.value.is_private) {
|
|
// Hide everything
|
|
icons.push('<img src="' + this.egw().image('private', 'calendar') + '"/>');
|
|
}
|
|
else {
|
|
if (this.options.value.app !== 'calendar') {
|
|
icons.push('<img src="' + this.egw().image('navbar', this.options.value.app) + '" title="' + this.egw().lang(this.options.value.app) + '"/>');
|
|
}
|
|
if (this.options.value.priority == 3) {
|
|
icons.push('<img src="' + this.egw().image('high', 'calendar') + '" title="' + this.egw().lang('high priority') + '"/>');
|
|
}
|
|
if (this.options.value.public == '0') {
|
|
// Show private flag
|
|
icons.push('<img src="' + this.egw().image('private', 'calendar') + '"/>');
|
|
}
|
|
if (this.options.value['recur_type']) {
|
|
icons.push('<img src="' + this.egw().image('recur', 'calendar') + '" title="' + this.egw().lang('recurring event') + '"/>');
|
|
}
|
|
// icons for single user, multiple users or group(s) and resources
|
|
var single = '<img src="' + this.egw().image('single', 'calendar') + '" title="' + '"/>';
|
|
var multiple = '<img src="' + this.egw().image('users', 'calendar') + '" title="' + '"/>';
|
|
for (var uid in this.options.value['participants']) {
|
|
// @ts-ignore
|
|
if (Object.keys(this.options.value.participants).length == 1 && !isNaN(uid)) {
|
|
icons.push(single);
|
|
break;
|
|
}
|
|
// @ts-ignore
|
|
if (!isNaN(uid) && icons.indexOf(multiple) === -1) {
|
|
icons.push(multiple);
|
|
}
|
|
/*
|
|
* TODO: resource icons
|
|
elseif(!isset($icons[$uid[0]]) && isset($this->bo->resources[$uid[0]]) && isset($this->bo->resources[$uid[0]]['icon']))
|
|
{
|
|
$icons[$uid[0]] = html::image($this->bo->resources[$uid[0]]['app'],
|
|
($this->bo->resources[$uid[0]]['icon'] ? $this->bo->resources[$uid[0]]['icon'] : 'navbar'),
|
|
lang($this->bo->resources[$uid[0]]['app']),
|
|
'width="16px" height="16px"');
|
|
}
|
|
*/
|
|
}
|
|
if (this.options.value.alarm && !jQuery.isEmptyObject(this.options.value.alarm) && !this.options.value.is_private) {
|
|
icons.push('<img src="' + this.egw().image('alarm', 'calendar') + '" title="' + this.egw().lang('alarm') + '"/>');
|
|
}
|
|
if (this.options.value.participants[egw.user('account_id')] && this.options.value.participants[egw.user('account_id')][0] == 'U') {
|
|
icons.push('<img src="' + this.egw().image('needs-action', 'calendar') + '" title="' + this.egw().lang('Needs action') + '"/>');
|
|
}
|
|
if (this.options.value["##videoconference"]) {
|
|
icons.push('<img src="' + this.egw().image('videoconference', 'calendar') + '" title="' + this.egw().lang('videoconference') + '"/>');
|
|
}
|
|
}
|
|
// Always include non-blocking, regardless of privacy
|
|
if (this.options.value.non_blocking) {
|
|
icons.push('<img src="' + this.egw().image('nonblocking', 'calendar') + '" title="' + this.egw().lang('non blocking') + '"/>');
|
|
}
|
|
return icons;
|
|
};
|
|
/**
|
|
* Bind the click handler for opening the video conference
|
|
*
|
|
* Tooltips are placed in the DOM directly in the body, managed by egw.
|
|
*/
|
|
et2_calendar_event.prototype._bind_videoconference = function () {
|
|
var vc_event = 'click.calendar_videoconference';
|
|
jQuery('body').off(vc_event)
|
|
.on(vc_event, '[data-videoconference]', function (event) {
|
|
app.calendar.joinVideoConference(this.dataset.videoconference);
|
|
});
|
|
};
|
|
/**
|
|
* Get a text representation of the timespan of the event. Either start
|
|
* - end, or 'all day'
|
|
*
|
|
* @param {Object} event Event to get the timespan for
|
|
* @param {number} event.start_m Event start, in minutes from midnight
|
|
* @param {number} event.end_m Event end, in minutes from midnight
|
|
*
|
|
* @return {string} Timespan
|
|
*/
|
|
et2_calendar_event.prototype._get_timespan = function (event) {
|
|
var timespan = '';
|
|
if (event['start_m'] === 0 && event['end_m'] >= 24 * 60 - 1) {
|
|
if (event['end_m'] > 24 * 60) {
|
|
// @ts-ignore
|
|
timespan = jQuery.datepicker.formatTime(egw.preference("timeformat") === "12" ? "h:mmtt" : "HH:mm", {
|
|
hour: event.start_m / 60,
|
|
minute: event.start_m % 60,
|
|
seconds: 0,
|
|
timezone: 0
|
|
}, { "ampm": (egw.preference("timeformat") === "12") }
|
|
// @ts-ignore
|
|
).trim() + ' - ' + jQuery.datepicker.formatTime(egw.preference("timeformat") === "12" ? "h:mmtt" : "HH:mm", {
|
|
hour: event.end_m / 60,
|
|
minute: event.end_m % 60,
|
|
seconds: 0,
|
|
timezone: 0
|
|
}, { "ampm": (egw.preference("timeformat") === "12") }).trim();
|
|
}
|
|
else {
|
|
timespan = this.egw().lang('Whole day');
|
|
}
|
|
}
|
|
else {
|
|
var duration = event.multiday ?
|
|
(event.end - event.start) / 60000 :
|
|
(event.end_m - event.start_m);
|
|
duration = Math.floor(duration / 60) + this.egw().lang('h') + (duration % 60 ? duration % 60 : '');
|
|
// @ts-ignore
|
|
timespan = jQuery.datepicker.formatTime(egw.preference("timeformat") === "12" ? "h:mmtt" : "HH:mm", {
|
|
hour: event.start_m / 60,
|
|
minute: event.start_m % 60,
|
|
seconds: 0,
|
|
timezone: 0
|
|
}, { "ampm": (egw.preference("timeformat") === "12") }).trim();
|
|
// @ts-ignore
|
|
timespan += ' - ' + jQuery.datepicker.formatTime(egw.preference("timeformat") === "12" ? "h:mmtt" : "HH:mm", {
|
|
hour: event.end_m / 60,
|
|
minute: event.end_m % 60,
|
|
seconds: 0,
|
|
timezone: 0
|
|
}, { "ampm": (egw.preference("timeformat") === "12") }).trim();
|
|
timespan += ': ' + duration;
|
|
}
|
|
return timespan;
|
|
};
|
|
/**
|
|
* Make sure event data has all proper values, and format them as expected
|
|
* @param {Object} event
|
|
*/
|
|
et2_calendar_event.prototype._values_check = function (event) {
|
|
// Make sure ID is a string
|
|
if (event.id) {
|
|
event.id = '' + event.id;
|
|
}
|
|
// Parent might be a daycol or a planner_row
|
|
var parent = this.getParent();
|
|
// Use dates as objects
|
|
if (typeof event.start !== 'object') {
|
|
parent.date_helper.set_value(event.start);
|
|
event.start = new Date(parent.date_helper.getValue());
|
|
}
|
|
if (typeof event.end !== 'object') {
|
|
parent.date_helper.set_value(event.end);
|
|
event.end = new Date(parent.date_helper.getValue());
|
|
}
|
|
// We need minutes for durations
|
|
if (typeof event.start_m === 'undefined') {
|
|
event.start_m = event.start.getUTCHours() * 60 + event.start.getUTCMinutes();
|
|
event.end_m = event.end.getUTCHours() * 60 + event.end.getUTCMinutes();
|
|
}
|
|
if (typeof event.multiday === 'undefined') {
|
|
event.multiday = (event.start.getUTCFullYear() !== event.end.getUTCFullYear() ||
|
|
event.start.getUTCMonth() !== event.end.getUTCMonth() ||
|
|
event.start.getUTCDate() != event.end.getUTCDate());
|
|
}
|
|
if (!event.start.getUTCHours() && !event.start.getUTCMinutes() && event.end.getUTCHours() == 23 && event.end.getUTCMinutes() == 59) {
|
|
event.whole_day_on_top = (event.non_blocking && event.non_blocking != '0');
|
|
}
|
|
};
|
|
/**
|
|
* Check to see if the provided event information is for the same date as
|
|
* what we're currently expecting, and that it has not been changed.
|
|
*
|
|
* If the date has changed, we adjust the associated daywise caches to move
|
|
* the event's ID to where it should be. This check allows us to be more
|
|
* directly reliant on the data cache, and less on any other control logic
|
|
* elsewhere first.
|
|
*
|
|
* @param {Object} event Map of event data from cache
|
|
* @param {string} event.date For non-recurring, single day events, this is
|
|
* the date the event is on.
|
|
* @param {string} event.start Start of the event (used for multi-day events)
|
|
* @param {string} event.end End of the event (used for multi-day events)
|
|
*
|
|
* @return {Boolean} Provided event data is for the same date
|
|
*/
|
|
et2_calendar_event.prototype._sameday_check = function (event) {
|
|
// Event somehow got orphaned, or deleted
|
|
if (!this.getParent() || event === null) {
|
|
return false;
|
|
}
|
|
// Also check participants against owner
|
|
var owner_match = et2_calendar_event.owner_check(event, this.getParent());
|
|
// Simple, same day
|
|
if (owner_match && this.options.value.date && event.date == this.options.value.date) {
|
|
return true;
|
|
}
|
|
// Multi-day non-recurring event spans days - date does not match
|
|
var event_start = new Date(event.start);
|
|
var event_end = new Date(event.end);
|
|
var parent = this.getParent();
|
|
if (owner_match && (parent instanceof et2_widget_daycol_1.et2_calendar_daycol) && parent.getDate() >= event_start && parent.getDate() <= event_end) {
|
|
return true;
|
|
}
|
|
// Delete all old actions
|
|
if (this._actionObject) {
|
|
this._actionObject.clear();
|
|
this._actionObject.unregisterActions();
|
|
this._actionObject = null;
|
|
}
|
|
// Update daywise caches
|
|
var new_cache_id = CalendarApp._daywise_cache_id(event.date, this.getParent().options.owner);
|
|
var new_daywise = egw.dataGetUIDdata(new_cache_id);
|
|
new_daywise = new_daywise && new_daywise.data ? new_daywise.data : [];
|
|
var old_cache_id = '';
|
|
if (this.options.value && this.options.value.date) {
|
|
old_cache_id = CalendarApp._daywise_cache_id(this.options.value.date, parent.options.owner);
|
|
}
|
|
if (new_cache_id != old_cache_id) {
|
|
var old_daywise = egw.dataGetUIDdata(old_cache_id);
|
|
old_daywise = old_daywise && old_daywise.data ? old_daywise.data : [];
|
|
old_daywise.splice(old_daywise.indexOf(this.options.value.row_id), 1);
|
|
egw.dataStoreUID(old_cache_id, old_daywise);
|
|
if (new_daywise.indexOf(event.row_id) < 0) {
|
|
new_daywise.push(event.row_id);
|
|
}
|
|
if (egw.dataHasUID(new_cache_id)) {
|
|
egw.dataStoreUID(new_cache_id, new_daywise);
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
/**
|
|
* Check that the event passes the given status filter.
|
|
* Status filter is set in the sidebox and used when fetching several events, but if user changes their status
|
|
* for an event, it may no longer match and have to be removed.
|
|
*
|
|
* @param event
|
|
* @param filter
|
|
* @param owner The owner of the target / parent, not the event owner
|
|
* @private
|
|
*/
|
|
et2_calendar_event.prototype._status_check = function (event, filter, owner) {
|
|
var _a;
|
|
if (!owner || !event) {
|
|
return false;
|
|
}
|
|
// If we're doing a bunch, just one passing is enough
|
|
if (typeof owner !== "string") {
|
|
var pass = false;
|
|
for (var j = 0; j < owner.length && pass == false; j++) {
|
|
pass = pass || this._status_check(event, filter, owner[j]);
|
|
}
|
|
return pass;
|
|
}
|
|
// Show also events just owned by selected user
|
|
// Group members can be owner too, those get handled when we check group memberships below
|
|
if (filter == 'owner' && owner == event.owner) {
|
|
return true;
|
|
}
|
|
// Get the relevant participant
|
|
var participant = event.participants[owner];
|
|
// If filter says don't look in groups, skip it all
|
|
if (!participant && filter === 'no-enum-groups') {
|
|
return false;
|
|
}
|
|
// Couldn't find the current owner in the participant list, check groups & resources
|
|
if (!participant) {
|
|
var options = null;
|
|
if (app.calendar && app.calendar.sidebox_et2 && app.calendar.sidebox_et2.getWidgetById('owner')) {
|
|
options = app.calendar.sidebox_et2.getWidgetById('owner').taglist.getSelection();
|
|
}
|
|
if ((isNaN(parseInt(owner)) || parseInt(owner) < 0) && options && typeof options.find == "function") {
|
|
var resource = options.find(function (element) {
|
|
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"; });
|
|
if (matching_participant.length > 0) {
|
|
return this._status_check(event, filter, matching_participant);
|
|
}
|
|
else if (filter == 'owner' && resource && resource.resources && resource.resources.indexOf(event.owner)) {
|
|
// owner param was a group but event is owned by someone in that group
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
var status = et2_calendar_event.split_status(participant);
|
|
switch (filter) {
|
|
default:
|
|
case 'all':
|
|
return true;
|
|
case 'default': // Show all status, but rejected
|
|
return status !== 'R';
|
|
case 'accepted': //Show only accepted events
|
|
return status === 'A';
|
|
case 'unknown': // Show only invitations, not yet accepted or rejected
|
|
return status === 'U';
|
|
case 'tentative': // Show only tentative accepted events
|
|
return status === 'T';
|
|
case 'delegated': // Show only delegated events
|
|
return status === 'D';
|
|
case 'rejected': // Show only rejected events
|
|
return status === 'R';
|
|
// Handled above
|
|
//case 'owner': // Show also events just owned by selected user
|
|
case 'hideprivate': // Show all events, as if they were private
|
|
// handled server-side
|
|
return true;
|
|
case 'showonlypublic': // Show only events flagged as public, -not checked as private
|
|
return event.public == '1';
|
|
// Handled above
|
|
// case 'no-enum-groups': // Do not include events of group members
|
|
case 'not-unknown': // Show all status, but unknown
|
|
return status !== 'U';
|
|
case 'deleted': // Show events that have been deleted
|
|
return event.deleted;
|
|
}
|
|
};
|
|
et2_calendar_event.prototype.attachToDOM = function () {
|
|
var result = _super.prototype.attachToDOM.call(this);
|
|
// Remove the binding for the click handler, unless there's something
|
|
// custom here.
|
|
if (!this.onclick) {
|
|
jQuery(this.node).off("click");
|
|
}
|
|
return result;
|
|
};
|
|
/**
|
|
* Click handler calling custom handler set via onclick attribute to this.onclick.
|
|
* All other handling is done by the timegrid widget.
|
|
*
|
|
* @param {Event} _ev
|
|
* @returns {boolean}
|
|
*/
|
|
et2_calendar_event.prototype.click = function (_ev) {
|
|
var result = true;
|
|
if (typeof this.onclick == 'function') {
|
|
// Make sure function gets a reference to the widget, splice it in as 2. argument if not
|
|
var args = Array.prototype.slice.call(arguments);
|
|
if (args.indexOf(this) == -1)
|
|
args.splice(1, 0, this);
|
|
result = this.onclick.apply(this, args);
|
|
}
|
|
return result;
|
|
};
|
|
/**
|
|
* Show the recur prompt for this event
|
|
*
|
|
* Calls et2_calendar_event.recur_prompt with this event's value.
|
|
*
|
|
* @param {et2_calendar_event~prompt_callback} callback
|
|
* @param {Object} [extra_data]
|
|
*/
|
|
et2_calendar_event.prototype.recur_prompt = function (callback, extra_data) {
|
|
et2_calendar_event.recur_prompt(this.options.value, callback, extra_data);
|
|
};
|
|
/**
|
|
* Show the series split prompt for this event
|
|
*
|
|
* Calls et2_calendar_event.series_split_prompt with this event's value.
|
|
*
|
|
* @param {et2_calendar_event~prompt_callback} callback
|
|
*/
|
|
et2_calendar_event.prototype.series_split_prompt = function (callback) {
|
|
et2_calendar_event.series_split_prompt(this.options.value, this.options.value.recur_date, callback);
|
|
};
|
|
/**
|
|
* Copy the actions set on the parent, apply them to self
|
|
*
|
|
* This can take a while to do, so we try to do it only when needed - on mouseover
|
|
*/
|
|
et2_calendar_event.prototype._copy_parent_actions = function () {
|
|
// Copy actions set in parent
|
|
if (!this.options.readonly && !this.getParent().options.readonly) {
|
|
var action_parent = this;
|
|
while (action_parent != null && !action_parent.options.actions &&
|
|
!(action_parent instanceof et2_container)) {
|
|
action_parent = action_parent.getParent();
|
|
}
|
|
try {
|
|
this._link_actions(action_parent.options.actions || {});
|
|
this._need_actions_linked = false;
|
|
}
|
|
catch (e) {
|
|
// something went wrong, but keep quiet about it
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Link the actions to the DOM nodes / widget bits.
|
|
*
|
|
* @param {object} actions {ID: {attributes..}+} map of egw action information
|
|
*/
|
|
et2_calendar_event.prototype._link_actions = function (actions) {
|
|
if (!this._actionObject) {
|
|
// Get the top level element - timegrid or so
|
|
var objectManager = this.getParent()._actionObject || this.getParent().getParent()._actionObject ||
|
|
egw_getAppObjectManager(true).getObjectById(this.getParent().getParent().getParent().id) || egw_getAppObjectManager(true);
|
|
this._actionObject = objectManager.getObjectById('calendar::' + this.options.value.row_id);
|
|
}
|
|
if (this._actionObject == null) {
|
|
// Add a new container to the object manager which will hold the widget
|
|
// objects
|
|
this._actionObject = objectManager.insertObject(false, new egwActionObject('calendar::' + this.options.value.row_id, objectManager, et2_calendar_event.et2_event_action_object_impl(this, this.getDOMNode()), this._actionManager || objectManager.manager.getActionById('calendar::' + this.options.value.row_id) || objectManager.manager));
|
|
}
|
|
else {
|
|
this._actionObject.setAOI(et2_calendar_event.et2_event_action_object_impl(this, this.getDOMNode(this)));
|
|
}
|
|
// Delete all old objects
|
|
this._actionObject.clear();
|
|
this._actionObject.unregisterActions();
|
|
// Go over the widget & add links - this is where we decide which actions are
|
|
// 'allowed' for this widget at this time
|
|
var action_links = this._get_action_links(actions);
|
|
action_links.push('egw_link_drag');
|
|
action_links.push('egw_link_drop');
|
|
if (this._actionObject.parent.getActionLink('invite')) {
|
|
action_links.push('invite');
|
|
}
|
|
this._actionObject.updateActionLinks(action_links);
|
|
};
|
|
/**
|
|
* Code for implementing et2_IDetachedDOM
|
|
*
|
|
* @param {array} _attrs array to add further attributes to
|
|
*/
|
|
et2_calendar_event.prototype.getDetachedAttributes = function (_attrs) {
|
|
};
|
|
et2_calendar_event.prototype.getDetachedNodes = function () {
|
|
return [this.getDOMNode()];
|
|
};
|
|
et2_calendar_event.prototype.setDetachedAttributes = function (_nodes, _values) {
|
|
};
|
|
// Static class stuff
|
|
/**
|
|
* Check event owner against a parent object
|
|
*
|
|
* As an event is edited, its participants may change. Also, as the state
|
|
* changes we may change which events are displayed and show the same event
|
|
* in several places for different users. Here we check the event participants
|
|
* against an owner value (which may be an array) to see if the event should be
|
|
* displayed or included.
|
|
*
|
|
* @param {Object} event - Event information
|
|
* @param {et2_widget_daycol|et2_widget_planner_row} parent - potential parent object
|
|
* that has an owner option
|
|
* @param {boolean} [owner_too] - Include the event owner in consideration, or only
|
|
* event participants
|
|
*
|
|
* @return {boolean} Should the event be displayed
|
|
*/
|
|
et2_calendar_event.owner_check = function (event, parent, owner_too) {
|
|
var owner_match = true;
|
|
if (typeof owner_too === 'undefined' && app.calendar.state.status_filter) {
|
|
owner_too = app.calendar.state.status_filter === 'owner';
|
|
}
|
|
var options = null;
|
|
if (app.calendar && app.calendar.sidebox_et2 && app.calendar.sidebox_et2.getWidgetById('owner')) {
|
|
options = app.calendar.sidebox_et2.getWidgetById('owner').taglist.getSelection();
|
|
}
|
|
else {
|
|
options = parent.getArrayMgr("sel_options").getRoot().getEntry('owner');
|
|
}
|
|
if (event.participants && typeof parent.options.owner != 'undefined' && parent.options.owner.length > 0) {
|
|
var parent_owner = jQuery.extend([], typeof parent.options.owner !== 'object' ?
|
|
[parent.options.owner] :
|
|
parent.options.owner);
|
|
owner_match = false;
|
|
var length_1 = parent_owner.length;
|
|
for (var i = 0; i < length_1; i++) {
|
|
// Handle groups & grouped resources like mailing lists, they won't match so
|
|
// we need the list - pull it from sidebox owner
|
|
if ((isNaN(parent_owner[i]) || parent_owner[i] < 0) && options && typeof options.find == "function") {
|
|
var resource = options.find(function (element) { return element.id == parent_owner[i]; }) || {};
|
|
if (resource && resource.resources) {
|
|
parent_owner.splice(i, 1);
|
|
i--;
|
|
parent_owner = parent_owner.concat(resource.resources);
|
|
}
|
|
}
|
|
}
|
|
var participants_1 = jQuery.extend([], Object.keys(event.participants));
|
|
var _loop_1 = function () {
|
|
var id = participants_1[i];
|
|
// Expand group invitations
|
|
if (parseInt(id) < 0) {
|
|
if (options && options.find && (resource = options.find(function (element) { return element.id === id; })) && resource.resources) {
|
|
participants_1 = participants_1.concat(resource.resources);
|
|
}
|
|
else {
|
|
// Add in groups, if we can get them (this is asynchronous)
|
|
egw.accountData(id, 'account_id', true, function (members) {
|
|
participants_1 = participants_1.concat(Object.keys(members));
|
|
}, this_1);
|
|
}
|
|
}
|
|
if (parent.options.owner == id ||
|
|
parent_owner.indexOf &&
|
|
parent_owner.indexOf(id) >= 0) {
|
|
owner_match = true;
|
|
return "break";
|
|
}
|
|
};
|
|
var this_1 = this, resource;
|
|
for (var i = 0; i < participants_1.length; i++) {
|
|
var state_1 = _loop_1();
|
|
if (state_1 === "break")
|
|
break;
|
|
}
|
|
}
|
|
if (owner_too && !owner_match) {
|
|
owner_match = (parent.options.owner == event.owner ||
|
|
parent_owner.indexOf &&
|
|
parent_owner.indexOf(event.owner) >= 0);
|
|
}
|
|
return owner_match;
|
|
};
|
|
/**
|
|
* @callback et2_calendar_event~prompt_callback
|
|
* @param {string} button_id - One of ok, exception, series, single or cancel
|
|
* depending on which buttons are on the prompt
|
|
* @param {Object} event_data - Event information - whatever you passed in to
|
|
* the prompt.
|
|
*/
|
|
/**
|
|
* Recur prompt
|
|
* If the event is recurring, asks the user if they want to edit the event as
|
|
* an exception, or change the whole series. Then the callback is called.
|
|
*
|
|
* If callback is not provided, egw.open() will be used to open an edit dialog.
|
|
*
|
|
* If you call this on a single (non-recurring) event, the callback will be
|
|
* executed immediately, with the passed button_id as 'single'.
|
|
*
|
|
* @param {Object} event_data - Event information
|
|
* @param {string} event_data.id - Unique ID for the event, possibly with a
|
|
* timestamp
|
|
* @param {string|Date} event_data.start - Start date/time for the event
|
|
* @param {number} event_data.recur_type - Recur type, or 0 for a non-recurring event
|
|
* @param {et2_calendar_event~prompt_callback} [callback] - Callback is
|
|
* called with the button (exception, series, single or cancel) and the event
|
|
* data.
|
|
* @param {Object} [extra_data] - Additional data passed to the callback, used
|
|
* as extra parameters for default callback
|
|
*
|
|
* @augments {et2_calendar_event}
|
|
*/
|
|
et2_calendar_event.recur_prompt = function (event_data, callback, extra_data) {
|
|
var egw;
|
|
var edit_id = event_data.app_id;
|
|
var edit_date = event_data.start;
|
|
// seems window.opener somehow in certain conditions could be from different origin
|
|
// we try to catch the exception and in this case retrieve the egw object from current window.
|
|
try {
|
|
egw = this.egw ? (typeof this.egw == 'function' ? this.egw() : this.egw) : window.opener && typeof window.opener.egw != 'undefined' ? window.opener.egw('calendar') : window.egw('calendar');
|
|
}
|
|
catch (e) {
|
|
egw = window.egw('calendar');
|
|
}
|
|
var that = this;
|
|
var extra_params = extra_data && typeof extra_data == 'object' ? extra_data : {};
|
|
extra_params.date = edit_date.toJSON ? edit_date.toJSON() : edit_date;
|
|
if (typeof callback != 'function') {
|
|
callback = function (_button_id) {
|
|
switch (_button_id) {
|
|
case 'exception':
|
|
extra_params.exception = '1';
|
|
egw.open(edit_id, event_data.app || 'calendar', 'edit', extra_params);
|
|
break;
|
|
case 'series':
|
|
case 'single':
|
|
egw.open(edit_id, event_data.app || 'calendar', 'edit', extra_params);
|
|
break;
|
|
case 'cancel':
|
|
default:
|
|
break;
|
|
}
|
|
};
|
|
}
|
|
if (parseInt(event_data.recur_type)) {
|
|
var buttons = [
|
|
{ text: egw.lang("Edit exception"), id: "exception", class: "ui-priority-primary", "default": true },
|
|
{ text: egw.lang("Edit series"), id: "series" },
|
|
{ text: egw.lang("Cancel"), id: "cancel" }
|
|
];
|
|
et2_dialog.show_dialog(function (button_id) { callback.call(that, button_id, event_data); }, (!event_data.is_private ? event_data['title'] : egw.lang('private')) + "\n" +
|
|
egw.lang("Do you want to edit this event as an exception or the whole series?"), egw.lang("This event is part of a series"), {}, buttons, et2_dialog.QUESTION_MESSAGE);
|
|
}
|
|
else {
|
|
callback.call(this, 'single', event_data);
|
|
}
|
|
};
|
|
/**
|
|
* Split series prompt
|
|
*
|
|
* If the event is recurring and the user adjusts the time or duration, we may need
|
|
* to split the series, ending the current one and creating a new one with the changes.
|
|
* This prompts the user if they really want to do that.
|
|
*
|
|
* There is no default callback, and nothing happens if you call this on a
|
|
* single (non-recurring) event
|
|
*
|
|
* @param {Object} event_data - Event information
|
|
* @param {string} event_data.id - Unique ID for the event, possibly with a timestamp
|
|
* @param {string|Date} instance_date - The date of the edited instance of the event
|
|
* @param {et2_calendar_event~prompt_callback} callback - Callback is
|
|
* called with the button (ok or cancel) and the event data.
|
|
* @augments {et2_calendar_event}
|
|
*/
|
|
et2_calendar_event.series_split_prompt = function (event_data, instance_date, callback) {
|
|
var egw;
|
|
// seems window.opener somehow in certian conditions could be from different origin
|
|
// we try to catch the exception and in this case retrieve the egw object from current window.
|
|
try {
|
|
egw = this.egw ? (typeof this.egw == 'function' ? this.egw() : this.egw) : window.opener && typeof window.opener.egw != 'undefined' ? window.opener.egw('calendar') : window.egw('calendar');
|
|
}
|
|
catch (e) {
|
|
egw = window.egw('calendar');
|
|
}
|
|
var that = this;
|
|
if (typeof instance_date == 'string') {
|
|
instance_date = new Date(instance_date);
|
|
}
|
|
// Check for modifying a series that started before today
|
|
var tempDate = new Date();
|
|
var today = new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate(), tempDate.getHours(), -tempDate.getTimezoneOffset(), tempDate.getSeconds());
|
|
var termination_date = instance_date < today ? egw.lang('today') : date(egw.preference('dateformat'), instance_date);
|
|
if (parseInt(event_data.recur_type)) {
|
|
et2_dialog.show_dialog(function (button_id) { callback.call(that, button_id, event_data); }, (!event_data.is_private ? event_data['title'] : egw.lang('private')) + "\n" +
|
|
egw.lang("Do you really want to change the start of this series? If you do, the original series will be terminated as of %1 and a new series for the future reflecting your changes will be created.", termination_date), egw.lang("This event is part of a series"), {}, et2_dialog.BUTTONS_OK_CANCEL, et2_dialog.WARNING_MESSAGE);
|
|
}
|
|
};
|
|
et2_calendar_event.drag_helper = function (event, ui) {
|
|
ui.helper.width(ui.width());
|
|
};
|
|
/**
|
|
* splits the combined status, quantity and role
|
|
*
|
|
* @param {string} status - combined value, O: status letter: U, T, A, R
|
|
* @param {int} [quantity] - quantity
|
|
* @param {string} [role]
|
|
* @return string status U, T, A or R, same as $status parameter on return
|
|
*/
|
|
et2_calendar_event.split_status = function (status, quantity, role) {
|
|
quantity = 1;
|
|
role = 'REQ-PARTICIPANT';
|
|
//error_log(__METHOD__.__LINE__.array2string($status));
|
|
var matches = null;
|
|
if (typeof status === 'string' && status.length > 1) {
|
|
matches = status.match(/^.([0-9]*)(.*)$/gi);
|
|
}
|
|
if (matches) {
|
|
if (parseInt(matches[1]) > 0)
|
|
quantity = parseInt(matches[1]);
|
|
if (matches[2])
|
|
role = matches[2];
|
|
status = status[0];
|
|
}
|
|
else if (status === true) {
|
|
status = 'U';
|
|
}
|
|
return status;
|
|
};
|
|
/**
|
|
* The egw_action system requires an egwActionObjectInterface Interface implementation
|
|
* to tie actions to DOM nodes. I'm not sure if we need this.
|
|
*
|
|
* The class extension is different than the widgets
|
|
*
|
|
* @param {et2_DOMWidget} widget
|
|
* @param {Object} node
|
|
*
|
|
*/
|
|
et2_calendar_event.et2_event_action_object_impl = function (widget, node) {
|
|
var aoi = new et2_core_DOMWidget_1.et2_action_object_impl(widget, node).getAOI();
|
|
// _outerCall may be used to determine, whether the state change has been
|
|
// evoked from the outside and the stateChangeCallback has to be called
|
|
// or not.
|
|
aoi.doSetState = function (_state, _outerCall) {
|
|
};
|
|
return aoi;
|
|
};
|
|
et2_calendar_event._attributes = {
|
|
"value": {
|
|
type: "any",
|
|
default: et2_no_init
|
|
},
|
|
"onclick": {
|
|
"description": "JS code which is executed when the element is clicked. " +
|
|
"If no handler is provided, or the handler returns true and the event is not read-only, the " +
|
|
"event will be opened according to calendar settings."
|
|
}
|
|
};
|
|
return et2_calendar_event;
|
|
}(et2_core_valueWidget_1.et2_valueWidget));
|
|
exports.et2_calendar_event = et2_calendar_event;
|
|
et2_core_widget_1.et2_register_widget(et2_calendar_event, ["calendar-event"]);
|
|
//# sourceMappingURL=et2_widget_event.js.map
|