Implement drag to invite / move to for planner by user view

This commit is contained in:
nathangray 2016-07-14 10:58:54 -06:00
parent eba29d3541
commit 115f420996
3 changed files with 375 additions and 8 deletions

View File

@ -840,7 +840,7 @@ var et2_calendar_event = (function(){ "use strict"; return et2_valueWidget.exten
if(!this._actionObject)
{
// Get the top level element - timegrid or so
var objectManager = this.getParent().getParent()._actionObject ||
var objectManager = this.getParent()._actionObject || this.getParent().getParent()._actionObject ||
egw_getAppObjectManager(true).getObjectById(this._parent._parent._parent.id) || egw_getAppObjectManager(true);
this._actionObject = objectManager.getObjectById('calendar::'+this.options.value.row_id);
}

View File

@ -236,10 +236,15 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
*/
resize:function(event, ui)
{
planner._drag_helper(this,{
top:ui.position.top,
left: ui.position.left + ui.helper.width()
},ui.helper.outerHeight());
if(planner.options.group_by == 'month')
{
var position = {left: event.clientX, top: event.clientY};
}
else
{
var position = {top:ui.position.top, left: ui.position.left + ui.helper.width()};
}
planner._drag_helper(this,position,ui.helper.outerHeight());
}
});
})
@ -1215,6 +1220,51 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
var aoi = new et2_action_object_impl(this,this.getDOMNode());
/**
* Determine if we allow a dropped event to use the invite/change actions,
* and enable or disable them appropriately
*
* @param {egwAction} action
* @param {et2_calendar_event} event The event widget being dragged
* @param {egwActionObject} target Planner action object
*/
var _invite_enabled = function(action, event, target)
{
var event = event.iface.getWidget();
var planner = target.iface.getWidget() || false;
//debugger;
if(event === planner || !event || !planner ||
!event.options || !event.options.value.participants || !planner.options.owner
)
{
return false;
}
var owner_match = false;
var own_row = false;
for(var id in event.options.value.participants)
{
planner.iterateOver(function(row) {
// Check scroll section or header section
if(row.div.hasClass('drop-hover') || row.div.has(':hover'))
{
owner_match = owner_match || row.node.dataset[planner.options.group_by] === ''+id;
own_row = (row === event.getParent());
}
}, this, et2_calendar_planner_row);
}
var enabled = !owner_match &&
// Not inside its own row
!own_row;
widget_object.getActionLink('invite').enabled = enabled;
widget_object.getActionLink('change_participant').enabled = enabled;
// If invite or change participant are enabled, drag is not
widget_object.getActionLink('egw_link_drop').enabled = !enabled;
};
aoi.doTriggerEvent = function(_event, _data) {
// Determine target node
@ -1294,7 +1344,7 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
this._init_links_dnd(widget_object.manager, action_links);
widget_object.updateActionLinks(action_links);
//widget_object.updateActionLinks(action_links);
this._actionObject = widget_object;
},
@ -1311,6 +1361,8 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
var self = this;
var drop_action = mgr.getActionById('egw_link_drop');
var drop_change_participant = mgr.getActionById('change_participant');
var drop_invite = mgr.getActionById('invite');
var drag_action = mgr.getActionById('egw_link_drag');
// Check if this app supports linking
@ -1371,11 +1423,83 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
).sendRequest();
}
},true);
drop_action.acceptedTypes = ['default','link'];
drop_action.hideOnDisabled = true;
// Create the drop action for moving events between planner rows
var invite_action = function(action, source, target) {
// Extract link IDs
var links = [];
var id = '';
for(var i = 0; i < source.length; i++)
{
// Check for no ID (invalid) or same manager (dragging an event)
if(!source[i].id) continue;
if(source[i].manager === target.manager)
{
// Find the row, could have dropped on an event
var row = target.iface.getWidget();
while(target.parent && row.instanceOf && !row.instanceOf(et2_calendar_planner_row))
{
target = target.parent;
row = target.iface.getWidget();
}
// Leave the helper there until the update is done
var loading = action.ui.helper.clone(true).appendTo(jQuery('body'));
// and add a loading icon so user knows something is happening
if(jQuery('.calendar_timeDemo',loading).length == 0)
{
jQuery('.calendar_calEventHeader',loading).addClass('loading');
}
else
{
jQuery('.calendar_timeDemo',loading).after('<div class="loading"></div>');
}
var event_data = egw.dataGetUIDdata(source[i].id).data;
et2_calendar_event.recur_prompt(event_data, function(button_id) {
if(button_id === 'cancel' || !button_id)
{
return;
}
var add_owner = jQuery.extend([],row.node.dataset.participants);
egw().json('calendar.calendar_uiforms.ajax_invite', [
button_id==='series' ? event_data.id : event_data.app_id,
add_owner,
action.id === 'change_participant' ?
jQuery.extend([],source[i].iface.getWidget().getParent().node.dataset.participants) :
[]
],
function() { loading.remove();}
).sendRequest(true);
});
// Ok, stop.
return false;
}
}
};
drop_change_participant = mgr.addAction('drop', 'change_participant', egw.lang('Move to'), egw.image('participant'), invite_action,true);
drop_change_participant.acceptedTypes = ['calendar'];
drop_change_participant.hideOnDisabled = true;
drop_invite = mgr.addAction('drop', 'invite', egw.lang('Invite'), egw.image('participant'), invite_action,true);
drop_invite.acceptedTypes = ['calendar'];
drop_invite.hideOnDisabled = true;
}
if(actionLinks.indexOf(drop_action.id) < 0)
{
actionLinks.push(drop_action.id);
}
actionLinks.push(drop_invite.id);
actionLinks.push(drop_change_participant.id);
// Accept other links, and files dragged from the filemanager
// This does not handle files dragged from the desktop. They are
// handled by et2_nextmatch, since it needs DOM stuff
@ -1400,7 +1524,7 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
{
actionLinks.push(drag_action.id);
}
drag_action.set_dragType('link');
drag_action.set_dragType(['link','calendar']);
},
/**
@ -1864,7 +1988,29 @@ var et2_calendar_planner = (function(){ "use strict"; return et2_calendar_view.e
else
{
// Find the correct row so we know which month, then get the offset
var row = jQuery(document.elementFromPoint(x, y)).closest('.calendar_plannerRowWidget');
var hidden_nodes = [];
var row = null;
// Hide any drag or tooltips that may interfere
do
{
row = document.elementFromPoint(x, y);
if(this.div.has(row).length == 0)
{
hidden_nodes.push(jQuery(row).hide());
}
else
{
break;
}
} while(row.nodeName !== 'BODY');
// Restore hidden nodes
for(var i = 0; i < hidden_nodes.length; i++)
{
hidden_nodes[i].show();
}
row = jQuery(row).closest('.calendar_plannerRowWidget');
var row_widget = null;
for(var i = 0; i < this._children.length && row.length > 0; i++)
{

View File

@ -73,6 +73,8 @@ var et2_calendar_planner_row = (function(){ "use strict"; return et2_valueWidget
this.set_label(this.options.label);
this._draw();
this._link_actions([]);
return true;
},
@ -96,6 +98,225 @@ var et2_calendar_planner_row = (function(){ "use strict"; return et2_valueWidget
}
},
/**
* Link the actions to the DOM nodes / widget bits.
*
* @param {object} actions {ID: {attributes..}+} map of egw action information
*/
_link_actions: function(actions)
{
// Get the parent? Might be a grid row, might not. Either way, it is
// just a container with no valid actions
var objectManager = egw_getObjectManager(this.getInstanceManager().app,true,1);
objectManager = objectManager.getObjectById(this.getInstanceManager().uniqueId,2) || objectManager;
var parent = objectManager.getObjectById(this.id,1) || objectManager.getObjectById(this._parent.id,1) || objectManager;
if(!parent)
{
debugger;
egw.debug('error','No parent objectManager found');
return;
}
// This binds into the egw action system. Most user interactions (drag to move, resize)
// are handled internally using jQuery directly.
var widget_object = this._actionObject || parent.getObjectById(this.id);
var aoi = new et2_action_object_impl(this,this.getDOMNode());
var planner = this.getParent();
for(var i = 0; i < parent.children.length; i++)
{
var parent_finder = jQuery(parent.children[i].iface.doGetDOMNode()).find(this.div);
if(parent_finder.length > 0)
{
parent = parent.children[i];
break;
}
}
// Determine if we allow a dropped event to use the invite/change actions
var _invite_enabled = function(action, event, target)
{
var event = event.iface.getWidget();
var row = target.iface.getWidget() || false;
if(event === row || !event || !row ||
!event.options || !event.options.value.participants
)
{
return false;
}
var owner_match = false;
var own_row = event.getParent() === row;
for(var id in event.options.value.participants)
{
owner_match = owner_match || row.node.dataset.participants === ''+id;
}
var enabled = !owner_match &&
// Not inside its own timegrid
!own_row;
widget_object.getActionLink('invite').enabled = enabled;
widget_object.getActionLink('change_participant').enabled = enabled;
// If invite or change participant are enabled, drag is not
widget_object.getActionLink('egw_link_drop').enabled = !enabled;
};
aoi.doTriggerEvent = function(_event, _data) {
// Determine target node
var event = _data.event || false;
if(!event) return;
if(_data.ui.draggable.hasClass('rowNoEdit')) return;
/*
We have to handle the drop in the normal event stream instead of waiting
for the egwAction system so we can get the helper, and destination
*/
if(event.type === 'drop' && widget_object.getActionLink('egw_link_drop').enabled)
{
this.getWidget().getParent()._event_drop.call(
jQuery('.calendar_d-n-d_timeCounter',_data.ui.helper)[0],
this.getWidget().getParent(), event, _data.ui,
this.getWidget()
);
}
var drag_listener = function(_event, ui) {
if(planner.options.group_by === 'month')
{
var position = {left: _event.clientX, top: _event.clientY};
}
else
{
var position = {top:ui.position.top, left: ui.position.left - jQuery(this).parent().offset().left};
}
aoi.getWidget().getParent()._drag_helper(
jQuery('.calendar_d-n-d_timeCounter',ui.helper)[0],
position,0
);
var event = _data.ui.draggable.data('selected')[0];
if(!event || event.id && event.id.indexOf('calendar') !== 0)
{
event = false;
}
if(event)
{
_invite_enabled(
widget_object.getActionLink('invite').actionObj,
event,
widget_object
);
}
};
var time = jQuery('.calendar_d-n-d_timeCounter',_data.ui.helper);
switch(_event)
{
// Triggered once, when something is dragged into the timegrid's div
case EGW_AI_DRAG_OVER:
// Listen to the drag and update the helper with the time
// This part lets us drag between different timegrids
_data.ui.draggable.on('drag.et2_timegrid_row'+widget_object.id, drag_listener);
_data.ui.draggable.on('dragend.et2_timegrid_row'+widget_object.id, function() {
_data.ui.draggable.off('drag.et2_timegrid_row' + widget_object.id);
});
widget_object.iface.getWidget().div.addClass('drop-hover');
// Disable invite / change actions for same calendar or already participant
var event = _data.ui.draggable.data('selected')[0];
if(!event || event.id && event.id.indexOf('calendar') !== 0)
{
event = false;
}
if(event)
{
_invite_enabled(
widget_object.getActionLink('invite').actionObj,
event,
widget_object
);
}
if(time.length)
{
// The out will trigger after the over, so we count
time.data('count',time.data('count')+1);
}
else
{
_data.ui.helper.prepend('<div class="calendar_d-n-d_timeCounter" data-count="1"><span></span></div>');
}
break;
// Triggered once, when something is dragged out of the timegrid
case EGW_AI_DRAG_OUT:
// Stop listening
_data.ui.draggable.off('drag.et2_timegrid_row'+widget_object.id);
// Remove highlight
widget_object.iface.getWidget().div.removeClass('drop-hover');
// Out triggers after the over, count to not accidentally remove
time.data('count',time.data('count')-1);
if(time.length && time.data('count') <= 0)
{
time.remove();
}
break;
}
};
if (widget_object == null) {
// Add a new container to the object manager which will hold the widget
// objects
widget_object = parent.insertObject(false, new egwActionObject(
this.id, parent, aoi,
this._actionManager|| parent.manager.getActionById(this.id) || parent.manager
));
}
else
{
widget_object.setAOI(aoi);
}
this._actionObject = widget_object;
// Delete all old objects
widget_object.clear();
widget_object.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);
this.getParent()._init_links_dnd(widget_object.manager, action_links);
widget_object.updateActionLinks(action_links);
},
/**
* Get all action-links / id's of 1.-level actions from a given action object
*
* Here we are only interested in drop events.
*
* @param actions
* @returns {Array}
*/
_get_action_links: function(actions)
{
var action_links = [];
// TODO: determine which actions are allowed without an action (empty actions)
for(var i in actions)
{
var action = actions[i];
if(action.type == 'drop')
{
action_links.push(typeof action.id != 'undefined' ? action.id : i);
}
}
return action_links;
},
/**
* Draw the individual divs for weekends and events
*/