Merge pull request #648 from anandpdoshi/anand-wip-4.1

Fixes in Website Route system and other misc fixes
This commit is contained in:
Rushabh Mehta 2014-06-28 10:50:00 +05:30
commit c4fbcd5b5a
31 changed files with 138 additions and 75 deletions

View file

@ -44,3 +44,4 @@ class DocstatusTransitionError(ValidationError): pass
class TimestampMismatchError(ValidationError): pass
class EmptyTableError(ValidationError): pass
class LinkExistsError(ValidationError): pass
class InvalidEmailAddressError(ValidationError): pass

View file

@ -292,7 +292,7 @@ class BaseDocument(object):
return
for df in self.meta.get_select_fields():
if not (self.get(df.fieldname) and df.options):
if df.fieldname=="naming_series" or not (self.get(df.fieldname) and df.options):
continue
options = (df.options or "").split("\n")

View file

@ -22,7 +22,7 @@ def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None,
elif isinstance(target_doc, basestring):
target_doc = frappe.get_doc(json.loads(target_doc))
if not target_doc.has_permission("create"):
if not ignore_permissions and not target_doc.has_permission("create"):
target_doc.raise_no_permission_to("create")
map_doc(source_doc, target_doc, table_maps[source_doc.doctype])

View file

@ -44,19 +44,26 @@ def export_doc(doctype, name, module=None):
def get_doctype_module(doctype):
return frappe.db.get_value('DocType', doctype, 'module') or "core"
doctype_modules = {}
doctype_python_modules = {}
def load_doctype_module(doctype, module=None, prefix=""):
key = (prefix or "") + doctype
if not module:
module = get_doctype_module(doctype)
if not key in doctype_modules:
if not module:
module = get_doctype_module(doctype)
doctype_modules[key] = frappe.get_module(get_module_name(doctype, module, prefix))
app = get_module_app(module)
return doctype_modules[key]
key = (app, doctype, prefix)
def get_module_name(doctype, module, prefix=""):
from frappe.modules import scrub
if key not in doctype_python_modules:
doctype_python_modules[key] = frappe.get_module(get_module_name(doctype, module, prefix))
return doctype_python_modules[key]
def get_module_name(doctype, module, prefix="", app=None):
return '{app}.{module}.doctype.{doctype}.{prefix}{doctype}'.format(\
app = scrub(frappe.local.module_app[scrub(module)]),
module = scrub(module), doctype = scrub(doctype), prefix=prefix)
app = scrub(app or get_module_app(module)),
module = scrub(module),
doctype = scrub(doctype),
prefix=prefix)
def get_module_app(module):
return frappe.local.module_app[scrub(module)]

View file

@ -44,4 +44,4 @@ frappe.patches.v4_0.remove_user_owner_custom_field
execute:frappe.delete_doc("DocType", "Website Template")
execute:frappe.reload_doc('website', 'doctype', 'website_route') #2014-06-17
execute:frappe.db.sql("""update `tabProperty Setter` set property_type='Text' where property in ('options', 'default')""") #2014-06-20
frappe.patches.v4_1.set_outgoing_email_footer

View file

@ -1,11 +0,0 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import frappe
from erpnext.setup.install import default_mail_footer
def execute():
mail_footer = frappe.db.get_default('mail_footer') or ''
mail_footer += default_mail_footer
frappe.db.set_value("Outgoing Email Settings", "Outgoing Email Settings", "footer", mail_footer)

View file

@ -6,6 +6,8 @@ frappe.utils.full_name = function(fn, ln) {
}
function fmt_money(v, format){
// deprecated!
// for backward compatibility
return format_number(v, format);
}

View file

