@@ -377,7 +377,7 @@ def get_module(modulename): | |||||
return importlib.import_module(modulename) | return importlib.import_module(modulename) | ||||
def scrub(txt): | def scrub(txt): | ||||
return txt.replace(' ','_').replace('-', '_').replace('/', '_').lower() | |||||
return txt.replace(' ','_').replace('-', '_').lower() | |||||
def unscrub(txt): | def unscrub(txt): | ||||
return txt.replace('_',' ').replace('-', ' ').title() | return txt.replace('_',' ').replace('-', ' ').title() | ||||
@@ -1,5 +1,5 @@ | |||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | ||||
# MIT License. See license.txt | |||||
# MIT License. See license.txt | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
from frappe.utils.minify import JavascriptMinify | from frappe.utils.minify import JavascriptMinify | ||||
@@ -16,7 +16,7 @@ def bundle(no_compress, make_copy=False): | |||||
# build js files | # build js files | ||||
make_asset_dirs(make_copy=make_copy) | make_asset_dirs(make_copy=make_copy) | ||||
build(no_compress) | build(no_compress) | ||||
def watch(no_compress): | def watch(no_compress): | ||||
"""watch and rebuild if necessary""" | """watch and rebuild if necessary""" | ||||
import time | import time | ||||
@@ -25,18 +25,18 @@ def watch(no_compress): | |||||
while True: | while True: | ||||
if files_dirty(): | if files_dirty(): | ||||
build(no_compress=True) | build(no_compress=True) | ||||
time.sleep(3) | time.sleep(3) | ||||
def make_asset_dirs(make_copy=False): | def make_asset_dirs(make_copy=False): | ||||
assets_path = os.path.join(frappe.local.sites_path, "assets") | assets_path = os.path.join(frappe.local.sites_path, "assets") | ||||
for dir_path in [ | for dir_path in [ | ||||
os.path.join(assets_path, 'js'), | |||||
os.path.join(assets_path, 'js'), | |||||
os.path.join(assets_path, 'css')]: | os.path.join(assets_path, 'css')]: | ||||
if not os.path.exists(dir_path): | if not os.path.exists(dir_path): | ||||
os.makedirs(dir_path) | os.makedirs(dir_path) | ||||
# symlink app/public > assets/app | # symlink app/public > assets/app | ||||
for app_name in frappe.get_all_apps(True): | for app_name in frappe.get_all_apps(True): | ||||
pymodule = frappe.get_module(app_name) | pymodule = frappe.get_module(app_name) | ||||
@@ -53,7 +53,7 @@ def build(no_compress=False): | |||||
assets_path = os.path.join(frappe.local.sites_path, "assets") | assets_path = os.path.join(frappe.local.sites_path, "assets") | ||||
for target, sources in get_build_maps().iteritems(): | for target, sources in get_build_maps().iteritems(): | ||||
pack(os.path.join(assets_path, target), sources, no_compress) | |||||
pack(os.path.join(assets_path, target), sources, no_compress) | |||||
shutil.copy(os.path.join(os.path.dirname(os.path.abspath(frappe.__file__)), 'data', 'languages.txt'), frappe.local.sites_path) | shutil.copy(os.path.join(os.path.dirname(os.path.abspath(frappe.__file__)), 'data', 'languages.txt'), frappe.local.sites_path) | ||||
# reset_app_html() | # reset_app_html() | ||||
@@ -79,39 +79,46 @@ def get_build_maps(): | |||||
else: | else: | ||||
s = os.path.join(app_path, source) | s = os.path.join(app_path, source) | ||||
source_paths.append(s) | source_paths.append(s) | ||||
build_maps[target] = source_paths | build_maps[target] = source_paths | ||||
except Exception, e: | except Exception, e: | ||||
print path | print path | ||||
raise | raise | ||||
return build_maps | return build_maps | ||||
timestamps = {} | timestamps = {} | ||||
def pack(target, sources, no_compress): | def pack(target, sources, no_compress): | ||||
from cStringIO import StringIO | from cStringIO import StringIO | ||||
outtype, outtxt = target.split(".")[-1], '' | outtype, outtxt = target.split(".")[-1], '' | ||||
jsm = JavascriptMinify() | jsm = JavascriptMinify() | ||||
for f in sources: | for f in sources: | ||||
suffix = None | suffix = None | ||||
if ':' in f: f, suffix = f.split(':') | if ':' in f: f, suffix = f.split(':') | ||||
if not os.path.exists(f) or os.path.isdir(f): continue | if not os.path.exists(f) or os.path.isdir(f): continue | ||||
timestamps[f] = os.path.getmtime(f) | timestamps[f] = os.path.getmtime(f) | ||||
try: | try: | ||||
with open(f, 'r') as sourcefile: | |||||
with open(f, 'r') as sourcefile: | |||||
data = unicode(sourcefile.read(), 'utf-8', errors='ignore') | data = unicode(sourcefile.read(), 'utf-8', errors='ignore') | ||||
if outtype=="js" and (not no_compress) and suffix!="concat" and (".min." not in f): | |||||
extn = f.rsplit(".", 1)[1] | |||||
if outtype=="js" and extn=="js" and (not no_compress) and suffix!="concat" and (".min." not in f): | |||||
tmpin, tmpout = StringIO(data.encode('utf-8')), StringIO() | tmpin, tmpout = StringIO(data.encode('utf-8')), StringIO() | ||||
jsm.minify(tmpin, tmpout) | jsm.minify(tmpin, tmpout) | ||||
outtxt += unicode(tmpout.getvalue() or '', 'utf-8').strip('\n') + ';' | outtxt += unicode(tmpout.getvalue() or '', 'utf-8').strip('\n') + ';' | ||||
elif outtype=="js" and extn=="html": | |||||
# add to frappe.templates | |||||
content = data.replace("\n", " ").replace("'", "\'") | |||||
outtxt += """frappe.templates["{key}"] = '{content}';\n""".format(\ | |||||
key=f.rsplit("/", 1)[1][:-5], content=content) | |||||
else: | else: | ||||
outtxt += ('\n/*\n *\t%s\n */' % f) | outtxt += ('\n/*\n *\t%s\n */' % f) | ||||
outtxt += '\n' + data + '\n' | outtxt += '\n' + data + '\n' | ||||
except Exception, e: | except Exception, e: | ||||
print "--Error in:" + f + "--" | print "--Error in:" + f + "--" | ||||
print frappe.get_traceback() | print frappe.get_traceback() | ||||
@@ -119,10 +126,10 @@ def pack(target, sources, no_compress): | |||||
if not no_compress and outtype == 'css': | if not no_compress and outtype == 'css': | ||||
pass | pass | ||||
#outtxt = cssmin(outtxt) | #outtxt = cssmin(outtxt) | ||||
with open(target, 'w') as f: | with open(target, 'w') as f: | ||||
f.write(outtxt.encode("utf-8")) | f.write(outtxt.encode("utf-8")) | ||||
print "Wrote %s - %sk" % (target, str(int(os.path.getsize(target)/1024))) | print "Wrote %s - %sk" % (target, str(int(os.path.getsize(target)/1024))) | ||||
def files_dirty(): | def files_dirty(): | ||||
@@ -135,4 +142,4 @@ def files_dirty(): | |||||
return True | return True | ||||
else: | else: | ||||
return False | return False | ||||
@@ -230,7 +230,7 @@ def setup_utilities(parser): | |||||
# clear | # clear | ||||
parser.add_argument("--clear_web", default=False, action="store_true", | parser.add_argument("--clear_web", default=False, action="store_true", | ||||
help="Clear website cache") | help="Clear website cache") | ||||
parser.add_argument("--build_sitemap", default=False, action="store_true", | |||||
parser.add_argument("--build_website", default=False, action="store_true", | |||||
help="Build Website Route") | help="Build Website Route") | ||||
parser.add_argument("--sync_statics", default=False, action="store_true", | parser.add_argument("--sync_statics", default=False, action="store_true", | ||||
help="Sync files from templates/statics to Web Pages") | help="Sync files from templates/statics to Web Pages") | ||||
@@ -352,7 +352,7 @@ def add_to_installed_apps(*apps): | |||||
all_apps = frappe.get_all_apps(with_frappe=True) | all_apps = frappe.get_all_apps(with_frappe=True) | ||||
for each in apps: | for each in apps: | ||||
if each in all_apps: | if each in all_apps: | ||||
add_to_installed_apps(each, rebuild_sitemap=False) | |||||
add_to_installed_apps(each, rebuild_website=False) | |||||
frappe.destroy() | frappe.destroy() | ||||
@cmd | @cmd | ||||
@@ -402,13 +402,11 @@ def update(remote=None, branch=None, reload_gunicorn=False): | |||||
subprocess.check_output("killall -HUP gunicorn".split()) | subprocess.check_output("killall -HUP gunicorn".split()) | ||||
@cmd | @cmd | ||||
def latest(rebuild_website_config=True, quiet=False): | |||||
def latest(rebuild_website=True, quiet=False): | |||||
import frappe.modules.patch_handler | import frappe.modules.patch_handler | ||||
import frappe.model.sync | import frappe.model.sync | ||||
from frappe.website import rebuild_config | |||||
from frappe.utils.fixtures import sync_fixtures | from frappe.utils.fixtures import sync_fixtures | ||||
import frappe.translate | import frappe.translate | ||||
from frappe.website import statics | |||||
verbose = not quiet | verbose = not quiet | ||||
@@ -419,17 +417,11 @@ def latest(rebuild_website_config=True, quiet=False): | |||||
frappe.modules.patch_handler.run_all() | frappe.modules.patch_handler.run_all() | ||||
# sync | # sync | ||||
frappe.model.sync.sync_all(verbose=verbose) | frappe.model.sync.sync_all(verbose=verbose) | ||||
sync_fixtures() | |||||
sync = statics.sync() | |||||
sync.start() | |||||
sync.start(rebuild=True) | |||||
# build website config if any changes in templates etc. | |||||
if rebuild_website_config: | |||||
rebuild_config() | |||||
frappe.translate.clear_cache() | frappe.translate.clear_cache() | ||||
sync_fixtures() | |||||
if rebuild_website: | |||||
build_website() | |||||
finally: | finally: | ||||
frappe.destroy() | frappe.destroy() | ||||
@@ -561,10 +553,11 @@ def clear_all_sessions(): | |||||
frappe.destroy() | frappe.destroy() | ||||
@cmd | @cmd | ||||
def build_sitemap(): | |||||
from frappe.website import rebuild_config | |||||
def build_website(): | |||||
import frappe.website.sync | |||||
frappe.connect() | frappe.connect() | ||||
rebuild_config() | |||||
frappe.website.sync.sync() | |||||
frappe.db.commit() | |||||
frappe.destroy() | frappe.destroy() | ||||
@cmd | @cmd | ||||
@@ -110,6 +110,7 @@ def get_customer_supplier(args=None): | |||||
def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]', send_me_a_copy=False): | def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]', send_me_a_copy=False): | ||||
footer = None | footer = None | ||||
if sent_via: | if sent_via: | ||||
if hasattr(sent_via, "get_sender"): | if hasattr(sent_via, "get_sender"): | ||||
d.sender = sent_via.get_sender(d) or d.sender | d.sender = sent_via.get_sender(d) or d.sender | ||||
@@ -13,6 +13,10 @@ from frappe.model.document import Document | |||||
from frappe.model.db_schema import type_map | from frappe.model.db_schema import type_map | ||||
from frappe.core.doctype.property_setter.property_setter import make_property_setter | from frappe.core.doctype.property_setter.property_setter import make_property_setter | ||||
form_grid_templates = { | |||||
"fields": "templates/form_grid/fields.html" | |||||
} | |||||
class DocType(Document): | class DocType(Document): | ||||
def validate(self): | def validate(self): | ||||
if not frappe.conf.get("developer_mode"): | if not frappe.conf.get("developer_mode"): | ||||
@@ -1,88 +1,105 @@ | |||||
{ | { | ||||
"allow_copy": 1, | |||||
"creation": "2014-03-03 19:48:01.000000", | |||||
"description": "Email Settings for Outgoing and Incoming Emails.", | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"allow_copy": 1, | |||||
"creation": "2014-03-03 19:48:01", | |||||
"description": "Email Settings for Outgoing and Incoming Emails.", | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"fields": [ | "fields": [ | ||||
{ | { | ||||
"description": "SMTP Server (e.g. smtp.gmail.com)", | |||||
"fieldname": "mail_server", | |||||
"fieldtype": "Data", | |||||
"label": "Outgoing Mail Server", | |||||
"description": "SMTP Server (e.g. smtp.gmail.com)", | |||||
"fieldname": "mail_server", | |||||
"fieldtype": "Data", | |||||
"in_list_view": 1, | |||||
"label": "Outgoing Mail Server", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "<a href=\"https://en.wikipedia.org/wiki/Transport_Layer_Security\" target=\"_blank\">[?]</a>", | |||||
"fieldname": "use_ssl", | |||||
"fieldtype": "Check", | |||||
"label": "Use TLS", | |||||
"description": "<a href=\"https://en.wikipedia.org/wiki/Transport_Layer_Security\" target=\"_blank\">[?]</a>", | |||||
"fieldname": "use_ssl", | |||||
"fieldtype": "Check", | |||||
"in_list_view": 1, | |||||
"label": "Use TLS", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "If non standard port (e.g. 587)", | |||||
"fieldname": "mail_port", | |||||
"fieldtype": "Int", | |||||
"label": "Port", | |||||
"description": "If non standard port (e.g. 587)", | |||||
"fieldname": "mail_port", | |||||
"fieldtype": "Int", | |||||
"in_list_view": 1, | |||||
"label": "Port", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "cb0", | |||||
"fieldtype": "Column Break", | |||||
"fieldname": "cb0", | |||||
"fieldtype": "Column Break", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "Set Login and Password if authentication is required.", | |||||
"fieldname": "mail_login", | |||||
"fieldtype": "Data", | |||||
"label": "Login Id", | |||||
"description": "Set Login and Password if authentication is required.", | |||||
"fieldname": "mail_login", | |||||
"fieldtype": "Data", | |||||
"in_list_view": 1, | |||||
"label": "Login Id", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "Check this if you want to send emails as this id only (in case of restriction by your email provider).", | |||||
"fieldname": "always_use_login_id_as_sender", | |||||
"fieldtype": "Check", | |||||
"label": "Always use above Login Id as sender", | |||||
"description": "Check this if you want to send emails as this id only (in case of restriction by your email provider).", | |||||
"fieldname": "always_use_login_id_as_sender", | |||||
"fieldtype": "Check", | |||||
"label": "Always use above Login Id as sender", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "mail_password", | |||||
"fieldtype": "Password", | |||||
"label": "Mail Password", | |||||
"fieldname": "mail_password", | |||||
"fieldtype": "Password", | |||||
"label": "Mail Password", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "System generated mails will be sent from this email id.", | |||||
"fieldname": "auto_email_id", | |||||
"fieldtype": "Data", | |||||
"label": "Auto Email Id", | |||||
"description": "System generated mails will be sent from this email id.", | |||||
"fieldname": "auto_email_id", | |||||
"fieldtype": "Data", | |||||
"label": "Auto Email Id", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"default": "1", | |||||
"description": "If checked, an email with an attached HTML format will be added to part of the EMail body as well as attachment. To only send as attachment, uncheck this.", | |||||
"fieldname": "send_print_in_body_and_attachment", | |||||
"fieldtype": "Check", | |||||
"label": "Send Print in Body and Attachment", | |||||
"default": "1", | |||||
"description": "If checked, an email with an attached HTML format will be added to part of the EMail body as well as attachment. To only send as attachment, uncheck this.", | |||||
"fieldname": "send_print_in_body_and_attachment", | |||||
"fieldtype": "Check", | |||||
"label": "Send Print in Body and Attachment", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
{ | |||||
"fieldname": "section_break_10", | |||||
"fieldtype": "Section Break", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"default": "<div style=\"padding: 7px; text-align: right; color: #888\"><small>Sent via \n\t<a style=\"color: #888\" href=\"http://frappe.io\">Frappe</a></div>", | |||||
"fieldname": "footer", | |||||
"fieldtype": "Text Editor", | |||||
"label": "Email Footer", | |||||
"permlevel": 0, | |||||
"reqd": 0 | |||||
} | } | ||||
], | |||||
"icon": "icon-cog", | |||||
"idx": 1, | |||||
"in_create": 1, | |||||
"issingle": 1, | |||||
"modified": "2014-03-03 20:20:09.000000", | |||||
"modified_by": "Administrator", | |||||
"module": "Core", | |||||
"name": "Outgoing Email Settings", | |||||
"owner": "Administrator", | |||||
], | |||||
"icon": "icon-cog", | |||||
"idx": 1, | |||||
"in_create": 1, | |||||
"issingle": 1, | |||||
"modified": "2014-06-26 02:15:27.070400", | |||||
"modified_by": "Administrator", | |||||
"module": "Core", | |||||
"name": "Outgoing Email Settings", | |||||
"owner": "Administrator", | |||||
"permissions": [ | "permissions": [ | ||||
{ | { | ||||
"create": 1, | |||||
"permlevel": 0, | |||||
"read": 1, | |||||
"role": "System Manager", | |||||
"create": 1, | |||||
"permlevel": 0, | |||||
"read": 1, | |||||
"role": "System Manager", | |||||
"write": 1 | "write": 1 | ||||
} | } | ||||
] | ] | ||||
} | |||||
} |
@@ -23,3 +23,6 @@ class OutgoingEmailSettings(Document): | |||||
# exceptions are handled in session connect | # exceptions are handled in session connect | ||||
sess = smtpserver.sess | sess = smtpserver.sess | ||||
def get_mail_footer(): | |||||
return frappe.db.get_value("Outgoing Email Settings", "Outgoing Email Settings", "footer") or "" |
@@ -20,7 +20,7 @@ | |||||
@media (max-width: 768px) { | @media (max-width: 768px) { | ||||
.case-wrapper { | .case-wrapper { | ||||
margin: 12px; | |||||
margin: 9px; | |||||
width: 70px; | width: 70px; | ||||
height: 80px; | height: 80px; | ||||
} | } | ||||
@@ -35,6 +35,7 @@ frappe.PermissionEngine = Class.extend({ | |||||
me.setup_appframe(); | me.setup_appframe(); | ||||
} | } | ||||
}); | }); | ||||
}, | }, | ||||
setup_appframe: function() { | setup_appframe: function() { | ||||
var me = this; | var me = this; | ||||
@@ -192,8 +193,8 @@ frappe.PermissionEngine = Class.extend({ | |||||
} | } | ||||
var checkbox = $("<div class='col-md-4'><div class='checkbox'>\ | var checkbox = $("<div class='col-md-4'><div class='checkbox'>\ | ||||
<label><input type='checkbox'>"+__(label)+"</input></label>\ | |||||
</div></div>").appendTo(cell) | |||||
<label><input type='checkbox'>"+__(label)+"</input></label>" | |||||
+ (d.help || "") + "</div></div>").appendTo(cell) | |||||
.attr("data-fieldname", fieldname) | .attr("data-fieldname", fieldname) | ||||
.css("text-transform", "capitalize"); | .css("text-transform", "capitalize"); | ||||
@@ -214,9 +215,12 @@ frappe.PermissionEngine = Class.extend({ | |||||
me.set_show_users(role_cell, d.role); | me.set_show_users(role_cell, d.role); | ||||
if (d.permlevel===0) { | if (d.permlevel===0) { | ||||
d.help = '<div style="margin-left: 20px;">\ | |||||
<a class="show-user-permissions small">Show User Pemissions</a></div>'; | |||||
add_check(role_cell, d, "apply_user_permissions") | add_check(role_cell, d, "apply_user_permissions") | ||||
.removeClass("col-md-4") | .removeClass("col-md-4") | ||||
.css({"margin-top": "15px"}); | .css({"margin-top": "15px"}); | ||||
d.help = ""; | |||||
} | } | ||||
var cell = add_cell(row, d, "permlevel"); | var cell = add_cell(row, d, "permlevel"); | ||||
@@ -290,6 +294,12 @@ frappe.PermissionEngine = Class.extend({ | |||||
}, | }, | ||||
add_check_events: function() { | add_check_events: function() { | ||||
var me = this; | var me = this; | ||||
this.body.on("click", ".show-user-permissions", function() { | |||||
frappe.route_options = { doctype: me.get_doctype() || "" }; | |||||
frappe.set_route("user-permissions"); | |||||
}); | |||||
this.body.on("click", "input[type='checkbox']", function() { | this.body.on("click", "input[type='checkbox']", function() { | ||||
var chk = $(this); | var chk = $(this); | ||||
var args = { | var args = { | ||||
@@ -2,7 +2,7 @@ frappe.pages['user-permissions'].onload = function(wrapper) { | |||||
frappe.ui.make_app_page({ | frappe.ui.make_app_page({ | ||||
parent: wrapper, | parent: wrapper, | ||||
title: "User Permissions Manager", | title: "User Permissions Manager", | ||||
icon: "icon-user", | |||||
icon: "icon-shield", | |||||
single_column: true | single_column: true | ||||
}); | }); | ||||
$(wrapper).find(".layout-main").html("<div class='user-settings' style='min-height: 200px;'></div>\ | $(wrapper).find(".layout-main").html("<div class='user-settings' style='min-height: 200px;'></div>\ | ||||
@@ -45,6 +45,10 @@ frappe.UserPermissions = Class.extend({ | |||||
}, | }, | ||||
make: function() { | make: function() { | ||||
var me = this; | var me = this; | ||||
this.wrapper.appframe.add_primary_action("Role Permissions", function() { | |||||
frappe.route_options = { doctype: me.get_doctype() || "" }; | |||||
frappe.set_route("permission-manager"); | |||||
}, "icon-lock"); | |||||
return frappe.call({ | return frappe.call({ | ||||
module:"frappe.core", | module:"frappe.core", | ||||
page:"user_permissions", | page:"user_permissions", | ||||
@@ -44,3 +44,4 @@ class DocstatusTransitionError(ValidationError): pass | |||||
class TimestampMismatchError(ValidationError): pass | class TimestampMismatchError(ValidationError): pass | ||||
class EmptyTableError(ValidationError): pass | class EmptyTableError(ValidationError): pass | ||||
class LinkExistsError(ValidationError): pass | class LinkExistsError(ValidationError): pass | ||||
class InvalidEmailAddressError(ValidationError): pass |
@@ -26,7 +26,7 @@ web_include_css = [ | |||||
"style_settings.css" | "style_settings.css" | ||||
] | ] | ||||
website_clear_cache = "frappe.templates.generators.website_group.clear_cache" | |||||
website_clear_cache = "frappe.website.doctype.website_group.website_group.clear_cache" | |||||
write_file_keys = ["file_url", "file_name"] | write_file_keys = ["file_url", "file_name"] | ||||
@@ -34,34 +34,33 @@ notification_config = "frappe.core.notifications.get_notification_config" | |||||
before_tests = "frappe.utils.install.before_tests" | before_tests = "frappe.utils.install.before_tests" | ||||
website_generators = ["Web Page", "Blog Post", "Website Group", "Blog Category"] | |||||
# permissions | # permissions | ||||
permission_query_conditions = { | permission_query_conditions = { | ||||
"Event": "frappe.core.doctype.event.event.get_permission_query_conditions", | |||||
"ToDo": "frappe.core.doctype.todo.todo.get_permission_query_conditions" | |||||
} | |||||
"Event": "frappe.core.doctype.event.event.get_permission_query_conditions", | |||||
"ToDo": "frappe.core.doctype.todo.todo.get_permission_query_conditions" | |||||
} | |||||
has_permission = { | has_permission = { | ||||
"Event": "frappe.core.doctype.event.event.has_permission", | |||||
"ToDo": "frappe.core.doctype.todo.todo.has_permission" | |||||
} | |||||
# bean | |||||
"Event": "frappe.core.doctype.event.event.has_permission", | |||||
"ToDo": "frappe.core.doctype.todo.todo.has_permission" | |||||
} | |||||
doc_events = { | doc_events = { | ||||
"*": { | |||||
"on_update": "frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications", | |||||
"on_cancel": "frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications", | |||||
"on_trash": "frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications" | |||||
}, | |||||
"User Vote": { | |||||
"after_insert": "frappe.templates.generators.website_group.clear_cache_on_doc_event" | |||||
}, | |||||
"Website Route Permission": { | |||||
"on_update": "frappe.templates.generators.website_group.clear_cache_on_doc_event" | |||||
} | |||||
"*": { | |||||
"on_update": "frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications", | |||||
"on_cancel": "frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications", | |||||
"on_trash": "frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications" | |||||
}, | |||||
"User Vote": { | |||||
"after_insert": "frappe.website.doctype.website_group.website_group.clear_cache_on_doc_event" | |||||
}, | |||||
"Website Route Permission": { | |||||
"on_update": "frappe.website.doctype.website_group.website_group.clear_cache_on_doc_event" | |||||
} | } | ||||
} | |||||
scheduler_events = { | scheduler_events = { | ||||
"all": ["frappe.utils.email_lib.bulk.flush"], | "all": ["frappe.utils.email_lib.bulk.flush"], | ||||
@@ -72,6 +71,8 @@ scheduler_events = { | |||||
"frappe.sessions.clear_expired_sessions", | "frappe.sessions.clear_expired_sessions", | ||||
], | ], | ||||
"hourly": [ | "hourly": [ | ||||
"frappe.templates.generators.website_group.clear_event_cache" | |||||
"frappe.website.doctype.website_group.website_group.clear_event_cache" | |||||
] | ] | ||||
} | } | ||||
mail_footer = "frappe.core.doctype.outgoing_email_settings.outgoing_email_settings.get_mail_footer" |
@@ -10,8 +10,8 @@ import os, json | |||||
import frappe | import frappe | ||||
import frappe.database | import frappe.database | ||||
import getpass | import getpass | ||||
from frappe import _ | |||||
from frappe.model.db_schema import DbManager | from frappe.model.db_schema import DbManager | ||||
import frappe.website.sync | |||||
from frappe.model.sync import sync_for | from frappe.model.sync import sync_for | ||||
from frappe.utils.fixtures import sync_fixtures | from frappe.utils.fixtures import sync_fixtures | ||||
@@ -116,20 +116,22 @@ def install_app(name, verbose=False, set_as_patched=True): | |||||
for after_install in app_hooks.after_install or []: | for after_install in app_hooks.after_install or []: | ||||
frappe.get_attr(after_install)() | frappe.get_attr(after_install)() | ||||
sync_fixtures() | |||||
print "Installing Fixtures..." | |||||
sync_fixtures(name) | |||||
frappe.flags.in_install_app = False | frappe.flags.in_install_app = False | ||||
def add_to_installed_apps(app_name, rebuild_sitemap=True): | |||||
def add_to_installed_apps(app_name, rebuild_website=True): | |||||
installed_apps = frappe.get_installed_apps() | installed_apps = frappe.get_installed_apps() | ||||
if not app_name in installed_apps: | if not app_name in installed_apps: | ||||
installed_apps.append(app_name) | installed_apps.append(app_name) | ||||
frappe.db.set_global("installed_apps", json.dumps(installed_apps)) | frappe.db.set_global("installed_apps", json.dumps(installed_apps)) | ||||
frappe.db.commit() | frappe.db.commit() | ||||
if rebuild_sitemap: | |||||
from frappe.website.doctype.website_template.website_template import rebuild_website_template | |||||
rebuild_website_template() | |||||
if rebuild_website: | |||||
frappe.website.sync.sync() | |||||
frappe.db.commit() | |||||
frappe.clear_cache() | frappe.clear_cache() | ||||
@@ -292,7 +292,7 @@ class BaseDocument(object): | |||||
return | return | ||||
for df in self.meta.get_select_fields(): | 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 | continue | ||||
options = (df.options or "").split("\n") | options = (df.options or "").split("\n") | ||||
@@ -22,7 +22,7 @@ def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None, | |||||
elif isinstance(target_doc, basestring): | elif isinstance(target_doc, basestring): | ||||
target_doc = frappe.get_doc(json.loads(target_doc)) | 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") | target_doc.raise_no_permission_to("create") | ||||
map_doc(source_doc, target_doc, table_maps[source_doc.doctype]) | map_doc(source_doc, target_doc, table_maps[source_doc.doctype]) | ||||
@@ -9,7 +9,6 @@ from __future__ import unicode_literals | |||||
import frappe | import frappe | ||||
import os, sys | import os, sys | ||||
from frappe.modules.import_file import import_file_by_path | from frappe.modules.import_file import import_file_by_path | ||||
from frappe.utils import get_path, cstr | |||||
from frappe.modules.patch_handler import block_user | from frappe.modules.patch_handler import block_user | ||||
def sync_all(force=0, verbose=False): | def sync_all(force=0, verbose=False): | ||||
@@ -23,40 +22,41 @@ def sync_all(force=0, verbose=False): | |||||
frappe.clear_cache() | frappe.clear_cache() | ||||
def sync_for(app_name, force=0, sync_everything = False, verbose=False): | def sync_for(app_name, force=0, sync_everything = False, verbose=False): | ||||
files = [] | |||||
for module_name in frappe.local.app_modules.get(app_name) or []: | for module_name in frappe.local.app_modules.get(app_name) or []: | ||||
folder = os.path.dirname(frappe.get_module(app_name + "." + module_name).__file__) | folder = os.path.dirname(frappe.get_module(app_name + "." + module_name).__file__) | ||||
walk_and_sync(folder, force, sync_everything, verbose=verbose) | |||||
files += get_doc_files(folder, force, sync_everything, verbose=verbose) | |||||
def walk_and_sync(start_path, force=0, sync_everything = False, verbose=False): | |||||
"""walk and sync all doctypes and pages""" | |||||
modules = [] | |||||
document_type = ['doctype', 'page', 'report', 'print_format'] | |||||
l = len(files) | |||||
if l: | |||||
for i, doc_path in enumerate(files): | |||||
if import_file_by_path(doc_path, force=force) and verbose: | |||||
complete = int(float(i+1) / l * 40) | |||||
sys.stdout.write("\rSyncing {0}: [{1}{2}]".format(app_name, "="*complete, " "*(40-complete))) | |||||
sys.stdout.flush() | |||||
#print module_name + ' | ' + doctype + ' | ' + name | |||||
for path, folders, files in os.walk(start_path): | |||||
# sort folders so that doctypes are synced before pages or reports | |||||
frappe.db.commit() | |||||
for dontwalk in (".git", "locale", "public"): | |||||
if dontwalk in folders: | |||||
folders.remove(dontwalk) | |||||
print "" | |||||
folders.sort() | |||||
if sync_everything or (os.path.basename(os.path.dirname(path)) in document_type): | |||||
for f in files: | |||||
f = cstr(f) | |||||
if f.endswith(".json"): | |||||
doc_name = f.split(".json")[0] | |||||
if doc_name == os.path.basename(path): | |||||
def get_doc_files(start_path, force=0, sync_everything = False, verbose=False): | |||||
"""walk and sync all doctypes and pages""" | |||||
module_name = path.split(os.sep)[-3] | |||||
doctype = path.split(os.sep)[-2] | |||||
name = path.split(os.sep)[-1] | |||||
out = [] | |||||
document_type = ['doctype', 'page', 'report', 'print_format'] | |||||
for doctype in document_type: | |||||
doctype_path = os.path.join(start_path, doctype) | |||||
if os.path.exists(doctype_path): | |||||
if import_file_by_path(os.path.join(path, f), force=force) and verbose: | |||||
print module_name + ' | ' + doctype + ' | ' + name | |||||
# Note: sorted is a hack because custom* and doc* need | |||||
# be synced first | |||||
frappe.db.commit() | |||||
for docname in sorted(os.listdir(doctype_path)): | |||||
if os.path.isdir(os.path.join(doctype_path, docname)): | |||||
doc_path = os.path.join(doctype_path, docname, docname) + ".json" | |||||
if os.path.exists(doc_path): | |||||
out.append(doc_path) | |||||
return modules | |||||
return out |
@@ -44,13 +44,26 @@ def export_doc(doctype, name, module=None): | |||||
def get_doctype_module(doctype): | def get_doctype_module(doctype): | ||||
return frappe.db.get_value('DocType', doctype, 'module') or "core" | return frappe.db.get_value('DocType', doctype, 'module') or "core" | ||||
doctype_python_modules = {} | |||||
def load_doctype_module(doctype, module=None, prefix=""): | def load_doctype_module(doctype, module=None, prefix=""): | ||||
if not module: | if not module: | ||||
module = get_doctype_module(doctype) | module = get_doctype_module(doctype) | ||||
return frappe.get_module(get_module_name(doctype, module, prefix)) | |||||
def get_module_name(doctype, module, prefix=""): | |||||
from frappe.modules import scrub | |||||
app = get_module_app(module) | |||||
key = (app, doctype, prefix) | |||||
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(\ | 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)] |
@@ -41,4 +41,7 @@ frappe.patches.v4_0.fix_attach_field_file_url | |||||
execute:frappe.reset_perms("User") #2014-06-13 | execute:frappe.reset_perms("User") #2014-06-13 | ||||
execute:frappe.db.sql("""delete from `tabUserRole` where ifnull(parentfield, '')=''""") #2014-06-17 | execute:frappe.db.sql("""delete from `tabUserRole` where ifnull(parentfield, '')=''""") #2014-06-17 | ||||
frappe.patches.v4_0.remove_user_owner_custom_field | 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 | execute:frappe.db.sql("""update `tabProperty Setter` set property_type='Text' where property in ('options', 'default')""") #2014-06-20 | ||||
@@ -17,11 +17,11 @@ def execute(): | |||||
frappe.reload_doc("website", "doctype", frappe.scrub(d)) | frappe.reload_doc("website", "doctype", frappe.scrub(d)) | ||||
rename_field_if_exists(d, "parent_website_sitemap", "parent_website_route") | rename_field_if_exists(d, "parent_website_sitemap", "parent_website_route") | ||||
frappe.reload_doc("website", "doctype", "website_template") | |||||
#frappe.reload_doc("website", "doctype", "website_template") | |||||
frappe.reload_doc("website", "doctype", "website_route") | frappe.reload_doc("website", "doctype", "website_route") | ||||
frappe.reload_doc("website", "doctype", "website_route_permission") | frappe.reload_doc("website", "doctype", "website_route_permission") | ||||
rename_field_if_exists("Website Route", "website_sitemap_config", "website_template") | |||||
#rename_field_if_exists("Website Route", "website_sitemap_config", "website_template") | |||||
rename_field_if_exists("Website Route Permission", "website_sitemap", "website_route") | rename_field_if_exists("Website Route Permission", "website_sitemap", "website_route") | ||||
for d in ("blog_category", "blog_post", "web_page", "website_route", "website_group", "post", "user_vote"): | for d in ("blog_category", "blog_post", "web_page", "website_route", "website_group", "post", "user_vote"): | ||||
@@ -1,23 +1,24 @@ | |||||
import frappe | import frappe | ||||
def execute(): | def execute(): | ||||
from frappe.website.doctype.website_template.website_template import \ | |||||
get_pages_and_generators, get_template_controller | |||||
frappe.reload_doc("website", "doctype", "website_template") | |||||
frappe.reload_doc("website", "doctype", "website_route") | |||||
for app in frappe.get_installed_apps(): | |||||
pages, generators = get_pages_and_generators(app) | |||||
for g in generators: | |||||
doctype = frappe.get_attr(get_template_controller(app, g["path"], g["fname"]) + ".doctype") | |||||
module = frappe.db.get_value("DocType", doctype, "module") | |||||
frappe.reload_doc(frappe.scrub(module), "doctype", frappe.scrub(doctype)) | |||||
frappe.db.sql("""update `tabBlog Category` set `title`=`name` where ifnull(`title`, '')=''""") | |||||
frappe.db.sql("""update `tabWebsite Route` set idx=null""") | |||||
for doctype in ["Blog Category", "Blog Post", "Web Page", "Website Group"]: | |||||
frappe.db.sql("""update `tab{}` set idx=null""".format(doctype)) | |||||
from frappe.website.doctype.website_template.website_template import rebuild_website_template | |||||
rebuild_website_template() | |||||
pass | |||||
# from frappe.website.doctype.website_template.website_template import \ | |||||
# get_pages_and_generators, get_template_controller | |||||
# | |||||
# frappe.reload_doc("website", "doctype", "website_template") | |||||
# frappe.reload_doc("website", "doctype", "website_route") | |||||
# | |||||
# for app in frappe.get_installed_apps(): | |||||
# pages, generators = get_pages_and_generators(app) | |||||
# for g in generators: | |||||
# doctype = frappe.get_attr(get_template_controller(app, g["path"], g["fname"]) + ".doctype") | |||||
# module = frappe.db.get_value("DocType", doctype, "module") | |||||
# frappe.reload_doc(frappe.scrub(module), "doctype", frappe.scrub(doctype)) | |||||
# | |||||
# frappe.db.sql("""update `tabBlog Category` set `title`=`name` where ifnull(`title`, '')=''""") | |||||
# frappe.db.sql("""update `tabWebsite Route` set idx=null""") | |||||
# for doctype in ["Blog Category", "Blog Post", "Web Page", "Website Group"]: | |||||
# frappe.db.sql("""update `tab{}` set idx=null""".format(doctype)) | |||||
# | |||||
# from frappe.website.doctype.website_template.website_template import rebuild_website_template | |||||
# rebuild_website_template() |
@@ -6,9 +6,9 @@ from __future__ import unicode_literals | |||||
import frappe | import frappe | ||||
def execute(): | def execute(): | ||||
frappe.db.sql("""update `tabWebsite Route` ws set ref_doctype=(select wsc.ref_doctype | |||||
from `tabWebsite Template` wsc where wsc.name=ws.website_template) | |||||
where ifnull(page_or_generator, '')!='Page'""") | |||||
# frappe.db.sql("""update `tabWebsite Route` ws set ref_doctype=(select wsc.ref_doctype | |||||
# from `tabWebsite Template` wsc where wsc.name=ws.website_template) | |||||
# where ifnull(page_or_generator, '')!='Page'""") | |||||
frappe.reload_doc("website", "doctype", "website_settings") | frappe.reload_doc("website", "doctype", "website_settings") | ||||
@@ -32,7 +32,6 @@ | |||||
"public/css/tag-it.css", | "public/css/tag-it.css", | ||||
"public/css/bootstrap.css", | "public/css/bootstrap.css", | ||||
"public/css/bootstrap-responsive.css", | |||||
"public/css/font-awesome.css", | "public/css/font-awesome.css", | ||||
"public/css/desk.css", | "public/css/desk.css", | ||||
"public/css/appframe.css", | "public/css/appframe.css", | ||||
@@ -67,6 +66,9 @@ | |||||
"public/js/frappe/router.js", | "public/js/frappe/router.js", | ||||
"public/js/frappe/desk.js", | "public/js/frappe/desk.js", | ||||
"public/js/frappe/defaults.js", | "public/js/frappe/defaults.js", | ||||
"public/js/lib/microtemplate.js", | |||||
"public/html/print_template.html", | |||||
"public/js/legacy/globals.js", | "public/js/legacy/globals.js", | ||||
"public/js/legacy/datatype.js", | "public/js/legacy/datatype.js", | ||||
@@ -0,0 +1,17 @@ | |||||
<!DOCTYPE html> | |||||
<html lang="en"> | |||||
<head> | |||||
<meta charset="utf-8"> | |||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||||
<meta name="description" content=""> | |||||
<meta name="author" content=""> | |||||
<title>{%= title %}</title> | |||||
<link href="{%= frappe.urllib.get_base_url() %}/assets/frappe/css/bootstrap.css" rel="stylesheet"> | |||||
</head> | |||||
<body> | |||||
<div class="container"> | |||||
{%= content %} | |||||
</div> | |||||
</body> | |||||
</html> |
@@ -6,6 +6,11 @@ frappe.ui.form.Grid = Class.extend({ | |||||
$.extend(this, opts); | $.extend(this, opts); | ||||
this.fieldinfo = {}; | this.fieldinfo = {}; | ||||
this.doctype = this.df.options; | this.doctype = this.df.options; | ||||
this.template = null; | |||||
if(this.frm.meta.__form_grid_templates | |||||
&& this.frm.meta.__form_grid_templates[this.df.fieldname]) { | |||||
this.template = this.frm.meta.__form_grid_templates[this.df.fieldname]; | |||||
} | |||||
this.is_grid = true; | this.is_grid = true; | ||||
}, | }, | ||||
make: function() { | make: function() { | ||||
@@ -61,7 +66,7 @@ frappe.ui.form.Grid = Class.extend({ | |||||
if(!force && this.data_rows_are_same(data)) { | if(!force && this.data_rows_are_same(data)) { | ||||
// soft refresh | // soft refresh | ||||
this.header_row.refresh(); | |||||
this.header_row && this.header_row.refresh(); | |||||
for(var i in this.grid_rows) { | for(var i in this.grid_rows) { | ||||
this.grid_rows[i].refresh(); | this.grid_rows[i].refresh(); | ||||
} | } | ||||
@@ -261,38 +266,27 @@ frappe.ui.form.GridRow = Class.extend({ | |||||
}, | }, | ||||
make_static_display: function() { | make_static_display: function() { | ||||
var me = this; | var me = this; | ||||
this.make_static_display_template(); | |||||
this.row.empty(); | this.row.empty(); | ||||
$('<div class="col col-xs-1 row-index">' + (this.doc ? this.doc.idx : "#")+ '</div>') | |||||
$('<div class="col-xs-1 row-index">' + (this.doc ? this.doc.idx : "#")+ '</div>') | |||||
.appendTo(this.row); | .appendTo(this.row); | ||||
for(var ci in this.static_display_template) { | |||||
var df = this.static_display_template[ci][0]; | |||||
var colsize = this.static_display_template[ci][1]; | |||||
var txt = this.doc ? | |||||
frappe.format(this.doc[df.fieldname], df, null, this.doc) : | |||||
__(df.label); | |||||
if(this.doc && df.fieldtype === "Select") { | |||||
txt = __(txt); | |||||
} | |||||
var add_class = (["Text", "Small Text"].indexOf(df.fieldtype)===-1) ? | |||||
" grid-overflow-ellipsis" : " grid-overflow-no-ellipsis"; | |||||
add_class += (["Int", "Currency", "Float"].indexOf(df.fieldtype)!==-1) ? | |||||
" text-right": ""; | |||||
$col = $('<div class="col col-xs-'+colsize+add_class+'"></div>') | |||||
.html(txt) | |||||
.attr("data-fieldname", df.fieldname) | |||||
.data("df", df) | |||||
.appendTo(this.row) | |||||
if(!this.doc) $col.css({"font-weight":"bold"}) | |||||
if(this.grid.template) { | |||||
$('<div class="col-xs-10">').appendTo(this.row) | |||||
.html(frappe.render(this.grid.template, {doc:this.doc, frm:this.frm, grid_row: this})); | |||||
} else { | |||||
this.add_visible_columns(); | |||||
} | } | ||||
// TODO find a better solution | |||||
// append button column | |||||
this.add_buttons(); | |||||
$(this.frm.wrapper).trigger("grid-row-render", [this]); | |||||
}, | |||||
add_buttons: function() { | |||||
var me = this; | |||||
if(this.doc && this.grid.is_editable()) { | if(this.doc && this.grid.is_editable()) { | ||||
if(!this.grid.$row_actions) { | if(!this.grid.$row_actions) { | ||||
this.grid.$row_actions = $('<div class="col-md-1 pull-right" \ | |||||
this.grid.$row_actions = $('<div class="col-xs-1 pull-right" \ | |||||
style="text-align: right; padding-right: 5px;">\ | style="text-align: right; padding-right: 5px;">\ | ||||
<span class="text-success grid-insert-row" style="padding: 4px;">\ | <span class="text-success grid-insert-row" style="padding: 4px;">\ | ||||
<i class="icon icon-plus-sign"></i></span>\ | <i class="icon icon-plus-sign"></i></span>\ | ||||
@@ -311,8 +305,34 @@ frappe.ui.form.GridRow = Class.extend({ | |||||
} | } | ||||
} | } | ||||
$(this.frm.wrapper).trigger("grid-row-render", [this]); | |||||
}, | }, | ||||
add_visible_columns: function() { | |||||
this.make_static_display_template(); | |||||
for(var ci in this.static_display_template) { | |||||
var df = this.static_display_template[ci][0]; | |||||
var colsize = this.static_display_template[ci][1]; | |||||
var txt = this.doc ? | |||||
frappe.format(this.doc[df.fieldname], df, null, this.doc) : | |||||
__(df.label); | |||||
if(this.doc && df.fieldtype === "Select") { | |||||
txt = __(txt); | |||||
} | |||||
var add_class = (["Text", "Small Text"].indexOf(df.fieldtype)===-1) ? | |||||
" grid-overflow-ellipsis" : " grid-overflow-no-ellipsis"; | |||||
add_class += (["Int", "Currency", "Float"].indexOf(df.fieldtype)!==-1) ? | |||||
" text-right": ""; | |||||
$col = $('<div class="col col-xs-'+colsize+add_class+'"></div>') | |||||
.html(txt) | |||||
.attr("data-fieldname", df.fieldname) | |||||
.data("df", df) | |||||
.appendTo(this.row) | |||||
if(!this.doc) $col.css({"font-weight":"bold"}) | |||||
} | |||||
}, | |||||
make_static_display_template: function() { | make_static_display_template: function() { | ||||
if(this.static_display_template) return; | if(this.static_display_template) return; | ||||
@@ -443,55 +463,6 @@ frappe.ui.form.GridRow = Class.extend({ | |||||
$.extend(me.fields_dict[fieldname], fi); | $.extend(me.fields_dict[fieldname], fi); | ||||
}) | }) | ||||
// var me = this, | |||||
// make_row = function(label) { | |||||
// if(label) | |||||
// $('<div><h4 style="margin-bottom: 0px;"><b>'+ label +'</b></h4>\ | |||||
// <hr style="margin-top: 10px;"></div>') | |||||
// .appendTo(me.form_area); | |||||
// | |||||
// var row = $('<div class="row">') | |||||
// .appendTo(me.form_area); | |||||
// | |||||
// var col_spans = 6; | |||||
// if(row.parents(".form-column:first").hasClass("col-md-6")) | |||||
// col_spans = 12; | |||||
// | |||||
// var col1 = $('<div class="col-md-'+col_spans+'"></div>').appendTo(row), | |||||
// col2 = $('<div class="col-md-'+col_spans+'"></div>').appendTo(row); | |||||
// | |||||
// return [col1, col2]; | |||||
// }, | |||||
// cols = make_row(), | |||||
// cnt = 0; | |||||
// | |||||
// $.each(me.docfields, function(ci, df) { | |||||
// if(!df.hidden) { | |||||
// if(df.fieldtype=="Section Break") { | |||||
// cols = make_row(df.label); | |||||
// cnt = 0; | |||||
// return; | |||||
// } | |||||
// var fieldwrapper = $('<div>') | |||||
// .appendTo(cols[cnt % 2]) | |||||
// var fieldobj = make_field(df, me.parent_df.options, | |||||
// fieldwrapper.get(0), me.frm); | |||||
// fieldobj.docname = me.doc.name; | |||||
// fieldobj.refresh(); | |||||
// fieldobj.input && | |||||
// $(fieldobj.input).css({"max-height": "100px"}); | |||||
// | |||||
// // set field properties | |||||
// // used for setting custom get queries in links | |||||
// if(me.grid.fieldinfo[df.fieldname]) | |||||
// $.extend(fieldobj, me.grid.fieldinfo[df.fieldname]); | |||||
// | |||||
// me.fields.push(fieldobj); | |||||
// me.fields_dict[df.fieldname] = fieldobj; | |||||
// cnt++; | |||||
// } | |||||
// }); | |||||
this.toggle_add_delete_button_display(this.wrapper.find(".panel:first")); | this.toggle_add_delete_button_display(this.wrapper.find(".panel:first")); | ||||
this.grid.open_grid_row = this; | this.grid.open_grid_row = this; | ||||
@@ -41,7 +41,6 @@ frappe.ui.set_user_background = function(src, selector, style) { | |||||
frappe.dom.set_style(repl('%(selector)s { \ | frappe.dom.set_style(repl('%(selector)s { \ | ||||
background: url("%(src)s") center center;\ | background: url("%(src)s") center center;\ | ||||
background-attachment: fixed; \ | background-attachment: fixed; \ | ||||
background-size: 100%; \ | |||||
%(style)s \ | %(style)s \ | ||||
}', {src:src, selector:selector, style: style==="Fill Screen" ? "background-size: cover;" : ""})); | }', {src:src, selector:selector, style: style==="Fill Screen" ? "background-size: cover;" : ""})); | ||||
} | } | ||||
@@ -247,5 +247,5 @@ frappe.utils = { | |||||
var dataURL = canvas.toDataURL("image/jpeg"); | var dataURL = canvas.toDataURL("image/jpeg"); | ||||
setTimeout(function() { callback(dataURL); }, 10 ); | setTimeout(function() { callback(dataURL); }, 10 ); | ||||
} | } | ||||
} | |||||
}, | |||||
}; | }; |
@@ -1,8 +1,8 @@ | |||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | ||||
// MIT License. See license.txt | |||||
// MIT License. See license.txt | |||||
// provide a namespace | // provide a namespace | ||||
if(!window.frappe) | |||||
if(!window.frappe) | |||||
window.frappe = {}; | window.frappe = {}; | ||||
frappe.provide = function(namespace) { | frappe.provide = function(namespace) { | ||||
// docs: create a namespace // | // docs: create a namespace // | ||||
@@ -22,4 +22,5 @@ frappe.provide("locals"); | |||||
frappe.provide("frappe.settings"); | frappe.provide("frappe.settings"); | ||||
frappe.provide("frappe.utils"); | frappe.provide("frappe.utils"); | ||||
frappe.provide("frappe.ui"); | frappe.provide("frappe.ui"); | ||||
frappe.provide("frappe.modules"); | |||||
frappe.provide("frappe.modules"); | |||||
frappe.provide("frappe.templates"); |
@@ -71,6 +71,7 @@ frappe.request.call = function(opts) { | |||||
500: function() { | 500: function() { | ||||
msgprint(__("Server Error: Please check your server logs or contact tech support.")) | msgprint(__("Server Error: Please check your server logs or contact tech support.")) | ||||
opts.error && opts.error(); | opts.error && opts.error(); | ||||
} | } | ||||
}, | }, | ||||
async: opts.async | async: opts.async | ||||
@@ -144,7 +145,6 @@ frappe.request.prepare = function(opts) { | |||||
} | } | ||||
frappe.request.cleanup = function(opts, r) { | frappe.request.cleanup = function(opts, r) { | ||||
// stop button indicator | // stop button indicator | ||||
if(opts.btn) $(opts.btn).done_working(); | if(opts.btn) $(opts.btn).done_working(); | ||||
@@ -259,12 +259,19 @@ frappe.views.DocListView = frappe.ui.Listing.extend({ | |||||
this.appframe.add_icon_btn("2", "icon-shield", | this.appframe.add_icon_btn("2", "icon-shield", | ||||
__("User Permissions Manager"), function() { | __("User Permissions Manager"), function() { | ||||
frappe.route_options = { | frappe.route_options = { | ||||
property: me.doctype | |||||
doctype: me.doctype | |||||
}; | }; | ||||
frappe.set_route("user-permissions"); | frappe.set_route("user-permissions"); | ||||
}); | }); | ||||
} | } | ||||
if(in_list(user_roles, "System Manager")) { | if(in_list(user_roles, "System Manager")) { | ||||
this.appframe.add_icon_btn("2", "icon-lock", | |||||
__("Role Permissions Manager"), function() { | |||||
frappe.route_options = { | |||||
doctype: me.doctype | |||||
}; | |||||
frappe.set_route("permission-manager"); | |||||
}); | |||||
this.appframe.add_icon_btn("2", "icon-glass", __("Customize"), function() { | this.appframe.add_icon_btn("2", "icon-glass", __("Customize"), function() { | ||||
frappe.set_route("Form", "Customize Form", { | frappe.set_route("Form", "Customize Form", { | ||||
doctype: me.doctype | doctype: me.doctype | ||||
@@ -107,8 +107,9 @@ frappe.views.QueryReport = Class.extend({ | |||||
}, | }, | ||||
callback: function(r) { | callback: function(r) { | ||||
me.appframe.set_title(__("Query Report")+": " + __(me.report_name)); | me.appframe.set_title(__("Query Report")+": " + __(me.report_name)); | ||||
frappe.dom.eval(r.message || ""); | |||||
frappe.dom.eval(r.message.script || ""); | |||||
me.setup_filters(); | me.setup_filters(); | ||||
me.setup_html_format(r.message.html_format); | |||||
me.refresh(); | me.refresh(); | ||||
} | } | ||||
}); | }); | ||||
@@ -124,6 +125,37 @@ frappe.views.QueryReport = Class.extend({ | |||||
this.wrapper.find(".no-report-area").html(msg).toggle(true); | this.wrapper.find(".no-report-area").html(msg).toggle(true); | ||||
} | } | ||||
}, | }, | ||||
setup_html_format: function(html_format) { | |||||
var me = this; | |||||
if(html_format) { | |||||
this.appframe.add_primary_action(__('Print'), function() { | |||||
if(!me.data) { | |||||
msgprint(__("Run the report first")); | |||||
return; | |||||
} | |||||
var data = []; | |||||
$.each(me.data, function(i, d) { | |||||
var newd = {}; data.push(newd); | |||||
$.each(d, function(k, v) { | |||||
newd[k.replace(/ /g, "_").toLowerCase()] = v; }); | |||||
}); | |||||
var content = frappe.render(html_format, | |||||
{data: data, filters:me.get_values(), report:me}); | |||||
var html = frappe.render(frappe.templates.print_template, { | |||||
title: __(me.report_name), content: content | |||||
}); | |||||
var w = window.open(); | |||||
w.document.write(html); | |||||
w.document.close(); | |||||
}, "icon-print"); | |||||
} | |||||
}, | |||||
setup_filters: function() { | setup_filters: function() { | ||||
this.clear_filters(); | this.clear_filters(); | ||||
var me = this; | var me = this; | ||||
@@ -6,6 +6,8 @@ frappe.utils.full_name = function(fn, ln) { | |||||
} | } | ||||
function fmt_money(v, format){ | function fmt_money(v, format){ | ||||
// deprecated! | |||||
// for backward compatibility | |||||
return format_number(v, format); | return format_number(v, format); | ||||
} | } | ||||
@@ -0,0 +1,34 @@ | |||||
// Simple JavaScript Templating | |||||
// Adapted from John Resig - http://ejohn.org/ - MIT Licensed | |||||
frappe.template = {compiled: {}, debug:{}}; | |||||
frappe.template.compile = function(str) { | |||||
if(str.indexOf("'")!==-1) { | |||||
console.log("Warning: Single quotes (') may not work in templates"); | |||||
} | |||||
if(!frappe.template.compiled[str]) { | |||||
fn_str = "var p=[],print=function(){p.push.apply(p,arguments)};" + | |||||
// Introduce the data as local variables using with(){} | |||||
"with(obj){p.push('" + | |||||
// Convert the template into pure JavaScript | |||||
str | |||||
.replace(/[\r\t\n]/g, " ") | |||||
.split("{%").join("\t") | |||||
.replace(/((^|%})[^\t]*)'/g, "$1\r") | |||||
.replace(/\t=(.*?)%}/g, "',$1,'") | |||||
.split("\t").join("');") | |||||
.split("%}").join("p.push('") | |||||
.split("\r").join("\\'") | |||||
+ "');}return p.join('');"; | |||||
frappe.template.debug[str] = fn_str; | |||||
frappe.template.compiled[str] = new Function("obj", fn_str); | |||||
} | |||||
return frappe.template.compiled[str]; | |||||
}; | |||||
frappe.render = function(str, data) { | |||||
return frappe.template.compile(str)(data); | |||||
}; |
@@ -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"> | <link rel="icon" href="{{ favicon or "" }}" type="image/x-icon"> | ||||
{%- block head_include %}{% endblock -%} | {%- block head_include %}{% endblock -%} | ||||
{%- block head -%} | {%- 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 %} | {%- for link in web_include_css %} | ||||
<link type="text/css" rel="stylesheet" href="{{ link }}"> | <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="container"> | ||||
<div class="row"> | <div class="row"> | ||||
<div class="col-sm-9 page-header-left"> | <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"> | <div data-html-block="header"> | ||||
{%- if header is defined -%}{{ header }}{%- endif -%} | {%- if header is defined -%}{{ header }}{%- endif -%} | ||||
</div> | </div> | ||||
<div class="page-breadcrumbs" data-html-block="breadcrumbs"> | <div class="page-breadcrumbs" data-html-block="breadcrumbs"> | ||||
{%- if breadcrumbs is defined -%}{{ breadcrumbs }}{%- endif -%} | {%- if breadcrumbs is defined -%}{{ breadcrumbs }}{%- endif -%} | ||||
</div> | </div> | ||||
<!-- <a class="visible-xs toggle-sidebar no-decoration pull-right"> | |||||
<i class="icon-chevron-down"></i> | |||||
</a> --> | |||||
</div> | </div> | ||||
<div class="col-sm-3 text-right"> | <div class="col-sm-3 text-right"> | ||||
<div class="page-header-right"></div> | <div class="page-header-right"></div> | ||||
@@ -2,4 +2,4 @@ | |||||
<p>You have a new message from: <b>{{ from }}</b></p> | <p>You have a new message from: <b>{{ from }}</b></p> | ||||
<p>{{ message }}</p> | <p>{{ message }}</p> | ||||
<hr> | <hr> | ||||
<p><a href="{{ link }}">Login and view in Browser</a></p> | |||||
<p><a href="{{ link }}">Login and view in Browser</a></p> |
@@ -2,9 +2,9 @@ | |||||
<p>Dear {{ first_name }}{% if last_name %} {{ last_name}}{% endif %},</p> | <p>Dear {{ first_name }}{% if last_name %} {{ last_name}}{% endif %},</p> | ||||
<p>A new account has been created for you.</p> | <p>A new account has been created for you.</p> | ||||
<p>Your login id is: <b>{{ user }}</b> | <p>Your login id is: <b>{{ user }}</b> | ||||
<p>Click on the button below to complete your registration and set a new password.</p> | |||||
<p><a class="btn-primary" href="{{ link }}">Complete Registration</a></p> | |||||
<p>Click on the link below to complete your registration and set a new password.</p> | |||||
<p><b><a href="{{ link }}">Complete Registration</a></b></p> | |||||
<br> | <br> | ||||
<p>You can also copy-paste this link in your browser <a href="{{ link }}">{{ link }}</a></p> | <p>You can also copy-paste this link in your browser <a href="{{ link }}">{{ link }}</a></p> | ||||
<p>Thank you,<br> | <p>Thank you,<br> | ||||
{{ user_fullname }}</p> | |||||
{{ user_fullname }}</p> |
@@ -4,273 +4,16 @@ | |||||
<meta name="viewport" content="width=device-width" /> | <meta name="viewport" content="width=device-width" /> | ||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
<title>{{ subject or "" }}</title> | <title>{{ subject or "" }}</title> | ||||
<style> | |||||
/* ------------------------------------- | |||||
GLOBAL | |||||
------------------------------------- */ | |||||
img { | |||||
max-width: 100%; | |||||
} | |||||
body { | |||||
width: 100% !important; | |||||
height: 100%; | |||||
} | |||||
.wrapper { | |||||
background-color: #eee; | |||||
} | |||||
.wrapper * { | |||||
margin:0; | |||||
padding:0; | |||||
font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; | |||||
font-size: 100%; | |||||
line-height: 1.6; | |||||
} | |||||
/* ------------------------------------- | |||||
ELEMENTS | |||||
------------------------------------- */ | |||||
.wrapper a { | |||||
color: #348eda; | |||||
} | |||||
.btn-primary{ | |||||
text-decoration:none; | |||||
color: #FFF !important; | |||||
background-color: #348eda; | |||||
border:solid #348eda; | |||||
border-width:10px 20px; | |||||
line-height:2; | |||||
font-weight:bold; | |||||
margin-right:10px; | |||||
text-align:center; | |||||
cursor:pointer; | |||||
display: inline-block; | |||||
border-radius: 25px; | |||||
} | |||||
.btn-secondary { | |||||
text-decoration:none; | |||||
color: #FFF !important; | |||||
background-color: #aaa; | |||||
border:solid #aaa; | |||||
border-width:10px 20px; | |||||
line-height:2; | |||||
font-weight:bold; | |||||
margin-right:10px; | |||||
text-align:center; | |||||
cursor:pointer; | |||||
display: inline-block; | |||||
border-radius: 25px; | |||||
} | |||||
.last { | |||||
margin-bottom: 0; | |||||
} | |||||
.first { | |||||
margin-top: 0; | |||||
} | |||||
.padding{ | |||||
padding:10px 0; | |||||
} | |||||
.left-padding { | |||||
padding-left: 10px; | |||||
} | |||||
.breadcrumb { | |||||
list-style: none; | |||||
} | |||||
.breadcrumb > li { | |||||
display: inline-block; | |||||
margin-left: 0px; | |||||
margin-right: 5px; | |||||
} | |||||
/* ------------------------------------- | |||||
BODY | |||||
------------------------------------- */ | |||||
table.body-wrap { | |||||
width: 100%; | |||||
padding: 10px; | |||||
} | |||||
table.body-wrap .container{ | |||||
border: 1px solid #f0f0f0; | |||||
} | |||||
/* ------------------------------------- | |||||
FOOTER | |||||
------------------------------------- */ | |||||
table.footer-wrap { | |||||
width: 100%; | |||||
clear:both!important; | |||||
} | |||||
.footer-wrap .container p { | |||||
font-size:12px; | |||||
color:#666; | |||||
} | |||||
table.footer-wrap a{ | |||||
color: #999; | |||||
} | |||||
/* ------------------------------------- | |||||
TYPOGRAPHY | |||||
------------------------------------- */ | |||||
.wrapper h1, | |||||
.wrapper h2, | |||||
.wrapper h3{ | |||||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; | |||||
line-height: 1.1; | |||||
margin-top:15px; | |||||
margin-bottom:15px; | |||||
color:#000; | |||||
line-height: 1.2; | |||||
font-weight:200; | |||||
} | |||||
.wrapper h1 { | |||||
font-size: 36px; | |||||
} | |||||
.wrapper h2 { | |||||
font-size: 28px; | |||||
} | |||||
.wrapper h3 { | |||||
font-size: 22px; | |||||
} | |||||
.wrapper hr { | |||||
margin: 20px 0; | |||||
border-top: 1px solid #eee; | |||||
} | |||||
.wrapper p, | |||||
.wrapper ul, | |||||
.wrapper ol { | |||||
margin-bottom: 10px; | |||||
font-weight: normal; | |||||
font-size:14px; | |||||
} | |||||
.wrapper ul li, | |||||
.wrapper ol li { | |||||
margin-left:5px; | |||||
list-style-position: inside; | |||||
} | |||||
/* --------------------------------------------------- | |||||
RESPONSIVENESS | |||||
Nuke it from orbit. It's the only way to be sure. | |||||
------------------------------------------------------ */ | |||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */ | |||||
.container { | |||||
display:block!important; | |||||
max-width:600px!important; | |||||
margin:0 auto!important; /* makes it centered */ | |||||
clear:both!important; | |||||
} | |||||
/* Set the padding on the td rather than the div for Outlook compatibility */ | |||||
.body-wrap .container{ | |||||
padding:20px; | |||||
} | |||||
/* This should also be a block element, so that it will fill 100% of the .container */ | |||||
.content { | |||||
max-width:600px; | |||||
margin:0 auto; | |||||
display:block; | |||||
} | |||||
/* Let's make sure tables in the content area are 100% wide */ | |||||
.content table { | |||||
width: 100%; | |||||
} | |||||
a.no-decoration { | |||||
text-decoration: none; | |||||
color: inherit; | |||||
} | |||||
small, .small { | |||||
font-size: 85%; | |||||
} | |||||
.text-muted { | |||||
color: #999999; | |||||
} | |||||
pre, code { | |||||
font-family: monospace !important; | |||||
} | |||||
</style> | |||||
</head> | </head> | ||||
<body> | <body> | ||||
<div class="wrapper"> | |||||
<!-- body --> | <!-- body --> | ||||
<table class="body-wrap"> | |||||
<tr> | |||||
<td></td> | |||||
<td class="container" bgcolor="#FFFFFF"> | |||||
<!-- content --> | |||||
<div class="content"> | |||||
<table> | |||||
<tr> | |||||
<td> | |||||
{{ content }} | |||||
</td> | |||||
</tr> | |||||
</table> | |||||
</div> | |||||
<!-- /content --> | |||||
</td> | |||||
<td></td> | |||||
</tr> | |||||
</table> | |||||
<!-- /body --> | |||||
<div style="font-family: Helvetica, Arial, sans-serif; font-size: 14px;">{{ content }}</div> | |||||
<!-- footer --> | <!-- footer --> | ||||
<table class="footer-wrap"> | |||||
<tr> | |||||
<td></td> | |||||
<td class="container"> | |||||
<!-- content --> | |||||
<div class="content"> | |||||
<table> | |||||
<tr> | |||||
<td align="center"> | |||||
{{ footer }} | |||||
</td> | |||||
</tr> | |||||
</table> | |||||
</div> | |||||
<!-- /content --> | |||||
<div style="margin-top: 30px; font-family: Helvetica, Arial, sans-serif; font-size: 11px;"> | |||||
{{ footer }}</div> | |||||
</td> | |||||
<td></td> | |||||
</tr> | |||||
</table> | |||||
<!-- /footer --> | <!-- /footer --> | ||||
</div> | |||||
<div class="print-html">{{ print_html or "" }}</div> | <div class="print-html">{{ print_html or "" }}</div> | ||||
</body> | </body> | ||||
</html> | </html> |
@@ -0,0 +1,41 @@ | |||||
{% if(doc) { %} | |||||
<div class="row"> | |||||
<div class="col-sm-4"> | |||||
<p> | |||||
{% if (doc.fieldtype==="Section Break") { %}<strong>{% } %} | |||||
{% if (doc.reqd) { %}<span class="text-danger">{% } %} | |||||
{% if (doc.hidden) { %} | |||||
<span class="text-muted"><i class="icon-eye-close"></i>{% } %} | |||||
{% if(doc.fieldtype==="Link") { %}<i class="icon-link"></i>{% } %} | |||||
{% if(doc.fieldtype==="Table") { %}<i class="icon-th"></i>{% } %} | |||||
{%= doc.label %} | |||||
{% if (doc.hidden) { %}</span>{% } %} | |||||
{% if (doc.reqd) { %}</span>{% } %} | |||||
{% if (doc.fieldtype=="Section Break") { %}</strong>{% } %} | |||||
</p> | |||||
{% if (doc.description) { %}<p class="text-muted small">{%= doc.description %}</p>{% } %} | |||||
</div> | |||||
<div class="col-sm-4"> | |||||
{%= doc.fieldtype %} | |||||
<br><span class="small">{%= doc.fieldname %}</span> | |||||
</div> | |||||
<div class="col-sm-4"> | |||||
{%= doc.options && doc.options.split("\n").join("<br>") || "" %} | |||||
{% if(doc["default"]) { %} | |||||
<br>{%= __("Default") %}: <strong>{%= doc["default"] %}</strong> | |||||
{% } %} | |||||
</div> | |||||
</div> | |||||
{% } else { %} | |||||
<div class="row"> | |||||
<div class="col-sm-4"> | |||||
{%= __("Label") %} | |||||
</div> | |||||
<div class="col-sm-4"> | |||||
{%= __("Field Type") %} | |||||
</div> | |||||
<div class="col-sm-4"> | |||||
{%= __("Options") %} | |||||
</div> | |||||
</div> | |||||
{% } %} |
@@ -1,2 +0,0 @@ | |||||
doctype = "Blog Category" | |||||
no_cache = 1 |
@@ -1,81 +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 frappe.utils import global_date_format, get_fullname, cint | |||||
from frappe.website.utils import find_first_image | |||||
doctype = "Blog Post" | |||||
condition_field = "published" | |||||
sort_by = "published_on" | |||||
sort_order = "desc" | |||||
def get_context(context): | |||||
blog_post = context.doc | |||||
# this is for double precaution. usually it wont reach this code if not published | |||||
if not cint(blog_post.published): | |||||
raise Exception, "This blog has not been published yet!" | |||||
# temp fields | |||||
blog_post.full_name = get_fullname(blog_post.owner) | |||||
blog_post.updated = global_date_format(blog_post.published_on) | |||||
if blog_post.blogger: | |||||
blog_post.blogger_info = frappe.get_doc("Blogger", blog_post.blogger).as_dict() | |||||
blog_post.description = blog_post.blog_intro or blog_post.content[:140] | |||||
blog_post.metatags = { | |||||
"name": blog_post.title, | |||||
"description": blog_post.description, | |||||
} | |||||
image = find_first_image(blog_post.content) | |||||
if image: | |||||
blog_post.metatags["image"] = image | |||||
blog_post.categories = frappe.db.sql_list("select name from `tabBlog Category` order by name") | |||||
blog_post.comment_list = frappe.db.sql("""\ | |||||
select comment, comment_by_fullname, creation | |||||
from `tabComment` where comment_doctype="Blog Post" | |||||
and comment_docname=%s order by creation""", (blog_post.name,), as_dict=1) or [] | |||||
return blog_post.__dict__ | |||||
@frappe.whitelist(allow_guest=True) | |||||
def get_blog_list(start=0, by=None, category=None): | |||||
condition = "" | |||||
if by: | |||||
condition = " and t1.blogger='%s'" % by.replace("'", "\'") | |||||
if category: | |||||
condition += " and t1.blog_category='%s'" % category.replace("'", "\'") | |||||
query = """\ | |||||
select | |||||
t1.title, t1.name, t3.name as page_name, t1.published_on as creation, | |||||
day(t1.published_on) as day, monthname(t1.published_on) as month, | |||||
year(t1.published_on) as year, | |||||
ifnull(t1.blog_intro, t1.content) as content, | |||||
t2.full_name, t2.avatar, t1.blogger, | |||||
(select count(name) from `tabComment` where | |||||
comment_doctype='Blog Post' and comment_docname=t1.name) as comments | |||||
from `tabBlog Post` t1, `tabBlogger` t2, `tabWebsite Route` t3 | |||||
where ifnull(t1.published,0)=1 | |||||
and t1.blogger = t2.name | |||||
and t3.docname = t1.name | |||||
and t3.ref_doctype = "Blog Post" | |||||
%(condition)s | |||||
order by published_on desc, name asc | |||||
limit %(start)s, 20""" % {"start": start, "condition": condition} | |||||
result = frappe.db.sql(query, as_dict=1) | |||||
# strip html tags from content | |||||
for res in result: | |||||
res['published'] = global_date_format(res['creation']) | |||||
res['content'] = res['content'][:140] | |||||
return result |
@@ -1,44 +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 frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow | |||||
from frappe.website.utils import find_first_image | |||||
doctype = "Web Page" | |||||
condition_field = "published" | |||||
def get_context(context): | |||||
web_page = frappe._dict(context.doc.as_dict()) | |||||
web_page.main_section = web_page.main_section or "" | |||||
if web_page.slideshow: | |||||
web_page.update(get_slideshow(web_page)) | |||||
if web_page.enable_comments: | |||||
web_page.comment_list = frappe.db.sql("""select | |||||
comment, comment_by_fullname, creation | |||||
from `tabComment` where comment_doctype="Web Page" | |||||
and comment_docname=%s order by creation""", web_page.name, as_dict=1) or [] | |||||
web_page.update({ | |||||
"style": web_page.css or "", | |||||
"script": web_page.javascript or "" | |||||
}) | |||||
web_page.update(context) | |||||
web_page.metatags = { | |||||
"name": web_page.title, | |||||
"description": web_page.description or web_page.main_section[:150] | |||||
} | |||||
image = find_first_image(web_page.main_section) | |||||
if image: | |||||
web_page.metatags["image"] = image | |||||
if not web_page.header: | |||||
web_page.header = web_page.title | |||||
return web_page |
@@ -1,240 +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 frappe.website.permissions import get_access | |||||
from frappe.website.render import can_cache | |||||
from frappe.templates.website_group.forum import get_post_list_html | |||||
doctype = "Website Group" | |||||
no_cache = 1 | |||||
def get_context(context): | |||||
group, view = guess_group_view(context) | |||||
try: | |||||
if not has_access(context.access, view): | |||||
raise frappe.PermissionError | |||||
return get_group_context(group, view, context) | |||||
except frappe.DoesNotExistError: | |||||
return { | |||||
"content": '<div class="alert alert-danger full-page">' | |||||
'The page you are looking for does not exist.</div>' | |||||
} | |||||
except frappe.PermissionError: | |||||
return { | |||||
"content": '<div class="alert alert-danger full-page">' | |||||
'You are not permitted to view this page.</div>' | |||||
} | |||||
def get_group_context(group, view, context): | |||||
cache_key = "website_group_context:{}:{}".format(group, view) | |||||
views = get_views(context.doc.group_type) | |||||
view = frappe._dict(views.get(view)) | |||||
if can_cache(view.no_cache): | |||||
group_context = frappe.cache().get_value(cache_key) | |||||
if group_context: | |||||
return group_context | |||||
group_context = build_group_context(group, view, views, context) | |||||
if can_cache(view.get("no_cache")): | |||||
frappe.cache().set_value(cache_key, group_context) | |||||
return group_context | |||||
def build_group_context(group, view, views, context): | |||||
title = "{} - {}".format(context.doc.group_title, view.get("label")) | |||||
group_context = frappe._dict({ | |||||
"group": context.doc, | |||||
"view": view, | |||||
"views": [v[1] for v in sorted(views.iteritems(), key=lambda (k, v): v.get("idx"))], | |||||
"title": title, | |||||
"pathname": context.pathname | |||||
}) | |||||
group_context.update(build_view_context(group_context)) | |||||
return group_context | |||||
def build_view_context(context): | |||||
from frappe.templates.website_group.post import get_post_context | |||||
if context.view.name in ("popular", "feed", "open", "closed", "upcoming", "past"): | |||||
context.post_list_html = get_post_list_html(context.group.name, context.view.name) | |||||
elif context.view.name == "edit": | |||||
context.post = frappe.get_doc("Post", frappe.form_dict.name).as_dict() | |||||
if context.post.assigned_to: | |||||
context.user = frappe.get_doc("User", context.post.assigned_to) | |||||
elif context.view.name == "settings": | |||||
context.users = frappe.db.sql("""select p.*, wsp.`read`, wsp.`write`, wsp.`admin` | |||||
from `tabUser` p, `tabWebsite Route Permission` wsp | |||||
where wsp.website_route=%s and wsp.user=p.name""", context.pathname, as_dict=True) | |||||
elif context.view.name == "post": | |||||
context.update(get_post_context(context)) | |||||
return context | |||||
def guess_group_view(context): | |||||
group = context.docname | |||||
view = frappe.form_dict.view or get_default_view(context.doc.group_type) | |||||
return group, view | |||||
def get_default_view(group_type): | |||||
for view, opts in get_views(group_type).iteritems(): | |||||
if opts.get("default"): | |||||
return view | |||||
def get_views(group_type=None): | |||||
if group_type: | |||||
group_views = frappe._dict(views[group_type]) | |||||
else: | |||||
group_views = {} | |||||
for group_type in views: | |||||
group_views.update(views[group_type].copy()) | |||||
group_views.update(common_views) | |||||
if group_type == "Forum": | |||||
group_views["post"]["upvote"] = True | |||||
return group_views | |||||
def has_access(access, view): | |||||
if view=="settings": | |||||
return access.get("admin") | |||||
elif view in ("add", "edit"): | |||||
return access.get("write") | |||||
else: | |||||
return access.get("read") | |||||
def clear_cache(path=None, website_group=None): | |||||
from frappe.templates.website_group.post import clear_post_cache | |||||
if path: | |||||
website_groups = [frappe.db.get_value("Website Route", path, "docname")] | |||||
elif website_group: | |||||
website_groups = [website_group] | |||||
else: | |||||
clear_post_cache() | |||||
website_groups = frappe.db.sql_list("""select name from `tabWebsite Group`""") | |||||
cache = frappe.cache() | |||||
all_views = get_views() | |||||
for group in website_groups: | |||||
for view in all_views: | |||||
cache.delete_value("website_group_context:{}:{}".format(group, view)) | |||||
def clear_event_cache(): | |||||
for group in frappe.db.sql_list("""select name from `tabWebsite Group` where group_type='Event'"""): | |||||
clear_cache(website_group=group) | |||||
def clear_cache_on_doc_event(doc, method, *args, **kwargs): | |||||
clear_cache(path=doc.website_route, website_group=doc.website_group) | |||||
def get_pathname(group): | |||||
return frappe.db.get_value("Website Route", {"ref_doctype": "Website Group", | |||||
"docname": group}) | |||||
views = { | |||||
"Forum": { | |||||
"popular": { | |||||
"name": "popular", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Popular", | |||||
"icon": "icon-heart", | |||||
"default": True, | |||||
"upvote": True, | |||||
"idx": 1 | |||||
}, | |||||
"feed": { | |||||
"name": "feed", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Feed", | |||||
"icon": "icon-rss", | |||||
"upvote": True, | |||||
"idx": 2 | |||||
} | |||||
}, | |||||
"Tasks": { | |||||
"open": { | |||||
"name": "open", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Open", | |||||
"icon": "icon-inbox", | |||||
"default": True, | |||||
"upvote": True, | |||||
"idx": 1 | |||||
}, | |||||
"closed": { | |||||
"name": "closed", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Closed", | |||||
"icon": "icon-smile", | |||||
"idx": 2 | |||||
} | |||||
}, | |||||
"Events": { | |||||
"upcoming": { | |||||
"name": "upcoming", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Upcoming", | |||||
"icon": "icon-calendar", | |||||
"default": True, | |||||
"idx": 1 | |||||
}, | |||||
"past": { | |||||
"name": "past", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Past", | |||||
"icon": "icon-time", | |||||
"idx": 2 | |||||
} | |||||
} | |||||
} | |||||
common_views = { | |||||
"post": { | |||||
"name": "post", | |||||
"template_path": "templates/website_group/post.html", | |||||
"label": "Post", | |||||
"icon": "icon-comments", | |||||
"hidden": True, | |||||
"no_cache": True, | |||||
"idx": 3 | |||||
}, | |||||
"edit": { | |||||
"name": "edit", | |||||
"template_path": "templates/website_group/edit_post.html", | |||||
"label": "Edit Post", | |||||
"icon": "icon-pencil", | |||||
"hidden": True, | |||||
"no_cache": True, | |||||
"idx": 4 | |||||
}, | |||||
"add": { | |||||
"name": "add", | |||||
"template_path": "templates/website_group/edit_post.html", | |||||
"label": "Add Post", | |||||
"icon": "icon-plus", | |||||
"hidden": True, | |||||
"idx": 5 | |||||
}, | |||||
"settings": { | |||||
"name": "settings", | |||||
"template_path": "templates/website_group/settings.html", | |||||
"label": "Settings", | |||||
"icon": "icon-cog", | |||||
"hidden": True, | |||||
"idx": 6 | |||||
} | |||||
} |
@@ -8,9 +8,8 @@ var blog = { | |||||
get_list: function() { | get_list: function() { | ||||
$.ajax({ | $.ajax({ | ||||
method: "GET", | method: "GET", | ||||
url: "/", | |||||
url: "/api/method/frappe.website.doctype.blog_post.blog_post.get_blog_list", | |||||
data: { | data: { | ||||
cmd: "frappe.templates.generators.blog_post.get_blog_list", | |||||
start: blog.start, | start: blog.start, | ||||
by: get_url_arg("by"), | by: get_url_arg("by"), | ||||
category: window.category || get_url_arg("category") | category: window.category || get_url_arg("category") | ||||
@@ -18,7 +18,7 @@ | |||||
<fieldset> | <fieldset> | ||||
<input class="form-control" name="comment_by_fullname" placeholder="Your Name" type="text"/><br> | <input class="form-control" name="comment_by_fullname" placeholder="Your Name" type="text"/><br> | ||||
<input class="form-control" name="comment_by" | <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 | <textarea class="form-control" name="comment" rows=10 | ||||
placeholder="Comment"/> | placeholder="Comment"/> | ||||
</textarea><br> | </textarea><br> | ||||
@@ -62,6 +62,11 @@ $(document).ready(function() { | |||||
return false; | return false; | ||||
} | } | ||||
if (!valid_email(args.comment_by)) { | |||||
frappe.msgprint("Please enter a valid email address."); | |||||
return false; | |||||
} | |||||
frappe.call({ | frappe.call({ | ||||
btn: this, | btn: this, | ||||
type: "POST", | type: "POST", | ||||
@@ -43,7 +43,7 @@ def add_comment(args=None): | |||||
ifnull(unsubscribed, 0)=0""", (comment.comment_doctype, comment.comment_docname))] | ifnull(unsubscribed, 0)=0""", (comment.comment_doctype, comment.comment_docname))] | ||||
owner = frappe.db.get_value(comment.comment_doctype, comment.comment_docname, "owner") | 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 | from frappe.utils.email_lib.bulk import send | ||||
@@ -124,5 +124,6 @@ frappe.ready(function() { | |||||
window.location.hash = "#login"; | window.location.hash = "#login"; | ||||
login.bind_events(); | login.bind_events(); | ||||
login.login(); | login.login(); | ||||
$(".form-signup, .form-forgot").removeClass("hide"); | |||||
$(document).trigger('login_rendered'); | $(document).trigger('login_rendered'); | ||||
}); | }); |
@@ -0,0 +1,5 @@ | |||||
{%- if metatags -%} | |||||
{%- for name in metatags %} | |||||
<meta name="{{ name }}" content="{{ metatags[name]|striptags }}" data-html-block="meta_block"> | |||||
{%- endfor -%} | |||||
{%- endif -%} |
@@ -39,7 +39,7 @@ | |||||
</a> | </a> | ||||
<ul class="dropdown-menu"> | <ul class="dropdown-menu"> | ||||
{%- for child in post_login -%} | {%- 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.class %} class="{{ child.class }}" {% endif %}> | ||||
{%- if child.url -%} | {%- if child.url -%} | ||||
@@ -47,7 +47,6 @@ | |||||
{%- if child.icon -%} | {%- if child.icon -%} | ||||
<i class="icon-fixed-width {{ child.icon }}"></i> | <i class="icon-fixed-width {{ child.icon }}"></i> | ||||
{%- endif -%} | {%- endif -%} | ||||
{{ child.label }} | {{ child.label }} | ||||
</a> | </a> | ||||
{%- endif -%} | {%- endif -%} | ||||
@@ -2,10 +2,10 @@ | |||||
{% if children -%} | {% if children -%} | ||||
{%- for child in children -%} | {%- for child in children -%} | ||||
<div class="sidebar-item"> | <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 | <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 %}"> | <a href="{{ child.name }}" class="no-decoration {% if child.name == pathname %}active{% endif %}"> | ||||
{{ child.page_title }} | {{ child.page_title }} | ||||
{% if not child.public_read %} | {% if not child.public_read %} | ||||
@@ -7,7 +7,7 @@ | |||||
<div class="row"> | <div class="row"> | ||||
<div class="col-xs-1 text-right" style="padding-right: 0px;"><b>{{ loop.index }}.</b></div> | <div class="col-xs-1 text-right" style="padding-right: 0px;"><b>{{ loop.index }}.</b></div> | ||||
<div class="col-xs-11"> | <div class="col-xs-11"> | ||||
<a href="{{ item.name }}">{{ item.page_title }}</a> | |||||
<a href="/{{ item.name }}">{{ item.page_title }}</a> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</li> | </li> | ||||
@@ -1,4 +1,4 @@ | |||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | # 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 |
@@ -4,5 +4,6 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe | import frappe | ||||
page_title = "Blog" | |||||
def get_context(context): | def get_context(context): | ||||
return frappe.get_doc("Blog Settings", "Blog Settings").as_dict() | return frappe.get_doc("Blog Settings", "Blog Settings").as_dict() |
@@ -43,7 +43,7 @@ | |||||
</form> | </form> | ||||
<form class="form-signin form-signup" role="form"> | |||||
<form class="form-signin form-signup hide" role="form"> | |||||
<h2 class="form-signin-heading">{{ _("Sign Up") }}</h2> | <h2 class="form-signin-heading">{{ _("Sign Up") }}</h2> | ||||
<input type="text" id="signup_fullname" | <input type="text" id="signup_fullname" | ||||
class="form-control" placeholder="{{ _('Full Name') }}" required autofocus> | class="form-control" placeholder="{{ _('Full Name') }}" required autofocus> | ||||
@@ -56,7 +56,7 @@ | |||||
</form> | </form> | ||||
<form class="form-signin form-forgot" role="form"> | |||||
<form class="form-signin form-forgot hide" role="form"> | |||||
<h2 class="form-signin-heading">{{ _("Forgot Password") }}</h2> | <h2 class="form-signin-heading">{{ _("Forgot Password") }}</h2> | ||||
<input type="email" id="forgot_email" | <input type="email" id="forgot_email" | ||||
class="form-control" placeholder="{{ _('Email Id') }}" required autofocus> | class="form-control" placeholder="{{ _('Email Id') }}" required autofocus> | ||||
@@ -1,5 +1,5 @@ | |||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | ||||
# MIT License. See license.txt | |||||
# MIT License. See license.txt | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
@@ -15,16 +15,14 @@ def get_context(context): | |||||
"""generate the sitemap XML""" | """generate the sitemap XML""" | ||||
host = get_request_site_address() | host = get_request_site_address() | ||||
links = [] | links = [] | ||||
for l in frappe.db.sql("""select `tabWebsite Route`.page_name, `tabWebsite Route`.lastmod | |||||
from `tabWebsite Route`, `tabWebsite Template` | |||||
where | |||||
`tabWebsite Route`.website_template = `tabWebsite Template`.name | |||||
and ifnull(`tabWebsite Template`.no_sitemap, 0)=0""", | |||||
for l in frappe.db.sql("""select page_name, lastmod, controller | |||||
from `tabWebsite Route`""", | |||||
as_dict=True): | as_dict=True): | ||||
links.append({ | |||||
"loc": urllib.basejoin(host, urllib.quote(l.page_name.encode("utf-8"))), | |||||
"lastmod": l.lastmod | |||||
}) | |||||
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"))), | |||||
"lastmod": l.lastmod | |||||
}) | |||||
return {"links":links} | return {"links":links} | ||||
@@ -8,58 +8,58 @@ from frappe.website.permissions import get_access | |||||
@frappe.whitelist(allow_guest=True) | @frappe.whitelist(allow_guest=True) | ||||
def get_post_list_html(group, view, limit_start=0, limit_length=20): | def get_post_list_html(group, view, limit_start=0, limit_length=20): | ||||
from frappe.templates.generators.website_group import get_views | |||||
from frappe.website.doctype.website_group.website_group import get_views | |||||
# verify permission for paging | # verify permission for paging | ||||
if frappe.local.form_dict.cmd == "get_post_list_html": | if frappe.local.form_dict.cmd == "get_post_list_html": | ||||
pathname = frappe.db.get_value("Website Route", | |||||
pathname = frappe.db.get_value("Website Route", | |||||
{"ref_doctype": "Website Group", "docname": group}) | {"ref_doctype": "Website Group", "docname": group}) | ||||
access = get_access(pathname) | access = get_access(pathname) | ||||
if not access.get("read"): | if not access.get("read"): | ||||
return frappe.PermissionError | return frappe.PermissionError | ||||
conditions = "" | conditions = "" | ||||
values = [group] | values = [group] | ||||
group_type = frappe.db.get_value("Website Group", group, "group_type") | group_type = frappe.db.get_value("Website Group", group, "group_type") | ||||
if group_type == "Events": | if group_type == "Events": | ||||
# should show based on time upto precision of hour | # should show based on time upto precision of hour | ||||
# because the current hour should also be in upcoming | # because the current hour should also be in upcoming | ||||
values.append(now_datetime().replace(minute=0, second=0, microsecond=0)) | values.append(now_datetime().replace(minute=0, second=0, microsecond=0)) | ||||
if view in ("feed", "closed"): | if view in ("feed", "closed"): | ||||
order_by = "p.creation desc" | order_by = "p.creation desc" | ||||
if view == "closed": | if view == "closed": | ||||
conditions += " and p.is_task=1 and p.status='Closed'" | conditions += " and p.is_task=1 and p.status='Closed'" | ||||
elif view in ("popular", "open"): | elif view in ("popular", "open"): | ||||
now = get_datetime_str(now_datetime()) | now = get_datetime_str(now_datetime()) | ||||
order_by = """(p.upvotes + post_reply_count - (timestampdiff(hour, p.creation, \"{}\") / 2)) desc, | |||||
order_by = """(p.upvotes + post_reply_count - (timestampdiff(hour, p.creation, \"{}\") / 2)) desc, | |||||
p.creation desc""".format(now) | p.creation desc""".format(now) | ||||
if view == "open": | if view == "open": | ||||
conditions += " and p.is_task=1 and p.status='Open'" | conditions += " and p.is_task=1 and p.status='Open'" | ||||
elif view == "upcoming": | elif view == "upcoming": | ||||
conditions += " and p.is_event=1 and p.event_datetime >= %s" | conditions += " and p.is_event=1 and p.event_datetime >= %s" | ||||
order_by = "p.event_datetime asc" | order_by = "p.event_datetime asc" | ||||
elif view == "past": | elif view == "past": | ||||
conditions += " and p.is_event=1 and p.event_datetime < %s" | conditions += " and p.is_event=1 and p.event_datetime < %s" | ||||
order_by = "p.event_datetime desc" | order_by = "p.event_datetime desc" | ||||
values += [int(limit_start), int(limit_length)] | values += [int(limit_start), int(limit_length)] | ||||
posts = frappe.db.sql("""select p.*, pr.user_image, pr.first_name, pr.last_name, | posts = frappe.db.sql("""select p.*, pr.user_image, pr.first_name, pr.last_name, | ||||
(select count(pc.name) from `tabPost` pc where pc.parent_post=p.name) as post_reply_count | (select count(pc.name) from `tabPost` pc where pc.parent_post=p.name) as post_reply_count | ||||
from `tabPost` p, `tabUser` pr | from `tabPost` p, `tabUser` pr | ||||
where p.website_group = %s and pr.name = p.owner and ifnull(p.parent_post, '')='' | |||||
where p.website_group = %s and pr.name = p.owner and ifnull(p.parent_post, '')='' | |||||
{conditions} order by {order_by} limit %s, %s""".format(conditions=conditions, order_by=order_by), | {conditions} order by {order_by} limit %s, %s""".format(conditions=conditions, order_by=order_by), | ||||
tuple(values), as_dict=True, debug=True) | tuple(values), as_dict=True, debug=True) | ||||
context = { "posts": posts, "limit_start": limit_start, "view": get_views(group_type)[view] } | context = { "posts": posts, "limit_start": limit_start, "view": get_views(group_type)[view] } | ||||
return frappe.get_template("templates/includes/post_list.html").render(context) | return frappe.get_template("templates/includes/post_list.html").render(context) | ||||
@@ -7,7 +7,7 @@ from frappe import _ | |||||
from frappe.utils import get_fullname | from frappe.utils import get_fullname | ||||
from frappe.website.permissions import get_access | from frappe.website.permissions import get_access | ||||
from frappe.utils.file_manager import save_file | from frappe.utils.file_manager import save_file | ||||
from frappe.templates.generators.website_group import get_pathname | |||||
from frappe.website.doctype.website_group.website_group import get_pathname | |||||
def get_post_context(context): | def get_post_context(context): | ||||
post = frappe.get_doc("Post", frappe.form_dict.name) | post = frappe.get_doc("Post", frappe.form_dict.name) | ||||
@@ -130,7 +130,7 @@ def save_post(post, content, picture=None, picture_name=None, title=None, | |||||
return post.parent_post or post.name | return post.parent_post or post.name | ||||
def process_picture(post, picture_name, picture): | def process_picture(post, picture_name, picture): | ||||
from frappe.templates.generators.website_group import clear_cache | |||||
from frappe.website.doctype.website_group.website_group import clear_cache | |||||
post.picture_url = save_file(picture_name, picture, "Post", post.name, decode=True).file_url | post.picture_url = save_file(picture_name, picture, "Post", post.name, decode=True).file_url | ||||
frappe.db.set_value("Post", post.name, "picture_url", post.picture_url) | frappe.db.set_value("Post", post.name, "picture_url", post.picture_url) | ||||
@@ -4,7 +4,7 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe | import frappe | ||||
from frappe.website.permissions import get_access, clear_permissions | from frappe.website.permissions import get_access, clear_permissions | ||||
from frappe.templates.generators.website_group import get_pathname | |||||
from frappe.website.doctype.website_group.website_group import get_pathname | |||||
from frappe.utils.email_lib.bulk import send | from frappe.utils.email_lib.bulk import send | ||||
@frappe.whitelist() | @frappe.whitelist() | ||||
@@ -12,17 +12,17 @@ def suggest_user(term, group): | |||||
pathname = get_pathname(group) | pathname = get_pathname(group) | ||||
if not get_access(pathname).get("admin"): | if not get_access(pathname).get("admin"): | ||||
raise frappe.PermissionError | raise frappe.PermissionError | ||||
users = frappe.db.sql("""select pr.name, pr.first_name, pr.last_name, | |||||
users = frappe.db.sql("""select pr.name, pr.first_name, pr.last_name, | |||||
pr.user_image, pr.location | pr.user_image, pr.location | ||||
from `tabUser` pr | |||||
from `tabUser` pr | |||||
where (pr.first_name like %(term)s or pr.last_name like %(term)s) | where (pr.first_name like %(term)s or pr.last_name like %(term)s) | ||||
and pr.user_type = "Website User" | and pr.user_type = "Website User" | ||||
and pr.user_image is not null and pr.enabled=1 | and pr.user_image is not null and pr.enabled=1 | ||||
and not exists(select wsp.name from `tabWebsite Route Permission` wsp | |||||
where wsp.website_route=%(group)s and wsp.user=pr.name)""", | |||||
and not exists(select wsp.name from `tabWebsite Route Permission` wsp | |||||
where wsp.website_route=%(group)s and wsp.user=pr.name)""", | |||||
{"term": "%{}%".format(term), "group": pathname}, as_dict=True) | {"term": "%{}%".format(term), "group": pathname}, as_dict=True) | ||||
template = frappe.get_template("templates/includes/user_display.html") | template = frappe.get_template("templates/includes/user_display.html") | ||||
return [{ | return [{ | ||||
"value": "{} {}".format(pr.first_name or "", pr.last_name or ""), | "value": "{} {}".format(pr.first_name or "", pr.last_name or ""), | ||||
@@ -35,7 +35,7 @@ def add_sitemap_permission(group, user): | |||||
pathname = get_pathname(group) | pathname = get_pathname(group) | ||||
if not get_access(pathname).get("admin"): | if not get_access(pathname).get("admin"): | ||||
raise frappe.PermissionError | raise frappe.PermissionError | ||||
permission = frappe.get_doc({ | permission = frappe.get_doc({ | ||||
"doctype": "Website Route Permission", | "doctype": "Website Route Permission", | ||||
"website_route": pathname, | "website_route": pathname, | ||||
@@ -43,11 +43,11 @@ def add_sitemap_permission(group, user): | |||||
"read": 1 | "read": 1 | ||||
}) | }) | ||||
permission.insert(ignore_permissions=True) | permission.insert(ignore_permissions=True) | ||||
user = permission.as_dict() | user = permission.as_dict() | ||||
user.update(frappe.db.get_value("User", user.user, | |||||
user.update(frappe.db.get_value("User", user.user, | |||||
["name", "first_name", "last_name", "user_image", "location"], as_dict=True)) | ["name", "first_name", "last_name", "user_image", "location"], as_dict=True)) | ||||
return frappe.get_template("templates/includes/sitemap_permission.html").render({ | return frappe.get_template("templates/includes/sitemap_permission.html").render({ | ||||
"user": user | "user": user | ||||
}) | }) | ||||
@@ -57,18 +57,18 @@ def update_permission(group, user, perm, value): | |||||
pathname = get_pathname(group) | pathname = get_pathname(group) | ||||
if not get_access(pathname).get("admin"): | if not get_access(pathname).get("admin"): | ||||
raise frappe.PermissionError | raise frappe.PermissionError | ||||
permission = frappe.get_doc("Website Route Permission", {"website_route": pathname, "user": user}) | permission = frappe.get_doc("Website Route Permission", {"website_route": pathname, "user": user}) | ||||
permission.set(perm, int(value)) | permission.set(perm, int(value)) | ||||
permission.save(ignore_permissions=True) | permission.save(ignore_permissions=True) | ||||
# send email | # send email | ||||
if perm=="admin" and int(value): | if perm=="admin" and int(value): | ||||
group_title = frappe.db.get_value("Website Route", pathname, "page_title") | group_title = frappe.db.get_value("Website Route", pathname, "page_title") | ||||
subject = "You have been made Administrator of Group " + group_title | subject = "You have been made Administrator of Group " + group_title | ||||
send(recipients=[user], | |||||
send(recipients=[user], | |||||
subject= subject, add_unsubscribe_link=False, | subject= subject, add_unsubscribe_link=False, | ||||
message="""<h3>Group Notification<h3>\ | message="""<h3>Group Notification<h3>\ | ||||
<p>%s</p>\ | <p>%s</p>\ | ||||
@@ -82,15 +82,15 @@ def update_description(group, description): | |||||
group = frappe.get_doc("Website Group", group) | group = frappe.get_doc("Website Group", group) | ||||
group.group_description = description | group.group_description = description | ||||
group.save(ignore_permissions=True) | group.save(ignore_permissions=True) | ||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def add_website_group(group, new_group, public_read, public_write, group_type="Forum"): | def add_website_group(group, new_group, public_read, public_write, group_type="Forum"): | ||||
if not get_access(get_pathname(group)).get("admin"): | if not get_access(get_pathname(group)).get("admin"): | ||||
raise frappe.PermissionError | raise frappe.PermissionError | ||||
parent_website_route = frappe.db.get_value("Website Route", | |||||
parent_website_route = frappe.db.get_value("Website Route", | |||||
{"ref_doctype": "Website Group", "docname": group}) | {"ref_doctype": "Website Group", "docname": group}) | ||||
frappe.get_doc({ | frappe.get_doc({ | ||||
"doctype": "Website Group", | "doctype": "Website Group", | ||||
"group_name": group + "-" + new_group, | "group_name": group + "-" + new_group, | ||||
@@ -99,4 +99,4 @@ def add_website_group(group, new_group, public_read, public_write, group_type="F | |||||
"group_type": group_type, | "group_type": group_type, | ||||
"public_read": int(public_read), | "public_read": int(public_read), | ||||
"public_write": int(public_write) | "public_write": int(public_write) | ||||
}).insert(ignore_permissions=True) | |||||
}).insert(ignore_permissions=True) |
@@ -119,6 +119,12 @@ app_version = "0.0.1" | |||||
# "Role": "home_page" | # "Role": "home_page" | ||||
# }} | # }} | ||||
# Generators | |||||
# ---------- | |||||
# automatically create page for each record of this doctype | |||||
# website_generators = ["Web Page"] | |||||
# Installation | # Installation | ||||
# ------------ | # ------------ | ||||
@@ -16,6 +16,7 @@ class BulkLimitCrossedError(frappe.ValidationError): pass | |||||
def send(recipients=None, sender=None, doctype='User', email_field='email', | def send(recipients=None, sender=None, doctype='User', email_field='email', | ||||
subject='[No Subject]', message='[No Content]', ref_doctype=None, ref_docname=None, | subject='[No Subject]', message='[No Content]', ref_doctype=None, ref_docname=None, | ||||
add_unsubscribe_link=True): | add_unsubscribe_link=True): | ||||
def is_unsubscribed(rdata): | def is_unsubscribed(rdata): | ||||
if not rdata: | if not rdata: | ||||
return 1 | return 1 | ||||
@@ -81,7 +82,7 @@ def add(email, sender, subject, formatted, text_content=None, | |||||
try: | try: | ||||
e.message = get_email(email, sender=e.sender, formatted=formatted, subject=subject, | e.message = get_email(email, sender=e.sender, formatted=formatted, subject=subject, | ||||
text_content=text_content).as_string() | text_content=text_content).as_string() | ||||
except frappe.ValidationError: | |||||
except frappe.InvalidEmailAddressError: | |||||
# bad email id - don't add to queue | # bad email id - don't add to queue | ||||
return | return | ||||
@@ -4,15 +4,16 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe | import frappe | ||||
from frappe import msgprint, throw, _ | from frappe import msgprint, throw, _ | ||||
from frappe.utils import scrub_urls, cstr | |||||
from frappe.utils import scrub_urls | |||||
import email.utils | import email.utils | ||||
from markdown2 import markdown | |||||
def get_email(recipients, sender='', msg='', subject='[No Subject]', | def get_email(recipients, sender='', msg='', subject='[No Subject]', | ||||
text_content = None, footer=None, print_html=None, formatted=None): | text_content = None, footer=None, print_html=None, formatted=None): | ||||
"""send an html email as multipart with attachments and all""" | """send an html email as multipart with attachments and all""" | ||||
emailobj = EMail(sender, recipients, subject) | emailobj = EMail(sender, recipients, subject) | ||||
if (not '<br>' in msg) and (not '<p>' in msg) and (not '<div' in msg): | |||||
msg = msg.replace('\n', '<br>') | |||||
msg = markdown(msg) | |||||
emailobj.set_html(msg, text_content, footer=footer, print_html=print_html, formatted=formatted) | emailobj.set_html(msg, text_content, footer=footer, print_html=print_html, formatted=formatted) | ||||
return emailobj | return emailobj | ||||
@@ -151,17 +152,18 @@ class EMail: | |||||
def _validate(email): | def _validate(email): | ||||
"""validate an email field""" | """validate an email field""" | ||||
if email and not validate_email_add(email): | 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 | return email | ||||
if not self.sender: | if not self.sender: | ||||
self.sender = frappe.db.get_value('Outgoing Email Settings', None, | self.sender = frappe.db.get_value('Outgoing Email Settings', None, | ||||
'auto_email_id') or frappe.conf.get('auto_email_id') or None | 'auto_email_id') or frappe.conf.get('auto_email_id') or None | ||||
if not self.sender: | 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: | if not "expires_on" in frappe.conf: | ||||
msgprint(_("Alternatively, you can also specify 'auto_email_id' in site_config.json")) | 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.sender = _validate(self.sender) | ||||
self.reply_to = _validate(self.reply_to) | self.reply_to = _validate(self.reply_to) | ||||
@@ -189,7 +191,6 @@ class EMail: | |||||
def get_formatted_html(subject, message, footer=None, print_html=None): | def get_formatted_html(subject, message, footer=None, print_html=None): | ||||
# imported here to avoid cyclic import | # imported here to avoid cyclic import | ||||
import inlinestyler.utils | |||||
message = scrub_urls(message) | message = scrub_urls(message) | ||||
rendered_email = frappe.get_template("templates/emails/standard.html").render({ | rendered_email = frappe.get_template("templates/emails/standard.html").render({ | ||||
@@ -200,22 +201,17 @@ def get_formatted_html(subject, message, footer=None, print_html=None): | |||||
"subject": subject | "subject": subject | ||||
}) | }) | ||||
# if in a test case, do not inline css | |||||
if frappe.local.flags.in_test: | |||||
return rendered_email | |||||
return cstr(inlinestyler.utils.inline_css(rendered_email)) | |||||
return rendered_email | |||||
def get_footer(footer=None): | def get_footer(footer=None): | ||||
"""append a footer (signature)""" | """append a footer (signature)""" | ||||
footer = footer or "" | footer = footer or "" | ||||
# control panel | |||||
footer += frappe.db.get_default('mail_footer') or '' | |||||
# hooks | # hooks | ||||
for f in frappe.get_hooks("mail_footer"): | for f in frappe.get_hooks("mail_footer"): | ||||
footer += frappe.get_attr(f) | |||||
# mail_footer could be a function that returns a value | |||||
mail_footer = frappe.get_attr(f) | |||||
footer += (mail_footer if isinstance(mail_footer, basestring) else mail_footer()) | |||||
footer += "<!--unsubscribe link here-->" | footer += "<!--unsubscribe link here-->" | ||||
@@ -6,8 +6,12 @@ from __future__ import unicode_literals | |||||
import frappe, os | import frappe, os | ||||
from frappe.core.page.data_import_tool.data_import_tool import import_doc, export_fixture, export_csv | from frappe.core.page.data_import_tool.data_import_tool import import_doc, export_fixture, export_csv | ||||
def sync_fixtures(): | |||||
for app in frappe.get_installed_apps(): | |||||
def sync_fixtures(app=None): | |||||
if app: | |||||
apps = [app] | |||||
else: | |||||
apps = frappe.get_installed_apps() | |||||
for app in apps: | |||||
if os.path.exists(frappe.get_app_path(app, "fixtures")): | if os.path.exists(frappe.get_app_path(app, "fixtures")): | ||||
for fname in os.listdir(frappe.get_app_path(app, "fixtures")): | for fname in os.listdir(frappe.get_app_path(app, "fixtures")): | ||||
if fname.endswith(".json") or fname.endswith(".csv"): | if fname.endswith(".json") or fname.endswith(".csv"): | ||||
@@ -1,7 +1,2 @@ | |||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | ||||
# MIT License. See license.txt | # MIT License. See license.txt | ||||
from __future__ import unicode_literals | |||||
from frappe.website.doctype.website_template.website_template \ | |||||
import rebuild_website_template as rebuild_config |
@@ -53,9 +53,13 @@ def build_context(sitemap_options): | |||||
# provide doc | # provide doc | ||||
if context.doctype and context.docname: | if context.doctype and context.docname: | ||||
context.doc = frappe.get_doc(context.doctype, context.docname) | |||||
doc = frappe.get_doc(context.doctype, context.docname) | |||||
context.doc = doc | |||||
context.update(doc.as_dict()) | |||||
if hasattr(context.doc, "get_context"): | |||||
context.update(context.doc.get_context(context) or {}) | |||||
if context.controller: | |||||
elif context.controller: | |||||
module = frappe.get_module(context.controller) | module = frappe.get_module(context.controller) | ||||
if module and hasattr(module, "get_context"): | if module and hasattr(module, "get_context"): | ||||
@@ -63,7 +67,7 @@ def build_context(sitemap_options): | |||||
add_metatags(context) | 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) | context.data = render_blocks(context) | ||||
return context | return context | ||||
@@ -6,15 +6,21 @@ import frappe | |||||
from frappe.website.website_generator import WebsiteGenerator | from frappe.website.website_generator import WebsiteGenerator | ||||
from frappe.website.render import clear_cache | from frappe.website.render import clear_cache | ||||
template = "templates/generators/blog_category.html" | |||||
no_cache = True | |||||
class BlogCategory(WebsiteGenerator): | class BlogCategory(WebsiteGenerator): | ||||
def autoname(self): | def autoname(self): | ||||
# to override autoname of WebsiteGenerator | # to override autoname of WebsiteGenerator | ||||
self.name = self.category_name | self.name = self.category_name | ||||
def get_page_title(self): | def get_page_title(self): | ||||
return self.title or self.name | return self.title or self.name | ||||
def on_update(self): | def on_update(self): | ||||
WebsiteGenerator.on_update(self) | WebsiteGenerator.on_update(self) | ||||
clear_cache() | clear_cache() | ||||
def get_parent_website_route(self): | |||||
parent_website_sitemap = super(BlogCategory, self).get_parent_website_route() | |||||
return parent_website_sitemap or "blog" |
@@ -44,7 +44,8 @@ | |||||
"in_list_view": 1, | "in_list_view": 1, | ||||
"label": "Blog Category", | "label": "Blog Category", | ||||
"options": "Blog Category", | "options": "Blog Category", | ||||
"permlevel": 0 | |||||
"permlevel": 0, | |||||
"reqd": 1 | |||||
}, | }, | ||||
{ | { | ||||
"fieldname": "parent_website_route", | "fieldname": "parent_website_route", | ||||
@@ -95,7 +96,7 @@ | |||||
"icon": "icon-quote-left", | "icon": "icon-quote-left", | ||||
"idx": 1, | "idx": 1, | ||||
"max_attachments": 5, | "max_attachments": 5, | ||||
"modified": "2014-05-27 03:49:07.888408", | |||||
"modified": "2014-06-27 05:08:37.936947", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Website", | "module": "Website", | ||||
"name": "Blog Post", | "name": "Blog Post", | ||||
@@ -7,19 +7,22 @@ import frappe, re | |||||
from frappe.website.website_generator import WebsiteGenerator | from frappe.website.website_generator import WebsiteGenerator | ||||
from frappe.website.render import clear_cache | from frappe.website.render import clear_cache | ||||
from frappe import _ | |||||
from frappe.utils import today | |||||
from frappe.utils import today, cint, global_date_format, get_fullname | |||||
from frappe.website.utils import find_first_image, get_comment_list | |||||
order_by = "`tabBlog Post`.published_on desc" | |||||
condition_field = "published" | |||||
template = "templates/generators/blog_post.html" | |||||
class BlogPost(WebsiteGenerator): | class BlogPost(WebsiteGenerator): | ||||
save_versions = True | save_versions = True | ||||
def get_page_title(self): | def get_page_title(self): | ||||
return self.title | return self.title | ||||
def validate(self): | def validate(self): | ||||
if not self.blog_intro: | if not self.blog_intro: | ||||
self.blog_intro = self.content[:140] | self.blog_intro = self.content[:140] | ||||
re.sub("\<[^>]*\>", "", self.blog_intro) | |||||
self.blog_intro = re.sub("\<[^>]*\>", "", self.blog_intro) | |||||
if self.blog_intro: | if self.blog_intro: | ||||
self.blog_intro = self.blog_intro[:140] | self.blog_intro = self.blog_intro[:140] | ||||
@@ -27,22 +30,93 @@ class BlogPost(WebsiteGenerator): | |||||
if self.published and not self.published_on: | if self.published and not self.published_on: | ||||
self.published_on = today() | self.published_on = today() | ||||
self.parent_website_route = frappe.db.get_value("Website Route", | |||||
{"ref_doctype": "Blog Category", "docname": self.blog_category}) | |||||
# make sure route for category exists | |||||
self.parent_website_route = self.get_category_route() | |||||
if not self.parent_website_route: | |||||
frappe.get_doc("Blog Category", self.blog_category).save(ignore_permissions=True) | |||||
self.parent_website_route = self.get_category_route() | |||||
# update posts | # update posts | ||||
frappe.db.sql("""update tabBlogger set posts=(select count(*) from `tabBlog Post` | frappe.db.sql("""update tabBlogger set posts=(select count(*) from `tabBlog Post` | ||||
where ifnull(blogger,'')=tabBlogger.name) | where ifnull(blogger,'')=tabBlogger.name) | ||||
where name=%s""", (self.blogger,)) | where name=%s""", (self.blogger,)) | ||||
def get_category_route(self): | |||||
return frappe.db.get_value("Website Route", | |||||
{"ref_doctype": "Blog Category", "docname": self.blog_category}) | |||||
def on_update(self): | def on_update(self): | ||||
WebsiteGenerator.on_update(self) | WebsiteGenerator.on_update(self) | ||||
clear_cache("writers") | clear_cache("writers") | ||||
def get_context(self, context): | |||||
# this is for double precaution. usually it wont reach this code if not published | |||||
if not cint(self.published): | |||||
raise Exception, "This blog has not been published yet!" | |||||
# temp fields | |||||
context.full_name = get_fullname(self.owner) | |||||
context.updated = global_date_format(self.published_on) | |||||
if self.blogger: | |||||
context.blogger_info = frappe.get_doc("Blogger", self.blogger).as_dict() | |||||
context.description = self.blog_intro or self.content[:140] | |||||
context.metatags = { | |||||
"name": self.title, | |||||
"description": context.description, | |||||
} | |||||
image = find_first_image(self.content) | |||||
if image: | |||||
context.metatags["image"] = image | |||||
context.categories = frappe.db.sql_list("""select name from | |||||
`tabBlog Category` order by name""") | |||||
context.comment_list = get_comment_list(self.doctype, self.name) | |||||
return context | |||||
def clear_blog_cache(): | def clear_blog_cache(): | ||||
for blog in frappe.db.sql_list("""select page_name from | for blog in frappe.db.sql_list("""select page_name from | ||||
`tabBlog Post` where ifnull(published,0)=1"""): | `tabBlog Post` where ifnull(published,0)=1"""): | ||||
clear_cache(blog) | clear_cache(blog) | ||||
clear_cache("writers") | clear_cache("writers") | ||||
@frappe.whitelist(allow_guest=True) | |||||
def get_blog_list(start=0, by=None, category=None): | |||||
condition = "" | |||||
if by: | |||||
condition = " and t1.blogger='%s'" % by.replace("'", "\'") | |||||
if category: | |||||
condition += " and t1.blog_category='%s'" % category.replace("'", "\'") | |||||
query = """\ | |||||
select | |||||
t1.title, t1.name, t3.name as page_name, t1.published_on as creation, | |||||
day(t1.published_on) as day, monthname(t1.published_on) as month, | |||||
year(t1.published_on) as year, | |||||
ifnull(t1.blog_intro, t1.content) as content, | |||||
t2.full_name, t2.avatar, t1.blogger, | |||||
(select count(name) from `tabComment` where | |||||
comment_doctype='Blog Post' and comment_docname=t1.name) as comments | |||||
from `tabBlog Post` t1, `tabBlogger` t2, `tabWebsite Route` t3 | |||||
where ifnull(t1.published,0)=1 | |||||
and t1.blogger = t2.name | |||||
and t3.docname = t1.name | |||||
and t3.ref_doctype = "Blog Post" | |||||
%(condition)s | |||||
order by published_on desc, name asc | |||||
limit %(start)s, 20""" % {"start": start, "condition": condition} | |||||
result = frappe.db.sql(query, as_dict=1) | |||||
# strip html tags from content | |||||
for res in result: | |||||
res['published'] = global_date_format(res['creation']) | |||||
res['content'] = res['content'][:140] | |||||
return result | |||||
@@ -39,7 +39,7 @@ class Post(Document): | |||||
def on_update(self): | def on_update(self): | ||||
from frappe.templates.website_group.post import clear_post_cache | from frappe.templates.website_group.post import clear_post_cache | ||||
from frappe.templates.generators.website_group import clear_cache | |||||
from frappe.website.doctype.website_group.website_group import clear_cache | |||||
clear_cache(website_group=self.website_group) | clear_cache(website_group=self.website_group) | ||||
clear_post_cache(self.parent_post or self.name) | clear_post_cache(self.parent_post or self.name) | ||||
@@ -5,56 +5,34 @@ import frappe | |||||
test_records = frappe.get_test_records('Web Page') | test_records = frappe.get_test_records('Web Page') | ||||
class TestWebPage(unittest.TestCase): | class TestWebPage(unittest.TestCase): | ||||
def setUp(self): | |||||
frappe.db.sql("delete from `tabWeb Page`") | |||||
frappe.db.sql("delete from `tabWebsite Route` where ref_doctype='Web Page'") | |||||
for t in test_records: | |||||
frappe.get_doc(t).insert() | |||||
def test_check_sitemap(self): | def test_check_sitemap(self): | ||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-1"}), "test-web-page-1") | {"ref_doctype":"Web Page", "docname": "test-web-page-1"}), "test-web-page-1") | ||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-2"}), "test-web-page-1/test-web-page-2") | {"ref_doctype":"Web Page", "docname": "test-web-page-2"}), "test-web-page-1/test-web-page-2") | ||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-3"}), "test-web-page-1/test-web-page-3") | {"ref_doctype":"Web Page", "docname": "test-web-page-3"}), "test-web-page-1/test-web-page-3") | ||||
def test_check_idx(self): | |||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-2"}, 'idx'), 0) | |||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-3"}, 'idx'), 1) | |||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-5"}, 'idx'), 2) | |||||
def test_check_rename(self): | def test_check_rename(self): | ||||
web_page = frappe.get_doc("Web Page", "test-web-page-1") | web_page = frappe.get_doc("Web Page", "test-web-page-1") | ||||
web_page.parent_website_route = "test-web-page-4" | web_page.parent_website_route = "test-web-page-4" | ||||
web_page.save() | web_page.save() | ||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-2"}), | |||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-2"}), | |||||
"test-web-page-4/test-web-page-1/test-web-page-2") | "test-web-page-4/test-web-page-1/test-web-page-2") | ||||
web_page.parent_website_route = "" | web_page.parent_website_route = "" | ||||
web_page.save() | web_page.save() | ||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-2"}), | |||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-2"}), | |||||
"test-web-page-1/test-web-page-2") | "test-web-page-1/test-web-page-2") | ||||
def test_check_move(self): | |||||
web_page = frappe.get_doc("Web Page", "test-web-page-3") | |||||
web_page.parent_website_route = "test-web-page-4" | |||||
web_page.save() | |||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-2"}, 'idx'), 0) | |||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-3"}, 'idx'), 0) | |||||
self.assertEquals(frappe.db.get_value("Website Route", | |||||
{"ref_doctype":"Web Page", "docname": "test-web-page-5"}, 'idx'), 1) | |||||
web_page = frappe.get_doc("Web Page", "test-web-page-3") | |||||
web_page.parent_website_route = "test-web-page-1" | |||||
web_page.save() |
@@ -18,6 +18,7 @@ $.extend(cur_frm.cscript, { | |||||
}, | }, | ||||
refresh: function(doc) { | refresh: function(doc) { | ||||
cur_frm.cscript.layout(doc); | cur_frm.cscript.layout(doc); | ||||
cur_frm.set_intro(""); | |||||
if (!doc.__islocal && doc.published) { | if (!doc.__islocal && doc.published) { | ||||
cur_frm.set_intro(__("Published on website at: {0}", | cur_frm.set_intro(__("Published on website at: {0}", | ||||
[repl('<a href="/%(website_route)s" target="_blank">/%(website_route)s</a>', doc.__onload)])); | [repl('<a href="/%(website_route)s" target="_blank">/%(website_route)s</a>', doc.__onload)])); | ||||
@@ -1,173 +1,180 @@ | |||||
{ | { | ||||
"allow_attach": 1, | |||||
"creation": "2013-03-28 10:35:30", | |||||
"description": "Page to show on the website\n", | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "Transaction", | |||||
"allow_attach": 1, | |||||
"creation": "2013-03-28 10:35:30", | |||||
"description": "Page to show on the website\n", | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "Transaction", | |||||
"fields": [ | "fields": [ | ||||
{ | { | ||||
"fieldname": "section_title", | |||||
"fieldtype": "Section Break", | |||||
"label": "Title", | |||||
"fieldname": "section_title", | |||||
"fieldtype": "Section Break", | |||||
"label": "Title", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "Title / headline of your page", | |||||
"fieldname": "title", | |||||
"fieldtype": "Data", | |||||
"label": "Title", | |||||
"permlevel": 0, | |||||
"description": "Title / headline of your page", | |||||
"fieldname": "title", | |||||
"fieldtype": "Data", | |||||
"label": "Title", | |||||
"permlevel": 0, | |||||
"reqd": 1 | "reqd": 1 | ||||
}, | |||||
{ | |||||
"description": "Page url name (auto-generated)", | |||||
"fieldname": "page_name", | |||||
"fieldtype": "Data", | |||||
"in_list_view": 1, | |||||
"label": "Page Name", | |||||
"permlevel": 0, | |||||
}, | |||||
{ | |||||
"description": "Page url name (auto-generated)", | |||||
"fieldname": "page_name", | |||||
"fieldtype": "Data", | |||||
"in_list_view": 1, | |||||
"label": "Page Name", | |||||
"permlevel": 0, | |||||
"read_only": 0 | "read_only": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "parent_website_route", | |||||
"fieldtype": "Link", | |||||
"label": "Parent Website Page", | |||||
"options": "Website Route", | |||||
"fieldname": "parent_website_route", | |||||
"fieldtype": "Link", | |||||
"label": "Parent Website Page", | |||||
"options": "Website Route", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "published", | |||||
"fieldtype": "Check", | |||||
"label": "Published", | |||||
"fieldname": "published", | |||||
"fieldtype": "Check", | |||||
"label": "Published", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "cb1", | |||||
"fieldtype": "Column Break", | |||||
"permlevel": 0, | |||||
"description": "0 is highest", | |||||
"fieldname": "idx", | |||||
"fieldtype": "Int", | |||||
"label": "Priority", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "cb1", | |||||
"fieldtype": "Column Break", | |||||
"permlevel": 0, | |||||
"width": "50%" | "width": "50%" | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "Description for page header.", | |||||
"fieldname": "description", | |||||
"fieldtype": "Small Text", | |||||
"label": "Description", | |||||
"description": "Description for page header.", | |||||
"fieldname": "description", | |||||
"fieldtype": "Small Text", | |||||
"label": "Description", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "Page content", | |||||
"fieldname": "sb1", | |||||
"fieldtype": "Section Break", | |||||
"label": "Content", | |||||
"description": "Page content", | |||||
"fieldname": "sb1", | |||||
"fieldtype": "Section Break", | |||||
"label": "Content", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "Begin this page with a slideshow of images", | |||||
"fieldname": "slideshow", | |||||
"fieldtype": "Link", | |||||
"label": "Slideshow", | |||||
"options": "Website Slideshow", | |||||
"description": "Begin this page with a slideshow of images", | |||||
"fieldname": "slideshow", | |||||
"fieldtype": "Link", | |||||
"label": "Slideshow", | |||||
"options": "Website Slideshow", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "Content in markdown format that appears on the main side of your page", | |||||
"fieldname": "main_section", | |||||
"fieldtype": "Text Editor", | |||||
"label": "Main Section", | |||||
"description": "Content in markdown format that appears on the main side of your page", | |||||
"fieldname": "main_section", | |||||
"fieldtype": "Text Editor", | |||||
"label": "Main Section", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"depends_on": "eval:!doc.__islocal", | |||||
"description": "Link to other pages in the side bar and next section", | |||||
"fieldname": "sb2", | |||||
"fieldtype": "Section Break", | |||||
"label": "More", | |||||
"depends_on": "eval:!doc.__islocal", | |||||
"description": "Link to other pages in the side bar and next section", | |||||
"fieldname": "sb2", | |||||
"fieldtype": "Section Break", | |||||
"label": "More", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "HTML for header section. Optional", | |||||
"fieldname": "header", | |||||
"fieldtype": "Text", | |||||
"label": "Header", | |||||
"description": "HTML for header section. Optional", | |||||
"fieldname": "header", | |||||
"fieldtype": "Text", | |||||
"label": "Header", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "enable_comments", | |||||
"fieldtype": "Check", | |||||
"label": "Enable Comments", | |||||
"fieldname": "enable_comments", | |||||
"fieldtype": "Check", | |||||
"label": "Enable Comments", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "text_align", | |||||
"fieldtype": "Select", | |||||
"label": "Text Align", | |||||
"options": "Left\nCenter\nRight", | |||||
"fieldname": "text_align", | |||||
"fieldtype": "Select", | |||||
"label": "Text Align", | |||||
"options": "Left\nCenter\nRight", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "custom_javascript", | |||||
"fieldtype": "Section Break", | |||||
"label": "Custom Javascript", | |||||
"fieldname": "custom_javascript", | |||||
"fieldtype": "Section Break", | |||||
"label": "Custom Javascript", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"description": "Add code as <script>", | |||||
"fieldname": "insert_code", | |||||
"fieldtype": "Check", | |||||
"label": "Insert Code", | |||||
"description": "Add code as <script>", | |||||
"fieldname": "insert_code", | |||||
"fieldtype": "Check", | |||||
"label": "Insert Code", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"depends_on": "insert_code", | |||||
"fieldname": "javascript", | |||||
"fieldtype": "Code", | |||||
"label": "Javascript", | |||||
"options": "Javascript", | |||||
"depends_on": "insert_code", | |||||
"fieldname": "javascript", | |||||
"fieldtype": "Code", | |||||
"label": "Javascript", | |||||
"options": "Javascript", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "custom_css", | |||||
"fieldtype": "Section Break", | |||||
"label": "Custom CSS", | |||||
"fieldname": "custom_css", | |||||
"fieldtype": "Section Break", | |||||
"label": "Custom CSS", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"fieldname": "insert_style", | |||||
"fieldtype": "Check", | |||||
"label": "Insert Style", | |||||
"fieldname": "insert_style", | |||||
"fieldtype": "Check", | |||||
"label": "Insert Style", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | |||||
}, | |||||
{ | { | ||||
"depends_on": "insert_style", | |||||
"fieldname": "css", | |||||
"fieldtype": "Code", | |||||
"label": "CSS", | |||||
"options": "CSS", | |||||
"depends_on": "insert_style", | |||||
"fieldname": "css", | |||||
"fieldtype": "Code", | |||||
"label": "CSS", | |||||
"options": "CSS", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
} | } | ||||
], | |||||
"icon": "icon-file-alt", | |||||
"idx": 1, | |||||
"max_attachments": 20, | |||||
"modified": "2014-05-26 03:36:51.942919", | |||||
"modified_by": "Administrator", | |||||
"module": "Website", | |||||
"name": "Web Page", | |||||
"owner": "Administrator", | |||||
], | |||||
"icon": "icon-file-alt", | |||||
"idx": 1, | |||||
"max_attachments": 20, | |||||
"modified": "2014-06-17 05:56:30.267409", | |||||
"modified_by": "Administrator", | |||||
"module": "Website", | |||||
"name": "Web Page", | |||||
"owner": "Administrator", | |||||
"permissions": [ | "permissions": [ | ||||
{ | { | ||||
"cancel": 0, | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "Website Manager", | |||||
"submit": 0, | |||||
"cancel": 0, | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "Website Manager", | |||||
"submit": 0, | |||||
"write": 1 | "write": 1 | ||||
} | } | ||||
] | ] | ||||
} | |||||
} |
@@ -2,14 +2,43 @@ | |||||
# MIT License. See license.txt | # MIT License. See license.txt | ||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe, os, time, re | |||||
import frappe, re | |||||
import requests, requests.exceptions | import requests, requests.exceptions | ||||
from frappe.website.website_generator import WebsiteGenerator | from frappe.website.website_generator import WebsiteGenerator | ||||
from frappe.website.utils import cleanup_page_name | |||||
from frappe.utils import cint | |||||
from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow | |||||
from frappe.website.utils import find_first_image, get_comment_list | |||||
template = "templates/generators/web_page.html" | |||||
condition_field = "published" | |||||
class WebPage(WebsiteGenerator): | class WebPage(WebsiteGenerator): | ||||
save_versions = True | save_versions = True | ||||
def get_context(self, context): | |||||
if context.slideshow: | |||||
context.update(get_slideshow(self)) | |||||
if self.enable_comments: | |||||
context.comment_list = get_comment_list(self.doctype, self.name) | |||||
context.update({ | |||||
"style": self.css or "", | |||||
"script": self.javascript or "" | |||||
}) | |||||
context.metatags = { | |||||
"name": self.title, | |||||
"description": self.description or (self.main_section or "")[:150] | |||||
} | |||||
image = find_first_image(self.main_section or "") | |||||
if image: | |||||
context.metatags["image"] = image | |||||
if not context.header: | |||||
context.header = self.title | |||||
return context | |||||
def check_broken_links(): | def check_broken_links(): | ||||
cnt = 0 | cnt = 0 | ||||
@@ -4,17 +4,250 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe | import frappe | ||||
from frappe.website.website_generator import WebsiteGenerator | from frappe.website.website_generator import WebsiteGenerator | ||||
from frappe.templates.generators.website_group import clear_cache | |||||
from frappe.model.naming import make_autoname | |||||
from frappe.website.render import can_cache | |||||
from frappe.templates.website_group.forum import get_post_list_html | |||||
no_cache = True | |||||
template = "templates/generators/website_group.html" | |||||
class WebsiteGroup(WebsiteGenerator): | class WebsiteGroup(WebsiteGenerator): | ||||
def get_page_title(self): | def get_page_title(self): | ||||
return self.group_title | return self.group_title | ||||
def on_update(self): | def on_update(self): | ||||
WebsiteGenerator.on_update(self) | WebsiteGenerator.on_update(self) | ||||
clear_cache(website_group=self.name) | clear_cache(website_group=self.name) | ||||
def after_insert(self): | def after_insert(self): | ||||
clear_cache(path=self.parent_website_route) | clear_cache(path=self.parent_website_route) | ||||
def get_context(self, context): | |||||
group, view = guess_group_view(context) | |||||
try: | |||||
if not has_access(context.access, view): | |||||
raise frappe.PermissionError | |||||
return get_group_context(group, view, context) | |||||
except frappe.DoesNotExistError: | |||||
return { | |||||
"content": '<div class="alert alert-danger full-page">' | |||||
'The page you are looking for does not exist.</div>' | |||||
} | |||||
except frappe.PermissionError: | |||||
return { | |||||
"content": '<div class="alert alert-danger full-page">' | |||||
'You are not permitted to view this page.</div>' | |||||
} | |||||
return context | |||||
def get_group_context(group, view, context): | |||||
cache_key = "website_group_context:{}:{}".format(group, view) | |||||
views = get_views(context.doc.group_type) | |||||
view = frappe._dict(views.get(view)) | |||||
if can_cache(view.no_cache): | |||||
group_context = frappe.cache().get_value(cache_key) | |||||
if group_context: | |||||
return group_context | |||||
group_context = build_group_context(group, view, views, context) | |||||
if can_cache(view.get("no_cache")): | |||||
frappe.cache().set_value(cache_key, group_context) | |||||
return group_context | |||||
def build_group_context(group, view, views, context): | |||||
title = "{} - {}".format(context.doc.group_title, view.get("label")) | |||||
group_context = frappe._dict({ | |||||
"group": context.doc, | |||||
"view": view, | |||||
"views": [v[1] for v in sorted(views.iteritems(), key=lambda (k, v): v.get("idx"))], | |||||
"title": title, | |||||
"pathname": context.pathname | |||||
}) | |||||
group_context.update(build_view_context(group_context)) | |||||
return group_context | |||||
def build_view_context(context): | |||||
from frappe.templates.website_group.post import get_post_context | |||||
if context.view.name in ("popular", "feed", "open", "closed", "upcoming", "past"): | |||||
context.post_list_html = get_post_list_html(context.group.name, context.view.name) | |||||
elif context.view.name == "edit": | |||||
context.post = frappe.get_doc("Post", frappe.form_dict.name).as_dict() | |||||
if context.post.assigned_to: | |||||
context.user = frappe.get_doc("User", context.post.assigned_to) | |||||
elif context.view.name == "settings": | |||||
context.users = frappe.db.sql("""select p.*, wsp.`read`, wsp.`write`, wsp.`admin` | |||||
from `tabUser` p, `tabWebsite Route Permission` wsp | |||||
where wsp.website_route=%s and wsp.user=p.name""", context.pathname, as_dict=True) | |||||
elif context.view.name == "post": | |||||
context.update(get_post_context(context)) | |||||
return context | |||||
def guess_group_view(context): | |||||
group = context.docname | |||||
view = frappe.form_dict.view or get_default_view(context.doc.group_type) | |||||
return group, view | |||||
def get_default_view(group_type): | |||||
for view, opts in get_views(group_type).iteritems(): | |||||
if opts.get("default"): | |||||
return view | |||||
def get_views(group_type=None): | |||||
if group_type: | |||||
group_views = frappe._dict(views[group_type]) | |||||
else: | |||||
group_views = {} | |||||
for group_type in views: | |||||
group_views.update(views[group_type].copy()) | |||||
group_views.update(common_views) | |||||
if group_type == "Forum": | |||||
group_views["post"]["upvote"] = True | |||||
return group_views | |||||
def has_access(access, view): | |||||
if view=="settings": | |||||
return access.get("admin") | |||||
elif view in ("add", "edit"): | |||||
return access.get("write") | |||||
else: | |||||
return access.get("read") | |||||
def clear_cache(path=None, website_group=None): | |||||
from frappe.templates.website_group.post import clear_post_cache | |||||
if path: | |||||
website_groups = [frappe.db.get_value("Website Route", path, "docname")] | |||||
elif website_group: | |||||
website_groups = [website_group] | |||||
else: | |||||
clear_post_cache() | |||||
website_groups = frappe.db.sql_list("""select name from `tabWebsite Group`""") | |||||
cache = frappe.cache() | |||||
all_views = get_views() | |||||
for group in website_groups: | |||||
for view in all_views: | |||||
cache.delete_value("website_group_context:{}:{}".format(group, view)) | |||||
def clear_event_cache(): | |||||
for group in frappe.db.sql_list("""select name from `tabWebsite Group` where group_type='Event'"""): | |||||
clear_cache(website_group=group) | |||||
def clear_cache_on_doc_event(doc, method, *args, **kwargs): | |||||
clear_cache(path=doc.website_route, website_group=doc.website_group) | |||||
def get_pathname(group): | |||||
return frappe.db.get_value("Website Route", {"ref_doctype": "Website Group", | |||||
"docname": group}) | |||||
views = { | |||||
"Forum": { | |||||
"popular": { | |||||
"name": "popular", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Popular", | |||||
"icon": "icon-heart", | |||||
"default": True, | |||||
"upvote": True, | |||||
"idx": 1 | |||||
}, | |||||
"feed": { | |||||
"name": "feed", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Feed", | |||||
"icon": "icon-rss", | |||||
"upvote": True, | |||||
"idx": 2 | |||||
} | |||||
}, | |||||
"Tasks": { | |||||
"open": { | |||||
"name": "open", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Open", | |||||
"icon": "icon-inbox", | |||||
"default": True, | |||||
"upvote": True, | |||||
"idx": 1 | |||||
}, | |||||
"closed": { | |||||
"name": "closed", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Closed", | |||||
"icon": "icon-smile", | |||||
"idx": 2 | |||||
} | |||||
}, | |||||
"Events": { | |||||
"upcoming": { | |||||
"name": "upcoming", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Upcoming", | |||||
"icon": "icon-calendar", | |||||
"default": True, | |||||
"idx": 1 | |||||
}, | |||||
"past": { | |||||
"name": "past", | |||||
"template_path": "templates/website_group/forum.html", | |||||
"label": "Past", | |||||
"icon": "icon-time", | |||||
"idx": 2 | |||||
} | |||||
} | |||||
} | |||||
common_views = { | |||||
"post": { | |||||
"name": "post", | |||||
"template_path": "templates/website_group/post.html", | |||||
"label": "Post", | |||||
"icon": "icon-comments", | |||||
"hidden": True, | |||||
"no_cache": True, | |||||
"idx": 3 | |||||
}, | |||||
"edit": { | |||||
"name": "edit", | |||||
"template_path": "templates/website_group/edit_post.html", | |||||
"label": "Edit Post", | |||||
"icon": "icon-pencil", | |||||
"hidden": True, | |||||
"no_cache": True, | |||||
"idx": 4 | |||||
}, | |||||
"add": { | |||||
"name": "add", | |||||
"template_path": "templates/website_group/edit_post.html", | |||||
"label": "Add Post", | |||||
"icon": "icon-plus", | |||||
"hidden": True, | |||||
"idx": 5 | |||||
}, | |||||
"settings": { | |||||
"name": "settings", | |||||
"template_path": "templates/website_group/settings.html", | |||||
"label": "Settings", | |||||
"icon": "icon-cog", | |||||
"hidden": True, | |||||
"idx": 6 | |||||
} | |||||
} |
@@ -1,7 +1,7 @@ | |||||
{ | { | ||||
"allow_rename": 1, | "allow_rename": 1, | ||||
"autoname": "field:page_name", | "autoname": "field:page_name", | ||||
"creation": "2013-11-18 15:38:40.000000", | |||||
"creation": "2013-11-18 15:38:40", | |||||
"docstatus": 0, | "docstatus": 0, | ||||
"doctype": "DocType", | "doctype": "DocType", | ||||
"fields": [ | "fields": [ | ||||
@@ -38,8 +38,9 @@ | |||||
}, | }, | ||||
{ | { | ||||
"fieldname": "docname", | "fieldname": "docname", | ||||
"fieldtype": "Data", | |||||
"fieldtype": "Dynamic Link", | |||||
"label": "Docname", | "label": "Docname", | ||||
"options": "ref_doctype", | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"read_only": 1 | "read_only": 1 | ||||
}, | }, | ||||
@@ -50,10 +51,16 @@ | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | }, | ||||
{ | { | ||||
"fieldname": "website_template", | |||||
"fieldtype": "Link", | |||||
"label": "Website Template", | |||||
"options": "Website Template", | |||||
"fieldname": "template", | |||||
"fieldtype": "Read Only", | |||||
"label": "Template", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "controller", | |||||
"fieldtype": "Read Only", | |||||
"label": "Controller", | |||||
"options": "", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | }, | ||||
{ | { | ||||
@@ -112,7 +119,7 @@ | |||||
} | } | ||||
], | ], | ||||
"idx": 1, | "idx": 1, | ||||
"modified": "2014-02-24 12:46:59.000000", | |||||
"modified": "2014-06-27 05:04:57.721756", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Website", | "module": "Website", | ||||
"name": "Website Route", | "name": "Website Route", | ||||
@@ -22,59 +22,45 @@ class WebsiteRoute(NestedSet): | |||||
return url | return url | ||||
def validate(self): | def validate(self): | ||||
if self.get_url() != self.name: | |||||
self.rename() | |||||
self.check_if_page_name_is_unique() | self.check_if_page_name_is_unique() | ||||
self.make_private_if_parent_is_private() | |||||
if not self.is_new(): | |||||
self.renumber_if_moved() | |||||
self.set_idx() | |||||
def renumber_if_moved(self): | |||||
current_parent = frappe.db.get_value("Website Route", self.name, "parent_website_route") | |||||
if current_parent and current_parent != self.parent_website_route: | |||||
# move-up | |||||
# sitemap | |||||
frappe.db.sql("""update `tabWebsite Route` set idx=idx-1 | |||||
where parent_website_route=%s and idx>%s""", (current_parent, self.idx)) | |||||
# source table | |||||
frappe.db.sql("""update `tab{0}` set idx=idx-1 | |||||
where parent_website_route=%s and idx>%s""".format(self.ref_doctype), | |||||
(current_parent, self.idx)) | |||||
self.idx = None | |||||
if not frappe.flags.in_sync_website: | |||||
self.make_private_if_parent_is_private() | |||||
def on_update(self): | def on_update(self): | ||||
if not frappe.flags.in_rebuild_config: | |||||
if self.get_url() != self.name: | |||||
self.rename() | |||||
if not frappe.flags.in_sync_website: | |||||
NestedSet.on_update(self) | NestedSet.on_update(self) | ||||
self.clear_cache() | self.clear_cache() | ||||
def set_idx(self): | |||||
if self.parent_website_route: | |||||
if self.idx == None: | |||||
self.set_idx_as_last() | |||||
def set_idx_as_last(self): | |||||
# new, append | |||||
self.idx = int(frappe.db.sql("""select ifnull(max(ifnull(idx, -1)), -1) | |||||
from `tabWebsite Route` | |||||
where ifnull(parent_website_route, '')=%s and name!=%s""", | |||||
(self.parent_website_route or '', | |||||
self.name))[0][0]) + 1 | |||||
def rename(self): | |||||
def rename(self, new_page_name=None, new_parent_website_route=None): | |||||
self.old_name = self.name | self.old_name = self.name | ||||
self.old_parent_website_route = self.parent_website_route | |||||
# get new route | |||||
if new_page_name != None: | |||||
self.page_name = new_page_name | |||||
if new_parent_website_route != None: | |||||
self.parent_website_route = new_parent_website_route | |||||
self.name = self.get_url() | self.name = self.get_url() | ||||
frappe.db.sql("""update `tabWebsite Route` set name=%s where name=%s""", | |||||
(self.name, self.old_name)) | |||||
# update values (don't run triggers) | |||||
frappe.db.sql("""update `tabWebsite Route` set | |||||
name=%s, page_name=%s, parent_website_route=%s where name=%s""", | |||||
(self.name, self.page_name, self.parent_website_route, self.old_name)) | |||||
self.rename_links() | self.rename_links() | ||||
self.rename_descendants() | self.rename_descendants() | ||||
self.clear_cache(self.old_name) | 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): | def rename_links(self): | ||||
for doctype in frappe.db.sql_list("""select parent from tabDocField where fieldtype='Link' and | |||||
fieldname='parent_website_route' and options='Website Route'"""): | |||||
for doctype in frappe.db.sql_list("""select parent from tabDocField | |||||
where fieldtype='Link' | |||||
and fieldname='parent_website_route' | |||||
and options='Website Route' | |||||
and parent!='Website Route'"""): | |||||
for name in frappe.db.sql_list("""select name from `tab{}` | for name in frappe.db.sql_list("""select name from `tab{}` | ||||
where parent_website_route=%s""".format(doctype), self.old_name): | where parent_website_route=%s""".format(doctype), self.old_name): | ||||
frappe.db.set_value(doctype, name, "parent_website_route", self.name) | frappe.db.set_value(doctype, name, "parent_website_route", self.name) | ||||
@@ -82,7 +68,7 @@ class WebsiteRoute(NestedSet): | |||||
def rename_descendants(self): | def rename_descendants(self): | ||||
# rename children | # rename children | ||||
for name in frappe.db.sql_list("""select name from `tabWebsite Route` | for name in frappe.db.sql_list("""select name from `tabWebsite Route` | ||||
where parent_website_route=%s""", self.name): | |||||
where parent_website_route=%s""", self.old_name): | |||||
child = frappe.get_doc("Website Route", name) | child = frappe.get_doc("Website Route", name) | ||||
child.parent_website_route = self.name | child.parent_website_route = self.name | ||||
child.save() | child.save() | ||||
@@ -92,7 +78,7 @@ class WebsiteRoute(NestedSet): | |||||
if self.page_or_generator == "Page": | if self.page_or_generator == "Page": | ||||
# for a page, name and website sitemap config form a unique key | # for a page, name and website sitemap config form a unique key | ||||
exists = frappe.db.sql("""select name from `tabWebsite Route` | exists = frappe.db.sql("""select name from `tabWebsite Route` | ||||
where name=%s and website_template!=%s""", (self.name, self.website_template)) | |||||
where name=%s""", self.name) | |||||
else: | else: | ||||
# for a generator, name, ref_doctype and docname make a unique key | # for a generator, name, ref_doctype and docname make a unique key | ||||
exists = frappe.db.sql("""select name from `tabWebsite Route` | exists = frappe.db.sql("""select name from `tabWebsite Route` | ||||
@@ -119,39 +105,13 @@ class WebsiteRoute(NestedSet): | |||||
def clear_cache(self, name=None): | def clear_cache(self, name=None): | ||||
from frappe.website.render import clear_cache | from frappe.website.render import clear_cache | ||||
clear_cache(name or self.name) | |||||
if self.parent_website_route: | |||||
clear_cache(self.parent_website_route) | |||||
def add_to_sitemap(options): | |||||
website_route = frappe.new_doc("Website Route") | |||||
for key in sitemap_fields: | |||||
website_route.set(key, options.get(key)) | |||||
if not website_route.page_name: | |||||
website_route.page_name = options.get("link_name") | |||||
website_route.website_template = options.get("link_name") | |||||
website_route.insert(ignore_permissions=True) | |||||
return website_route.idx | |||||
def update_sitemap(website_route, options): | |||||
website_route = frappe.get_doc("Website Route", website_route) | |||||
for key in sitemap_fields: | |||||
website_route.set(key, options.get(key)) | |||||
if not website_route.page_name: | |||||
# for pages | |||||
website_route.page_name = options.get("link_name") | |||||
website_route.website_template = options.get("link_name") | |||||
website_route.ignore_links = True | |||||
website_route.save(ignore_permissions=True) | |||||
if name: | |||||
clear_cache(name) | |||||
else: | |||||
if self.parent_website_route: | |||||
clear_cache(self.parent_website_route) | |||||
return website_route.idx | |||||
clear_cache(self.name) | |||||
def remove_sitemap(page_name=None, ref_doctype=None, docname=None): | def remove_sitemap(page_name=None, ref_doctype=None, docname=None): | ||||
if page_name: | if page_name: | ||||
@@ -159,11 +119,3 @@ def remove_sitemap(page_name=None, ref_doctype=None, docname=None): | |||||
elif ref_doctype and docname: | elif ref_doctype and docname: | ||||
frappe.delete_doc("Website Route", frappe.db.sql_list("""select name from `tabWebsite Route` | frappe.delete_doc("Website Route", frappe.db.sql_list("""select name from `tabWebsite Route` | ||||
where ref_doctype=%s and docname=%s""", (ref_doctype, docname)), ignore_permissions=True, force=True) | where ref_doctype=%s and docname=%s""", (ref_doctype, docname)), ignore_permissions=True, force=True) | ||||
def cleanup_sitemap(): | |||||
"""remove sitemap records where its config do not exist anymore""" | |||||
to_delete = frappe.db.sql_list("""select name from `tabWebsite Route` ws | |||||
where not exists(select name from `tabWebsite Template` wsc | |||||
where wsc.name=ws.website_template)""") | |||||
frappe.delete_doc("Website Route", to_delete, ignore_permissions=True) |
@@ -9,7 +9,6 @@ import frappe | |||||
from frappe.model.document import Document | from frappe.model.document import Document | ||||
class WebsiteSlideshow(Document): | class WebsiteSlideshow(Document): | ||||
def on_update(self): | def on_update(self): | ||||
# a slide show can be in use and any change in it should get reflected | # a slide show can be in use and any change in it should get reflected | ||||
from frappe.website.render import clear_cache | from frappe.website.render import clear_cache | ||||
@@ -1,130 +0,0 @@ | |||||
{ | |||||
"autoname": "field:link_name", | |||||
"creation": "2013-11-18 15:35:00.000000", | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "System", | |||||
"fields": [ | |||||
{ | |||||
"fieldname": "page_or_generator", | |||||
"fieldtype": "Select", | |||||
"label": "Page or Generator", | |||||
"options": "Page\nGenerator", | |||||
"permlevel": 0, | |||||
"read_only": 1 | |||||
}, | |||||
{ | |||||
"fieldname": "ref_doctype", | |||||
"fieldtype": "Link", | |||||
"label": "Ref DocType", | |||||
"options": "DocType", | |||||
"permlevel": 0, | |||||
"read_only": 1 | |||||
}, | |||||
{ | |||||
"fieldname": "link_name", | |||||
"fieldtype": "Data", | |||||
"label": "Link Name", | |||||
"permlevel": 0, | |||||
"read_only": 1 | |||||
}, | |||||
{ | |||||
"fieldname": "page_title", | |||||
"fieldtype": "Data", | |||||
"label": "Page Title", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "base_template_path", | |||||
"fieldtype": "Data", | |||||
"label": "Base Template Path", | |||||
"permlevel": 0, | |||||
"read_only": 1 | |||||
}, | |||||
{ | |||||
"fieldname": "template_path", | |||||
"fieldtype": "Data", | |||||
"label": "Template Path", | |||||
"permlevel": 0, | |||||
"read_only": 1 | |||||
}, | |||||
{ | |||||
"fieldname": "controller", | |||||
"fieldtype": "Data", | |||||
"label": "Controller", | |||||
"permlevel": 0, | |||||
"read_only": 1 | |||||
}, | |||||
{ | |||||
"fieldname": "lastmod", | |||||
"fieldtype": "Data", | |||||
"label": "Lastmod", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "no_cache", | |||||
"fieldtype": "Check", | |||||
"label": "No Cache", | |||||
"permlevel": 0, | |||||
"read_only": 1 | |||||
}, | |||||
{ | |||||
"fieldname": "no_sitemap", | |||||
"fieldtype": "Check", | |||||
"label": "No Sitemap", | |||||
"permlevel": 0, | |||||
"read_only": 1 | |||||
}, | |||||
{ | |||||
"fieldname": "no_sidebar", | |||||
"fieldtype": "Check", | |||||
"label": "No Sidebar", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "page_name_field", | |||||
"fieldtype": "Data", | |||||
"label": "Page Name Field", | |||||
"permlevel": 0, | |||||
"read_only": 1 | |||||
}, | |||||
{ | |||||
"fieldname": "condition_field", | |||||
"fieldtype": "Data", | |||||
"label": "Condition Field", | |||||
"permlevel": 0, | |||||
"read_only": 1 | |||||
}, | |||||
{ | |||||
"fieldname": "sort_by", | |||||
"fieldtype": "Data", | |||||
"label": "Sort By", | |||||
"permlevel": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "sort_order", | |||||
"fieldtype": "Data", | |||||
"label": "Sort Order", | |||||
"permlevel": 0 | |||||
} | |||||
], | |||||
"idx": 1, | |||||
"modified": "2014-02-24 12:47:44.000000", | |||||
"modified_by": "Administrator", | |||||
"module": "Website", | |||||
"name": "Website Template", | |||||
"owner": "Administrator", | |||||
"permissions": [ | |||||
{ | |||||
"cancel": 0, | |||||
"create": 0, | |||||
"delete": 1, | |||||
"export": 0, | |||||
"permlevel": 0, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "System Manager", | |||||
"write": 0 | |||||
} | |||||
] | |||||
} |
@@ -1,142 +0,0 @@ | |||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||||
# MIT License. See license.txt | |||||
# For license information, please see license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
import frappe.utils | |||||
import os | |||||
from frappe import _ | |||||
from frappe.website.doctype.website_route.website_route import add_to_sitemap, update_sitemap, cleanup_sitemap | |||||
from frappe.utils.nestedset import rebuild_tree | |||||
from frappe.model.document import Document | |||||
class WebsiteTemplate(Document): | |||||
def after_insert(self): | |||||
if self.page_or_generator == "Page": | |||||
website_route = frappe.db.get_value("Website Route", | |||||
{"website_template": self.name, "page_or_generator": "Page"}) | |||||
opts = self.as_dict() | |||||
opts.update({"public_read": 1}) | |||||
if website_route: | |||||
update_sitemap(website_route, opts) | |||||
else: | |||||
add_to_sitemap(opts) | |||||
else: | |||||
condition = "" | |||||
if self.condition_field: | |||||
condition = " where ifnull(%s, 0)=1" % self.condition_field | |||||
for name in frappe.db.sql_list("""select name from `tab{doctype}` | |||||
{condition} order by idx asc, {sort_field} {sort_order}""".format( | |||||
doctype = self.ref_doctype, | |||||
condition = condition, | |||||
sort_field = getattr(self, "sort_field", "name"), | |||||
sort_order = getattr(self, "sort_order", "asc") | |||||
)): | |||||
doc = frappe.get_doc(self.ref_doctype, name) | |||||
# regenerate route | |||||
doc.run_method("on_update") | |||||
def rebuild_website_template(): | |||||
# TODO | |||||
frappe.flags.in_rebuild_config = True | |||||
frappe.db.sql("""delete from `tabWebsite Template`""") | |||||
for app in frappe.get_installed_apps(): | |||||
if app=="webnotes": app="frappe" | |||||
build_website_template(app) | |||||
cleanup_sitemap() | |||||
frappe.flags.in_rebuild_config = False | |||||
# enable nested set and rebuild | |||||
rebuild_tree("Website Route", "parent_website_route") | |||||
frappe.db.commit() | |||||
def build_website_template(app): | |||||
config = {"pages": {}, "generators":{}} | |||||
pages, generators = get_pages_and_generators(app) | |||||
for args in pages: | |||||
add_website_template(**args) | |||||
for args in generators: | |||||
add_website_template(**args) | |||||
frappe.db.commit() | |||||
def get_pages_and_generators(app): | |||||
pages = [] | |||||
generators = [] | |||||
app_path = frappe.get_app_path(app) | |||||
for config_type in ("pages", "generators"): | |||||
path = os.path.join(app_path, "templates", config_type) | |||||
if os.path.exists(path): | |||||
for fname in os.listdir(path): | |||||
fname = frappe.utils.cstr(fname) | |||||
if fname.split(".")[-1] in ("html", "xml", "js", "css"): | |||||
if config_type=="pages": | |||||
pages.append({"page_or_generator": "Page", "app": app, "path": path, | |||||
"fname":fname, "app_path":app_path}) | |||||
else: | |||||
generators.append({"page_or_generator": "Generator", "app": app, "path": path, | |||||
"fname":fname, "app_path":app_path}) | |||||
return pages, generators | |||||
def add_website_template(page_or_generator, app, path, fname, app_path): | |||||
name = fname[:-5] if fname.endswith(".html") else fname | |||||
wsc = frappe._dict({ | |||||
"doctype": "Website Template", | |||||
"page_or_generator": page_or_generator, | |||||
"link_name": name, | |||||
"template_path": os.path.relpath(os.path.join(path, fname), app_path), | |||||
}) | |||||
wsc.controller = get_template_controller(app, path, fname) | |||||
if wsc.controller: | |||||
# verbose print wsc.controller | |||||
module = frappe.get_module(wsc.controller) | |||||
wsc.no_cache = getattr(module, "no_cache", 0) | |||||
wsc.no_sitemap = wsc.no_cache or getattr(module, "no_sitemap", 0) | |||||
wsc.no_sidebar = wsc.no_sidebar or getattr(module, "no_sidebar", 0) | |||||
wsc.ref_doctype = getattr(module, "doctype", None) | |||||
wsc.page_name_field = getattr(module, "page_name_field", "page_name") | |||||
wsc.condition_field = getattr(module, "condition_field", None) | |||||
wsc.sort_by = getattr(module, "sort_by", "name") | |||||
wsc.sort_order = getattr(module, "sort_order", "asc") | |||||
wsc.base_template_path = getattr(module, "base_template_path", None) | |||||
wsc.page_title = getattr(module, "page_title", _(name.title())) | |||||
if frappe.db.exists("Website Template", wsc.link_name): | |||||
# found by earlier app, override | |||||
frappe.db.sql("""delete from `tabWebsite Template` where name=%s""", (wsc.link_name,)) | |||||
frappe.get_doc(wsc).insert() | |||||
return name | |||||
def get_template_controller(app, path, fname): | |||||
controller = None | |||||
controller_name = fname.split(".")[0].replace("-", "_") + ".py" | |||||
controller_path = os.path.join(path, controller_name) | |||||
if os.path.exists(controller_path): | |||||
controller = app + "." + os.path.relpath(controller_path[:-3], frappe.get_app_path(app)).replace(os.path.sep, ".") | |||||
return controller | |||||
@@ -107,6 +107,10 @@ $.extend(frappe, { | |||||
} catch(e) { | } catch(e) { | ||||
console.log(data.exc); | 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{ | } else{ | ||||
if(opts.btn) { | if(opts.btn) { | ||||
$(opts.btn).addClass("btn-success"); | $(opts.btn).addClass("btn-success"); | ||||
@@ -286,6 +290,10 @@ $.extend(frappe, { | |||||
$('[data-html-block]').each(function(i, section) { | $('[data-html-block]').each(function(i, section) { | ||||
var $section = $(section); | var $section = $(section); | ||||
var stype = $section.attr("data-html-block"); | var stype = $section.attr("data-html-block"); | ||||
// handle meta separately | |||||
if (stype==="meta_block") return; | |||||
var block_data = data[stype] || ""; | var block_data = data[stype] || ""; | ||||
// NOTE: use frappe.ready instead of $.ready for reliable execution | // NOTE: use frappe.ready instead of $.ready for reliable execution | ||||
@@ -307,6 +315,12 @@ $.extend(frappe, { | |||||
}); | }); | ||||
if(data.title) $("title").html(data.title); | 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 | // change id of current page | ||||
$(".page-container").attr("id", "page-" + data.path); | $(".page-container").attr("id", "page-" + data.path); | ||||
@@ -1,24 +1,23 @@ | |||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | ||||
// MIT License. See license.txt" | // MIT License. See license.txt" | ||||
frappe.pages['sitemap-browser'].onload = function(wrapper) { | |||||
frappe.pages['sitemap-browser'].onload = function(wrapper) { | |||||
frappe.ui.make_app_page({ | frappe.ui.make_app_page({ | ||||
parent: wrapper, | parent: wrapper, | ||||
title: 'Sitemap Browser', | title: 'Sitemap Browser', | ||||
}); | |||||
}); | |||||
wrapper.appframe.add_module_icon("Website") | wrapper.appframe.add_module_icon("Website") | ||||
wrapper.appframe.set_title_right('Refresh', function() { | |||||
wrapper.appframe.set_title_right('Refresh', function() { | |||||
frappe.website.sitemap.tree.rootnode.reload(); | frappe.website.sitemap.tree.rootnode.reload(); | ||||
}); | }); | ||||
$(wrapper) | $(wrapper) | ||||
.find(".layout-side-section") | .find(".layout-side-section") | ||||
.html('<div class="text-muted">'+ | |||||
__('Click on a link to get options to expand get options ') + | |||||
__('Add') + ' / ' + __('Edit') + ' / '+ __('Remove') + '.</div>') | |||||
.html('<div class="text-muted">'+ | |||||
__('Click on a link to get options') + '</div>') | |||||
frappe.website.sitemap = new frappe.website.SitemapBrowser( | |||||
frappe.website.sitemap = new frappe.website.SitemapBrowser( | |||||
$(wrapper) | $(wrapper) | ||||
.find(".layout-main-section") | .find(".layout-main-section") | ||||
.css({ | .css({ | ||||
@@ -34,31 +33,13 @@ frappe.website.SitemapBrowser = Class.extend({ | |||||
$(parent).empty(); | $(parent).empty(); | ||||
var me = this; | var me = this; | ||||
this.tree = new frappe.ui.Tree({ | this.tree = new frappe.ui.Tree({ | ||||
parent: $(parent), | |||||
parent: $(parent), | |||||
label: "Sitemap", | label: "Sitemap", | ||||
method: 'frappe.website.page.sitemap_browser.sitemap_browser.get_children', | method: 'frappe.website.page.sitemap_browser.sitemap_browser.get_children', | ||||
toolbar: [ | toolbar: [ | ||||
{ | { | ||||
toggle_btn: true, | toggle_btn: true, | ||||
}, | }, | ||||
{ | |||||
label: __("Update Parent"), | |||||
click: function(node, btn) { | |||||
me.update_parent(); | |||||
} | |||||
}, | |||||
{ | |||||
label: __("Up"), | |||||
click: function(node, btn) { | |||||
me.up_or_down("up"); | |||||
} | |||||
}, | |||||
{ | |||||
label: __("Down"), | |||||
click: function(node, btn) { | |||||
me.up_or_down("down"); | |||||
} | |||||
}, | |||||
{ | { | ||||
label: __("Open"), | label: __("Open"), | ||||
click: function(node, btn) { | click: function(node, btn) { | ||||
@@ -66,7 +47,7 @@ frappe.website.SitemapBrowser = Class.extend({ | |||||
} | } | ||||
} | } | ||||
] | ] | ||||
// drop: function(dragged_node, dropped_node, dragged_element, dropped_element) { | // drop: function(dragged_node, dropped_node, dragged_element, dropped_element) { | ||||
// frappe.website.sitemap.update_parent(dragged_node.label, dropped_node.label, function(r) { | // frappe.website.sitemap.update_parent(dragged_node.label, dropped_node.label, function(r) { | ||||
// if(!r.exc) { | // if(!r.exc) { | ||||
@@ -78,7 +59,7 @@ frappe.website.SitemapBrowser = Class.extend({ | |||||
}); | }); | ||||
this.tree.rootnode.$a | this.tree.rootnode.$a | ||||
.data('node-data', {value: "Sitemap", expandable:1}) | .data('node-data', {value: "Sitemap", expandable:1}) | ||||
.click(); | |||||
.click(); | |||||
}, | }, | ||||
selected_node: function() { | selected_node: function() { | ||||
return this.tree.$w.find('.tree-link.selected'); | return this.tree.$w.find('.tree-link.selected'); | ||||
@@ -87,67 +68,4 @@ frappe.website.SitemapBrowser = Class.extend({ | |||||
var node = this.selected_node(); | var node = this.selected_node(); | ||||
frappe.set_route("Form", "Website Route", node.data("label")); | frappe.set_route("Form", "Website Route", node.data("label")); | ||||
}, | }, | ||||
up_or_down: function(up_or_down) { | |||||
var node = this.tree.get_selected_node(); | |||||
frappe.call({ | |||||
method: "frappe.website.page.sitemap_browser.sitemap_browser.move", | |||||
args: { | |||||
"name": node.label, | |||||
"up_or_down": up_or_down | |||||
}, | |||||
callback: function(r) { | |||||
if(r.message==="ok") { | |||||
node.parent.insertBefore(up_or_down==="up" ? | |||||
node.parent.prev() : node.parent.next().next()); | |||||
//(node.parent_node || node).reload(); | |||||
} | |||||
} | |||||
}); | |||||
}, | |||||
update_parent: function() { | |||||
var me = this; | |||||
if(!this.move_dialog) { | |||||
this.move_dialog = new frappe.ui.Dialog({ | |||||
title: __("Move"), | |||||
fields: [ | |||||
{ | |||||
fieldtype: "Link", | |||||
fieldname: "new_parent", | |||||
label: "New Parent", | |||||
reqd: 1, | |||||
options: "Website Route" | |||||
}, | |||||
{ | |||||
fieldtype: "Button", | |||||
fieldname: "update", | |||||
label: "Update", | |||||
} | |||||
] | |||||
}); | |||||
this.move_dialog.get_input("update").on("click", function() { | |||||
var node = me.tree.get_selected_node(); | |||||
var values = me.move_dialog.get_values(); | |||||
if(!values) return; | |||||
me._update_parent(node.label, values.new_parent, function(r) { | |||||
me.move_dialog.hide(); | |||||
(node.parent_node || node).reload(); | |||||
}) | |||||
}); | |||||
} | |||||
this.move_dialog.show(); | |||||
this.move_dialog.get_input("new_parent").val(""); | |||||
}, | |||||
_update_parent: function(name, parent, callback) { | |||||
frappe.call({ | |||||
method: "frappe.website.page.sitemap_browser.sitemap_browser.update_parent", | |||||
args: { | |||||
"name": name, | |||||
"new_parent": parent | |||||
}, | |||||
callback: function(r) { | |||||
callback(r); | |||||
} | |||||
}); | |||||
} | |||||
}); | |||||
}); |
@@ -164,7 +164,9 @@ def clear_cache(path=None): | |||||
if path: | if path: | ||||
delete_page_cache(path) | delete_page_cache(path) | ||||
for p in frappe.db.sql_list('''select name from | |||||
`tabWebsite Route` where name like "{0}/%"'''.format(path.replace('"', '\"'))): | |||||
delete_page_cache(p) | |||||
else: | else: | ||||
for p in frappe.db.sql_list("""select name from `tabWebsite Route`"""): | for p in frappe.db.sql_list("""select name from `tabWebsite Route`"""): | ||||
if p is not None: | if p is not None: | ||||
@@ -24,21 +24,24 @@ def build_sitemap_options(path): | |||||
sitemap_options = frappe._dict(frappe.get_doc("Website Route", path).as_dict()) | sitemap_options = frappe._dict(frappe.get_doc("Website Route", path).as_dict()) | ||||
home_page = get_home_page() | home_page = get_home_page() | ||||
sitemap_config = frappe.get_doc("Website Template", | |||||
sitemap_options.get("website_template")).as_dict() | |||||
if sitemap_options.controller: | |||||
module = frappe.get_module(sitemap_options.controller) | |||||
# get sitemap config fields too | |||||
for fieldname in ("base_template_path", "template_path", "controller", | |||||
"no_cache", "no_sitemap", "page_name_field", "condition_field"): | |||||
sitemap_options[fieldname] = sitemap_config.get(fieldname) | |||||
# get sitemap config fields too | |||||
for prop in ("base_template_path", "template", "no_cache", "no_sitemap", | |||||
"condition_field"): | |||||
if hasattr(module, prop): | |||||
sitemap_options[prop] = getattr(module, prop) | |||||
sitemap_options.doctype = sitemap_options.ref_doctype | sitemap_options.doctype = sitemap_options.ref_doctype | ||||
sitemap_options.title = sitemap_options.page_title | sitemap_options.title = sitemap_options.page_title | ||||
sitemap_options.pathname = sitemap_options.name | sitemap_options.pathname = sitemap_options.name | ||||
# establish hierarchy | # establish hierarchy | ||||
sitemap_options.parents = frappe.db.sql("""select name, page_title from `tabWebsite Route` | |||||
where lft < %s and rgt > %s order by lft asc""", (sitemap_options.lft, sitemap_options.rgt), as_dict=True) | |||||
sitemap_options.parents = frappe.db.sql("""select name, page_title from | |||||
`tabWebsite Route` | |||||
where lft < %s and rgt > %s | |||||
order by lft asc""", (sitemap_options.lft, sitemap_options.rgt), as_dict=True) | |||||
if not sitemap_options.no_sidebar: | if not sitemap_options.no_sidebar: | ||||
sitemap_options.children = get_route_children(sitemap_options.pathname, home_page) | sitemap_options.children = get_route_children(sitemap_options.pathname, home_page) | ||||
@@ -70,14 +73,16 @@ def get_route_children(pathname, home_page=None): | |||||
if children: | if children: | ||||
# if children are from generator and sort order is specified, then get that condition | # if children are from generator and sort order is specified, then get that condition | ||||
website_template = frappe.get_doc("Website Template", children[0].website_template) | |||||
if website_template.sort_by!="name": | |||||
module = frappe.get_module(children[0].controller) | |||||
if hasattr(module, "sort_by"): | |||||
children = frappe.db.sql("""select t1.* from | children = frappe.db.sql("""select t1.* from | ||||
`tabWebsite Route` t1, `tab{ref_doctype}` t2 | `tabWebsite Route` t1, `tab{ref_doctype}` t2 | ||||
where ifnull(t1.parent_website_route,'')=%s | where ifnull(t1.parent_website_route,'')=%s | ||||
and t1.public_read=1 | and t1.public_read=1 | ||||
and t1.docname = t2.name | and t1.docname = t2.name | ||||
order by t2.{sort_by} {sort_order}""".format(**website_template.as_dict()), | |||||
order by {sort_by}""".format( | |||||
ref_doctype = children[0].ref_doctype, | |||||
sort_by = module.sort_by), | |||||
pathname, as_dict=True) | pathname, as_dict=True) | ||||
children = [frappe.get_doc("Website Route", pathname)] + children | children = [frappe.get_doc("Website Route", pathname)] + children | ||||
@@ -2,15 +2,15 @@ | |||||
# MIT License. See license.txt | # MIT License. See license.txt | ||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe, os, time | |||||
import frappe, os, time, sys | |||||
from frappe import _ | |||||
from frappe.utils import cint | from frappe.utils import cint | ||||
from markdown2 import markdown | from markdown2 import markdown | ||||
from frappe.website.sitemap import get_route_children, get_next | |||||
# from frappe.website.sitemap import get_route_children, get_next | |||||
def sync_statics(rebuild=False): | def sync_statics(rebuild=False): | ||||
s = sync() | s = sync() | ||||
s.verbose = True | |||||
while True: | while True: | ||||
s.start(rebuild) | s.start(rebuild) | ||||
frappe.db.commit() | frappe.db.commit() | ||||
@@ -19,17 +19,19 @@ def sync_statics(rebuild=False): | |||||
class sync(object): | class sync(object): | ||||
def start(self, rebuild=False): | def start(self, rebuild=False): | ||||
self.verbose = False | |||||
self.synced = [] | self.synced = [] | ||||
self.synced_paths = [] | |||||
self.to_insert = [] | |||||
self.to_update = [] | |||||
self.updated = 0 | self.updated = 0 | ||||
self.rebuild = rebuild | self.rebuild = rebuild | ||||
for app in frappe.get_installed_apps(): | for app in frappe.get_installed_apps(): | ||||
self.sync_for_app(app) | self.sync_for_app(app) | ||||
self.insert_and_update() | |||||
self.cleanup() | self.cleanup() | ||||
if self.updated: | |||||
print str(self.updated) + " files updated" | |||||
def sync_for_app(self, app): | def sync_for_app(self, app): | ||||
self.statics_path = frappe.get_app_path(app, "templates", "statics") | self.statics_path = frappe.get_app_path(app, "templates", "statics") | ||||
if os.path.exists(self.statics_path): | if os.path.exists(self.statics_path): | ||||
@@ -38,17 +40,15 @@ class sync(object): | |||||
def sync_folder(self, basepath, folders, files): | def sync_folder(self, basepath, folders, files): | ||||
folder_route = os.path.relpath(basepath, self.statics_path) | |||||
self.get_index_txt(basepath, files) | self.get_index_txt(basepath, files) | ||||
self.sync_index_page(basepath, files) | |||||
index_found = self.sync_index_page(basepath, files) | |||||
if not frappe.db.exists("Website Route", folder_route) and basepath!=self.statics_path: | |||||
if not index_found and basepath!=self.statics_path: | |||||
# not synced either by generator or by index.html | # not synced either by generator or by index.html | ||||
return | return | ||||
if self.index: | if self.index: | ||||
self.sync_using_given_index(basepath, folders, files) | self.sync_using_given_index(basepath, folders, files) | ||||
else: | else: | ||||
self.sync_alphabetically(basepath, folders, [filename for filename in files if filename.endswith('html') or filename.endswith('md')]) | self.sync_alphabetically(basepath, folders, [filename for filename in files if filename.endswith('html') or filename.endswith('md')]) | ||||
@@ -64,7 +64,7 @@ class sync(object): | |||||
fname = "index." + extn | fname = "index." + extn | ||||
if fname in files: | if fname in files: | ||||
self.sync_file(fname, os.path.join(basepath, fname), None) | self.sync_file(fname, os.path.join(basepath, fname), None) | ||||
return | |||||
return True | |||||
def sync_using_given_index(self, basepath, folders, files): | def sync_using_given_index(self, basepath, folders, files): | ||||
for i, page_name in enumerate(self.index): | for i, page_name in enumerate(self.index): | ||||
@@ -108,12 +108,42 @@ class sync(object): | |||||
["name", "idx", "static_file_timestamp", "docname"], as_dict=True) | ["name", "idx", "static_file_timestamp", "docname"], as_dict=True) | ||||
if route_details: | if route_details: | ||||
self.update_web_page(route_details, fpath, priority, parent_website_route) | |||||
page = self.get_route_details_for_update(route_details, fpath, | |||||
priority, parent_website_route) | |||||
if page: | |||||
self.to_update.append(page) | |||||
else: | else: | ||||
# Route does not exist, new page | # Route does not exist, new page | ||||
self.insert_web_page(route, fpath, page_name, priority, parent_website_route) | |||||
page = self.get_web_page_for_insert(route, fpath, page_name, | |||||
priority, parent_website_route) | |||||
self.to_insert.append(page) | |||||
def insert_web_page(self, route, fpath, page_name, priority, parent_website_route): | |||||
self.synced.append(route) | |||||
def insert_and_update(self): | |||||
if self.to_insert: | |||||
for i, page in enumerate(self.to_insert): | |||||
if self.verbose: | |||||
print "Inserting " + page.route | |||||
else: | |||||
sys.stdout.write("\rInserting statics {0}/{1}".format(i+1, len(self.to_insert))) | |||||
sys.stdout.flush() | |||||
self.insert_web_page(page) | |||||
if not self.verbose: print "" | |||||
if self.to_update: | |||||
for i, route_details in enumerate(self.to_update): | |||||
if self.verbose: | |||||
print "Updating " + route_details.name | |||||
else: | |||||
sys.stdout.write("\rUpdating statics {0}/{1}".format(i+1, len(self.to_update))) | |||||
sys.stdout.flush() | |||||
self.update_web_page(route_details) | |||||
if not self.verbose: print "" | |||||
def get_web_page_for_insert(self, route, fpath, page_name, priority, parent_website_route): | |||||
page = frappe.get_doc({ | page = frappe.get_doc({ | ||||
"doctype":"Web Page", | "doctype":"Web Page", | ||||
"idx": priority, | "idx": priority, | ||||
@@ -122,66 +152,75 @@ class sync(object): | |||||
"parent_website_route": parent_website_route | "parent_website_route": parent_website_route | ||||
}) | }) | ||||
page.fpath = fpath | |||||
page.route = route | |||||
page.update(get_static_content(fpath, page_name, route)) | page.update(get_static_content(fpath, page_name, route)) | ||||
return page | |||||
def insert_web_page(self, page): | |||||
try: | try: | ||||
page.insert() | page.insert() | ||||
except frappe.NameError: | |||||
except frappe.NameError, e: | |||||
print e | |||||
# page exists, if deleted static, delete it and try again | # page exists, if deleted static, delete it and try again | ||||
old_route = frappe.get_doc("Website Route", {"ref_doctype":"Web Page", | old_route = frappe.get_doc("Website Route", {"ref_doctype":"Web Page", | ||||
"docname": page.name}) | "docname": page.name}) | ||||
if old_route.static_file_timestamp and not os.path.exists(os.path.join(self.statics_path, | |||||
old_route.name)): | |||||
if old_route.static_file_timestamp and \ | |||||
not os.path.exists(os.path.join(self.statics_path, old_route.name)): | |||||
frappe.delete_doc("Web Page", page.name) | frappe.delete_doc("Web Page", page.name) | ||||
page.insert() # retry | page.insert() # retry | ||||
# update timestamp | # update timestamp | ||||
route_doc = frappe.get_doc("Website Route", {"ref_doctype": "Web Page", | route_doc = frappe.get_doc("Website Route", {"ref_doctype": "Web Page", | ||||
"docname": page.name}) | "docname": page.name}) | ||||
route_doc.static_file_timestamp = cint(os.path.getmtime(fpath)) | |||||
route_doc.static_file_timestamp = cint(os.path.getmtime(page.fpath)) | |||||
route_doc.save() | route_doc.save() | ||||
self.updated += 1 | |||||
print route_doc.name + " inserted" | |||||
self.synced.append(route) | |||||
def update_web_page(self, route_details, fpath, priority, parent_website_route): | |||||
def get_route_details_for_update(self, route_details, fpath, priority, parent_website_route): | |||||
out = None | |||||
if not route_details.docname: | if not route_details.docname: | ||||
print "Ignoring {0} because page found".format(route_details.name) | print "Ignoring {0} because page found".format(route_details.name) | ||||
return | return | ||||
if str(cint(os.path.getmtime(fpath)))!= route_details.static_file_timestamp \ | if str(cint(os.path.getmtime(fpath)))!= route_details.static_file_timestamp \ | ||||
or (cint(route_details.idx) != cint(priority) and (priority is not None) \ | or (cint(route_details.idx) != cint(priority) and (priority is not None) \ | ||||
or self.rebuild): | or self.rebuild): | ||||
page = frappe.get_doc("Web Page", route_details.docname) | |||||
page.update(get_static_content(fpath, route_details.docname, route_details.name)) | |||||
page.idx = priority | |||||
page.save() | |||||
out = route_details | |||||
out.idx = priority | |||||
out.fpath = fpath | |||||
route_doc = frappe.get_doc("Website Route", route_details.name) | |||||
route_doc.static_file_timestamp = cint(os.path.getmtime(fpath)) | |||||
route_doc.save() | |||||
return out | |||||
print route_doc.name + " updated" | |||||
self.updated += 1 | |||||
def update_web_page(self, route_details): | |||||
page = frappe.get_doc("Web Page", route_details.docname) | |||||
page.update(get_static_content(route_details.fpath, | |||||
route_details.docname, route_details.name)) | |||||
page.save() | |||||
self.synced.append(route_details.name) | |||||
route_doc = frappe.get_doc("Website Route", route_details.name) | |||||
route_doc.static_file_timestamp = cint(os.path.getmtime(route_details.fpath)) | |||||
route_doc.save() | |||||
def cleanup(self): | def cleanup(self): | ||||
if self.synced: | if self.synced: | ||||
# delete static web pages that are not in immediate list | |||||
frappe.delete_doc("Web Page", frappe.db.sql_list("""select docname | frappe.delete_doc("Web Page", frappe.db.sql_list("""select docname | ||||
from `tabWebsite Route` | from `tabWebsite Route` | ||||
where ifnull(static_file_timestamp,'')!='' and name not in ({}) | where ifnull(static_file_timestamp,'')!='' and name not in ({}) | ||||
order by (rgt-lft) asc""".format(', '.join(["%s"]*len(self.synced))), | order by (rgt-lft) asc""".format(', '.join(["%s"]*len(self.synced))), | ||||
tuple(self.synced))) | tuple(self.synced))) | ||||
else: | else: | ||||
# delete all static web pages | |||||
frappe.delete_doc("Web Page", frappe.db.sql_list("""select docname | frappe.delete_doc("Web Page", frappe.db.sql_list("""select docname | ||||
from `tabWebsite Route` | from `tabWebsite Route` | ||||
where ifnull(static_file_timestamp,'')!='' | where ifnull(static_file_timestamp,'')!='' | ||||
order by (rgt-lft) asc""")) | order by (rgt-lft) asc""")) | ||||
def delete_static_web_pages(): | |||||
for name in frappe.db.sql_list("""select docname from `tabWebsite Route` | |||||
where ifnull(static_file_timestamp,'')!=''"""): | |||||
frappe.db.sql("delete from `tabWeb Page` where name=%s", name) | |||||
def get_static_content(fpath, docname, route): | def get_static_content(fpath, docname, route): | ||||
d = frappe._dict({}) | d = frappe._dict({}) | ||||
@@ -197,21 +236,21 @@ def get_static_content(fpath, docname, route): | |||||
d.title = first_line[2:] | d.title = first_line[2:] | ||||
content = "\n".join(lines[1:]) | content = "\n".join(lines[1:]) | ||||
if "{index}" in content: | |||||
children = get_route_children(route) | |||||
html = frappe.get_template("templates/includes/static_index.html").render({ | |||||
"items":children}) | |||||
content = content.replace("{index}", html) | |||||
if "{next}" in content: | |||||
next_item = get_next(route) | |||||
html = "" | |||||
if next_item: | |||||
html = '''<p> | |||||
<br><a href="{name}" class="btn btn-primary"> | |||||
{page_title} <i class="icon-chevron-right"></i></a> | |||||
</p>'''.format(**next_item) | |||||
content = content.replace("{next}", html) | |||||
# if "{index}" in content: | |||||
# children = get_route_children(route) | |||||
# html = frappe.get_template("templates/includes/static_index.html").render({ | |||||
# "items":children}) | |||||
# content = content.replace("{index}", html) | |||||
# | |||||
# if "{next}" in content: | |||||
# next_item = get_next(route) | |||||
# html = "" | |||||
# if next_item: | |||||
# html = '''<p> | |||||
# <br><a href="{name}" class="btn btn-primary"> | |||||
# {page_title} <i class="icon-chevron-right"></i></a> | |||||
# </p>'''.format(**next_item) | |||||
# content = content.replace("{next}", html) | |||||
content = markdown(content) | content = markdown(content) | ||||
@@ -0,0 +1,116 @@ | |||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||||
# MIT License. See license.txt | |||||
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): | |||||
if app: | |||||
apps = [app] | |||||
else: | |||||
apps = frappe.get_installed_apps() | |||||
print "Resetting..." | |||||
render.clear_cache() | |||||
# delete all static web pages | |||||
statics.delete_static_web_pages() | |||||
# delete all routes (resetting) | |||||
frappe.db.sql("delete from `tabWebsite Route`") | |||||
print "Finding routes..." | |||||
routes, generators = [], [] | |||||
for app in apps: | |||||
routes += get_sync_pages(app) | |||||
generators += get_sync_generators(app) | |||||
sync_pages(routes) | |||||
sync_generators(generators) | |||||
# sync statics | |||||
statics_sync = statics.sync() | |||||
statics_sync.start() | |||||
def sync_pages(routes): | |||||
l = len(routes) | |||||
if l: | |||||
for i, r in enumerate(routes): | |||||
r.insert(ignore_permissions=True) | |||||
sys.stdout.write("\rUpdating pages {0}/{1}".format(i+1, l)) | |||||
sys.stdout.flush() | |||||
print "" | |||||
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): | |||||
app_path = frappe.get_app_path(app) | |||||
pages = [] | |||||
path = os.path.join(app_path, "templates", "pages") | |||||
if os.path.exists(path): | |||||
for fname in os.listdir(path): | |||||
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 = 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) | |||||
return pages | |||||
def get_sync_generators(app): | |||||
generators = [] | |||||
for doctype in frappe.get_hooks("website_generators", app_name = app): | |||||
condition, order_by = "", "name asc" | |||||
module = load_doctype_module(doctype) | |||||
if hasattr(module, "condition_field"): | |||||
condition = " where ifnull({0}, 0)=1 ".format(module.condition_field) | |||||
if hasattr(module, "sort_by"): | |||||
order_by = "{0} {1}".format(module.sort_by, module.sort_order) | |||||
for name in frappe.db.sql_list("select name from `tab{0}` {1} order by {2}".format(doctype, | |||||
condition, order_by)): | |||||
generators.append((doctype, name)) | |||||
return generators |
@@ -10,6 +10,8 @@ from jinja2.utils import concat | |||||
from jinja2 import meta | from jinja2 import meta | ||||
import re | import re | ||||
from sitemap import get_next | |||||
def render_blocks(context): | def render_blocks(context): | ||||
"""returns a dict of block name and its rendered content""" | """returns a dict of block name and its rendered content""" | ||||
@@ -27,7 +29,7 @@ def render_blocks(context): | |||||
for block, render in template.blocks.items(): | for block, render in template.blocks.items(): | ||||
out[block] = scrub_relative_urls(concat(render(template.new_context(context)))) | out[block] = scrub_relative_urls(concat(render(template.new_context(context)))) | ||||
_render_blocks(context["template_path"]) | |||||
_render_blocks(context["template"]) | |||||
# default blocks if not found | # default blocks if not found | ||||
if "title" not in out and out.get("header"): | if "title" not in out and out.get("header"): | ||||
@@ -39,19 +41,37 @@ def render_blocks(context): | |||||
if "header" not in out and out.get("title"): | if "header" not in out and out.get("title"): | ||||
out["header"] = out["title"] | out["header"] = out["title"] | ||||
if not out["header"].startswith("<h"): | |||||
if out.get("header") and not out["header"].startswith("<h"): | |||||
out["header"] = "<h2>" + out["header"] + "</h2>" | out["header"] = "<h2>" + out["header"] + "</h2>" | ||||
if "breadcrumbs" not in out: | if "breadcrumbs" not in out: | ||||
out["breadcrumbs"] = scrub_relative_urls( | out["breadcrumbs"] = scrub_relative_urls( | ||||
frappe.get_template("templates/includes/breadcrumbs.html").render(context)) | 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", ""): | if "<!-- no-sidebar -->" in out.get("content", ""): | ||||
out["no_sidebar"] = 1 | out["no_sidebar"] = 1 | ||||
if "<!-- title:" in out.get("content", ""): | if "<!-- title:" in out.get("content", ""): | ||||
out["title"] = re.findall('<!-- title:([^>]*) -->', out.get("content"))[0].strip() | out["title"] = re.findall('<!-- title:([^>]*) -->', out.get("content"))[0].strip() | ||||
if "{index}" in out.get("content", "") and context.get("children"): | |||||
html = frappe.get_template("templates/includes/static_index.html").render({ | |||||
"items": context["children"]}) | |||||
out["content"] = out["content"].replace("{index}", html) | |||||
if "{next}" in out.get("content", ""): | |||||
next_item = get_next(context["pathname"]) | |||||
if next_item: | |||||
if next_item.name[0]!="/": next_item.name = "/" + next_item.name | |||||
html = '''<p><br><a href="{name}" class="btn btn-primary"> | |||||
{page_title} <i class="icon-chevron-right"></i></a> | |||||
</p>'''.format(**next_item) | |||||
out["content"] = out["content"].replace("{next}", html) | |||||
if "sidebar" not in out and not out.get("no_sidebar"): | if "sidebar" not in out and not out.get("no_sidebar"): | ||||
out["sidebar"] = scrub_relative_urls( | out["sidebar"] = scrub_relative_urls( | ||||
frappe.get_template("templates/includes/sidebar.html").render(context)) | frappe.get_template("templates/includes/sidebar.html").render(context)) | ||||
@@ -20,6 +20,12 @@ def find_first_image(html): | |||||
def can_cache(no_cache=False): | def can_cache(no_cache=False): | ||||
return not (frappe.conf.disable_website_cache or no_cache) | return not (frappe.conf.disable_website_cache or no_cache) | ||||
def get_comment_list(doctype, name): | |||||
return frappe.db.sql("""select | |||||
comment, comment_by_fullname, creation | |||||
from `tabComment` where comment_doctype=%s | |||||
and comment_docname=%s order by creation""", (doctype, name), as_dict=1) or [] | |||||
def get_home_page(): | def get_home_page(): | ||||
def _get_home_page(): | def _get_home_page(): | ||||
role_home_page = frappe.get_hooks("role_home_page") | role_home_page = frappe.get_hooks("role_home_page") | ||||
@@ -7,129 +7,117 @@ from frappe.model.document import Document | |||||
from frappe.model.naming import append_number_if_name_exists | from frappe.model.naming import append_number_if_name_exists | ||||
from frappe.website.utils import cleanup_page_name | from frappe.website.utils import cleanup_page_name | ||||
from frappe.utils import now | from frappe.utils import now | ||||
from frappe.modules import get_module_name, load_doctype_module | |||||
from frappe.website.doctype.website_route.website_route import add_to_sitemap, update_sitemap, remove_sitemap | |||||
from frappe.website.doctype.website_route.website_route import remove_sitemap | |||||
class WebsiteGenerator(Document): | class WebsiteGenerator(Document): | ||||
def autoname(self): | def autoname(self): | ||||
self.setup_generator() | |||||
if not self.website_template: return | |||||
self.name = self.get_page_name() | self.name = self.get_page_name() | ||||
append_number_if_name_exists(self) | append_number_if_name_exists(self) | ||||
def onload(self): | def onload(self): | ||||
self.get("__onload").website_route = frappe.db.get_value("Website Route", {"ref_doctype": self.doctype, "docname": self.name}) | |||||
def set_page_name(self): | |||||
"""set page name based on parent page_name and title""" | |||||
page_name = cleanup_page_name(self.get_page_title()) | |||||
if self.is_new(): | |||||
self.set(self.website_template.page_name_field, page_name) | |||||
else: | |||||
frappe.db.set(self, self.website_template.page_name_field, page_name) | |||||
return page_name | |||||
self.get("__onload").website_route = self.get_route() | |||||
def get_parent_website_route(self): | def get_parent_website_route(self): | ||||
return self.parent_website_route | |||||
def setup_generator(self): | |||||
if not hasattr(self, "website_template"): | |||||
website_template = frappe.db.get_values("Website Template", | |||||
{"ref_doctype": self.doctype}, "*") | |||||
if website_template: | |||||
self.website_template = website_template[0] | |||||
else: | |||||
self.website_template = None | |||||
return self.get("parent_website_route", "") | |||||
def on_update(self): | def on_update(self): | ||||
self.update_sitemap() | self.update_sitemap() | ||||
if getattr(self, "save_versions", False): | if getattr(self, "save_versions", False): | ||||
frappe.add_version(self) | frappe.add_version(self) | ||||
def after_rename(self, olddn, newdn, merge): | |||||
self.setup_generator() | |||||
if not self.website_template: return | |||||
def get_route(self): | |||||
parent = self.get_parent_website_route() | |||||
return ((parent + "/") if parent else "") + self.get_page_name() | |||||
frappe.db.sql("""update `tabWebsite Route` | |||||
set docname=%s where ref_doctype=%s and docname=%s""", (newdn, self.doctype, olddn)) | |||||
def get_route_docname(self, name=None): | |||||
return frappe.db.get_value("Website Route", | |||||
{"ref_doctype":self.doctype, "docname": name or self.name}) | |||||
if merge: | |||||
remove_sitemap(ref_doctype=self.doctype, docname=olddn) | |||||
def after_rename(self, olddn, newdn, merge): | |||||
if self.is_condition_field_enabled(): | |||||
self.update_route(self.get_route_docname()) | |||||
def on_trash(self): | def on_trash(self): | ||||
self.setup_generator() | |||||
if not self.website_template: return | |||||
remove_sitemap(ref_doctype=self.doctype, docname=self.name) | remove_sitemap(ref_doctype=self.doctype, docname=self.name) | ||||
def is_condition_field_enabled(self): | |||||
self.controller_module = load_doctype_module(self.doctype) | |||||
if hasattr(self.controller_module, "condition_field"): | |||||
return self.get(self.controller_module.condition_field) and True or False | |||||
else: | |||||
return True | |||||
def update_sitemap(self): | def update_sitemap(self): | ||||
self.setup_generator() | |||||
if not self.website_template: return | |||||
# update route of all descendants | |||||
route_docname = self.get_route_docname() | |||||
if self.website_template.condition_field and \ | |||||
not self.get(self.website_template.condition_field): | |||||
# condition field failed, remove and return! | |||||
remove_sitemap(ref_doctype=self.doctype, docname=self.name) | |||||
if not self.is_condition_field_enabled(): | |||||
frappe.delete_doc("Website Route", route_docname, ignore_permissions=True) | |||||
return | return | ||||
self.add_or_update_sitemap() | |||||
if route_docname: | |||||
self.update_route(route_docname) | |||||
else: | |||||
self.insert_route() | |||||
def add_or_update_sitemap(self): | |||||
page_name = self.get_page_name() | |||||
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()) | |||||
existing_site_map = frappe.db.get_value("Website Route", {"ref_doctype": self.doctype, | |||||
"docname": self.name}) | |||||
route.idx = self.idx | |||||
route.page_title = self.get_page_title() | |||||
self.update_permissions(route) | |||||
route.save() | |||||
def insert_route(self): | |||||
if self.modified: | if self.modified: | ||||
# for sitemap.xml | |||||
lastmod = frappe.utils.get_datetime(self.modified).strftime("%Y-%m-%d") | lastmod = frappe.utils.get_datetime(self.modified).strftime("%Y-%m-%d") | ||||
else: | else: | ||||
lastmod = now() | lastmod = now() | ||||
opts = frappe._dict({ | |||||
route = frappe.new_doc("Website Route") | |||||
route.update({ | |||||
"page_or_generator": "Generator", | "page_or_generator": "Generator", | ||||
"ref_doctype":self.doctype, | "ref_doctype":self.doctype, | ||||
"idx": self.idx, | "idx": self.idx, | ||||
"docname": self.name, | "docname": self.name, | ||||
"page_name": page_name, | |||||
"link_name": self.website_template.name, | |||||
"page_name": self.get_page_name(), | |||||
"controller": get_module_name(self.doctype, self.meta.module), | |||||
"template": self.controller_module.template, | |||||
"lastmod": lastmod, | "lastmod": lastmod, | ||||
"parent_website_route": self.get_parent_website_route(), | "parent_website_route": self.get_parent_website_route(), | ||||
"page_title": self.get_page_title(), | |||||
"public_read": 1 if not self.website_template.no_sidebar else 0 | |||||
"page_title": self.get_page_title() | |||||
}) | }) | ||||
self.update_permissions(opts) | |||||
if existing_site_map: | |||||
idx = update_sitemap(existing_site_map, opts) | |||||
else: | |||||
idx = add_to_sitemap(opts) | |||||
if idx!=None and self.idx != idx: | |||||
frappe.db.set(self, "idx", idx) | |||||
self.update_permissions(route) | |||||
route.ignore_links = True | |||||
route.insert(ignore_permissions=True) | |||||
def update_permissions(self, opts): | |||||
def update_permissions(self, route): | |||||
if self.meta.get_field("public_read"): | if self.meta.get_field("public_read"): | ||||
opts.public_read = self.public_read | |||||
opts.public_write = self.public_write | |||||
route.public_read = self.public_read | |||||
route.public_write = self.public_write | |||||
else: | else: | ||||
opts.public_read = 1 | |||||
route.public_read = 1 | |||||
def get_page_name(self): | def get_page_name(self): | ||||
page_name = self._get_page_name() | |||||
if not page_name: | |||||
page_name = self.set_page_name() | |||||
return self.get_or_make_page_name() | |||||
return self._get_page_name() | |||||
def get_page_name_field(self): | |||||
return self.page_name_field if hasattr(self, "page_name_field") else "page_name" | |||||
def _get_page_name(self): | |||||
if self.meta.get_field(self.website_template.page_name_field): | |||||
return self.get(self.website_template.page_name_field) | |||||
else: | |||||
return cleanup_page_name(self.get_page_title()) | |||||
def get_or_make_page_name(self): | |||||
page_name = self.get(self.get_page_name_field()) | |||||
if not page_name: | |||||
page_name = cleanup_page_name(self.get_page_title()) | |||||
if self.is_new(): | |||||
self.set(self.get_page_name_field(), page_name) | |||||
return page_name | |||||
def get_page_title(self): | def get_page_title(self): | ||||
return self.get("title") or (self.name.replace("-", " ").replace("_", " ").title()) | return self.get("title") or (self.name.replace("-", " ").replace("_", " ").title()) |
@@ -6,7 +6,7 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe, os | import frappe, os | ||||
from frappe.model.meta import Meta | from frappe.model.meta import Meta | ||||
from frappe.modules import scrub, get_module_path | |||||
from frappe.modules import scrub, get_module_path, load_doctype_module | |||||
from frappe.model.workflow import get_workflow_name | from frappe.model.workflow import get_workflow_name | ||||
###### | ###### | ||||
@@ -35,10 +35,11 @@ class FormMeta(Meta): | |||||
self.add_code() | self.add_code() | ||||
self.load_print_formats() | self.load_print_formats() | ||||
self.load_workflows() | self.load_workflows() | ||||
self.load_form_grid_templates() | |||||
def as_dict(self, no_nulls=False): | def as_dict(self, no_nulls=False): | ||||
d = super(FormMeta, self).as_dict(no_nulls=no_nulls) | d = super(FormMeta, self).as_dict(no_nulls=no_nulls) | ||||
for k in ("__js", "__css", "__list_js", "__calendar_js", "__map_js", | |||||
for k in ("__js", "__css", "__list_js", "__calendar_js", "__map_js", "__form_grid_templates", | |||||
"__linked_with", "__messages", "__print_formats", "__workflow_docs"): | "__linked_with", "__messages", "__print_formats", "__workflow_docs"): | ||||
d[k] = self.get(k) | d[k] = self.get(k) | ||||
@@ -153,6 +154,17 @@ class FormMeta(Meta): | |||||
self.set("__workflow_docs", workflow_docs) | self.set("__workflow_docs", workflow_docs) | ||||
def load_form_grid_templates(self): | |||||
module = load_doctype_module(self.name) | |||||
app = module.__name__.split(".")[0] | |||||
templates = {} | |||||
if hasattr(module, "form_grid_templates"): | |||||
for key, path in module.form_grid_templates.iteritems(): | |||||
with open(frappe.get_app_path(app, path), "r") as f: | |||||
templates[key] = f.read() | |||||
self.set("__form_grid_templates", templates) | |||||
def render_jinja(content): | def render_jinja(content): | ||||
if "{% include" in content: | if "{% include" in content: | ||||
content = frappe.get_jenv().from_string(content).render() | content = frappe.get_jenv().from_string(content).render() | ||||
@@ -5,7 +5,6 @@ from __future__ import unicode_literals | |||||
import frappe | import frappe | ||||
import os, json | import os, json | ||||
import types | |||||
from frappe import _ | from frappe import _ | ||||
from frappe.modules import scrub, get_module_path | from frappe.modules import scrub, get_module_path | ||||
@@ -31,11 +30,16 @@ def get_script(report_name): | |||||
module_path = get_module_path(module) | module_path = get_module_path(module) | ||||
report_folder = os.path.join(module_path, "report", scrub(report.name)) | report_folder = os.path.join(module_path, "report", scrub(report.name)) | ||||
script_path = os.path.join(report_folder, scrub(report.name) + ".js") | script_path = os.path.join(report_folder, scrub(report.name) + ".js") | ||||
print_path = os.path.join(report_folder, scrub(report.name) + ".html") | |||||
script = None | |||||
script, html_format = None, None | |||||
if os.path.exists(script_path): | if os.path.exists(script_path): | ||||
with open(script_path, "r") as script: | |||||
script = script.read() | |||||
with open(script_path, "r") as f: | |||||
script = f.read() | |||||
if os.path.exists(print_path): | |||||
with open(print_path, "r") as f: | |||||
html_format = f.read() | |||||
if not script and report.javascript: | if not script and report.javascript: | ||||
script = report.javascript | script = report.javascript | ||||
@@ -47,7 +51,10 @@ def get_script(report_name): | |||||
if frappe.lang != "en": | if frappe.lang != "en": | ||||
frappe.response["__messages"] = frappe.get_lang_dict("report", report_name) | frappe.response["__messages"] = frappe.get_lang_dict("report", report_name) | ||||
return script | |||||
return { | |||||
"script": script, | |||||
"html_format": html_format | |||||
} | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def run(report_name, filters=()): | def run(report_name, filters=()): | ||||
@@ -148,8 +155,6 @@ def get_linked_doctypes(columns): | |||||
def get_user_match_filters(doctypes, ref_doctype): | def get_user_match_filters(doctypes, ref_doctype): | ||||
match_filters = {} | match_filters = {} | ||||
doctypes_meta = {} | |||||
tables = [] | |||||
for dt in doctypes: | for dt in doctypes: | ||||
match_filters.update(frappe.widgets.reportview.build_match_conditions(dt, False)) | match_filters.update(frappe.widgets.reportview.build_match_conditions(dt, False)) | ||||
@@ -150,6 +150,8 @@ def scrub_user_tags(tagcount): | |||||
rdict = {} | rdict = {} | ||||
tagdict = dict(tagcount) | tagdict = dict(tagcount) | ||||
for t in tagdict: | for t in tagdict: | ||||
if not t: | |||||
continue | |||||
alltags = t.split(',') | alltags = t.split(',') | ||||
for tag in alltags: | for tag in alltags: | ||||
if tag: | if tag: | ||||
@@ -16,8 +16,6 @@ slugify | |||||
termcolor | termcolor | ||||
werkzeug | werkzeug | ||||
semantic_version | semantic_version | ||||
lxml | |||||
inlinestyler | |||||
rauth>=0.6.2 | rauth>=0.6.2 | ||||
requests==1.2.3 | requests==1.2.3 | ||||
celery | celery | ||||