Commit 098d0195 authored by Romain Courteaud's avatar Romain Courteaud

erp5_web_js_style: minimal web style customizable with Javascript

This is a first prototype showing how a site rendering can be modified
on the style with a renderJS gadget (to remove, add, modify current page
elements).

The navigation is automatically handle by the javascript (with the
pushState API), which really speed up the navigation on powerless
machine (like Pinebook Pro).
parent 57aa77fa
Pipeline #10331 failed with stage
in 0 seconds
<registered_skin_selection>
<skin_folder_selection>
<skin_folder>erp5_web_js_style_ui</skin_folder>
<skin_selection>Jsstyle</skin_selection>
</skin_folder_selection>
<skin_folder_selection>
<skin_folder>erp5_xhtml_style</skin_folder>
<skin_selection>Jsstyle</skin_selection>
</skin_folder_selection>
</registered_skin_selection>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_web_js_style</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Base_edit</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string>multipart/form-data</string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>my_configuration_style_gadget_url</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>WebSection_viewJsstylePreference</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>WebSection_viewHateoasPreference</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_view</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Web Section Preference</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_configuration_style_gadget_url</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>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_reference</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Style Gadget URL</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>business_template_skin_layer_priority</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>float</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>business_template_skin_layer_priority</string> </key>
<value> <float>99.0</float> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_web_js_style_ui</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
import cgi
import re
web_site = context
def _(string_to_escape):
return cgi.escape("%s" % string_to_escape, quote=False)
def __(string_to_escape):
return cgi.escape("%s" % string_to_escape, quote=True)
def generateSectionListHTML(result_list, section_list):
if (section_list):
result_list.append('<ul>')
for section in section_list:
result_list.append('<li><a href="%s">%s</a>' % (_(section['url']), __(section['translated_title'])))
generateSectionListHTML(result_list, section['subsection'])
result_list.append('</li>')
result_list.append('</ul>')
# Language
result_list = ['<nav id="language"><ul>']
available_language_set = web_site.getLayoutProperty("available_language_set", default=['en'])
default_language = web_site.getLayoutProperty("default_available_language", default='en')
website_url_set = {}
root_website_url = web_site.getOriginalDocument().absolute_url()
website_url_pattern = r'^%s(?:%s)*(/|$)' % (
re.escape(root_website_url),
'|'.join('/' + re.escape(x) for x in available_language_set))
for language in available_language_set:
if language == default_language:
website_url_set[language] = re.sub(website_url_pattern, r'%s/\1' % root_website_url, web_site.absolute_url())
else:
website_url_set[language] = re.sub(website_url_pattern, r'%s/%s/\1' % (root_website_url, language), web_site.absolute_url())
for language, url in website_url_set.items():
result_list += '<li><a href="%s" hreflang="%s"><abbr lang="%s">%s</abbr></a></li>' % (__(url), _(language), _(language), __(language))
result_list.append('</ul></nav>')
# Sitemap
result_list.append('<nav id="sitemap">')
result_list.append('<a href="%s">%s</a>' % (_(web_site.absolute_url()), __(web_site.getTranslatedTitle())))
generateSectionListHTML(result_list, web_site.WebSection_getSiteMapTree(depth=99, include_subsection=1))
result_list.append('</nav>')
return ''.join(result_list)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>WebSite_generateNavigationHTML</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
body {
display: grid;
grid-template-raws: repeat(3, 1fr);
}
body > nav#sitemap {
grid-row: 1;
background-color: #DDD;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
min-height: 2.5em;
align-items: center;
}
body > nav#sitemap > a {
padding-left: 1em;
padding-right: 1em;
}
body > nav#sitemap ul {
margin: 0;
}
body > nav#sitemap ul,
body > nav#sitemap li,
body > nav#sitemap a {
padding: 0;
display: flex;
flex: 1;
}
body > nav#sitemap ul:not(:first-child),
body > nav#sitemap li:not(:first-child),
body > nav#sitemap a:not(:first-child) {
padding: 0 1em;
}
body main {
grid-row: 2;
}
body main fieldset {
border: none;
}
body main fieldset > div {
display: flex;
flex-flow: row nowrap;
}
body main fieldset > div > label {
margin-right: 1em;
font-weight: bold;
min-width: 10%;
}
body > nav#language {
grid-row: 3;
background-color: #DDD;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
min-height: 2.5em;
align-items: center;
}
body > nav#language > a {
padding-left: 1em;
padding-right: 1em;
}
body > nav#language ul {
margin: 0;
}
body > nav#language ul,
body > nav#language li,
body > nav#language a {
padding: 0;
display: flex;
flex: 1;
}
body > nav#language ul:not(:first-child),
body > nav#language li:not(:first-child),
body > nav#language a:not(:first-child) {
padding: 0 1em;
}
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>jsstyle.css</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/css</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>
/*globals window, document, RSVP, rJS, XMLHttpRequest, DOMParser, URL,
loopEventListener, history */
/*jslint indent: 2, maxlen: 80*/
(function (window, document, RSVP, rJS, XMLHttpRequest, DOMParser, URL,
loopEventListener, history) {
"use strict";
// XXX Copy/paste from renderjs
function ajax(url) {
var xhr;
function resolver(resolve, reject) {
function handler() {
try {
if (xhr.readyState === 0) {
// UNSENT
reject(xhr);
} else if (xhr.readyState === 4) {
// DONE
if ((xhr.status < 200) || (xhr.status >= 300) ||
(!/^text\/html[;]?/.test(
xhr.getResponseHeader("Content-Type") || ""
))) {
reject(xhr);
} else {
resolve(xhr);
}
}
} catch (e) {
reject(e);
}
}
xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onreadystatechange = handler;
xhr.setRequestHeader('Accept', 'text/html');
xhr.withCredentials = true;
xhr.send();
}
function canceller() {
if ((xhr !== undefined) && (xhr.readyState !== xhr.DONE)) {
xhr.abort();
}
}
return new RSVP.Promise(resolver, canceller);
}
function removeHash(url) {
var index = url.indexOf('#');
if (index > 0) {
url = url.substring(0, index);
}
return url;
}
function scrollToHash(hash) {
var scroll_element = null;
if (hash) {
hash = hash.split('#', 2)[1];
if (hash === undefined) {
hash = "";
}
if (hash) {
scroll_element = document.querySelector(hash);
}
}
if (scroll_element === null) {
window.scrollTo(0, 0);
} else {
scroll_element.scrollIntoView(true);
}
}
function renderPage(gadget, page_url, hash) {
return new RSVP.Queue(RSVP.hash({
xhr: ajax(page_url),
style_gadget: gadget.getDeclaredGadget('renderer')
}))
.push(function (result_dict) {
var dom_parser = (new DOMParser()).parseFromString(
result_dict.xhr.responseText,
'text/html'
);
document.title = dom_parser.title;
return result_dict.style_gadget.render(
dom_parser.body.querySelector('main').innerHTML
);
})
.push(function () {
return scrollToHash(hash);
});
}
function listenURLChange() {
var gadget = this;
// prevent automatic page location restoration
if (history.scrollRestoration) {
history.scrollRestoration = 'manual';
}
function handlePopState() {
return renderPage(gadget, window.location.href, window.location.hash);
}
function handleClick(evt) {
var target_element = evt.target.closest('a'),
base_uri = document.baseURI,
link_url;
if (!target_element) {
// Only handle link
return;
}
if (target_element.target === "_blank") {
// Open in a new tab
return;
}
if (evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) {
return;
}
link_url = new URL(target_element.href, base_uri);
if (link_url.href.indexOf(base_uri) !== 0) {
// Only handle sub path of the base url
return;
}
if (link_url.hash) {
// If new link has an hash, check if the path/query parts are identical
// if so, do not refresh the content and
// let browser scroll to the correct element
if (removeHash(link_url.href) === removeHash(window.location.href)) {
return;
}
}
evt.preventDefault();
return renderPage(gadget, target_element.href, link_url.hash)
.push(function () {
// Important: pushState must be called AFTER the page rendering
// to ensure popstate listener is correctly working
// when the user will click on back/forward browser buttons
history.pushState(null, null, target_element.href);
}, function (error) {
// Implement support for managed error
// (like URL is not an HTML document parsable)
// and redirect in such case
window.location = target_element.href;
});
}
return RSVP.all([
loopEventListener(window, 'popstate', false, handlePopState, false),
loopEventListener(gadget.element, 'click', false, handleClick,
false)
]);
}
rJS(window)
.ready(function () {
// Hide the page as fast as possible
// this.element.hidden = true;
this.main_element = this.element.querySelector('main');
})
.allowPublicAcquisition("reportServiceError", function () {
this.element.hidden = false;
throw rJS.AcquisitionError();
})
.declareJob("listenURLChange", listenURLChange)
.declareService(function () {
var gadget = this,
style_gadget,
body = gadget.element,
style_gadget_url = body.getAttribute("data-nostyle-gadget-url");
if (!style_gadget_url) {
// No style configured, use backend only rendering
return rJS.declareCSS("jsstyle.css", document.head);
}
// Clear the DOM
while (body.firstChild) {
body.firstChild.remove();
}
return gadget.declareGadget(style_gadget_url, {scope: 'renderer'})
.push(function (result) {
style_gadget = result;
return style_gadget.render(gadget.main_element.innerHTML);
})
.push(function () {
// Trigger URL handling
gadget.listenURLChange();
body.appendChild(style_gadget.element);
gadget.element.hidden = false;
scrollToHash(window.location.hash);
}, function (error) {
gadget.element.hidden = false;
throw error;
});
});
}(window, document, RSVP, rJS, XMLHttpRequest, DOMParser, URL,
loopEventListener, history));
\ 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>_Cacheable__manager_id</string> </key>
<value> <string>http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>jsstyle.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>
.flattenNav() {
background-color: #DDD;
// Put the title and the first list on the same line
display: flex;
flex-wrap: wrap;
justify-content: space-between;
// Increase header size
min-height: 2.5em;
align-items: center;
> a {
padding-left: 1em;
padding-right: 1em;
}
ul {
margin: 0;
}
ul, li, a {
padding: 0;
display: flex;
flex: 1;
&:not(:first-child) {
padding: 0 1em;
}
}
}
body {
display: grid;
grid-template-raws: repeat(3, 1fr);
> nav#sitemap {
grid-row: 1;
.flattenNav();
}
main {
grid-row: 2;
fieldset {
border: none;
> div {
display: flex;
flex-flow: row nowrap;
> label {
margin-right: 1em;
font-weight: bold;
min-width: 10%;
}
}
}
}
> nav#language {
grid-row: 3;
.flattenNav();
}
}
\ 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>_Cacheable__manager_id</string> </key>
<value> <string>http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>jsstyle.less</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/less</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>
body, a {
background-color: black;
color: white;
}
body {max-width: 40em;}
label {display: block;}
input:not([type=submit]):not([type=file]) {width: 100%;}
textarea {height: 15em;width: 100%;}
\ 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>_Cacheable__manager_id</string> </key>
<value> <string>http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>jsstyle_demo.css</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/css</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>
<html>
<head>
<link rel="stylesheet" href="jsstyle_demo.css">
<script src="portal_skins/erp5_xhtml_style/rsvp.js"></script>
<script src="portal_skins/erp5_xhtml_style/renderjs.js"></script>
<script src="jsstyle_demo.js"></script>
</head>
<body>
<header><h1>JS Style Demo</h1></header>
<main></main>
</body>
</html>
\ 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>_Cacheable__manager_id</string> </key>
<value> <string>http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>jsstyle_demo.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>
/*globals window, document, RSVP, rJS*/
/*jslint indent: 2, maxlen: 80*/
(function () {
"use strict";
rJS(window)
.declareMethod("render", function (main_innerhtml) {
var gadget = this;
var div = document.createElement('div');
div.innerHTML = main_innerhtml;
gadget.element.querySelector('main').innerHTML = div.querySelector('div.input').firstChild.innerHTML;
});
}());
\ 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>_Cacheable__manager_id</string> </key>
<value> <string>http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>jsstyle_demo.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>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view_main</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<tal:block xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n">
<tal:block metal:define-macro="master">
<tal:block tal:define="
dummy python: request.set('is_web_mode', True);
dummy python: request.set('ignore_layout', False);
dummy python: request.set('editable_mode', False);
web_site python: here.getWebSiteValue();
global_definitions_macros here/global_definitions/macros;">
<tal:block metal:use-macro="global_definitions_macros/header_definitions" />
<html>
<head>
<base tal:attributes="href python: '%s/' % web_site.absolute_url()" />
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1" />
<title tal:content="python: here.getTranslatedTitle() or web_site.getTranslatedTitle()"></title>
<noscript>
<link rel="stylesheet" href="jsstyle.css">
</noscript>
<!-- Prevent conflicts with Web Page reference provided by erp5_web_renderjs_ui -->
<script src="portal_skins/erp5_xhtml_style/rsvp.js"></script>
<script src="portal_skins/erp5_xhtml_style/renderjs.js"></script>
<script src="gadget_global.js"></script>
<script src="jsstyle.js"></script>
</head>
<body tal:attributes="data-nostyle-gadget-url python: web_site.getLayoutProperty('configuration_style_gadget_url', default='')">
<tal:block tal:content="structure python: web_site.WebSite_generateNavigationHTML()"></tal:block>
<main><tal:block metal:define-slot="main"/></main>
</body>
</html>
</tal:block>
</tal:block>
</tal:block>
\ No newline at end of file
erp5_web
\ No newline at end of file
GPL
\ No newline at end of file
erp5_web_js_style_ui | Jsstyle
erp5_xhtml_style | Jsstyle
\ No newline at end of file
erp5_web_js_style
erp5_web_js_style_ui
\ No newline at end of file
erp5_web_js_style
\ No newline at end of file
0.1
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Zuite" module="Products.Zelenium.zuite"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>js_style_zuite</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testJsStyleDemoStyle</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test JS Style Demo Style</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test JS Style Demo Style</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/test_js_style_demo_style/</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//header/h1[text()='JS Style Demo']</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//header/h1[text()='JS Style Demo']</td>
<td></td>
</tr>
<tr>
<td>assertElementNotPresent</td>
<td>//nav[@id='sitemap']/a[text()='No Style']</td>
<td></td>
</tr>
<tr>
<td>assertElementNotPresent</td>
<td>//nav[@id='language']//a[@hreflang='en']</td>
<td></td>
</tr>
<tr>
<td>assertElementNotPresent</td>
<td>//div[@class='input']/span[@class='headline' and text()='Demo Style']</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//main[text()='Demo Style']</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testJsStyleNoStyle</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test JS Style No Style</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test JS Style No Style</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/test_js_style_no_style/</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//nav[@id='sitemap']/a[text()='No Style']</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//nav[@id='language']//a[@hreflang='en']</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//div[@class='input']/span[@class='headline' and text()='No Style']</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Site" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_folders_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Copy_or_Move_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>__before_publishing_traverse__</string> </key>
<value>
<object>
<klass>
<global name="MultiHook" module="ZPublisher.BeforeTraverse"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_defined_in_class</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>_hookname</string> </key>
<value> <string>__before_publishing_traverse__</string> </value>
</item>
<item>
<key> <string>_list</string> </key>
<value>
<list>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</list>
</value>
</item>
<item>
<key> <string>_prior</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>__before_traverse__</string> </key>
<value>
<dictionary>
<item>
<key>
<tuple>
<int>99</int>
<string>ERP5 Web Site/test_js_style_demo_style</string>
</tuple>
</key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>configuration_style_gadget_url</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>available_language</string> </key>
<value>
<tuple>
<string>en</string>
</tuple>
</value>
</item>
<item>
<key> <string>configuration_style_gadget_url</string> </key>
<value> <string>jsstyle_demo.html</string> </value>
</item>
<item>
<key> <string>container_layout</string> </key>
<value> <string>erp5_web_layout</string> </value>
</item>
<item>
<key> <string>content_layout</string> </key>
<value> <string>erp5_web_content_layout</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test_js_style_demo_style</string> </value>
</item>
<item>
<key> <string>layout_configuration_form_id</string> </key>
<value> <string>WebSection_viewJsstylePreference</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Site</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>skin_selection_name</string> </key>
<value> <string>Jsstyle</string> </value>
</item>
<item>
<key> <string>static_language_selection</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Demo Style</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="WebSiteTraversalHook" module="Products.ERP5.Document.WebSite"/>
</pickle>
<pickle>
<dictionary/>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Site" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_folders_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Copy_or_Move_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>__before_publishing_traverse__</string> </key>
<value>
<object>
<klass>
<global name="MultiHook" module="ZPublisher.BeforeTraverse"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_defined_in_class</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>_hookname</string> </key>
<value> <string>__before_publishing_traverse__</string> </value>
</item>
<item>
<key> <string>_list</string> </key>
<value>
<list>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</list>
</value>
</item>
<item>
<key> <string>_prior</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>__before_traverse__</string> </key>
<value>
<dictionary>
<item>
<key>
<tuple>
<int>99</int>
<string>ERP5 Web Site/test_js_style_no_style</string>
</tuple>
</key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>available_language</string> </key>
<value>
<tuple>
<string>en</string>
</tuple>
</value>
</item>
<item>
<key> <string>container_layout</string> </key>
<value> <string>erp5_web_layout</string> </value>
</item>
<item>
<key> <string>content_layout</string> </key>
<value> <string>erp5_web_content_layout</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test_js_style_no_style</string> </value>
</item>
<item>
<key> <string>layout_configuration_form_id</string> </key>
<value> <string>WebSection_viewJsstylePreference</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Site</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>skin_selection_name</string> </key>
<value> <string>Jsstyle</string> </value>
</item>
<item>
<key> <string>static_language_selection</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>No Style</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="WebSiteTraversalHook" module="Products.ERP5.Document.WebSite"/>
</pickle>
<pickle>
<dictionary/>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
##############################################################################
#
# Copyright (c) 2020 Nexedi SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import unittest
from Products.ERP5Type.tests.ERP5TypeFunctionalTestCase import ERP5TypeFunctionalTestCase
class TestFunctionalJsStyle(ERP5TypeFunctionalTestCase):
foreground = 0
run_only = "js_style_zuite"
def getBusinessTemplateList(self):
return (
'erp5_web_js_style',
'erp5_web_js_style_test',
'erp5_ui_test_core',
)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestFunctionalJsStyle))
return suite
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testFunctionalJsStyle</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Test for JS Style</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testFunctionalJsStyle</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
erp5_ui_test_core
erp5_ui_test
erp5_web_js_style
\ No newline at end of file
web_site_module/test_js_style_*_style
\ No newline at end of file
web_site_module/test_js_style_*_style
\ No newline at end of file
portal_tests/js_style_zuite
portal_tests/js_style_zuite/**
web_site_module/test_js_style_*_style
\ No newline at end of file
test.erp5.testFunctionalJsStyle
\ No newline at end of file
erp5_full_text_mroonga_catalog
\ No newline at end of file
erp5_web_js_style_test
\ No newline at end of file
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