Merge branch 'master' into perpetual

This commit is contained in:
Nabin Hait 2013-08-26 16:54:55 +05:30
commit 4da3ae4571
30 changed files with 221 additions and 169 deletions

View file

@ -13,18 +13,4 @@ class DocType:
def on_update(self):
# clear cache on save
webnotes.clear_cache()
def upload_many(self,form):
pass
def upload_callback(self,form):
pass
def execute_test(self, arg=''):
if webnotes.user.name=='Guest':
raise Exception, 'Guest cannot call execute test!'
out = ''
exec(arg and arg or self.doc.test_code)
webnotes.msgprint('that worked!')
return out
webnotes.clear_cache()

View file

@ -17,9 +17,11 @@ class DocType:
def on_update(self):
webnotes.clear_cache(doctype=self.doc.dt)
webnotes.cache().delete_value("_server_script:" + self.doc.dt)
def on_trash(self):
webnotes.clear_cache(doctype=self.doc.dt)
webnotes.cache().delete_value("_server_script:" + self.doc.dt)
def get_custom_server_script(doctype):
custom_script = webnotes.cache().get_value("_server_script:" + doctype)

View file

@ -228,8 +228,8 @@ def validate_permissions(permissions, for_remove=False):
doctype = permissions and permissions[0].parent
issingle = issubmittable = False
if doctype:
issingle = webnotes.conn.get_value("DocType", doctype, "issingle")
issubmittable = webnotes.conn.get_value("DocType", doctype, "is_submittable")
issingle = cint(webnotes.conn.get_value("DocType", doctype, "issingle"))
issubmittable = cint(webnotes.conn.get_value("DocType", doctype, "is_submittable"))
def get_txt(d):
return "For %s (level %s) in %s row %s:" % (d.role, d.permlevel, d.parent, d.idx)
@ -279,6 +279,9 @@ def validate_permissions(permissions, for_remove=False):
if d.submit and not issubmittable:
webnotes.msgprint(doctype + " is not Submittable, cannot assign submit rights.",
raise_exception=True)
elif d.amend and not issubmittable:
webnotes.msgprint(doctype + " is not Submittable, cannot assign amend rights.",
raise_exception=True)
for d in permissions:
if not d.permlevel:

View file

@ -80,7 +80,8 @@ def get_events(start, end, user=None, for_reminder=False):
def add_event(e, date):
new_event = e.copy()
new_event.starts_on = date + " " + e.starts_on.split(" ")[1]
new_event.ends_on = date + " " + e.ends_on.split(" ")[1]
if e.ends_on:
new_event.ends_on = date + " " + e.ends_on.split(" ")[1]
add_events.append(new_event)
for e in events:
@ -151,4 +152,4 @@ def get_events(start, end, user=None, for_reminder=False):
for w in weekdays:
del e[w]
return events
return events

View file

