Commit 2c60486b authored by Romain Courteaud's avatar Romain Courteaud Committed by Jérome Perrin

Migrate job gantt widget.

parent 0e39fb46
......@@ -12,3 +12,4 @@ outputJSON.json
trace*.xls
npm-debug.log
node_modules/
tmp/
......@@ -6,7 +6,7 @@ module.exports = function (grunt) {
var global_config = {
src: "dream/platform/src2/",
lib: "dream/platform/vendor/",
// tmp: "tmp",
tmp: "tmp",
dest: "dream/platform/static/"
};
......@@ -19,6 +19,7 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-curl');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-zip');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
......@@ -146,6 +147,18 @@ module.exports = function (grunt) {
relative_dest: 'lib/qunit.js',
dest: "<%= global_config.dest %>/<%= copy.qunitjs.relative_dest %>"
},
dhtmlxganttjs: {
src: "<%= global_config.lib %>/dhtmlxgantt.js",
// src: "<%= unzip.dhtmlxgantt.dest %>/codebase/dhtmlxgantt.js",
relative_dest: 'lib/dhtmlxgantt.js',
dest: "<%= global_config.dest %>/<%= copy.dhtmlxganttjs.relative_dest %>"
},
dhtmlxganttcss: {
src: "<%= global_config.lib %>/dhtmlxgantt.css",
// src: "<%= unzip.dhtmlxgantt.dest %>/codebase/dhtmlxgantt.css",
relative_dest: 'lib/dhtmlxgantt.css',
dest: "<%= global_config.dest %>/<%= copy.dhtmlxganttcss.relative_dest %>"
},
qunitcss: {
src: 'node_modules/qunitjs/qunit/qunit.css',
relative_dest: 'lib/qunit.css',
......@@ -210,6 +223,10 @@ module.exports = function (grunt) {
relative_dest: 'lib/jquery.flot.stack.js',
dest: '<%= global_config.dest %>/<%= curl.jqueryflotstack.relative_dest %>'
},
dhtmlxgantt: {
src: 'http://dhtmlx.com/x/download/regular/dhtmlxGantt.zip',
dest: '<%= global_config.tmp %>/dhtmlxGantt.zip'
},
handsontablejs: {
src: 'https://raw.githubusercontent.com/warpech/' +
'jquery-handsontable/v0.10.5/dist/jquery.handsontable.full.js',
......@@ -239,6 +256,13 @@ module.exports = function (grunt) {
}
// qunit: {
// all: ['test/index.html']
},
unzip: {
dhtmlxgantt: {
src: '<%= curl.dhtmlxgantt.dest %>',
dest: '<%= global_config.tmp %>/dhtmlxGantt/'
}
}
});
......@@ -246,7 +270,7 @@ module.exports = function (grunt) {
grunt.registerTask('default', ['all']);
grunt.registerTask('all', ['lint', 'build']);
grunt.registerTask('lint', ['jslint']);
grunt.registerTask('dep', ['curl']);
grunt.registerTask('dep', ['curl', 'unzip']);
// grunt.registerTask('test', ['qunit']);
grunt.registerTask('build', ['concat', 'copy', 'uglify', 'less']);
......
......@@ -8,7 +8,6 @@
<link rel="stylesheet" href="css/skeleton_wide.css">
<link rel="stylesheet" href="css/layout.css">
<link rel="stylesheet" href="css/jquery-ui-1.10.3.custom.min.css">
<link rel="stylesheet" type="text/css" href="lib/dhtmlxgantt/dhtmlxgantt.css">
<link rel="stylesheet" href="css/demo-new.css">
<link rel="stylesheet" href="css/font-awesome.css">
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
......@@ -83,7 +82,6 @@
<div id="capacity_graphs"></div>
</div>
<div id="job_gantt" style='width:1320px; height:800px;'></div>
<div id="job_schedule_spreadsheet" style="overflow: scroll;"></div>
</div>
......@@ -103,9 +101,6 @@
<script type="text/javascript" src="lib/jquery.flot.js"></script>
<script type="text/javascript" src="lib/jquery.flot.stack.js"></script>
<!-- gantt chart -->
<script type="text/javascript" src="lib/dhtmlxgantt/dhtmlxgantt.js"></script>
<!-- nice display of dates -->
<script type="text/javascript" src="lib/moment-with-langs.min.js"></script>
......
<?php
/*
@author dhtmlx.com
@license GPL, see license.txt
*/
require_once("dataview_connector.php");
/*! Connector class for DataView
**/
class ChartConnector extends DataViewConnector{
public function __construct($res,$type=false,$item_type=false,$data_type=false){
parent::__construct($res,$type,$item_type,$data_type);
}
}
?>
\ No newline at end of file
<?php
/*
@author dhtmlx.com
@license GPL, see license.txt
*/
require_once("base_connector.php");
/*! DataItem class for Combo component
**/
class ComboDataItem extends DataItem{
private $selected;//!< flag of selected option
function __construct($data,$config,$index){
parent::__construct($data,$config,$index);
$this->selected=false;
}
/*! mark option as selected
*/
function select(){
$this->selected=true;
}
/*! return self as XML string, starting part
*/
function to_xml_start(){
if ($this->skip) return "";
return "<option ".($this->selected?"selected='true'":"")."value='".$this->get_id()."'><![CDATA[".$this->data[$this->config->text[0]["name"]]."]]>";
}
/*! return self as XML string, ending part
*/
function to_xml_end(){
if ($this->skip) return "";
return "</option>";
}
}
/*! Connector for the dhtmlxCombo
**/
class ComboConnector extends Connector{
private $filter; //!< filtering mask from incoming request
private $position; //!< position from incoming request
/*! constructor
Here initilization of all Masters occurs, execution timer initialized
@param res
db connection resource
@param type
string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided
@param item_type
name of class, which will be used for item rendering, optional, DataItem will be used by default
@param data_type
name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default.
*/
public function __construct($res,$type=false,$item_type=false,$data_type=false){
if (!$item_type) $item_type="ComboDataItem";
parent::__construct($res,$type,$item_type,$data_type);
}
//parse GET scoope, all operations with incoming request must be done here
function parse_request(){
parent::parse_request();
if (isset($_GET["pos"])){
if (!$this->dload) //not critical, so just write a log message
LogMaster::log("Dyn loading request received, but server side was not configured to process dyn. loading. ");
else
$this->request->set_limit($_GET["pos"],$this->dload);
}
if (isset($_GET["mask"]))
$this->request->set_filter($this->config->text[0]["db_name"],$_GET["mask"]."%","LIKE");
LogMaster::log($this->request);
}
/*! renders self as xml, starting part
*/
public function xml_start(){
if ($this->request->get_start())
return "<complete add='true'>";
else
return "<complete>";
}
/*! renders self as xml, ending part
*/
public function xml_end(){
return "</complete>";
}
}
?>
\ No newline at end of file
<?php
/*
@author dhtmlx.com
@license GPL, see license.txt
*/
class ConvertService{
private $url;
private $type;
private $name;
private $inline;
public function __construct($url){
$this->url = $url;
$this->pdf();
EventMaster::attach_static("connectorInit",array($this, "handle"));
}
public function pdf($name = "data.pdf", $inline = false){
$this->type = "pdf";
$this->name = $name;
$this->inline = $inline;
}
public function excel($name = "data.xls", $inline = false){
$this->type = "excel";
$this->name = $name;
$this->inline = $inline;
}
public function handle($conn){
$conn->event->attach("beforeOutput",array($this,"convert"));
}
private function as_file($size, $name, $inline){
header('Content-Type: application/force-download');
header('Content-Type: application/octet-stream');
header('Content-Type: application/download');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.$size);
if ($inline)
header('Content-Disposition: inline; filename="'.$name.'";');
else
header('Content-Disposition: attachment; filename="'.basename($name).'";');
}
public function convert($conn, $out){
$str_out = str_replace("<rows>","<rows profile='color'>", $out);
$str_out = str_replace("<head>","<head><columns>", $str_out);
$str_out = str_replace("</head>","</columns></head>", $str_out);
if ($this->type == "pdf")
header("Content-type: application/pdf");
else
header("Content-type: application/ms-excel");
$handle = curl_init($this->url);
curl_setopt($handle, CURLOPT_POST, true);
curl_setopt($handle, CURLOPT_HEADER, false);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($handle, CURLOPT_POSTFIELDS, "grid_xml=".urlencode($str_out));
$out->reset();
$out->set_type("pdf");
$out->add(curl_exec($handle));
$this->as_file(strlen((string)$out), $this->name, $this->inline);
curl_close($handle);
}
}
?>
\ No newline at end of file
<?php
/*
@author dhtmlx.com
@license GPL, see license.txt
*/
require_once("data_connector.php");
class DelayedConnector extends Connector{
protected $init_flag=false;//!< used to prevent rendering while initialization
private $data_mode=false;//!< flag to separate xml and data request modes
private $data_result=false;//<! store results of query
public function dataMode($name){
$this->data_mode = $name;
$this->data_result=array();
}
public function getDataResult(){
return $this->data_result;
}
public function render(){
if (!$this->init_flag){
$this->init_flag=true;
return "";
}
return parent::render();
}
protected function output_as_xml($res){
if ($this->data_mode){
while ($data=$this->sql->get_next($res)){
$this->data_result[]=$data[$this->data_mode];
}
}
else
return parent::output_as_xml($res);
}
protected function end_run(){
if (!$this->data_mode)
parent::end_run();
}
}
class CrossOptionsConnector extends Connector{
public $options, $link;
private $master_name, $link_name, $master_value;
public function __construct($res,$type=false,$item_type=false,$data_type=false){
$this->options = new OptionsConnector($res,$type,$item_type,$data_type);
$this->link = new DelayedConnector($res,$type,$item_type,$data_type);
EventMaster::attach_static("connectorInit",array($this, "handle"));
}
public function handle($conn){
if ($conn instanceof DelayedConnector) return;
if ($conn instanceof OptionsConnector) return;
$this->master_name = $this->link->get_config()->id["db_name"];
$this->link_name = $this->options->get_config()->id["db_name"];
$this->link->event->attach("beforeFilter",array($this, "get_only_related"));
if (isset($_GET["dhx_crosslink_".$this->link_name])){
$this->get_links($_GET["dhx_crosslink_".$this->link_name]);
die();
}
if (!$this->dload){
$conn->event->attach("beforeRender", array($this, "getOptions"));
$conn->event->attach("beforeRenderSet", array($this, "prepareConfig"));
}
$conn->event->attach("afterProcessing", array($this, "afterProcessing"));
}
public function prepareConfig($conn, $res, $config){
$config->add_field($this->link_name);
}
public function getOptions($data){
$this->link->dataMode($this->link_name);
$this->get_links($data->get_value($this->master_name));
$data->set_value($this->link_name, implode(",",$this->link->getDataResult()));
}
public function get_links($id){
$this->master_value = $id;
$this->link->render();
}
public function get_only_related($filters){
$index = $filters->index($this->master_name);
if ($index!==false){
$filters->rules[$index]["value"]=$this->master_value;
} else
$filters->add($this->master_name, $this->master_value, "=");
}
public function afterProcessing($action){
$status = $action->get_status();
$master_key = $action->get_value($this->master_name);
$link_key = $action->get_value($this->link_name);
$link_key = explode(',', $link_key);
if ($status == "inserted")
$master_key = $action->get_new_id();
switch ($status){
case "deleted":
$this->link->delete($master_key);
break;
case "updated":
//cross link options not loaded yet, so we can skip update
if (!array_key_exists($this->link_name, $action->get_data()))
break;
//else, delete old options and continue in insert section to add new values
$this->link->delete($master_key);
case "inserted":
for ($i=0; $i < sizeof($link_key); $i++)
if ($link_key[$i]!="")
$this->link->insert(array(
$this->link_name => $link_key[$i],
$this->master_name => $master_key
));
break;
}
}
}
class JSONCrossOptionsConnector extends CrossOptionsConnector{
public $options, $link;
private $master_name, $link_name, $master_value;
public function __construct($res,$type=false,$item_type=false,$data_type=false){
$this->options = new JSONOptionsConnector($res,$type,$item_type,$data_type);
$this->link = new DelayedConnector($res,$type,$item_type,$data_type);
EventMaster::attach_static("connectorInit",array($this, "handle"));
}
}
?>
\ No newline at end of file
<?php
/*
@author dhtmlx.com
@license GPL, see license.txt
*/
require_once("base_connector.php");
/*! DataItem class for DataView component
**/
class DataViewDataItem extends DataItem{
/*! return self as XML string
*/
function to_xml(){
if ($this->skip) return "";
$str="<item id='".$this->get_id()."' >";
for ($i=0; $i<sizeof($this->config->text); $i++){
$extra = $this->config->text[$i]["name"];
$str.="<".$extra."><![CDATA[".$this->data[$extra]."]]></".$extra.">";
}
return $str."</item>";
}
}
/*! Connector class for DataView
**/
class DataViewConnector extends Connector{
/*! constructor
Here initilization of all Masters occurs, execution timer initialized
@param res
db connection resource
@param type
string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided
@param item_type
name of class, which will be used for item rendering, optional, DataItem will be used by default
@param data_type
name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default.
*/
public function __construct($res,$type=false,$item_type=false,$data_type=false){
if (!$item_type) $item_type="DataViewDataItem";
if (!$data_type) $data_type="DataProcessor";
parent::__construct($res,$type,$item_type,$data_type);
}
//parse GET scoope, all operations with incoming request must be done here
function parse_request(){
parent::parse_request();
if (isset($_GET["posStart"]) && isset($_GET["count"]))
$this->request->set_limit($_GET["posStart"],$_GET["count"]);
}
/*! renders self as xml, starting part
*/
protected function xml_start(){
$attributes = "";
foreach($this->attributes as $k=>$v)
$attributes .= " ".$k."='".$v."'";
$start.= ">";
if ($this->dload){
if ($pos=$this->request->get_start())
return "<data pos='".$pos."'".$attributes.">";
else
return "<data total_count='".$this->sql->get_size($this->request)."'".$attributes.">";
}
else
return "<data".$attributes.">";
}
}
?>
\ No newline at end of file
<?php
/*
@author dhtmlx.com
@license GPL, see license.txt
*/
require_once("db_common.php");
/*! Implementation of DataWrapper for PostgreSQL
**/
class AdoDBDataWrapper extends DBDataWrapper{
protected $last_result;
public function query($sql){
LogMaster::log($sql);
if (is_array($sql)) {
$res = $this->connection->SelectLimit($sql['sql'], $sql['numrows'], $sql['offset']);
} else {
$res = $this->connection->Execute($sql);
}
if ($res===false) throw new Exception("ADODB operation failed\n".$this->connection->ErrorMsg());
$this->last_result = $res;
return $res;
}
public function get_next($res){
if (!$res)
$res = $this->last_result;
if ($res->EOF)
return false;
$row = $res->GetRowAssoc(false);
$res->MoveNext();
return $row;
}
protected function get_new_id(){
return $this->connection->Insert_ID();
}
public function escape($data){
return $this->connection->addq($data);
}
/*! escape field name to prevent sql reserved words conflict
@param data
unescaped data
@return
escaped data
*/
public function escape_name($data){
if ((strpos($data,"`")!==false || is_int($data)) || (strpos($data,".")!==false))
return $data;
return '`'.$data.'`';
}
protected function select_query($select,$from,$where,$sort,$start,$count){
if (!$from)
return $select;
$sql="SELECT ".$select." FROM ".$from;
if ($where) $sql.=" WHERE ".$where;
if ($sort) $sql.=" ORDER BY ".$sort;
if ($start || $count) {
$sql=array("sql"=>$sql,'numrows'=>$count, 'offset'=>$start);
}
return $sql;
}
}
?>
\ No newline at end of file
<?php
/*
@author dhtmlx.com
@license GPL, see license.txt
*/
require_once('db_common.php');
if (!defined('DHX_IGNORE_EMPTY_ROWS')) {
define('DHX_IGNORE_EMPTY_ROWS', true);
}
class ExcelDBDataWrapper extends DBDataWrapper {
public $emptyLimit = 10;
public function excel_data($points){
$path = $this->connection;
$excel = PHPExcel_IOFactory::createReaderForFile($path);
$excel = $excel->load($path);
$result = array();
$excelWS = $excel->getActiveSheet();
for ($i=0; $i < sizeof($points); $i++) {
$c = array();
preg_match("/^([a-zA-Z]+)(\d+)/", $points[$i], $c);
if (count($c) > 0) {
$col = PHPExcel_Cell::columnIndexFromString($c[1]) - 1;
$cell = $excelWS->getCellByColumnAndRow($col, (int)$c[2]);
$result[] = $cell->getValue();
}
}
return $result;
}
public function select($source) {
$path = $this->connection;
$excel = PHPExcel_IOFactory::createReaderForFile($path);
$excel->setReadDataOnly(false);
$excel = $excel->load($path);
$excRes = new ExcelResult();
$excelWS = $excel->getActiveSheet();
$addFields = true;
$coords = array();
if ($source->get_source() == '*') {
$coords['start_row'] = 0;
$coords['end_row'] = false;
} else {
$c = array();
preg_match("/^([a-zA-Z]+)(\d+)/", $source->get_source(), $c);
if (count($c) > 0) {
$coords['start_row'] = (int) $c[2];
} else {
$coords['start_row'] = 0;
}
$c = array();
preg_match("/:(.+)(\d+)$/U", $source->get_source(), $c);
if (count($c) > 0) {
$coords['end_row'] = (int) $c[2];
} else {
$coords['end_row'] = false;
}
}
$i = $coords['start_row'];
$end = 0;
while ((($coords['end_row'] == false)&&($end < $this->emptyLimit))||(($coords['end_row'] !== false)&&($i < $coords['end_row']))) {
$r = Array();
$emptyNum = 0;
for ($j = 0; $j < count($this->config->text); $j++) {
$col = PHPExcel_Cell::columnIndexFromString($this->config->text[$j]['name']) - 1;
$cell = $excelWS->getCellByColumnAndRow($col, $i);
if (PHPExcel_Shared_Date::isDateTime($cell)) {
$r[PHPExcel_Cell::stringFromColumnIndex($col)] = PHPExcel_Shared_Date::ExcelToPHP($cell->getValue());
} else if ($cell->getDataType() == 'f') {
$r[PHPExcel_Cell::stringFromColumnIndex($col)] = $cell->getCalculatedValue();
} else {
$r[PHPExcel_Cell::stringFromColumnIndex($col)] = $cell->getValue();
}
if ($r[PHPExcel_Cell::stringFromColumnIndex($col)] == '') {
$emptyNum++;