Merge branch 'website-wip' of github.com:webnotes/wnframework into website-wip

This commit is contained in:
Anand Doshi 2013-09-13 11:49:01 +05:30
commit 9e9e336224
21 changed files with 254 additions and 209 deletions

View file

@ -84,13 +84,10 @@ class DocType:
# update index
if not self.doc.custom:
from webnotes.modules import scrub
doctype = scrub(self.doc.name)
module = __import__(scrub(self.doc.module) + ".doctype." + doctype + "." + doctype,
fromlist=[""])
from webnotes.model.code import load_doctype_module
module = load_doctype_module( self.doc.name, self.doc.module)
if hasattr(module, "on_doctype_update"):
module.on_doctype_update()
webnotes.clear_cache(doctype=self.doc.name)
def check_link_replacement_error(self):
@ -315,4 +312,4 @@ def make_module_and_roles(doclist, perm_doctype="DocPerm"):
if e.args[0]==1146:
pass
else:
raise e
raise e

View file

@ -91,8 +91,8 @@ class DocType:
if self.in_insert:
webnotes.msgprint("New user created. - %s" % self.doc.name)
if cint(self.doc.send_invite_email):
webnotes.msgprint("Sent welcome mail.")
self.send_welcome_mail(self.doc.new_password)
webnotes.msgprint("Sent welcome mail.")
else:
self.password_reset_mail(self.doc.new_password)
webnotes.msgprint("Password updated.")
@ -129,12 +129,10 @@ To login to %(product)s, please go to:
Thank you,<br>
%(user_fullname)s
"""
import startup
self.send_login_mail("Your " +startup.product_name + " password has been reset", txt, password)
self.send_login_mail("Your " + webnotes.get_config().get("app_name") + " password has been reset", txt, password)
def send_welcome_mail(self, password):
"""send welcome mail to user with password and login url"""
import startup
txt = """
## %(company)s
@ -153,25 +151,28 @@ To login to your new %(product)s account, please go to:
Thank you,<br>
%(user_fullname)s
"""
self.send_login_mail("Welcome to " + startup.product_name, txt, password)
self.send_login_mail("Welcome to " + webnotes.get_config().get("app_name"), txt, password)
def send_login_mail(self, subject, txt, password):
"""send mail with login details"""
import os
import startup
from webnotes.utils.email_lib import sendmail_md
from webnotes.profile import get_user_fullname
from webnotes.utils import get_request_site_address
full_name = get_user_fullname(webnotes.session['user'])
if full_name == "Guest":
full_name = "Administrator"
args = {
'first_name': self.doc.first_name or self.doc.last_name or "user",
'user': self.doc.name,
'password': password,
'company': webnotes.conn.get_default('company') or startup.product_name,
'company': webnotes.conn.get_default('company') or webnotes.get_config().get("app_name"),
'login_url': get_request_site_address(),
'product': startup.product_name,
'user_fullname': get_user_fullname(webnotes.session['user'])
'product': webnotes.get_config().get("app_name"),
'user_fullname': full_name
}
sender = webnotes.session.user not in ("Administrator", "Guest") and webnotes.session.user or None

View file

@ -467,3 +467,9 @@ def delete_child_rows(rows, doctype):
"""delete child rows for all parents"""
for p in list(set([r[1] for r in rows])):
webnotes.conn.sql("""delete from `tab%s` where parent=%s""" % (doctype, '%s'), p)
def import_file_by_path(path):
from webnotes.utils.datautils import read_csv_content
print "Importing " + path
with open(path, "r") as infile:
upload(rows = read_csv_content(infile.read()))

View file

@ -3,9 +3,19 @@
"public/css/all-web.css": [
"lib/public/css/bootstrap.css",
"lib/public/css/font-awesome.css",
"lib/public/css/nprogress.css",
"lib/website/css/website.css"
]
},
{
"public/js/all-web.min.js": [
"lib/public/js/lib/bootstrap.min.js",
"lib/public/js/wn/misc/number_format.js",
"lib/public/js/lib/nprogress.js",
"lib/website/js/website.js"
]
},
{
"public/css/all-app.css": [
@ -23,14 +33,6 @@
"lib/public/css/nprogress.css",
]
},
{
"public/js/all-web.min.js": [
"lib/public/js/lib/bootstrap.min.js",
"lib/public/js/wn/misc/number_format.js",
"lib/website/js/website.js"
]
},
{
"public/js/all-app.min.js": [

View file

@ -141,7 +141,7 @@ wn.ui.form.Attachments = Class.extend({
this.add_to_attachments(fileid, filename);
this.refresh();
if(fieldname) {
this.frm.set_value(fieldname, "files/"+filename);
this.frm.set_value(fieldname, wn.utils.get_file_link(filename));
this.frm.cscript[fieldname] && this.frm.cscript[fieldname](this.frm.doc);
}
}

View file

@ -568,7 +568,7 @@ wn.ui.form.ControlSelect = wn.ui.form.ControlData.extend({
this.set_description("");
var options = [""];
for(var fname in fl) {
if(fname.substr(0,4)!="http")
if(fname.indexOf("/")===-1)
fname = "files/" + fname;
options.push(fname);
}

View file

@ -157,8 +157,9 @@ wn.editors.BootstrapWYSIWYG = Class.extend({
me.$textarea.val(html_beautify(me.$editor.cleanHtml()));
me.$parent.find(".for-rich-text").toggle(false);
me.$parent.find(".for-html").toggle(true);
me.$parent.find(".btn-html").addClass("btn-info").prop("disabled", true);
me.$parent.find(".btn-rich-text").removeClass("btn-info").prop("disabled", false);
console.log(me.$parent.find(".btn-html"));
me.$parent.find(".btn-html").attr("disabled", "disabled");
me.$parent.find(".btn-rich-text").attr("disabled", false);
me.current_editor = me.$textarea;
});
@ -167,8 +168,9 @@ wn.editors.BootstrapWYSIWYG = Class.extend({
me.$editor.html(me.$textarea.val());
me.$parent.find(".for-rich-text").toggle(true);
me.$parent.find(".for-html").toggle(false);
me.$parent.find(".btn-html").removeClass("btn-info").prop("disabled", false);
me.$parent.find(".btn-rich-text").addClass("btn-info").prop("disabled", true);
me.$parent.find(".btn-rich-text").attr("disabled", "disabled");
me.$parent.find(".btn-html").attr("disabled", false);
me.current_editor = me.$editor;
});

View file

@ -5,8 +5,13 @@ wn.provide('wn.utils');
wn.utils = {
get_file_link: function(filename) {
return wn.utils.is_url(filename) || (filename.indexOf("images/")!=-1) || (filename.indexOf("files/")!=-1)
? filename : 'files/' + filename;
if(wn.utils.is_url(filename)) {
return filename;
} else if(filename.indexOf("/")===-1) {
return "files/" + filename;
} else {
return filename;
}
},
is_html: function(txt) {
if(txt.indexOf("<br>")==-1 && txt.indexOf("<p")==-1

View file

@ -278,7 +278,7 @@ def has_permission(doctype, ptype="read", refdoc=None):
and ifnull(p.permlevel,0) = 0
and (p.role="All" or p.role in (select `role` from tabUserRole where `parent`=%s))
""" % ("%s", ptype, "%s"), (doctype, session.user), as_dict=1)
if refdoc:
match_failed = {}
for p in perms:

