Merge branch 'master' of github.com:webnotes/erpnext

This commit is contained in:
Anand Doshi 2012-12-17 14:13:59 +05:30
commit 834adfab60
19 changed files with 278 additions and 307 deletions

View File

@ -59,7 +59,7 @@
<h4>Icons</h4> <h4>Icons</h4>
<ul> <ul>
<li>The Noun Project</li> <li>The Noun Project</li>
<li>Glyphicons</li> <li>Font Awesome: http://fortawesome.github.com/Font-Awesome</li>
</ul> </ul>
<h4>Web Frameworks</h4> <h4>Web Frameworks</h4>

View File

@ -1,4 +1,7 @@
erpnext.updates = [ erpnext.updates = [
["14th December 2012", [
"Website Module: Major Refactor - removed framework code from website."
]],
["12th December 2012", [ ["12th December 2012", [
"Attachments: Attachments can be set as URLs or File Uploads. This will help if people want to share documents from Google Docs, Dropbox and other such services (esp for the Product listings on websites).", "Attachments: Attachments can be set as URLs or File Uploads. This will help if people want to share documents from Google Docs, Dropbox and other such services (esp for the Product listings on websites).",
"Global Defaults: Session Expiry can now be set in Global Defaults.", "Global Defaults: Session Expiry can now be set in Global Defaults.",

View File

@ -7,10 +7,11 @@
"app/public/js/startup.css" "app/public/js/startup.css"
], ],
"public/js/all-web.js": [ "public/js/all-web.js": [
"app/public/js/startup.js", "app/public/js/website_utils.js"
"app/public/js/conf.js"
], ],
"public/js/all-app.js": [ "public/js/all-app.js": [
"app/public/js/startup.js",
"app/public/js/conf.js",
"app/public/js/modules.js", "app/public/js/modules.js",
"app/public/js/toolbar.js", "app/public/js/toolbar.js",
"app/public/js/feature_setup.js", "app/public/js/feature_setup.js",

View File

@ -136,23 +136,6 @@ erpnext.startup.set_periodic_updates = function() {
wn.updates.id = setInterval(erpnext.update_messages, 60000); wn.updates.id = setInterval(erpnext.update_messages, 60000);
} }
// subject, sender, description
erpnext.send_message = function(opts) {
if(opts.btn) {
$(opts.btn).start_working();
}
wn.call({
method: 'website.send_message',
args: opts,
callback: function(r) {
if(opts.btn) {
$(opts.btn).done_working();
}
if(opts.callback)opts.callback(r)
}
});
}
erpnext.hide_naming_series = function() { erpnext.hide_naming_series = function() {
if(cur_frm.fields_dict.naming_series) { if(cur_frm.fields_dict.naming_series) {
hide_field('naming_series'); hide_field('naming_series');

View File

@ -0,0 +1,53 @@
var erpnext = {};
// subject, sender, description
erpnext.send_message = function(opts) {
if(opts.btn) {
$(opts.btn).attr("disabled", "disabled");
}
$.ajax({
method: "POST",
url: "server.py",
data: {
cmd: "website.send_message",
subject: opts.subject,
sender: opts.sender,
message: typeof opts.message == "string"
? opts.message
: JSON.stringify(opts.message)
},
dataType: "json",
success: function(data) {
if(opts.btn) {
$(opts.btn).attr("disabled", false);
}
if(opts.callback)
opts.callback(data);
}
});
}
function valid_email(id) {
if(id.toLowerCase().search("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")==-1)
return 0; else return 1; }
function get_url_arg(name) {
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( window.location.href );
if(results == null)
return "";
else
return decodeURIComponent(results[1]);
}
function repl(s, dict) {
if(s==null)return '';
for(key in dict) {
s = s.split("%("+key+")s").join(dict[key]);
}
return s;
}

View File

@ -0,0 +1,22 @@
# 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/>.
from __future__ import unicode_literals
import webnotes
class DocType:
def __init__(self, d, dl):
self.doc, self.doclist = d, dl

View File

@ -6,6 +6,8 @@ install_docs = [
import webnotes import webnotes
max_tickets_per_hour = 200
@webnotes.whitelist(allow_guest=True) @webnotes.whitelist(allow_guest=True)
def send_message(): def send_message():
from webnotes.model.doc import Document from webnotes.model.doc import Document
@ -17,15 +19,21 @@ def send_message():
d.raised_by = webnotes.form_dict.get('sender') d.raised_by = webnotes.form_dict.get('sender')
if not d.description: if not d.description:
webnotes.msgprint('Please write something', raise_exception=True) webnotes.response["message"] = 'Please write something'
return
if not d.raised_by: if not d.raised_by:
webnotes.msgprint('Please give us your email id so that we can write back to you', raise_exception=True) webnotes.response["message"] = 'Email Id Required'
return
# make lead or contact # guest method, cap max writes per hour
if webnotes.conn.sql("""select count(*) from `tabSupport Ticket`
where TIMEDIFF(NOW(), modified) < '01:00:00'""")[0][0] > max_tickets_per_hour:
webnotes.response["message"] = "Sorry: we believe we have received an unreasonably high number of requests of this kind. Please try later"
return
d.save() d.save()
webnotes.msgprint('Thank you!') webnotes.response["message"] = 'Thank You'
def get_site_address(): def get_site_address():
from webnotes.utils import get_request_site_address from webnotes.utils import get_request_site_address

View File

@ -9,8 +9,7 @@ import website.utils
def get_blog_list(args=None): def get_blog_list(args=None):
""" """
args = { args = {
'limit_start': 0, 'start': 0,
'limit_page_length': 10,
} }
""" """
import webnotes import webnotes
@ -24,10 +23,8 @@ def get_blog_list(args=None):
comment_doctype='Blog' and comment_docname=`tabBlog`.name) as comments comment_doctype='Blog' and comment_docname=`tabBlog`.name) as comments
from `tabBlog` from `tabBlog`
where ifnull(published,0)=1 where ifnull(published,0)=1
order by creation desc, name asc""" order by creation desc, name asc
limit %s, 5""" % args.start
from webnotes.widgets.query_builder import add_limit_to_query
query, args = add_limit_to_query(query, args)
result = webnotes.conn.sql(query, args, as_dict=1) result = webnotes.conn.sql(query, args, as_dict=1)
@ -41,38 +38,6 @@ def get_blog_list(args=None):
if not res['content']: if not res['content']:
res['content'] = website.utils.get_html(res['page_name']) res['content'] = website.utils.get_html(res['page_name'])
res['content'] = split_blog_content(res['content']) res['content'] = split_blog_content(res['content'])
res['content'] = res['content'][:1000]
return result
@webnotes.whitelist(allow_guest=True)
def get_recent_blog_list(args=None):
"""
args = {
'limit_start': 0,
'limit_page_length': 5,
'name': '',
}
"""
import webnotes
if not args: args = webnotes.form_dict
query = """\
select name, page_name, title, left(content, 100) as content
from tabBlog
where ifnull(published,0)=1 and
name!=%(name)s order by creation desc"""
from webnotes.widgets.query_builder import add_limit_to_query
query, args = add_limit_to_query(query, args)
result = webnotes.conn.sql(query, args, as_dict=1)
# strip html tags from content
import webnotes.utils
for res in result:
res['content'] = webnotes.utils.strip_html(res['content'])
return result return result

View File

@ -6,4 +6,8 @@
.comment-content { .comment-content {
margin-left: 20px; margin-left: 20px;
} }
input {
width: 240px;
}
</style> </style>

View File

@ -5,6 +5,7 @@
} }
.layout-wrapper { .layout-wrapper {
background-color: #fff;
padding: 10px; padding: 10px;
box-shadow: 1px 1px 3px 3px #ccc; box-shadow: 1px 1px 3px 3px #ccc;
font-size: 12px; font-size: 12px;

View File

@ -2,7 +2,6 @@
{% block javascript %} {% block javascript %}
{% include "js/blog_page.js" %} {% include "js/blog_page.js" %}
{% include "js/blog_subscribe.js" %}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
@ -28,15 +27,32 @@
<div class="blog-comments"> <div class="blog-comments">
{% if not comment_list %} {% if not comment_list %}
<div class="no-result help hide"> <div class="alert no-comment">
<p>Be the first one to comment</p> <p>Be the first one to comment</p>
<br />
</div> </div>
{% endif %} {% endif %}
{% include 'html/comment.html' %} {% include 'html/comment.html' %}
</div> </div>
<button class="btn add-comment">Add Comment</button> <div><button class="btn add-comment">Add Comment</button></div>
<div style="display: none; margin-top: 10px;"
id="comment-form" class="well">
<div class="alert" style="display:none;"></div>
<form>
<p>
<input name="comment_by_fullname" placeholder="Your Name" />
</p>
<p>
<input name="comment_by" placeholder="Your Email Id" />
</p>
<p>
<textarea name="comment" placeholder="Comment" />
</textarea>
</p>
<p>
<button class="btn btn-info" id="submit-comment">Submit</button>
</form>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,28 +5,12 @@
{% block header %} {% block header %}
{{ super() }} {{ super() }}
<script> <script>
window.page_name = "{{ name }}"; {% block javascript %}
{% endblock %}
$(document).bind('app_ready', function() {
var _page = new wn.views.Page(window.page_name);
// page script
{% block javascript %}
{% endblock %}
// trigger onload
_page.trigger('onload');
// activate page
wn.container.change_to(window.page_name);
});
</script> </script>
{% block css %} {% block css %}
{% if insert_style %} {% if insert_style %}
<style>{{ css }}</style> <style>{{ css }}</style>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% endblock %} {% endblock %}

View File

@ -0,0 +1 @@
{% extends "html/page.html" %}

View File

@ -15,29 +15,54 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// js inside blog page // js inside blog page
wn.pages['{{ name }}'].onload = function(wrapper) {
erpnext.blog_list = new wn.ui.Listing({ $(document).ready(function() {
parent: $(wrapper).find('#blog-list').get(0), // make list of blogs
method: 'website.helpers.blog.get_blog_list', blog.get_list();
hide_refresh: true,
no_toolbar: true, $("#next-page").click(function() {
render_row: function(parent, data) { blog.get_list();
if(!data.comments) { })
data.comment_text = 'No comments yet.' });
} else if (data.comments===1) {
data.comment_text = '1 comment.' var blog = {
start: 0,
get_list: function() {
$.ajax({
method: "GET",
url: "server.py",
data: {
cmd: "website.helpers.blog.get_blog_list",
start: blog.start
},
dataType: "json",
success: function(data) {
blog.render(data.message);
}
});
},
render: function(data) {
var $wrap = $("#blog-list");
$.each(data, function(i, b) {
// comments
if(!b.comments) {
b.comment_text = 'No comments yet.'
} else if (b.comments===1) {
b.comment_text = '1 comment.'
} else { } else {
data.comment_text = data.comments + ' comments.' b.comment_text = b.comments + ' comments.'
} }
if(data.content && data.content.length==1000) { $(repl('<h2><a href="%(page_name)s.html">%(title)s</a></h2>\
data.content += repl('... <a href="%(page_name)s.html">(read on)</a>', data);
}
parent.innerHTML = repl('<h2><a href="%(page_name)s.html">%(title)s</a></h2>\
<div class="help">%(comment_text)s</div>\ <div class="help">%(comment_text)s</div>\
%(content)s<br /><br />', data); %(content)s<br />\
}, <p><a href="%(page_name)s">Read with comments...</a></p>\
page_length: 10 <hr /><br />', b)).appendTo($wrap);
}); });
erpnext.blog_list.run(); blog.start += data.length;
if(!data.length) {
$("#next-page").toggle(false)
.parent().append("<div class='alert'>Nothing more to show.</div>");
}
}
} }

View File

@ -16,166 +16,61 @@
// js inside blog page // js inside blog page
wn.provide('erpnext.blog'); $(document).ready(function() {
wn.pages['{{ name }}'].onload = function(wrapper) { var n_comments = $(".comment-row").length;
erpnext.blog.wrapper = wrapper;
// sidebar if(n_comments) {
//erpnext.blog.render_recent_list(wrapper); $(".no_comment").toggle(false);
// unhide no-result if no comments found
erpnext.blog.toggle_no_result(wrapper);
// bind add comment button to comment dialog
erpnext.blog.make_comment_dialog(wrapper);
// hide add comment button after 50 comments
erpnext.blog.toggle_add_comment_btn(wrapper);
}
erpnext.blog.adjust_page_height = function(wrapper) {
if (!wrapper) { wrapper = erpnext.blog.wrapper; }
if (!wrapper) { return; }
// adjust page height based on sidebar height
var $main_page = $(wrapper).find('.layout-main-section');
var $sidebar = $(wrapper).find('.layout-side-section');
if ($sidebar.height() > $main_page.height()) {
$main_page.height($sidebar.height());
} }
} if(n_comments > 50) {
$(".add-comment").toggle(false)
erpnext.blog.render_recent_list = function(wrapper) { .parent().append("<div class='alert'>Comments are closed.</div>")
if (!wrapper) { wrapper = erpnext.blog.wrapper; }
if (!wrapper) { return; }
wrapper.recent_list = new wn.ui.Listing({
parent: $(wrapper).find('.recent-posts'),
no_toolbar: true,
method: 'website.helpers.blog.get_recent_blog_list',
get_args: function() {
return { name: '{{ name }}' }
},
hide_refresh: true,
render_row: function(parent, data) {
if(data.content && data.content.length>=100) data.content += '...';
parent.innerHTML = repl('<div style="font-size: 80%">\
<a href="%(page_name)s.html">%(title)s</a>\
<div class="comment">%(content)s</div><br></div>', data);
// adjust page height depending on sidebar height
erpnext.blog.adjust_page_height(wrapper);
},
page_length: 5,
});
wrapper.recent_list.run();
}
erpnext.blog.toggle_no_result = function(wrapper) {
if (!wrapper) { wrapper = erpnext.blog.wrapper; }
if (!wrapper) { return; }
var $blog_comments = $(wrapper).find('.blog-comments');
var $comment_rows = $blog_comments.find('.comment-row');
var $no_result = $blog_comments.find('.no-result');
if ($comment_rows.length == 0) {
$no_result.removeClass('hide');
} else {
$no_result.addClass('hide');
} }
} $(".add-comment").click(function() {
$("#comment-form").toggle();
erpnext.blog.make_comment_dialog = function(wrapper) { $("#comment-form input, #comment-form, textarea").val("");
if (!wrapper) { wrapper = erpnext.blog.wrapper; } })
if (!wrapper) { return; } $("#submit-comment").click(function() {
var args = {
var $comment_btn = $(wrapper).find('button.add-comment'); comment_by_fullname: $("[name='comment_by_fullname']").val(),
comment_by: $("[name='comment_by']").val(),
$comment_btn.click(function() { comment: $("[name='comment']").val(),
if(!erpnext.blog.comment_dialog) { cmd: "website.helpers.blog.add_comment",
var d = new wn.ui.Dialog({ comment_doctype: "Blog",
title: 'Add Comment', comment_docname: "{{ name }}",
fields: [ page_name: "{{ page_name }}"
{
fieldname: 'comment_by_fullname', label: 'Your Name',
reqd: 1, fieldtype: 'Data'
},
{
fieldname: 'comment_by', label: 'Email Id',
reqd: 1, fieldtype: 'Data'
},
{
fieldname: 'comment', label: 'Comment',
reqd: 1, fieldtype: 'Text'
},
{
fieldname: 'post_comment', label: 'Post Comment',
fieldtype: 'Button'
},
],
});
erpnext.blog.comment_dialog = d;
} }
erpnext.blog.comment_dialog.fields_dict.post_comment $("#comment-form .alert").toggle(false);
.input.onclick = function() {
erpnext.blog.add_comment(wrapper); if(!args.comment_by_fullname || !args.comment_by || !args.comment) {
$("#comment-form .alert")
.html("All fields are necessary to submit the comment.")
.toggle(true);
return false;
} }
erpnext.blog.comment_dialog.show();
});
} $.ajax({
method: "POST",
erpnext.blog.add_comment = function(wrapper) { url: "server.py",
var args = erpnext.blog.comment_dialog.get_values(); data: args,
dataType: "json",
if(!args) return; success: function(data) {
if(data.exc) {
args.comment_doctype = 'Blog'; $("#comment-form .alert")
args.comment_docname = '{{ name }}'; .html(data.exc)
args.page_name = '{{ page_name }}'; .toggle(true)
} else {
wn.call({ $(data.message).appendTo(".blog-comments");
method: 'website.helpers.blog.add_comment', $(".no_comment").toggle(false);
args: args, $(".add-comment").toggle(false);
btn: this, $("#comment-form")
callback: function(r) { .replaceWith("<div class='alert'>Thank you for your comment!</div>")
if(!r.exc) { }
erpnext.blog.add_comment_to_page(wrapper, r.message);
erpnext.blog.comment_dialog.hide();
} }
} })
});
}
erpnext.blog.add_comment_to_page = function(wrapper, comment) { return false;
$blog_comments = $(wrapper).find('.blog-comments'); })
$comment_rows = $blog_comments.find('.comment-row'); })
if ($comment_rows.length) {
$blog_comments.append(comment);
} else {
$blog_comments.append(comment);
}
erpnext.blog.toggle_no_result(wrapper);
erpnext.blog.toggle_add_comment_btn(wrapper);
}
erpnext.blog.toggle_add_comment_btn = function(wrapper) {
var $wrapper = $(wrapper);
if ($wrapper.find('.blog-comments .comment-row').length > 50) {
var $comment_btn = $wrapper.find('button.add-comment');
$comment_btn.addClass('hide');
// show comments are close
$wrapper.find('.blog-comments').append("\
<div class=\"help\"> \
<p>Comments Closed</p> \
<br /> \
</div>");
}
}

View File

@ -1,13 +1,13 @@
wn.provide('erpnext.login'); var login = {};
$(document).ready(function(wrapper) { $(document).ready(function(wrapper) {
$('#login_btn').click(erpnext.login.doLogin) $('#login_btn').click(login.do_login)
$('#password').keypress(function(ev){ $('#password').keypress(function(ev){
if(ev.which==13 && $('#password').val()) { if(ev.which==13 && $('#password').val()) {
$('form').submit(function() { $('form').submit(function() {
erpnext.login.doLogin(); login.do_login();
return false; return false;
}); });
} }
@ -16,54 +16,60 @@ $(document).ready(function(wrapper) {
}) })
// Login // Login
erpnext.login.doLogin = function(){ login.do_login = function(){
var args = {}; var args = {};
args['usr']=$("#login_id").val(); args['usr']=$("#login_id").val();
args['pwd']=$("#password").val(); args['pwd']=$("#password").val();
if(!args.usr || !args.pwd) { if(!args.usr || !args.pwd) {
msgprint("Sorry, you can't login if you don't enter both the email id and password.") login.set_message("Both login and password required.");
} }
$('#login_btn').set_working(); $('#login_btn').attr("disabled", "disabled");
$('#login_message').empty(); $('#login_message').toggle(false);
$c("login", args, function(r, rtext) { $.ajax({
$('#login_btn').done_working(); type: "POST",
if(r.message=="Logged In"){ url: "server.py",
window.location.href='app.html' + (get_url_arg('page') data: {cmd:"login", usr:args.usr, pwd: args.pwd},
? ('?page='+get_url_arg('page')) : ''); dataType: "json",
} else { success: function(data) {
$i('login_message').innerHTML = '<span style="color: RED;">' $('#login_btn').attr("disabled", false);
+(r.message)+'</span>'; if(data.message=="Logged In") {
} window.location.href = "app.html";
}); } else {
login.set_message(data.message);
}
}
})
return false; return false;
} }
login.show_forgot_password = function(){
erpnext.login.show_forgot_password = function(){
// create dialog // create dialog
var d = new wn.ui.Dialog({ var login_id = $("#login_id").val();
title:"Forgot Password", if(!login_id || !valid_email(login_id)) {
fields: [ login.set_message("Please set your login id (which is your email where the password will be sent);");
{'label':'Email Id', 'fieldname':'email_id', 'fieldtype':'Data', 'reqd':true}, return;
{'label':'Email Me A New Password', 'fieldname':'run', 'fieldtype':'Button'} }
] login.set_message("Sending email with new password...");
}); $("#forgot-password").remove();
$(d.fields_dict.run.input).click(function() { $.ajax({
var values = d.get_values(); method: "POST",
if(!values) return; url: "server.py",
wn.call({ data: {
method:'reset_password', cmd: "reset_password",
args: { user: values.email_id }, user: login_id
callback: function() { },
d.hide(); success: function(data) {
} login.set_message("A new password has been sent to your email id.", "GREEN");
}) }
}) })
d.show(); }
login.set_message = function(message, color) {
$('#login_message').html(message).toggle(true);
} }

View File

@ -2,7 +2,6 @@
{% block javascript %} {% block javascript %}
{% include "js/blog.js" %} {% include "js/blog.js" %}
{% include "js/blog_subscribe.js" %}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
@ -18,9 +17,12 @@
<div class="layout-main"> <div class="layout-main">
<h1>Blog</h1> <h1>Blog</h1>
<br> <br>
<div id="blog-list"> <div id="blog-list" style="min-height: 400px;">
<!-- blog list will be generated dynamically --> <!-- blog list will be generated dynamically -->
</div> </div>
<div style="text-align: center;">
<button id="next-page" class="btn">More...</button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -14,6 +14,7 @@
{% block body %} {% block body %}
<div id='login_wrapper'> <div id='login_wrapper'>
<div class='layout-wrapper layout-main'> <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> <h3><i class="icon-lock" style="margin-top: 7px"></i> Login</h3>
<form autocomplete="on"> <form autocomplete="on">
<table border="0" class="login-box"> <table border="0" class="login-box">
@ -33,15 +34,12 @@
class="btn btn-small btn-primary">Login</button> class="btn btn-small btn-primary">Login</button>
</td> </td>
</tr> </tr>
<tr>
<td>&nbsp;</td>
<td id="login_message">&nbsp;</td>
</tr>
</tbody> </tbody>
</table> </table>
</form> </form>
<p style="text-align: center"><span class="link_type" <br>
onclick="erpnext.login.show_forgot_password()">Forgot Password</span></p> <p style="text-align: center"><a id="forgot-password"
onclick="return login.show_forgot_password()">Forgot Password</a></p>
</div> </div>
<div class="login-footer"> <div class="login-footer">
<a href="index.html">Home</a> | <a href="index.html">Home</a> |

View File

@ -33,6 +33,10 @@ page_map = {
'Item': webnotes._dict({ 'Item': webnotes._dict({
"template": 'html/product_page.html', "template": 'html/product_page.html',
"condition_field": "show_in_website", "condition_field": "show_in_website",
}),
'Item Group': webnotes._dict({
"template": "html/product_group.html",
"condition_field": "show_in_website"
}) })
} }