@ -14,12 +14,9 @@ Built on Frappe.io. Free and Open Source Framework for the Web. https://frappe.i
<link rel="icon" href="{{ favicon or "" }}" type="image/x-icon">
{%- block head_include %}{% endblock -%}
{%- block head -%}
{%- if metatags -%}
{%- for name in metatags %}
<meta name="{{ name }}" content="{{ metatags[name]|striptags }}">
{%- endfor -%}
{%- endif -%}
{% if meta_block is defined %}
{{ meta_block }}
{% endif %}
{%- for link in web_include_css %}
<link type="text/css" rel="stylesheet" href="{{ link }}">
@ -50,15 +47,15 @@ Built on Frappe.io. Free and Open Source Framework for the Web. https://frappe.i
<div class="container">
<div class="row">
<div class="col-sm-9 page-header-left">
<a class="visible-xs toggle-sidebar no-decoration pull-right">
<i class="icon-chevron-down"></i>
</a>
<div data-html-block="header">
{%- if header is defined -%}{{ header }}{%- endif -%}
</div>
<div class="page-breadcrumbs" data-html-block="breadcrumbs">
{%- if breadcrumbs is defined -%}{{ breadcrumbs }}{%- endif -%}
</div>
<!-- <a class="visible-xs toggle-sidebar no-decoration pull-right">
<i class="icon-chevron-down"></i>
</a> -->
</div>
<div class="col-sm-3 text-right">
<div class="page-header-right"></div>

View file

