* [wip] grid in web form * [complete] webform in grid * [minor] configurable sidebar items in web form * [fix] tests * [style]version-14
@@ -2,7 +2,7 @@ | |||
"allow_copy": 0, | |||
"allow_import": 0, | |||
"allow_rename": 1, | |||
"autoname": "field:subject", | |||
"autoname": "", | |||
"beta": 0, | |||
"creation": "2013-01-10 16:34:31", | |||
"custom": 0, | |||
@@ -16,6 +16,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "email_group", | |||
"fieldtype": "Link", | |||
"hidden": 0, | |||
@@ -42,6 +43,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "subject", | |||
"fieldtype": "Small Text", | |||
"hidden": 0, | |||
@@ -66,6 +68,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"description": "", | |||
"fieldname": "send_from", | |||
"fieldtype": "Data", | |||
@@ -91,6 +94,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "email_sent", | |||
"fieldtype": "Check", | |||
"hidden": 0, | |||
@@ -115,6 +119,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "newsletter_content", | |||
"fieldtype": "Section Break", | |||
"hidden": 0, | |||
@@ -139,6 +144,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "message", | |||
"fieldtype": "Text Editor", | |||
"hidden": 0, | |||
@@ -163,6 +169,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"description": "", | |||
"fieldname": "test_the_newsletter", | |||
"fieldtype": "Section Break", | |||
@@ -188,6 +195,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"description": "A Lead with this email id should exist", | |||
"fieldname": "test_email_id", | |||
"fieldtype": "Data", | |||
@@ -213,6 +221,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "test_send", | |||
"fieldtype": "Button", | |||
"hidden": 0, | |||
@@ -247,7 +256,7 @@ | |||
"istable": 0, | |||
"max_attachments": 0, | |||
"menu_index": 0, | |||
"modified": "2016-08-05 06:26:58.705579", | |||
"modified": "2016-09-13 11:33:47.892910", | |||
"modified_by": "Administrator", | |||
"module": "Email", | |||
"name": "Newsletter", | |||
@@ -15,6 +15,9 @@ from frappe.email.queue import send | |||
from frappe.email.doctype.email_group.email_group import add_subscribers | |||
class Newsletter(Document): | |||
def autoname(self): | |||
self.name = self.subject | |||
def onload(self): | |||
if self.email_sent: | |||
self.get("__onload").status_count = dict(frappe.db.sql("""select status, count(name) | |||
@@ -228,3 +228,10 @@ a.no-decoration:active { | |||
-webkit-filter: grayscale(100%); | |||
filter: grayscale(100%); | |||
} | |||
.uppercase { | |||
padding-bottom: 4px; | |||
text-transform: uppercase; | |||
font-size: 12px; | |||
letter-spacing: 0.4px; | |||
color: #8D99A6; | |||
} |
@@ -228,6 +228,13 @@ a.no-decoration:active { | |||
-webkit-filter: grayscale(100%); | |||
filter: grayscale(100%); | |||
} | |||
.uppercase { | |||
padding-bottom: 4px; | |||
text-transform: uppercase; | |||
font-size: 12px; | |||
letter-spacing: 0.4px; | |||
color: #8D99A6; | |||
} | |||
.nav-pills a, | |||
.nav-pills a:hover { | |||
border-bottom: none; | |||
@@ -228,6 +228,13 @@ a.no-decoration:active { | |||
-webkit-filter: grayscale(100%); | |||
filter: grayscale(100%); | |||
} | |||
.uppercase { | |||
padding-bottom: 4px; | |||
text-transform: uppercase; | |||
font-size: 12px; | |||
letter-spacing: 0.4px; | |||
color: #8D99A6; | |||
} | |||
.avatar { | |||
display: inline-block; | |||
vertical-align: middle; | |||
@@ -263,3 +263,12 @@ a.no-decoration& { | |||
-webkit-filter: grayscale(100%); | |||
filter: grayscale(100%); | |||
} | |||
.uppercase { | |||
padding-bottom: 4px; | |||
text-transform: uppercase; | |||
font-size: 12px; | |||
letter-spacing: 0.4px; | |||
color: @text-muted; | |||
} | |||
@@ -18,7 +18,7 @@ | |||
{{ _("Submit") if frappe.form_dict.new else _("Update") }}</button> | |||
{% elif is_list %} | |||
<div style="padding-bottom: 15px;"> | |||
<a href="/{{ pathname + args }}{{ delimeter }}new=1" class="btn btn-primary btn-new btn-sm"> | |||
<a href="/{{ pathname }}{{ delimeter }}new=1" class="btn btn-primary btn-new btn-sm"> | |||
{{ _("New {0}").format(_(doc_type)) }} | |||
</a> | |||
</div> | |||
@@ -50,17 +50,22 @@ | |||
<br> | |||
{%- macro properties(field) %} | |||
name="{{ field.fieldname }}" id="{{ field.fieldname }}" | |||
name="{{ field.fieldname }}" | |||
{% if field.placeholder -%} placeholder="{{ _(field.placeholder) }}" {%- endif %} | |||
data-label="{{ _(field.label) }}" data-fieldtype="{{ field.fieldtype }}" | |||
data-doctype="{{ field.parent }}" data-default="{{ field.default or "" }}" | |||
{{ (field.reqd and field.fieldtype!="Attach") and "required" or "" }} | |||
{{ field.read_only and "disabled" or "" }} | |||
{% endmacro -%} | |||
{%- macro value(field) -%}{% if doc %}{{ | |||
doc.get(field.fieldname) or | |||
field.default or | |||
frappe.form_dict.get(field.fieldname) or "" }}{% else %}{{ getCookie(field.options) or field.default or "" }}{% endif %}{%- endmacro -%} | |||
{%- macro value(field, _doc) -%} | |||
{%- if _doc -%} | |||
{{ _doc.get(field.fieldname) or field.default | |||
or frappe.form_dict.get(field.fieldname) or "" }} | |||
{%- else -%} | |||
{{ field.default or "" }} | |||
{%- endif -%} | |||
{%- endmacro -%} | |||
{%- macro help(field) -%} | |||
{% if field.description -%} | |||
@@ -73,7 +78,7 @@ | |||
{{ _(field.label) }}</label> | |||
{% endmacro %} | |||
{% macro render_field(field) %} | |||
{% macro render_field(field, _doc=None, with_label=True) %} | |||
{% if field.hidden %} | |||
<input type="hidden" | |||
name="{{ field.fieldname }}" {% if field.default -%} value="{{ field.default }}" {%- endif %}> | |||
@@ -83,18 +88,18 @@ | |||
</div> | |||
{% elif field.fieldtype in ("Data", "Date", "Datetime") %} | |||
<div class="form-group"> | |||
{{ label(field) }} | |||
{% if with_label %}{{ label(field) }}{% endif %} | |||
<input type="text" class="form-control" {{ properties(field) }} | |||
value="{{ value(field) }}"> | |||
value="{{ value(field, _doc) }}"> | |||
{{ help(field) }} | |||
</div> | |||
{% elif field.fieldtype=="Select" %} | |||
<div class="form-group"> | |||
{{ label(field) }} | |||
{% if with_label %}{{ label(field) }}{% endif %} | |||
<select class="form-control" {{ properties(field) }}> | |||
{% for option in field.options.split("\n") -%} | |||
<option value="{{ option }}" | |||
{{ 'selected="selected"' if value(field)==option else '' }}> | |||
{{ 'selected="selected"' if value(field, _doc)==option else '' }}> | |||
{{ option }}</option> | |||
{%- endfor %} | |||
</select> | |||
@@ -102,24 +107,24 @@ | |||
</div> | |||
{% elif field.fieldtype=="Text" %} | |||
<div class="form-group"> | |||
{{ label(field) }} | |||
{% if with_label %}{{ label(field) }}{% endif %} | |||
{{ help(field) }} | |||
<textarea class="form-control" style="height: 200px;" | |||
{{ properties(field) }}>{{ value(field) }}</textarea> | |||
{{ properties(field) }}>{{ value(field, _doc) }}</textarea> | |||
</div> | |||
{% elif field.fieldtype=="Attach" %} | |||
<div class="form-group"> | |||
{{ label(field) }} | |||
{% if value(field) -%} | |||
{% if with_label %}{{ label(field) }}{% endif %} | |||
{% if value(field, _doc) -%} | |||
<p class="small"> | |||
<a href="{{ doc.get(field.fieldname) }}" target="blank"> | |||
{{ doc.get(field.fieldname) }} | |||
{{ _doc.get(field.fieldname) }} | |||
</a> | |||
<br><button class="btn btn-small btn-default btn-xs | |||
change-attach" style="margin-top: 5px;">Change</button> | |||
</p> | |||
{%- endif %} | |||
<p class="{{ value(field) and 'hide' or '' }} attach-input-wrap"> | |||
<p class="{{ value(field, _doc) and 'hide' or '' }} attach-input-wrap"> | |||
<input type="file" style="margin-top: 7px;" | |||
{{ properties(field) }}> | |||
</p> | |||
@@ -128,18 +133,65 @@ | |||
{% elif field.fieldtype=="Check" %} | |||
<div class="form-group"> | |||
<div class="checkbox"> | |||
{% if with_label %} | |||
<label> | |||
<input type="checkbox" id="{{ field.fieldname }}" | |||
name="{{ field.fieldname }}" | |||
{{ doc and doc.get(field.fieldname) and 'checked' or '' }}> | |||
{{ _(field.label) }} | |||
</label> | |||
{% endif %} | |||
{{ help(field) }} | |||
</div> | |||
</div> | |||
{% endif %} | |||
{% endmacro %} | |||
{% macro render_row(field, d=None, hidden=False) %} | |||
<tr class="web-form-grid-row {% if hidden %}hidden{% endif %}" | |||
{% 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 }}%;"> | |||
{% if d!=None %} | |||
{{ render_field(df, d, False) }} | |||
{% else %} | |||
<span class='text-muted small'>{{ df.label }}</span> | |||
{% endif %} | |||
</{{ 'th' if d.name==None else 'td' }}> | |||
{% endif %} | |||
{% endfor %} | |||
<td style="width: 8.3333%"> | |||
{% if d!=None %} | |||
<button class='btn btn-default btn-sm btn-remove'><i class='icon-remove'></i></button> | |||
{% endif %} | |||
</td> | |||
</tr> | |||
{% endmacro %} | |||
{% macro render_table(field) %} | |||
<div class='web-form-grid' | |||
data-fieldname='{{ field.fieldname }}' data-doctype='{{ field.options }}'> | |||
<table class='table table-bordered'> | |||
<thead> | |||
{{ render_row(field) }} | |||
</thead> | |||
<tbody> | |||
{{ render_row(field, {}, True) }} | |||
{% if doc.get(field.fieldname) %} | |||
{% for d in doc.get(field.fieldname) %} | |||
{{ render_row(field, d) }} | |||
{% endfor %} | |||
{% else %} | |||
{{ render_row(field, {}) }} | |||
{% endif %} | |||
</tbody> | |||
</table> | |||
</div> | |||
<p><button class='btn btn-default btn-sm btn-add-row' | |||
data-fieldname='{{ field.fieldname }}'>{{ _("Add Row") }}</button></p> | |||
{% endmacro %} | |||
<div class="form-message text-muted hide"></div> | |||
<form role="form" | |||
data-web-form="{{ name }}" data-owner="{{ doc.owner }}"> | |||
@@ -153,23 +205,29 @@ | |||
</div> | |||
</div> | |||
{% endif %} | |||
<input type="hidden" name="web_form" value="{{ name }}"> | |||
<input type="hidden" name="doctype" value="{{ doc_type }}"> | |||
{% if frappe.form_dict.name -%} | |||
<input type="hidden" name="name" value="{{ frappe.form_dict.name }}"> | |||
<input type="hidden" name="name" value="{{ frappe.form_dict.name }}" | |||
data-doctype="{{ doc_type }}"> | |||
{%- endif %} | |||
{% for section in layout %} | |||
{% for section in layout %} | |||
{% if section.label %} | |||
<h5 class='uppercase'>{{ _(section.label) }}</h5> | |||
{% endif %} | |||
<div class="row"> | |||
{% for column in section %} | |||
<div class="col-sm-{{ (12 / (section|len))|int }}" style="max-width: 500px;"> | |||
{% for column in section.columns %} | |||
<div class="col-sm-{{ (12 / (section.columns|len))|int }}"> | |||
{% for field in column %} | |||
{{ render_field(field) }} | |||
{% if field.fieldtype=='Table' %} | |||
{{ render_table(field) }} | |||
{% else %} | |||
{{ render_field(field, doc) }} | |||
{% endif %} | |||
{% endfor %} | |||
</div> | |||
{% endfor %} | |||
</div> | |||
{% endfor %} | |||
{% endfor %} | |||
</form> | |||
{% if allow_comments and not frappe.form_dict.new -%} | |||
<div class="comments"> | |||
@@ -186,8 +244,10 @@ | |||
<script> | |||
frappe.ready(function() { | |||
window.file_reading = false; | |||
window.success_message = "{{ success_message or "" }}"; | |||
window.success_message = "{{ success_message or "" }}"; | |||
frappe.datepicker_format = "{{ frappe.date_format.replace('yyyy', 'yy') }}"; | |||
var web_form_doctype = "{{ doc_type }}"; | |||
var web_form_name = "{{ name }}"; | |||
var $form = $("form[data-web-form='{{ name }}']"); | |||
// read file attachment | |||
@@ -223,20 +283,61 @@ frappe.ready(function() { | |||
return false; | |||
}); | |||
// submit | |||
$(".btn-form-submit").on("click", function() { | |||
var args = {}; | |||
if(window.saving) | |||
return; | |||
window.saving = true; | |||
// add row | |||
$('.btn-add-row').on('click', function() { | |||
var fieldname = $(this).attr('data-fieldname'); | |||
var grid = $('.web-form-grid[data-fieldname="'+fieldname+'"]'); | |||
var new_row = grid.find('.web-form-grid-row.hidden').clone() | |||
.appendTo(grid.find('tbody')) | |||
.attr('data-name', '') | |||
.removeClass('hidden'); | |||
new_row.find('input').each(function() { | |||
$(this) | |||
.val($(this).attr('data-default') || "") | |||
.removeClass('hasDatepicker') | |||
.attr('id', ''); | |||
}); | |||
setup_date_picker(new_row); | |||
return false; | |||
}); | |||
if(window.file_reading) { | |||
window.saving = false; | |||
frappe.msgprint("Reading file, please retry."); | |||
return; | |||
} | |||
// remove row | |||
$('.web-form-grid').on('click', '.btn-remove', function() { | |||
$(this).parents('.web-form-grid-row:first').remove(); | |||
return false; | |||
}); | |||
// get document data | |||
var get_data = function() { | |||
var doc = get_data_for_doctype($form, web_form_doctype); | |||
doc.doctype = web_form_doctype; | |||
// get data from child tables | |||
$('.web-form-grid').each(function() { | |||
var fieldname = $(this).attr('data-fieldname'); | |||
var doctype = $(this).attr('data-doctype'); | |||
doc[fieldname] = []; | |||
$form.find("[name]").each(function() { | |||
// get data from each row | |||
$(this).find('[data-child-row=1]:visible').each(function() { | |||
var d = get_data_for_doctype($(this), doctype); | |||
// set name of child record (if set) | |||
var name = $(this).attr('data-name'); | |||
if(name) { d.name = name; } | |||
doc[fieldname].push(d); | |||
}); | |||
}); | |||
return doc; | |||
} | |||
// get data from input elements | |||
// for the given doctype | |||
var get_data_for_doctype = function(parent, doctype) { | |||
var out = {}; | |||
parent.find("[name][data-doctype='"+ doctype +"']").each(function() { | |||
var $input = $(this); | |||
var input_type = $input.attr("type"); | |||
if(input_type==="file") { | |||
@@ -257,13 +358,30 @@ frappe.ready(function() { | |||
throw "mandatory missing"; | |||
} | |||
args[$input.attr("name")] = val; | |||
out[$input.attr("name")] = val; | |||
}); | |||
return out; | |||
} | |||
// submit | |||
$(".btn-form-submit").on("click", function() { | |||
if(window.saving) | |||
return; | |||
window.saving = true; | |||
if(window.file_reading) { | |||
window.saving = false; | |||
frappe.msgprint("Reading file, please retry."); | |||
return; | |||
} | |||
frappe.call({ | |||
type: "POST", | |||
method: "frappe.website.doctype.web_form.web_form.accept", | |||
args: args, | |||
args: { | |||
data: get_data(), | |||
web_form: web_form_name | |||
}, | |||
freeze: true, | |||
btn: $form.find("[type='submit']"), | |||
callback: function(data) { | |||
@@ -278,7 +396,7 @@ frappe.ready(function() { | |||
} else { | |||
frappe.msgprint(__('Successfully Updated')); | |||
setTimeout(function() { | |||
window.location.href = "{{ success_url }}"; | |||
//window.location.href = "{{ success_url }}"; | |||
}, 3000); | |||
} | |||
} | |||
@@ -311,40 +429,49 @@ frappe.ready(function() { | |||
} | |||
}) | |||
// import date picker / timepicker if required | |||
{% if "Date" in types %} | |||
frappe.require("assets/frappe/js/lib/jquery/jquery-ui.min.js"); | |||
frappe.require("assets/frappe/js/lib/jquery/bootstrap_theme/jquery-ui.selected.css"); | |||
$form.find("[data-fieldtype='Date']").datepicker({ | |||
altFormat:'yy-mm-dd', | |||
changeYear: true, | |||
yearRange: "-70Y:+10Y", | |||
dateFormat: frappe.datepicker_format, | |||
}); | |||
// setup datepicker in all inputs within the given element | |||
var setup_date_picker = function(ele) { | |||
var $dates = ele.find("[data-fieldtype='Date']"); | |||
var $date_times = ele.find("[data-fieldtype='Datetime']"); | |||
// setup date | |||
if($dates.length) { | |||
frappe.require("assets/frappe/js/lib/jquery/jquery-ui.min.js"); | |||
frappe.require("assets/frappe/js/lib/jquery/bootstrap_theme/jquery-ui.selected.css"); | |||
$dates.datepicker({ | |||
altFormat:'yy-mm-dd', | |||
changeYear: true, | |||
yearRange: "-70Y:+10Y", | |||
dateFormat: frappe.datepicker_format, | |||
}); | |||
// convert dates to user format | |||
$form.find("[data-fieldtype='Date']").each(function() { | |||
var val = $(this).val(); | |||
if(val) { | |||
$(this).val($.datepicker.formatDate(frappe.datepicker_format, | |||
$.datepicker.parseDate("yy-mm-dd", val))); | |||
// convert dates to user format | |||
$dates.each(function() { | |||
var val = $(this).val(); | |||
if(val) { | |||
$(this).val($.datepicker.formatDate(frappe.datepicker_format, | |||
$.datepicker.parseDate("yy-mm-dd", val))); | |||
} | |||
}); | |||
} | |||
}) | |||
{% endif %} | |||
// setup datetime | |||
if($date_times.length) { | |||
frappe.require("assets/frappe/js/lib/jquery/jquery.ui.slider.min.js"); | |||
frappe.require("assets/frappe/js/lib/jquery/jquery.ui.sliderAccess.js"); | |||
frappe.require("assets/frappe/js/lib/jquery/jquery.ui.timepicker-addon.css"); | |||
frappe.require("assets/frappe/js/lib/jquery/jquery.ui.timepicker-addon.js"); | |||
$date_times.datetimepicker({ | |||
altFormat:'yy-mm-dd', | |||
changeYear: true, | |||
yearRange: "-70Y:+10Y", | |||
dateFormat: frappe.datepicker_format, | |||
}); | |||
} | |||
} | |||
{% if "Datetime" in types %} | |||
frappe.require("assets/frappe/js/lib/jquery/jquery.ui.slider.min.js"); | |||
frappe.require("assets/frappe/js/lib/jquery/jquery.ui.sliderAccess.js"); | |||
frappe.require("assets/frappe/js/lib/jquery/jquery.ui.timepicker-addon.css"); | |||
frappe.require("assets/frappe/js/lib/jquery/jquery.ui.timepicker-addon.js"); | |||
$form.find("[data-fieldtype='Datetime']").datetimepicker({ | |||
altFormat:'yy-mm-dd', | |||
changeYear: true, | |||
yearRange: "-70Y:+10Y", | |||
dateFormat: frappe.datepicker_format, | |||
}) | |||
{% endif %} | |||
setup_date_picker($form); | |||
}); | |||
{% if script is defined %} | |||
@@ -355,5 +482,20 @@ frappe.ready(function() { | |||
{% endblock %} | |||
{% block style %} | |||
{% if style is defined %}<style>{{ style }}</style>{% endif %} | |||
<style> | |||
input, select { | |||
max-width: 500px; | |||
} | |||
.web-form-grid-row input, .web-form-grid-row select { | |||
border: 0px; | |||
padding: 0px; | |||
} | |||
.web-form-grid-row input:focus, .web-form-grid-row select:focus { | |||
box-shadow: none; | |||
} | |||
.web-form-grid-row .form-group { | |||
margin: 0px; | |||
} | |||
{% if style is defined %}{{ style }}{% endif %} | |||
</style> | |||
{% endblock %} |
@@ -95,14 +95,15 @@ def add_sidebar_data(context): | |||
from frappe.utils.user import get_fullname_and_avatar | |||
import frappe.www.list | |||
sidebar_items = json.loads(frappe.cache().get('sidebar_items') or '[]') | |||
if not sidebar_items: | |||
sidebar_items = frappe.get_all('Portal Menu Item', | |||
fields=['title', 'route', 'reference_doctype', 'show_always'], | |||
filters={'enabled': 1}, order_by='idx asc') | |||
frappe.cache().set('portal_menu_items', json.dumps(sidebar_items)) | |||
if not context.sidebar_items: | |||
sidebar_items = json.loads(frappe.cache().get_value('portal_menu_items') or '[]') | |||
if not sidebar_items: | |||
sidebar_items = frappe.get_all('Portal Menu Item', | |||
fields=['title', 'route', 'reference_doctype', 'show_always'], | |||
filters={'enabled': 1, 'parent': 'Portal Settings'}, order_by='idx asc') | |||
frappe.cache().set_value('portal_menu_items', json.dumps(sidebar_items)) | |||
context.sidebar_items = sidebar_items | |||
info = get_fullname_and_avatar(frappe.session.user) | |||
@@ -14,6 +14,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "title", | |||
"fieldtype": "Data", | |||
"hidden": 0, | |||
@@ -39,6 +40,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "enabled", | |||
"fieldtype": "Check", | |||
"hidden": 0, | |||
@@ -64,6 +66,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "route", | |||
"fieldtype": "Data", | |||
"hidden": 0, | |||
@@ -78,7 +81,7 @@ | |||
"precision": "", | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
"read_only": 1, | |||
"read_only": 0, | |||
"report_hide": 0, | |||
"reqd": 1, | |||
"search_index": 0, | |||
@@ -89,6 +92,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"description": "", | |||
"fieldname": "reference_doctype", | |||
"fieldtype": "Link", | |||
@@ -116,6 +120,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "target", | |||
"fieldtype": "Data", | |||
"hidden": 0, | |||
@@ -141,6 +146,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "show_always", | |||
"fieldtype": "Check", | |||
"hidden": 0, | |||
@@ -173,7 +179,7 @@ | |||
"issingle": 0, | |||
"istable": 1, | |||
"max_attachments": 0, | |||
"modified": "2016-07-11 03:28:03.883801", | |||
"modified": "2016-09-13 13:08:58.890253", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Portal Menu Item", | |||
@@ -3,7 +3,7 @@ | |||
from __future__ import unicode_literals | |||
import frappe | |||
import unittest | |||
import unittest, json | |||
from frappe.website.render import build_page | |||
from frappe.website.doctype.web_form.web_form import accept | |||
@@ -38,12 +38,12 @@ class TestWebForm(unittest.TestCase): | |||
def test_accept(self): | |||
frappe.set_user("Administrator") | |||
frappe.form_dict.web_form = "manage-events" | |||
frappe.form_dict.doctype = "Event" | |||
frappe.form_dict.subject = "_Test Event Web Form" | |||
frappe.form_dict.description = "_Test Event Description" | |||
frappe.form_dict.starts_on = "2014-09-09" | |||
accept() | |||
accept(web_form='manage-events', data=json.dumps({ | |||
'doctype': 'Event', | |||
'subject': '_Test Event Web Form', | |||
'description': '_Test Event Description', | |||
'starts_on': '2014-09-09' | |||
})) | |||
self.event_name = frappe.db.get_value("Event", | |||
{"subject": "_Test Event Web Form"}) | |||
@@ -51,17 +51,18 @@ class TestWebForm(unittest.TestCase): | |||
def test_edit(self): | |||
self.test_accept() | |||
frappe.form_dict.web_form = "manage-events" | |||
frappe.form_dict.doctype = "Event" | |||
frappe.form_dict.name = self.event_name | |||
frappe.form_dict.subject = "_Test Event Web Form" | |||
frappe.form_dict.description = "_Test Event Description 1" | |||
frappe.form_dict.starts_on = "2014-09-09" | |||
doc={ | |||
'doctype': 'Event', | |||
'subject': '_Test Event Web Form', | |||
'description': '_Test Event Description 1', | |||
'starts_on': '2014-09-09', | |||
'name': self.event_name | |||
} | |||
self.assertNotEquals(frappe.db.get_value("Event", | |||
self.event_name, "description"), frappe.form_dict.description) | |||
self.event_name, "description"), doc.get('description')) | |||
accept() | |||
accept(web_form='manage-events', data=json.dumps(doc)) | |||
self.assertEquals(frappe.db.get_value("Event", | |||
self.event_name, "description"), frappe.form_dict.description) | |||
self.event_name, "description"), doc.get('description')) |
@@ -5,8 +5,12 @@ frappe.web_form = { | |||
if(doc.doc_type) { | |||
frappe.model.with_doctype(doc.doc_type, function() { | |||
var fields = $.map(frappe.get_doc("DocType", frm.doc.doc_type).fields, function(d) { | |||
return frappe.model.no_value_type.indexOf(d.fieldtype)===-1 ? | |||
d.fieldname : null; | |||
if(frappe.model.no_value_type.indexOf(d.fieldtype)===-1 | |||
|| d.fieldtype==='Table') { | |||
return {label: d.label + ' ('+d.fieldtype+')', value:d.fieldname} | |||
} else { | |||
return null; | |||
} | |||
}) | |||
frappe.meta.get_docfield("Web Form Field", "fieldname", frm.doc.name).options | |||
= [""].concat(fields); | |||
@@ -40,18 +44,26 @@ frappe.ui.form.on("Web Form", { | |||
}); | |||
frappe.ui.form.on("Web Form Field", "fieldname", function(frm, doctype, name) { | |||
var doc = frappe.get_doc(doctype, name); | |||
var df = $.map(frappe.get_doc("DocType", frm.doc.doc_type).fields, function(d) { | |||
return doc.fieldname==d.fieldname ? d : null; })[0]; | |||
frappe.ui.form.on("Web Form Field", { | |||
fieldtype: function(frm, doctype, name) { | |||
var doc = frappe.get_doc(doctype, name); | |||
if(fieldtype==='Section Break' || fieldtype==='Section Break') { | |||
doc.fieldname = ''; | |||
frm.refresh_field("web_form_fields"); | |||
} | |||
}, | |||
fieldname: function(frm, doctype, name) { | |||
var doc = frappe.get_doc(doctype, name); | |||
var df = $.map(frappe.get_doc("DocType", frm.doc.doc_type).fields, function(d) { | |||
return doc.fieldname==d.fieldname ? d : null; })[0]; | |||
doc.label = df.label; | |||
doc.reqd = df.reqd; | |||
doc.options = df.options; | |||
doc.fieldtype = frappe.meta.get_docfield("Web Form Field", "fieldtype") | |||
.options.split("\n").indexOf(df.fieldtype)===-1 ? "Data" : df.fieldtype; | |||
doc.description = df.description; | |||
doc["default"] = df["default"]; | |||
doc.label = df.label; | |||
doc.reqd = df.reqd; | |||
doc.options = df.options; | |||
doc.fieldtype = frappe.meta.get_docfield("Web Form Field", "fieldtype") | |||
.options.split("\n").indexOf(df.fieldtype)===-1 ? "Data" : df.fieldtype; | |||
doc.description = df.description; | |||
doc["default"] = df["default"]; | |||
frm.refresh_field("web_form_fields"); | |||
} | |||
}); |
@@ -9,11 +9,13 @@ | |||
"docstatus": 0, | |||
"doctype": "DocType", | |||
"document_type": "Document", | |||
"editable_grid": 0, | |||
"fields": [ | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "title", | |||
"fieldtype": "Data", | |||
"hidden": 0, | |||
@@ -38,6 +40,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "route", | |||
"fieldtype": "Data", | |||
"hidden": 0, | |||
@@ -63,6 +66,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "doc_type", | |||
"fieldtype": "Link", | |||
"hidden": 0, | |||
@@ -88,6 +92,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "module", | |||
"fieldtype": "Link", | |||
"hidden": 1, | |||
@@ -114,6 +119,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "column_break_4", | |||
"fieldtype": "Column Break", | |||
"hidden": 0, | |||
@@ -137,6 +143,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "is_standard", | |||
"fieldtype": "Check", | |||
"hidden": 0, | |||
@@ -162,6 +169,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "published", | |||
"fieldtype": "Check", | |||
"hidden": 0, | |||
@@ -186,6 +194,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "login_required", | |||
"fieldtype": "Check", | |||
"hidden": 0, | |||
@@ -210,6 +219,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"depends_on": "login_required", | |||
"fieldname": "allow_edit", | |||
"fieldtype": "Check", | |||
@@ -235,6 +245,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"depends_on": "login_required", | |||
"fieldname": "allow_multiple", | |||
"fieldtype": "Check", | |||
@@ -260,6 +271,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"depends_on": "allow_multiple", | |||
"fieldname": "allow_delete", | |||
"fieldtype": "Check", | |||
@@ -286,6 +298,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"depends_on": "login_required", | |||
"fieldname": "allow_comments", | |||
"fieldtype": "Check", | |||
@@ -311,6 +324,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "fields", | |||
"fieldtype": "Section Break", | |||
"hidden": 0, | |||
@@ -335,6 +349,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "introduction_text", | |||
"fieldtype": "Text", | |||
"hidden": 0, | |||
@@ -359,6 +374,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "web_form_fields", | |||
"fieldtype": "Table", | |||
"hidden": 0, | |||
@@ -384,6 +400,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "actions", | |||
"fieldtype": "Section Break", | |||
"hidden": 0, | |||
@@ -408,6 +425,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"description": "Message to be displayed on successful completion", | |||
"fieldname": "success_message", | |||
"fieldtype": "Text", | |||
@@ -433,6 +451,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"description": "Go to this url after completing the form.", | |||
"fieldname": "success_url", | |||
"fieldtype": "Data", | |||
@@ -454,10 +473,64 @@ | |||
"set_only_once": 0, | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
"columns": 0, | |||
"fieldname": "sidebar_settings", | |||
"fieldtype": "Section Break", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"label": "Sidebar Settings", | |||
"length": 0, | |||
"no_copy": 0, | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
"read_only": 0, | |||
"report_hide": 0, | |||
"reqd": 0, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "sidebar_items", | |||
"fieldtype": "Table", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"label": "Sidebar Items", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "Portal Menu Item", | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
"read_only": 0, | |||
"report_hide": 0, | |||
"reqd": 0, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "advanced", | |||
"fieldtype": "Section Break", | |||
"hidden": 0, | |||
@@ -483,6 +556,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"description": "Text to be displayed for Link to Web Page if this form has a web page. Link route will be automatically generated based on `page_name` and `parent_website_route`", | |||
"fieldname": "web_page_link_text", | |||
"fieldtype": "Data", | |||
@@ -509,6 +583,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"description": "In JSON as [{\"title\":\"Jobs\", \"name\":\"jobs\"}]", | |||
"fieldname": "breadcrumbs", | |||
"fieldtype": "Code", | |||
@@ -543,7 +618,7 @@ | |||
"issingle": 0, | |||
"istable": 0, | |||
"max_attachments": 0, | |||
"modified": "2016-06-24 15:37:23.369646", | |||
"modified": "2016-09-13 13:08:13.720070", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Web Form", | |||
@@ -19,6 +19,7 @@ class WebForm(WebsiteGenerator): | |||
) | |||
def onload(self): | |||
super(WebForm, self).onload() | |||
if self.is_standard and not frappe.conf.developer_mode: | |||
self.use_meta_fields() | |||
@@ -31,9 +32,10 @@ class WebForm(WebsiteGenerator): | |||
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 convert_links_to_selects(self): | |||
def reset_field_parent_and_convert_links_to_selects(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" | |||
@@ -45,7 +47,7 @@ class WebForm(WebsiteGenerator): | |||
else: | |||
df.options = "\n".join([""] + options) | |||
def use_meta_fields(self): | |||
'''Override default properties for standard web forms''' | |||
meta = frappe.get_meta(self.doc_type) | |||
@@ -107,8 +109,8 @@ def get_context(context): | |||
frappe.form_dict.is_web_form = 1 | |||
logged_in = frappe.session.user != "Guest" | |||
args, delimeter = make_route_string(frappe.form_dict) | |||
context.args = args | |||
doc, delimeter = make_route_string(frappe.form_dict) | |||
context.doc = doc | |||
context.delimeter = delimeter | |||
# check permissions | |||
@@ -118,7 +120,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.convert_links_to_selects() | |||
self.reset_field_parent_and_convert_links_to_selects() | |||
if self.is_standard: | |||
self.use_meta_fields() | |||
@@ -160,7 +162,6 @@ def get_context(context): | |||
context.parents = self.get_parents(context) | |||
context.types = [f.fieldtype for f in self.web_form_fields] | |||
if context.success_message: | |||
context.success_message = context.success_message.replace("\n", | |||
"<br>").replace("'", "\'") | |||
@@ -193,14 +194,20 @@ def get_context(context): | |||
def get_layout(self): | |||
layout = [] | |||
for df in self.web_form_fields: | |||
if df.fieldtype=="Section Break" or not layout: | |||
layout.append([]) | |||
if not layout: | |||
layout.append({'columns': []}) | |||
if df.fieldtype=="Section Break": | |||
layout.append({'label': df.label, 'columns': [] }) | |||
if df.fieldtype=="Column Break" or not layout[-1]: | |||
layout[-1].append([]) | |||
if not layout[-1]['columns']: | |||
layout[-1]['columns'].append([]) | |||
if df.fieldtype=="Column Break" or not layout[-1]['columns']: | |||
layout[-1]['columns'].append([]) | |||
if df.fieldtype not in ("Section Break", "Column Break"): | |||
layout[-1][-1].append(df) | |||
layout[-1]['columns'][-1].append(df) | |||
return layout | |||
@@ -215,40 +222,38 @@ def get_context(context): | |||
return parents | |||
@frappe.whitelist(allow_guest=True) | |||
def accept(): | |||
args = frappe.form_dict | |||
def accept(web_form, data): | |||
data = frappe._dict(json.loads(data)) | |||
files = [] | |||
web_form = frappe.get_doc("Web Form", args.web_form) | |||
if args.doctype != web_form.doc_type: | |||
web_form = frappe.get_doc("Web Form", web_form) | |||
if data.doctype != web_form.doc_type: | |||
frappe.throw(_("Invalid Request")) | |||
elif args.name and not web_form.allow_edit: | |||
elif data.name and not web_form.allow_edit: | |||
frappe.throw(_("You are not allowed to update this Web Form Document")) | |||
if args.name: | |||
if data.name: | |||
# update | |||
doc = frappe.get_doc(args.doctype, args.name) | |||
doc = frappe.get_doc(data.doctype, data.name) | |||
else: | |||
# insert | |||
doc = frappe.new_doc(args.doctype) | |||
doc = frappe.new_doc(data.doctype) | |||
# set values | |||
for fieldname, value in args.iteritems(): | |||
if fieldname not in ("web_form", "cmd", "owner"): | |||
if value and value.startswith("{"): | |||
try: | |||
filedata = json.loads(value) | |||
if "__file_attachment" in filedata: | |||
files.append((fieldname, filedata)) | |||
continue | |||
for fieldname, value in data.iteritems(): | |||
if value and isinstance(value, dict): | |||
try: | |||
if "__file_attachment" in value: | |||
files.append((fieldname, value)) | |||
continue | |||
except ValueError: | |||
pass | |||
except ValueError: | |||
pass | |||
doc.set(fieldname, value) | |||
doc.set(fieldname, value) | |||
if args.name: | |||
if doc.name: | |||
if has_web_form_permission(doc.doctype, doc.name, "write"): | |||
doc.save(ignore_permissions=True) | |||
else: | |||
@@ -14,16 +14,18 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"fieldname": "fieldname", | |||
"columns": 0, | |||
"fieldname": "fieldtype", | |||
"fieldtype": "Select", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 1, | |||
"label": "Fieldname", | |||
"label": "Fieldtype", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "Attach\nCheck\nData\nDate\nDatetime\nHTML\nLink\nSelect\nText\nTable\nSection Break\nColumn Break", | |||
"permlevel": 0, | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
@@ -38,17 +40,17 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"fieldname": "fieldtype", | |||
"columns": 0, | |||
"fieldname": "fieldname", | |||
"fieldtype": "Select", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 1, | |||
"label": "Fieldtype", | |||
"label": "Fieldname", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "Attach\nCheck\nData\nDate\nDatetime\nHTML\nLink\nSelect\nText\nSection Break\nColumn Break", | |||
"permlevel": 0, | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
@@ -63,6 +65,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "label", | |||
"fieldtype": "Data", | |||
"hidden": 0, | |||
@@ -87,6 +90,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "reqd", | |||
"fieldtype": "Check", | |||
"hidden": 0, | |||
@@ -111,6 +115,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "read_only", | |||
"fieldtype": "Check", | |||
"hidden": 0, | |||
@@ -135,6 +140,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "hidden", | |||
"fieldtype": "Check", | |||
"hidden": 0, | |||
@@ -159,6 +165,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "column_break_4", | |||
"fieldtype": "Column Break", | |||
"hidden": 0, | |||
@@ -182,6 +189,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "options", | |||
"fieldtype": "Text", | |||
"hidden": 0, | |||
@@ -206,6 +214,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "section_break_6", | |||
"fieldtype": "Section Break", | |||
"hidden": 0, | |||
@@ -229,6 +238,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "description", | |||
"fieldtype": "Text", | |||
"hidden": 0, | |||
@@ -253,6 +263,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "column_break_8", | |||
"fieldtype": "Column Break", | |||
"hidden": 0, | |||
@@ -276,6 +287,7 @@ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "default", | |||
"fieldtype": "Data", | |||
"hidden": 0, | |||
@@ -307,7 +319,7 @@ | |||
"issingle": 0, | |||
"istable": 1, | |||
"max_attachments": 0, | |||
"modified": "2016-07-11 03:28:09.794347", | |||
"modified": "2016-09-13 12:39:31.889290", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Web Form Field", | |||