Commit 873b0bba 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>
<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, parseInt*/
(function (window, rJS, RSVP, document, SimpleQuery, ComplexQuery, Query, parseInt) {
"use strict";
/*jshint esnext: true */
const STATUS_OK = "green";
const STATUS_NOT_OK = "red";
const NONE_STATUS = "none";
const RADIX = 10;
const STATUS_SPAN = "status";
const TOTAL_SPAN = "total";
const OUTDATED_SPAN = "outdated";
//XXX hardcoded portal_type-title dict (build a template?)
const PORTAL_TITLE_DICT = {"Task": "Tasks",
"Test Result" : "Test Results",
"Bug" : "Bugs",
"Project Milestone" : "Milestones",
"Task Report": "Task Reports"};
function getProjectId(id) {
var segments = id.split("/");
if (segments.length === 2) {
return id;
}
return segments.slice(0, -1).join("/");
}
function getProjectSpanId(project_id, portal_type, span_title) {
return [project_id, portal_type, span_title].join("-").replace("/", "-").replace(" ", "-");
}
function getComplexQuery(query_dict, operator, extra_query) {
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]
}));
}
}
if (extra_query) {
query_list.push(extra_query);
}
return new ComplexQuery({
operator: operator,
query_list: query_list,
type: "complex"
});
}
function getProjectDocumentList(gadget, limit_date) {
var document_query,
portal_type_list = ["Task", "Bug", "Task Report"],
valid_state_list = ["planned", "auto_planned", "ordered", "confirmed", "ready", "stopped", "started"];
document_query = Query.objectToSearchText(new SimpleQuery({
key: "source_project__validation_state",
operator: "=",
type: "simple",
value: "validated"
}));
document_query += ' AND simulation_state: ("' + valid_state_list.join('", "') + '")';
document_query += ' AND portal_type: ("' + portal_type_list.join('", "') + '")';
if (limit_date) {
document_query += ' AND modification_date: < "' + limit_date + '"';
}
return gadget.jio_allDocs({
query: document_query,
limit: 10000,
select_list: ['source_project__relative_url', 'portal_type', 'count(*)'],
group_by: ['portal_type', 'source_project__relative_url'],
sort_on: [["modification_date", "descending"]]
})
.push(function (result) {
return result.data.rows;
});
}
function getProjectElementList(gadget) {
var project_query = Query.objectToSearchText(
getComplexQuery({"portal_type" : "Project",
"validation_state" : "validated"},
"AND"));
return gadget.jio_allDocs({
query: project_query,
limit: 10000,
select_list: ['title'],
sort_on: [["modification_date", "ascending"]]
})
.push(function (result) {
return result.data.rows;
});
}
function renderProjectLine(project_id, portal_type, total_count, outdated_count) {
var total_span = document.getElementById(getProjectSpanId(project_id, portal_type, TOTAL_SPAN)),
outdated_span = document.getElementById(getProjectSpanId(project_id, portal_type, OUTDATED_SPAN)),
status_span = document.getElementById(getProjectSpanId(project_id, portal_type, STATUS_SPAN));
total_span.textContent = parseInt(total_span.textContent, RADIX) + total_count;
outdated_span.textContent = parseInt(outdated_span.textContent, RADIX) + outdated_count;
if (outdated_count > 0) {
status_span.classList.remove(STATUS_OK);
status_span.classList.add(STATUS_NOT_OK);
} else if (!status_span.classList.value.includes(STATUS_NOT_OK)) {
status_span.classList.add(STATUS_OK);
}
}
function renderProjectList(project_list) {
var i,
item,
project_html,
left_div_html,
project_html_element_list,
left_line_html,
ul_list = document.getElementById("js-project-list"),
project_id,
project_dict,
type,
outdated;
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");
//TODO
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(project_id, portal_type, title, total_count, out_count, status_color) {
var line_div = document.createElement('div'),
status = document.createElement('span'),
name = document.createElement('span'),
outdated = document.createElement('span'),
total = document.createElement('span'),
open_bracket = document.createElement('span'),
close_bracket = document.createElement('span');
line_div.classList.add("project-line");
status.classList.add(STATUS_SPAN);
status.classList.add(status_color);
status.classList.add("margined");
status.setAttribute("id", getProjectSpanId(project_id, portal_type, STATUS_SPAN));
name.classList.add("name");
name.classList.add("margined");
name.innerHTML = title;
total.classList.add("margined");
total.innerHTML = total_count;
total.setAttribute("id", getProjectSpanId(project_id, portal_type, TOTAL_SPAN));
outdated.innerHTML = out_count;
outdated.setAttribute("id", getProjectSpanId(project_id, portal_type, OUTDATED_SPAN));
line_div.appendChild(status);
line_div.appendChild(name);
line_div.appendChild(total);
open_bracket.innerHTML = "(";
close_bracket.innerHTML = ")";
line_div.appendChild(open_bracket);
line_div.appendChild(outdated);
line_div.appendChild(close_bracket);
return line_div;
}
for (i = 0; i < project_list.length; i += 1) {
project_html_element_list = createProjectHtmlElement(project_list[i].id, project_list[i].value.title);
project_html = project_html_element_list[0];
left_div_html = project_html_element_list[1];
for (type in PORTAL_TITLE_DICT) {
if (PORTAL_TITLE_DICT.hasOwnProperty(type)) {
left_line_html = createProjectLineHtmlElement(project_list[i].id, type,
((PORTAL_TITLE_DICT.hasOwnProperty(type)) ? PORTAL_TITLE_DICT[type] : type),
0, 0, NONE_STATUS);
left_div_html.appendChild(left_line_html);
}
}
ul_list.appendChild(project_html);
}
}
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 (project_list) {
return renderProjectList(project_list);
})
.push(function (project_list) {
//run the rest of queries and render async
          gadget.renderMilestoneInfo();
          gadget.renderProjectDocumentInfo();
          gadget.renderOutdatedDocumentInfo();
          gadget.renderTestResultInfo();
        });
})
.declareJob("renderMilestoneInfo", function () {
      var gadget = this,
        i, outdated,
        milestone_list,
        milestone_query = Query.objectToSearchText(
        getComplexQuery({"portal_type" : "Project Milestone",
                         "validation_state" : "validated",
                         "parent__portal_type" : "Project"},
                        "AND")),
        milestone_limit_date = new Date();
      milestone_limit_date.setFullYear(milestone_limit_date.getFullYear() - 1);
      return new RSVP.Queue()
        .push(function () {
          return gadget.jio_allDocs({
            query: milestone_query,
            limit: 10000,
            select_list: ["title", 'portal_type', "parent__title", "parent__relative_url"],
            sort_on: [["modification_date", "descending"]]
          })
          .push(function (result) {
            milestone_list = result.data.rows;
            for (i = 0; i < milestone_list.length; i += 1) {
outdated = (new Date(milestone_list[i].value.modification_date) < milestone_limit_date) ? 1 : 0;
renderProjectLine(getProjectId(milestone_list[i].id),
milestone_list[i].value.portal_type,
1, outdated);
            }
          });
        });
    })
    .declareJob("renderProjectDocumentInfo", function () {
      var gadget = this,
        i,
        document_list;
return getProjectDocumentList(gadget)
      .push(function (document_list) {
        for (i = 0; i < document_list.length; i += 1) {
renderProjectLine(getProjectId(document_list[i].value.source_project__relative_url),
document_list[i].value.portal_type,
1, 0);
        }
      });
    })
    .declareJob("renderOutdatedDocumentInfo", function () {
      var gadget = this,
        i,
        document_list,
        limit_date = new Date();
      limit_date.setFullYear(limit_date.getFullYear() - 1);
limit_date = limit_date.toISOString();
//XXX For testing
limit_date = new Date().toISOString();
limit_date = limit_date.substring(0, limit_date.length - 5).replace("T", " ");
return getProjectDocumentList(gadget, limit_date)
      .push(function (document_list) {
        for (i = 0; i < document_list.length; i += 1) {
renderProjectLine(getProjectId(document_list[i].value.source_project__relative_url),
document_list[i].value.portal_type,
0, 1);
        }
      });
    })
    .declareJob("renderTestResultInfo", function () {
      var gadget = this,
        i,
        test_list,
        test_result_query = Query.objectToSearchText(
          getComplexQuery({"portal_type" : "Test Result",
                           "source_project__validation_state" : "validated"},
                          "AND")),
        test_state_list = ["failed", "stopped", "public_stopped"];
      test_result_query += ' AND simulation_state: ("' + test_state_list.join('", "') + '")';
      return gadget.jio_allDocs({
        query: test_result_query,
        limit: 10000,
        select_list: ['source_project__relative_url', 'portal_type'],
        group_by: ['source_project__relative_url'],
        sort_on: [["modification_date", "descending"]]
      })
      .push(function (result) {
        test_list = result.data.rows;
        for (i = 0; i < test_list.length; i += 1) {
//TODO total and outdated values should come with the test-result-row
//(all_tests and failed) but those are _local_properties
renderProjectLine(test_list[i].value.source_project__relative_url,
test_list[i].value.portal_type,
//
1, //parseInt(test_list[i].value.all_test, RADIX)
0); //parseInt(test_list[i].value.failed, RADIX)
        }
      });
    })
.declareMethod('getContent', function () {
return {};
})
.declareMethod('checkValidity', function () {
return true;
});
}(window, rJS, RSVP, document, SimpleQuery, ComplexQuery, Query, parseInt));
\ 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