Calendar et2 conversion work in progress.

- Some context menu actions
- Reduce code duplication in views
- Fix views still using iframe
This commit is contained in:
Nathan Gray 2015-06-30 22:26:59 +00:00
parent 90c578b8f4
commit 925c29ce9d
8 changed files with 444 additions and 465 deletions

View File

@ -417,7 +417,7 @@ class calendar_ui
} }
$this->view = $states['view'] = $func; $this->view = $states['view'] = $func;
} }
$this->view_menuaction = $this->view == 'listview' ? 'calendar.calendar_uilist.listview' : 'calendar.calendar_uiviews.'.$this->view; $this->view_menuaction = $this->view == 'listview' ? 'calendar.calendar_uilist.listview' : 'calendar.calendar_uiviews.index';
if ($this->debug > 0 || $this->debug == 'manage_states') $this->bo->debug_message('uical::manage_states(%1) session was %2, states now %3',True,$set_states,$states_session,$states); if ($this->debug > 0 || $this->debug == 'manage_states') $this->bo->debug_message('uical::manage_states(%1) session was %2, states now %3',True,$set_states,$states_session,$states);
// save the states in the session only when we are in calendar // save the states in the session only when we are in calendar
@ -705,7 +705,7 @@ class calendar_ui
), ),
array( array(
'text' => lang('yearview'), 'text' => lang('yearview'),
'value' => '{"view":"year", "menuaction":"calendar.calendar_uiviews.year"}', 'value' => '{"view":"year", "menuaction":"calendar.calendar_uiviews.index"}',
'selected' => $this->view == 'year', 'selected' => $this->view == 'year',
), ),
array( array(
@ -836,6 +836,7 @@ class calendar_ui
{ {
$event['app_id'] .= ':'.$event['recur_date']; $event['app_id'] .= ':'.$event['recur_date'];
} }
$event['parts'] = implode(",\n",$this->bo->participants($event,true));
// Change dates // Change dates
foreach(calendar_egw_record::$types['date-time'] as $field) foreach(calendar_egw_record::$types['date-time'] as $field)

View File

@ -2523,7 +2523,6 @@ class calendar_uiforms extends calendar_ui
$date =& $this->bo->so->startOfDay($date); $date =& $this->bo->so->startOfDay($date);
$date->setUser(); $date->setUser();
} }
error_log("Loading event for " . $date);
$event = $this->bo->read($eventId, $date, true); $event = $this->bo->read($eventId, $date, true);
$preserv['actual_date'] = $date; // remember the date clicked $preserv['actual_date'] = $date; // remember the date clicked
@ -2595,6 +2594,65 @@ class calendar_uiforms extends calendar_ui
} }
} }
/**
* Change the status via ajax
* @param string $eventId
* @param integer $user
* @param string $status
*/
function ajax_status($eventId, $uid, $status)
{
list($eventId, $date) = explode(':',$eventId);
$old_event=$event=$this->bo->read($eventId);
// If we have a recuring event for a particular day, make an exception
if ($event['recur_type'] != MCAL_RECUR_NONE && $date)
{
$date = new egw_time($date, egw_time::$user_timezone);
if (!empty($event['whole_day']))
{
$date =& $this->bo->so->startOfDay($date);
$date->setUser();
}
$event = $this->bo->read($eventId, $date, true);
$preserv['actual_date'] = $date; // remember the date clicked
// For DnD, always create an exception
$this->_create_exception($event,$preserv);
unset($event['id']);
$date = $date->format('ts');
}
if($event['participants'][$uid])
{
$q = $r = null;
calendar_so::split_status($event['participants'][$uid],$q,$r);
$event['participants'][$uid] = $status = calendar_so::combine_status($status,$q,$r);
$this->bo->set_status($event['id'],$uid,$status,0,true);
}
$conflicts=$this->bo->update($event);
$response = egw_json_response::get();
if(!is_array($conflicts))
{
// Directly update stored data. If event is still visible, it will
// be notified & update itself.
$this->to_client($event);
$response->call('egw.dataStoreUID','calendar::'.$event['id'].($date?':'.$date:''),$event);
}
else
{
$response->call(
'egw_openWindowCentered2',
$GLOBALS['egw_info']['server']['webserver_url'].'/index.php?menuaction=calendar.calendar_uiforms.edit
&cal_id='.$event['id']
.'&start='.$event['start']
.'&end='.$event['end']
.'&non_interactive=true'
.'&cancel_needs_refresh=true',
'',750,410);
}
}
/** /**
* imports a mail as Calendar * imports a mail as Calendar
* *

View File

@ -211,7 +211,7 @@ class calendar_uiviews extends calendar_ui
/** /**
* Show the last view or the default one, if no last * Show the last view or the default one, if no last
*/ */
function index($content) function index($content=array())
{ {
if($content['merge']) if($content['merge'])
{ {
@ -245,7 +245,6 @@ class calendar_uiviews extends calendar_ui
$GLOBALS['egw_info']['flags']['nonavbar'] = true; $GLOBALS['egw_info']['flags']['nonavbar'] = true;
$this->manage_states($_GET); $this->manage_states($_GET);
$old_calendar = $this->{$this->view}(); $old_calendar = $this->{$this->view}();
echo $old_calendar;
return; return;
} }
@ -493,6 +492,7 @@ class calendar_uiviews extends calendar_ui
$content .= html::a_href(html::image('phpgwapi','first',lang('previous'),$options=' alt="<<"'),array( $content .= html::a_href(html::image('phpgwapi','first',lang('previous'),$options=' alt="<<"'),array(
'menuaction' => $this->view_menuaction, 'menuaction' => $this->view_menuaction,
'date' => date('Ymd',strtotime('-1 year',strtotime($this->date))), 'date' => date('Ymd',strtotime('-1 year',strtotime($this->date))),
'view' => 'year'
)); ));
$content .= '</span>'."\n"; $content .= '</span>'."\n";
} }
@ -506,6 +506,7 @@ class calendar_uiviews extends calendar_ui
$content .= html::a_href(html::image('phpgwapi','last',lang('next'),$options=' alt=">>"'),array( $content .= html::a_href(html::image('phpgwapi','last',lang('next'),$options=' alt=">>"'),array(
'menuaction' => $this->view_menuaction, 'menuaction' => $this->view_menuaction,
'date' => date('Ymd',strtotime('+1 year',strtotime($this->date))), 'date' => date('Ymd',strtotime('+1 year',strtotime($this->date))),
'view' => 'year'
)); ));
$content .= '</span>'."\n"; $content .= '</span>'."\n";
} }
@ -2872,6 +2873,14 @@ class calendar_uiviews extends calendar_ui
'" data-date ="'.$this->bo->date2string($event['start']).'|'.$data['popup'].'">'."\n".$data['html'].$indent."</div>\n"; '" data-date ="'.$this->bo->date2string($event['start']).'|'.$data['popup'].'">'."\n".$data['html'].$indent."</div>\n";
} }
/**
* Get the actions for the non-list views
*
* We use the actions from the list as a base, and only change what we have to
* to get it to work outside of a nextmatch.
*
* @return Array
*/
protected static function get_actions() protected static function get_actions()
{ {
// Just copy from the list, but change to match our needs // Just copy from the list, but change to match our needs
@ -2889,10 +2898,11 @@ class calendar_uiviews extends calendar_ui
{ {
$action['enabled'] = 'javaScript:app.calendar.is_event'; $action['enabled'] = 'javaScript:app.calendar.is_event';
} }
//$action['disableClass'] = 'view_row';
//$action['hideOnDisabled'] = true;
} }
$actions['copy']['open'] = '{"app": "calendar", "type": "add", "extra": "cal_id=$id&action=copy"}';
$actions['copy']['onExecute'] = 'javaScript:app.calendar.action_open';
foreach($actions['status']['children'] as $id => &$status) foreach($actions['status']['children'] as $id => &$status)
{ {
$status = array( $status = array(
@ -2901,21 +2911,22 @@ class calendar_uiviews extends calendar_ui
'onExecute' => 'javaScript:app.calendar.status' 'onExecute' => 'javaScript:app.calendar.status'
); );
} }
/*
$actions['drag_calendar'] = array( if ($actions['filemanager'])
'dragType' => array('calendar'), {
'type' => 'drag', $actions['filemanager']['url'] = '/index.php?'. $actions['filemanager']['url'];
'enabled' => 'javaScript:app.calendar.is_event' $actions['filemanager']['onExecute'] = 'javaScript:app.calendar.action_open';
); }
/* if ($actions['infolog_app'])
Calendar DnD is handled internally {
$actions['drop_calendar'] = array( $actions['infolog_app']['open'] = '{"app": "infolog", "type": "add", "extra": "type=task&action=$app&action_id=$id"}';
'acceptedTypes' => array('calendar'), $actions['infolog_app']['onExecute'] = 'javaScript:app.calendar.action_open';
'type' => 'drop', }
'onExecute' => 'javaScript:app.calendar.move' if ($actions['timesheet'])
); {
* $actions['timesheet']['open'] = '{"app": "timesheet", "type": "add", "extra": "link_app[]=$app&link_id[]=$id"}';
*/ $actions['timesheet']['onExecute'] = 'javaScript:app.calendar.action_open';
}
return $actions; return $actions;
} }

