diff --git a/calendar/inc/class.calendar_hooks.inc.php b/calendar/inc/class.calendar_hooks.inc.php index 64f05366db..db4767dd2e 100644 --- a/calendar/inc/class.calendar_hooks.inc.php +++ b/calendar/inc/class.calendar_hooks.inc.php @@ -117,10 +117,11 @@ class calendar_hooks '1' => lang('Yes'), '0' => lang('No'), ); - $grid_views = array( - 'all' => lang('all'), - 'day_week' => lang('Dayview').', '.lang('Four days view').' & '.lang('Weekview'), - 'day4' => lang('Dayview').' & '.lang('Four days view'), + $list_views = array( + 0 => lang('None'), + 'weekN' => lang('Multiple week view'), + 'week' => lang('Weekview'), + 'day4' => lang('Four days view'), 'day' => lang('Dayview'), ); $updates = array( @@ -346,19 +347,16 @@ class calendar_hooks 'admin' => False, 'forced' => 'user', ), - //ATM:Disable the use_time_grid preference - //@TODO: the whole use_time_grid preference should be removed - // after we decided that is not neccessary to have it at all - /*'use_time_grid' => array( - 'type' => 'select', - 'label' => 'Views with fixed time intervals', + 'use_time_grid' => array( + 'type' => 'multiselect', + 'label' => 'Views showing a list of events', 'name' => 'use_time_grid', - 'values' => $grid_views, - 'help' => 'For which views should calendar show distinct lines with a fixed time interval.', + 'values' => $list_views, + 'help' => 'For which views should calendar just a list of events instead of distinct lines with a fixed time interval.', 'xmlrpc' => True, 'admin' => False, - 'forced' => 'all', - ),*/ + 'default' => ['weekN'], + ), 'link_title' => array( 'type' => 'multiselect', 'label' => 'Link title for events to show', diff --git a/calendar/js/app.js b/calendar/js/app.js index fe0525a0d4..bef38f4e26 100644 --- a/calendar/js/app.js +++ b/calendar/js/app.js @@ -2082,6 +2082,10 @@ app.classes.calendar = AppJS.extend( { widget.set_show_weekend(view.show_weekend(state.state)); } + if(widget.set_granularity) + { + widget.set_granularity(view.granularity(state.state)); + } if(widget.id == value[row_index].id && widget.get_end_date().toJSON() == value[row_index].end_date ) @@ -2097,16 +2101,6 @@ app.classes.calendar = AppJS.extend( widget.set_value(value[row_index++]); } },this, et2_calendar_view); - grid.iterateOver(function(widget) { - if(widget.set_granularity) - { - widget.set_granularity(view.granularity(state.state)); - } - if(widget.resize) - { - widget.resize(); - } - },this,et2_calendar_view); // Single day with multiple owners still needs owners split to satisfy // caching keys, otherwise they'll cache consolidated @@ -3327,7 +3321,10 @@ app.classes.calendar = AppJS.extend( * How big or small are the displayed time chunks? */ granularity: function(state) { - return parseInt(egw.preference('interval','calendar')) || 30; + var list = egw.preference('use_time_grid','calendar').split(','); + return list.indexOf(state.view) >= 0 ? + 0 : + parseInt(egw.preference('interval','calendar')) || 30; }, extend: function(sub) { @@ -3505,7 +3502,8 @@ jQuery.extend(app.classes.calendar,{ return week_start; }, granularity: function(state) { - return 120; + // Always a list, not a grid + return 0; }, scroll: function(delta) { diff --git a/calendar/js/et2_widget_daycol.js b/calendar/js/et2_widget_daycol.js index d31d1ebcca..2b19cf45dd 100644 --- a/calendar/js/et2_widget_daycol.js +++ b/calendar/js/et2_widget_daycol.js @@ -160,29 +160,36 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResizea } } - this.display_settings.rowsToDisplay = ((this.display_settings.wd_end - this.display_settings.wd_start)/this.display_settings.granularity); - this.display_settings.rowHeight= (100/this.display_settings.rowsToDisplay).toFixed(1); - this.display_settings.titleHeight = (this.title.height()/this.div.height())*100; - - // adding divs to click on for each row / time-span - for(var t = 0,i = 1; t < 1440; t += this.display_settings.granularity,++i) + if(this.display_settings.granularity === 0) { - var linkData = { - 'menuaction':'calendar.calendar_uiforms.edit', - 'date' : this.options.date, - 'hour' : sprintf("%02d",Math.floor(t / 60)), - 'minute' : sprintf("%02d",Math.floor(t % 60)) - }; - if (this.options.owner) linkData['owner'] = this.options.owner; + this.div.attr('data-date', this.options.date); + } + else + { + this.display_settings.rowsToDisplay = ((this.display_settings.wd_end - this.display_settings.wd_start)/this.display_settings.granularity); + this.display_settings.rowHeight= (100/this.display_settings.rowsToDisplay).toFixed(1); + this.display_settings.titleHeight = (this.title.height()/this.div.height())*100; - var droppableDateTime = linkData['date'] + "T" + linkData['hour'] + linkData['minute']; - var droppableID='drop_'+droppableDateTime+'_O'+(this.options.owner<0?'group'+Math.abs(this.options.owner):this.options.owner); + // adding divs to click on for each row / time-span + for(var t = 0,i = 1; t < 1440; t += this.display_settings.granularity,++i) + { + var linkData = { + 'menuaction':'calendar.calendar_uiforms.edit', + 'date' : this.options.date, + 'hour' : sprintf("%02d",Math.floor(t / 60)), + 'minute' : sprintf("%02d",Math.floor(t % 60)) + }; + if (this.options.owner) linkData['owner'] = this.options.owner; - var hour = jQuery('
'+this.options.value.description+'
'); } this.body diff --git a/calendar/js/et2_widget_timegrid.js b/calendar/js/et2_widget_timegrid.js index 3d054edb47..7d6ecdc64d 100644 --- a/calendar/js/et2_widget_timegrid.js +++ b/calendar/js/et2_widget_timegrid.js @@ -55,7 +55,7 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes name: "Granularity", type: "integer", default: parseInt(egw.preference('interval','calendar')) || 30, - description: "How many minutes per row" + description: "How many minutes per row, or 0 to display events as a list" }, "onchange": { "name": "onchange", @@ -194,6 +194,9 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes * for planner view to resize horizontally. */ this.div.on('mouseover', '.calendar_calEvent:not(.ui-resizable):not(.rowNoEdit)', function() { + // Only resize in timegrid + if(timegrid.options.granularity === 0) return; + // Load the event timegrid._get_event_info(this); var that = this; @@ -331,7 +334,7 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes { this.scrolling.scrollTop(element.dropEnd.position().top); } - else if (element.dropEnd.prev() && element.dropEnd.prev().position().top < this.scrolling[0].scrollTop) + else if (element.dropEnd.prev().length && element.dropEnd.prev().position().top < this.scrolling[0].scrollTop) { this.scrolling.scrollTop(element.dropEnd.prev().position().top); } @@ -341,6 +344,12 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes { time = this.egw().lang('Whole day'); } + else if (this.options.granularity === 0) + { + // No times, keep what's in the event + // Add class to helper to keep formatting + $j(helper).addClass('calendar_calTimeGridList'); + } else { time = jQuery.datepicker.formatTime( @@ -392,7 +401,13 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes if(event_data.app == 'calendar' && event_widget.options.value.whole_day) { event_widget._parent.date_helper.set_hours(0); - event_widget._parent.date_helper.set_minutes(0) + event_widget._parent.date_helper.set_minutes(0); + } + else if (timegrid.options.granularity === 0) + { + // List, not time grid - keep time + event_widget._parent.date_helper.set_hours(event_widget.options.value.start.getUTCHours()); + event_widget._parent.date_helper.set_minutes(event_widget.options.value.start.getUTCMinutes()); } else { @@ -507,6 +522,7 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes } this.update_timer = window.setTimeout(jQuery.proxy(function() { this.widget.update_timer = null; + window.clearTimeout(this.resize_timer); this.widget.loader.hide().show(); // Update actions @@ -600,12 +616,8 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes _drawTimes: function() { $j('.calendar_calTimeRow',this.div).remove(); - var wd_start = 60*this.options.day_start; - var wd_end = 60*this.options.day_end; - var granularity = this.options.granularity; - var totalDisplayMinutes = wd_end - wd_start; - var rowsToDisplay = Math.ceil((totalDisplayMinutes+60)/granularity); - + this.div.toggleClass('calendar_calTimeGridList', this.options.granularity === 0); + this.gridHeader .attr('data-date', this.options.start_date) .attr('data-owner', this.options.owner) @@ -613,14 +625,35 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes .append(this.owner.getDOMNode()) .append(this.dayHeader) .appendTo(this.div); + + // Max with 18 avoids problems when it's not shown + var header_height = Math.max(this.gridHeader.outerHeight(true), 18); + + this.scrolling + .appendTo(this.div) + .off() + + // No time grid - list + if(this.options.granularity === 0) + { + this.scrolling.css('height','100%'); + this.days.css('height', '100%'); + this.iterateOver(function(day) { + day.resize(); + },this,et2_calendar_daycol); + return; + } + + var wd_start = 60*this.options.day_start; + var wd_end = 60*this.options.day_end; + var granularity = this.options.granularity; + var totalDisplayMinutes = wd_end - wd_start; + var rowsToDisplay = Math.ceil((totalDisplayMinutes+60)/granularity); - // Max with 45 avoids problems when it's not shown - var header_height = Math.max(this.gridHeader.outerHeight(true), 45); this.scrolling .css('height', (this.div.innerHeight() - header_height)+'px') - .appendTo(this.div) - .off().on('scroll', jQuery.proxy(this._scroll, this)); + .on('scroll', jQuery.proxy(this._scroll, this)); // Percent var rowHeight = (100/rowsToDisplay).toFixed(1); @@ -701,6 +734,9 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes { window.clearTimeout(this.resize_timer); } + // No point if it is just going to be redone completely + if(this.upate_timer) return; + this.resize_timer = window.setTimeout(jQuery.proxy(function() { if(this._resizeTimes) { @@ -727,12 +763,23 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes var new_height = this.scrolling.height() / rowsToDisplay; this.rowHeight = new_height; var rows = $j('.calendar_calTimeRow',this.scrolling).height(this.rowHeight); - this.days.css('height', (this.rowHeight*rows.length)+'px'); + if(!rows.length && this.options.granularity) + { + return this._drawTimes(); + } + this.days.css('height', this.options.granularity === 0 ? + '100%' : + (this.rowHeight*rows.length)+'px' + ); $j('.calendar_calAddEvent',this.scrolling).height(this.rowHeight); // Scroll to start of day this._top_time = (wd_start * this.rowHeight) / this.options.granularity; this.scrolling.scrollTop(this._top_time); + + this.iterateOver(function(child) { + child.resize(); + },this, et2_IResizeable); }, /** @@ -749,7 +796,9 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes this.day_list = this._calculate_day_list(this.options.start_date, this.options.end_date, this.options.show_weekend); } // For a single day, we show each owner in their own daycol - var daily_owner = this.day_list.length === 1 && this.options.owner.length < parseInt(egw.preference('day_consolidate','calendar')); + var daily_owner = this.day_list.length === 1 && + this.options.owner.length > 1 && + this.options.owner.length < parseInt(egw.preference('day_consolidate','calendar')); var daycols_needed = daily_owner ? this.options.owner.length : this.day_list.length; var day_width = ( Math.min( $j(this.getInstanceManager().DOMContainer).width(),this.days.width())/daycols_needed); if(!day_width || !this.day_list) @@ -815,6 +864,9 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes this.day_widgets.splice(delete_index--,1); } + // Adjust and scroll to start of day + this.resizeTimes(); + // Create / update day widgets with dates and data for(var i = 0; i < this.day_widgets.length; i++) { @@ -848,9 +900,6 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes this.set_label(''); } - // Adjust and scroll to start of day - this._resizeTimes(); - // Handle not fully visible elements this._scroll(); @@ -1356,15 +1405,24 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes */ set_granularity: function(minutes) { - // Avoid 0 or less - minutes = Math.max(1,minutes); + // Avoid < 0 + minutes = Math.max(0,minutes); if(this.options.granularity !== minutes) { - this.options.granularity = minutes; - this._drawTimes(); + if(this.options.granularity === 0 || minutes === 0) + { + this.options.granularity = minutes; + // Need to re-do a bunch to make sure this is propagated + this.invalidate(); + } + else + { + this.options.granularity = minutes; + this._drawTimes(); + } } - else + else if (!this.update_timer) { this.resizeTimes(); } @@ -1545,13 +1603,20 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes x = Math.round(x); y = Math.round(y); - var nodes = $j('.calendar_calAddEvent[data-hour],.calendar_calDayColHeader',this.div).removeClass('drop-hover').filter(function() { - var offset = $j(this).offset(); - var range={x:[offset.left,offset.left+$j(this).outerWidth()],y:[offset.top,offset.top+$j(this).outerHeight()]}; - - var i = (x >=range.x[0] && x <= range.x[1]) && (y >= range.y[0] && y <= range.y[1]); - return i; - }).addClass("drop-hover"); + var nodes = this.options.granularity === 0 ? + $j('.calendar_calDayCol',this.div) : + $j('.calendar_calAddEvent[data-hour],.calendar_calDayColHeader',this.div); + + nodes = nodes + .removeClass('drop-hover') + .filter(function() { + var offset = $j(this).offset(); + var range={x:[offset.left,offset.left+$j(this).outerWidth()],y:[offset.top,offset.top+$j(this).outerHeight()]}; + + var i = (x >=range.x[0] && x <= range.x[1]) && (y >= range.y[0] && y <= range.y[1]); + return i; + }) + nodes.addClass("drop-hover"); return nodes; }, @@ -1611,7 +1676,7 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes height -= $j('#calendar-toolbar').outerHeight(true); this.options.height = Math.floor(height / rowCount); - + // Allow for borders & padding this.options.height -= 2*((this.div.outerWidth(true) - this.div.innerWidth()) + parseInt(this.div.parent().css('padding-top'))); if(this.options.height+"px" !== this.div.css('height')) @@ -1626,9 +1691,9 @@ var et2_calendar_timegrid = et2_calendar_view.extend([et2_IDetachedDOM, et2_IRes } // Try to resize width, though animations cause problems - var total_width = ( $j(this.getInstanceManager().DOMContainer).width() - ( + var total_width = Math.min(this.div.width(),( $j(this.getInstanceManager().DOMContainer).width() - ( this.days.innerWidth() ? this.div.innerWidth() - this.days.innerWidth() : 0 - )); + ))); var day_width = (total_width > 0 ? total_width : $j(this.getInstanceManager().DOMContainer).width())/this.day_widgets.length; // update day widgets for(var i = 0; i < this.day_widgets.length; i++) diff --git a/calendar/setup/setup.inc.php b/calendar/setup/setup.inc.php index 4dbb70ba6f..fafe82ed5b 100755 --- a/calendar/setup/setup.inc.php +++ b/calendar/setup/setup.inc.php @@ -10,7 +10,7 @@ */ $setup_info['calendar']['name'] = 'calendar'; -$setup_info['calendar']['version'] = '14.3.902'; +$setup_info['calendar']['version'] = '14.3.903'; $setup_info['calendar']['app_order'] = 3; $setup_info['calendar']['enable'] = 1; $setup_info['calendar']['index'] = 'calendar.calendar_uiviews.index&ajax=true'; diff --git a/calendar/setup/tables_update.inc.php b/calendar/setup/tables_update.inc.php index 7cce063ce7..3900a54017 100644 --- a/calendar/setup/tables_update.inc.php +++ b/calendar/setup/tables_update.inc.php @@ -2683,3 +2683,23 @@ function calendar_upgrade14_3_901() }); return $GLOBALS['setup_info']['calendar']['currentver'] = '14.3.902'; } + +/** + * Change grid/list preference values to list each view + */ +function calendar_upgrade14_3_902() +{ + preferences::change_preference('calendar', 'use_time_grid', function($attr, $old_value, $owner) { + switch($old_value) + { + case 'all': + return 0; + case 'day_week': + return ['day','day4','week']; + case 'day4': + return ['day','day4']; + } + return null; + }); + return $GLOBALS['setup_info']['calendar']['currentver'] = '14.3.903'; +} diff --git a/calendar/templates/default/app.css b/calendar/templates/default/app.css index d0b1f49fa9..600a916749 100644 --- a/calendar/templates/default/app.css +++ b/calendar/templates/default/app.css @@ -191,6 +191,7 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget width: 100%; overflow-y: auto; overflow-x: hidden; + cursor: default; } .calendar_calTimeGrid .loading,.calendar_plannerWidget .loading { @@ -298,6 +299,7 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget border-right: 0px; overflow: hidden; z-index: 40; + cursor: pointer; } .calendar_calDayColHeader .hiddenEventBefore { height: 2px; @@ -602,7 +604,9 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget .calendar_calEventBody > p, .calendar_calEventBodySmall > p { white-space: pre-wrap; } - +.calendar_calEventBody .calendar_calTimespan { + display: none; +} .calendar_calEventBodySmall{ font-size: 95%; } @@ -615,6 +619,59 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget font-weight: bold; } +/* Events as displayed in a list, not sized by time */ +.calendar_calTimeGridList .calendar_calTimeGridScroll { + overflow-y: hidden; +} +.calendar_calTimeGridList .calendar_calGridHeader { + min-height: 1em; +} +.calendar_calTimeGridList .calendar_calDayColAllDay { + display: none; +} +.calendar_calTimeGridList .calendar_calDayColHeader { + min-height: initial; + padding-bottom: 0px; +} +.calendar_calTimeGridList .calendar_calEvent { + min-height: 1.1em; + position: relative; +} +.calendar_calTimeGridList .calendar_calEvent:not([data-full_day]) { + border-top: none; + border-left: none; + border-right: none; +} +.calendar_calTimeGridList .calendar_calEventHeader { + display: none !important; +} +.calendar_calTimeGridList .calendar_calEventTitle { + font-weight: normal; + display: inline-block; + max-height:2.4em; + white-space: nowrap; +} +.calendar_calTimeGridList .calendar_calEventBody:not(.calendar_calEventBodySmall) { + overflow: hidden; +} +.calendar_calTimeGridList .calendar_calEventBody .calendar_calTimespan { + display: inline-block; + float: right; + padding-left: 1em; + opacity: 0.8; + font-size: 90% +} +.calendar_calTimeGridList .calendar_calEventBody > p { + display: none; +} +.calendar_calTimeGridList .calendar_calDayCol .hiddenEventAfter:hover { + /* Need !important to override calculated height*/ + top: initial !important; + bottom: 0px; + white-space: pre; + height: 100%; +} + /* table of the dayView containing 2 cols: 1) day-view, 2) todos */ .calendar_calDayView{