This is an automated email from the ASF dual-hosted git repository.
jleroux pushed a commit to branch trunk
in repositoryhttps://gitbox.apache.org/repos/asf/ofbiz-plugins.git
The following commit(s) were added to refs/heads/trunk by this push:
new 5009ffd84 Fixed: jsgantt-improved bloks qs.js update (OFBIZ-13339)
5009ffd84 is described below
commit 5009ffd842011a1d469f35a9f29beb4db6af956e
Author: Jacques Le Roux<[email protected]>
AuthorDate: Tue Apr 14 18:29:27 2026 +0200
Fixed: jsgantt-improved bloks qs.js update (OFBIZ-13339)
Reapply "Fixed: jsgantt-improved bloks qs.js update (OFBIZ-13339)"
This reverts commit c7d82dd26980e5f693c45826c689633be00543e0.
Reapply "Fixed: jsgantt-improved bloks qs.js update (OFBIZ-13339)"
This reverts commit 5292829b317f8e5fd39a4db65392cf8db8070b08.
We recently completely removed the Gant feature in project application because
it had multiple serious security issues, and we thought that it was not
maintained.
Finally withhttps://github.com/jsGanttImproved/jsgantt-improved/issues/384
The jsgantt-improved project should be safe again. At least, according to
the
author of the fix, npm does not find any issue.
I'm still unsure because the security issues were reported to us by
Dependabot and not npm. And before npm did not alert us, maybe because it
only
verifies packages in framework and application (not sure about that, I'll
dig it)
As Jacopo mentioned on dev ML, I missed to update npm. After asking to Mario Mol,
the last version is now
3.0.0:https://www.npmjs.com/package/jsgantt-improved
So the package-lock.json and package.json have been updated.
We will see if Dependabot does not report security issues, else a revert of this
commit will be necessary again.
If it's OK a backport to 24.09 will be done.
Thanks: Mario Mol
---
projectmgr/README.md | 1 +
projectmgr/build.gradle | 26 +++
projectmgr/config/ProjectMgrUiLabels.xml | 24 +++
.../src/docs/asciidoc/_include/prjm_project.adoc | 1 +
.../org/apache/ofbiz/projectmgr/GanttChart.groovy | 177 +++++++++++++++++++++
projectmgr/template/project/GanttChart.ftl | 26 +++
.../webapp/projectmgr/WEB-INF/controller.xml | 10 +-
projectmgr/webapp/projectmgr/WEB-INF/web.xml | 2 +-
projectmgr/webapp/projectmgr/package-lock.json | 28 ++++
projectmgr/webapp/projectmgr/package.json | 9 ++
projectmgr/webapp/projectmgr/static/projectmgr.css | 25 +++
projectmgr/webapp/projectmgr/static/projectmgr.js | 40 +++++
projectmgr/widget/CommonScreens.xml | 1 +
projectmgr/widget/ProjectMenus.xml | 8 +
projectmgr/widget/ProjectScreens.xml | 32 ++++
15 files changed, 407 insertions(+), 3 deletions(-)
diff --git a/projectmgr/README.md b/projectmgr/README.md
index 9ffa2dda5..dec6f01f3 100644
--- a/projectmgr/README.md
+++ b/projectmgr/README.md
@@ -13,6 +13,7 @@ The Project Management application enables organisations to
manage their interna
Resource assignement to projects
Resource allocation to tasks
Approval of time spent on tasks
+ Gantt charts for projects, phases and tasks
Time registration with different rates
Generate invoice from a project
Project copy
diff --git a/projectmgr/build.gradle b/projectmgr/build.gradle
new file mode 100644
index 000000000..6e020480f
--- /dev/null
+++ b/projectmgr/build.gradle
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+plugins {
+ id 'ofbiz-node-conventions'
+}
+
+node {
+ nodeProjectDir = file("webapp/projectmgr")
+}
diff --git a/projectmgr/config/ProjectMgrUiLabels.xml
b/projectmgr/config/ProjectMgrUiLabels.xml
index b369123db..b07a11874 100644
--- a/projectmgr/config/ProjectMgrUiLabels.xml
+++ b/projectmgr/config/ProjectMgrUiLabels.xml
@@ -833,6 +833,18 @@
<value xml:lang="zh">项目账单</value>
<value xml:lang="zh-TW">專案帳單</value>
</property>
+ <property key="PageTitleProjectGanttChart">
+ <value xml:lang="de">Projekt Gantt Chart</value>
+ <value xml:lang="en">Project Gantt Chart</value>
+ <value xml:lang="fr">Graphe Gantt du projet</value>
+ <value xml:lang="it">Progetto grafico gantt</value>
+ <value xml:lang="ja">プロジェクトガントチャート</value>
+ <value xml:lang="nl">Gant chart</value>
+ <value xml:lang="th">แผนผังคุมการกำหนดโปรเจค</value>
+ <value xml:lang="vi">Biểu đồ CHUNG của dự án</value>
+ <value xml:lang="zh">项目甘特图</value>
+ <value xml:lang="zh-TW">專案甘特圖</value>
+ </property>
<property key="PageTitleProjectInformation">
<value xml:lang="de">Projektinformationen</value>
<value xml:lang="en">Project Information</value>
@@ -1494,6 +1506,18 @@
<value xml:lang="zh">周五</value>
<value xml:lang="zh-TW">週五</value>
</property>
+ <property key="ProjectMgrGanttChart">
+ <value xml:lang="de">Gantt Chart</value>
+ <value xml:lang="en">Gantt Chart</value>
+ <value xml:lang="fr">Diagramme de Gantt</value>
+ <value xml:lang="it">Grafico gantt</value>
+ <value xml:lang="ja">ガントチャート</value>
+ <value xml:lang="nl">GANT chart</value>
+ <value xml:lang="th">แผนผังคุมการกำหนดงาน</value>
+ <value xml:lang="vi">Biểu đồ CHUNG</value>
+ <value xml:lang="zh">甘特图</value>
+ <value xml:lang="zh-TW">甘特圖</value>
+ </property>
<property key="ProjectMgrListMailingList">
<value xml:lang="de">Liste Mailing-List auf</value>
<value xml:lang="en">List Mailing List</value>
diff --git a/projectmgr/src/docs/asciidoc/_include/prjm_project.adoc
b/projectmgr/src/docs/asciidoc/_include/prjm_project.adoc
index 837b28d6c..b986246a7 100644
--- a/projectmgr/src/docs/asciidoc/_include/prjm_project.adoc
+++ b/projectmgr/src/docs/asciidoc/_include/prjm_project.adoc
@@ -23,6 +23,7 @@ It is also the starting point to navigate to the various sub
screens related to
== Sub screens
Following sub screens are provided:
+* Gantt chart
* Mailing List
* Phases
* Tasks
diff --git
a/projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/GanttChart.groovy
b/projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/GanttChart.groovy
new file mode 100644
index 000000000..2f375a7b5
--- /dev/null
+++ b/projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/GanttChart.groovy
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+package org.apache.ofbiz.projectmgr
+
+import groovy.json.JsonOutput
+import org.apache.ofbiz.base.util.UtilDateTime
+import org.apache.ofbiz.entity.GenericValue
+import org.apache.ofbiz.entity.condition.EntityCondition
+import org.apache.ofbiz.entity.condition.EntityConditionList
+import org.apache.ofbiz.entity.condition.EntityExpr
+import org.apache.ofbiz.entity.condition.EntityOperator
+
+import jakarta.servlet.http.HttpSession
+import java.math.RoundingMode
+import java.sql.Timestamp
+
+final String taskStatusCompleted = 'PTS_COMPLETED'
+final int defaultTaskDurationDays = 3
+final String milestoneWorkEffortTypeId = 'MILESTONE'
+final String permissionCheckEntity = 'PROJECTMGR'
+final BigDecimal hundredPercent = new BigDecimal(100)
+
+String projectId = parameters.projectId
+GenericValue userLogin = parameters.userLogin as GenericValue
+
+Map<String, Object> result = runService('getProject', [projectId: projectId,
userLogin: userLogin])
+Map<String, Object> project = result.projectInfo as Map<String, Object>
+if (project == null) {
+ return
+}
+
+context.chartStart = project.startDate ?: UtilDateTime.nowTimestamp() //
default today's date
+context.chartEnd = project.completionDate ?:
UtilDateTime.addDaysToTimestamp(UtilDateTime.nowTimestamp(), 14)
+
+List ganttList = []
+List<Map<String, Object>> ganttItems = []
+
+result = runService('getProjectPhaseList', [userLogin: userLogin, projectId:
projectId])
+List<Map<String, Object>> phases = result.phaseList as List<Map<String,
Object>>
+if (phases) {
+ phases.each { phase ->
+ Map<String, Object> newPhase = phase
+ newPhase.phaseNr = phase.phaseId
+ if (!newPhase.estimatedStartDate && newPhase.actualStartDate) {
+ newPhase.estimatedStartDate = newPhase.actualStartDate
+ }
+ newPhase.estimatedStartDate = newPhase.estimatedStartDate ?:
context.chartStart
+
+ if (!newPhase.estimatedCompletionDate &&
newPhase.actualCompletionDate) {
+ newPhase.estimatedCompletionDate =
newPhase.actualCompletionDateDate
+ }
+ newPhase.estimatedCompletionDate = newPhase.estimatedCompletionDate
+ ?: UtilDateTime.addDaysToTimestamp(newPhase.estimatedStartDate
as Timestamp, defaultTaskDurationDays)
+
+ newPhase.workEffortTypeId = 'PHASE'
+ ganttList.add(newPhase)
+
+ ganttItems << [
+ pID: phase.phaseId as Integer,
+ pName: phase.phaseSeqNum ? "${phase.phaseSeqNum}.
${phase.phaseName}" : phase.phaseName,
+ pStart: '',
+ pEnd: '',
+ pPlanStart: '',
+ pPlanEnd: '',
+ pClass: 'ggroupblack',
+ pLink: '',
+ pMile: 0,
+ pRes: '',
+ pComp: 0,
+ pGroup: 1,
+ pParent: 0,
+ pOpen: 1,
+ pDepend: '',
+ ]
+
+ EntityConditionList<EntityExpr> cond = EntityCondition.makeCondition([
+ EntityCondition.makeCondition('currentStatusId',
EntityOperator.NOT_EQUAL, 'PTS_CANCELLED'),
+ EntityCondition.makeCondition('workEffortParentId',
EntityOperator.EQUALS, phase.phaseId)
+ ], EntityOperator.AND)
+ List<GenericValue> tasks =
from('WorkEffort').where(cond).orderBy('sequenceNum', 'workEffortName').queryList()
+
+ if (tasks) {
+ tasks.each { task ->
+ Map<String, Object> resultTaskInfo =
runService('getProjectTask', [userLogin: userLogin, taskId: task.workEffortId])
+ Map<String, Object> taskInfo = resultTaskInfo.taskInfo as
Map<String, Object>
+ taskInfo.taskNr = task.workEffortId
+ taskInfo.phaseNr = phase.phaseId
+
+ taskInfo.resource = ((taskInfo.plannedHours &&
+ taskStatusCompleted != taskInfo.currentStatusId &&
+ taskInfo.plannedHours > taskInfo.actualHours) ?
taskInfo.plannedHours : taskInfo.actualHours) +
+ ' Hrs'
+
+ BigDecimal durationHours = resultTaskInfo.plannedHours as
BigDecimal
+ if (taskStatusCompleted == taskInfo.currentStatusId) {
+ taskInfo.completion = hundredPercent
+ } else {
+ BigDecimal actualHours = taskInfo.actualHours as BigDecimal
+ BigDecimal plannedHours = taskInfo.plannedHours as
BigDecimal
+ taskInfo.completion = (actualHours && plannedHours)
+ ? (actualHours *
hundredPercent).divide(plannedHours, 0, RoundingMode.UP)
+ : 0
+ }
+ if (!taskInfo.estimatedStartDate && taskInfo.actualStartDate) {
+ taskInfo.estimatedStartDate = taskInfo.actualStartDate
+ }
+ taskInfo.estimatedStartDate = taskInfo.estimatedStartDate ?:
newPhase.estimatedStartDate
+
+ if (!taskInfo.estimatedCompletionDate &&
taskInfo.actualCompletionDate) {
+ taskInfo.estimatedCompletionDate =
taskInfo.actualCompletionDate
+ }
+ if (!taskInfo.estimatedCompletionDate && !durationHours) {
+ taskInfo.estimatedCompletionDate =
+
UtilDateTime.addDaysToTimestamp(newPhase.estimatedStartDate as Timestamp,
+ defaultTaskDurationDays)
+ } else if (!taskInfo.estimatedCompletionDate && durationHours)
{
+ taskInfo.estimatedCompletionDate =
+
UtilDateTime.addDaysToTimestamp(newPhase.estimatedStartDate as Timestamp,
+ durationHours / 8)
+ }
+
+ taskInfo.workEffortTypeId = task.workEffortTypeId
+ if (security.hasEntityPermission(permissionCheckEntity,
'_READ', session as HttpSession) ||
+ security.hasEntityPermission(permissionCheckEntity,
'_ADMIN', session as HttpSession)) {
+ taskInfo.url =
'/projectmgr/control/taskView?workEffortId=' + task.workEffortId
+ } else {
+ taskInfo.url = ''
+ }
+
+ List<GenericValue> preTaskAssociations =
from('WorkEffortAssoc')
+ .where('workEffortIdTo', task.workEffortId)
+ .orderBy('workEffortIdFrom')
+ .queryList()
+
+ taskInfo.preDecessor =
preTaskAssociations*.getRelatedOne('FromWorkEffort', false)
+ .collect { we -> we.workEffortId as String }
+ .join(',')
+
+ ganttItems << [
+ pID: taskInfo.taskNr as Integer,
+ pName: taskInfo.taskSeqNum ? "${taskInfo.taskSeqNum}.
${taskInfo.taskName}" : taskInfo.taskName,
+ pStart: (taskInfo.estimatedStartDate as
Timestamp).toLocalDateTime().dateString,
+ pEnd: (taskInfo.estimatedCompletionDate as
Timestamp).toLocalDateTime().dateString,
+ pPlanStart: (taskInfo.estimatedStartDate as
Timestamp).toLocalDateTime().dateString,
+ pPlanEnd: (taskInfo.estimatedCompletionDate as
Timestamp).toLocalDateTime().dateString,
+ pClass: taskInfo.workEffortTypeId ==
milestoneWorkEffortTypeId ? 'gmilestone' : 'gtaskgreen',
+ pLink: taskInfo.url,
+ pMile: taskInfo.workEffortTypeId ==
milestoneWorkEffortTypeId ? 1 : 0,
+ pRes: taskInfo.resource,
+ pComp: taskInfo.completion,
+ pGroup: 0,
+ pParent: taskInfo.phaseNr,
+ pOpen: taskInfo.workEffortTypeId ==
milestoneWorkEffortTypeId ? 0 : 1,
+ pDepend: taskInfo.preDecessor ?: '',
+ ]
+ }
+ }
+ }
+}
+
+context.phaseTaskListJson = JsonOutput.toJson(ganttItems)
diff --git a/projectmgr/template/project/GanttChart.ftl
b/projectmgr/template/project/GanttChart.ftl
new file mode 100644
index 000000000..cb08a3697
--- /dev/null
+++ b/projectmgr/template/project/GanttChart.ftl
@@ -0,0 +1,26 @@
+<#-- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -->
+<#--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<div style="position:relative" class="gantt" id="GanttChartDIV"></div>
+
+<input id="ofbizGantItemsJson" type="hidden" value="${phaseTaskListJson}"/>
+
+<script type="text/javascript"
src="/projectmgr/node_modules/jsgantt-improved/dist/jsgantt.js"></script>
+<script type="text/javascript" src="/projectmgr/static/projectmgr.js"></script>
diff --git a/projectmgr/webapp/projectmgr/WEB-INF/controller.xml
b/projectmgr/webapp/projectmgr/WEB-INF/controller.xml
index 4ce7ef832..5123cb3e6 100644
--- a/projectmgr/webapp/projectmgr/WEB-INF/controller.xml
+++ b/projectmgr/webapp/projectmgr/WEB-INF/controller.xml
@@ -34,6 +34,11 @@
<response name="success" type="view" value="main"/>
</request-map>
+ <request-map uri="ganttChart">
+ <security https="true" auth="true"/>
+ <response name="success" type="view" value="ganttChart"/>
+ </request-map>
+
<request-map uri="ListSubProjects">
<security https="true" auth="true"/>
<response name="success" type="view" value="ListSubProjects"/>
@@ -107,8 +112,8 @@
<request-map uri="scheduleProject">
<security https="true" auth="true"/>
<event type="service" invoke="scheduleProject"/>
- <response name="success" type="view" value="projectView"/>
- <response name="error" type="view" value="projectView"/>
+ <response name="success" type="view" value="ganttChart"/>
+ <response name="error" type="view" value="ganttChart"/>
</request-map>
<request-map uri="FindResource">
<security https="true" auth="true"/>
@@ -692,6 +697,7 @@
<view-map name="EditPartyRates" type="screen"
page="component://projectmgr/widget/ResourceScreens.xml#EditPartyRates"/>
<view-map name="main" type="screen" page="component://projectmgr/widget/CommonScreens.xml#Main"/>
+ <view-map name="ganttChart" type="screen"
page="component://projectmgr/widget/ProjectScreens.xml#ganttChart"/>
<view-map name="ListSubProjects" type="screen"
page="component://projectmgr/widget/ProjectScreens.xml#ListSubProjects"/>
<view-map name="FindResource" type="screen"
page="component://projectmgr/widget/ResourceScreens.xml#FindResource"/>
<view-map name="FindProject" type="screen"
page="component://projectmgr/widget/ProjectScreens.xml#FindProject"/>
diff --git a/projectmgr/webapp/projectmgr/WEB-INF/web.xml
b/projectmgr/webapp/projectmgr/WEB-INF/web.xml
index d86c38317..2aa29a5d7 100644
--- a/projectmgr/webapp/projectmgr/WEB-INF/web.xml
+++ b/projectmgr/webapp/projectmgr/WEB-INF/web.xml
@@ -64,7 +64,7 @@
<filter-class>org.apache.ofbiz.webapp.control.ControlFilter</filter-class>
<init-param>
<param-name>allowedPaths</param-name>
-
<param-value>/error:/control:/select:/index.html:/index.jsp:/default.html:/default.jsp:/images</param-value>
+
<param-value>/error:/control:/select:/index.html:/index.jsp:/default.html:/default.jsp:/images:/static:/js:/node_modules/jsgantt-improved</param-value>
</init-param>
<init-param>
<param-name>redirectPath</param-name>
diff --git a/projectmgr/webapp/projectmgr/package-lock.json
b/projectmgr/webapp/projectmgr/package-lock.json
new file mode 100644
index 000000000..0ebc10662
--- /dev/null
+++ b/projectmgr/webapp/projectmgr/package-lock.json
@@ -0,0 +1,28 @@
+{
+ "name": "ofbiz-projectmgr",
+ "requires": true,
+ "lockfileVersion": 1,
+ "dependencies": {
+ "@types/node": {
+ "version": "24.12.2",
+
"resolved":"https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz",
+ "integrity":
"sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==",
+ "requires": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "jsgantt-improved": {
+ "version": "3.0.0",
+
"resolved":"https://registry.npmjs.org/jsgantt-improved/-/jsgantt-improved-3.0.0.tgz",
+ "integrity":
"sha512-0OtLBSmb5e0WAroRhp0sT+hJBSYM9x0zsGxXpv+RSVpoQ4HovE5+G5c0SDD+1ogXld9dkJtbDGCmvJQYhxCxwA==",
+ "requires": {
+ "@types/node": "^24.3.0"
+ }
+ },
+ "undici-types": {
+ "version": "7.16.0",
+
"resolved":"https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity":
"sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="
+ }
+ }
+}
diff --git a/projectmgr/webapp/projectmgr/package.json
b/projectmgr/webapp/projectmgr/package.json
new file mode 100644
index 000000000..22902caa1
--- /dev/null
+++ b/projectmgr/webapp/projectmgr/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "ofbiz-projectmgr",
+ "description": "ofbiz-plugins projectmgr NPM dependencies configuration",
+ "repository":"https://github.com/apache/ofbiz-plugins",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "jsgantt-improved": "^3.0.0"
+ }
+}
diff --git a/projectmgr/webapp/projectmgr/static/projectmgr.css
b/projectmgr/webapp/projectmgr/static/projectmgr.css
new file mode 100644
index 000000000..ac6061151
--- /dev/null
+++ b/projectmgr/webapp/projectmgr/static/projectmgr.css
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* This CSS file is used for the Project Manager component. */
+
+/* Remove any margins that my have been set on all divs by the themes, such as
Rainbowstone */
+.gchartcontainer div {
+ margin: unset
+}
diff --git a/projectmgr/webapp/projectmgr/static/projectmgr.js
b/projectmgr/webapp/projectmgr/static/projectmgr.js
new file mode 100644
index 000000000..f506ffd42
--- /dev/null
+++ b/projectmgr/webapp/projectmgr/static/projectmgr.js
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const ganttItemsJson = document.getElementById("ofbizGantItemsJson").value;
+const ganttItems = JSON.parse(ganttItemsJson);
+
+const g = new JSGantt.GanttChart(document.getElementById('GanttChartDIV'),
'day');
+g.setShowRes(1); // Show/Hide Resource (0/1)
+g.setShowDur(1); // Show/Hide Duration (0/1)
+g.setShowComp(1); // Show/Hide % Complete(0/1)
+g.setShowTaskInfoLink(1);
+g.setShowTaskInfoNotes(0)
+
+g.setDateTaskTableDisplayFormat('dd mon yyyy');
+g.setDayMajorDateDisplayFormat('dd mon');
+
+for (t of ganttItems) {
+ g.AddTaskItem(
+ new JSGantt.TaskItem(t.pID, t.pName, t.pStart, t.pEnd, t.pClass,
t.pLink, t.pMile, t.pRes, t.pComp,
+ t.pGroup, t.pParent, t.pOpen, t.pDepend, "", "", g)
+ );
+}
+
+g.Draw();
diff --git a/projectmgr/widget/CommonScreens.xml
b/projectmgr/widget/CommonScreens.xml
index 7216090d1..647770272 100644
--- a/projectmgr/widget/CommonScreens.xml
+++ b/projectmgr/widget/CommonScreens.xml
@@ -39,6 +39,7 @@ under the License.
<!-- layoutSettings.headerImageUrl can be used to specify an
application specific logo; if not set,
then the global layoutSettings.commonHeaderImageUrl
(specified in GlobalDecorator) will be used. -->
<!--<set field="layoutSettings.headerImageUrl" value="/images/ofbiz_logo.png"
global="true"/>-->
+ <set field="layoutSettings.styleSheets[]"
value="/projectmgr/static/projectmgr.css" global="true"/>
<set field="activeApp" value="projectmgr" global="true"/>
<set field="applicationMenuName" value="ProjectMgrAppBar"
global="true"/>
<set field="applicationMenuLocation"
value="${parameters.mainMenuLocation}" global="true"/>
diff --git a/projectmgr/widget/ProjectMenus.xml
b/projectmgr/widget/ProjectMenus.xml
index cab8eb9e5..0a0b3f163 100644
--- a/projectmgr/widget/ProjectMenus.xml
+++ b/projectmgr/widget/ProjectMenus.xml
@@ -83,6 +83,14 @@
"projectId", projectId));
return result.get("hasPermission"); }"
type="Boolean"/>
</actions>
+ <menu-item name="ganttchart"
title="${uiLabelMap.ProjectMgrGanttChart}">
+ <condition>
+ <if-compare field="hasViewPermission" operator="equals"
value="true"/>
+ </condition>
+ <link target="ganttChart">
+ <parameter param-name="projectId"/>
+ </link>
+ </menu-item>
<menu-item name="projectView"
title="${uiLabelMap.ProjectMgrProjectSummary}">
<condition>
<if-compare field="hasViewPermission" operator="equals"
value="true"/>
diff --git a/projectmgr/widget/ProjectScreens.xml
b/projectmgr/widget/ProjectScreens.xml
index fddf47acd..0ee555b9f 100644
--- a/projectmgr/widget/ProjectScreens.xml
+++ b/projectmgr/widget/ProjectScreens.xml
@@ -419,6 +419,29 @@ under the License.
</widgets>
</section>
</screen>
+ <screen name="ganttChart">
+ <section>
+ <actions>
+ <set field="titleProperty" value="ProjectMgrGanttChart"/>
+ <set field="tabButtonItem" value="ganttchart"/>
+ <set field="layoutSettings.styleSheets[]"
value="/projectmgr/node_modules/jsgantt-improved/dist/jsgantt.css" global="true"/>
+ <set field="layoutSettings.styleSheets[]"
value="/projectmgr/static/projectmgr.css" global="true"/>
+ <script
location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/GanttChart.groovy"/>
+ </actions>
+ <widgets>
+ <label text="${project}"/>
+ <decorator-screen name="CommonProjectDecorator"
location="${parameters.mainDecoratorLocation}">
+ <decorator-section name="body">
+ <screenlet
title="${uiLabelMap.PageTitleProjectGanttChart}">
+ <platform-specific>
+ <html><html-template multi-block="true"
location="component://projectmgr/template/project/GanttChart.ftl"/></html>
+ </platform-specific>
+ </screenlet>
+ </decorator-section>
+ </decorator-screen>
+ </widgets>
+ </section>
+ </screen>
<screen name="SubProjectsInfo">
<section>
<actions>
@@ -957,6 +980,8 @@ under the License.
<section>
<actions>
<property-map resource="ProjectMgrUiLabels" map-name="uiLabelMap"
global="true"/>
+ <set field="layoutSettings.styleSheets[]"
value="/projectmgr/node_modules/jsgantt-improved/dist/jsgantt.css" global="true"/>
+ <set field="layoutSettings.styleSheets[]"
value="/projectmgr/static/projectmgr.css" global="true"/>
<entity-condition entity-name="WorkEffortPartyAssignByGroup"
list="projects" filter-by-date="true">
<condition-list combine="and">
<condition-expr field-name="partyId"
from-field="userLogin.partyId"/>
@@ -974,6 +999,7 @@ under the License.
<actions>
<set field="projectId"
from-field="project.workEffortId"/>
<set field="parameters.projectId"
from-field="project.workEffortId"/>
+ <script
location="component://projectmgr/src/main/groovy/org/apache/ofbiz/projectmgr/GanttChart.groovy"/>
<service service-name="getProject"
result-map="result">
<field-map field-name="projectId"
from-field="projectId"/>
</service>
@@ -1016,6 +1042,12 @@ under the License.
</widgets>
</section>
</container>
+ <container style="clear"/>
+ <screenlet
title="${uiLabelMap.PageTitleProjectGanttChart}">
+ <platform-specific>
+ <html><html-template multi-block="true"
location="component://projectmgr/template/project/GanttChart.ftl"/></html>
+ </platform-specific>
+ </screenlet>
</widgets>
</section>
</iterate-section>