* [enhance] web form pagination frappe/shishuvan#3 * [minor] add label in web form grid * [minor] select module for exporting web form * [fix] more fixes, #2057version-14
@@ -1,2 +1,5 @@ | |||
- Search for help from within the app. Click on "Help" | |||
- Send a popup to all users on login for a new Note by checking on "Notify users with a popup when they log in" | |||
- Send a popup to all users on login for a new Note by checking on "Notify users with a popup when they log in" | |||
- Updates to Web Form | |||
- Add grids (child tables) | |||
- Add page breaks (for long forms) |
@@ -200,6 +200,13 @@ def export_fixtures(context): | |||
def import_doc(context, path, force=False): | |||
"Import (insert/update) doclist. If the argument is a directory, all files ending with .json are imported" | |||
from frappe.core.page.data_import_tool import data_import_tool | |||
if not os.path.exists(path): | |||
path = os.path.join('..', path) | |||
if not os.path.exists(path): | |||
print 'Invalid path {0}'.format(path) | |||
sys.exit(1) | |||
for site in context.sites: | |||
try: | |||
frappe.init(site=site) | |||
@@ -222,6 +229,12 @@ def import_csv(context, path, only_insert=False, submit_after_import=False, igno | |||
from frappe.utils.csvutils import read_csv_content | |||
site = get_site(context) | |||
if not os.path.exists(path): | |||
path = os.path.join('..', path) | |||
if not os.path.exists(path): | |||
print 'Invalid path {0}'.format(path) | |||
sys.exit(1) | |||
with open(path, 'r') as csvfile: | |||
content = read_csv_content(csvfile.read()) | |||
@@ -440,6 +440,7 @@ select.form-control { | |||
} | |||
.form-headline { | |||
padding: 0px 15px; | |||
margin: 0px; | |||
} | |||
.form-headline .alert { | |||
font-size: 12px; | |||
@@ -153,10 +153,9 @@ frappe.ui.to_do_dialog = function(opts){ | |||
var dialog = new frappe.ui.Dialog({ | |||
title: __('Add to To Do'), | |||
fields: [ | |||
{fieldtype:'Check', fieldname:'myself', label:__("Assign to me"), "default":0}, | |||
{fieldtype: 'Section Break'}, | |||
{fieldtype: 'Link', fieldname:'assign_to', options:'User', | |||
label:__("Assign To"), reqd:true, filters: {'user_type': 'System User'}}, | |||
{fieldtype:'Check', fieldname:'myself', label:__("Assign to me"), "default":0}, | |||
{fieldtype:'Small Text', fieldname:'description', label:__("Comment"), reqd:true}, | |||
{fieldtype: 'Section Break'}, | |||
{fieldtype: 'Column Break'}, | |||
@@ -7,11 +7,13 @@ | |||
<div class="small form-clickable-section grid-footer"> | |||
<div class="row"> | |||
<div class="col-sm-6 grid-buttons"> | |||
<a href="#" class="grid-add-multiple-rows btn btn-xs btn-default hide" | |||
<button type="reset" | |||
class="grid-add-multiple-rows btn btn-xs btn-default hide" | |||
style="margin-right: 10px;"> | |||
{%= __("Add multiple rows") %}</a> | |||
<a href="#" class="btn btn-xs btn-default grid-add-row"> | |||
{%= __("Add new row") %}</a> | |||
<!-- hack to allow firefox include this in tabs --> | |||
<button type="reset" class="btn btn-xs btn-default grid-add-row"> | |||
{%= __("Add new row") %}</button> | |||
</div> | |||
<div class="col-sm-6 text-right"> | |||
<a href="#" class="grid-download btn btn-xs btn-default hide" | |||
@@ -555,6 +555,7 @@ select.form-control { | |||
.form-headline { | |||
padding: 0px 15px; | |||
margin: 0px; | |||
} | |||
.form-headline .alert { | |||
@@ -15,7 +15,7 @@ | |||
<a href="{{ cancel_url or pathname }}" class="btn btn-default btn-sm"> | |||
{{ _("Cancel") }}</a> | |||
<button type="submit" class="btn btn-primary btn-sm btn-form-submit"> | |||
{{ _("Submit") if frappe.form_dict.new else _("Update") }}</button> | |||
{{ _("Submit") if frappe.form_dict.new else _("Save") }}</button> | |||
{% elif is_list %} | |||
<div style="padding-bottom: 15px;"> | |||
<a href="/{{ pathname }}{{ delimeter }}new=1" class="btn btn-primary btn-new btn-sm"> | |||
@@ -93,6 +93,18 @@ | |||
value="{{ value(field, _doc) }}"> | |||
{{ help(field) }} | |||
</div> | |||
{% elif field.fieldtype=="Link" %} | |||
<div class="form-group"> | |||
{% if with_label %}{{ label(field) }}{% endif %} | |||
<select class="form-control" {{ properties(field) }}> | |||
{% for option in ([{"name":""}] + frappe.get_all(field.options)) -%} | |||
<option value="{{ option.name }}" | |||
{{ 'selected="selected"' if value(field, _doc)==option.name else '' }}> | |||
{{ option.name }}</option> | |||
{%- endfor %} | |||
</select> | |||
{{ help(field) }} | |||
</div> | |||
{% elif field.fieldtype=="Select" %} | |||
<div class="form-group"> | |||
{% if with_label %}{{ label(field) }}{% endif %} | |||
@@ -136,8 +148,8 @@ | |||
{% if with_label %} | |||
<label> | |||
<input type="checkbox" id="{{ field.fieldname }}" | |||
name="{{ field.fieldname }}" | |||
{{ doc and doc.get(field.fieldname) and 'checked' or '' }}> | |||
name="{{ field.fieldname }}" data-doctype="{{ field.parent }}" | |||
{{ _doc and _doc.get(field.fieldname) and 'checked' or '' }}> | |||
{{ _(field.label) }} | |||
</label> | |||
{% endif %} | |||
@@ -152,7 +164,7 @@ | |||
{% if d != None %}data-name="{{ d.name }}" data-child-row=1{% endif %}> | |||
{% for df in frappe.get_meta(field.options).fields %} | |||
{% if df.in_list_view %} | |||
<{{ 'th' if d==None else 'td' }} style="width: {{ (field.columns or 2) * 8.3333 }}%;"> | |||
<{{ 'th' if d==None else 'td' }} style="width: {{ (df.columns or 2) * 8.3333 }}%;"> | |||
{% if d!=None %} | |||
{{ render_field(df, d, False) }} | |||
{% else %} | |||
@@ -170,6 +182,7 @@ | |||
{% endmacro %} | |||
{% macro render_table(field) %} | |||
{{ label(field) }} | |||
<div class='web-form-grid' | |||
data-fieldname='{{ field.fieldname }}' data-doctype='{{ field.options }}'> | |||
<table class='table table-bordered'> | |||
@@ -192,6 +205,15 @@ | |||
data-fieldname='{{ field.fieldname }}'>{{ _("Add Row") }}</button></p> | |||
{% endmacro %} | |||
{% if layout|len > 1 %} | |||
<div class="text-center slide-progress text-extra-muted"> | |||
{% for page in layout %} | |||
<i data-idx="{{ loop.index }}" class="icon-fixed-width | |||
{% if loop.index==1 %}icon-circle{% else %}icon-circle-blank{% endif %}"></i> | |||
{% endfor %} | |||
</div> | |||
{% endif %} | |||
<div class="form-message text-muted hide"></div> | |||
<form role="form" | |||
data-web-form="{{ name }}" data-owner="{{ doc.owner }}"> | |||
@@ -210,24 +232,50 @@ | |||
data-doctype="{{ doc_type }}"> | |||
{%- endif %} | |||
{% for section in layout %} | |||
{% if section.label %} | |||
<h5 class='uppercase'>{{ _(section.label) }}</h5> | |||
{% endif %} | |||
<div class="row"> | |||
{% for column in section.columns %} | |||
<div class="col-sm-{{ (12 / (section.columns|len))|int }}"> | |||
{% for field in column %} | |||
{% if field.fieldtype=='Table' %} | |||
{{ render_table(field) }} | |||
{% else %} | |||
{{ render_field(field, doc) }} | |||
{% endif %} | |||
{% endfor %} | |||
</div> | |||
{% endfor %} | |||
{% for page in layout %} | |||
<div class="web-form-page{% if loop.index > 1 %} hidden{% endif %}" | |||
data-idx="{{ loop.index }}"> | |||
<h2>{{ page.label }}</h2> | |||
{% for section in page.sections %} | |||
<div class="section"> | |||
{% if section.label %} | |||
<h5 class='uppercase'>{{ _(section.label) }}</h5> | |||
{% endif %} | |||
<div class="row"> | |||
{% for column in section.columns %} | |||
<div class="col-sm-{{ (12 / (section.columns|len))|int }}"> | |||
{% for field in column %} | |||
{% if field.fieldtype=='Table' %} | |||
{{ render_table(field) }} | |||
{% else %} | |||
{{ render_field(field, doc) }} | |||
{% endif %} | |||
{% endfor %} | |||
</div> | |||
{% endfor %} | |||
</div> | |||
</div> | |||
{% endfor %} | |||
<!-- page footer buttons --> | |||
<div class='text-right'> | |||
<br> | |||
{% if loop.index > 1 %} | |||
<button class='btn btn-sm btn-default btn-change-section' | |||
data-idx="{{ loop.index - 1 }}"> | |||
{{ _("Previous") }}</button> | |||
{% endif %} | |||
{% if loop.index == layout|len %} | |||
<button type="submit" class="btn btn-primary btn-sm btn-form-submit"> | |||
{{ _("Submit") if frappe.form_dict.new else _("Update") }}</button> | |||
{% elif layout|len > 1 %} | |||
<button class="btn btn-primary btn-sm btn-change-section" | |||
data-idx="{{ loop.index + 1 }}"> | |||
{{ _("Next") }}</button> | |||
{% endif %} | |||
</div> | |||
</div> | |||
{% endfor %} | |||
</form> | |||
{% if allow_comments and not frappe.form_dict.new -%} | |||
<div class="comments"> | |||
@@ -283,6 +331,29 @@ frappe.ready(function() { | |||
return false; | |||
}); | |||
// change section | |||
$('.btn-change-section, .slide-progress .icon-fixed-width').on('click', function() { | |||
var idx = $(this).attr('data-idx'); | |||
show_slide(idx); | |||
}); | |||
show_slide = function(idx) { | |||
// hide all sections | |||
$('.web-form-page').addClass('hidden'); | |||
// slide-progress | |||
$('.slide-progress .icon-fixed-width.icon-circle') | |||
.removeClass('icon-circle').addClass('icon-circle-blank'); | |||
$('.slide-progress .icon-fixed-width[data-idx="'+idx+'"]') | |||
.removeClass('icon-circle-blank').addClass('icon-circle'); | |||
// un hide target section | |||
$('.web-form-page[data-idx="'+idx+'"]') | |||
.removeClass('hidden') | |||
.find(':input:first').focus(); | |||
} | |||
// add row | |||
$('.btn-add-row').on('click', function() { | |||
var fieldname = $(this).attr('data-fieldname'); | |||
@@ -343,7 +414,7 @@ frappe.ready(function() { | |||
if(input_type==="file") { | |||
var val = $input.get(0).filedata; | |||
} else if(input_type==="checkbox") { | |||
var val = $input.is(":checked") ? 1 : 0; | |||
var val = $input.prop("checked") ? 1 : 0; | |||
} else if($input.attr("data-fieldtype")==="Date") { | |||
var val = $.datepicker.formatDate("yy-mm-dd", | |||
$input.datepicker('getDate')); | |||
@@ -496,6 +567,24 @@ input, select { | |||
.web-form-grid-row .form-group { | |||
margin: 0px; | |||
} | |||
.slide-progress { | |||
/*border-top: 1px solid #d1d8dd;*/ | |||
margin-top: -7px; | |||
padding: 15px 0px; | |||
} | |||
.slide-progress .icon-fixed-width { | |||
vertical-align: middle; | |||
margin-right: 7px; | |||
cursor: pointer; | |||
} | |||
.slide-progress .icon-circle-blank { | |||
font-size: 12px; | |||
} | |||
.slide-progress .icon-circle { | |||
font-size: 14px; | |||
} | |||
{% if style is defined %}{{ style }}{% endif %} | |||
</style> | |||
{% endblock %} |
@@ -95,7 +95,7 @@ | |||
"columns": 0, | |||
"fieldname": "module", | |||
"fieldtype": "Link", | |||
"hidden": 1, | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
@@ -618,7 +618,7 @@ | |||
"issingle": 0, | |||
"istable": 0, | |||
"max_attachments": 0, | |||
"modified": "2016-09-13 13:08:13.720070", | |||
"modified": "2016-09-20 07:06:10.839930", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Web Form", | |||
@@ -26,27 +26,17 @@ class WebForm(WebsiteGenerator): | |||
def validate(self): | |||
super(WebForm, self).validate() | |||
self.module = frappe.db.get_value('DocType', self.doc_type, 'module') | |||
if not self.module: | |||
self.module = frappe.db.get_value('DocType', self.doc_type, 'module') | |||
if (not (frappe.flags.in_install or frappe.flags.in_patch or frappe.flags.in_test or frappe.flags.in_fixtures) | |||
and self.is_standard and not frappe.conf.developer_mode): | |||
frappe.throw(_("You need to be in developer mode to edit a Standard Web Form")) | |||
def reset_field_parent_and_convert_links_to_selects(self): | |||
def reset_field_parent(self): | |||
'''Convert link fields to select with names as options''' | |||
for df in self.web_form_fields: | |||
df.parent = self.doc_type | |||
if df.fieldtype == "Link": | |||
options = [d.name for d in frappe.get_all(df.options)] | |||
df.fieldtype = "Select" | |||
if len(options)==1: | |||
df.options = options[0] | |||
df.default = options[0] | |||
df.hidden = 1 | |||
else: | |||
df.options = "\n".join([""] + options) | |||
def use_meta_fields(self): | |||
'''Override default properties for standard web forms''' | |||
@@ -78,7 +68,7 @@ class WebForm(WebsiteGenerator): | |||
from frappe.modules import get_module_path | |||
# json | |||
export_to_files(record_list=[['Web Form', self.name]]) | |||
export_to_files(record_list=[['Web Form', self.name]], record_module=self.module) | |||
# write files | |||
path = os.path.join(get_module_path(self.module), 'web_form', scrub(self.name), scrub(self.name)) | |||
@@ -120,7 +110,7 @@ def get_context(context): | |||
if frappe.form_dict.name and not has_web_form_permission(self.doc_type, frappe.form_dict.name): | |||
frappe.throw(_("You don't have the permissions to access this document"), frappe.PermissionError) | |||
self.reset_field_parent_and_convert_links_to_selects() | |||
self.reset_field_parent() | |||
if self.is_standard: | |||
self.use_meta_fields() | |||
@@ -167,7 +157,7 @@ def get_context(context): | |||
"<br>").replace("'", "\'") | |||
self.add_custom_context_and_script(context) | |||
def add_custom_context_and_script(self, context): | |||
'''Update context from module if standard and append script''' | |||
if self.is_standard: | |||
@@ -193,21 +183,54 @@ def get_context(context): | |||
def get_layout(self): | |||
layout = [] | |||
for df in self.web_form_fields: | |||
if not layout: | |||
layout.append({'columns': []}) | |||
def add_page(df=None): | |||
new_page = {'sections': []} | |||
layout.append(new_page) | |||
if df and df.fieldtype=='Page Break': | |||
new_page['label'] = df.label | |||
if df.fieldtype=="Section Break": | |||
layout.append({'label': df.label, 'columns': [] }) | |||
return new_page | |||
if not layout[-1]['columns']: | |||
layout[-1]['columns'].append([]) | |||
def add_section(df=None): | |||
new_section = {'columns': []} | |||
layout[-1]['sections'].append(new_section) | |||
if df and df.fieldtype=='Section Break': | |||
new_section['label'] = df.label | |||
if df.fieldtype=="Column Break" or not layout[-1]['columns']: | |||
layout[-1]['columns'].append([]) | |||
return new_section | |||
def add_column(df=None): | |||
new_col = [] | |||
layout[-1]['sections'][-1]['columns'].append(new_col) | |||
return new_col | |||
page, section, column = None, None, None | |||
for df in self.web_form_fields: | |||
if df.fieldtype not in ("Section Break", "Column Break"): | |||
layout[-1]['columns'][-1].append(df) | |||
# breaks | |||
if df.fieldtype=='Page Break': | |||
page = add_page(df) | |||
section, column = None, None | |||
if df.fieldtype=='Section Break': | |||
section = add_section(df) | |||
column = None | |||
if df.fieldtype=='Column Break': | |||
column = add_column(df) | |||
# input | |||
if df.fieldtype not in ('Section Break', 'Column Break', 'Page Break'): | |||
if not page: | |||
page = add_page() | |||
section, column = None, None | |||
if not section: | |||
section = add_section() | |||
column = None | |||
if not column: | |||
column = add_column() | |||
column.append(df) | |||
return layout | |||
@@ -25,7 +25,7 @@ | |||
"label": "Fieldtype", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "Attach\nCheck\nData\nDate\nDatetime\nHTML\nLink\nSelect\nText\nTable\nSection Break\nColumn Break", | |||
"options": "Attach\nCheck\nData\nDate\nDatetime\nHTML\nLink\nSelect\nText\nTable\nSection Break\nColumn Break\nPage Break", | |||
"permlevel": 0, | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
@@ -319,7 +319,7 @@ | |||
"issingle": 0, | |||
"istable": 1, | |||
"max_attachments": 0, | |||
"modified": "2016-09-13 12:39:31.889290", | |||
"modified": "2016-09-19 08:02:36.033335", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Web Form Field", | |||