forked from extern/egroupware
Gantt chart progress
This commit is contained in:
parent
38419b2abe
commit
ccdb54a366
@ -22,6 +22,25 @@ class etemplate_widget_gantt extends etemplate_widget_box
|
|||||||
{
|
{
|
||||||
// No legacy options
|
// No legacy options
|
||||||
protected $legacy_options = array();
|
protected $legacy_options = array();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate input
|
||||||
|
*
|
||||||
|
* @param string $cname current namespace
|
||||||
|
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
|
||||||
|
* @param array $content
|
||||||
|
* @param array &$validated=array() validated content
|
||||||
|
*/
|
||||||
|
public function validate($cname, array $expand, array $content, &$validated=array())
|
||||||
|
{
|
||||||
|
$value = self::get_array($content, $cname);
|
||||||
|
$validated[$cname] = array(
|
||||||
|
'action' => $value['action'],
|
||||||
|
'selected' => $value['selected']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// register class for layout widgets, which can have an own namespace
|
// register class for layout widgets, which can have an own namespace
|
||||||
//etemplate_widget::registerWidget('etemplate_widget_box', array('box', 'hbox', 'vbox', 'groupbox'));
|
//etemplate_widget::registerWidget('etemplate_widget_box', array('box', 'hbox', 'vbox', 'groupbox'));
|
@ -33,7 +33,7 @@
|
|||||||
* @see http://docs.dhtmlx.com/gantt/index.html
|
* @see http://docs.dhtmlx.com/gantt/index.html
|
||||||
* @augments et2_valueWidget
|
* @augments et2_valueWidget
|
||||||
*/
|
*/
|
||||||
var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
var et2_gantt = et2_valueWidget.extend([et2_IResizeable,et2_IInput],
|
||||||
{
|
{
|
||||||
// Filters are inside gantt namespace
|
// Filters are inside gantt namespace
|
||||||
createNamespace: true,
|
createNamespace: true,
|
||||||
@ -70,7 +70,11 @@ var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
|||||||
autosize: 'y',
|
autosize: 'y',
|
||||||
// Date rounding happens either way, but this way it rounds to the displayed grid resolution
|
// Date rounding happens either way, but this way it rounds to the displayed grid resolution
|
||||||
// Also avoids a potential infinite loop thanks to how the dates are rounded with false
|
// Also avoids a potential infinite loop thanks to how the dates are rounded with false
|
||||||
round_dnd_dates: true,
|
round_dnd_dates: false,
|
||||||
|
// Round resolution
|
||||||
|
time_step: parseInt(this.egw().preference('interval','calendar') || 15),
|
||||||
|
min_duration: 1 * 60 * 1000, // 1 minute
|
||||||
|
|
||||||
scale_unit: 'day',
|
scale_unit: 'day',
|
||||||
date_scale: '%d',
|
date_scale: '%d',
|
||||||
subscales: [
|
subscales: [
|
||||||
@ -244,6 +248,43 @@ var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
|||||||
this.egw().debug('warning', 'Problem rendering gantt', e);
|
this.egw().debug('warning', 'Problem rendering gantt', e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* getValue has to return the value of the input widget
|
||||||
|
*/
|
||||||
|
getValue: function() {
|
||||||
|
return this.value;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is dirty returns true if the value of the widget has changed since it
|
||||||
|
* was loaded.
|
||||||
|
*/
|
||||||
|
isDirty: function() {
|
||||||
|
return this.value != null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes the dirty flag to be reseted.
|
||||||
|
*/
|
||||||
|
resetDirty: function() {
|
||||||
|
this.value = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the data to see if it is valid, as far as the client side can tell.
|
||||||
|
* Return true if it's not possible to tell on the client side, because the server
|
||||||
|
* will have the chance to validate also.
|
||||||
|
*
|
||||||
|
* The messages array is to be populated with everything wrong with the data,
|
||||||
|
* so don't stop checking after the first problem unless it really makes sense
|
||||||
|
* to ignore other problems.
|
||||||
|
*
|
||||||
|
* @param {String[]} messages List of messages explaining the failure(s).
|
||||||
|
* messages should be fairly short, and already translated.
|
||||||
|
*
|
||||||
|
* @return {boolean} True if the value is valid (enough), false to fail
|
||||||
|
*/
|
||||||
|
isValid: function(messages) {return true},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a URL to fetch the data from the server.
|
* Set a URL to fetch the data from the server.
|
||||||
@ -272,6 +313,8 @@ var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
|||||||
var scale_unit = 'day';
|
var scale_unit = 'day';
|
||||||
var date_scale = '%d';
|
var date_scale = '%d';
|
||||||
var step = 1;
|
var step = 1;
|
||||||
|
var time_step = this.gantt_config.time_step;
|
||||||
|
var min_column_width = this.gantt_config.min_column_width;
|
||||||
|
|
||||||
// No level? Auto calculate.
|
// No level? Auto calculate.
|
||||||
if(level > 4) level = 4;
|
if(level > 4) level = 4;
|
||||||
@ -317,7 +360,7 @@ var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
|||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
// Less than a year, several months
|
// Less than a year, several months
|
||||||
subscales.push({unit: "month", step: 1, date: '%F %Y'});
|
subscales.push({unit: "month", step: 1, date: '%F %Y', class: 'et2_clickable'});
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
default:
|
default:
|
||||||
@ -326,13 +369,14 @@ var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
|||||||
scale_unit = 'hour';
|
scale_unit = 'hour';
|
||||||
date_scale = this.egw().preference('timeformat') == '24' ? "%G" : "%g";
|
date_scale = this.egw().preference('timeformat') == '24' ? "%G" : "%g";
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1: // A day or two, scale in Minutes
|
||||||
// A day or two, scale in Minutes
|
|
||||||
subscales.push({unit: "day", step: 1, date: '%F %d'});
|
subscales.push({unit: "day", step: 1, date: '%F %d'});
|
||||||
date_scale = this.egw().preference('timeformat') == '24' ? "%G:%i" : "%g:%i";
|
date_scale = this.egw().preference('timeformat') == '24' ? "%G:%i" : "%g:%i";
|
||||||
|
|
||||||
step = parseInt(this.egw().preference('interval','calendar') || 15);
|
step = parseInt(this.egw().preference('interval','calendar') || 15);
|
||||||
|
time_step = 1;
|
||||||
scale_unit = 'minute';
|
scale_unit = 'minute';
|
||||||
|
min_column_width = 50;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply settings
|
// Apply settings
|
||||||
@ -340,6 +384,8 @@ var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
|||||||
this.gantt.config.scale_unit = scale_unit;
|
this.gantt.config.scale_unit = scale_unit;
|
||||||
this.gantt.config.date_scale = date_scale;
|
this.gantt.config.date_scale = date_scale;
|
||||||
this.gantt.config.step = step;
|
this.gantt.config.step = step;
|
||||||
|
this.gantt.config.time_step = time_step;
|
||||||
|
this.gantt.config.min_column_width = min_column_width;
|
||||||
|
|
||||||
this.options.zoom = level;
|
this.options.zoom = level;
|
||||||
return level;
|
return level;
|
||||||
@ -353,6 +399,20 @@ var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
|||||||
|
|
||||||
// Click on scale to zoom - top zooms out, bottom zooms in
|
// Click on scale to zoom - top zooms out, bottom zooms in
|
||||||
this.gantt_node.on('click','.gantt_scale_line', function(e) {
|
this.gantt_node.on('click','.gantt_scale_line', function(e) {
|
||||||
|
var current_position = e.target.offsetLeft / $j(e.target.parentNode).width();
|
||||||
|
|
||||||
|
// Some crazy stuff make sure timing is OK to scroll after re-render
|
||||||
|
// TODO: Make this more consistently go to where you click
|
||||||
|
var id = gantt_widget.gantt.attachEvent("onGanttRender", function() {
|
||||||
|
console.log('Render');
|
||||||
|
gantt_widget.gantt.detachEvent(id);
|
||||||
|
gantt_widget.gantt.scrollTo(parseInt($j('.gantt_task_scale',gantt_widget.gantt_node).width() *current_position),0);
|
||||||
|
window.setTimeout(function() {
|
||||||
|
console.log("Scroll to");
|
||||||
|
gantt_widget.gantt.scrollTo(parseInt($j('.gantt_task_scale',gantt_widget.gantt_node).width() *current_position),0);
|
||||||
|
},100);
|
||||||
|
});
|
||||||
|
|
||||||
if(this.parentNode.firstChild == this)
|
if(this.parentNode.firstChild == this)
|
||||||
{
|
{
|
||||||
// Zoom out
|
// Zoom out
|
||||||
@ -365,6 +425,12 @@ var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
|||||||
gantt_widget.set_zoom(gantt_widget.options.zoom - 1);
|
gantt_widget.set_zoom(gantt_widget.options.zoom - 1);
|
||||||
gantt_widget.gantt.render();
|
gantt_widget.gantt.render();
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
window.setTimeout(function() {
|
||||||
|
console.log("Scroll to");
|
||||||
|
gantt_widget.gantt.scrollTo(parseInt($j('.gantt_task_scale',gantt_widget.gantt_node).width() *current_position),0);
|
||||||
|
},50);
|
||||||
|
*/
|
||||||
});
|
});
|
||||||
|
|
||||||
this.gantt.attachEvent("onContextMenu",function(taskId, linkId, e) {
|
this.gantt.attachEvent("onContextMenu",function(taskId, linkId, e) {
|
||||||
@ -397,6 +463,26 @@ var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update server for links
|
||||||
|
var link_update = function(id, link) {
|
||||||
|
if(gantt_widget.options.ajax_update)
|
||||||
|
{
|
||||||
|
link.parent = this.getTask(link.source).parent;
|
||||||
|
var value = gantt_widget.getInstanceManager().getValues(gantt_widget.getInstanceManager().widgetContainer);
|
||||||
|
|
||||||
|
var request = gantt_widget.egw().json(gantt_widget.options.ajax_update,
|
||||||
|
[link,value], function(new_id) {
|
||||||
|
if(new_id)
|
||||||
|
{
|
||||||
|
link.id = new_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).sendRequest(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.gantt.attachEvent("onAfterLinkAdd", link_update);
|
||||||
|
this.gantt.attachEvent("onAfterLinkDelete", link_update);
|
||||||
|
|
||||||
// Bind AJAX for dynamic expansion
|
// Bind AJAX for dynamic expansion
|
||||||
// TODO: This could be improved
|
// TODO: This could be improved
|
||||||
this.gantt.attachEvent("onTaskOpened", function(id, item) {
|
this.gantt.attachEvent("onTaskOpened", function(id, item) {
|
||||||
@ -522,6 +608,29 @@ var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
|||||||
|
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
|
|
||||||
|
// Submit with most actions
|
||||||
|
this._actionManager.setDefaultExecute(jQuery.proxy(function(action, selected) {
|
||||||
|
var ids = [];
|
||||||
|
for(var i = 0; i < selected.length; i++)
|
||||||
|
{
|
||||||
|
ids.push(selected[i].id);
|
||||||
|
}
|
||||||
|
this.value = {
|
||||||
|
action: action.id,
|
||||||
|
selected: ids
|
||||||
|
};
|
||||||
|
|
||||||
|
// downloads need a regular submit via POST (no Ajax)
|
||||||
|
if (action.data.postSubmit)
|
||||||
|
{
|
||||||
|
this.getInstanceManager().postSubmit();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.getInstanceManager().submit();
|
||||||
|
}
|
||||||
|
}, this));
|
||||||
|
|
||||||
// Get the top level element for the tree
|
// Get the top level element for the tree
|
||||||
var objectManager = egw_getAppObjectManager(true);
|
var objectManager = egw_getAppObjectManager(true);
|
||||||
var widget_object = objectManager.getObjectById(this.id);
|
var widget_object = objectManager.getObjectById(this.id);
|
||||||
@ -536,6 +645,7 @@ var et2_gantt = et2_valueWidget.extend([et2_IResizeable],
|
|||||||
*/
|
*/
|
||||||
_link_task: function(taskId)
|
_link_task: function(taskId)
|
||||||
{
|
{
|
||||||
|
if(!taskId) return;
|
||||||
var objectManager = egw_getObjectManager(this.id,false);
|
var objectManager = egw_getObjectManager(this.id,false);
|
||||||
var obj = null;
|
var obj = null;
|
||||||
if(!(obj = objectManager.getObjectById(taskId)))
|
if(!(obj = objectManager.getObjectById(taskId)))
|
||||||
@ -599,17 +709,19 @@ et2_register_widget(et2_gantt, ["gantt"]);
|
|||||||
// Localize to user's language - breaks if file is not there
|
// Localize to user's language - breaks if file is not there
|
||||||
//egw.includeJS("/phpgwapi/js/dhtmlxGantt/codebase/locale/locale_" + egw.preference('lang') + ".js");
|
//egw.includeJS("/phpgwapi/js/dhtmlxGantt/codebase/locale/locale_" + egw.preference('lang') + ".js");
|
||||||
|
|
||||||
// Set icon to match application
|
$j(function() {
|
||||||
gantt.templates.grid_file = function(item) {
|
// Set icon to match application
|
||||||
|
gantt.templates.grid_file = function(item) {
|
||||||
if(!item.pe_app || !egw.image(item.pe_icon)) return "<div class='gantt_tree_icon gantt_file'></div>";
|
if(!item.pe_app || !egw.image(item.pe_icon)) return "<div class='gantt_tree_icon gantt_file'></div>";
|
||||||
return "<div class='gantt_tree_icon' style='background-image: url(\"" + egw.image(item.pe_icon) + "\");'/></div>";
|
return "<div class='gantt_tree_icon' style='background-image: url(\"" + egw.image(item.pe_icon) + "\");'/></div>";
|
||||||
}
|
|
||||||
|
|
||||||
// Show nicer intervals in minute duration
|
|
||||||
gantt.templates.date_scale = function(date) {
|
|
||||||
if(gantt.config.scale_unit == 'minute')
|
|
||||||
{
|
|
||||||
date.setMinutes((date.getMinutes() % this.gantt.config.step) * this.gantt.config.step);
|
|
||||||
}
|
}
|
||||||
return gantt.date.date_to_str(gantt.config.date_scale)(date);
|
|
||||||
}
|
// CSS for scale row, turns on clickable
|
||||||
|
gantt.templates.scale_row_class = function(scale) {
|
||||||
|
if(scale.unit != 'minute' && scale.unit != 'month')
|
||||||
|
{
|
||||||
|
return scale.class || 'et2_clickable';
|
||||||
|
}
|
||||||
|
return scale.class;
|
||||||
|
}
|
||||||
|
});
|
@ -688,8 +688,13 @@ etemplate2.prototype.getValues = function(_root)
|
|||||||
// Store the value of the widget and reset its dirty flag
|
// Store the value of the widget and reset its dirty flag
|
||||||
if (value !== null)
|
if (value !== null)
|
||||||
{
|
{
|
||||||
|
// Namespaced container has its own value, don't put it down one more level
|
||||||
|
if (_widget.createNamespace)
|
||||||
|
{
|
||||||
|
jQuery.extend(_target,value);
|
||||||
|
}
|
||||||
// Merge, if possible (link widget)
|
// Merge, if possible (link widget)
|
||||||
if(typeof _target[id] == 'object' && typeof value == 'object')
|
else if(typeof _target[id] == 'object' && typeof value == 'object')
|
||||||
{
|
{
|
||||||
_target[id] = jQuery.extend({},_target[id],value);
|
_target[id] = jQuery.extend({},_target[id],value);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user