added comment timeline and 'Fold' type form break

This commit is contained in:
Rushabh Mehta 2014-08-11 18:04:27 +05:30
parent 20466ca6d5
commit 218484bb0d
30 changed files with 325 additions and 211 deletions

View file

@ -16,6 +16,12 @@
"reqd": 1,
"search_index": 0
},
{
"fieldname": "comment_type",
"fieldtype": "Data",
"label": "Comment Type",
"permlevel": 0
},
{
"fieldname": "comment_by",
"fieldtype": "Data",
@ -100,7 +106,7 @@
"icon": "icon-comments",
"idx": 1,
"issingle": 0,
"modified": "2014-07-14 12:14:08.315217",
"modified": "2014-08-11 05:43:35.647132",
"modified_by": "Administrator",
"module": "Core",
"name": "Comment",

View file

@ -0,0 +1,6 @@
[
{
"doctype": "Comment",
"name": "_Test Comment 1"
}
]

View file

@ -34,7 +34,7 @@
"label": "Type",
"oldfieldname": "fieldtype",
"oldfieldtype": "Select",
"options": "Attach\nButton\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime",
"options": "Attach\nButton\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime",
"permlevel": 0,
"reqd": 1,
"search_index": 1
@ -259,7 +259,6 @@
"width": "50px"
},
{
"description": "Print Width of the field, if the field is a column in a table",
"fieldname": "print_width",
"fieldtype": "Data",
"label": "Print Width",
@ -305,7 +304,7 @@
"in_dialog": 1,
"issingle": 0,
"istable": 1,
"modified": "2014-08-05 08:29:06.769568",
"modified": "2014-08-11 07:00:52.537012",
"modified_by": "Administrator",
"module": "Core",
"name": "DocField",

View file

@ -191,7 +191,7 @@ def validate_fields(fields):
frappe.throw(_("Fieldname {0} appears multiple times in rows {1}").format(fieldname, ", ".join(duplicates)))
def check_illegal_mandatory(d):
if d.fieldtype in ('HTML', 'Button', 'Section Break', 'Column Break') and d.reqd:
if (d.fieldtype in no_value_fields) and d.fieldtype!="Table" and d.reqd:
frappe.throw(_("Field {0} of type {1} cannot be mandatory").format(d.label, d.fieldtype))
def check_link_table_options(d):
@ -228,6 +228,14 @@ def validate_fields(fields):
or (doctype_pointer[0].options!="DocType"):
frappe.throw(_("Options 'Dynamic Link' type of field must point to another Link Field with options as 'DocType'"))
def check_fold(fields):
for i, f in enumerate(fields):
if f.fieldtype=="Fold":
prev = fields[i-1]
if prev.fieldtype != "Section Break" \
or (prev.fieldtype=="Section Break" and not prev.label):
frappe.throw(_("Fold must come after labelled Section Break"))
for d in fields:
if not d.permlevel: d.permlevel = 0
if not d.fieldname:
@ -241,6 +249,7 @@ def validate_fields(fields):
check_in_list_view(d)
check_min_items_in_list(fields)
check_fold(fields)
def validate_permissions_for_doctype(doctype, for_remove=False):
doctype = frappe.get_doc("DocType", doctype)

View file

@ -28,7 +28,7 @@ class PropertySetter(Document):
return frappe.db.sql("""select fieldname, label, fieldtype
from tabDocField
where parent=%s
and fieldtype not in ('Section Break', 'Column Break', 'HTML', 'Read Only', 'Table')
and fieldtype not in ('Section Break', 'Column Break', 'HTML', 'Read Only', 'Table', 'Fold')
and ifnull(fieldname, '') != ''
order by label asc""", dt, as_dict=1)

View file

@ -9,19 +9,20 @@ from frappe.model.document import Document
class ToDo(Document):
def validate(self):
if self.is_new():
self.add_comment(frappe._("Assignment Added"))
self.add_comment(frappe._("Assigned to {0}").format(self.owner), "Assigned")
else:
cur_status = frappe.db.get_value("ToDo", self.name, "status")
if cur_status != self.status:
self.add_comment(frappe._("Assignment Status Changed"))
self.add_comment(frappe._("Assignment Status Changed"), "Assignment Completed")
def add_comment(self, text):
def add_comment(self, text, comment_type):
if not self.reference_type and self.reference_name:
return
comment = frappe.get_doc({
"doctype":"Comment",
"comment_by": frappe.session.user,
"comment_type": comment_type,
"comment_doctype": self.reference_type,
"comment_docname": self.reference_name,
"comment": """<div>{text}:

View file

@ -77,4 +77,6 @@
padding-top: 14px;
padding-bottom: 50px;
margin-bottom: -50px;
border: 0px;
background-color: transparent;
}

View file

@ -7,7 +7,7 @@ import frappe
import json
no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'Button', 'Image']
no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'Button', 'Image', 'Fold']
default_fields = ['doctype','name','owner','creation','modified','modified_by','parent','parentfield','parenttype','idx','docstatus']
integer_docfield_properties = ["reqd", "search_index", "in_list_view", "permlevel", "hidden", "read_only", "ignore_user_permissions", "allow_on_submit", "report_hide", "in_filter", "no_copy", "print_hide"]

View file

@ -428,9 +428,11 @@ class Document(BaseDocument):
elif self._action=="submit":
self.run_method("on_update")
self.run_method("on_submit")
self.add_comment("Submitted")
elif self._action=="cancel":
self.run_method("on_cancel")
self.check_no_back_links_exist()
self.add_comment("Cancelled")
elif self._action=="update_after_submit":
self.run_method("on_update_after_submit")
@ -564,3 +566,13 @@ class Document(BaseDocument):
def get_url(self):
return "/desk#Form/{doctype}/{name}".format(doctype=self.doctype, name=self.name)
def add_comment(self, comment_type, text=None):
comment = frappe.get_doc({
"doctype":"Comment",
"comment_by": frappe.session.user,
"comment_type": comment_type,
"comment_doctype": self.doctype,
"comment_docname": self.name,
"comment": text or comment_type
}).insert(ignore_permissions=True)

View file

@ -8,17 +8,11 @@
.appframe-wrapper {
background-color: #fff;
min-height: 400px;
box-shadow: 1px 0px 1px rgba(0,0,0,0.4);
/*box-shadow: 1px 0px 1px rgba(0,0,0,0.4);*/
}
.appframe-titlebar {
border-bottom: 1px solid #ddd;
}
.appframe-footer {
border-top: 1px solid #ddd;
/*background-color: rgba(255, 255, 255, 0.9);*/
background-color: #f9f9f9;
border-bottom: 1px solid #c7c7c7;
}
.appframe-titlebar, .appframe-iconbar, .appframe-form, .appframe-primary-actions {
@ -26,13 +20,17 @@
background-color: #f9f9f9;
}
.appframe-primary-actions {
border-bottom: 1px solid #c7c7c7;
}
.appframe-primary-actions .btn {
margin: 10px;
margin-left: 0px;
}
.appframe-iconbar {
border-bottom: 1px solid #ddd;
border-bottom: 1px solid #c7c7c7;
}
.titlebar-item {
@ -148,7 +146,7 @@ h2.titlebar-left-item {
}
.iconbar-4 {
border-left: 1px solid #ddd;
border-left: 1px solid #c7c7c7;
padding-left: 4px;
}
@ -185,11 +183,6 @@ h2.titlebar-left-item {
color: orange;
}
.appframe-footer {
padding-top: 15px;
}
.workflow-button-area {
margin-bottom: 15px;
}

View file

@ -1,7 +1,7 @@
.avatar {
display: inline-block;
vertical-align: middle;
border-radius: 50%;
border-radius: 5px;
overflow: hidden;
background-color: #ddd;
border: 1px solid #eee;

View file

@ -66,8 +66,16 @@ div#freeze {
text-align: center;
}
/* listing */
.app-page {
border: 1px solid #c7c7c7;
border-radius: 4px;
margin-top: 15px;
padding: 0px;
overflow: hidden;
}
/* listing */
.show_filters {
padding-top: 15px;
padding-bottom: 15px;
@ -122,6 +130,13 @@ div#freeze {
/*margin-top: -15px;*/
}
.form-page-header {
border-top: 1px solid #eee;
margin: 15px -15px -15px -15px;
padding: 10px 15px;
background-color: #f9f9f9;
}
.form-control {
padding: 6px 8px;
}
@ -262,6 +277,43 @@ div#freeze {
}
/* form */
.comment-connector {
height: 30px;
margin-left: 70px;
border-left: 1px solid #d7d7d7;
}
.comment-body {
border-left: 1px solid #d7d7d7;
padding: 5px 15px 15px 30px;
}
.comment-body p {
margin-bottom: 5px;
}
.comment-icon {
margin-right: -14px !important;
margin-left: 15px;
z-index: 1;
}
.icon-timeline {
color: #fff;
height: 29px;
width: 29px;
padding: 7px 9px;
border-radius: 50%;
background-color: #d7d7d7;
text-align: center;
display: inline-block;
float: left;
}
.comment {
margin-top: 0px;
}
.frappe-editor {
cursor: text;
}
@ -295,7 +347,8 @@ ul.linked-with-list li {
.grid-heading-row {
padding: 8px 15px;
border-bottom: 1px solid #dddddd;
border-bottom: 1px solid #c7c7c7;
background-color: #f9f9f9;
font-weight: bold;
}
@ -303,7 +356,7 @@ ul.linked-with-list li {
padding-bottom: 5px;
margin-bottom: 5px;
margin-top: 8px;
border-bottom: 1px solid #dddddd;
border-bottom: 1px solid #c7c7c7;
}
.rows .grid-row .data-row, .rows .grid-row .panel-heading {
@ -403,6 +456,7 @@ ul.linked-with-list li {
margin-bottom: 7px;
}
/* hack */
.ui-datepicker { z-index: 9999999 !important; }
.ui-autocomplete {

View file

@ -35,8 +35,7 @@ frappe.Application = Class.extend({
// load boot info
this.load_bootinfo();
// page container
this.make_page_container();
if(user!="Guest") this.set_user_display_settings();
// navbar
this.make_nav_bar();
@ -44,8 +43,6 @@ frappe.Application = Class.extend({
// favicon
this.set_favicon();
if(user!="Guest") this.set_user_display_settings();
this.setup_keyboard_shortcuts();
// control panel startup code
@ -58,10 +55,14 @@ frappe.Application = Class.extend({
localStorage.removeItem("session_lost_route");
}
// route to home page
frappe.route();
}
// page container
this.make_page_container();
// route to home page
frappe.route();
// trigger app startup
$(document).trigger('startup');
@ -176,7 +177,7 @@ frappe.Application = Class.extend({
make_nav_bar: function() {
// toolbar
if(frappe.boot) {
frappe.container.frappe_toolbar = new frappe.ui.toolbar.Toolbar();
frappe.frappe_toolbar = new frappe.ui.toolbar.Toolbar();
}
},
logout: function() {

View file

@ -174,52 +174,6 @@ frappe.get_shade = function(color, factor) {
+ get_hex(get_int(color.substr(4,2)) + factor)
}
frappe.get_gradient_css = function(col, diff) {
if(!diff) diff = 10
var col1 = frappe.get_shade(col, diff);
var col2 = frappe.get_shade(col, -diff);
return "\nbackground-color: " + col + " !important;"
+"\nbackground: -moz-linear-gradient(top, #"+col1+" 0%, #"+col2+" 99%) !important;"
+"\nbackground:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#"+col1+"), color-stop(99%,#"+col2+")) !important;"
+"\nbackground:-webkit-linear-gradient(top, #"+col1+" 0%,#"+col2+" 99%) !important;"
+"\nbackground:-o-linear-gradient(top, #"+col1+" 0%,#"+col2+" 99%) !important;"
+"\nbackground:-ms-linear-gradient(top, #"+col1+" 0%,#"+col2+" 99%) !important;"
+"\nbackground:-o-linear-gradient(top, #"+col1+" 0%,#"+col2+" 99%) !important;"
+"\nbackground:linear-gradient(top, #"+col1+" 0%,#%"+col2+" 99%) !important;"
+"\nfilter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#"+col1+"', endColorstr='#"+col1+"',GradientType=0 ) !important;"
}
$.fn.gradientify = function(col) {
if(!col) col = this.css("background-color");
var col1 = frappe.get_shade(col, 1.05);
var col2 = frappe.get_shade(col, 0.95);
this.css({
"background": "-moz-linear-gradient(top, #"+col1+" 0%, #"+col2+" 99%)"
});
this.css({
"background": "-webkit-gradient(linear, left top, left bottom, color-stop(0%,#"+col1+"), color-stop(99%,#"+col2+"))"
});
this.css({
"background": "-webkit-linear-gradient(top, #"+col1+" 0%,#"+col2+" 99%)"
});
this.css({
"background": "-o-linear-gradient(top, #"+col1+" 0%,#"+col2+" 99%);"
});
this.css({
"background": "-ms-linear-gradient(top, #"+col1+" 0%,#"+col2+" 99%);"
});
this.css({
"background": "-o-linear-gradient(top, #"+col1+" 0%,#"+col2+" 99%);"
});
this.css({
"background": "linear-gradient(top, #"+col1+" 0%,#%"+col2+" 99%);"
});
this.css({
"filter": "progid:DXImageTransform.Microsoft.gradient( startColorstr='#"+col1+"', endColorstr='#"+col1+"',GradientType=0 )"
});
}
frappe.get_cookie = function(c) {
var clist = (document.cookie+'').split(';');
var cookies = {};
@ -265,21 +219,9 @@ frappe.dom.set_box_shadow = function(ele, spread) {
return $(this);
}
$.fn.set_working = function() {
var ele = this.get(0);
$(ele).prop('disabled', true);
if(ele.loading_img) {
$(ele.loading_img).toggle(true);
} else {
ele.loading_img = $('<img src="assets/frappe/images/ui/button-load.gif" \
style="margin-left: 4px; margin-bottom: -2px; display: inline;" />')
.insertAfter(ele);
}
this.prop('disabled', true);
}
$.fn.done_working = function() {
var ele = this.get(0);
$(ele).prop('disabled', false);
if(ele.loading_img) {
$(ele.loading_img).toggle(false);
};
this.prop('disabled', false);
}
})(jQuery);

View file

@ -9,25 +9,30 @@ frappe.ui.form.Comments = Class.extend({
make: function() {
var me = this;
this.wrapper =this.parent;
this.row = $("<div class='row'>").appendTo(this.parent);
this.input = $('<div class="col-md-10" style="margin-top: 5px;">\
<textarea style="height: 80px" class="form-control"></textarea></div>')
.appendTo(this.row)
.find("textarea");
this.button = $('<div class="col-md-1">\
<button class="btn btn-default btn-go" class="col-md-1" style="margin-top: 5px;">\
<i class="icon-ok"></i></button>\
</div>')
.appendTo(this.row)
.find("button")
$('<div class="comment-connector"></div>').appendTo(this.parent);
this.list = $('<div class="comments"></div>')
.appendTo(this.parent);
this.row = $(repl('<div class="media comment" data-name="%(name)s">\
<span class="pull-left avatar avatar-small">\
<img class="media-object" src="%(image)s">\
</span>\
<div class="media-body">\
<textarea style="height: 80px" class="form-control"></textarea>\
<div class="text-right" style="margin-top: 10px">\
<button class="btn btn-default btn-go btn-sm">\
<i class="icon-ok"></i> Add comment</button>\
</div>\
<span class="small text-muted">%(fullname)s</span>\
</div>\
</div>', {image: frappe.user_info(user).image,
fullname: user_fullname})).appendTo(this.parent);
this.input = this.row.find(".form-control");
this.button = this.row.find(".btn-go")
.click(function() {
me.add_comment(this);
});
this.list = $('<div class="comments" style="margin-top: 15px;"></div>')
.appendTo(this.parent);
},
get_comments: function() {
return this.frm.get_docinfo().comments;
},
refresh: function() {
var me = this;
@ -37,7 +42,10 @@ frappe.ui.form.Comments = Class.extend({
}
this.wrapper.toggle(true);
this.list.empty();
var comments = this.get_comments();
comments = [{"comment": "Created", "comment_type": "Created",
"comment_by": this.frm.doc.owner, "creation": this.frm.doc.creation}].concat(this.get_comments());
$.each(comments, function(i, c) {
if(frappe.model.can_delete("Comment")) {
c["delete"] = '<a class="close" href="#">&times;</a>';
@ -47,15 +55,52 @@ frappe.ui.form.Comments = Class.extend({
c.image = frappe.user_info(c.comment_by).image || frappe.get_gravatar(c.comment_by);
c.comment_on = dateutil.comment_when(c.creation);
c.fullname = frappe.user_info(c.comment_by).fullname;
c.comment = frappe.markdown(c.comment);
$(repl('<div class="media col-md-10 comment" data-name="%(name)s">\
if(!c.comment_type) c.comment_type = "Comment"
c.icon = {
"Created": "icon-plus",
"Submitted": "icon-lock",
"Cancelled": "icon-remove",
"Assigned": "icon-user",
"Assignment Completed": "icon-ok",
"Comment": "icon-comment",
"Workflow": "icon-arrow-right",
}[c.comment_type]
c.icon_bg = {
"Created": "#1abc9c",
"Submitted": "#3498db",
"Cancelled": "#c0392b",
"Assigned": "#f39c12",
"Assignment Completed": "#16a085",
"Comment": "#7f8c8d",
"Workflow": "#2c3e50"
}[c.comment_type]
if(c.comment_type==="Workflow") {
c.comment_html = repl('<span class="label label-%(style)s">%(text)s</span>', {
style: frappe.utils.guess_style(c.comment),
text: c.comment
});
} else {
c.comment_html = frappe.markdown(c.comment);
}
$(repl('<div class="media comment" data-name="%(name)s">\
<span class="pull-left avatar avatar-small">\
<img class="media-object" src="%(image)s">\
</span>\
<div class="media-body">%(delete)s\
<div>%(comment)s</div>\
<span class="small text-muted">%(fullname)s / %(comment_on)s</span>\
<span class="pull-left comment-icon">\
<i class="%(icon)s icon-timeline" \
style="background-color: %(icon_bg)s"></i>\
</span>\
<div class="media-body comment-body">\
%(comment_html)s\
<div>\
<span class="small text-muted">\
%(fullname)s / %(comment_on)s %(delete)s</span>\
</div>\
</div>\
</div>', c))
.appendTo(me.list)
@ -67,37 +112,44 @@ frappe.ui.form.Comments = Class.extend({
});
},
get_comments: function() {
return this.frm.get_docinfo().comments
},
add_comment: function(btn) {
var me = this,
txt = me.input.val();
var txt = this.input.val();
if(txt) {
var comment = {
doctype: "Comment",
comment_doctype: me.frm.doctype,
comment_docname: me.frm.docname,
comment: txt,
comment_by: user
};
return frappe.call({
method: "frappe.widgets.form.utils.add_comment",
args: {
doc:comment
},
btn: btn,
callback: function(r) {
if(!r.exc) {
me.frm.get_docinfo().comments =
[r.message].concat(me.get_comments());
me.frm.toolbar.show_infobar();
me.input.val("");
me.refresh();
}
}
});
this.insert_comment("Comment", txt, btn);
}
},
insert_comment: function(comment_type, comment, btn) {
var me = this;
return frappe.call({
method: "frappe.widgets.form.utils.add_comment",
args: {
doc:{
doctype: "Comment",
comment_type: comment_type || "Comment",
comment_doctype: this.frm.doctype,
comment_docname: this.frm.docname,
comment: comment,
comment_by: user
}
},
btn: btn,
callback: function(r) {
if(!r.exc) {
me.frm.get_docinfo().comments =
me.get_comments().concat([r.message]);
me.frm.toolbar.show_infobar();
me.input.val("");
me.refresh();
}
}
});
},
delete_comment: function(name) {
var me = this;
return frappe.call({

View file

@ -1045,7 +1045,7 @@ frappe.ui.form.ControlTable = frappe.ui.form.Control.extend({
var prev_fieldtype = cur_frm.meta.fields[this.df.idx - 2].fieldtype;
}
if(["Column Break", "Section Break", "HTML"].indexOf(prev_fieldtype)===-1) {
if(frappe.model.layout_fields.indexOf(prev_fieldtype)===-1) {
$("<label>" + this.df.label + "<label>").appendTo(this.wrapper);
}

View file

@ -8,7 +8,7 @@ frappe.ui.form.Dashboard = Class.extend({
.css({"margin-bottom":"20px", "padding-bottom":"10px"})
.prependTo(this.frm.layout.wrapper);
this.body = $('<div></div>').appendTo(this.wrapper).css("margin-bottom", "20px");
},
reset: function() {
this.wrapper.toggle(false);
@ -17,7 +17,7 @@ frappe.ui.form.Dashboard = Class.extend({
},
set_headline: function(html) {
if(!this.headline)
this.headline =
this.headline =
$('<div class="form-headline col-md-12">').prependTo(this.body);
this.headline.html(html);
this.wrapper.toggle(true);
@ -26,7 +26,7 @@ frappe.ui.form.Dashboard = Class.extend({
this.set_headline(repl('<div class="alert %(alert_class)s">%(icon)s%(text)s</div>', {
"alert_class": alert_class || "alert-info",
"icon": icon ? '<i class="'+icon+'" /> ' : "",
"text": text
"text": text
}));
},
add_doctype_badge: function(doctype, fieldname) {
@ -46,10 +46,10 @@ frappe.ui.form.Dashboard = Class.extend({
<span class="badge pull-right">-</span>\
</div></div>', {label:label, icon: frappe.boot.doctype_icons[doctype]}))
.appendTo(this.body)
badge.find(".badge-link").click(onclick);
this.wrapper.toggle(true);
return badge.find(".alert-badge");
},
set_badge_count: function(data) {
@ -62,7 +62,7 @@ frappe.ui.form.Dashboard = Class.extend({
},
add_progress: function(title, percent) {
var progress_chart = this.make_progress_chart(title);
if(!$.isArray(percent)) {
var width = cint(percent) < 1 ? 1 : percent;
var progress_class = "";
@ -70,36 +70,36 @@ frappe.ui.form.Dashboard = Class.extend({
progress_class = "progress-bar-danger";
if(width > 99.9)
progress_class = "progress-bar-success";
percent = [{
title: title,
width: width,
progress_class: progress_class
}];
}
var progress = $('<div class="progress"></div>').appendTo(progress_chart);
$.each(percent, function(i, opts) {
$(repl('<div class="progress-bar %(progress_class)s" style="width: %(width)s%" \
title="%(title)s"></div>', opts)).appendTo(progress);
});
this.wrapper.toggle(true);
},
make_progress_chart: function(title) {
var progress_area = this.body.find(".progress-area");
if(!progress_area.length) {
progress_area = $('<div class="progress-area">').appendTo(this.body);
progress_area = $('<div class="progress-area" style="margin-top: 10px">').appendTo(this.body);
}
var progress_chart = $('<div class="progress-chart"><h5>'+title+'</h5></div>')
var progress_chart = $('<div class="progress-chart" title="'+title+'"></div>')
.appendTo(progress_area);
var n_charts = progress_area.find(".progress-chart").length,
cols = Math.floor(12 / n_charts);
progress_area.find(".progress-chart")
.removeClass().addClass("progress-chart col-md-" + cols);
return progress_chart;
}
});
});

View file

@ -26,7 +26,6 @@ frappe.ui.form.Footer = Class.extend({
<div class="after-save row">\
<div class="col-md-8">\
<div class="form-comments">\
<h5><i class="icon-comments"></i> '+__("Comments")+'</h5>\
</div>\
</div>\
<div class="col-md-4">\

View file

@ -18,7 +18,7 @@ frappe.ui.form.Grid = Class.extend({
this.wrapper = $('<div>\
<div class="form-grid">\
<div class="grid-heading-row" style="font-size: 15px; background-color: #f9f9f9;"></div>\
<div class="grid-heading-row" style="font-size: 15px;"></div>\
<div class="panel-body" style="padding-top: 7px;">\
<div class="rows"></div>\
<div class="small grid-footer">\
@ -349,7 +349,7 @@ frappe.ui.form.GridRow = Class.extend({
for(var ci in this.docfields) {
var df = this.docfields[ci];
if(!df.hidden && df.in_list_view && this.grid.frm.get_perm(df.permlevel, "read")
&& !in_list(["Section Break", "Column Break"], df.fieldtype)) {
&& !in_list(frappe.model.layout_fields, df.fieldtype)) {
var colsize = 2;
switch(df.fieldtype) {
case "Text":
@ -558,7 +558,7 @@ frappe.ui.form.GridRow = Class.extend({
var visible_columns = $.map(this.docfields, function(df) {
if(df.print_hide || df.hidden
|| in_list(blacklist, df.fieldname)
|| in_list(["Section Break", "Column Break"], df.fieldtype))
|| in_list(frappe.model.layout_fields, df.fieldtype))
return null;
else
return df;

View file

@ -2,14 +2,21 @@
// MIT License. See license.txt
frappe.provide("frappe.ui.form");
// - page
// - section
// - column
// - section
frappe.ui.form.Layout = Class.extend({
init: function(opts) {
this.views = {};
this.pages = [];
this.sections = [];
this.fields_list = [];
this.fields_dict = {};
this.labelled_section_count = 0;
this.ignore_types = ["Section Break", "Column Break"];
this.ignore_types = frappe.model.layout_fields;
$.extend(this, opts);
},
@ -65,6 +72,9 @@ frappe.ui.form.Layout = Class.extend({
}
$.each(this.fields, function(i, df) {
switch(df.fieldtype) {
case "Fold":
me.make_page(df);
break;
case "Section Break":
me.make_section(df);
break;
@ -91,6 +101,7 @@ frappe.ui.form.Layout = Class.extend({
.addClass("col-md-" + colspan);
},
make_field: function(df, colspan) {
!this.section && this.make_section();
!this.column && this.make_column();
var fieldobj = make_field(df, this.doctype, this.column.get(0), this.frm);
fieldobj.layout = this;
@ -100,12 +111,34 @@ frappe.ui.form.Layout = Class.extend({
fieldobj.perm = this.frm.perm;
}
},
make_page: function(df) {
var head = $('<div class="form-page-header">\
<button class="btn btn-default btn-primary btn-fold">'+__("View Details")
+'</button>\
</div>').appendTo(this.wrapper);
this.page = $('<div class="form-page hide"></div>').appendTo(this.wrapper);
head.find(".btn-fold").on("click", function() {
var page = $(this).parent().next();
if(page.hasClass("hide")) {
$(this).removeClass("btn-primary").html(__("Hide Details"));
page.removeClass("hide");
} else {
$(this).addClass("btn-primary").html(__("View Details"));
page.addClass("hide");
}
});
this.section = null;
},
make_section: function(df) {
if(this.section) {
//$("<hr>").appendTo(this.wrapper);
if(!this.page) {
this.page = $('<div class="form-page"></div>').appendTo(this.wrapper);
}
this.section = $('<div class="row">')
.appendTo(this.wrapper);
.appendTo(this.page);
this.sections.push(this.section);
var section = this.section[0];

View file

@ -1,11 +1,11 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
// MIT License. See license.txt
frappe.ui.form.States = Class.extend({
init: function(opts) {
$.extend(this, opts);
this.state_fieldname = frappe.workflow.get_state_fieldname(this.frm.doctype);
// no workflow?
if(!this.state_fieldname)
return;
@ -17,13 +17,13 @@ frappe.ui.form.States = Class.extend({
me.refresh();
})
},
make: function() {
this.parent = this.frm.appframe.parent
.find(".workflow-button-area")
.empty()
.removeClass("hide");
this.workflow_button = $('<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">\
<i class="icon-small"></i> <span class="state-text"></span>\
<span class="caret"></span></button>')
@ -44,13 +44,13 @@ frappe.ui.form.States = Class.extend({
title: "Workflow: "
+ frappe.workflow.workflows[me.frm.doctype].name
})
var next_html = $.map(frappe.workflow.get_transitions(me.frm.doctype, state),
function(d) {
var next_html = $.map(frappe.workflow.get_transitions(me.frm.doctype, state),
function(d) {
return d.action.bold() + __(" by Role ") + d.allowed;
}).join(", ") || __("None: End of Workflow").bold();
$(d.body).html("<p>"+__("Current status")+": " + state.bold() + "</p>"
+ "<p>"+__("Document is only editable by users of role")+": "
+ "<p>"+__("Document is only editable by users of role")+": "
+ frappe.workflow.get_document_state(me.frm.doctype,
state).allow_edit.bold() + "</p>"
+ "<p>"+__("Next actions")+": "+ next_html +"</p>"
@ -61,7 +61,7 @@ frappe.ui.form.States = Class.extend({
d.show();
});
},
refresh: function() {
// hide if its not yet saved
if(this.frm.doc.__islocal) {
@ -71,14 +71,14 @@ frappe.ui.form.States = Class.extend({
}
this.make();
// state text
var state = this.get_state();
if(state) {
// show current state on the button
this.workflow_button.find(".state-text").text(state);
var state_doc = frappe.get_doc("Workflow State", state);
if (state_doc) {
@ -102,7 +102,7 @@ frappe.ui.form.States = Class.extend({
}
}
},
show_actions: function(state) {
var $ul = this.dropdown;
$ul.empty();
@ -110,10 +110,10 @@ frappe.ui.form.States = Class.extend({
$.each(frappe.workflow.get_transitions(this.frm.doctype, state), function(i, d) {
if(in_list(user_roles, d.allowed)) {
d.icon = frappe.get_list("Workflow State", d.next_state).icon;
$(repl('<li><a href="#" data-action="%(action)s">\
<i class="icon icon-%(icon)s"></i> %(action)s</a></li>', d))
.appendTo($ul);
.appendTo($ul);
}
});
@ -130,14 +130,14 @@ frappe.ui.form.States = Class.extend({
this.frm.set_value(this.state_fieldname, default_state);
}
},
get_state: function() {
if(!this.frm.doc[this.state_fieldname]) {
this.set_default_state();
}
}
return this.frm.doc[this.state_fieldname];
},
bind_action: function() {
var me = this;
this.dropdown.on("click", "[data-action]", function() {
@ -147,7 +147,7 @@ frappe.ui.form.States = Class.extend({
// set new state
var next_state = frappe.workflow.get_next_state(me.frm.doctype,
me.frm.doc[me.state_fieldname], action);
me.frm.doc[me.state_fieldname], action);
me.frm.doc[me.state_fieldname] = next_state;
var new_state = frappe.workflow.get_document_state(me.frm.doctype, next_state);
var new_docstatus = cint(new_state.doc_status);
@ -164,26 +164,30 @@ frappe.ui.form.States = Class.extend({
me.frm.refresh();
}
// success - add a comment
var success = function() {
me.frm.comments.insert_comment("Workflow", next_state);
}
if(new_docstatus==1 && me.frm.doc.docstatus==0) {
me.frm.savesubmit(null, on_error);
me.frm.savesubmit(null, success, on_error);
} else if(new_docstatus==0 && me.frm.doc.docstatus==0) {
me.frm.save("Save", null, null, on_error);
me.frm.save("Save", success, null, on_error);
} else if(new_docstatus==1 && me.frm.doc.docstatus==1) {
me.frm.save("Update", null, null, on_error);
me.frm.save("Update", success, null, on_error);
} else if(new_docstatus==2 && me.frm.doc.docstatus==1) {
me.frm.savecancel(null, on_error);
me.frm.savecancel(null, success, on_error);
} else {
msgprint(__("Document Status transition from ") + me.frm.doc.docstatus + " "
+ __("to") +
msgprint(__("Document Status transition from ") + me.frm.doc.docstatus + " "
+ __("to") +
new_docstatus + " " + __("is not allowed."));
msgprint(__("Document Status transition from {0} to {1} is not allowed", [me.frm.doc.docstatus, new_docstatus]));
return false;
}
// hide dropdown
me.workflow_button.dropdown('toggle');
return false;
})
}
}
});

View file

@ -35,9 +35,11 @@ frappe.get_gravatar = function(email_id) {
}
frappe.ui.set_user_background = function(src, selector, style) {
if(!selector) selector = "body";
if(!selector) selector = "#page-desktop";
if(!style) style = "Fill Screen";
if(!src) src = "assets/frappe/images/ui/random-polygons.jpg";
frappe.dom.set_style(repl('%(selector)s { \
background: url("%(src)s") center center;\
background-attachment: fixed; \

View file

@ -5,7 +5,9 @@ frappe.provide('frappe.model');
$.extend(frappe.model, {
no_value_type: ['Section Break', 'Column Break', 'HTML', 'Table',
'Button', 'Image'],
'Button', 'Image', 'Fold'],
layout_fields: ['Section Break', 'Column Break', 'Fold'],
std_fields_list: ['name', 'owner', 'creation', 'modified', 'modified_by',
'_user_tags', '_comments', 'docstatus', 'parent', 'parenttype', 'parentfield', 'idx'],

View file

@ -304,7 +304,7 @@ frappe.ui.make_app_page = function(opts) {
]
*/
$wrapper = $(opts.parent)
$('<div class="appframe-titlebar">\
$('<div class="app-page container"><div class="appframe-titlebar">\
<div class="container">\
<div class="row">\
<div class="titlebar-item col-sm-8">\
@ -333,7 +333,7 @@ frappe.ui.make_app_page = function(opts) {
<div class="workflow-button-area btn-group pull-right hide"></div>\
<div class="clearfix"></div>\
</div>\
</div>\
</div></div>\
<div class="appframe-footer hide"></div>').appendTo($wrapper);
if(opts.single_column) {

View file

@ -15,7 +15,7 @@ frappe.views.Container = Class.extend({
this.pagemargin = 50;
},
add_page: function(label, onshow, onhide) {
var page = $('<div class="content"></div>')
var page = $('<div class="content page-container"></div>')
.attr('id', "page-" + label)
.attr("data-page-route", label)
.toggle(false)

View file

@ -614,7 +614,7 @@ _f.Frm.prototype._save = function(save_action, callback, btn, on_error) {
}
_f.Frm.prototype.savesubmit = function(btn, on_error) {
_f.Frm.prototype.savesubmit = function(btn, callback, on_error) {
var me = this;
this.validate_form_action("Submit");
frappe.confirm(__("Permanently Submit {0}?", [this.docname]), function() {
@ -628,13 +628,14 @@ _f.Frm.prototype.savesubmit = function(btn, on_error) {
me.save('Submit', function(r) {
if(!r.exc) {
callback && callback();
me.script_manager.trigger("on_submit");
}
}, btn, on_error);
});
};
_f.Frm.prototype.savecancel = function(btn, on_error) {
_f.Frm.prototype.savecancel = function(btn, callback, on_error) {
var me = this;
this.validate_form_action('Cancel');
frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), function() {
@ -649,6 +650,7 @@ _f.Frm.prototype.savecancel = function(btn, on_error) {
var after_cancel = function(r) {
if(!r.exc) {
me.refresh();
callback && callback();
me.script_manager.trigger("after_cancel");
} else {
on_error();

View file

@ -503,6 +503,7 @@ $.extend(_p, {
render_normal: function(field, data, i) {
switch(field.fieldtype) {
case: 'Fold': break;
case 'Section Break':
me.layout.addrow();

View file

@ -172,10 +172,6 @@ def make_layout(doc, meta):
layout = [filter(lambda s: any(filter(lambda c: any(c), s)), page) for page in layout]
return layout
def is_visible(df):
no_display = ("Section Break", "Column Break", "Button")
return (df.fieldtype not in no_display) and not df.get("__print_hide") and not df.print_hide
def has_value(df, doc):
value = doc.get(df.fieldname)
if value in (None, ""):

View file

@ -11,8 +11,6 @@ import frappe
# utility functions like cint, int, flt, etc.
from frappe.utils.data import *
no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'FlexTable',
'Button', 'Image', 'Graph']
default_fields = ['doctype', 'name', 'owner', 'creation', 'modified', 'modified_by',
'parent', 'parentfield', 'parenttype', 'idx', 'docstatus']

View file

@ -104,9 +104,9 @@ def add_attachments(dt, dn):
return attachments
def add_comments(dt, dn, limit=20):
cl = frappe.db.sql("""select name, comment, comment_by, creation from `tabComment`
cl = frappe.db.sql("""select name, comment, comment_by, creation, comment_type from `tabComment`
where comment_doctype=%s and comment_docname=%s
order by creation desc limit %s""" % ('%s','%s', limit), (dt, dn), as_dict=1)
order by creation asc limit %s""" % ('%s','%s', limit), (dt, dn), as_dict=1)
return cl