Merge branch 'navupdate' of github.com:webnotes/erpnext into navupdate

This commit is contained in:
Nabin Hait 2012-02-29 10:44:51 +05:30
commit 902b14592f
28 changed files with 921 additions and 304 deletions

View File

@ -1043,7 +1043,7 @@ div.dialog_head {
} }
div.dialog_body { div.dialog_body {
padding: 8px 4px 16px 4px; padding: 8px 8px 16px;
border-radius: 5px; border-radius: 5px;
-moz-border-radius: 5px; -moz-border-radius: 5px;
-webkit-border-radius: 5px; -webkit-border-radius: 5px;

View File

@ -362,7 +362,7 @@ div.dialog_head {
} }
div.dialog_body { div.dialog_body {
padding: 8px 4px 16px 4px; padding: 8px 8px 16px;
border-radius: 5px; border-radius: 5px;
-moz-border-radius: 5px; -moz-border-radius: 5px;
-webkit-border-radius: 5px; -webkit-border-radius: 5px;

View File

@ -44,34 +44,59 @@ erpnext.desktop.add_classes = function() {
erpnext.desktop.render = function() { erpnext.desktop.render = function() {
var icons = [ var icons = [
{ gradient: 'brown', sprite: 'feed', label: 'Activity', link: '#!Event Updates' }, { gradient: 'blue', sprite: 'account', label: 'Accounts', link: '#!accounts-home',
{ gradient: 'blue', sprite: 'account', label: 'Accounts', link: '#!accounts-home' }, is_module: 'Accounts'},
{ gradient: 'green', sprite: 'selling', label: 'Selling', link: '#!selling-home' }, { gradient: 'green', sprite: 'selling', label: 'Selling', link: '#!selling-home',
{ gradient: 'yellow', sprite: 'stock', label: 'Stock', link: '#!stock-home' }, is_module: 'Selling'},
{ gradient: 'red', sprite: 'buying', label: 'Buying', link: '#!buying-home' }, { gradient: 'yellow', sprite: 'stock', label: 'Stock', link: '#!stock-home',
{ gradient: 'purple', sprite: 'support', label: 'Support', link: '#!support-home' }, is_module: 'Stock'},
{ gradient: 'ocean', sprite: 'hr', label: 'Human<br />Resources', link: '#!hr-home' }, { gradient: 'red', sprite: 'buying', label: 'Buying', link: '#!buying-home',
{ gradient: 'violet', sprite: 'project', label: 'Projects', link: '#!projects-home' }, is_module: 'Buying'},
{ gradient: 'dark-red', sprite: 'production', label: 'Production', link: '#!production-home' }, { gradient: 'purple', sprite: 'support', label: 'Support', link: '#!support-home',
{ gradient: 'leaf-green', sprite: 'website', label: 'Website', link: '#!website-home' }, is_module: 'Support'},
{ gradient: 'grey', sprite: 'setting', label: 'Settings', link: '#!Setup' }, { gradient: 'ocean', sprite: 'hr', label: 'Human<br />Resources', link: '#!hr-home',
{ gradient: 'bright-green', sprite: 'dashboard', label: 'Dashboard', link: '#!dashboard' }, is_module: 'HR'},
//{ gradient: 'dark-blue', sprite: 'report', label: 'Report' }, { gradient: 'violet', sprite: 'project', label: 'Projects', link: '#!projects-home',
{ gradient: 'pink', sprite: 'messages', label: 'Messages', link: '#!messages' }, is_module: 'Projects'},
{ gradient: 'bright-yellow', sprite: 'todo', label: 'To Do', link: '#!todo' }, { gradient: 'dark-red', sprite: 'production', label: 'Production', link: '#!production-home',
{ gradient: 'peacock', sprite: 'calendar', label: 'Calendar', link: '#!calendar' }, is_module: 'Production'},
{ gradient: 'ultra-dark-green', sprite: 'kb', label: 'Knowledge<br />Base', link: '#!questions' }, { gradient: 'leaf-green', sprite: 'website', label: 'Website', link: '#!website-home',
is_module: 'Website'},
] ]
$.each(icons, function(i, v) { var add_icon = function(v) {
var icon_case = $('#icon-grid').append(repl('\ $('#icon-grid').append(repl('\
<div id="%(sprite)s" class="case-wrapper"><a href="%(link)s">\ <div id="%(sprite)s" class="case-wrapper"><a href="%(link)s">\
<div class="case-border case-%(gradient)s">\ <div class="case-border case-%(gradient)s">\
<div class="sprite-image sprite-%(sprite)s"></div>\ <div class="sprite-image sprite-%(sprite)s"></div>\
</div></a>\ </div></a>\
<div class="case-label">%(label)s</div>\ <div class="case-label">%(label)s</div>\
</div>', v)); </div>', v));
}); }
var get_module = function(m) {
for(var i in icons) {
if(icons[i].is_module==m) return icons[i]
}
}
// activity
add_icon({ gradient: 'brown', sprite: 'feed', label: 'Activity', link: '#!Event Updates'});
// modules
for(var i in wn.boot.modules_list)
add_icon(get_module(wn.boot.modules_list[i]));
// setup
if(user_roles.indexOf('System Manager')!=-1)
add_icon({ gradient: 'grey', sprite: 'setting', label: 'Setup', link: '#!Setup' });
// apps
add_icon({ gradient: 'bright-green', sprite: 'dashboard', label: 'Dashboard', link: '#!dashboard' });
add_icon({ gradient: 'bright-yellow', sprite: 'todo', label: 'To Do', link: '#!todo' });
add_icon({ gradient: 'pink', sprite: 'messages', label: 'Messages', link: '#!messages' });
add_icon({ gradient: 'peacock', sprite: 'calendar', label: 'Calendar', link: '#!calendar' });
add_icon({ gradient: 'ultra-dark-green', sprite: 'kb', label: 'Knowledge<br />Base', link: '#!questions' });
erpnext.desktop.show_pending_notifications(); erpnext.desktop.show_pending_notifications();

View File

@ -758,253 +758,3 @@ MemberCoversationComment = function(cell, det, conv) {
// ========================== Role object =====================================
pscript.all_roles = null;
RoleObj = function(profile_id){
this.roles_dict = {};
this.profile_id = profile_id;
this.setup_done = 0;
var d = new Dialog(500,500,'Assign Roles');
d.make_body([
['HTML','roles']
]);
this.dialog = d;
this.make_role_body(profile_id);
this.make_help_body();
this.body.innerHTML = '<span style="color:#888">Loading...</span> <img src="lib/images/ui/button-load.gif">'
var me=this;
d.onshow = function() {
if(!me.setup_done)
me.get_all_roles(me.profile_id);
}
}
// make role body
RoleObj.prototype.make_role_body = function(id){
var me = this;
var d = this.dialog;
this.role_div = $a(d.widgets['roles'],'div');
this.head = $a(this.role_div,'div','',{marginLeft:'4px', marginBottom:'4px',fontWeight:'bold'});
this.body = $a(this.role_div,'div');
this.footer = $a(this.role_div,'div');
this.update_btn = $btn(this.footer,'Update',function() { me.update_roles(me.profile_id); },{marginRight:'4px'},'',1);
}
// make help body
RoleObj.prototype.make_help_body = function(){
var me = this;
var d = this.dialog;
this.help_div = $a(d.widgets['roles'],'div');
var head = $a(this.help_div,'div'); this.help_div.head = head;
var body = $a(this.help_div,'div'); this.help_div.body = body;
var tail = $a(this.help_div,'div'); this.help_div.tail = tail;
var back_btn = $btn(tail,'Back', function() {
// back to assign roles
$(me.role_div).slideToggle('medium');
$(me.help_div).slideToggle('medium');
});
this.help_div.back_btn = back_btn;
$dh(this.help_div);
}
// get all roles
RoleObj.prototype.get_all_roles = function(id){
if(pscript.all_roles) {
this.make_roles(id);
return;
}
var me = this;
var callback = function(r,rt){
pscript.all_roles = r.message;
me.make_roles(id);
}
$c_obj('Company Control','get_all_roles','',callback);
}
// make roles
RoleObj.prototype.make_roles = function(id){
var me = this;
var list = pscript.all_roles;
me.setup_done = 1;
me.body.innerHTML = '';
var tbl = make_table( me.body, cint(list.length / 2) + 1,4,'100%',['5%','45%','5%','45%'],{padding:'4px'});
var in_right = 0; var ridx = 0;
for(i=0;i<list.length;i++){
var cidx = in_right * 2;
me.make_checkbox(tbl, ridx, cidx, list[i]);
me.make_label(tbl, ridx, cidx + 1, list[i]);
// change column
if(in_right) {in_right = 0; ridx++ } else in_right = 1;
}
me.get_user_roles(id);
}
// make checkbox
RoleObj.prototype.make_checkbox = function(tbl,ridx,cidx, role){
var me = this;
var a = $a_input($a($td(tbl, ridx, cidx),'div'),'checkbox');
a.role = role;
me.roles_dict[role] = a;
$y(a,{width:'20px'});
$y($td(tbl, ridx, cidx),{textAlign:'right'});
}
// make label
RoleObj.prototype.make_label = function(tbl, ridx, cidx, role){
var me = this;
var t = make_table($td(tbl, ridx, cidx),1,2,null,['16px', null],{marginRight:'5px'});
var ic = $a($td(t,0,0), 'img','',{cursor:'pointer', marginRight:'5px'});
ic.src= 'lib/images/icons/help.png';
ic.role = role;
ic.onclick = function(){
me.get_permissions(this.role);
}
$td(t,0,1).innerHTML= role;
}
// get user roles
RoleObj.prototype.get_user_roles = function(id){
var me = this;
me.head.innerHTML = 'Roles for ' + id;
$ds(me.role_div);
$dh(me.help_div);
var callback = function(r,rt){
me.set_user_roles(r.message);
}
$c_obj('Company Control','get_user_roles', id,callback);
}
// set user roles
RoleObj.prototype.set_user_roles = function(list){
var me = this;
for(d in me.roles_dict){
me.roles_dict[d].checked = 0;
}
for(l=0; l<list.length; l++){
me.roles_dict[list[l]].checked = 1;
}
}
// update roles
RoleObj.prototype.update_roles = function(id){
var me = this;
if(id == user && has_common(['System Manager'], user_roles) && !me.roles_dict['System Manager'].checked){
var callback = function(r,rt){
if(r.message){
if(r.message > 1){
var c = confirm("You have unchecked the System Manager role.\nYou will lose administrative rights and will not be able to set roles.\n\nDo you want to continue anyway?");
if(!c) return;
}
else{
var c = "There should be atleast one user with System Manager role.";
me.roles_dict['System Manager'].checked = 1;
}
}
me.set_roles(id);
}
$c_obj('Company Control','get_sm_count','',callback);
}
else{
me.set_roles(id);
}
}
// set roles
RoleObj.prototype.set_roles = function(id){
var me = this;
var role_list = [];
for(d in me.roles_dict){
if(me.roles_dict[d].checked){
role_list.push(d);
}
}
var callback = function(r,rt){
me.update_btn.done_working();
me.dialog.hide();
}
var arg = {'usr':id, 'role_list':role_list};
me.update_btn.set_working();
$c_obj('Company Control','update_roles',docstring(arg), callback);
}
// get permission
RoleObj.prototype.get_permissions = function(role){
var me = this;
var callback = function(r,rt){
$(me.help_div).slideToggle('medium');
$(me.role_div).slideToggle('medium');
me.set_permissions(r.message, role);
}
$c_obj('Company Control','get_permission',role,callback);
}
// set permission
RoleObj.prototype.set_permissions = function(perm, role){
var me = this;
me.help_div.body.innerHTML ='';
if(perm){
me.help_div.head.innerHTML = 'Permissions for ' + role + ':<br><br>';
perm_tbl = make_table(me.help_div.body,cint(perm.length)+2,7,'100%',['30%','10%','10%','10%','10%','10%','10%'],{padding:'4px'});
var head_lst = ['Document','Read','Write','Create','Submit','Cancel','Amend'];
for(var i=0; i<(head_lst.length-1);i++){
$td(perm_tbl,0,i).innerHTML= "<b>"+head_lst[i]+"</b>";
}
var accept_img1 = 'lib/images/icons/accept.gif';
var cancel_img1 = 'lib/images/icons/cancel.gif';
for(i=1; i<perm.length+1; i++){
$td(perm_tbl,i,0).innerHTML= get_doctype_label(perm[i-1][0]);
for(var j=1;j<(head_lst.length-1);j++){
if(perm[i-1][j]){
var accept_img = $a($td(perm_tbl,i,j), 'img'); accept_img.src= accept_img1;
}
else {
var cancel_img = $a($td(perm_tbl,i,j), 'img'); cancel_img.src= cancel_img1;
}
$y($td(perm_tbl,i,j),{textAlign:'center'});
}
}
}
else
me.help_div.head.innerHTML = 'No Permission set for ' + role + '.<br><br>';
}

View File

@ -0,0 +1,14 @@
<div class="layout-wrapper">
<a class="close" onclick="window.history.back();">&times;</a>
<h1>Modules Setup</h1>
<hr>
<div class="help" style="width: 300px; float: right">
Select checkbox to show / hide module. Drag around to move order.
</div>
<div id="modules-list">
</div>
<div>
<button class="btn btn-small btn-primary" id="modules-update"
onclick="wn.pages.modules_setup.update()">Update</button>
</div>
</div>

View File

@ -0,0 +1,50 @@
wn.require('lib/js/lib/jquery-ui-sortable.min.js');
$.extend(wn.pages.modules_setup, {
modules: ['Accounts', 'Selling', 'Buying', 'Stock', 'Production', 'Projects',
'Support', 'HR', 'Website'],
onload: function(wrapper) {
wn.pages.modules_setup.refresh(wn.boot.modules_list);
},
refresh: function(ml) {
$('#modules-list').empty();
// checked modules
for(i in ml) {
$('#modules-list').append(repl('<p style="cursor:move;">\
<input type="checkbox" data-module="%(m)s"> \
%(m)s</p>', {m:ml[i]}));
}
$('#modules-list [data-module]').attr('checked', true);
// unchecked modules
var all = wn.pages.modules_setup.modules;
for(i in all) {
if(!$('#modules-list [data-module="'+all[i]+'"]').length) {
$('#modules-list').append(repl('<p style="cursor:move;">\
<input type="checkbox" data-module="%(m)s"> \
%(m)s</p>', {m:all[i]}));
}
}
$('#modules-list').sortable();
},
update: function() {
var ml = [];
$('#modules-list [data-module]').each(function() {
if($(this).attr('checked'))
ml.push($(this).attr('data-module'));
});
wn.call({
method: 'setup.page.modules_setup.modules_setup.update',
args: {
ml: JSON.stringify(ml)
},
callback: function(r) {
},
btn: $('#modules-update').get(0)
});
}
});

View File

@ -0,0 +1,8 @@
import webnotes
@webnotes.whitelist()
def update(arg=None):
"""update modules"""
webnotes.conn.set_global('modules_list', webnotes.form_dict['ml'])
webnotes.msgprint('Updated')
webnotes.clear_cache()

View File

@ -0,0 +1,28 @@
# Page, modules_setup
[
# These values are common in all dictionaries
{
'creation': '2012-02-28 17:48:39',
'docstatus': 0,
'modified': '2012-02-28 17:48:39',
'modified_by': u'Administrator',
'owner': u'Administrator'
},
# These values are common for all Page
{
'doctype': 'Page',
'module': u'Setup',
'name': '__common__',
'page_name': u'modules_setup',
'standard': u'Yes',
'title': u'Modules Setup'
},
# Page, modules_setup
{
'doctype': 'Page',
'name': u'modules_setup'
}
]

View File

@ -20,7 +20,7 @@
<div class="setup-column"> <div class="setup-column">
<h3>Users and Permissions</h3> <h3>Users and Permissions</h3>
<p> <p>
<b><a href="#!My Company">Users</a></b><br> <b><a href="#!users">Users</a></b><br>
<span class="help">Add/remove users, set roles, passwords etc</span> <span class="help">Add/remove users, set roles, passwords etc</span>
</p> </p>
<p> <p>
@ -31,6 +31,10 @@
<b><a href="#!List/Authorization Rule">Amount based Authorization Rules</a></b><br> <b><a href="#!List/Authorization Rule">Amount based Authorization Rules</a></b><br>
<span class="help">Restrict submission rights based on amount</span> <span class="help">Restrict submission rights based on amount</span>
</p> </p>
<p>
<b><a href="#!modules_setup">Modules Setup</a></b><br>
<span class="help">Show, hide modules</span>
</p>
</div> </div>
<div class="setup-column"> <div class="setup-column">
<h3>Data</h3> <h3>Data</h3>

View File

@ -88,6 +88,8 @@ def boot_session(bootinfo):
import webnotes.model.doctype import webnotes.model.doctype
bootinfo['docs'] += webnotes.model.doctype.get('Event') bootinfo['docs'] += webnotes.model.doctype.get('Event')
bootinfo['modules_list'] = webnotes.conn.get_global('modules_list')
def get_letter_heads(): def get_letter_heads():
"""load letter heads with startup""" """load letter heads with startup"""
import webnotes import webnotes

View File

@ -73,20 +73,35 @@ erpnext.toolbar.add_modules = function() {
$('<li class="dropdown">\ $('<li class="dropdown">\
<a class="dropdown-toggle" data-toggle="dropdown" href="#"\ <a class="dropdown-toggle" data-toggle="dropdown" href="#"\
onclick="return false;">Modules<b class="caret"></b></a>\ onclick="return false;">Modules<b class="caret"></b></a>\
<ul class="dropdown-menu">\ <ul class="dropdown-menu modules">\
<li><a href="#!accounts-home" data-module="Accounts">Accounts</a></li>\
<li><a href="#!selling-home" data-module="Selling">Selling</a></li>\
<li><a href="#!stock-home" data-module="Stock">Stock</a></li>\
<li><a href="#!buying-home" data-module="Buying">Buying</a></li>\
<li><a href="#!support-home" data-module="Support">Support</a></li>\
<li><a href="#!hr-home" data-module="HR">Human Resources</a></li>\
<li><a href="#!projects-home" data-module="Projects">Projects</a></li>\
<li><a href="#!production-home" data-module="Production">Production</a></li>\
<li><a href="#!website-home" data-module="Website">Website</a></li>\
<li class="divider"></li>\
<li><a href="#!Setup" data-module="Setup">Setup</a></li>\
</ul>\ </ul>\
</li>').prependTo('.navbar .nav:first'); </li>').prependTo('.navbar .nav:first');
$('.navbar .nav:first')
// if no modules list then show all
if(wn.boot.modules_list)
wn.boot.modules_list = JSON.parse(wn.boot.modules_list);
else
wn.boot.modules_list = ['Accounts', 'Selling', 'Buying', 'Stock',
'Production', 'Projects', 'Support', 'HR', 'Website'];
// add to dropdown
for(var i in wn.boot.modules_list) {
var m = wn.boot.modules_list[i]
args = {
module: m,
module_page: m.toLowerCase(),
module_label: m=='HR' ? 'Human Resources' : m
}
$('.navbar .modules').append(repl('<li><a href="#!%(module_page)s-home" \
data-module="%(module)s">%(module_label)s</a></li>', args));
}
// setup for system manager
if(user_roles.indexOf("System Manager")!=-1) {
$('.navbar .modules').append('<li class="divider"></li>\
<li><a href="#!Setup" data-module="Setup">Setup</a></li>');
}
} }

View File

@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# Please edit this list and import only required elements # Please edit this list and import only required elements
import webnotes import webnotes
from webnotes.utils import add_days, add_months, add_years, cint, cstr, date_diff, default_fields, flt, fmt_money, formatdate, generate_hash, getTraceback, get_defaults, get_first_day, get_last_day, getdate, has_common, month_name, now, nowdate, replace_newlines, sendmail, set_default, str_esc_quote, user_format, validate_email_add from webnotes.utils import add_days, add_months, add_years, cint, cstr, date_diff, default_fields, flt, fmt_money, formatdate, generate_hash, getTraceback, get_defaults, get_first_day, get_last_day, getdate, has_common, month_name, now, nowdate, replace_newlines, sendmail, set_default, str_esc_quote, user_format, validate_email_add

View File

@ -1,3 +1,19 @@
// ERPNext - web based ERP (http://erpnext.com)
// Copyright (C) 2012 Web Notes Technologies Pvt Ltd
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
wn.provide('erpnext.messages'); wn.provide('erpnext.messages');
wn.pages.messages.onload = function(wrapper) { wn.pages.messages.onload = function(wrapper) {

View File

@ -1,3 +1,19 @@
# ERPNext - web based ERP (http://erpnext.com)
# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import webnotes import webnotes
@webnotes.whitelist() @webnotes.whitelist()

View File

@ -1,3 +1,19 @@
// ERPNext - web based ERP (http://erpnext.com)
// Copyright (C) 2012 Web Notes Technologies Pvt Ltd
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
wn.provide('erpnext.todo'); wn.provide('erpnext.todo');
erpnext.todo.refresh = function() { erpnext.todo.refresh = function() {

View File

@ -1,3 +1,19 @@
# ERPNext - web based ERP (http://erpnext.com)
# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import webnotes import webnotes
from webnotes.model.doc import Document from webnotes.model.doc import Document

View File

View File

@ -0,0 +1,35 @@
.user-card {
border-radius: 5px;
width: 200px;
margin: 11px;
padding: 11px;
background-color: #FFEDBD;
box-shadow: 3px 3px 5px #888;
float: left;
overflow: hidden;
}
.user-card.disabled {
background-color: #eee;
}
.user-card img {
height: 60px;
}
.user-role {
padding: 5px;
width: 45%;
float: left;
}
table.user-perm {
border-collapse: collapse;
}
table.user-perm td, table.user-perm th {
padding: 5px;
text-align: center;
border-bottom: 1px solid #aaa;
min-width: 30px;
}

View File

@ -0,0 +1,13 @@
<div class="layout-wrapper">
<a class="close" onclick="window.history.back();">&times;</a>
<h1>Users</h1>
<hr>
<div class="help" style="margin-bottom: 20px">Add, disable, delete users and change their roles, passwords and security settings</div>
<div style="margin: 11px">
<button class="btn btn-small add-user" onclick="wn.pages.users.add_user()"
><i class="icon-plus"></i> Add User</button>
</div>
<div class="users-area">
</div>
<div style="clear: both"></div>
</div>

View File

@ -0,0 +1,377 @@
// ERPNext - web based ERP (http://erpnext.com)
// Copyright (C) 2012 Web Notes Technologies Pvt Ltd
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
$.extend(wn.pages.users, {
onload: function(wrapper) {
wn.pages.users.profiles = {};
wn.pages.users.refresh();
wn.pages.users.setup();
wn.pages.users.role_editor = new erpnext.RoleEditor();
},
setup: function() {
// set roles
$('.users-area').on('click', '.btn.user-roles', function() {
var uid = $(this).parent().parent().attr('data-name');
wn.pages.users.role_editor.show(uid);
});
// settings
$('.users-area').on('click', '.btn.user-settings', function() {
var uid = $(this).parent().parent().attr('data-name');
wn.pages.users.show_settings(uid);
});
// delete
$('.users-area').on('click', 'a.close', function() {
$card = $(this).parent();
var uid = $card.attr('data-name');
$card.css('opacity', 0.6);
wn.call({
method: 'utilities.page.users.users.delete',
args: {'uid': uid},
callback: function(r,rt) {
if(!r.exc)
$card.fadeOut()
}
});
})
},
refresh: function() {
// make the list
wn.call({
method:'utilities.page.users.users.get',
callback: function(r, rt) {
$('.users-area').empty();
for(var i in r.message) {
var p = r.message[i];
wn.pages.users.profiles[p.name] = p;
wn.pages.users.render(p);
}
}
});
},
render: function(data) {
if(data.file_list) {
data.imgsrc = 'files/' + data.files_list.split('\n')[0].split(',')[1];
} else {
data.imgsrc = 'lib/images/ui/no_img_' + (data.gender=='Female' ? 'f' : 'm');
}
data.fullname = wn.boot.user_fullnames[data.name];
data.delete_html = '';
if(!data.enabled)
data.delete_html = '<a class="close" title="delete">&times;</a>';
$('.users-area').append(repl('<div class="user-card" data-name="%(name)s">\
%(delete_html)s\
<img src="%(imgsrc)s">\
<div class="user-info">\
<b class="user-fullname">%(fullname)s</b><br>\
%(name)s<br>\
<button class="btn btn-small user-roles"><i class="icon-user"></i> Roles</button>\
<button class="btn btn-small user-settings"><i class="icon-cog"></i> Settings</button>\
</div>\
</div>', data));
if(!data.enabled) {
$('.users-area .user-card:last')
.addClass('disabled')
.find('.user-fullname').html('Disabled');
}
},
show_settings: function(uid) {
var me = wn.pages.users;
if(!me.settings_dialog)
me.make_settings_dialog();
var p = me.profiles[uid];
me.uid = uid;
me.settings_dialog.set_values({
restrict_ip: p.restrict_ip || '',
login_before: p.login_before || '',
login_after: p.login_after || '',
enabled: p.enabled || 0,
new_password: ''
});
me.settings_dialog.show();
},
make_settings_dialog: function() {
var me = wn.pages.users;
me.settings_dialog = new wn.widgets.Dialog({
title: 'Set User Security',
width: 500,
fields: [
{
label:'Enabled',
description: 'Uncheck to disable',
fieldtype: 'Check', fieldname: 'enabled'
},
{
label:'IP Address',
description: 'Restrict user login by IP address, partial ips (111.111.111), \
multiple addresses (separated by commas) allowed',
fieldname:'restrict_ip', fieldtype:'Data'
},
{
label:'Login After',
description: 'User can only login after this hour (0-24)',
fieldtype: 'Int', fieldname: 'login_after'
},
{
label:'Login Before',
description: 'User can only login before this hour (0-24)',
fieldtype: 'Int', fieldname: 'login_before'
},
{
label:'New Password',
description: 'Update the current user password',
fieldtype: 'Data', fieldname: 'new_password'
},
{
label:'Update', fieldtype:'Button', fieldname:'update'
}
]
});
this.settings_dialog.fields_dict.update.input.onclick = function() {
var btn = this;
this.set_working();
var args = me.settings_dialog.get_values();
args.user = me.uid;
if (args.new_password) {
me.get_password(btn, args);
} else {
btn.set_working();
me.update_security(args);
}
};
},
update_security: function(args) {
var me = wn.pages.users;
$c_page('utilities', 'users', 'update_security', JSON.stringify(args), function(r,rt) {
if(r.exc) {
msgprint(r.exc);
return;
}
me.settings_dialog.hide();
$.extend(me.profiles[me.uid], me.settings_dialog.get_values());
me.refresh();
});
},
get_password: function(btn, args) {
var me = wn.pages.users;
var pass_d = new wn.widgets.Dialog({
title: 'Your Password',
width: 300,
fields: [
{
label: 'Please Enter <b style="color: black">Your Password</b>',
description: "Your password is required to update the user's password",
fieldtype: 'Password', fieldname: 'sys_admin_pwd', reqd: 1
},
{
label: 'Continue', fieldtype: 'Button', fieldname: 'continue'
}
]
});
pass_d.fields_dict.continue.input.onclick = function() {
btn.pwd_dialog.hide();
args.sys_admin_pwd = btn.pwd_dialog.get_values().sys_admin_pwd;
btn.set_working();
me.update_security(args);
btn.done_working();
}
pass_d.show();
btn.pwd_dialog = pass_d;
btn.done_working();
},
add_user: function() {
var me = wn.pages.users;
var d = new wn.widgets.Dialog({
title: 'Add User',
width: 400,
fields: [{
fieldtype: 'Data', fieldname: 'user', reqd: 1,
label: 'Email Id of the user to add'
}, {
fieldtype: 'Data', fieldname: 'first_name', reqd: 1, label: 'First Name'
}, {
fieldtype: 'Data', fieldname: 'last_name', label: 'Last Name'
}, {
fieldtype: 'Data', fieldname: 'password', reqd: 1, label: 'Password'
}, {
fieldtype: 'Button', label: 'Add', fieldname: 'add'
}]
});
d.make();
d.fields_dict.add.input.onclick = function() {
v = d.get_values();
if(v) {
d.fields_dict.add.input.set_working();
$c_page('utilities', 'users', 'add_user', v, function(r,rt) {
if(r.exc) { msgprint(r.exc); return; }
else {
d.hide();
me.refresh();
}
})
}
}
d.show();
}
});
erpnext.RoleEditor = Class.extend({
init: function() {
this.dialog = new wn.widgets.Dialog({
title: 'Set Roles'
});
var me = this;
$(this.dialog.body).html('<div class="help">Loading...</div>')
wn.call({
method:'utilities.page.users.users.get_roles',
callback: function(r) {
me.roles = r.message;
me.show_roles();
}
});
},
show_roles: function() {
var me = this;
$(this.dialog.body).empty();
for(var i in this.roles) {
$(this.dialog.body).append(repl('<div class="user-role" \
data-user-role="%(role)s">\
<input type="checkbox"> \
<a href="#"><i class="icon-question-sign"></i></a> %(role)s\
</div>', {role: this.roles[i]}));
}
$(this.dialog.body).append('<div style="clear: both">\
<button class="btn btn-small btn-primary">Save</button></div>');
$(this.dialog.body).find('button.btn-primary').click(function() {
me.save();
});
$(this.dialog.body).find('.user-role a').click(function() {
me.show_permissions($(this).parent().attr('data-user-role'))
return false;
})
},
show: function(uid) {
var me = this;
this.uid = uid;
this.dialog.show();
// set user roles
wn.call({
method:'utilities.page.users.users.get_user_roles',
args: {uid:uid},
callback: function(r, rt) {
$(me.dialog.body).find('input[type="checkbox"]').attr('checked', false);
for(var i in r.message) {
$(me.dialog.body)
.find('[data-user-role="'+r.message[i]
+'"] input[type="checkbox"]').attr('checked',true);
}
}
})
},
save: function() {
var set_roles = [];
var unset_roles = [];
$(this.dialog.body).find('[data-user-role]').each(function() {
var $check = $(this).find('input[type="checkbox"]');
if($check.attr('checked')) {
set_roles.push($(this).attr('data-user-role'));
} else {
unset_roles.push($(this).attr('data-user-role'));
}
})
wn.call({
method:'utilities.page.users.users.update_roles',
args: {
set_roles: JSON.stringify(set_roles),
unset_roles: JSON.stringify(unset_roles),
uid: this.uid
},
btn: $(this.dialog.body).find('.btn-primary').get(0),
callback: function() {
}
})
},
show_permissions: function(role) {
// show permissions for a role
var me = this;
if(!this.perm_dialog)
this.make_perm_dialog()
$(this.perm_dialog.body).empty();
wn.call({
method:'utilities.page.users.users.get_perm_info',
args: {role: role},
callback: function(r) {
var $body = $(me.perm_dialog.body);
$body.append('<table class="user-perm"><tbody><tr>\
<th style="text-align: left">Document Type</th>\
<th>Level</th>\
<th>Read</th>\
<th>Write</th>\
<th>Submit</th>\
<th>Cancel</th>\
<th>Amend</th></tr></tbody></table>');
for(var i in r.message) {
var perm = r.message[i];
// if permission -> icon
for(key in perm) {
if(key!='parent' && key!='permlevel') {
if(perm[key]) {
perm[key] = '<i class="icon-ok"></i>';
} else {
perm[key] = '';
}
}
}
$body.find('tbody').append(repl('<tr>\
<td style="text-align: left">%(parent)s</td>\
<td>%(permlevel)s</td>\
<td>%(read)s</td>\
<td>%(write)s</td>\
<td>%(submit)s</td>\
<td>%(cancel)s</td>\
<td>%(amend)s</td>\
</tr>', perm))
}
me.perm_dialog.show();
}
});
},
make_perm_dialog: function() {
this.perm_dialog = new wn.widgets.Dialog({
title:'Role Permissions',
width: 500
});
}
})

View File

@ -0,0 +1,192 @@
# ERPNext - web based ERP (http://erpnext.com)
# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import webnotes
import json
from webnotes.model.doc import Document
from webnotes.utils import cint
@webnotes.whitelist()
def get(arg=None):
"""return all users"""
return webnotes.conn.sql("""select name, file_list, enabled, gender,
restrict_ip, login_before, login_after from tabProfile
where docstatus<2 and name not in ('Administrator', 'Guest') order by
ifnull(enabled,0) desc, name""", as_dict=1)
@webnotes.whitelist()
def get_roles(arg=None):
"""return all roles"""
return [r[0] for r in webnotes.conn.sql("""select name from tabRole
where name not in ('Administrator', 'Guest', 'All') order by name""")]
@webnotes.whitelist()
def get_user_roles(arg=None):
"""get roles for a user"""
return [r[0] for r in webnotes.conn.sql("""select role from tabUserRole
where parent=%s""", webnotes.form_dict['uid'])]
@webnotes.whitelist()
def get_perm_info(arg=None):
"""get permission info"""
return webnotes.conn.sql("""select parent, permlevel, `read`, `write`, submit,
cancel, amend from tabDocPerm where role=%s
and docstatus<2 order by parent, permlevel""",
webnotes.form_dict['role'], as_dict=1)
@webnotes.whitelist()
def update_roles(arg=None):
"""update set and unset roles"""
# remove roles
unset = json.loads(webnotes.form_dict['unset_roles'])
webnotes.conn.sql("""delete from tabUserRole where parent='%s'
and role in ('%s')""" % (webnotes.form_dict['uid'], "','".join(unset)))
# check for 1 system manager
if not webnotes.conn.sql("""select parent from tabUserRole where role='System Manager'
and docstatus<2"""):
webnotes.msgprint("Sorry there must be atleast one 'System Manager'")
raise webnotes.ValidationError
# add roles
roles = get_user_roles()
toset = json.loads(webnotes.form_dict['set_roles'])
for role in toset:
if not role in roles:
d = Document('UserRole')
d.role = role
d.parent = webnotes.form_dict['uid']
d.save()
webnotes.msgprint('Roles Updated')
@webnotes.whitelist()
def update_security(args=''):
args = json.loads(args)
webnotes.conn.set_value('Profile', args['user'], 'restrict_ip', args.get('restrict_ip'))
webnotes.conn.set_value('Profile', args['user'], 'login_after', args.get('login_after'))
webnotes.conn.set_value('Profile', args['user'], 'login_before', args.get('login_before'))
webnotes.conn.set_value('Profile', args['user'], 'enabled', int(args.get('enabled',0)) or 0)
if 'new_password' in args:
if cint(webnotes.conn.get_value('Control Panel',None,'sync_with_gateway')):
import server_tools.gateway_utils
res = server_tools.gateway_utils.change_password('', args['new_password'],
args['user'], args['sys_admin_pwd'])
if 'Traceback' not in res['message']:
webnotes.msgprint(res['message'])
webnotes.conn.sql("update tabProfile set password=password(%s) where name=%s",
(args['new_password'], args['user']))
else:
webnotes.msgprint('Settings Updated')
#
# user addition
#
@webnotes.whitelist()
def add_user(args):
args = json.loads(args)
# erpnext-saas
if cint(webnotes.conn.get_value('Control Panel', None, 'sync_with_gateway')):
from server_tools.gateway_utils import add_user_gateway
add_user_gateway(args['user'])
add_profile(args)
@webnotes.whitelist()
def add_profile(args):
from webnotes.utils import validate_email_add, now
email = args['user']
sql = webnotes.conn.sql
if not email:
email = webnotes.form_dict.get('user')
if not validate_email_add(email):
raise Exception
return 'Invalid Email Id'
if sql("select name from tabProfile where name = %s", email):
# exists, enable it
sql("update tabProfile set enabled = 1, docstatus=0 where name = %s", email)
webnotes.msgprint('Profile exists, enabled it with new password')
else:
# does not exist, create it!
pr = Document('Profile')
pr.name = email
pr.email = email
pr.first_name = args.get('first_name')
pr.last_name = args.get('last_name')
pr.enabled = 1
pr.user_type = 'System User'
pr.save(1)
if args.get('password'):
sql("""
UPDATE tabProfile
SET password = PASSWORD(%s), modified = %s
WHERE name = %s""", (args.get('password'), now, email))
send_welcome_mail(email, args)
@webnotes.whitelist()
def send_welcome_mail(email, args):
"""send welcome mail to user with password and login url"""
pr = Document('Profile', email)
from webnotes.utils.email_lib import sendmail_md
args.update({
'company': webnotes.conn.get_default('company'),
'password': args.get('password'),
'account_url': webnotes.conn.get_default('account_url')
})
if not args.get('last_name'): args['last_name'] = ''
sendmail_md(pr.email, subject="Welcome to ERPNext", msg=welcome_txt % args, from_defs=1)
#
# delete user
#
@webnotes.whitelist()
def delete(arg=None):
"""delete user"""
webnotes.conn.sql("update tabProfile set enabled=0, docstatus=2 where name=%s",
webnotes.form_dict['uid'])
# erpnext-saas
if int(webnotes.conn.get_value('Control Panel', None, 'sync_with_gateway')):
from server_tools.gateway_utils import remove_user_gateway
remove_user_gateway(webnotes.form_dict['uid'])
webnotes.login_manager.logout(user=webnotes.form_dict['uid'])
welcome_txt = """
## %(company)s
Dear %(first_name)s %(last_name)s
Welcome!
A new account has been created for you, here are your details:
login-id: %(user)s
password: %(password)s
To login to your new ERPNext account, please go to:
%(account_url)s
"""

View File

@ -0,0 +1,28 @@
# Page, users
[
# These values are common in all dictionaries
{
'creation': '2012-02-28 10:29:39',
'docstatus': 0,
'modified': '2012-02-28 10:29:39',
'modified_by': u'Administrator',
'owner': u'Administrator'
},
# These values are common for all Page
{
'doctype': 'Page',
'module': u'Utilities',
'name': '__common__',
'page_name': u'users',
'standard': u'Yes',
'title': u'Users'
},
# Page, users
{
'doctype': 'Page',
'name': u'users'
}
]

View File

@ -38,10 +38,25 @@ def init():
# init request # init request
try: try:
webnotes.http_request = webnotes.auth.HTTPRequest() webnotes.http_request = webnotes.auth.HTTPRequest()
return True
except webnotes.AuthenticationError, e: except webnotes.AuthenticationError, e:
pass pass
except webnotes.UnknownDomainError, e: except webnotes.UnknownDomainError, e:
print "Location: " + (webnotes.defs.redirect_404) print "Location: " + (webnotes.defs.redirect_404)
except webnotes.SessionStopped, e:
if 'cmd' in webnotes.form_dict:
webnotes.handler.print_json()
else:
print "Content-Type: text/html"
print
print """<html>
<body style="background-color: #EEE;">
<h3 style="width: 900px; background-color: #FFF; border: 2px solid #AAA; padding: 20px; font-family: Arial; margin: 20px auto">
Updating.
We will be back in a few moments...
</h3>
</body>
</html>"""
def respond(): def respond():
import webnotes import webnotes
@ -55,5 +70,5 @@ def respond():
print webnotes.cms.index.get() print webnotes.cms.index.get()
if __name__=="__main__": if __name__=="__main__":
init() if init():
respond() respond()

View File

@ -2240,20 +2240,16 @@ $.each(new_comments,function(i,v){var msg='New Message: '+(v[1].length<=100?v[1]
erpnext.toolbar.add_modules=function(){$('<li class="dropdown">\ erpnext.toolbar.add_modules=function(){$('<li class="dropdown">\
<a class="dropdown-toggle" data-toggle="dropdown" href="#"\ <a class="dropdown-toggle" data-toggle="dropdown" href="#"\
onclick="return false;">Modules<b class="caret"></b></a>\ onclick="return false;">Modules<b class="caret"></b></a>\
<ul class="dropdown-menu">\ <ul class="dropdown-menu modules">\
<li><a href="#!accounts-home" data-module="Accounts">Accounts</a></li>\
<li><a href="#!selling-home" data-module="Selling">Selling</a></li>\
<li><a href="#!stock-home" data-module="Stock">Stock</a></li>\
<li><a href="#!buying-home" data-module="Buying">Buying</a></li>\
<li><a href="#!support-home" data-module="Support">Support</a></li>\
<li><a href="#!hr-home" data-module="HR">Human Resources</a></li>\
<li><a href="#!projects-home" data-module="Projects">Projects</a></li>\
<li><a href="#!production-home" data-module="Production">Production</a></li>\
<li><a href="#!website-home" data-module="Website">Website</a></li>\
<li class="divider"></li>\
<li><a href="#!Setup" data-module="Setup">Setup</a></li>\
</ul>\ </ul>\
</li>').prependTo('.navbar .nav:first');$('.navbar .nav:first')} </li>').prependTo('.navbar .nav:first');if(wn.boot.modules_list)
wn.boot.modules_list=JSON.parse(wn.boot.modules_list);else
wn.boot.modules_list=['Accounts','Selling','Buying','Stock','Production','Projects','Support','HR','Website'];for(var i in wn.boot.modules_list){var m=wn.boot.modules_list[i]
args={module:m,module_page:m.toLowerCase(),module_label:m=='HR'?'Human Resources':m}
$('.navbar .modules').append(repl('<li><a href="#!%(module_page)s-home" \
data-module="%(module)s">%(module_label)s</a></li>',args));}
if(user_roles.indexOf("System Manager")!=-1){$('.navbar .modules').append('<li class="divider"></li>\
<li><a href="#!Setup" data-module="Setup">Setup</a></li>');}}
/* /*
* erpnext/startup/feature_setup.js * erpnext/startup/feature_setup.js
*/ */

View File

@ -1 +1 @@
747 752