View File

@ -18,6 +18,13 @@
/** /**
* UI for calendar * UI for calendar
* *
* Calendar has multiple different views of the same data. All the templates
* for the different view are loaded at the start, then the view objects
* in app.classes.calendar.views are used to manage the different views.
* update_state() is used to change the state between the different views.
*
* The event widgets and the nextmatch get the data from egw.data.
*
* @augments AppJS * @augments AppJS
*/ */
app.classes.calendar = AppJS.extend( app.classes.calendar = AppJS.extend(
@ -32,181 +39,6 @@ app.classes.calendar = AppJS.extend(
*/ */
sidebox_et2: null, sidebox_et2: null,
/**
* etemplates and settings for the different views some (day view)
* use more than one template, some use the same template as others,
* most need different handling for their various attributes.
*
* Attributes are setter: function to calculate value
*/
views: {
day: {
etemplates: ['calendar.view','calendar.todo'],
set_start_date: function(state) {
return state.date ? new Date(state.date) : new Date();
},
set_end_date: function(state) {
var d = state.date ? new Date(state.date) : new Date();
d.setUTCHours(23);
return d;
},
set_owner: function(state) {
return state.owner || 0;
},
set_show_weekend: function(state)
{
state.days = '1';
return parseInt(egw.preference('days_in_weekview','calendar')) == 7;
}
},
day4: {
etemplates: ['calendar.view'],
set_start_date: function(state) {
return state.date ? new Date(state.date) : new Date();
},
set_end_date: function(state) {
var d = state.date ? new Date(state.date) : new Date();
d.setUTCHours(24*4-1);
return d;
},
set_owner: function(state) {
return state.owner || 0;
},
set_show_weekend: function(state)
{
state.days = '4';
return parseInt(egw.preference('days_in_weekview','calendar')) == 7;
}
},
week: {
etemplates: ['calendar.view'],
set_start_date: function(state) {
return app.calendar.date.start_of_week(state.date || new Date());
},
set_end_date: function(state) {
var d = app.calendar.date.start_of_week(state.date || new Date());
// Always 7 days, we just turn weekends on or off
d.setUTCHours(24*7-1);
return d;
},
set_owner: function(state) {
return state.owner || 0;
},
set_show_weekend: function(state)
{
state.days = '' + (state.days >= 5 ? state.days : egw.preference('days_in_weekview','calendar') || 7);
return parseInt(state.days) == 7;
}
},
weekN: {
etemplates: ['calendar.view'],
set_start_date: function(state) {
return app.calendar.date.start_of_week(state.date || new Date());
},
set_end_date: function(state) {
var d = app.calendar.date.start_of_week(state.date || new Date());
// Always 7 days, we just turn weekends on or off
d.setUTCHours(24*7-1);
return d;
},
set_show_weekend: function(state)
{
state.days = '' + (state.days >= 5 ? state.days : egw.preference('days_in_weekview','calendar') || 7);
return parseInt(state.days) == 7;
}
},
month: {
etemplates: ['calendar.view'],
set_start_date: function(state) {
var d = state.date ? new Date(state.date) : new Date();
d.setUTCDate(1);
d.setUTCHours(0);
d.setUTCMinutes(0);
d.setUTCSeconds(0);
state.date = d.toJSON();
return app.calendar.date.start_of_week(d);
},
set_end_date: function(state) {
var d = state.date ? new Date(state.date) : new Date();
d = new Date(d.getFullYear(),d.getUTCMonth() + 1, 0);
var week_start = app.calendar.date.start_of_week(d);
if(week_start < d) week_start.setUTCHours(24*7);
week_start.setUTCHours(week_start.getUTCHours()-1);
return week_start;
},
},
planner: {
etemplates: ['calendar.planner'],
set_group_by: function(state) {
return state.cat_id? state.cat_id : (state.sortby ? state.sortby : 0);
},
set_start_date: function(state) {
var d = state.date ? new Date(state.date) : new Date();
if(state.sortby && state.sortby === 'month')
{
d.setUTCDate(1);
}
else if (!state.planner_days)
{
if(d.getUTCDate() < 15)
{
d.setUTCDate(1);
return app.calendar.date.start_of_week(d);
}
else
{
return app.calendar.date.start_of_week(d);
}
}
return d;
},
set_end_date: function(state) {
var d = state.date ? new Date(state.date) : new Date();
if(state.sortby && state.sortby === 'month')
{
d.setUTCDate(0);
d.setUTCFullYear(d.getUTCFullYear() + 1);
}
else if (state.planner_days)
{
d.setUTCDate(d.getUTCDate() + parseInt(state.planner_days)-1);
}
else if (app.calendar.state.last)
{
d = new Date(app.calendar.state.last);
}
else if (!state.planner_days)
{
if (d.getUTCDate() < 15)
{
d.setUTCDate(0);
d.setUTCMonth(d.getUTCMonth()+1);
d = app.calendar.date.end_of_week(d);
}
else
{
d.setUTCMonth(d.getUTCMonth()+1);
d = app.calendar.date.end_of_week(d);
}
}
return d;
},
set_owner: function(state) {
return state.owner || 0;
}
},
listview: {
etemplates: ['calendar.list'],
set_start_date: function(state)
{
var d = state.date ? new Date(state.date) : new Date();
return d;
}
}
},
/** /**
* Current internal state * Current internal state
*/ */
@ -327,23 +159,23 @@ app.classes.calendar = AppJS.extend(
var hidden = typeof this.state.view !== 'undefined'; var hidden = typeof this.state.view !== 'undefined';
var all_loaded = true; var all_loaded = true;
// Record the templates for the views so we can switch between them // Record the templates for the views so we can switch between them
for(var view in this.views) for(var view in app.classes.calendar.views)
{ {
var index = this.views[view].etemplates.indexOf(_name) var index = app.classes.calendar.views[view].etemplates.indexOf(_name)
if(index > -1) if(index > -1)
{ {
this.views[view].etemplates[index] = _et2; app.classes.calendar.views[view].etemplates[index] = _et2;
// If a template disappears, we want to release it // If a template disappears, we want to release it
$j(_et2.DOMContainer).one('clear',jQuery.proxy(function() { $j(_et2.DOMContainer).one('clear',jQuery.proxy(function() {
this.view[index] = _name; this.view[index] = _name;
},{view: this.views[view], index: index, name: _name})); },{view: app.classes.calendar.views[view], index: index, name: _name}));
if(this.state.view === view) if(this.state.view === view)
{ {
hidden = false; hidden = false;
} }
} }
this.views[view].etemplates.forEach(function(et) {all_loaded = all_loaded && typeof et !== 'string';}); app.classes.calendar.views[view].etemplates.forEach(function(et) {all_loaded = all_loaded && typeof et !== 'string';});
} }
// Start hidden, except for current view // Start hidden, except for current view
@ -445,10 +277,9 @@ app.classes.calendar = AppJS.extend(
{ {
return _url.replace(/menuaction=[^&]+/, 'menuaction=calendar.calendar_uilist.listview&ajax=true'); return _url.replace(/menuaction=[^&]+/, 'menuaction=calendar.calendar_uilist.listview&ajax=true');
} }
else if (this.sidebox_et2 && typeof this.views[state.view] == 'undefined') else if (this.sidebox_et2 && typeof app.classes.calendar.views[state.view] == 'undefined')
{ {
this.sidebox_et2.getWidgetById('iframe').set_src(_url); this.sidebox_et2.getWidgetById('iframe').set_src(_url);
this.sidebox
return true; return true;
} }
} }
@ -461,89 +292,6 @@ app.classes.calendar = AppJS.extend(
return false; return false;
}, },
/**
* Drag and Drop
*
*
*/
drag_n_drop: function()
{
var that = this;
//jQuery Calendar Event selector
var $iframeBody = jQuery("body")
//mouseover event handler for calendar tooltip
.on("mouseover", "div[data-tooltip]",function(){
var $ttp = jQuery(this);
//Check if the tooltip is already initialized
if (!$ttp.data('uiTooltip'))
{
$ttp.tooltip({
items: "[data-tooltip]",
show: false,
content: function()
{
var elem = $ttp;
if (elem.is("[data-tooltip]"))
return this.getAttribute('data-tooltip') ;
},
track:true,
open: function(event,ui){
ui.tooltip.removeClass("ui-tooltip");
ui.tooltip.addClass("calendar_uitooltip");
if (this.scrollHeight > this.clientHeight)
{
// bind on tooltip close event
$ttp.on("tooltipclose", function (event, ui){
// bind hover handler on tooltip helper in order to be able to freeze the tooltip and scrolling
ui.tooltip.hover(
function () {
var $ttp_helper = jQuery(this);
if (this.scrollHeight > this.clientHeight) $ttp_helper.stop(true).fadeTo(100, 1);
},
function () {
var $ttp_helper = jQuery(this);
$ttp_helper.fadeOut("100", function(){$ttp_helper.remove();});
}
);
});
}
}
});
}
else
{
$ttp.tooltip('enable');
}
})
// mousedown event handler for calendar tooltip to remove disable tooltip
.on("mousedown", "div[data-tooltip]", function(){
var $ttp = jQuery(this);
// Make sure the tooltip initialized before calling it
if ($ttp.data('uiTooltip'))
{
$ttp.tooltip("disable");
}
})
//Click event handler for integrated apps
.on("click","div.calendar_plannerEvent",function(ev){
var eventId = ev.currentTarget.getAttribute('data-date').split("|")[1];
var startDate = ev.currentTarget.getAttribute('data-date').split("|")[0];
var recurrFlag = ev.currentTarget.getAttribute('data-date').split("|")[2];
if (recurrFlag == "n")
{
egw.open(eventId,'calendar','edit');
}
else
{
that.edit_series(eventId,startDate);
}
})
},
/** /**
* Setup and handle sortable calendars. * Setup and handle sortable calendars.
* *
@ -590,7 +338,7 @@ app.classes.calendar = AppJS.extend(
start: function () start: function ()
{ {
// Put owners into row IDs // Put owners into row IDs
app.calendar.views[state.view].etemplates[0].widgetContainer.iterateOver(function(widget) { app.classes.calendar.views[state.view].etemplates[0].widgetContainer.iterateOver(function(widget) {
widget.div.parents('tr').attr('data-owner',widget.options.owner); widget.div.parents('tr').attr('data-owner',widget.options.owner);
},this,et2_calendar_timegrid) },this,et2_calendar_timegrid)
}, },
@ -638,18 +386,18 @@ app.classes.calendar = AppJS.extend(
var end = null; var end = null;
// Get the view to calculate // Get the view to calculate
if (app.calendar.views && app.calendar.state.view && app.calendar.views[app.calendar.state.view].set_end_date) var view = app.classes.calendar.views[app.calendar.state.view] || false;
if (view)
{ {
if(direction > 0) if(direction > 0)
{ {
start = app.calendar.views[app.calendar.state.view].set_end_date({date:start}); start = view.end_date({date:start});
} }
else else
{ {
start = app.calendar.views[app.calendar.state.view].set_start_date({date:start}); start = view.start_date({date:start});
} }
start.setUTCDate(start.getUTCDate()+direction); start.setUTCDate(start.getUTCDate()+direction);
end = app.calendar.views[app.calendar.state.view].set_end_date({date:start});
} }
// Calculate the current difference, and move // Calculate the current difference, and move
else if(app.calendar.state.first && app.calendar.state.last) else if(app.calendar.state.first && app.calendar.state.last)
@ -663,7 +411,7 @@ app.classes.calendar = AppJS.extend(
end = new Date(end.valueOf() + (delta * direction)); end = new Date(end.valueOf() + (delta * direction));
} }
app.calendar.update_state({date: start}); app.calendar.update_state({date:app.calendar.date.toString(start)});
return false; return false;
} }
@ -1100,6 +848,31 @@ app.classes.calendar = AppJS.extend(
} }
}, },
/**
* Application links from non-list events
*
* @param {egwAction} _action
* @param {egwActionObject[]} _events
*/
action_open: function(_action, _events)
{
var id = _events[0].id.split('::');
if(_action.data.open)
{
var open = JSON.parse(_action.data.open) || {};
var extra = open.extra || '';
extra = extra.replace(/(\$|%24)app/,id[0]).replace(/(\$|%24)id/,id[1]);
this.egw.open(open.id_data||'',open.app,open.type,extra);
}
else if (_action.data.url)
{
var url = _action.data.url;
url = url.replace(/(\$|%24)app/,id[0]).replace(/(\$|%24)id/,id[1]);
this.egw.open_link(url);
}
},
/** /**
* Change status (via AJAX) * Change status (via AJAX)
* *
@ -1119,11 +892,17 @@ app.classes.calendar = AppJS.extend(
switch(button_id) switch(button_id)
{ {
case 'exception': case 'exception':
egw().json(
'calendar.calendar_uiforms.ajax_status',
[event_data.app_id, egw.user('account_id'), _action.data.id]
).sendRequest(true);
break; break;
case 'series': case 'series':
case 'single': case 'single':
this.egw.open(event_data.id, event_data.app||'calendar', 'edit', {date:event_data.start}); egw().json(
'calendar.calendar_uiforms.ajax_status',
[event_data.id, egw.user('account_id'), _action.data.id]
).sendRequest(true);
break; break;
case 'cancel': case 'cancel':
default: default:
@ -1163,7 +942,7 @@ app.classes.calendar = AppJS.extend(
_action.data.url = _action.data.url.replace(/(\$|%24)id/,id); _action.data.url = _action.data.url.replace(/(\$|%24)id/,id);
_action.data.url = _action.data.url.replace(/(\$|%24)app/,app); _action.data.url = _action.data.url.replace(/(\$|%24)app/,app);
nm_action(_action, _senders); nm_action(_action, _senders,false,{ids:[id]});
_action.data.url = backup_url; // restore url _action.data.url = backup_url; // restore url
}, },
@ -1179,7 +958,7 @@ app.classes.calendar = AppJS.extend(
*/ */
cal_open: function(_action, _senders) cal_open: function(_action, _senders)
{ {
var js_integration_data = _action.parent.data.nextmatch.options.settings.js_integration_data || this.et2.getArrayMgr('content').data.nm.js_integration_data; var js_integration_data = _action.parent.data.nextmatch.options.settings.js_integration_data || this.et2.getArrayMgr('content').data.nm.js_integration_data;
var id = _senders[0].id; var id = _senders[0].id;
var matches = id.match(/^(?:calendar::)?([0-9]+):([0-9]+)$/); var matches = id.match(/^(?:calendar::)?([0-9]+):([0-9]+)$/);
@ -1381,7 +1160,7 @@ app.classes.calendar = AppJS.extend(
} }
var changed = []; var changed = [];
var new_state = jQuery.extend({}, this.state); var new_state = jQuery.extend({}, this.state);
if (typeof _set == 'object') if (typeof _set === 'object')
{ {
for(var s in _set) for(var s in _set)
{ {
@ -1457,7 +1236,7 @@ app.classes.calendar = AppJS.extend(
state = JSON.parse(state); state = JSON.parse(state);
} }
} }
if(typeof state.state != 'object' || !state.state.view) if(typeof state.state !== 'object' || !state.state.view)
{ {
state.state = {view: 'week'}; state.state = {view: 'week'};
} }
@ -1466,15 +1245,14 @@ app.classes.calendar = AppJS.extend(
state.state.date = new Date(); state.state.date = new Date();
} }
// Hide other views // Hide other views
for(var _view in this.views) for(var _view in app.classes.calendar.views)
{ {
if(state.state.view != _view && this.views[_view]) if(state.state.view != _view && app.classes.calendar.views[_view])
{ {
for(var i = 0; i < this.views[_view].etemplates.length; i++) for(var i = 0; i < app.classes.calendar.views[_view].etemplates.length; i++)
{ {
$j(this.views[_view].etemplates[i].DOMContainer).hide(); $j(app.classes.calendar.views[_view].etemplates[i].DOMContainer).hide();
} }
} }
} }
@ -1484,9 +1262,9 @@ app.classes.calendar = AppJS.extend(
} }
// Check for a supported client-side view // Check for a supported client-side view
if(this.views[state.state.view] && if(app.classes.calendar.views[state.state.view] &&
// Check that the view is instanciated // Check that the view is instanciated
typeof this.views[state.state.view].etemplates[0] !== 'string' && this.views[state.state.view].etemplates[0].widgetContainer typeof app.classes.calendar.views[state.state.view].etemplates[0] !== 'string' && app.classes.calendar.views[state.state.view].etemplates[0].widgetContainer
) )
{ {
// Doing an update - this includes the selected view, and the sidebox // Doing an update - this includes the selected view, and the sidebox
@ -1494,7 +1272,7 @@ app.classes.calendar = AppJS.extend(
// cause infinite loops. // cause infinite loops.
this.state_update_in_progress = true; this.state_update_in_progress = true;
var view = this.views[state.state.view]; var view = app.classes.calendar.views[state.state.view];
// Sanitize owner // Sanitize owner
switch(typeof state.state.owner) switch(typeof state.state.owner)
@ -1534,7 +1312,7 @@ app.classes.calendar = AppJS.extend(
state.state.view === 'month' ? 0 : // Calculate based on weeks in the month state.state.view === 'month' ? 0 : // Calculate based on weeks in the month
state.state.owner.length > (this.egw.config('calview_no_consolidate','phpgwapi') || 5) ? 1 : state.state.owner.length; state.state.owner.length > (this.egw.config('calview_no_consolidate','phpgwapi') || 5) ? 1 : state.state.owner.length;
var grid = this.views[this.state.view] ? this.views[this.state.view].etemplates[0].widgetContainer.getWidgetById('view') : false; var grid = view.etemplates[0].widgetContainer.getWidgetById('view');
/* /*
If the count is different, we need to have the correct number (just remove all & re-create) If the count is different, we need to have the correct number (just remove all & re-create)
@ -1545,7 +1323,7 @@ app.classes.calendar = AppJS.extend(
{ {
// Need to redo the number of grids // Need to redo the number of grids
var value = []; var value = [];
state.state.first = view.set_start_date(state.state).toJSON(); state.state.first = view.start_date(state.state).toJSON();
// We'll modify this one, so it needs to be a new object // We'll modify this one, so it needs to be a new object
var date = new Date(state.state.first); var date = new Date(state.state.first);
@ -1553,7 +1331,7 @@ app.classes.calendar = AppJS.extend(
switch(state.state.view) switch(state.state.view)
{ {
case 'month': case 'month':
var end = state.state.last = view.set_end_date(state.state); var end = state.state.last = view.end_date(state.state);
grid_count = Math.ceil((end - date) / (1000 * 60 * 60 * 24) / 7); grid_count = Math.ceil((end - date) / (1000 * 60 * 60 * 24) / 7);
// fall through // fall through
case 'weekN': case 'weekN':
@ -1572,7 +1350,7 @@ app.classes.calendar = AppJS.extend(
state.state.last=val.end_date.toJSON(); state.state.last=val.end_date.toJSON();
break; break;
default: default:
var end = state.state.last = view.set_end_date(state.state); var end = state.state.last = view.end_date(state.state);
for(var owner = 0; owner < grid_count && owner < state.state.owner.length; owner++) for(var owner = 0; owner < grid_count && owner < state.state.owner.length; owner++)
{ {
value.push({ value.push({
@ -1600,16 +1378,16 @@ app.classes.calendar = AppJS.extend(
if(typeof view[updater] === 'function') if(typeof view[updater] === 'function')
{ {
var value = view[updater].call(this,state.state); var value = view[updater].call(this,state.state);
if(updater === 'set_start_date') state.state.first = value.toJSON(); if(updater === 'start_date') state.state.first = this.date.toString(value);
if(updater === 'set_end_date') state.state.last = value.toJSON(); if(updater === 'end_date') state.state.last = this.date.toString(value);
// Set value // Set value
for(var i = 0; i < view.etemplates.length; i++) for(var i = 0; i < view.etemplates.length; i++)
{ {
view.etemplates[i].widgetContainer.iterateOver(function(widget) { view.etemplates[i].widgetContainer.iterateOver(function(widget) {
if(typeof widget[updater] === 'function') if(typeof widget['set_'+updater] === 'function')
{ {
widget[updater](value); widget['set_'+updater](value);
} }
}, this, et2_valueWidget); }, this, et2_valueWidget);
} }
@ -1649,11 +1427,15 @@ app.classes.calendar = AppJS.extend(
} }
this.state = jQuery.extend({},state.state); this.state = jQuery.extend({},state.state);
var formatDate = new Date(this.state.date);
formatDate = new Date(formatDate.valueOf() + formatDate.getTimezoneOffset() * 60 * 1000);
egw_app_header(view.header(state.state),'calendar');
if(state.state.view === 'listview') if(state.state.view === 'listview')
{ {
state.state.startdate = state.state.date; state.state.startdate = state.state.date;
state.state.col_filter = {participant: state.state.owner}; state.state.col_filter = {participant: state.state.owner};
var nm = this.views[_view].etemplates[0].widgetContainer.getWidgetById('nm'); var nm = view.etemplates[0].widgetContainer.getWidgetById('nm');
nm.applyFilters(state.state); nm.applyFilters(state.state);
} }
@ -1771,7 +1553,10 @@ app.classes.calendar = AppJS.extend(
} }
// setting internal state now, that linkHandler does not intercept switching from listview to any old view // setting internal state now, that linkHandler does not intercept switching from listview to any old view
this.state = jQuery.extend({},state.state); this.state = jQuery.extend({},state.state);
$j(this.sidebox_et2.getInstanceManager().DOMContainer).show(); if(this.sidebox_et2)
{
$j(this.sidebox_et2.getInstanceManager().DOMContainer).show();
}
var query = jQuery.extend({menuaction: menuaction},state.state||{}); var query = jQuery.extend({menuaction: menuaction},state.state||{});
@ -1911,6 +1696,18 @@ app.classes.calendar = AppJS.extend(
* All take either a Date object or full date with timestamp (Z) * All take either a Date object or full date with timestamp (Z)
*/ */
date: { date: {
toString: function(date)
{
// Ensure consistent formatting using UTC, avoids problems with comparison
// and timezones
if(typeof date === 'string') date = new Date(date);
return date.getUTCFullYear() +'-'+
sprintf("%02d",date.getUTCMonth()+1) + '-'+
sprintf("%02d",date.getUTCDate()) + 'T'+
sprintf("%02d",date.getUTCHours()) + ':'+
sprintf("%02d",date.getUTCMinutes()) + ':'+
sprintf("%02d",date.getUTCSeconds()) + 'Z';
},
start_of_week: function(date) start_of_week: function(date)
{ {
var d = new Date(date); var d = new Date(date);
@ -1976,5 +1773,235 @@ app.classes.calendar = AppJS.extend(
app.calendar.update_state({view: 'week', date: date.getValue()}); app.calendar.update_state({view: 'week', date: date.getValue()});
}); });
},
View: {
// List of etemplates to show for this view
etemplates: ['calendar.view'],
/**
* Translated label for header
* @param {Object} state
* @returns {string}
*/
header: function(state) {
var formatDate = new Date(state.date);
formatDate = new Date(formatDate.valueOf() + formatDate.getTimezoneOffset() * 60 * 1000);
return date(egw.preference('dateformat'),formatDate);
},
/**
* Get the start date for this view
* @param {Object} state
* @returns {Date}
*/
start_date: function(state) {
var d = state.date ? new Date(state.date) : new Date();
d.setUTCHours(0);
d.setUTCMinutes(0);
d.setUTCSeconds(0);
d.setUTCMilliseconds(0);
return d;
},
/**
* Get the end date for this view
* @param {Object} state
* @returns {Date}
*/
end_date: function(state) {
var d = state.date ? new Date(state.date) : new Date();
d.setUTCHours(23);
d.setUTCMinutes(59);
d.setUTCSeconds(59);
d.setUTCMilliseconds(0);
return d;
},
owner: function(state) {
return state.owner || 0;
},
show_weekend: function(state)
{
return parseInt(egw.preference('days_in_weekview','calendar')) == 7;
},
extend: function(sub)
{
return jQuery.extend({},this,{_super:this},sub);
}
} }
}); });
/**
* etemplates and settings for the different views some (day view)
* use more than one template, some use the same template as others,
* most need different handling for their various attributes.
*
* Not using the standard Class.extend here because it hides the members,
* and we want to be able to look inside them. This is done seperately instead
* of inside the normal object to allow access to the View object.
*/
jQuery.extend(app.classes.calendar,{
views: {
day: app.classes.calendar.prototype.View.extend({
header: function(state) {
return egw.lang('Day view') + ': ' + app.calendar.View.header.call(this, state);
},
etemplates: ['calendar.view','calendar.todo'],
start_date: function(state) {
var d = app.calendar.View.start_date.call(this, state);
state.date = app.calendar.date.toString(d);
return d;
},
show_weekend: function(state) {
state.days = '1';
return app.calendar.View.show_weekend.call(this,state);
}
}),
day4: app.classes.calendar.prototype.View.extend({
header: function(state) {
return egw.lang('Four days view') + ': ' + app.calendar.View.header.call(this, state);
},
end_date: function(state) {
var d = app.calendar.View.end_date.call(this,state);
state.days = '4';
d.setUTCHours(24*4-1);
d.setUTCMinutes(59);
d.setUTCSeconds(59);
d.setUTCMilliseconds(0);
return d;
}
}),
week: app.classes.calendar.prototype.View.extend({
header: function(state) {
return egw.lang('Week view') + ': ' + app.calendar.View.header.call(this, state);
},
start_date: function(state) {
return app.calendar.date.start_of_week(state.date || new Date());
},
end_date: function(state) {
var d = app.calendar.date.start_of_week(state.date || new Date());
// Always 7 days, we just turn weekends on or off
d.setUTCHours(24*7-1);
d.setUTCMinutes(59);
d.setUTCSeconds(59);
d.setUTCMilliseconds(0);
state.days = '' + (state.days >= 5 ? state.days : egw.preference('days_in_weekview','calendar') || 7);
return d;
},
show_weekend: function(state)
{
return parseInt(state.days) === 7;
}
}),
weekN: app.classes.calendar.prototype.View.extend({
header: function(state) {
return egw.lang('Multiple week view') + ': ' + app.calendar.View.header.call(this, state);
},
start_date: function(state) {
return app.calendar.date.start_of_week(state.date || new Date());
},
end_date: function(state) {
state.days = '' + (state.days >= 5 ? state.days : egw.preference('days_in_weekview','calendar') || 7);
var d = app.calendar.date.start_of_week(state.date || new Date());
// Always 7 days, we just turn weekends on or off
d.setUTCHours(24*7-1);
return d;
},
}),
month: app.classes.calendar.prototype.View.extend({
header: function(state)
{
var formatDate = new Date(state.date);
formatDate = new Date(formatDate.valueOf() + formatDate.getTimezoneOffset() * 60 * 1000);
return egw.lang('Month view') + ':' + egw.lang(date('F',formatDate)) + ' ' + date('Y',formatDate);
},
start_date: function(state) {
var d = app.calendar.View.start_date.call(this,state)
d.setUTCDate(1);
state.date = app.calendar.date.toString(d);
return app.calendar.date.start_of_week(d);
},
end_date: function(state) {
var d = app.calendar.View.end_date.call(this,state)
d = new Date(d.getFullYear(),d.getUTCMonth() + 1, 0);
var week_start = app.calendar.date.start_of_week(d);
if(week_start < d) week_start.setUTCHours(24*7);
week_start.setUTCHours(week_start.getUTCHours()-1);
return week_start;
},
}),
planner: app.classes.calendar.prototype.View.extend({
header: function(state) {
var startDate = new Date(state.first);
startDate = new Date(startDate.valueOf() + startDate.getTimezoneOffset() * 60 * 1000);
var endDate = new Date(state.last);
endDate = new Date(endDate.valueOf() + endDate.getTimezoneOffset() * 60 * 1000);
return egw.lang('Planner view') + ': ' + date(egw.preference('dateformat'),startDate) +
' - ' + date(egw.preference('dateformat'),endDate);
},
etemplates: ['calendar.planner'],
group_by: function(state) {
return state.cat_id? state.cat_id : (state.sortby ? state.sortby : 0);
},
start_date: function(state) {
var d = app.calendar.View.start_date.call(this, state);
if(state.sortby && state.sortby === 'month')
{
d.setUTCDate(1);
}
else if (!state.planner_days)
{
if(d.getUTCDate() < 15)
{
d.setUTCDate(1);
return app.calendar.date.start_of_week(d);
}
else
{
return app.calendar.date.start_of_week(d);
}
}
return d;
},
end_date: function(state) {
var d = app.calendar.View.end_date.call(this, state);
if(state.sortby && state.sortby === 'month')
{
d.setUTCDate(0);
d.setUTCFullYear(d.getUTCFullYear() + 1);
}
else if (state.planner_days)
{
d.setUTCDate(d.getUTCDate() + parseInt(state.planner_days)-1);
}
else if (app.calendar.state.last)
{
d = new Date(app.calendar.state.last);
}
else if (!state.planner_days)
{
if (d.getUTCDate() < 15)
{
d.setUTCDate(0);
d.setUTCMonth(d.getUTCMonth()+1);
d = app.calendar.date.end_of_week(d);
}
else
{
d.setUTCMonth(d.getUTCMonth()+1);
d = app.calendar.date.end_of_week(d);
}
}
return d;
}
}),
listview: app.classes.calendar.prototype.View.extend({
header: function() {return egw.lang('List view');},
etemplates: ['calendar.list']
})
}}
);

View File

@ -285,7 +285,7 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM],
} }
if(this.options.value.participants[egw.user('account_id')] && this.options.value.participants[egw.user('account_id')][0] == 'U') 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('cnr-pending','calendar')+'" title="'+lang('Needs action')+'"/>'); icons.push('<img src="'+this.egw().image('cnr-pending','calendar')+'" title="'+this.egw().lang('Needs action')+'"/>');
} }
return icons; return icons;
}, },