@ -27,7 +27,7 @@ wn.pages.messages.onload = function(wrapper) {
<h3 style="display: inline-block" id="message-title">Everyone</h3>\
</div><hr>\
<div id="post-message">\
<textarea style="width: 100%; height: 64px; margin-bottom: 7px;"></textarea>\
<textarea class="form-control" rows=3 style="margin-bottom: 15px;"></textarea>\
<div><button class="btn btn-default">Post</button></div><hr>\
</div>\
<div class="all-messages"></div>').appendTo($(wrapper).find('.layout-main-section'));
@ -198,8 +198,8 @@ erpnext.Messages = Class.extend({
p.status = p.has_session ? "Online" : "Offline";
$(repl('<p>\
<span class="avatar avatar-small" \
style="border: 3px solid %(status_color)s" \
title="%(status)s"><img src="%(image)s"></span>\
title="%(status)s"><img src="%(image)s"\
style="border: 3px solid %(status_color)s"></span>\
<a href="#!messages/%(name)s">%(fullname)s</a>\
</p>', p))
.appendTo($body);

View file

@ -93,14 +93,12 @@ wn.PermissionEngine = Class.extend({
me.doctype_select
= me.wrapper.appframe.add_select("doctypes",
[wn._("Select Document Type")+"..."].concat(r.message.doctypes))
.css("width", "200px")
.change(function() {
wn.set_route("permission-manager", $(this).val())
});
me.role_select
= me.wrapper.appframe.add_select("roles",
[wn._("Select Role")+"..."].concat(r.message.roles))
.css("width", "200px")
.change(function() {
me.refresh();
});

View file

@ -72,7 +72,6 @@ div#freeze {
.alert-badge {
padding-right: 15px;
margin-bottom: 0px;
}
.alert-badge a, .alert-badge a:hover {
@ -329,24 +328,27 @@ div#freeze {
.avatar {
display: inline-block;
vertical-align: middle;
border-radius: 50%;
overflow: hidden;
background-color: #ddd;
border: 1px solid #eee;
}
.avatar img {
background-color: #ddd;
border: 1px solid #eee;
border-radius: 50%;
width: 100%;
width: 100%;
height: auto;
}
.avatar-small {
margin-right: 5px;
width: 30px;
height: 30px;
}
.avatar-large {
margin-right: 10px;
width: 72px;
height: 30px;
}
/* slickgrid */
@ -375,8 +377,11 @@ div#freeze {
.missing-image {
background-color: #eee;
padding: 40px;
width: 112px;
display: table-cell;
vertical-align: middle;
text-align: center;
width: 140px;
height: 140px;
font-size: 32px;
color: #888;
}
@ -446,14 +451,17 @@ textarea[data-fieldtype="Small Text"] {
display: inline;
}
.hidden-sm-inline {
.hidden-xs-inline, .hidden-xs-inline-block {
display: none;
}
@media (min-width: 768px) {
.hidden-sm-inline {
.hidden-xs-inline {
display: inline;
}
.hidden-xs-inline-block {
display: inline-block;
}
}
.modal-backdrop {
@ -527,5 +535,4 @@ fieldset[disabled] .btn-default.active {
.form-group {
margin-bottom: 7px;
}
}

View file

@ -28,7 +28,7 @@ session_stopped = """<!DOCTYPE html>
<body style="background-color: #eee; font-family: Arial, Sans Serif;">
<div style="margin: 30px auto; width: 500px; background-color: #fff;
border: 1px solid #aaa; padding: 20px; text-align: center">
<b>Upgrading...</b>
<b>%(app_name)s Upgrading...</b>
<p>We will be back in a few moments.</p>
</div>
</body>
@ -41,12 +41,21 @@ def init():
def respond():
import webnotes
import webnotes.webutils
import MySQLdb
try:
return webnotes.webutils.render(webnotes.form_dict.get('page'))
except webnotes.SessionStopped:
print "Content-type: text/html"
print
print session_stopped
except MySQLdb.ProgrammingError, e:
if e.args[0]==1146:
print "Content-type: text/html"
print
print session_stopped % {"app_name": webnotes.get_config().app_name}
else:
raise e
if __name__=="__main__":
init()

View file

@ -101,7 +101,7 @@ _f.Frm.prototype.setup = function() {
});
this.frm_head = this.toolbar;
// create area for print fomrat
// create area for print format
this.setup_print_layout();
// 2 column layout
@ -278,7 +278,7 @@ _f.Frm.prototype.set_footnote = function(txt) {
_f.Frm.prototype.add_custom_button = function(label, fn, icon) {
this.appframe.add_button(label, fn, icon || "icon-arrow-right");
return this.appframe.add_button(label, fn, icon || "icon-arrow-right");
}
_f.Frm.prototype.clear_custom_buttons = function() {
this.toolbar.refresh()

View file

@ -63,7 +63,7 @@ $.extend(_p, {
<button class="btn btn-default btn-preview">Preview</button>\
</p>'},
]
})
});
dialog.$wrapper.find(".btn-print").click(function() {
var args = dialog.get_values();
@ -85,7 +85,7 @@ $.extend(_p, {
dialog.onshow = function() {
var $print = dialog.fields_dict.print_format.$input;
$print.add_options(cur_frm.print_formats);
$print.empty().add_options(cur_frm.print_formats);
if(cur_frm.$print_view_select && cur_frm.$print_view_select.val())
$print.val(cur_frm.$print_view_select.val());

62
public/js/lib/markdown.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -21,7 +21,7 @@ wn.ui.form.Comments = Class.extend({
.appendTo(this.row)
.find("button")
.click(function() {
me.add_comment();
me.add_comment(this);
});
this.list = $('<div class="comments" style="margin-top: 15px;"></div>')
.appendTo(this.parent);
@ -48,7 +48,7 @@ wn.ui.form.Comments = Class.extend({
c.comment_on = dateutil.comment_when(c.creation);
c.fullname = wn.user_info(c.comment_by).fullname;
$(repl('<div class="comment alert col-md-10" data-name="%(name)s">\
$(repl('<div class="comment alert alert-warning col-md-10" data-name="%(name)s">\
<div class="row">\
<div class="col-md-1">\
<span class="avatar avatar-small"><img src="%(image)s"></span>\
@ -68,7 +68,7 @@ wn.ui.form.Comments = Class.extend({
});
},
add_comment: function() {
add_comment: function(btn) {
var me = this,
txt = me.input.val();
@ -86,6 +86,7 @@ wn.ui.form.Comments = Class.extend({
args: {
doclist:[comment]
},
btn: btn,
callback: function(r) {
if(!r.exc) {
me.frm.get_docinfo().comments =

View file

@ -40,7 +40,7 @@ wn.ui.form.Control = Class.extend({
// returns "Read", "Write" or "None"
// as strings based on permissions
get_status: function(explain) {
if(!this.doctype)
if(!this.doctype)
return "Write";
return wn.perm.get_field_display_status(this.df,
locals[this.doctype][this.docname], this.perm, explain);

View file

@ -201,11 +201,14 @@ wn.ui.form.GridRow = Class.extend({
}
},
remove: function() {
var me = this;
me.wrapper.toggle(false);
wn.model.clear_doc(me.doc.doctype, me.doc.name);
me.frm.dirty();
me.grid.refresh();
if(this.grid.is_editable()) {
var me = this;
me.wrapper.toggle(false);
wn.model.clear_doc(me.doc.doctype, me.doc.name);
me.frm.script_manager.trigger(me.grid.df.fieldname + "_remove", me.doc.doctype, me.doc.name);
me.frm.dirty();
me.grid.refresh();
}
},
insert: function(show) {
var idx = this.doc.idx;

View file

@ -46,7 +46,7 @@ wn.tools.downloadify = function(data, roles, me) {
wn.markdown = function(txt) {
if(!wn.md2html) {
wn.require('lib/js/lib/showdown.js');
wn.require('lib/js/lib/markdown.js');
wn.md2html = new Showdown.converter();
}

View file

@ -23,7 +23,7 @@ wn.ui.AppFrame = Class.extend({
<div class="status-bar"></div>\
</div>\
</div>\
<div class="info-bar" style="display: none;"><ul class="hidden-sm-inline"></ul></div>\
<div class="info-bar" style="display: none;"><ul class="hidden-xs-inline"></ul></div>\
<div class="appframe-toolbar" style="display: none;">\
</div>\
<div>').prependTo(parent);
@ -212,7 +212,7 @@ wn.ui.AppFrame = Class.extend({
var append_or_prepend = is_title ? "prependTo" : "appendTo";
this.buttons[label] = $(repl('<button class="btn btn-default">\
%(icon)s <span class="hidden-sm-inline">%(label)s</span></button>', args))
%(icon)s <span class="hidden-xs-inline">%(label)s</span></button>', args))
[append_or_prepend](this.toolbar.find(".btn-group").css({"margin-right": "5px"}))
.attr("title", wn._(label))
.click(click);

View file

@ -159,16 +159,20 @@ wn.views.ListView = Class.extend({
// row #2
var row2 = $('<div class="row">\
<div class="col-xs-12">\
<div class="col-xs-7 col-md-offset-3">\
<div class="list-tag hidden-sm hidden-xs"></div></div>\
<div class="col-xs-2 timestamp" style="font-size: 90%; padding-right: 4px;\
color: #aaa; margin-top: -3px; text-align: right;">\
<div class="col-xs-3"></div>\
<div class="col-xs-7">\
<div class="list-tag hidden-xs"></div>\
<div class="list-last-modified visible-xs text-muted small"></div>\
</div>\
<div class="col-xs-2 timestamp small text-muted" style="padding-right: 4px;\
margin-top: -3px; text-align: right;">\
</div>\
</div>\
</div>').appendTo(row);
// modified
row2.find(".timestamp").html(comment_when(data.modified));
row2.find(".list-last-modified").html(wn._("Last updated by") + ": " + wn.user_info(data.modified_by).fullname);
// add tags
var tag_editor = new wn.ui.TagEditor({
@ -241,6 +245,7 @@ wn.views.ListView = Class.extend({
+ wn.user_info(data.modified_by).fullname))
.appendTo($(parent).css({"margin-top": "-5px"}))
.css({"max-width": "100%"})
.addClass("hidden-xs-inline-block")
}
else if(opts.content=='check') {
}

View file

@ -78,7 +78,8 @@ wn.views.moduleview.ModuleView = Class.extend({
var module_top = $(this.wrapper).find(".module-top");
var list_group = $('<div>')
.appendTo(module_top);
$('<hr>').insertAfter(module_top);
$('<hr>').css({"margin-top": "0px"})
.insertAfter(module_top);
} else {
var list_group = $('<ul class="list-group">\
<li class="list-group-item" style="background-color: #eee">\

View file

@ -227,15 +227,17 @@ wn.views.QueryReport = Class.extend({
},
make_data: function(result, columns) {
var me = this;
this.data = $.map(result, function(row, row_idx) {
this.data = [];
for(var row_idx=0, l=result.length; row_idx < l; row_idx++) {
var row = result[row_idx];
var newrow = {};
for(var i=1, j=me.columns.length; i<j; i++) {
newrow[me.columns[i].field] = row[i-1];
for(var i=1, j=this.columns.length; i<j; i++) {
newrow[this.columns[i].field] = row[i-1];
};
newrow._id = row_idx + 1;
newrow.id = newrow.name ? newrow.name : ("_" + newrow._id);
return newrow;
});
this.data.push(newrow);
}
},
make_dataview: function() {
// initialize the model

View file

@ -1,43 +0,0 @@
<style>
#login_wrapper {
width: 330px;
margin: 70px auto;
}
#login_wrapper,
#login_wrapper h3 {
color: #333;
}
#login_wrapper a {
color: #0088cc;
}
.layout-wrapper {
background-color: #fff;
padding: 10px;
box-shadow: 1px 1px 3px 3px #ccc;
font-size: 12px;
min-height: 100px;
border-radius: 5px;
}
#login_wrapper h3 {
text-align: center;
}
.login-banner {
margin-bottom: 20px;
}
.login-box td {
padding: 8px;
}
.login-box td input {
margin-bottom: 0px;
}
.login-footer {
text-align: center;
padding: 15px;
}
</style>

View file

@ -6,12 +6,9 @@ $(document).ready(function(wrapper) {
$('#login_btn').click(login.do_login);
$('#password').keypress(function(ev){
if(ev.which==13 && $('#password').val()) {
$('form').submit(function() {
login.do_login();
return false;
});
$('#pass').keypress(function(ev){
if(ev.which==13 && $('#pass').val()) {
$("#login_btn").click();
}
});
$(document).trigger('login_rendered');
@ -41,7 +38,7 @@ login.do_login = function(){
} else {
args.cmd = "login"
args.usr = ($("#login_id").val() || "").trim();
args.pwd = $("#password").val();
args.pwd = $("#pass").val();
if(!args.usr || !args.pwd) {
login.set_message("Both login and password required.");
@ -85,7 +82,7 @@ login.show_login = function() {
$("#password-row").toggle(true);
$("#full-name-row, #login_message").toggle(false);
$("#login_btn").html("Login").removeClass("btn-success");
$("#switch-view").html('<a style="margin-left: -35px;" \
$("#switch-view").html('<a \
onclick="return login.show_forgot_password()">Forgot Password?</a>');
if(!disable_signup) {

View file

@ -4,58 +4,56 @@
<script>
{% include "lib/templates/js/login.js" %}
</script>
{% include "lib/templates/css/login.css" %}
<style>
body, #container, .outer {
background-color: #F5EFE6 !important;
}
</style>
{% endblock %}
{% set title="Login" %}
{% block body %}
<div class="container" id='login_wrapper'>
<div class='layout-wrapper layout-main'>
<p id="login_message" class="alert" style="display: none;"></p>
<h3><i class="icon-lock" style="margin-top: 7px"></i> Login</h3>
<form autocomplete="on">
<table border="0" class="login-box">
<tbody>
<tr>
<td style="text-align: right; padding: 3px;"
id="login-label">Login Id</td>
<td><input id="login_id" type="email" style="width: 180px"
class="form-control" /></td>
</tr>
<tr id="password-row">
<td style="text-align: right; padding: 3px;" >Password</td>
<td><input id="password" type="password" style="width: 180px"
class="form-control" /></td>
</tr>
<tr id="full-name-row" style="display: none;">
<td style="text-align: right; padding: 3px;">Full Name</td>
<td><input id="full_name" type="text" style="width: 180px"
class="form-control" /></td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
<button type="submit" id="login_btn"
class="btn btn-primary">Login</button>
<img src="lib/images/ui/button-load.gif" id="login-spinner"
style="display: none;">
</td>
</tr>
</tbody>
</table>
</form>
<br>
<p style="text-align: center" id="switch-view"></p>
<div class="container">
<div class="row" style="margin-top: 100px;">
<div class="col-sm-offset-3 col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
<h4><i class="icon-lock"></i> Login</h4>
</div>
<div class="panel-body">
<p id="login_message" class="alert alert-warning"
style="display: none;"></p>
<div class="form-group">
<input id="login_id" type="text"
class="form-control" placeholder="Login Email Id">
</div>
<div class="form-group" id="full-name-row" style="display: none;">
<input id="full_name" type="text"
class="form-control" placeholder="Your Email Id">
</div>
<div class="form-group" id="password-row">
<input id="pass" type="password"
class="form-control" placeholder="Password">
</div>
<div class="form-group">
<button type="submit" id="login_btn"
class="btn btn-primary">Login</button>
<img src="lib/images/ui/button-load.gif" id="login-spinner" style="display: none;">
</div>
<p style="text-align: center" id="switch-view"></p>
</div>
</div>
</div>
</div>
<div class="web-footer login-footer container">
<a href="index.html">Home</a> |
<a href="https://erpnext.com">ERPNext</a><br><br>
{% if copyright %}
<div class="web-footer-copyright">&copy; {{ copyright }}
{% endif %}
<div class="row">
<div class="col-xs-12 web-footer login-footer text-center" style="margin-top: 25px">
<a href="index.html">Home</a> | <a href="https://erpnext.com">ERPNext</a><br><br>
{% if copyright %}
<div class="web-footer-copyright">&copy; {{ copyright }}</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}

View file

@ -479,6 +479,7 @@ def get_config():
def update_config(path):
with open(path, "r") as configfile:
this_config = json.loads(configfile.read())
_config.app_name = this_config.get("app_name")
_config.modules.update(this_config["modules"])
_config.web.pages.update(this_config["web"]["pages"])
_config.web.generators.update(this_config["web"]["generators"])

View file

@ -156,10 +156,12 @@ class LoginManager:
from startup import event_handlers
if hasattr(event_handlers, method):
getattr(event_handlers, method)(self)
return
except ImportError, e:
pass
cp = webnotes.bean("Control Panel", "Control Panel")
cp.run_method(method)
def validate_ip_address(self):
"""check if IP Address is valid"""
ip_list = webnotes.conn.get_value('Profile', self.user, 'restrict_ip', ignore=True)

View file

@ -19,7 +19,8 @@ class Database:
def __init__(self, host=None, user=None, password=None, ac_name=None, use_default = 0):
self.host = host or 'localhost'
self.user = user or conf.db_name
self.in_transaction = False
if ac_name:
self.user = self.get_db_login(ac_name) or conf.db_name
@ -430,14 +431,18 @@ class Database:
return webnotes.defaults.get_defaults(parent)
def begin(self):
return # not required
if not self.in_transaction:
self.sql("start transaction")
self.in_transaction = True
def commit(self):
self.sql("commit")
self.in_transaction = False
def rollback(self):
self.sql("ROLLBACK")
self.transaction_writes = 0
self.in_transaction = False
def field_exists(self, dt, fn):
return self.sql("select name from tabDocField where fieldname=%s and parent=%s", (dt, fn))

View file

@ -31,7 +31,7 @@ class Installer:
def import_from_db(self, target, source_path='', password = 'admin', verbose=0):
"""
a very simplified version, just for the time being..will eventually be deprecated once the framework stabilizes.
"""
"""
# delete user (if exists)
self.dbman.delete_user(target)
@ -73,6 +73,7 @@ class Installer:
# update admin password
self.create_auth_table()
self.update_admin_password(password)
return target
def install_app(self):

View file

@ -83,9 +83,10 @@ def get_server_obj(doc, doclist = [], basedoctype = ''):
# custom?
custom_script = get_custom_server_script(doc.doctype)
if custom_script:
global custom_class
exec custom_class + custom_script.replace('\t',' ') in locals()
return CustomDocType(doc, doclist)

View file

@ -8,6 +8,7 @@ import webnotes
user_time_zone = None
user_format = None
current_date = None
no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'FlexTable',
'Button', 'Image', 'Graph']
@ -193,7 +194,10 @@ def convert_utc_to_user_timezone(utc_timestamp):
def now():
"""return current datetime as yyyy-mm-dd hh:mm:ss"""
return now_datetime().strftime('%Y-%m-%d %H:%M:%S')
if current_date:
return getdate(current_date).strftime("%Y-%m-%d") + " " + now_datetime().strftime('%H:%M:%S')
else:
return now_datetime().strftime('%Y-%m-%d %H:%M:%S')
def nowdate():
"""return current date as yyyy-mm-dd"""

View file

@ -4,7 +4,7 @@
from __future__ import unicode_literals
"""
Scheduler will call the following events from the module
`startup.schedule_handler`
`startup.schedule_handler` and Control Panel (for server scripts)
execute_always
execute_daily
@ -74,6 +74,12 @@ def trigger(method):
except Exception:
return log(method)
try:
cp = webnotes.bean("Control Panel", "Control Panel")
cp.run_method(method)
except Exception:
return log(method)
def log(method):
"""log error in patch_log"""

8
wnf.py
View file

@ -398,12 +398,12 @@ def run():
inst.import_from_db(options.install_fresh, verbose = 1)
elif options.make_demo:
import utilities.make_demo
utilities.make_demo.make()
import utilities.demo.make_demo
utilities.demo.make_demo.make()
elif options.make_demo_fresh:
import utilities.make_demo
utilities.make_demo.make(True)
import utilities.demo.make_demo
utilities.demo.make_demo.make(True)
elif options.diff_ref_file is not None:
import webnotes.modules.diff