View file

@ -268,4 +268,7 @@ def update_password(user, password):
values (%s, password(%s))
on duplicate key update `password`=password(%s)""", (user,
password, password))
@webnotes.whitelist()
def get_logged_user():
return webnotes.session.user

View file

@ -87,7 +87,7 @@ class Installer:
if os.path.exists("app"):
sync_for("app", force=True, sync_everything=True)
if os.path.exists(os.path.join("app", "startup", "install_fixtures")):
self.import_fixtures()
@ -114,12 +114,15 @@ class Installer:
webnotes.conn.commit()
if f.endswith(".csv"):
from core.page.data_import_tool import data_import_tool
from webnotes.utils.datautils import read_csv_content
print "Importing " + f
with open(os.path.join(basepath, f), "r") as infile:
data_import_tool.upload(rows = read_csv_content(infile.read()))
webnotes.conn.commit()
from core.page.data_import_tool.data_import_tool import import_file_by_path
import_file_by_path(os.path.join(basepath, f))
webnotes.conn.commit()
if os.path.exists(os.path.join("app", "startup", "install_fixtures", "files")):
if not os.path.exists(os.path.join("public", "files")):
os.makedirs(os.path.join("public", "files"))
os.system("cp -r %s %s/" % (os.path.join("app", "startup", "install_fixtures", "files"),
os.path.join("public", "files")))
def import_core_docs(self):
install_docs = [

View file

@ -313,7 +313,13 @@ class Bean:
return webnotes.has_permission(self.doc.doctype, "read", self.doc)
def save(self, check_links=1):
if self.ignore_permissions or webnotes.has_permission(self.doc.doctype, "write", self.doc):
perm_to_check = "write"
if self.doc.fields.get("__islocal"):
perm_to_check = "create"
if not self.doc.owner:
self.doc.owner = webnotes.session.user
if self.ignore_permissions or webnotes.has_permission(self.doc.doctype, perm_to_check, self.doc):
self.to_docstatus = 0
self.prepare_for_save("save")
if not self.ignore_validate:
@ -324,7 +330,7 @@ class Bean:
self.save_children()
self.run_method('on_update')
else:
self.no_permission_to(_("Write"))
self.no_permission_to(_(perm_to_check.title()))
return self

View file

@ -10,6 +10,7 @@ _toc = ["webnotes.model.doc.Document"]
import webnotes
import webnotes.model.meta
import MySQLdb
from webnotes.utils import *
@ -130,7 +131,14 @@ class Document:
if is_single:
self._loadsingle()
else:
dataset = webnotes.conn.sql('select * from `%s%s` where name="%s"' % (self._prefix, self.doctype, self.name.replace('"', '\"')))
try:
dataset = webnotes.conn.sql('select * from `%s%s` where name="%s"' % (self._prefix, self.doctype, self.name.replace('"', '\"')))
except MySQLdb.ProgrammingError, e:
if e.args[0]==1146:
dataset = None
else:
raise e
if not dataset:
raise webnotes.DoesNotExistError, '[WNF] %s %s does not exist' % (self.doctype, self.name)
self._load_values(dataset[0], webnotes.conn.get_description())

View file

@ -34,35 +34,24 @@ def read_csv_content_from_attached_file(doc):
def read_csv_content(fcontent, ignore_encoding=False):
rows = []
decoded = False
for encoding in ["utf-8", "windows-1250", "windows-1252"]:
try:
fcontent = unicode(fcontent, encoding)
decoded = True
break
except UnicodeDecodeError, e:
continue
if not decoded:
webnotes.msgprint(wn._("Unknown file encoding. Tried utf-8, windows-1250, windows-1252."),
raise_exception=True)
try:
reader = csv.reader(fcontent.splitlines())
# decode everything
csvrows = [[val for val in row] for row in reader]
for row in csvrows:
newrow = []
for val in row:
added = False
for encoding in ["utf-8", "windows-1250", "windows-1252"]:
try:
newrow.append(unicode(val.strip(), 'utf-8'))
added = True
break
except UnicodeDecodeError, e:
continue
if not added:
if ignore_encoding:
newrow.append('')
else:
webnotes.msgprint("""Some character(s) in row #%s, column #%s are
not readable by utf-8. Ignoring them. If you are importing a non
english language, please make sure your file is saved in the 'utf-8'
encoding.""" % (csvrows.index(row)+1, row.index(val)+1))
raise Exception
rows.append(newrow)
rows = [[val.strip() for val in row] for row in reader]
return rows
except Exception, e:
webnotes.msgprint("Not a valid Comma Separated Value (CSV File)")

View file

@ -234,7 +234,10 @@ class SMTPServer:
from webnotes.utils import cint
# get defaults from control panel
es = webnotes.model.doc.Document('Email Settings','Email Settings')
try:
es = webnotes.model.doc.Document('Email Settings','Email Settings')
except webnotes.DoesNotExistError:
es = None
self._sess = None
if server:
@ -243,7 +246,7 @@ class SMTPServer:
self.use_ssl = cint(use_ssl)
self.login = login
self.password = password
elif es.outgoing_mail_server:
elif es and es.outgoing_mail_server:
self.server = es.outgoing_mail_server
self.port = es.mail_port
self.use_ssl = cint(es.use_ssl)

View file

@ -63,11 +63,16 @@ def get_uploaded_content():
def save_file(fname, content, dt, dn):
import filecmp
files_path = get_files_path()
from webnotes.model.code import load_doctype_module
files_path = get_path("public", "files")
module = load_doctype_module(dt, webnotes.conn.get_value("DocType", dt, "module"))
if hasattr(module, "attachments_folder"):
files_path = os.path.join(files_path, module.attachments_folder)
file_size = check_max_file_size(content)
temp_fname = write_file(content)
fname = scrub_file_name(fname)
temp_fname = write_file(content, files_path)
fname = scrub_file_name(fname)
fpath = os.path.join(files_path, fname)
fname_parts = fname.split(".", -1)
@ -97,7 +102,7 @@ def save_file(fname, content, dt, dn):
f = webnotes.bean({
"doctype": "File Data",
"file_name": fname,
"file_name": os.path.relpath(os.path.join(files_path, fname), get_path("public")),
"attached_to_doctype": dt,
"attached_to_name": dn,
"file_size": file_size
@ -143,11 +148,11 @@ def check_max_file_size(content):
return file_size
def write_file(content):
def write_file(content, files_path):
"""write file to disk with a random name (to compare)"""
# create account folder (if not exists)
webnotes.create_folder(get_files_path())
fname = os.path.join(get_files_path(), webnotes.generate_hash())
webnotes.create_folder(files_path)
fname = os.path.join(files_path, webnotes.generate_hash())
# write the file
with open(fname, 'w+') as f:
@ -168,28 +173,26 @@ def remove_file(fid):
"""Remove file and File Data entry"""
webnotes.delete_doc("File Data", fid)
def get_file_system_name(fname):
# get system name from File Data table
return webnotes.conn.sql("""select name, file_name from `tabFile Data`
where name=%s or file_name=%s""", (fname, fname))
def get_file(fname):
f = get_file_system_name(fname)
f = webnotes.conn.sql("""select file_name from `tabFile Data`
where name=%s or file_name=%s""", (fname, fname))
if f:
file_name = f[0][1]
file_name = f[0][0]
else:
file_name = fname
# read the file
import os
with open(os.path.join(get_files_path(), file_name), 'r') as f:
files_path = get_path("public", "files")
file_path = os.path.join(files_path, file_name)
if not os.path.exists(file_path):
# check in folders
for basepath, folders, files in os.walk(files_path):
if file_name in files:
file_path = os.path.join(basepath, file_name)
break
with open(file_path, 'r') as f:
content = f.read()
return [file_name, content]
files_path = None
def get_files_path():
global files_path
if not files_path:
files_path = get_path("public", "files")
return files_path

View file

@ -177,49 +177,18 @@ fieldset {
margin-bottom: 20px;
}
/* buttons */
.btn-default {
color: #ffffff;
background-color: #a7a9aa;
border-color: #a7a9aa;
.message-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1040;
background-color: #fff;
display: table;
}
.dropup .btn-default .caret,
.btn-default .caret {
border-bottom-color: #ffffff;
border-top-color: #ffffff;
}
.btn-default:hover,
.btn-default:focus,
.btn-default:active,
.btn-default.active,
.open .dropdown-toggle.btn-default {
background-color: #9a9c9d;
border-color: #8d9091;
color: #ffffff;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #a7a9aa;
border-color: #a7a9aa;
}
.label {
padding-top: 0.3em;
.message-overlay .content {
display: table-cell;
vertical-align: middle;
text-align: center;
}

View file

@ -2,77 +2,114 @@
// MIT License. See license.txt
if(!window.wn) wn = {};
wn.call = function(opts) {
if(opts.btn) {
$(opts.btn).prop("disabled", true);
}
if(opts.msg) {
$(opts.msg).toggle(false);
}
if(!opts.args) opts.args = {};
// get or post?
if(!opts.args._type) {
opts.args._type = opts.type || "GET";
}
// method
if(opts.method) {
opts.args.cmd = opts.method;
}
// stringify
$.each(opts.args, function(key, val) {
if(typeof val != "string") {
opts.args[key] = JSON.stringify(val);
wn = {
show_message: function(text, icon) {
if(!icon) icon="icon-refresh icon-spin";
treemapper.hide_message();
$('<div class="message-overlay"></div>')
.html('<div class="content"><i class="'+icon+' text-muted"></i><br>'
+text+'</div>').appendTo(document.body);
},
hide_message: function(text) {
$('.message-overlay').remove();
},
call: function(opts) {
wn.prepare_call(opts);
$.ajax({
type: "POST",
url: "server.py",
data: opts.args,
dataType: "json",
success: function(data) {
wn.process_response(opts, data);
},
error: function(response) {
NProgress.done();
console.error ? console.error(response) : console.log(response);
}
});
return false;
},
prepare_call: function(opts) {
if(opts.btn) {
$(opts.btn).prop("disabled", true);
}
});
$.ajax({
type: "POST",
url: "server.py",
data: opts.args,
dataType: "json",
success: function(data) {
if(opts.msg) {
$(opts.msg).toggle(false);
}
if(!opts.args) opts.args = {};
// get or post?
if(!opts.args._type) {
opts.args._type = opts.type || "GET";
}
// method
if(opts.method) {
opts.args.cmd = opts.method;
}
// stringify
$.each(opts.args, function(key, val) {
if(typeof val != "string") {
opts.args[key] = JSON.stringify(val);
}
});
NProgress.start();
},
process_response: function(opts, data) {
NProgress.done();
if(opts.btn) {
$(opts.btn).prop("disabled", false);
}
if(data.exc) {
if(opts.btn) {
$(opts.btn).prop("disabled", false);
$(opts.btn).addClass("btn-danger");
setTimeout(function() { $(opts.btn).removeClass("btn-danger"); }, 1000);
}
if(data.exc) {
if(opts.btn) {
$(opts.btn).addClass("btn-danger");
setTimeout(function() { $(opts.btn).removeClass("btn-danger"); }, 1000);
}
try {
var err = JSON.parse(data.exc);
if($.isArray(err)) {
err = err.join("\n");
}
console.error ? console.error(err) : console.log(err);
} catch(e) {
console.log(data.exc);
}
} else{
if(opts.btn) {
$(opts.btn).addClass("btn-success");
setTimeout(function() { $(opts.btn).removeClass("btn-success"); }, 1000);
try {
var err = JSON.parse(data.exc);
if($.isArray(err)) {
err = err.join("\n");
}
console.error ? console.error(err) : console.log(err);
} catch(e) {
console.log(data.exc);
}
if(opts.msg && data.message) {
$(opts.msg).html(data.message).toggle(true);
} else{
if(opts.btn) {
$(opts.btn).addClass("btn-success");
setTimeout(function() { $(opts.btn).removeClass("btn-success"); }, 1000);
}
if(opts.callback)
opts.callback(data);
},
error: function(response) {
console.error ? console.error(response) : console.log(response);
}
});
return false;
if(opts.msg && data.message) {
$(opts.msg).html(data.message).toggle(true);
}
if(opts.callback)
opts.callback(data);
},
show_message: function(text, icon) {
if(!icon) icon="icon-refresh icon-spin";
wn.hide_message();
$('<div class="message-overlay"></div>')
.html('<div class="content"><i class="'+icon+' text-muted"></i><br>'
+text+'</div>').appendTo(document.body);
},
hide_message: function(text) {
$('.message-overlay').remove();
},
get_sid: function() {
var sid = getCookie("sid");
return sid && sid!=="Guest";
}
}
// Utility functions
function valid_email(id) {

View file

@ -49,7 +49,7 @@ login.do_login = function(){
$('#login_btn').prop("disabled", true);
$("#login-spinner").toggle(true);
$('#login_message').toggle(false);
$.ajax({
type: "POST",
url: "server.py",
@ -69,7 +69,7 @@ login.do_login = function(){
window.location.href = "index";
}
} else {
login.set_message(data.message);
login.set_message(data.message || data._server_messages);
}
}
})
@ -77,9 +77,14 @@ login.do_login = function(){
return false;
}
login.set_heading = function(html) {
$(".panel-heading").html("<h4>" + html + "</h4>");
}
login.show_login = function() {
login.set_heading('<i class="icon-lock"></i> Login');
$("#login_wrapper h3").html("Login");
$("#login-label").html("Email Id");
$("#login_id").attr("placeholder", "Login Email Id");
$("#password-row").toggle(true);
$("#full-name-row, #login_message").toggle(false);
$("#login_btn").html("Login").removeClass("btn-success");
@ -98,8 +103,8 @@ login.show_login = function() {
}
login.show_sign_up = function() {
$("#login_wrapper h3").html("Sign Up");
$("#login-label").html("Email Id");
login.set_heading('<i class="icon-thumbs-up"></i> Sign Up');
$("#login_id").attr("placeholder", "Your Email Id");
$("#password-row, #login_message").toggle(false);
$("#full-name-row").toggle(true);
$("#login_btn").html("Sign Up").addClass("btn-success");
@ -108,8 +113,8 @@ login.show_sign_up = function() {
}
login.show_forgot_password = function() {
$("#login_wrapper h3").html("Forgot");
$("#login-label").html("Email Id");
login.set_heading('<i class="icon-question-sign"></i> Forgot');
$("#login_id").attr("placeholder", "Your Email Id");
$("#password-row, #login_message, #full-name-row").toggle(false);
$("#login_btn").html("Send Password").removeClass("btn-success");
$("#switch-view").html("<a onclick='return login.show_login()' href='#'>Login</a>");

View file

@ -25,7 +25,7 @@
</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">
class="form-control" placeholder="Your Full Name">
</div>
<div class="form-group" id="password-row">
<input id="pass" type="password"

14
wnf.py
View file

@ -537,14 +537,20 @@ def run():
import json
if os.path.isdir(options.import_doclist):
docs = [os.path.join(options.import_doclist, f) \
for f in os.listdir(options.import_doclist) if f.endswith(".json")]
for f in os.listdir(options.import_doclist)]
else:
docs = [options.import_doclist]
for f in docs:
with open(f, "r") as infile:
b = webnotes.bean(json.loads(infile.read())).insert_or_update()
print "Imported: " + b.doc.doctype + " / " + b.doc.name
if f.endswith(".json"):
with open(f, "r") as infile:
b = webnotes.bean(json.loads(infile.read())).insert_or_update()
print "Imported: " + b.doc.doctype + " / " + b.doc.name
webnotes.conn.commit()
if f.endswith(".csv"):
from core.page.data_import_tool.data_import_tool import import_file_by_path
import_file_by_path(f)
webnotes.conn.commit()
elif options.reset_perms:
for d in webnotes.conn.sql_list("""select name from `tabDocType`