Commit dec831df authored by Romain Courteaud's avatar Romain Courteaud

[erp5_web_renderjs_ui] Relation field allow to search by default if no value

Replace the airplane icon by a search button if not value is set on the field.
This make it more usable on mobile, where it's easier to browse than typing text.
parent db52797a
......@@ -1144,7 +1144,8 @@ div[data-gadget-scope='header'] .ui-header ul {
.relation-input {
display: flex;
}
.relation-input a {
.relation-input a,
.relation-input button {
width: 24pt;
overflow: hidden;
text-indent: -9999px;
......@@ -1153,7 +1154,8 @@ div[data-gadget-scope='header'] .ui-header ul {
padding-top: 3pt;
padding-bottom: 3pt;
}
.relation-input a::before {
.relation-input a::before,
.relation-input button::before {
float: left;
text-indent: 0;
margin-left: 6pt;
......
......@@ -228,7 +228,7 @@
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>vincent</string> </value>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
......@@ -242,7 +242,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>964.8294.44761.42632</string> </value>
<value> <string>964.8273.15694.60620</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>1513250374.08</float>
<float>1513255864.13</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -23,7 +23,6 @@
<input type='search' title="{{title}}" name="{{name}}" id="{{name}}" autocomplete="off" data-enhanced="true" value="{{value}}" >
<ul class="ui-listview ui-corner-all search_ul"></ul>
</div>
<a href={{href}} tabindex="-1" class="{{class_name}}" >Jump to this document</a>
</div>
</script>
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>963.11788.48702.26146</string> </value>
<value> <string>964.6712.62509.9898</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1512750412.99</float>
<float>1513168443.82</float>
<string>UTC</string>
</tuple>
</state>
......
/*jslint indent: 2, maxerr: 3, nomen: true, maxlen: 80 */
/*global window, rJS, RSVP, URI, Handlebars,
SimpleQuery, ComplexQuery, Query, QueryFactory*/
(function (window, rJS, RSVP, URI,
SimpleQuery, ComplexQuery, Query, QueryFactory, document*/
(function (window, rJS, RSVP, URI, document,
SimpleQuery, ComplexQuery, Query, QueryFactory, Handlebars) {
"use strict";
......@@ -44,16 +44,67 @@
gadget.element.textContent = gadget.state.value_text;
}
function displayEditableLink(gadget, class_name) {
return gadget.translateHtml(relation_input_template({
href: "#",
function buildEditableInputHTML(gadget) {
gadget.element.innerHTML = relation_input_template({
value: gadget.state.value_text,
title: gadget.state.title,
name: gadget.state.key,
class_name: class_name
}))
.push(function (html) {
gadget.element.innerHTML = html;
name: gadget.state.key
});
}
function createEditableLink(gadget, class_name) {
// Add an airplane link
var a = document.createElement('a'),
div = gadget.element.querySelector('input').parentElement;
a.setAttribute('tabindex', -1);
a.setAttribute('class', class_name);
a.textContent = 'Jump to this document';
if (div.nextElementSibling !== null) {
div.parentElement.removeChild(div.nextElementSibling);
}
div.parentElement.appendChild(a);
}
function createEditableButton(gadget, class_name) {
// Add a search button
var button = document.createElement('button'),
div = gadget.element.querySelector('input').parentElement;
button.setAttribute('tabindex', -1);
button.setAttribute('class', class_name);
button.setAttribute('type', 'button');
if (div.nextElementSibling !== null) {
div.parentElement.removeChild(div.nextElementSibling);
}
div.parentElement.appendChild(button);
}
function redirectToTheSearchListbox(gadget) {
return gadget.getFormContent({
format: "json"
})
.push(function (content) {
var input = gadget.element.querySelector('input'),
extended_search = "";
if (input.value) {
extended_search = Query.objectToSearchText(
new SimpleQuery({
key: gadget.state.catalog_index,
value: input.value
})
);
}
return gadget.redirect({
command: 'index',
options: {
page: "relation_search",
url: gadget.state.url,
extended_search: extended_search,
view: gadget.state.view,
back_field: gadget.state.key,
relation_index: gadget.state.relation_index
},
form_content: content
});
});
}
......@@ -85,8 +136,9 @@
view: options.view,
url: options.url,
allow_creation: options.allow_creation,
portal_types: options.portal_types,
translated_portal_types: options.translated_portal_types,
portal_types: JSON.stringify(options.portal_types),
translated_portal_types:
JSON.stringify(options.translated_portal_types),
has_focus: false,
relation_index: options.relation_index,
value_relative_url: options.value_relative_url,
......@@ -121,7 +173,6 @@
.onStateChange(function (modification_dict) {
var gadget = this,
queue = new RSVP.Queue(),
value_text = gadget.state.value_text,
// target_url,
SEARCHING_CLASS_STR = "ui-btn ui-corner-all ui-btn-icon-notext" +
......@@ -130,6 +181,8 @@
"ui-icon-plane ui-shadow-inset ui-btn-inline",
JUMP_OFF_CLASS_STR = "ui-btn ui-corner-all ui-btn-icon-notext " +
"ui-icon-plane ui-shadow-inset ui-btn-inline ui-disabled",
SEARCH_CLASS_STR = "ui-btn ui-corner-all ui-btn-icon-notext " +
"ui-icon-search ui-shadow-inset ui-btn-inline",
JUMP_ADD_CLASS_STR = "ui-btn ui-corner-all ui-btn-icon-notext " +
"ui-icon-plus ui-shadow-inset ui-btn-inline ui-disabled",
JUMP_UNKNOWN_CLASS_STR = "ui-btn ui-corner-all ui-btn-icon-notext " +
......@@ -145,12 +198,19 @@
if (modification_dict.hasOwnProperty('editable')) {
// First display of the input
queue.push(function () {
return displayEditableLink(gadget, JUMP_UNKNOWN_CLASS_STR);
});
buildEditableInputHTML(gadget);
}
return queue
// Display the airplane link or the search button
if ((gadget.state.value_relative_url) || (gadget.state.value_text)) {
createEditableLink(gadget, JUMP_UNKNOWN_CLASS_STR);
} else {
gadget.element.querySelector(".search_ul").innerHTML = "";
return createEditableButton(gadget, SEARCH_CLASS_STR);
}
// Check if the airplane link has to be updated
return RSVP.Queue()
.push(function () {
var plane = gadget.element.querySelector("a"),
ul = gadget.element.querySelector(".search_ul"),
......@@ -245,12 +305,17 @@
.push(function (result) {
var list = [],
i,
type = [];
type = [],
portal_types,
translated_portal_types;
if (gadget.state.allow_creation) {
for (i = 0; i < gadget.state.portal_types.length; i += 1) {
portal_types = JSON.parse(gadget.state.portal_types);
translated_portal_types =
JSON.parse(gadget.state.translated_portal_types);
for (i = 0; i < portal_types.length; i += 1) {
type.push({
name: gadget.state.translated_portal_types[i],
value: gadget.state.portal_types[i]
name: translated_portal_types[i],
value: portal_types[i]
});
}
}
......@@ -304,6 +369,11 @@
data_explore,
new_state = {};
if (evt.target.tagName.toLowerCase() === 'button') {
// Go to the search listbox
return redirectToTheSearchListbox(gadget);
}
if (evt.target.tagName.toLowerCase() === 'li') {
li = evt.target;
data_relative_url = li.getAttribute("data-relative-url");
......@@ -327,29 +397,7 @@
// Go to the search listbox
if (data_explore) {
return gadget.getFormContent({
format: "json"
})
.push(function (content) {
var input = gadget.element.querySelector('input');
return gadget.redirect({
command: 'index',
options: {
page: "relation_search",
url: gadget.state.url,
extended_search: Query.objectToSearchText(
new SimpleQuery({
key: gadget.state.catalog_index,
value: input.value
})
),
view: gadget.state.view,
back_field: gadget.state.key,
relation_index: gadget.state.relation_index
},
form_content: content
});
});
return redirectToTheSearchListbox(gadget);
}
}
}, false, false)
......@@ -398,5 +446,5 @@
});
}, true, false);
}(window, rJS, RSVP, URI,
}(window, rJS, RSVP, URI, document,
SimpleQuery, ComplexQuery, Query, QueryFactory, Handlebars));
......@@ -236,7 +236,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>958.2831.13618.12219</string> </value>
<value> <string>964.8568.54116.8601</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -254,7 +254,7 @@
</tuple>
<state>
<tuple>
<float>1489675175.72</float>
<float>1513266821.11</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -1344,7 +1344,7 @@ div[data-gadget-scope='header'] .ui-header {
**********************************************/
.relation-input {
display: flex;
a {
a, button {
.hide_text(@width: @quadruple-margin-size);
&::before {
......
<?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>testMultiRelationFieldEmptySearchWithListbox</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 RenderJS UI</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test RenderJS UI</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Clean Up -->
<tr>
<td>open</td>
<td>${base_url}/foo_module/ListBoxZuite_reset</td>
<td></td>
</tr>
<tr>
<td>assertTextPresent</td>
<td>Reset Successfully.</td>
<td></td>
</tr>
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/foo_module</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n='Add']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>link=Add</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n='Views']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n='Views']</td>
<td></td>
</tr>
<tr>
<td>storeLocation</td>
<td>url</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/wait_for_activities" />
<tr>
<td>open</td>
<td>${url}</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n='Relation Fields']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n='Relation Fields']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//input[@name='field_my_foo_category_title']</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//input[@name='field_my_foo_category_title']</td>
<td>a1</td>
</tr>
<tr>
<td>fireEvent</td>
<td>//input[@name='field_my_foo_category_title']</td>
<td>input</td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//li[@data-relative-url='portal_categories/foo_category/a/a1']</td>
<td></td>
</tr>
<tr>
<td>fireEvent</td>
<td>//input[@name='field_my_foo_category_title']</td>
<td>blur</td>
</tr>
<tr>
<td>click</td>
<td>//li[@data-relative-url='portal_categories/foo_category/a/a1']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[1]//input</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[1]//input</td>
<td>A New Foo</td>
</tr>
<tr>
<td>fireEvent</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[1]//input</td>
<td>input</td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//li[@name="Foo"]
<td></td>
</tr>
<tr>
<td>fireEvent</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[1]//input</td>
<td>blur</td>
</tr>
<tr>
<td>click</td>
<td>//li[@name="Foo"]
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[2]//input</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[2]//input</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//input[@name="field_my_title"]</td>
<td>TEST</td>
</tr>
<tr>
<td>click</td>
<td>//input[@name="field_my_frozen"]</td>
<td></td>
</tr>
<tr>
<td>assertChecked</td>
<td>//input[@name="field_my_frozen"]</td>
<td></td>
</tr>
<tr>
<td colspan="3"><b>Click on the search button of the empty multi relation field</b></td>
</tr>
<tr>
<td>click</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[2]//button</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/triggle_filter_and" />
<tal:block tal:define="filter_section_configuration python: {'index': 0}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/remove_filter_section" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_filter" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="parsed_query python: '';
search_query python: ''">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/check_search_in_form_list" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_listbox_loaded" />
<tr>
<td>click</td>
<td>//tbody/tr[1]//a</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//button[@class="responsive ui-btn ui-icon-warning ui-btn-icon-left ui-first-child ui-last-child "]</td>
<td></td>
</tr>
<tr>
<td>verifyValue</td>
<td>//input[@name="field_my_title"]</td>
<td>TEST</td>
</tr>
<tr>
<td>assertChecked</td>
<td>//input[@name="field_my_frozen"]</td>
<td></td>
</tr>
<tr>
<td>verifyValue</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[1]//input</td>
<td>A New Foo</td>
</tr>
<tr>
<td>verifyValue</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[2]//input</td>
<td>2</td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[3]</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/save" />
<tr>
<td>verifyValue</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[1]//input</td>
<td>A New Foo</td>
</tr>
<tr>
<td>verifyValue</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[2]//input</td>
<td>TEST</td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@data-gadget-scope='field_my_bar_category_title_list']//fieldset[3]</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>testRelationFieldEmptySearchWithListbox</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 RenderJS UI</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test RenderJS UI</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Clean Up -->
<tr>
<td>open</td>
<td>${base_url}/foo_module/ListBoxZuite_reset</td>
<td></td>
</tr>
<tr>
<td>assertTextPresent</td>
<td>Reset Successfully.</td>
<td></td>
</tr>
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/foo_module</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n='Add']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>link=Add</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Views"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Views"]</td>
<td></td>
</tr>
<tr>
<td>storeLocation</td>
<td>url</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/wait_for_activities" />
<tr>
<td>open</td>
<td>${url}</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Relation Fields"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Relation Fields"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//input[@name="field_my_successor_title"]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//input[@name='field_my_foo_category_title']</td>
<td>a1</td>
</tr>
<tr>
<td>fireEvent</td>
<td>//input[@name='field_my_foo_category_title']</td>
<td>input</td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//li[@data-relative-url='portal_categories/foo_category/a/a1']</td>
<td></td>
</tr>
<tr>
<td>fireEvent</td>
<td>//input[@name='field_my_foo_category_title']</td>
<td>blur</td>
</tr>
<tr>
<td>click</td>
<td>//li[@data-relative-url='portal_categories/foo_category/a/a1']</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//input[@name="field_my_title"]</td>
<td>TEST</td>
</tr>
<tr>
<td colspan="3"><b>Click on the search button of the empty relation field</b></td>
</tr>
<tr>
<td>click</td>
<td>//div[@data-gadget-scope="field_my_successor_title"]//button</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/triggle_filter_and" />
<tal:block tal:define="filter_section_configuration python: {'index': 0}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/remove_filter_section" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_filter" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="parsed_query python: '';
search_query python: ''">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/check_search_in_form_list" />
</tal:block>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Previous"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//tbody/tr[1]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//tbody/tr[1]//a</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//button[@class="responsive ui-btn ui-icon-warning ui-btn-icon-left ui-first-child ui-last-child "]</td>
<td></td>
</tr>
<tr>
<td>verifyValue</td>
<td>//input[@name="field_my_successor_title"]</td>
<td>2</td>
</tr>
<tr>
<td>verifyValue</td>
<td>//input[@name="field_my_title"]</td>
<td>TEST</td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/save" />
<tr>
<td>verifyValue</td>
<td>//input[@name="field_my_successor_title"]</td>
<td>TEST</td>
</tr>
<tr>
<td>verifyValue</td>
<td>//input[@name="field_my_title"]</td>
<td>TEST</td>
</tr>
</tbody></table>
</body>
</html>
\ 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