View File

@ -604,57 +604,27 @@ var et2_calendar_planner = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResize
title += ' '+t.getUTCFullYear(); title += ' '+t.getUTCFullYear();
// previous links // previous links
/* var prev = new Date(t);
$prev = $t_arr; prev.setUTCDate(1);
$prev['day'] = 1; prev.setUTCMonth(prev.getUTCMonth()-1);
if ($prev['month']-- <= 1)
{ var full = prev.toJSON();
$prev['month'] = 12; prev.setUTCDate(start.getUTCDate());
$prev['year']--; if (prev.getUTCDate() >= 15) prev = new Date(t); // we stay in the same month
} prev.setUTCDate(start.getUTCDate() < 15 ? 15 : 1);
if ($this->bo->date2ts($prev) < $start-20*DAY_s) var half = prev.toJSON();
{ title = this._scroll_button('first',full) + this._scroll_button('left',half) + title;
$prev['day'] = $this->day;
$full = $this->bo->date2string($prev);
if ($this->day >= 15) $prev = $t_arr; // we stay in the same month
$prev['day'] = $this->day < 15 ? 15 : 1;
$half = $this->bo->date2string($prev);
$title = html::a_href(html::image('phpgwapi','first',lang('back one month'),$options=' alt="<<"'),array(
'menuaction' => $this->view_menuaction,
'date' => $full,
)) . ' &nbsp; '.
html::a_href(html::image('phpgwapi','left',lang('back half a month'),$options=' alt="<"'),array(
'menuaction' => $this->view_menuaction,
'date' => $half,
)) . ' &nbsp; '.$title;
}
// next links // next links
$next = $t_arr; var next = new Date(t);
if ($next['month']++ >= 12) next.setUTCMonth(next.getUTCMonth()+1);
{ next.setUTCDate(start.getUTCDate() < 15 ? 15 : 1);
$next['month'] = 1; half = next.toJSON();
$next['year']++; next.setUTCMonth(next.getUTCMonth()+1);
} full = next.toJSON();
// dont show next scales, if there are more then 10 days in the next month or there is no next month
$days_in_next_month = (int) date('d',$end = $start+$days*DAY_s); title += this._scroll_button('right',half) + this._scroll_button('last',full);
if ($days_in_next_month <= 10 || date('m',$end) == date('m',$t))
{
if ($this->day >= 15) $next = $t_arr; // we stay in the same month
$next['day'] = $this->day;
$full = $this->bo->date2string($next);
if ($this->day < 15) $next = $t_arr; // we stay in the same month
$next['day'] = $this->day < 15 ? 15 : 1;
$half = $this->bo->date2string($next);
$title .= ' &nbsp; '.html::a_href(html::image('phpgwapi','right',lang('forward half a month'),$options=' alt=">>"'),array(
'menuaction' => $this->view_menuaction,
'date' => $half,
)). ' &nbsp; '.
html::a_href(html::image('phpgwapi','last',lang('forward one month'),$options=' alt=">>"'),array(
'menuaction' => $this->view_menuaction,
'date' => $full,
));
}
*/
} }
else else
{ {
@ -840,9 +810,9 @@ var et2_calendar_planner = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResize
* Create a pagination button, and inserts it * Create a pagination button, and inserts it
* *
*/ */
_scroll_button: function() _scroll_button: function(image, date)
{ {
return '<img class="et2_clickable" src="' + egw.image(image)+ '" data-date="' + (date.toJSON ? date.toJSON():date) + '"/>';
}, },
/** /**
@ -1339,7 +1309,6 @@ var et2_calendar_planner = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResize
else if (!jQuery.isEmptyObject(_ev.target.dataset)) else if (!jQuery.isEmptyObject(_ev.target.dataset))
{ {
// Click on a header, we can go there // Click on a header, we can go there
debugger
_ev.data = jQuery.extend({},_ev.target.parentNode.dataset, _ev.target.dataset); _ev.data = jQuery.extend({},_ev.target.parentNode.dataset, _ev.target.dataset);
this.change(_ev); this.change(_ev);
} }

View File

@ -188,97 +188,6 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
return this.dropEnd; return this.dropEnd;
}; };
this.div.on('mouseover', '.calendar_calEvent:not(.ui-resizable):not(.rowNoEdit)', function() {
// Load the event
timegrid._get_event_info(this);
var that = this;
//Resizable event handler
$j(this).resizable
({
distance: 10,
grid: [10000,timegrid.rowHeight],
autoHide: true,
handles: 's,se',
containment:'parent',
/**
* Triggered when the resizable is created.
*
* @param {event} event
* @param {Object} ui
*/
create:function(event, ui)
{
var resizeHelper = event.target.getAttribute('data-resize');
if (resizeHelper == 'WD' || resizeHelper == 'WDS')
{
jQuery(this).resizable('destroy');
}
},
/**
* Triggered at start of resizing a calEvent
*
* @param {event} event
* @param {Object} ui
*/
start:function(event, ui)
{
this.dropStart = timegrid._get_time_from_position(ui.element[0].getBoundingClientRect().left,ui.element[0].getBoundingClientRect().top).last();
this.dropDate = timegrid._get_event_info(this).start;
},
/**
* Triggered at the end of resizing the calEvent.
*
* @param {event} event
* @param {Object} ui
*/
stop:function(event, ui)
{
var e = new jQuery.Event('change');
e.originalEvent = event;
e.data = {duration: 0};
var event_data = timegrid._get_event_info(this);
var event_widget = timegrid.getWidgetById(event_data.id);
var sT = parseInt(this.dropStart.attr('data-hour'))* 60 + parseInt(this.dropStart.attr('data-minute'));
if (typeof this.dropEnd != 'undefined' && this.dropEnd.length == 1)
{
var eT = parseInt(this.dropEnd.attr('data-hour') * 60) + parseInt(this.dropEnd.attr('data-minute'));
e.data.duration = ((eT - sT)/60) * 3600;
if(event_widget)
{
event_widget.options.value.duration = e.data.duration;
}
$j(this).trigger(e);
// That cleared the resize handles, so remove for re-creation...
$j(this).resizable('destroy');
}
// Clear the helper, re-draw
event_widget.set_value(event_widget.options.value);
},
/**
* Triggered during the resize, on the drag of the resize handler
*
* @param {event} event
* @param {Object} ui
*/
resize:function(event, ui)
{
// Add 5px to make sure it doesn't land right on the edge of a div
drag_helper.call(this,event,ui.element[0],ui.helper.outerHeight()+5);
}
});
});
// Customize and override some draggable settings // Customize and override some draggable settings
this.div.on('dragcreate','.calendar_calEvent:not(.rowNoEdit)', function(event,ui) { this.div.on('dragcreate','.calendar_calEvent:not(.rowNoEdit)', function(event,ui) {
$j(this).draggable('option','cursorAt',false); $j(this).draggable('option','cursorAt',false);
@ -1192,7 +1101,7 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
* Set which user owns this. Owner is passed along to the individual * Set which user owns this. Owner is passed along to the individual
* days. * days.
* *
* @param {number} _owner Account ID * @param {number|number[]} _owner Account ID
* @returns {undefined} * @returns {undefined}
*/ */
set_owner: function(_owner) set_owner: function(_owner)
@ -1203,7 +1112,11 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
this.owner.set_value(typeof _owner == "string" || typeof _owner == "number" ? _owner : jQuery.extend([],_owner)); this.owner.set_value(typeof _owner == "string" || typeof _owner == "number" ? _owner : jQuery.extend([],_owner));
this.options.owner = _owner;//this.owner.getValue(); this.options.owner = _owner;//this.owner.getValue();
if(old !== this.options.owner && this.isAttached()) if(this.isAttached() && (
typeof old == "number" && typeof _owner == "number" && old !== this.options.owner ||
// Array of ids will not compare as equal
((typeof old === 'object' || typeof _owner === 'object') && old.toString() !== _owner.toString())
))
{ {
this.invalidate(true); this.invalidate(true);
} }

View File

@ -30,7 +30,7 @@ Egroupware
<buttononly align="center" class="sideboxstar" id="day" image="today" label="Today" onclick="app.calendar.update_state({view:'day',date:new Date()});"/> <buttononly align="center" class="sideboxstar" id="day" image="today" label="Today" onclick="app.calendar.update_state({view:'day',date:new Date()});"/>
<buttononly align="center" class="sideboxstar" id="week" image="week" label="Weekview" onclick="app.calendar.update_state({view:'week'});"/> <buttononly align="center" class="sideboxstar" id="week" image="week" label="Weekview" onclick="app.calendar.update_state({view:'week'});"/>
<buttononly align="center" class="sideboxstar" id="weekN" image="multiweek" label="Multiple week view" onclick="app.calendar.update_state({view:'weekN'});"/> <buttononly align="center" class="sideboxstar" id="weekN" image="multiweek" label="Multiple week view" onclick="app.calendar.update_state({view:'weekN'});"/>
<buttononly align="center" class="sideboxstar" id="month" image="month" label="Multiple week view" onclick="app.calendar.update_state({view:'month'});"/> <buttononly align="center" class="sideboxstar" id="month" image="month" label="Month view" onclick="app.calendar.update_state({view:'month'});"/>
<buttononly align="center" class="sideboxstar" id="planner" image="planner" label="Group planner" onclick="app.calendar.update_state({view:'planner',planner_days:0});"/> <buttononly align="center" class="sideboxstar" id="planner" image="planner" label="Group planner" onclick="app.calendar.update_state({view:'planner',planner_days:0});"/>
<buttononly align="center" class="sideboxstar" id="listview" image="list" label="Listview" onclick="app.calendar.update_state({view:'listview'});"/> <buttononly align="center" class="sideboxstar" id="listview" image="list" label="Listview" onclick="app.calendar.update_state({view:'listview'});"/>
</row> </row>