@ -18,7 +18,7 @@
<fieldset>
<input class="form-control" name="comment_by_fullname" placeholder="Your Name" type="text"/><br>
<input class="form-control" name="comment_by"
placeholder="Your Email Id" type="text"/><br>
placeholder="Your Email Id" type="email"/><br>
<textarea class="form-control" name="comment" rows=10
placeholder="Comment"/>
</textarea><br>
@ -62,6 +62,11 @@ $(document).ready(function() {
return false;
}
if (!valid_email(args.comment_by)) {
frappe.msgprint("Please enter a valid email address.");
return false;
}
frappe.call({
btn: this,
type: "POST",

View file

@ -43,7 +43,7 @@ def add_comment(args=None):
ifnull(unsubscribed, 0)=0""", (comment.comment_doctype, comment.comment_docname))]
owner = frappe.db.get_value(comment.comment_doctype, comment.comment_docname, "owner")
recipients = commentors if owner=="Administrator" else list(set(commentors + [owner]))
recipients = list(set(commentors if owner=="Administrator" else (commentors + [owner])))
from frappe.utils.email_lib.bulk import send

View file

@ -0,0 +1,5 @@
{%- if metatags -%}
{%- for name in metatags %}
<meta name="{{ name }}" content="{{ metatags[name]|striptags }}" data-html-block="meta_block">
{%- endfor -%}
{%- endif -%}

View file

@ -39,7 +39,7 @@
</a>
<ul class="dropdown-menu">
{%- for child in post_login -%}
<li data-label="{{ child.label }}"
<li {% if child.label %}data-label="{{ child.label }}" {% endif %}
{% if child.class %} class="{{ child.class }}" {% endif %}>
{%- if child.url -%}
@ -47,7 +47,6 @@
{%- if child.icon -%}
<i class="icon-fixed-width {{ child.icon }}"></i>
{%- endif -%}
{{ child.label }}
</a>
{%- endif -%}

View file

@ -2,10 +2,10 @@
{% if children -%}
{%- for child in children -%}
<div class="sidebar-item">
{% set is_parent = parents and child.name == parents[-1].name or (loop.first and child.name==pathname) %}
<i class="icon-fixed-width
{% if (child.lft != None) and (child.rgt - child.lft != 1) and (not loop.first) %}icon-chevron-right{% endif %}
{% if parents and child.name == parents[-1].name or (loop.first and child.name==pathname) %}icon-chevron-down{% endif %}"
style="margin-left: -17px; color: #ddd;"></i>
{% if (child.lft != None) and (child.rgt - child.lft != 1) and (not loop.first) %}icon-chevron-right{% endif %}"
style="margin-left: -15px; color: #ddd; {% if is_parent %}margin-left: -30px;{% endif %}"></i>
<a href="{{ child.name }}" class="no-decoration {% if child.name == pathname %}active{% endif %}">
{{ child.page_title }}
{% if not child.public_read %}

View file

@ -1,4 +1,4 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# MIT License. See license.txt
no_sitemap = 1
no_sitemap = 1

View file

@ -4,5 +4,6 @@
from __future__ import unicode_literals
import frappe
page_title = "Blog"
def get_context(context):
return frappe.get_doc("Blog Settings", "Blog Settings").as_dict()

View file

@ -18,7 +18,7 @@ def get_context(context):
for l in frappe.db.sql("""select page_name, lastmod, controller
from `tabWebsite Route`""",
as_dict=True):
module = frappe.get_module(l.controller)
module = frappe.get_module(l.controller) if l.controller else None
if not getattr(module, "no_sitemap", False):
links.append({
"loc": urllib.basejoin(host, urllib.quote(l.page_name.encode("utf-8"))),

View file

@ -16,6 +16,7 @@ class BulkLimitCrossedError(frappe.ValidationError): pass
def send(recipients=None, sender=None, doctype='User', email_field='email',
subject='[No Subject]', message='[No Content]', ref_doctype=None, ref_docname=None,
add_unsubscribe_link=True):
def is_unsubscribed(rdata):
if not rdata:
return 1
@ -81,7 +82,7 @@ def add(email, sender, subject, formatted, text_content=None,
try:
e.message = get_email(email, sender=e.sender, formatted=formatted, subject=subject,
text_content=text_content).as_string()
except frappe.ValidationError:
except frappe.InvalidEmailAddressError:
# bad email id - don't add to queue
return

View file

@ -152,17 +152,18 @@ class EMail:
def _validate(email):
"""validate an email field"""
if email and not validate_email_add(email):
throw(_("{0} is not a valid email id").format(email))
throw(_("{0} is not a valid email id").format(email), frappe.InvalidEmailAddressError)
return email
if not self.sender:
self.sender = frappe.db.get_value('Outgoing Email Settings', None,
'auto_email_id') or frappe.conf.get('auto_email_id') or None
if not self.sender:
msgprint(_("Please specify 'Auto Email Id' in Setup > Outgoing Email Settings"))
msg = _("Please specify 'Auto Email Id' in Setup > Outgoing Email Settings")
msgprint(msg)
if not "expires_on" in frappe.conf:
msgprint(_("Alternatively, you can also specify 'auto_email_id' in site_config.json"))
raise frappe.ValidationError
raise frappe.ValidationError, msg
self.sender = _validate(self.sender)
self.reply_to = _validate(self.reply_to)

View file

@ -67,7 +67,7 @@ def build_context(sitemap_options):
add_metatags(context)
if context.get("base_template_path") != context.get("template_path") and not context.get("rendered"):
if context.get("base_template_path") != context.get("template") and not context.get("rendered"):
context.data = render_blocks(context)
return context

View file

@ -20,3 +20,7 @@ class BlogCategory(WebsiteGenerator):
def on_update(self):
WebsiteGenerator.on_update(self)
clear_cache()
def get_parent_website_route(self):
parent_website_sitemap = super(BlogCategory, self).get_parent_website_route()
return parent_website_sitemap or "blog"

View file

@ -44,7 +44,8 @@
"in_list_view": 1,
"label": "Blog Category",
"options": "Blog Category",
"permlevel": 0
"permlevel": 0,
"reqd": 1
},
{
"fieldname": "parent_website_route",
@ -95,7 +96,7 @@
"icon": "icon-quote-left",
"idx": 1,
"max_attachments": 5,
"modified": "2014-05-27 03:49:07.888408",
"modified": "2014-06-27 05:08:37.936947",
"modified_by": "Administrator",
"module": "Website",
"name": "Blog Post",

View file

@ -22,7 +22,7 @@ class BlogPost(WebsiteGenerator):
def validate(self):
if not self.blog_intro:
self.blog_intro = self.content[:140]
re.sub("\<[^>]*\>", "", self.blog_intro)
self.blog_intro = re.sub("\<[^>]*\>", "", self.blog_intro)
if self.blog_intro:
self.blog_intro = self.blog_intro[:140]

View file

@ -27,10 +27,10 @@ class WebPage(WebsiteGenerator):
context.metatags = {
"name": self.title,
"description": self.description or self.main_section[:150]
"description": self.description or (self.main_section or "")[:150]
}
image = find_first_image(self.main_section)
image = find_first_image(self.main_section or "")
if image:
context.metatags["image"] = image

View file

@ -38,8 +38,9 @@
},
{
"fieldname": "docname",
"fieldtype": "Data",
"fieldtype": "Dynamic Link",
"label": "Docname",
"options": "ref_doctype",
"permlevel": 0,
"read_only": 1
},
@ -118,7 +119,7 @@
}
],
"idx": 1,
"modified": "2014-06-16 05:54:16.240406",
"modified": "2014-06-27 05:04:57.721756",
"modified_by": "Administrator",
"module": "Website",
"name": "Website Route",

View file

@ -23,17 +23,19 @@ class WebsiteRoute(NestedSet):
def validate(self):
self.check_if_page_name_is_unique()
self.make_private_if_parent_is_private()
if not frappe.flags.in_sync_website:
self.make_private_if_parent_is_private()
def on_update(self):
if self.get_url() != self.name:
self.rename()
if not frappe.flags.in_rebuild_config:
if not frappe.flags.in_sync_website:
NestedSet.on_update(self)
self.clear_cache()
def rename(self, new_page_name=None, new_parent_website_route=None):
self.old_name = self.name
self.old_parent_website_route = self.parent_website_route
# get new route
if new_page_name != None:
@ -50,6 +52,8 @@ class WebsiteRoute(NestedSet):
self.rename_links()
self.rename_descendants()
self.clear_cache(self.old_name)
self.clear_cache(self.old_parent_website_route)
self.clear_cache(self.parent_website_route)
def rename_links(self):
for doctype in frappe.db.sql_list("""select parent from tabDocField
@ -103,9 +107,10 @@ class WebsiteRoute(NestedSet):
from frappe.website.render import clear_cache
if name:
clear_cache(name)
elif self.parent_website_route:
clear_cache(self.parent_website_route)
else:
if self.parent_website_route:
clear_cache(self.parent_website_route)
clear_cache(self.name)
def remove_sitemap(page_name=None, ref_doctype=None, docname=None):

View file

@ -107,6 +107,10 @@ $.extend(frappe, {
} catch(e) {
console.log(data.exc);
}
if (opts.error_msg && data._server_messages) {
var server_messages = (JSON.parse(data._server_messages || '[]')).join("<br>");
$(opts.error_msg).html(server_messages).toggle(true);
}
} else{
if(opts.btn) {
$(opts.btn).addClass("btn-success");
@ -286,6 +290,10 @@ $.extend(frappe, {
$('[data-html-block]').each(function(i, section) {
var $section = $(section);
var stype = $section.attr("data-html-block");
// handle meta separately
if (stype==="meta_block") return;
var block_data = data[stype] || "";
// NOTE: use frappe.ready instead of $.ready for reliable execution
@ -307,6 +315,12 @@ $.extend(frappe, {
});
if(data.title) $("title").html(data.title);
// change meta tags
$('[data-html-block="meta_block"]').remove();
if(data.meta_block) {
$("head").append(data.meta_block);
}
// change id of current page
$(".page-container").attr("id", "page-" + data.path);

View file

@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe, os, sys
from frappe.modules import load_doctype_module
from frappe.utils.nestedset import rebuild_tree
import statics, render
def sync(app=None):
@ -46,11 +47,24 @@ def sync_pages(routes):
def sync_generators(generators):
l = len(generators)
if l:
frappe.flags.in_sync_website = True
for i, g in enumerate(generators):
doc = frappe.get_doc(g[0], g[1])
doc.ignore_links = True
doc.save(ignore_permissions=True)
sys.stdout.write("\rUpdating generators {0}/{1}".format(i+1, l))
sys.stdout.flush()
frappe.flags.in_sync_website = False
rebuild_tree("Website Route", "parent_website_route")
# HACK! update public_read, public_write
for name in frappe.db.sql_list("""select name from `tabWebsite Route` where ifnull(parent_website_route, '')!=''
order by lft"""):
route = frappe.get_doc("Website Route", name)
route.make_private_if_parent_is_private()
route.db_update()
print ""
def get_sync_pages(app):
@ -63,17 +77,24 @@ def get_sync_pages(app):
fname = frappe.utils.cstr(fname)
page_name, extn = fname.rsplit(".", 1)
if extn in ("html", "xml", "js", "css"):
route_page_name = page_name if extn=="html" else fname
# add website route
route = frappe.new_doc("Website Route")
route.page_or_generator = "Page"
route.template = os.path.relpath(os.path.join(path, fname), app_path)
route.page_name = page_name
route.page_name = route_page_name
route.public_read = 1
controller_path = os.path.join(path, page_name + ".py")
if os.path.exists(controller_path):
controller = app + "." + os.path.relpath(controller_path,
app_path).replace(os.path.sep, ".")[:-3]
route.controller = controller
try:
route.page_title = frappe.get_attr(controller + "." + "page_title")
except AttributeError:
pass
pages.append(route)

View file

@ -48,6 +48,10 @@ def render_blocks(context):
out["breadcrumbs"] = scrub_relative_urls(
frappe.get_template("templates/includes/breadcrumbs.html").render(context))
if "meta_block" not in out:
out["meta_block"] = frappe.get_template("templates/includes/meta_block.html").render(context)
if "<!-- no-sidebar -->" in out.get("content", ""):
out["no_sidebar"] = 1

View file

@ -35,11 +35,8 @@ class WebsiteGenerator(Document):
return frappe.db.get_value("Website Route",
{"ref_doctype":self.doctype, "docname": name or self.name})
def after_rename(self, olddn, newdn, merge):
route = self.get_route_docname(olddn)
if route:
frappe.get_doc("Website Route", route).rename()
self.update_route(self.get_route_docname())
def on_trash(self):
remove_sitemap(ref_doctype=self.doctype, docname=self.name)
@ -47,20 +44,30 @@ class WebsiteGenerator(Document):
def update_sitemap(self):
# update route of all descendants
route_docname = self.get_route_docname()
if route_docname:
if self.get_route() != route_docname:
frappe.get_doc("Website Route", route_docname)\
.rename(self.get_page_name(), self.get_parent_website_route())
else:
self.add_or_update_sitemap()
def add_or_update_sitemap(self):
# check if "condtion_field" property is okay
self.controller_module = load_doctype_module(self.doctype)
if hasattr(self.controller_module, "condition_field"):
if not self.get(self.controller_module.condition_field):
frappe.delete_doc("Website Route", route_docname, ignore_permissions=True)
return
if route_docname:
self.update_route(route_docname)
else:
self.insert_route()
def update_route(self, route_docname):
route = frappe.get_doc("Website Route", route_docname)
if self.get_route() != route_docname:
route.rename(self.get_page_name(), self.get_parent_website_route())
route.idx = self.idx
route.page_title = self.get_page_title()
self.update_permissions(route)
route.save()
def insert_route(self):
if self.modified:
# for sitemap.xml
lastmod = frappe.utils.get_datetime(self.modified).strftime("%Y-%m-%d")
@ -78,8 +85,7 @@ class WebsiteGenerator(Document):
"template": self.controller_module.template,
"lastmod": lastmod,
"parent_website_route": self.get_parent_website_route(),
"page_title": self.get_page_title(),
"public_read": 1 if not getattr(self, "no_sidebar", None) else 0
"page_title": self.get_page_title()
})
self.update_permissions(route)

View file

@ -157,7 +157,6 @@ class FormMeta(Meta):
def load_form_grid_templates(self):
module = load_doctype_module(self.name)
app = module.__name__.split(".")[0]
print module, app
templates = {}
if hasattr(module, "form_grid_templates"):
for key, path in module.form_grid_templates.iteritems():