added workflow help, fixed button primary action in form and refactored permissions

This commit is contained in:
Rushabh Mehta 2013-01-04 14:03:35 +05:30
parent a0212091f0
commit 9e6e2fe4c7
10 changed files with 147 additions and 127 deletions

View file

@ -25,7 +25,8 @@ wn.core.Workflow = wn.ui.form.Controller.extend({
fieldtype: ["not in", wn.model.no_value_type]
}),
function(d) { return d.fieldname; });
wn.meta.get_docfield("Workflow Document State", "update_field").options = fields;
wn.meta.get_docfield("Workflow Document State", "update_field").options
= [""].concat(fields);
}
});

View file

@ -94,6 +94,7 @@
"lib/public/js/wn/model/sync.js",
"lib/public/js/wn/model/create_new.js",
"lib/public/js/wn/model/perm.js",
"lib/public/js/wn/model/workflow.js",
"lib/public/js/wn/misc/user.js",
"lib/public/js/wn/misc/utils.js",

View file

@ -125,9 +125,6 @@ Field.prototype.set_description = function(txt) {
}
}
// Field Refresh
// --------------------------------------------------------------------------------------------
Field.prototype.get_status = function() {
// if used in filters
if(this.in_filter)
@ -143,39 +140,51 @@ Field.prototype.get_status = function() {
var ret;
// permission level
if(cur_frm.editable && p && p[WRITE] && !this.df.disabled)ret='Write';
else if(p && p[READ])ret='Read';
else ret='None';
// binary
if(this.df.fieldtype=='Binary')
ret = 'None'; // no display for binary
if(p && p[WRITE] && !this.df.disabled)
ret='Write';
else if(p && p[READ])
ret='Read';
else
ret='None';
// hidden
if(cint(this.df.hidden))
if(cint(this.df.hidden)) {
ret = 'None';
}
// for submit
if(ret=='Write' && cint(cur_frm.doc.docstatus) > 0) ret = 'Read';
if(ret=='Write' && cint(cur_frm.doc.docstatus) > 0) {
ret = 'Read';
}
// allow on submit
var a_o_s = cint(this.df.allow_on_submit);
if(a_o_s && (this.in_grid || (this.frm && this.frm.not_in_container))) {
if(a_o_s && (this.in_grid || (this.frm && this.frm.meta.istable))) {
// if grid is allow-on-submit, everything in it is too!
a_o_s = null;
if(this.in_grid) a_o_s = this.grid.field.df.allow_on_submit; // take from grid
if(this.frm && this.frm.not_in_container) { a_o_s = cur_grid.field.df.allow_on_submit;} // take from grid
}
if(cur_frm.editable && a_o_s && cint(cur_frm.doc.docstatus)>0 && !this.df.hidden) {
tmp_perm = wn.perm.get_perm(cur_frm.doctype, cur_frm.docname, 1);
if(tmp_perm[this.df.permlevel] && tmp_perm[this.df.permlevel][WRITE]) {
ret='Write';
if(this.in_grid)
a_o_s = this.grid.field.df.allow_on_submit;
if(this.frm.meta.istable) {
a_o_s = cur_grid.field.df.allow_on_submit;
}
}
// make a field read_only if read_only is checked (disregards write permission)
if(cint(this.df.read_only) && ret=="Write") ret = "Read";
if(ret=="Read" && a_o_s && cint(cur_frm.doc.docstatus)==1 &&
cur_frm.perm[this.df.permlevel][WRITE]) {
ret='Write';
}
// workflow state
if(ret=="Write" && cur_frm.read_only) {
ret = 'Read';
}
// make a field read_only if read_only
// is checked (disregards write permission)
if(ret=="Write" && cint(this.df.read_only)) {
ret = "Read";
}
return ret;
}

View file

@ -56,7 +56,7 @@ _f.Frm = function(doctype, parent, in_form) {
this.refresh_if_stale_for = 600;
var me = this;
this.is_editable = {};
this.last_view_is_edit = {};
this.opendocs = {};
this.sections = [];
this.grids = [];
@ -254,9 +254,9 @@ _f.Frm.prototype.rename_notify = function(dt, old, name) {
else
return;
// editable
this.is_editable[name] = this.is_editable[old];
delete this.is_editable[old];
// view_is_edit
this.last_view_is_edit[name] = this.last_view_is_edit[old];
delete this.last_view_is_edit[old];
// cleanup
if(this && this.opendocs[old]) {
@ -458,16 +458,8 @@ _f.Frm.prototype.check_doc_perm = function() {
var dt = this.parent_doctype?this.parent_doctype : this.doctype;
var dn = this.parent_docname?this.parent_docname : this.docname;
this.perm = wn.perm.get_perm(dt, dn);
this.orig_perm = wn.perm.get_perm(dt, dn, 1);
if(!this.perm[0][READ]) {
if(user=='Guest') {
// allow temp access? via encryted akey
if(_f.temp_access[dt] && _f.temp_access[dt][dn]) {
this.perm = [[1,0,0]]
return 1;
}
}
window.history.back();
return 0;
}
@ -492,6 +484,9 @@ _f.Frm.prototype.refresh = function(docname) {
// check permissions
if(!this.check_doc_perm()) return;
// read only (workflow)
this.read_only = wn.workflow.is_read_only(this.doctype, this.docname);
// set the doc
this.doc = wn.model.get_doc(this.doctype, this.docname);
@ -519,20 +514,20 @@ _f.Frm.prototype.refresh = function(docname) {
this.setnewdoc(this.docname);
}
// editable
// view_is_edit
if(this.doc.__islocal)
this.is_editable[this.docname] = 1; // new is editable
this.last_view_is_edit[this.docname] = 1; // new is view_is_edit
this.editable = this.is_editable[this.docname];
this.view_is_edit = this.last_view_is_edit[this.docname];
if(this.editable || (!this.editable && this.meta.istable)) {
// show form layout (with fields etc)
// ----------------------------------
if(this.view_is_edit || (!this.view_is_edit && this.meta.istable)) {
if(this.print_wrapper) {
$dh(this.print_wrapper);
$ds(this.page_layout.wrapper);
}
// header
if(!this.meta.istable) {
this.refresh_header();
@ -570,8 +565,6 @@ _f.Frm.prototype.refresh = function(docname) {
}
} else {
// show print layout
// ----------------------------------
this.refresh_header();
if(this.print_wrapper) {
this.refresh_print_layout();
@ -586,9 +579,10 @@ _f.Frm.prototype.refresh = function(docname) {
_f.Frm.prototype.refresh_footer = function() {
var f = this.page_layout.footer;
if(f.save_area) {
if(this.editable && (!this.meta.hide_toolbar) && (!this.meta.in_dialog || this.in_form)
&& this.doc.docstatus==0 && !this.meta.istable && this.perm[0][WRITE]
&& (this.fields && this.fields.length > 7) && !this.save_disabled) {
// if save button is there in the header
if(this.frm_head && this.frm_head.appframe.toolbar
&& this.frm_head.appframe.toolbar.find(".btn-save").length && !this.save_disabled
&& (this.fields && this.fields.length > 7)) {
f.show_save();
} else {
f.hide_save();
@ -729,15 +723,15 @@ _f.Frm.prototype.setnewdoc = function(docname) {
// Client Script
this.runclientscript('onload', this.doctype, this.docname);
this.is_editable[docname] = 1;
if(cint(this.meta.read_only_onload)) this.is_editable[docname] = 0;
this.last_view_is_edit[docname] = 1;
if(cint(this.meta.read_only_onload)) this.last_view_is_edit[docname] = 0;
this.opendocs[docname] = true;
}
_f.Frm.prototype.edit_doc = function() {
// set fields
this.is_editable[this.docname] = true;
this.last_view_is_edit[this.docname] = true;
this.refresh();
}
@ -996,7 +990,8 @@ _f.Frm.prototype.set_value_in_locals = function(dt, dn, fn, v) {
}
_f.Frm.prototype.set_unsaved = function() {
if(cur_frm.doc.__unsaved) return;
if(cur_frm.doc.__unsaved)
return;
cur_frm.doc.__unsaved = 1;
var frm_head;

View file

@ -207,29 +207,13 @@ _f.TableField.prototype.refresh = function() {
this.df['default']='';
this.grid.can_add_rows = false;
this.grid.can_edit = false
this.grid.can_edit = false;
if(st=='Write') {
if(cur_frm.editable && this.perm[this.df.permlevel] && this.perm[this.df.permlevel][WRITE]) {
this.grid.can_edit = true;
if(this.df['default'].toLowerCase()!='no toolbar')
this.grid.can_add_rows = true;
}
this.grid.can_edit = true;
if(this.df['default'].toLowerCase()!='no toolbar')
this.grid.can_add_rows = true;
// submitted or cancelled
if(cur_frm.editable && cur_frm.doc.docstatus > 0) {
if(this.df.allow_on_submit && cur_frm.doc.docstatus==1) {
this.grid.can_edit = true;
if(this.df['default'].toLowerCase()=='no toolbar') {
this.grid.can_add_rows = false;
} else {
this.grid.can_add_rows = true;
}
} else {
this.grid.can_add_rows = false;
this.grid.can_edit = false;
}
}
if(this.df['default'].toLowerCase()=='no add rows') {
this.grid.can_add_rows = false;
}

View file

@ -109,6 +109,8 @@ _f.FrmHeader = Class.extend({
cur_frm.save('Update', null, this);
}, '')
}
this.set_primary_button();
},
set_label: function(labinfo) {
this.$w.find('.label').remove();
@ -133,23 +135,19 @@ _f.FrmHeader = Class.extend({
// Edit
if(cur_frm.meta.read_only_onload && !cur_frm.doc.__islocal) {
if(!cur_frm.editable)
this.appframe.add_button('Edit', function() {
cur_frm.edit_doc();
},'icon-pencil');
else
this.appframe.add_button('Print View', function() {
cur_frm.is_editable[cur_frm.docname] = 0;
cur_frm.refresh(); }, 'icon-print' );
this.appframe.add_button('Print View', function() {
cur_frm.last_view_is_edit[cur_frm.docname] = 0;
cur_frm.refresh(); }, 'icon-print' );
}
var docstatus = cint(cur_frm.doc.docstatus);
// Save
if(docstatus==0 && p[WRITE]) {
if(docstatus==0 && p[WRITE] && !cur_frm.read_only) {
this.appframe.add_button('Save', function() {
cur_frm.save('Save', null, this);}, 'icon-save');
this.appframe.buttons['Save'].addClass('btn-info')
.html("<i class='icon-save'></i> Save (Ctrl+S)");
this.appframe.buttons['Save'].addClass("btn-save")
.html("<i class='icon-save'></i> <u>S</u>ave");
}
// Submit
@ -168,11 +166,29 @@ _f.FrmHeader = Class.extend({
this.appframe.add_button('Amend', function() {
cur_frm.amend_doc() }, 'icon-pencil');
}
this.set_primary_button();
},
show: function() {
},
hide: function() {
set_primary_button: function() {
if(!this.appframe.toolbar)
return;
var buttons = this.appframe.buttons;
// highlight save
this.appframe.toolbar.find("button").removeClass("btn-info");
if(buttons["Save"]) {
buttons["Save"].addClass("btn-info");
}
// highlight submit button
if(buttons["Submit"] && !cur_frm.doc.__unsaved) {
this.appframe.toolbar.find("button").removeClass("btn-info");
buttons["Submit"].addClass("btn-info");
// highlight update button
} else if(buttons["Update"] && cur_frm.doc.__unsaved) {
this.appframe.toolbar.find("button").removeClass("btn-info");
buttons["Update"].addClass("btn-info");
}
},
hide_close: function() {
this.$w.find('.close').toggle(false);

View file

@ -250,7 +250,7 @@ _f.Grid.prototype.set_cell_value = function(cell) {
$y($td(t,0,0),{paddingLeft:'4px'});
$td(t,0,0).innerHTML = cell.row.rowIndex + 1;
if(cur_frm.editable && this.can_edit) {
if(this.can_edit) {
$("<a title='Edit Row'><i class='icon-edit'></i></a>")
.click(function() {
_f.cur_grid = me;

View file

@ -23,7 +23,12 @@
wn.ui.form.States = Class.extend({
init: function(opts) {
$.extend(this, opts);
this.state_fieldname = wn.meta.get_state_fieldname(this.frm.doctype);
this.state_fieldname = wn.workflow.get_state_fieldname(this.frm.doctype);
// no workflow?
if(!this.state_fieldname)
return;
this.make();
this.bind_action();
@ -37,20 +42,47 @@ wn.ui.form.States = Class.extend({
this.$wrapper = $('<div class="states" style="margin-bottom: 11px; height: 26px;">\
<div class="btn-group">\
<button class="btn dropdown-toggle" data-toggle="dropdown">\
<i class="icon-small"></i> <span class="state-text"></span> <span class="caret"></span></button>\
<i class="icon-small"></i> <span class="state-text"></span> <span class="caret"></span>\
</button>\
<ul class="dropdown-menu">\
</ul>\
</div>\
<button class="btn btn-help">?</button>\
</div>').appendTo(this.frm.page_layout.body_header);
this.$wrapper.toggle(false);
this.setup_help();
},
setup_help: function() {
var me = this;
this.$wrapper.find(".btn-help").click(function() {
wn.workflow.setup(me.frm.doctype);
var state = me.get_state();
var d = new wn.ui.Dialog({
title: "Workflow: "
+ wn.workflow.workflows[me.frm.doctype].name
})
var next_html = $.map(wn.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: " + wn.workflow.get_document_state(me.frm.doctype,
state).allow_edit.bold() + "</p>"
+ "<p>Next actions: "+ next_html +"</p>"
+ (me.frm.doc.__islocal ? "<div class='alert'>Workflow will start after saving</div>" : "")
+ "<p class='help'>Note: Other permission rules may also apply</p>"
).css({padding: '15px'});
d.show();
});
},
refresh: function() {
// hide if its not yet saved
this.$wrapper.toggle(false);
if(this.frm.doc.__islocal) {
this.set_default_state();
return;
}
// state text
@ -68,26 +100,25 @@ wn.ui.form.States = Class.extend({
.addClass("icon-" + state_doc.icon);
// set the style
this.$wrapper.find(".btn").removeClass()
this.$wrapper.find(".btn:first").removeClass()
.addClass("btn dropdown-toggle")
.addClass("btn-" + state_doc.style.toLowerCase());
// show actions from that state
this.show_actions(state);
// disable if not allowed
if(!this.frm.doc.__islocal)
this.$wrapper.toggle(true);
this.$wrapper.toggle(true);
if(this.frm.doc.__islocal) {
this.$wrapper.find('.btn:first').attr('disabled', true);
}
}
},
show_actions: function(state) {
var $ul = this.$wrapper.find("ul");
$ul.empty();
$.each(wn.model.get("Workflow Transition", {
parent: this.frm.doctype,
state: state,
}), function(i, d) {
$.each(wn.workflow.get_transitions(this.frm.doctype, state), function(i, d) {
if(in_list(user_roles, d.allowed)) {
d.icon = wn.model.get("Workflow State", {name:d.next_state})[0].icon;
@ -96,21 +127,16 @@ wn.ui.form.States = Class.extend({
.appendTo($ul);
}
});
// disable the button if user cannot change state
this.$wrapper.find('.btn').attr('disabled', $ul.find("li").length ? false : true);
this.$wrapper.find('.btn:first')
.attr('disabled', $ul.find("li").length ? false : true);
},
set_default_state: function() {
var d = wn.model.get("Workflow Document State", {
parent: this.frm.doctype,
idx: 1
});
if(d && d.length) {
this.frm.set_value_in_locals(this.frm.doctype, this.frm.docname,
this.state_fieldname, d[0].state);
refresh_field(this.state_fieldname);
var default_state = wn.workflow.get_default_state(this.frm.doctype);
if(default_state) {
this.frm.set_value(this.state_fieldname, default_state);
}
},
@ -125,19 +151,12 @@ wn.ui.form.States = Class.extend({
var me = this;
$(this.$wrapper).on("click", "[data-action]", function() {
var action = $(this).attr("data-action");
var next_state = wn.model.get("Workflow Transition", {
parent: me.frm.doctype,
state: me.frm.doc[me.state_fieldname],
action: action,
})[0].next_state;
var next_state = wn.workflow.get_next_state(me.frm.doc,
me.frm.doc[me.state_fieldname], action);
me.frm.doc[me.state_fieldname] = next_state;
var new_state = wn.model.get("Workflow Document State", {
parent: me.frm.doctype,
state: next_state
})[0];
var new_state = wn.workflow.get_document_state(me.frm.doctype, next_state);
var new_docstatus = new_state.doc_status;
// update field and value

View file

@ -105,9 +105,4 @@ $.extend(wn.meta, {
}
},
get_state_fieldname: function(doctype) {
var wf = wn.model.get("Workflow", {document_type: doctype});
return wf.length ? wf[0].workflow_state_field : null;
},
});

View file

@ -40,7 +40,7 @@ $.extend(wn.perm, {
return perms[doctype][level][type];
},
get_perm: function(doctype, dn, ignore_submit) {
get_perm: function(doctype, dn) {
var perm = [[0,0],];
if(in_list(user_roles, 'Administrator'))
perm[0][READ] = 1;