tine20/Calendar/js/PollPanel.js
Rama main · réplica de lectura del repositorio original.
/*
* Tine 2.0
*
* @package Calendar
* @license http://www.gnu.org/licenses/agpl.html AGPL Version 3
* @author Cornelius Weiss <c.weiss@metaways.de>
* @copyright Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
*
*/
Ext.ns('Tine.Calendar');
require('../styles/PollPanel.scss');
Tine.Calendar.PollPanel = Ext.extend(Ext.Panel, {
/**
* @cfg {Tine.widgets.dialog.EditDialog}
*/
editDialog: null,
requiredGrant: 'editGrant',
/**
* @property {Tine.Calendar.Model.Poll} poll
*/
poll: null,
canonicalName: 'PollPanel',
xtype: 'form',
frame: true,
// grows with the active card (options form / alternative dates rows)
autoHeight: true,
layout: 'fit',
statusWeightMap: ["DECLINED", "NEEDS-ACTION", "TENTATIVE", "ACCEPTED"],
initComponent: function() {
var _ = window.lodash,
me = this;
this.app = Tine.Tinebase.appMgr.get('Calendar');
this.title = this.app.i18n._('Poll');
this.on('activate', this.onPanelActivate, this);
this.editDialog.on('load', this.onRecordLoad, this);
this.editDialog.on('recordUpdate', this.onRecordUpdate, this);
this.editDialog.on('dtStartChange', this.onMainTabDtStartChange, this);
this.tbar = [{
xtype: 'buttongroup',
frame: false,
items: [{
xtype: 'tbbtnlockedtoggle',
toggleGroup: 'cal-poll-panel',
pressed: true,
text: this.app.i18n._('Options'),
handler: this.changeCardPanel.createDelegate(this, [0]),
enableToggle: true
}, {
xtype: 'tbbtnlockedtoggle',
toggleGroup: 'cal-poll-panel',
ref: '../../alternativeDatesButton',
disabled: true,
pressed: false,
text: this.app.i18n._('Alternative Dates'),
handler: this.changeCardPanel.createDelegate(this, [1]),
enableToggle: true
}]
}];
this.action_setDefiniteEvent = new Ext.Action({
text: this.app.i18n._('Set as a definite event'),
iconCls: 'cal-polls-set-definite-action',
scope: this,
handler: this.onSetDefiniteEvent
});
this.items = [{
layout: 'card',
ref: 'cardPanel',
activeItem: 0,
layoutConfig: {
layoutOnCardChange: true,
deferredRender: true
},
autoHeight: true,
items: [{
// layout: 'form',
// frame: true,
xtype: 'columnform',
autoHeight: true,
border: false,
labelAlign: 'top',
width: '100%',
items: [[{
xtype: 'checkbox',
columnWidth: 0.40,
ref: '../../../../createPollCheckbox',
disabled: true,
hideLabel: true,
boxLabel: this.app.i18n._('Create a Poll for this Event'),
listeners: {scope: this, check: this.onCreatePollCheck}
}, {
xtype: 'textfield',
columnWidth: 0.60,
ref: '../../../../pollNameField',
disabled: true,
hideLabel: true,
emptyText: this.app.i18n._('Poll Name')
}], [{
xtype: 'displayfield',
columnWidth: 1,
hideLabel: true,
value: [
this.app.i18n._("Create scheduling suggestions and choose the best option based on user feedback."),
this.app.i18n._("Share the link below to poll for attendee status feedback."),
].join(' ')
}], [{
// @TODO implement clipbord field (trigger field)
xtype: 'field',
columnWidth: 1,
ref: '../../../../urlField',
fieldLabel: this.app.i18n._('Public URL'),
readOnly: true,
anchor: '100%'
}], [{
xtype: 'checkbox',
columnWidth: 0.40,
ref: '../../../../isPasswordProtectedCheckbox',
disabled: true,
hideLabel: true,
boxLabel: this.app.i18n._('Protect poll with a password'),
listeners: {scope: this, check: this.onIsPasswordProtectedCheck}
}, {
xtype: 'textfield',
columnWidth: 0.30,
ref: '../../../../pollPasswordField',
disabled: true,
hideLabel: true
}], [{
xtype: 'checkbox',
columnWidth: 1,
ref: '../../../../isLockedPollCheckbox',
disabled: true,
hideLabel: true,
boxLabel: this.app.i18n._('Only invited attendee can reply')
}]]
}, new Tine.widgets.grid.QuickaddGridPanel({
ref: '../alternativeEventsGrid',
autoHeight: true,
minHeight: 150,
border: false,
frame: false,
autoExpandColumn: 'info',
quickaddMandatory: 'dtstart',
recordClass: Tine.Calendar.Model.Event,
store: new Tine.Tinebase.data.RecordStore({
readOnly: true,
autoLoad: false,
recordClass: Tine.Calendar.Model.Event,
proxy: Tine.Calendar.backend,
pruneModifiedRecords: true,
getModifiedRecords: function() {return window.lodash.filter(this.modified, {dirty: true});},
sort: this.sortEventStore,
sortInfo: {
field: 'dtstart',
direction: 'ASC'
}
}),
resetAllOnNew: false,
contextMenuItems: [me.action_setDefiniteEvent],
listeners: {
scope: this,
afteredit: this.onAfterEventEdit,
activate: this.onEventsGridActivate
},
cm: new Ext.grid.ColumnModel([{
id: 'dtstart',
header: this.app.i18n._('Start Date'),
width: 200,
hideable: false,
sortable: true,
renderer: this.dateRenderer.createDelegate(this),
quickaddField: new Ext.ux.form.DateTimeField({
resetOnNew: false
}),
editor: new Ext.ux.form.DateTimeField({
allowBlank: false
})
}, {
id: 'info',
header: this.app.i18n._('Info'),
hideable: false,
sortable: true,
renderer: this.infoRenderer.createDelegate(this)
}])
})]
}];
this.supr().initComponent.apply(this, arguments);
},
dateRenderer: function(value, metaData, record, rowIndex, colIndex, store) {
if (this.editDialog.record.get('id') == record.get('id')) {
metaData.css = 'cal-pollpanel-alternativeevents-currentevent';
}
return value.format('l') + ', ' + Tine.Tinebase.common.dateTimeRenderer(value);
},
infoRenderer: function(value, metaData, record, rowIndex, colIndex, store) {
var _ = window.lodash,
attendeeStatus = _.groupBy(_.get(record, 'data.attendee', {}), 'status'),
statusStore = store = Tine.Tinebase.widgets.keyfield.StoreMgr.get(this.app, 'attendeeStatus'),
renderer = Tine.Tinebase.widgets.keyfield.Renderer.get(this.app, 'attendeeStatus');
return _.map(_.reverse([].concat(Tine.Calendar.PollPanel.prototype.statusWeightMap)), function(statusId) {
var count = _.get(attendeeStatus, statusId, []).length;
return '<span class="cal-pollpanel-alternativeevents-statustoken">' +
renderer(statusId) +
'<span class="cal-pollpanel-alternativeevents-statuscount">' + count + '</span>' +
'</span>';
}).join('');
},
infoSortValueFn: function(event) {
var _ = window.lodash,
attendeeStatus = _.groupBy(_.get(event, 'data.attendee', {}), 'status');
return _.reduce(attendeeStatus, function(result, attendee, id) {
return result + attendee.length * Math.max(0, _.indexOf(Tine.Calendar.PollPanel.prototype.statusWeightMap, id));
}, 0);
},
sortEventStore: function(fieldName, dir) {
if (fieldName != 'info') {
return Ext.data.JsonStore.prototype.sort.apply(this, arguments);
}
dir = this.sortToggle['info'] = (this.sortToggle['info'] || 'ASC').toggle('ASC', 'DESC');
this.data.sort(dir, function(a, b) {
return Tine.Calendar.PollPanel.prototype.infoSortValueFn(a) -
Tine.Calendar.PollPanel.prototype.infoSortValueFn(b);
});
this.sortInfo = {
field: 'info',
direction: dir
};
this.fireEvent('datachanged', this);
},
onPanelActivate: function() {
this.editDialog.rrulePanel.onRecordUpdate(this.editDialog.record);
this.setReadOnly(this.readOnly);
},
onCreatePollCheck: function(cb, checked) {
var pollData = null;
// initialize new poll
if (checked && !this.poll) {
var pollId = Tine.Tinebase.data.Record.generateUID(40),
event = this.editDialog.record;
this.alternativeEventsGrid.store.add([new Tine.Calendar.Model.Event({
// id: event.get('id'),
dtstart: event.get('dtstart'),
dtend: event.get('dtdend')
}, event.id)]);
this.alternativeEventsGrid.pollId = pollId;
pollData = {
id: pollId
};
}
this.onPollLoad(pollData);
},
onIsPasswordProtectedCheck: function(cb, checked) {
this.pollPasswordField.setDisabled(!checked);
},
onRecordLoad: function() {
var _ = window.lodash,
me = this;
// load poll
me.onPollLoad(this.editDialog.record.get('poll_id'));
this.alternativeEventsGrid.afterIsRendered().then(function() {
// preset quickadd
me.alternativeEventsGrid.getCols(true)[0].quickaddField.setValue(me.editDialog.record.get('dtstart'));
});
},
onPollLoad: function(pollData) {
var _ = window.lodash,
me = this,
record = me.editDialog.record,
evalGrants = me.editDialog.evalGrants,
isClosed = !!+_.get(pollData, 'closed', false),
hasRequiredGrant = !evalGrants || _.get(record, record.constructor.getMeta('grantsPath') + '.' + this.requiredGrant);
if (pollData) {
this.poll = new Tine.Calendar.Model.Poll(pollData);
Tine.Tinebase.common.assertComparable(this.poll.data);
} else {
this.poll = null;
}
// click vs. setValue!
this.createPollCheckbox.suspendEvents();
this.createPollCheckbox.setValue(!!this.poll);
this.createPollCheckbox.resumeEvents();
this.urlField.setValue(this.poll ? record.getPollUrl(this.poll.id) : '');
this.pollNameField.setValue(this.poll ? this.poll.get('name') : '');
this.isPasswordProtectedCheckbox.setValue(this.poll ? !!this.poll.get('password') : false);
this.pollPasswordField.setValue(this.poll && this.poll.get('password') ? this.poll.get('password') : '');
this.isLockedPollCheckbox.setValue(this.poll ? !!+this.poll.get('locked') : false);
if (this.poll) {
this.editDialog.rrulePanel.setDisabled(true);
}
this.setReadOnly(! hasRequiredGrant || isClosed);
},
setReadOnly: function(readOnly) {
var hasPoll = this.createPollCheckbox.getValue(),
isRecur = this.editDialog.record.get('rrule') || this.editDialog.record.isRecurException(),
hasPassword = this.isPasswordProtectedCheckbox.getValue();
// NOTE: it's not possible to remove a persistent poll here
// -> use choose definite event
this.createPollCheckbox.setDisabled(readOnly ||this.poll ||isRecur);
this.pollNameField.setDisabled(!hasPoll || readOnly);
this.isPasswordProtectedCheckbox.setDisabled(!hasPoll || readOnly);
this.pollPasswordField.setDisabled(!hasPoll || !hasPassword || readOnly);
this.isLockedPollCheckbox.setDisabled(!hasPoll || readOnly);
this.alternativeEventsGrid.setReadOnly(!hasPoll || readOnly);
this.alternativeDatesButton.setDisabled(!hasPoll);
this.readOnly = readOnly;
},
onEventsGridActivate: function() {
var me = this,
pollId = this.poll.get('id'),
event = this.editDialog.record;
if (! this.alternativeEventsGrid.loadMask) {
this.alternativeEventsGrid.loadMask = new Ext.LoadMask(this.alternativeEventsGrid.getEl(), {
msg: this.app.i18n._('loading alternative dates')
});
}
if (pollId != this.alternativeEventsGrid.pollId) {
this.alternativeEventsGrid.loadMask.show();
Tine.Calendar.getPollEvents(this.poll.get('id'))
.then(function (response) {
me.alternativeEventsGrid.store.loadData(response);
// event copy
if (!event.get('id')) {
me.alternativeEventsGrid.store.add([new Tine.Calendar.Model.Event({
// id: event.get('id'),
dtstart: event.get('dtstart'),
dtend: event.get('dtdend')
}, event.id)])
}
me.alternativeEventsGrid.pollId = pollId;
me.alternativeEventsGrid.loadMask.hide();
});
}
this.alternativeEventsGrid.setReadOnly(this.readOnly);
this.action_setDefiniteEvent.setDisabled(this.readOnly);
},
onRecordUpdate: function(editDialog, record) {
if (this.poll) {
this.poll.set('name', this.pollNameField.getValue());
this.poll.set('password', this.isPasswordProtectedCheckbox.getValue() ?
this.pollPasswordField.getValue() : null
);
this.poll.set('locked', this.isLockedPollCheckbox.getValue());
this.poll.set('alternative_dates', null);
if (this.poll.get('id') == this.alternativeEventsGrid.pollId) {
this.poll.set('alternative_dates', this.alternativeEventsGrid.getFromStoreAsArray(true));
}
record.set('poll_id', this.poll.data);
}
},
onMainTabDtStartChange: function(data) {
var scheduling = Ext.decode(data),
newValue = new Date(scheduling.newValue),
oldValue = new Date(scheduling.oldValue),
event = this.alternativeEventsGrid.store.getById(this.editDialog.record.id);
if (event) {
event.set('dtstart', newValue);
}
},
onAfterEventEdit: function(o) {
if (o.field == 'dtstart' && o.record.get('id') == this.editDialog.record.get('id')) {
var dtStartField = this.editDialog.getForm().findField('dtstart'),
newValue = o.record.get('dtstart'),
oldValue = o.record.modified['dtstart'];
dtStartField.setValue(newValue);
this.editDialog.onDtStartChange(dtStartField, newValue, oldValue);
}
},
onSetDefiniteEvent: function() {
require('./PollSetDefiniteEventAction');
var me = this,
ns = Tine.Calendar.eventActions.setDefiniteEventAction,
selected = this.alternativeEventsGrid.getSelectionModel().getSelections(),
event = selected[0],
dlg = this.editDialog;
if (selected.length > 1) {
Ext.Msg.alert(
this.app.i18n._('Select one Event Only'),
this.app.i18n._('You need to select exactly one event as the definite event.')
);
return;
}
ns.confirm()
.then(function() {
me.setDefiniteEventMask = new Ext.LoadMask(dlg.getEl(), {
msg: me.app.i18n._('Setting definite event')
});
me.setDefiniteEventMask.show();
return Tine.Calendar.setDefinitePollEvent(event.data);
})
.then(function(response) {
window.postal.publish({
channel : "thirdparty",
topic : "data.changed"
});
dlg.onCancel.defer(1000, dlg);
})
.catch(function(error) {
me.setDefiniteEventMask.hide();
});
},
changeCardPanel: function(idx) {
this.cardPanel.layout.setActiveItem(idx);
}
});