Commit 0b4d3b46 authored by Roque's avatar Roque

erp5_web_project_ui: new front page gadget (WIP)

parent 84bd681e
......@@ -111,12 +111,14 @@ CACHE:\n
\n
gadget_erp5_page_project_controller.html\n
gadget_erp5_page_project_controller.js\n
gadget_project_info.html\n
gadget_project_info.js\n
gadget_erp5_page_project_redirector.html\n
gadget_erp5_page_project_redirector.js\n
gadget_erp5_project_panel.html\n
gadget_erp5_project_panel.js\n
gadget_front_page_info.html\n
gadget_front_page_info.js\n
gadget_project_info.html\n
gadget_project_info.js\n
\n
favicon.ico\n
font-awesome/font-awesome-webfont.eot\n
......@@ -405,7 +407,7 @@ NETWORK:\n
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>979.50747.65222.58094</string> </value>
<value> <string>981.52234.40735.41557</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -423,7 +425,7 @@ NETWORK:\n
</tuple>
<state>
<tuple>
<float>1577216049.89</float>
<float>1581523847.97</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -62,6 +62,7 @@
<key> <string>bottom</string> </key>
<value>
<list>
<string>my_front_page_gadget_field</string>
<string>listbox</string>
</list>
</value>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="GadgetField" module="Products.ERP5Form.GadgetField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_front_page_gadget_field</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>no_validator</string> </key>
<value> <string>Does not support this operation.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>data_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>js_sandbox</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>renderjs_extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>validator_field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>validator_form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>data_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>js_sandbox</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>renderjs_extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>validator_field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>validator_form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>data_url</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value> <string>gadget_front_page_info.html</string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>js_sandbox</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>renderjs_extra</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>my_info_gadget_field</string> </value>
</item>
<item>
<key> <string>validator_field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>validator_form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Project front page gadget</title>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="gadget_front_page_info.js"></script>
<link rel="stylesheet" type="text/css" href="gadget_project_info.css">
</head>
<body>
<!-- FAKE CONTENT TO TEST STYLES -->
<div class="front-project-list">
<ul id="js-project-list">
</ul>
</div>
</body>
</html>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_front_page_info.html</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
/*jslint nomen: true, indent: 2 */
/*global window, rJS, RSVP, document, SimpleQuery, ComplexQuery, Query*/
(function (window, rJS, RSVP, document, SimpleQuery, ComplexQuery, Query) {
"use strict";
//TODO
//get lastest (unique) TR for each project
function getProjectElementList(gadget) {
//TODO ATTEMPT TO FILTER BY DATE
var date_query_list = [],
date_query,
now_date = new Date(),
one_year_old_date = new Date();
one_year_old_date.setFullYear(one_year_old_date.getFullYear() - 1);
//var date = "Wed, 16 Oct 2019 09:06:19 +0000";
//var js_date = new Date(date);
/*date_query_list.push(new SimpleQuery({
key: "portal_type",
operator: "=",
type: "simple",
value: "Task"
}));
date_query_list.push(new SimpleQuery({
type: "simple",
key: "creation_date",
value: {"query": one_year_old_date, "range": ">="}
//value: {'query': (one_year_old_date, now_date), 'range': 'minmax'}
}));
date_query = new ComplexQuery({
operator: "AND",
query_list: date_query_list,
type: "complex"
});*/
date_query = new SimpleQuery({
type: "simple",
key: "creation_date",
value: {"query": one_year_old_date, "range": ">="}
//value: {'query': (one_year_old_date, now_date), 'range': 'minmax'}
});
function getComplexQuery(query_dict, operator) {
var key,
query_list = [];
for (key in query_dict) {
if (query_dict.hasOwnProperty(key)) {
query_list.push(new SimpleQuery({
key: key,
operator: "=",
type: "simple",
value: query_dict[key]
}));
}
}
return new ComplexQuery({
operator: operator,
query_list: query_list,
type: "complex"
});
}
var i,
project_query = getComplexQuery({"portal_type" : "Project",
"validation_state" : "validated"},
"AND"),
milestone_query = getComplexQuery({"portal_type" : "Project Milestone",
"parent__validation_state" : "validated"},
"AND"),
non_milestone_query,
aux_complex_query,
aux_query_list = [],
query_list = [],
portal_type_list = ["Task", "Bug", "Task Report", "Benchmark Result"],
valid_state_list = ["planned", "ordered", "confirmed", "started", "stopped", "delivered", "ready", "failed", "public_stopped"];
//validated project tasks, bugs, etc
query_list.push(new SimpleQuery({
key: "source_project__validation_state",
operator: "=",
type: "simple",
value: "validated"
}));
//portal types
/*//ATTEMPT 1 TO AVOID ITERATE portal_type VALUE LIST
//(from jio documentation)
var in_list = function (object_value, comparison_value_list) {
return comparison_value_list.indexOf(object_value) >= 0;
};
aux_complex_query = new SimpleQuery({
type: "simple",
key: {
read_from: "portal_type",
equal_match: in_list
},
value: ["Task", "Bug", "Task Report", "Benchmark Result"]
});
// in_list function is never called, no query on portal types
//ATTEMPT 2 TO AVOID ITERATE portal_type VALUE LIST (same for states)
aux_complex_query = new SimpleQuery({
key: "portal_type",
operator: "=",
type: "simple",
value: ("Task", "Bug", "Task Report", "Benchmark Result")
});
//only last portal type value of the list is considered
//the rest, ignored*/
aux_query_list = [];
for (i = 0; i < portal_type_list.length; i += 1) {
aux_query_list.push(new SimpleQuery({
key: "portal_type",
operator: "=",
type: "simple",
value: portal_type_list[i]
}));
}
aux_complex_query = new ComplexQuery({
operator: "OR",
query_list: aux_query_list,
type: "complex"
});
query_list.push(aux_complex_query);
//states for tasks, bugs, reports, tests
aux_query_list = [];
for (i = 0; i < valid_state_list.length; i += 1) {
aux_query_list.push(new SimpleQuery({
key: "simulation_state",
operator: "=",
type: "simple",
value: valid_state_list[i]
}));
}
aux_complex_query = new ComplexQuery({
operator: "OR",
query_list: aux_query_list,
type: "complex"
});
query_list.push(aux_complex_query);
// TODO: filter result by too old creation date? to reduce results
query_list.push(date_query);
non_milestone_query = new ComplexQuery({
operator: "AND",
query_list: query_list,
type: "complex"
});
return new RSVP.Queue()
.push(function () {
var promise_list = [],
//TODO: review limit and fields
limit = [0, 1000],
select_list = ['source_project', 'portal_type', 'stop_date', 'modification_date', 'simulation_state', 'creation_date'];
// XXX: two separated queries because this query fails with not implemented error:
// ( parent__validation_state = "validated" OR source_project__validation_state = "validated" )
// TODO: do some research
promise_list.push(gadget.jio_allDocs({
query: Query.objectToSearchText(milestone_query),
limit: limit,
select_list: select_list,
sort_on: [["modification_date", "descending"]]
}));
promise_list.push(gadget.jio_allDocs({
query: Query.objectToSearchText(non_milestone_query),
limit: limit,
select_list: select_list,
sort_on: [["modification_date", "descending"]]
}));
promise_list.push(gadget.jio_allDocs({
query: Query.objectToSearchText(project_query),
limit: limit,
select_list: ['title'],
sort_on: [["modification_date", "descending"]]
}));
/*promise_list.push(gadget.jio_allDocs({
query: Query.objectToSearchText(date_query),
limit: [0, 30],
select_list: ['title', 'creation_date'],
sort_on: [["creation_date", "descending"]]
}));*/
return RSVP.all(promise_list);
})
.push(function (result_list) {
return [result_list[0].data.rows.concat(result_list[1].data.rows), result_list[2].data.rows];
});
}
function renderProjectList(element_list, project_list) {
var i,
project,
item,
status_ok,
project_id,
project_row,
project_li,
project_html_element_list,
project_line_html_element_list,
left_div,
left_line_div,
status_span,
total_span,
fail_span,
ul_list = document.getElementById("js-project-list"),
project_list_dict = {};
function getProjectId(id) {
var segments = id.split("/");
if (segments.length === 2) {
return id;
}
return segments.slice(0, -1).join("/");
}
function setStatus(item) {
//TODO check modification date and return item with status 0, 1 or 2 (green, orange, red)
//we need 2 limit dates that should be based on portal type (e.g. Milestones segmented by months, maybe Taks by weeks)
//where to set this limits dates? a manifest? site configuration?
var js_date = new Date(item.value.modification_date);
item.status = 0;
item.status_color = "green";
//for quick testing purposes
if (item.id === "project_module/1/7") {
item.status = 2;
item.status_color = "red";
}
if (item.id === "project_module/1/6") {
item.status = 1;
item.status_color = "orange";
}
if (item.id === "bug_module/5") {
item.status = 1;
item.status_color = "orange";
}
//for test results, check validation state (0 = pass, 2 = fail)
return item;
}
function createProjectHtmlElement(project_id, project_title) {
var project_element = document.createElement('li'),
box_div = document.createElement('div'),
title_div = document.createElement('div'),
info_div = document.createElement('div'),
left_info_div = document.createElement('div'),
right_div = document.createElement('div'),
right_line_div = document.createElement('div'),
forum_link = document.createElement('a');
box_div.classList.add("project-box");
title_div.classList.add("project-title");
title_div.innerHTML = project_title;
info_div.classList.add("project-info");
left_info_div.classList.add("left");
right_line_div.classList.add("project-line");
forum_link.href = "todo";
forum_link.innerHTML = project_id + " forum link";
right_line_div.appendChild(forum_link);
right_div.appendChild(right_line_div);
info_div.appendChild(left_info_div);
info_div.appendChild(right_div);
box_div.appendChild(title_div);
box_div.appendChild(info_div);
project_element.appendChild(box_div);
return [project_element, left_info_div];
}
function createProjectLineHtmlElement(portal_type, total_count, out_count, status_color) {
var line_div = document.createElement('div'),
status = document.createElement('span'),
name = document.createElement('span'),
fail = document.createElement('span'),
total = document.createElement('span');
line_div.classList.add("project-line");
status.classList.add("status");
status.classList.add(status_color);
name.classList.add("name");
name.innerHTML = portal_type;
total.innerHTML = total_count;
fail.innerHTML = "(" + out_count + ")";
line_div.appendChild(status);
line_div.appendChild(name);
line_div.appendChild(total);
line_div.appendChild(fail);
return [line_div, status, total, fail];
}
for (i = 0; i < project_list.length; i += 1) {
project_html_element_list = createProjectHtmlElement(project_list[i].id, project_list[i].value.title);
project_list_dict[project_list[i].id] = {"html_element" : project_html_element_list[0],
"left_div_html" : project_html_element_list[1]};
}
for (i = 0; i < element_list.length; i += 1) {
item = setStatus(element_list[i]);
status_ok = ((item.status > 0) ? 0 : 1);
project_id = ((item.value.source_project) ?
getProjectId(item.value.source_project) : getProjectId(item.id));
if (project_list_dict.hasOwnProperty(project_id)) {
if (project_list_dict[project_id].hasOwnProperty(item.value.portal_type)) {
project_row = project_list_dict[project_id][item.value.portal_type];
project_row.total_count += 1;
project_row.total_html.innerHTML = project_row.total_count;
if (!status_ok) {
project_row.out_count += 1;
project_row.out_html.innerHTML = "(" + project_row.out_count + ")";
if (project_row.status < item.status) {
project_row.status = item.status;
project_row.status_html.classList.remove("green", "red", "orange");
project_row.status_html.classList.add(item.status_color);
}
}
} else {
project_line_html_element_list = createProjectLineHtmlElement(item.value.portal_type, 1, 0 + !status_ok, item.status_color);
left_line_div = project_line_html_element_list[0];
status_span = project_line_html_element_list[1];
total_span = project_line_html_element_list[2];
fail_span = project_line_html_element_list[3];
project_list_dict[project_id].left_div_html.appendChild(left_line_div);
project_list_dict[project_id][item.value.portal_type] = { "status": item.status,
"status_html" : status_span,
"total_count" : 1,
"total_html" : total_span,
"out_count" : 0 + !status_ok,
"out_html" : fail_span
};
}
}
}
console.log("project_list_dict:", project_list_dict);
for (project in project_list_dict) {
if (project_list_dict.hasOwnProperty(project)) {
ul_list.appendChild(project_list_dict[project].html_element);
}
}
}
rJS(window)
.declareAcquiredMethod("getUrlForList", "getUrlForList")
.declareAcquiredMethod("getUrlFor", "getUrlFor")
.declareAcquiredMethod("jio_allDocs", "jio_allDocs")
.declareAcquiredMethod("jio_getAttachment", "jio_getAttachment")
.declareAcquiredMethod("getSetting", "getSetting")
.declareMethod('render', function (options) {
return this.changeState(options);
})
.onStateChange(function () {
var gadget = this;
return getProjectElementList(gadget)
.push(function (element_list) {
console.log("element_list:", element_list);
renderProjectList(element_list[0], element_list[1]);
});
})
.declareMethod('getContent', function () {
return {};
})
.declareMethod('checkValidity', function () {
return true;
});
}(window, rJS, RSVP, document, SimpleQuery, ComplexQuery, Query));
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_front_page_info.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/javascript</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
#generate-rss {
padding: 5.5pt;
margin-right: 12pt;
background-color: #FF6600;
border-color: #FF6600;
color: #FFFFFF;
border-radius: 0.325em;
border-width: 0.5px;
border-style: solid;
min-width: 6em;
line-height: 1.5;
display: none;
text-align: center;
width: fit-content;
/* FRONT PAGE */
div[data-gadget-url$="gadget_front_page_info.html"] .front-project-list {
margin-top: 10px;
}
.restore-button {
padding: 6pt;
margin-right: 12pt;
background-color: #FF6600;
color: #FFFFFF;
border-radius: 0.325em;
border-width: 0.5px;
div[data-gadget-url$="gadget_front_page_info.html"] .front-project-list .project-box {
margin-bottom: 10px;
border-style: solid;
min-width: 8em;
line-height: 1.5;
border-width: 1px;
border-radius: 5px;
padding: 5px;
padding: 5px;
padding-left: 15px;
}
#wrap1 iframe {
height: 100%;
div[data-gadget-url$="gadget_front_page_info.html"] a {
color: #267B87;
text-decoration: none;
}
#wrap2 iframe {
height: 100%;
div[data-gadget-url$="gadget_front_page_info.html"] .front-project-list .project-box .project-info {
display: inline-flex;
}
.gadget-content .ui-field-contain .graph-spinner {
position: relative;
top: 100px;
width: 120px;
div[data-gadget-url$="gadget_front_page_info.html"] .front-project-list .project-box .project-info .left {
width: 35%;
}
.gadget-content .ui-field-contain .count-spinner {
top: 20px;
width: 120px;
div[data-gadget-url$="gadget_front_page_info.html"] .front-project-list .project-box .project-line {
margin-bottom: 5px;
font-family: "Roboto", Arial, sans-serif;
font-size: 1.25em;
line-height: 1.58em;
letter-spacing: -.003em;
}
div[data-gadget-url$="gadget_front_page_info.html"] .front-project-list .project-box .project-title {
color: #19535F;
font-weight: normal;
font-size: 24px;
line-height: 33px;
font-weight: bold;
margin-bottom: 5px;
}
div[data-gadget-url$="gadget_front_page_info.html"] .front-project-list .project-box .status {
padding-left: 21px;
}
div[data-gadget-url$="gadget_front_page_info.html"] .front-project-list .project-box span {
margin-right: 5px;
}
div[data-gadget-url$="gadget_front_page_info.html"] .front-project-list .project-box .status.orange {
background: #f0ad4e !important;
}
div[data-gadget-url$="gadget_front_page_info.html"] .front-project-list .project-box .status.red {
background: #de1e00 !important;
}
div[data-gadget-url$="gadget_front_page_info.html"] .front-project-list .project-box .status.green {
background: #00b80c !important;
}
/* PROJECT PAGE */
.gadget-content .ui-field-contain .bottom .first-line-buttons {
-webkit-appearance: none;
margin-top: 0;
......@@ -123,4 +145,38 @@ input[type="submit"] {
.worklist-title {
color: #777777;
margin-top: 10px;
}
/* PROJECT HOME PAGE */
body.cke_editable h1 {
margin-top: 1.5em;
font-family: "Roboto", Arial, sans-serif;
color: #19535F;
font-size: 2em;
margin: 0.67em 0;
}
body.cke_editable h2 {
font-family: "Roboto", Arial, sans-serif;
font-weight: normal;
font-size: 25px;
line-height: 33px;
color: #19535F;
}
body.cke_editable p, body.cke_editable li, body.cke_editable span {
font-family: 'Heuristica', 'Helvetica', Times, serif;
}
body.cke_editable ul li, body.cke_editable p {
font-size: 1.25em;
line-height: 1.58em;
letter-spacing: -.003em;
}
body.cke_editable a {
color: #267B87;
text-decoration: none;
}
\ No newline at end of file
......@@ -14,13 +14,15 @@
function parseHTMLLinks(html, url) {
var parser = new DOMParser(), i,
//TODO create head and link html elements and prepend to doc instead of using text
styles_header = '<head><link rel="stylesheet" type="text/css" href="gadget_project_info.css"></head>',
oSerializer = new XMLSerializer(),
doc = parser.parseFromString(html, "text/html"),
link_list = doc.getElementsByTagName("a");
for (i = 0; i < link_list.length; i += 1) {
link_list[i].setAttribute('href', addRedirectionToReference(link_list[i].getAttribute('href'), url));
}
return oSerializer.serializeToString(doc);
return styles_header + oSerializer.serializeToString(doc);
}
function enableLink(link_element, url) {
......@@ -220,7 +222,7 @@
gadget.getSetting("hateoas_url")
];
if (modification_dict.publication_section) {
promise_list.push(gadget.getDeclaredGadget("editor")),
promise_list.push(gadget.getDeclaredGadget("editor"));
promise_list.push(getWebPageInfo(gadget, modification_dict.jio_key, modification_dict.publication_section));
}
return RSVP.all(promise_list);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment