commit
1804f7133d
90 changed files with 1491 additions and 1798 deletions
|
|
@ -377,7 +377,7 @@ def get_module(modulename):
|
|||
return importlib.import_module(modulename)
|
||||
|
||||
def scrub(txt):
|
||||
return txt.replace(' ','_').replace('-', '_').replace('/', '_').lower()
|
||||
return txt.replace(' ','_').replace('-', '_').lower()
|
||||
|
||||
def unscrub(txt):
|
||||
return txt.replace('_',' ').replace('-', ' ').title()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# 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.utils.minify import JavascriptMinify
|
||||
|
|
@ -16,7 +16,7 @@ def bundle(no_compress, make_copy=False):
|
|||
# build js files
|
||||
make_asset_dirs(make_copy=make_copy)
|
||||
build(no_compress)
|
||||
|
||||
|
||||
def watch(no_compress):
|
||||
"""watch and rebuild if necessary"""
|
||||
import time
|
||||
|
|
@ -25,18 +25,18 @@ def watch(no_compress):
|
|||
while True:
|
||||
if files_dirty():
|
||||
build(no_compress=True)
|
||||
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
def make_asset_dirs(make_copy=False):
|
||||
assets_path = os.path.join(frappe.local.sites_path, "assets")
|
||||
for dir_path in [
|
||||
os.path.join(assets_path, 'js'),
|
||||
os.path.join(assets_path, 'js'),
|
||||
os.path.join(assets_path, 'css')]:
|
||||
|
||||
|
||||
if not os.path.exists(dir_path):
|
||||
os.makedirs(dir_path)
|
||||
|
||||
|
||||
# symlink app/public > assets/app
|
||||
for app_name in frappe.get_all_apps(True):
|
||||
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")
|
||||
|
||||
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)
|
||||
# reset_app_html()
|
||||
|
|
@ -79,39 +79,46 @@ def get_build_maps():
|
|||
else:
|
||||
s = os.path.join(app_path, source)
|
||||
source_paths.append(s)
|
||||
|
||||
|
||||
build_maps[target] = source_paths
|
||||
except Exception, e:
|
||||
print path
|
||||
raise
|
||||
|
||||
|
||||
return build_maps
|
||||
|
||||
timestamps = {}
|
||||
|
||||
def pack(target, sources, no_compress):
|
||||
from cStringIO import StringIO
|
||||
|
||||
|
||||
outtype, outtxt = target.split(".")[-1], ''
|
||||
jsm = JavascriptMinify()
|
||||
|
||||
|
||||
for f in sources:
|
||||
suffix = None
|
||||
if ':' in f: f, suffix = f.split(':')
|
||||
if not os.path.exists(f) or os.path.isdir(f): continue
|
||||
timestamps[f] = os.path.getmtime(f)
|
||||
try:
|
||||
with open(f, 'r') as sourcefile:
|
||||
with open(f, 'r') as sourcefile:
|
||||
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()
|
||||
jsm.minify(tmpin, tmpout)
|
||||
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:
|
||||
outtxt += ('\n/*\n *\t%s\n */' % f)
|
||||
outtxt += '\n' + data + '\n'
|
||||
|
||||
|
||||
except Exception, e:
|
||||
print "--Error in:" + f + "--"
|
||||
print frappe.get_traceback()
|
||||
|
|
@ -119,10 +126,10 @@ def pack(target, sources, no_compress):
|
|||
if not no_compress and outtype == 'css':
|
||||
pass
|
||||
#outtxt = cssmin(outtxt)
|
||||
|
||||
|
||||
with open(target, 'w') as f:
|
||||
f.write(outtxt.encode("utf-8"))
|
||||
|
||||
|
||||
print "Wrote %s - %sk" % (target, str(int(os.path.getsize(target)/1024)))
|
||||
|
||||
def files_dirty():
|
||||
|
|
@ -135,4 +142,4 @@ def files_dirty():
|
|||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ def setup_utilities(parser):
|
|||
# clear
|
||||
parser.add_argument("--clear_web", default=False, action="store_true",
|
||||
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")
|
||||
parser.add_argument("--sync_statics", default=False, action="store_true",
|
||||
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)
|
||||
for each in apps:
|
||||
if each in all_apps:
|
||||
add_to_installed_apps(each, rebuild_sitemap=False)
|
||||
add_to_installed_apps(each, rebuild_website=False)
|
||||
frappe.destroy()
|
||||
|
||||
@cmd
|
||||
|
|
@ -402,13 +402,11 @@ def update(remote=None, branch=None, reload_gunicorn=False):
|
|||
subprocess.check_output("killall -HUP gunicorn".split())
|
||||
|
||||
@cmd
|
||||
def latest(rebuild_website_config=True, quiet=False):
|
||||
def latest(rebuild_website=True, quiet=False):
|
||||
import frappe.modules.patch_handler
|
||||
import frappe.model.sync
|
||||
from frappe.website import rebuild_config
|
||||
from frappe.utils.fixtures import sync_fixtures
|
||||
import frappe.translate
|
||||
from frappe.website import statics
|
||||
|
||||
verbose = not quiet
|
||||
|
||||
|
|
@ -419,17 +417,11 @@ def latest(rebuild_website_config=True, quiet=False):
|
|||
frappe.modules.patch_handler.run_all()
|
||||
# sync
|
||||
frappe.model.sync.sync_all(verbose=verbose)
|
||||
frappe.translate.clear_cache()
|
||||
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()
|
||||
|
||||
if rebuild_website:
|
||||
build_website()
|
||||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
|
|
@ -561,10 +553,11 @@ def clear_all_sessions():
|
|||
frappe.destroy()
|
||||
|
||||
@cmd
|
||||
def build_sitemap():
|
||||
from frappe.website import rebuild_config
|
||||
def build_website():
|
||||
import frappe.website.sync
|
||||
frappe.connect()
|
||||
rebuild_config()
|
||||
frappe.website.sync.sync()
|
||||
frappe.db.commit()
|
||||
frappe.destroy()
|
||||
|
||||
@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):
|
||||
footer = None
|
||||
|
||||
|
||||
if sent_via:
|
||||
if hasattr(sent_via, "get_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.core.doctype.property_setter.property_setter import make_property_setter
|
||||
|
||||
form_grid_templates = {
|
||||
"fields": "templates/form_grid/fields.html"
|
||||
}
|
||||
|
||||
class DocType(Document):
|
||||
def validate(self):
|
||||
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": [
|
||||
{
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "cb0",
|
||||
"fieldtype": "Column Break",
|
||||
"fieldname": "cb0",
|
||||
"fieldtype": "Column Break",
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "mail_password",
|
||||
"fieldtype": "Password",
|
||||
"label": "Mail Password",
|
||||
"fieldname": "mail_password",
|
||||
"fieldtype": "Password",
|
||||
"label": "Mail Password",
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
{
|
||||
"create": 1,
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"create": 1,
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"write": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,3 +23,6 @@ class OutgoingEmailSettings(Document):
|
|||
|
||||
# exceptions are handled in session connect
|
||||
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) {
|
||||
.case-wrapper {
|
||||
margin: 12px;
|
||||
margin: 9px;
|
||||
width: 70px;
|
||||
height: 80px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ frappe.PermissionEngine = Class.extend({
|
|||
me.setup_appframe();
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
setup_appframe: function() {
|
||||
var me = this;
|
||||
|
|
@ -192,8 +193,8 @@ frappe.PermissionEngine = Class.extend({
|
|||
}
|
||||
|
||||
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)
|
||||
.css("text-transform", "capitalize");
|
||||
|
||||
|
|
@ -214,9 +215,12 @@ frappe.PermissionEngine = Class.extend({
|
|||
me.set_show_users(role_cell, d.role);
|
||||
|
||||
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")
|
||||
.removeClass("col-md-4")
|
||||
.css({"margin-top": "15px"});
|
||||
d.help = "";
|
||||
}
|
||||
|
||||
var cell = add_cell(row, d, "permlevel");
|
||||
|
|
@ -290,6 +294,12 @@ frappe.PermissionEngine = Class.extend({
|
|||
},
|
||||
add_check_events: function() {
|
||||
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() {
|
||||
var chk = $(this);
|
||||
var args = {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ frappe.pages['user-permissions'].onload = function(wrapper) {
|
|||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: "User Permissions Manager",
|
||||
icon: "icon-user",
|
||||
icon: "icon-shield",
|
||||
single_column: true
|
||||
});
|
||||
$(wrapper).find(".layout-main").html("<div class='user-settings' style='min-height: 200px;'></div>\
|
||||
|
|
@ -45,6 +45,10 @@ frappe.UserPermissions = Class.extend({
|
|||
},
|
||||
make: function() {
|
||||
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({
|
||||
module:"frappe.core",
|
||||
page:"user_permissions",
|
||||
|
|
|
|||
|
|
@ -44,3 +44,4 @@ class DocstatusTransitionError(ValidationError): pass
|
|||
class TimestampMismatchError(ValidationError): pass
|
||||
class EmptyTableError(ValidationError): pass
|
||||
class LinkExistsError(ValidationError): pass
|
||||
class InvalidEmailAddressError(ValidationError): pass
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ web_include_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"]
|
||||
|
||||
|
|
@ -34,34 +34,33 @@ notification_config = "frappe.core.notifications.get_notification_config"
|
|||
|
||||
before_tests = "frappe.utils.install.before_tests"
|
||||
|
||||
website_generators = ["Web Page", "Blog Post", "Website Group", "Blog Category"]
|
||||
|
||||
# permissions
|
||||
|
||||
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 = {
|
||||
"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 = {
|
||||
"*": {
|
||||
"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 = {
|
||||
"all": ["frappe.utils.email_lib.bulk.flush"],
|
||||
|
|
@ -72,6 +71,8 @@ scheduler_events = {
|
|||
"frappe.sessions.clear_expired_sessions",
|
||||
],
|
||||
"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.database
|
||||
import getpass
|
||||
from frappe import _
|
||||
from frappe.model.db_schema import DbManager
|
||||
import frappe.website.sync
|
||||
from frappe.model.sync import sync_for
|
||||
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 []:
|
||||
frappe.get_attr(after_install)()
|
||||
|
||||
sync_fixtures()
|
||||
print "Installing Fixtures..."
|
||||
sync_fixtures(name)
|
||||
|
||||
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()
|
||||
if not app_name in installed_apps:
|
||||
installed_apps.append(app_name)
|
||||
frappe.db.set_global("installed_apps", json.dumps(installed_apps))
|
||||
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()
|
||||
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ class BaseDocument(object):
|
|||
return
|
||||
|
||||
for df in self.meta.get_select_fields():
|
||||
if not (self.get(df.fieldname) and df.options):
|
||||
if df.fieldname=="naming_series" or not (self.get(df.fieldname) and df.options):
|
||||
continue
|
||||
|
||||
options = (df.options or "").split("\n")
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None,
|
|||
elif isinstance(target_doc, basestring):
|
||||
target_doc = frappe.get_doc(json.loads(target_doc))
|
||||
|
||||
if not target_doc.has_permission("create"):
|
||||
if not ignore_permissions and not target_doc.has_permission("create"):
|
||||
target_doc.raise_no_permission_to("create")
|
||||
|
||||
map_doc(source_doc, target_doc, table_maps[source_doc.doctype])
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
import os, sys
|
||||
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
|
||||
|
||||
def sync_all(force=0, verbose=False):
|
||||
|
|
@ -23,40 +22,41 @@ def sync_all(force=0, verbose=False):
|
|||
frappe.clear_cache()
|
||||
|
||||
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 []:
|
||||
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):
|
||||
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
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
print ""
|
||||
|
||||
|
||||
def get_doc_files(start_path, force=0, sync_everything = False, verbose=False):
|
||||
"""walk and sync all doctypes and pages"""
|
||||
|
||||
modules = []
|
||||
|
||||
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):
|
||||
|
||||
for path, folders, files in os.walk(start_path):
|
||||
# sort folders so that doctypes are synced before pages or reports
|
||||
# Note: sorted is a hack because custom* and doc* need
|
||||
# be synced first
|
||||
|
||||
for dontwalk in (".git", "locale", "public"):
|
||||
if dontwalk in folders:
|
||||
folders.remove(dontwalk)
|
||||
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)
|
||||
|
||||
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):
|
||||
|
||||
module_name = path.split(os.sep)[-3]
|
||||
doctype = path.split(os.sep)[-2]
|
||||
name = path.split(os.sep)[-1]
|
||||
|
||||
if import_file_by_path(os.path.join(path, f), force=force) and verbose:
|
||||
print module_name + ' | ' + doctype + ' | ' + name
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
return modules
|
||||
return out
|
||||
|
|
|
|||
|
|
@ -44,13 +44,26 @@ def export_doc(doctype, name, module=None):
|
|||
def get_doctype_module(doctype):
|
||||
return frappe.db.get_value('DocType', doctype, 'module') or "core"
|
||||
|
||||
doctype_python_modules = {}
|
||||
def load_doctype_module(doctype, module=None, prefix=""):
|
||||
if not module:
|
||||
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(\
|
||||
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.db.sql("""delete from `tabUserRole` where ifnull(parentfield, '')=''""") #2014-06-17
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ def execute():
|
|||
frappe.reload_doc("website", "doctype", frappe.scrub(d))
|
||||
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_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")
|
||||
|
||||
for d in ("blog_category", "blog_post", "web_page", "website_route", "website_group", "post", "user_vote"):
|
||||
|
|
|
|||
|
|
@ -1,23 +1,24 @@
|
|||
import frappe
|
||||
|
||||
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
|
||||
|
||||
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")
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
|
||||
"public/css/tag-it.css",
|
||||
"public/css/bootstrap.css",
|
||||
"public/css/bootstrap-responsive.css",
|
||||
"public/css/font-awesome.css",
|
||||
"public/css/desk.css",
|
||||
"public/css/appframe.css",
|
||||
|
|
@ -67,6 +66,9 @@
|
|||
"public/js/frappe/router.js",
|
||||
"public/js/frappe/desk.js",
|
||||
"public/js/frappe/defaults.js",
|
||||
"public/js/lib/microtemplate.js",
|
||||
|
||||
"public/html/print_template.html",
|
||||
|
||||
"public/js/legacy/globals.js",
|
||||
"public/js/legacy/datatype.js",
|
||||
|
|
|
|||
17
frappe/public/html/print_template.html
Normal file
17
frappe/public/html/print_template.html
Normal file
|
|
@ -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);
|
||||
this.fieldinfo = {};
|
||||
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;
|
||||
},
|
||||
make: function() {
|
||||
|
|
@ -61,7 +66,7 @@ frappe.ui.form.Grid = Class.extend({
|
|||
|
||||
if(!force && this.data_rows_are_same(data)) {
|
||||
// soft refresh
|
||||
this.header_row.refresh();
|
||||
this.header_row && this.header_row.refresh();
|
||||
for(var i in this.grid_rows) {
|
||||
this.grid_rows[i].refresh();
|
||||
}
|
||||
|
|
@ -261,11 +266,49 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
},
|
||||
make_static_display: function() {
|
||||
var me = this;
|
||||
this.make_static_display_template();
|
||||
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);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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.grid.$row_actions) {
|
||||
this.grid.$row_actions = $('<div class="col-xs-1 pull-right" \
|
||||
style="text-align: right; padding-right: 5px;">\
|
||||
<span class="text-success grid-insert-row" style="padding: 4px;">\
|
||||
<i class="icon icon-plus-sign"></i></span>\
|
||||
<span class="grid-delete-row" style="padding: 4px;">\
|
||||
<i class="icon icon-trash"></i></span>\
|
||||
</div>');
|
||||
}
|
||||
$col = this.grid.$row_actions.clone().appendTo(this.row);
|
||||
|
||||
if($col.width() < 50) {
|
||||
$col.toggle(false);
|
||||
} else {
|
||||
$col.toggle(true);
|
||||
$col.find(".grid-insert-row").click(function() { me.insert(); return false; });
|
||||
$col.find(".grid-delete-row").click(function() { me.remove(); return false; });
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
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];
|
||||
|
|
@ -288,31 +331,8 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
if(!this.doc) $col.css({"font-weight":"bold"})
|
||||
}
|
||||
|
||||
// TODO find a better solution
|
||||
// append button column
|
||||
if(this.doc && this.grid.is_editable()) {
|
||||
if(!this.grid.$row_actions) {
|
||||
this.grid.$row_actions = $('<div class="col-md-1 pull-right" \
|
||||
style="text-align: right; padding-right: 5px;">\
|
||||
<span class="text-success grid-insert-row" style="padding: 4px;">\
|
||||
<i class="icon icon-plus-sign"></i></span>\
|
||||
<span class="grid-delete-row" style="padding: 4px;">\
|
||||
<i class="icon icon-trash"></i></span>\
|
||||
</div>');
|
||||
}
|
||||
$col = this.grid.$row_actions.clone().appendTo(this.row);
|
||||
|
||||
if($col.width() < 50) {
|
||||
$col.toggle(false);
|
||||
} else {
|
||||
$col.toggle(true);
|
||||
$col.find(".grid-insert-row").click(function() { me.insert(); return false; });
|
||||
$col.find(".grid-delete-row").click(function() { me.remove(); return false; });
|
||||
}
|
||||
}
|
||||
|
||||
$(this.frm.wrapper).trigger("grid-row-render", [this]);
|
||||
},
|
||||
|
||||
make_static_display_template: function() {
|
||||
if(this.static_display_template) return;
|
||||
|
||||
|
|
@ -443,55 +463,6 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
$.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.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 { \
|
||||
background: url("%(src)s") center center;\
|
||||
background-attachment: fixed; \
|
||||
background-size: 100%; \
|
||||
%(style)s \
|
||||
}', {src:src, selector:selector, style: style==="Fill Screen" ? "background-size: cover;" : ""}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,5 +247,5 @@ frappe.utils = {
|
|||
var dataURL = canvas.toDataURL("image/jpeg");
|
||||
setTimeout(function() { callback(dataURL); }, 10 );
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// MIT License. See license.txt
|
||||
// MIT License. See license.txt
|
||||
|
||||
// provide a namespace
|
||||
if(!window.frappe)
|
||||
if(!window.frappe)
|
||||
window.frappe = {};
|
||||
frappe.provide = function(namespace) {
|
||||
// docs: create a namespace //
|
||||
|
|
@ -22,4 +22,5 @@ frappe.provide("locals");
|
|||
frappe.provide("frappe.settings");
|
||||
frappe.provide("frappe.utils");
|
||||
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() {
|
||||
msgprint(__("Server Error: Please check your server logs or contact tech support."))
|
||||
opts.error && opts.error();
|
||||
|
||||
}
|
||||
},
|
||||
async: opts.async
|
||||
|
|
@ -144,7 +145,6 @@ frappe.request.prepare = function(opts) {
|
|||
}
|
||||
|
||||
frappe.request.cleanup = function(opts, r) {
|
||||
|
||||
// stop button indicator
|
||||
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",
|
||||
__("User Permissions Manager"), function() {
|
||||
frappe.route_options = {
|
||||
property: me.doctype
|
||||
doctype: me.doctype
|
||||
};
|
||||
frappe.set_route("user-permissions");
|
||||
});
|
||||
}
|
||||
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() {
|
||||
frappe.set_route("Form", "Customize Form", {
|
||||
doctype: me.doctype
|
||||
|
|
|
|||
|
|
@ -107,8 +107,9 @@ frappe.views.QueryReport = Class.extend({
|
|||
},
|
||||
callback: function(r) {
|
||||
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_html_format(r.message.html_format);
|
||||
me.refresh();
|
||||
}
|
||||
});
|
||||
|
|
@ -124,6 +125,37 @@ frappe.views.QueryReport = Class.extend({
|
|||
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() {
|
||||
this.clear_filters();
|
||||
var me = this;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ frappe.utils.full_name = function(fn, ln) {
|
|||
}
|
||||
|
||||
function fmt_money(v, format){
|
||||
// deprecated!
|
||||
// for backward compatibility
|
||||
return format_number(v, format);
|
||||
}
|
||||
|
||||
|
|
|
|||
34
frappe/public/js/lib/microtemplate.js
Normal file
34
frappe/public/js/lib/microtemplate.js
Normal file
|
|
@ -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">
|
||||
{%- block head_include %}{% endblock -%}
|
||||
{%- block head -%}
|
||||
|
||||
{%- if metatags -%}
|
||||
{%- for name in metatags %}
|
||||
<meta name="{{ name }}" content="{{ metatags[name]|striptags }}">
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{% if meta_block is defined %}
|
||||
{{ meta_block }}
|
||||
{% endif %}
|
||||
|
||||
{%- for link in web_include_css %}
|
||||
<link type="text/css" rel="stylesheet" href="{{ link }}">
|
||||
|
|
@ -50,15 +47,15 @@ Built on Frappe.io. Free and Open Source Framework for the Web. https://frappe.i
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-9 page-header-left">
|
||||
<a class="visible-xs toggle-sidebar no-decoration pull-right">
|
||||
<i class="icon-chevron-down"></i>
|
||||
</a>
|
||||
<div data-html-block="header">
|
||||
{%- if header is defined -%}{{ header }}{%- endif -%}
|
||||
</div>
|
||||
<div class="page-breadcrumbs" data-html-block="breadcrumbs">
|
||||
{%- if breadcrumbs is defined -%}{{ breadcrumbs }}{%- endif -%}
|
||||
</div>
|
||||
<!-- <a class="visible-xs toggle-sidebar no-decoration pull-right">
|
||||
<i class="icon-chevron-down"></i>
|
||||
</a> -->
|
||||
</div>
|
||||
<div class="col-sm-3 text-right">
|
||||
<div class="page-header-right"></div>
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
<p>You have a new message from: <b>{{ from }}</b></p>
|
||||
<p>{{ message }}</p>
|
||||
<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>A new account has been created for you.</p>
|
||||
<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>
|
||||
<p>You can also copy-paste this link in your browser <a href="{{ link }}">{{ link }}</a></p>
|
||||
<p>Thank you,<br>
|
||||
{{ user_fullname }}</p>
|
||||
{{ user_fullname }}</p>
|
||||
|
|
|
|||
|
|
@ -4,273 +4,16 @@
|
|||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<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>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
|
||||
<!-- 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 -->
|
||||
<table class="footer-wrap">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="container">
|
||||
<div style="margin-top: 30px; font-family: Helvetica, Arial, sans-serif; font-size: 11px;">
|
||||
{{ footer }}</div>
|
||||
|
||||
<!-- content -->
|
||||
<div class="content">
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
{{ footer }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /content -->
|
||||
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- /footer -->
|
||||
|
||||
</div>
|
||||
|
||||
<div class="print-html">{{ print_html or "" }}</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
41
frappe/templates/form_grid/fields.html
Normal file
41
frappe/templates/form_grid/fields.html
Normal file
|
|
@ -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() {
|
||||
$.ajax({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
url: "/api/method/frappe.website.doctype.blog_post.blog_post.get_blog_list",
|
||||
data: {
|
||||
cmd: "frappe.templates.generators.blog_post.get_blog_list",
|
||||
start: blog.start,
|
||||
by: get_url_arg("by"),
|
||||
category: window.category || get_url_arg("category")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<fieldset>
|
||||
<input class="form-control" name="comment_by_fullname" placeholder="Your Name" type="text"/><br>
|
||||
<input class="form-control" name="comment_by"
|
||||
placeholder="Your Email Id" type="text"/><br>
|
||||
placeholder="Your Email Id" type="email"/><br>
|
||||
<textarea class="form-control" name="comment" rows=10
|
||||
placeholder="Comment"/>
|
||||
</textarea><br>
|
||||
|
|
@ -62,6 +62,11 @@ $(document).ready(function() {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!valid_email(args.comment_by)) {
|
||||
frappe.msgprint("Please enter a valid email address.");
|
||||
return false;
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
btn: this,
|
||||
type: "POST",
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ def add_comment(args=None):
|
|||
ifnull(unsubscribed, 0)=0""", (comment.comment_doctype, comment.comment_docname))]
|
||||
|
||||
owner = frappe.db.get_value(comment.comment_doctype, comment.comment_docname, "owner")
|
||||
recipients = commentors if owner=="Administrator" else list(set(commentors + [owner]))
|
||||
recipients = list(set(commentors if owner=="Administrator" else (commentors + [owner])))
|
||||
|
||||
|
||||
from frappe.utils.email_lib.bulk import send
|
||||
|
|
|
|||
|
|
@ -124,5 +124,6 @@ frappe.ready(function() {
|
|||
window.location.hash = "#login";
|
||||
login.bind_events();
|
||||
login.login();
|
||||
$(".form-signup, .form-forgot").removeClass("hide");
|
||||
$(document).trigger('login_rendered');
|
||||
});
|
||||
|
|
|
|||
5
frappe/templates/includes/meta_block.html
Normal file
5
frappe/templates/includes/meta_block.html
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{%- if metatags -%}
|
||||
{%- for name in metatags %}
|
||||
<meta name="{{ name }}" content="{{ metatags[name]|striptags }}" data-html-block="meta_block">
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
{%- for child in post_login -%}
|
||||
<li data-label="{{ child.label }}"
|
||||
<li {% if child.label %}data-label="{{ child.label }}" {% endif %}
|
||||
{% if child.class %} class="{{ child.class }}" {% endif %}>
|
||||
|
||||
{%- if child.url -%}
|
||||
|
|
@ -47,7 +47,6 @@
|
|||
{%- if child.icon -%}
|
||||
<i class="icon-fixed-width {{ child.icon }}"></i>
|
||||
{%- endif -%}
|
||||
|
||||
{{ child.label }}
|
||||
</a>
|
||||
{%- endif -%}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
{% if children -%}
|
||||
{%- for child in children -%}
|
||||
<div class="sidebar-item">
|
||||
{% set is_parent = parents and child.name == parents[-1].name or (loop.first and child.name==pathname) %}
|
||||
<i class="icon-fixed-width
|
||||
{% if (child.lft != None) and (child.rgt - child.lft != 1) and (not loop.first) %}icon-chevron-right{% endif %}
|
||||
{% if parents and child.name == parents[-1].name or (loop.first and child.name==pathname) %}icon-chevron-down{% endif %}"
|
||||
style="margin-left: -17px; color: #ddd;"></i>
|
||||
{% if (child.lft != None) and (child.rgt - child.lft != 1) and (not loop.first) %}icon-chevron-right{% endif %}"
|
||||
style="margin-left: -15px; color: #ddd; {% if is_parent %}margin-left: -30px;{% endif %}"></i>
|
||||
<a href="{{ child.name }}" class="no-decoration {% if child.name == pathname %}active{% endif %}">
|
||||
{{ child.page_title }}
|
||||
{% if not child.public_read %}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<div class="row">
|
||||
<div class="col-xs-1 text-right" style="padding-right: 0px;"><b>{{ loop.index }}.</b></div>
|
||||
<div class="col-xs-11">
|
||||
<a href="{{ item.name }}">{{ item.page_title }}</a>
|
||||
<a href="/{{ item.name }}">{{ item.page_title }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
# MIT License. See license.txt
|
||||
|
||||
no_sitemap = 1
|
||||
no_sitemap = 1
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
page_title = "Blog"
|
||||
def get_context(context):
|
||||
return frappe.get_doc("Blog Settings", "Blog Settings").as_dict()
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
</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>
|
||||
<input type="text" id="signup_fullname"
|
||||
class="form-control" placeholder="{{ _('Full Name') }}" required autofocus>
|
||||
|
|
@ -56,7 +56,7 @@
|
|||
|
||||
</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>
|
||||
<input type="email" id="forgot_email"
|
||||
class="form-control" placeholder="{{ _('Email Id') }}" required autofocus>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# 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
|
||||
|
||||
|
|
@ -15,16 +15,14 @@ def get_context(context):
|
|||
"""generate the sitemap XML"""
|
||||
host = get_request_site_address()
|
||||
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):
|
||||
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}
|
||||
|
||||
|
|
@ -8,58 +8,58 @@ from frappe.website.permissions import get_access
|
|||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
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
|
||||
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})
|
||||
access = get_access(pathname)
|
||||
|
||||
|
||||
if not access.get("read"):
|
||||
return frappe.PermissionError
|
||||
|
||||
|
||||
conditions = ""
|
||||
values = [group]
|
||||
|
||||
|
||||
group_type = frappe.db.get_value("Website Group", group, "group_type")
|
||||
if group_type == "Events":
|
||||
# should show based on time upto precision of hour
|
||||
# because the current hour should also be in upcoming
|
||||
values.append(now_datetime().replace(minute=0, second=0, microsecond=0))
|
||||
|
||||
|
||||
if view in ("feed", "closed"):
|
||||
order_by = "p.creation desc"
|
||||
|
||||
|
||||
if view == "closed":
|
||||
conditions += " and p.is_task=1 and p.status='Closed'"
|
||||
|
||||
|
||||
elif view in ("popular", "open"):
|
||||
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)
|
||||
|
||||
|
||||
if view == "open":
|
||||
conditions += " and p.is_task=1 and p.status='Open'"
|
||||
|
||||
|
||||
elif view == "upcoming":
|
||||
conditions += " and p.is_event=1 and p.event_datetime >= %s"
|
||||
order_by = "p.event_datetime asc"
|
||||
|
||||
|
||||
elif view == "past":
|
||||
conditions += " and p.is_event=1 and p.event_datetime < %s"
|
||||
order_by = "p.event_datetime desc"
|
||||
|
||||
|
||||
values += [int(limit_start), int(limit_length)]
|
||||
|
||||
|
||||
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
|
||||
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),
|
||||
tuple(values), as_dict=True, debug=True)
|
||||
|
||||
|
||||
context = { "posts": posts, "limit_start": limit_start, "view": get_views(group_type)[view] }
|
||||
|
||||
|
||||
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.website.permissions import get_access
|
||||
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):
|
||||
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
|
||||
|
||||
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
|
||||
frappe.db.set_value("Post", post.name, "picture_url", post.picture_url)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
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
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
@ -12,17 +12,17 @@ def suggest_user(term, group):
|
|||
pathname = get_pathname(group)
|
||||
if not get_access(pathname).get("admin"):
|
||||
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
|
||||
from `tabUser` pr
|
||||
from `tabUser` pr
|
||||
where (pr.first_name like %(term)s or pr.last_name like %(term)s)
|
||||
and pr.user_type = "Website User"
|
||||
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)
|
||||
|
||||
|
||||
template = frappe.get_template("templates/includes/user_display.html")
|
||||
return [{
|
||||
"value": "{} {}".format(pr.first_name or "", pr.last_name or ""),
|
||||
|
|
@ -35,7 +35,7 @@ def add_sitemap_permission(group, user):
|
|||
pathname = get_pathname(group)
|
||||
if not get_access(pathname).get("admin"):
|
||||
raise frappe.PermissionError
|
||||
|
||||
|
||||
permission = frappe.get_doc({
|
||||
"doctype": "Website Route Permission",
|
||||
"website_route": pathname,
|
||||
|
|
@ -43,11 +43,11 @@ def add_sitemap_permission(group, user):
|
|||
"read": 1
|
||||
})
|
||||
permission.insert(ignore_permissions=True)
|
||||
|
||||
|
||||
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))
|
||||
|
||||
|
||||
return frappe.get_template("templates/includes/sitemap_permission.html").render({
|
||||
"user": user
|
||||
})
|
||||
|
|
@ -57,18 +57,18 @@ def update_permission(group, user, perm, value):
|
|||
pathname = get_pathname(group)
|
||||
if not get_access(pathname).get("admin"):
|
||||
raise frappe.PermissionError
|
||||
|
||||
|
||||
permission = frappe.get_doc("Website Route Permission", {"website_route": pathname, "user": user})
|
||||
permission.set(perm, int(value))
|
||||
permission.save(ignore_permissions=True)
|
||||
|
||||
|
||||
# send email
|
||||
if perm=="admin" and int(value):
|
||||
group_title = frappe.db.get_value("Website Route", pathname, "page_title")
|
||||
|
||||
|
||||
subject = "You have been made Administrator of Group " + group_title
|
||||
|
||||
send(recipients=[user],
|
||||
|
||||
send(recipients=[user],
|
||||
subject= subject, add_unsubscribe_link=False,
|
||||
message="""<h3>Group Notification<h3>\
|
||||
<p>%s</p>\
|
||||
|
|
@ -82,15 +82,15 @@ def update_description(group, description):
|
|||
group = frappe.get_doc("Website Group", group)
|
||||
group.group_description = description
|
||||
group.save(ignore_permissions=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_website_group(group, new_group, public_read, public_write, group_type="Forum"):
|
||||
if not get_access(get_pathname(group)).get("admin"):
|
||||
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})
|
||||
|
||||
|
||||
frappe.get_doc({
|
||||
"doctype": "Website 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,
|
||||
"public_read": int(public_read),
|
||||
"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"
|
||||
# }}
|
||||
|
||||
# Generators
|
||||
# ----------
|
||||
|
||||
# automatically create page for each record of this doctype
|
||||
# website_generators = ["Web Page"]
|
||||
|
||||
# Installation
|
||||
# ------------
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class BulkLimitCrossedError(frappe.ValidationError): pass
|
|||
def send(recipients=None, sender=None, doctype='User', email_field='email',
|
||||
subject='[No Subject]', message='[No Content]', ref_doctype=None, ref_docname=None,
|
||||
add_unsubscribe_link=True):
|
||||
|
||||
def is_unsubscribed(rdata):
|
||||
if not rdata:
|
||||
return 1
|
||||
|
|
@ -81,7 +82,7 @@ def add(email, sender, subject, formatted, text_content=None,
|
|||
try:
|
||||
e.message = get_email(email, sender=e.sender, formatted=formatted, subject=subject,
|
||||
text_content=text_content).as_string()
|
||||
except frappe.ValidationError:
|
||||
except frappe.InvalidEmailAddressError:
|
||||
# bad email id - don't add to queue
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import msgprint, throw, _
|
||||
from frappe.utils import scrub_urls, cstr
|
||||
from frappe.utils import scrub_urls
|
||||
import email.utils
|
||||
from markdown2 import markdown
|
||||
|
||||
|
||||
def get_email(recipients, sender='', msg='', subject='[No Subject]',
|
||||
text_content = None, footer=None, print_html=None, formatted=None):
|
||||
"""send an html email as multipart with attachments and all"""
|
||||
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)
|
||||
|
||||
return emailobj
|
||||
|
|
@ -151,17 +152,18 @@ class EMail:
|
|||
def _validate(email):
|
||||
"""validate an email field"""
|
||||
if email and not validate_email_add(email):
|
||||
throw(_("{0} is not a valid email id").format(email))
|
||||
throw(_("{0} is not a valid email id").format(email), frappe.InvalidEmailAddressError)
|
||||
return email
|
||||
|
||||
if not self.sender:
|
||||
self.sender = frappe.db.get_value('Outgoing Email Settings', None,
|
||||
'auto_email_id') or frappe.conf.get('auto_email_id') or None
|
||||
if not self.sender:
|
||||
msgprint(_("Please specify 'Auto Email Id' in Setup > Outgoing Email Settings"))
|
||||
msg = _("Please specify 'Auto Email Id' in Setup > Outgoing Email Settings")
|
||||
msgprint(msg)
|
||||
if not "expires_on" in frappe.conf:
|
||||
msgprint(_("Alternatively, you can also specify 'auto_email_id' in site_config.json"))
|
||||
raise frappe.ValidationError
|
||||
raise frappe.ValidationError, msg
|
||||
|
||||
self.sender = _validate(self.sender)
|
||||
self.reply_to = _validate(self.reply_to)
|
||||
|
|
@ -189,7 +191,6 @@ class EMail:
|
|||
|
||||
def get_formatted_html(subject, message, footer=None, print_html=None):
|
||||
# imported here to avoid cyclic import
|
||||
import inlinestyler.utils
|
||||
|
||||
message = scrub_urls(message)
|
||||
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
|
||||
})
|
||||
|
||||
# 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):
|
||||
"""append a footer (signature)"""
|
||||
footer = footer or ""
|
||||
|
||||
# control panel
|
||||
footer += frappe.db.get_default('mail_footer') or ''
|
||||
|
||||
# hooks
|
||||
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-->"
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,12 @@ from __future__ import unicode_literals
|
|||
import frappe, os
|
||||
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")):
|
||||
for fname in os.listdir(frappe.get_app_path(app, "fixtures")):
|
||||
if fname.endswith(".json") or fname.endswith(".csv"):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,2 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# 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
|
||||
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)
|
||||
|
||||
if module and hasattr(module, "get_context"):
|
||||
|
|
@ -63,7 +67,7 @@ def build_context(sitemap_options):
|
|||
|
||||
add_metatags(context)
|
||||
|
||||
if context.get("base_template_path") != context.get("template_path") and not context.get("rendered"):
|
||||
if context.get("base_template_path") != context.get("template") and not context.get("rendered"):
|
||||
context.data = render_blocks(context)
|
||||
|
||||
return context
|
||||
|
|
|
|||
|
|
@ -6,15 +6,21 @@ import frappe
|
|||
from frappe.website.website_generator import WebsiteGenerator
|
||||
from frappe.website.render import clear_cache
|
||||
|
||||
template = "templates/generators/blog_category.html"
|
||||
no_cache = True
|
||||
|
||||
class BlogCategory(WebsiteGenerator):
|
||||
|
||||
def autoname(self):
|
||||
# to override autoname of WebsiteGenerator
|
||||
self.name = self.category_name
|
||||
|
||||
|
||||
def get_page_title(self):
|
||||
return self.title or self.name
|
||||
|
||||
|
||||
def on_update(self):
|
||||
WebsiteGenerator.on_update(self)
|
||||
clear_cache()
|
||||
|
||||
def get_parent_website_route(self):
|
||||
parent_website_sitemap = super(BlogCategory, self).get_parent_website_route()
|
||||
return parent_website_sitemap or "blog"
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@
|
|||
"in_list_view": 1,
|
||||
"label": "Blog Category",
|
||||
"options": "Blog Category",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "parent_website_route",
|
||||
|
|
@ -95,7 +96,7 @@
|
|||
"icon": "icon-quote-left",
|
||||
"idx": 1,
|
||||
"max_attachments": 5,
|
||||
"modified": "2014-05-27 03:49:07.888408",
|
||||
"modified": "2014-06-27 05:08:37.936947",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Blog Post",
|
||||
|
|
|
|||
|
|
@ -7,19 +7,22 @@ import frappe, re
|
|||
|
||||
from frappe.website.website_generator import WebsiteGenerator
|
||||
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):
|
||||
save_versions = True
|
||||
|
||||
def get_page_title(self):
|
||||
return self.title
|
||||
|
||||
def validate(self):
|
||||
if not self.blog_intro:
|
||||
self.blog_intro = self.content[:140]
|
||||
re.sub("\<[^>]*\>", "", self.blog_intro)
|
||||
self.blog_intro = re.sub("\<[^>]*\>", "", self.blog_intro)
|
||||
|
||||
if self.blog_intro:
|
||||
self.blog_intro = self.blog_intro[:140]
|
||||
|
|
@ -27,22 +30,93 @@ class BlogPost(WebsiteGenerator):
|
|||
if self.published and not self.published_on:
|
||||
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
|
||||
frappe.db.sql("""update tabBlogger set posts=(select count(*) from `tabBlog Post`
|
||||
where ifnull(blogger,'')=tabBlogger.name)
|
||||
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):
|
||||
WebsiteGenerator.on_update(self)
|
||||
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():
|
||||
for blog in frappe.db.sql_list("""select page_name from
|
||||
`tabBlog Post` where ifnull(published,0)=1"""):
|
||||
clear_cache(blog)
|
||||
|
||||
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):
|
||||
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_post_cache(self.parent_post or self.name)
|
||||
|
|
|
|||
|
|
@ -5,56 +5,34 @@ import frappe
|
|||
test_records = frappe.get_test_records('Web Page')
|
||||
|
||||
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):
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
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):
|
||||
web_page = frappe.get_doc("Web Page", "test-web-page-1")
|
||||
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"}),
|
||||
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")
|
||||
|
||||
|
||||
web_page.parent_website_route = ""
|
||||
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")
|
||||
|
||||
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) {
|
||||
cur_frm.cscript.layout(doc);
|
||||
cur_frm.set_intro("");
|
||||
if (!doc.__islocal && doc.published) {
|
||||
cur_frm.set_intro(__("Published on website at: {0}",
|
||||
[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": [
|
||||
{
|
||||
"fieldname": "section_title",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Title",
|
||||
"fieldname": "section_title",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Title",
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "published",
|
||||
"fieldtype": "Check",
|
||||
"label": "Published",
|
||||
"fieldname": "published",
|
||||
"fieldtype": "Check",
|
||||
"label": "Published",
|
||||
"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%"
|
||||
},
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"description": "Page content",
|
||||
"fieldname": "sb1",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Content",
|
||||
"description": "Page content",
|
||||
"fieldname": "sb1",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Content",
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "enable_comments",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Comments",
|
||||
"fieldname": "enable_comments",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Comments",
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "custom_javascript",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Custom Javascript",
|
||||
"fieldname": "custom_javascript",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Custom Javascript",
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "custom_css",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Custom CSS",
|
||||
"fieldname": "custom_css",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Custom CSS",
|
||||
"permlevel": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "insert_style",
|
||||
"fieldtype": "Check",
|
||||
"label": "Insert Style",
|
||||
"fieldname": "insert_style",
|
||||
"fieldtype": "Check",
|
||||
"label": "Insert Style",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"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": [
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,43 @@
|
|||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, os, time, re
|
||||
import frappe, re
|
||||
import requests, requests.exceptions
|
||||
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):
|
||||
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():
|
||||
cnt = 0
|
||||
|
|
|
|||
|
|
@ -4,17 +4,250 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
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):
|
||||
|
||||
def get_page_title(self):
|
||||
return self.group_title
|
||||
|
||||
|
||||
def on_update(self):
|
||||
WebsiteGenerator.on_update(self)
|
||||
clear_cache(website_group=self.name)
|
||||
|
||||
|
||||
def after_insert(self):
|
||||
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,
|
||||
"autoname": "field:page_name",
|
||||
"creation": "2013-11-18 15:38:40.000000",
|
||||
"creation": "2013-11-18 15:38:40",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
|
|
@ -38,8 +38,9 @@
|
|||
},
|
||||
{
|
||||
"fieldname": "docname",
|
||||
"fieldtype": "Data",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Docname",
|
||||
"options": "ref_doctype",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
|
|
@ -50,10 +51,16 @@
|
|||
"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
|
||||
},
|
||||
{
|
||||
|
|
@ -112,7 +119,7 @@
|
|||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"modified": "2014-02-24 12:46:59.000000",
|
||||
"modified": "2014-06-27 05:04:57.721756",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Website Route",
|
||||
|
|
|
|||
|
|
@ -22,59 +22,45 @@ class WebsiteRoute(NestedSet):
|
|||
return url
|
||||
|
||||
def validate(self):
|
||||
if self.get_url() != self.name:
|
||||
self.rename()
|
||||
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):
|
||||
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)
|
||||
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_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()
|
||||
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_descendants()
|
||||
self.clear_cache(self.old_name)
|
||||
self.clear_cache(self.old_parent_website_route)
|
||||
self.clear_cache(self.parent_website_route)
|
||||
|
||||
def rename_links(self):
|
||||
for doctype in frappe.db.sql_list("""select parent from tabDocField 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{}`
|
||||
where parent_website_route=%s""".format(doctype), self.old_name):
|
||||
frappe.db.set_value(doctype, name, "parent_website_route", self.name)
|
||||
|
|
@ -82,7 +68,7 @@ class WebsiteRoute(NestedSet):
|
|||
def rename_descendants(self):
|
||||
# rename children
|
||||
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.parent_website_route = self.name
|
||||
child.save()
|
||||
|
|
@ -92,7 +78,7 @@ class WebsiteRoute(NestedSet):
|
|||
if self.page_or_generator == "Page":
|
||||
# for a page, name and website sitemap config form a unique key
|
||||
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:
|
||||
# for a generator, name, ref_doctype and docname make a unique key
|
||||
exists = frappe.db.sql("""select name from `tabWebsite Route`
|
||||
|
|
@ -119,39 +105,13 @@ class WebsiteRoute(NestedSet):
|
|||
|
||||
def clear_cache(self, name=None):
|
||||
from frappe.website.render import clear_cache
|
||||
clear_cache(name or self.name)
|
||||
if name:
|
||||
clear_cache(name)
|
||||
else:
|
||||
if self.parent_website_route:
|
||||
clear_cache(self.parent_website_route)
|
||||
|
||||
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)
|
||||
|
||||
return website_route.idx
|
||||
clear_cache(self.name)
|
||||
|
||||
def remove_sitemap(page_name=None, ref_doctype=None, docname=None):
|
||||
if page_name:
|
||||
|
|
@ -159,11 +119,3 @@ def remove_sitemap(page_name=None, ref_doctype=None, docname=None):
|
|||
elif ref_doctype and docname:
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
class WebsiteSlideshow(Document):
|
||||
|
||||
def on_update(self):
|
||||
# a slide show can be in use and any change in it should get reflected
|
||||
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) {
|
||||
console.log(data.exc);
|
||||
}
|
||||
if (opts.error_msg && data._server_messages) {
|
||||
var server_messages = (JSON.parse(data._server_messages || '[]')).join("<br>");
|
||||
$(opts.error_msg).html(server_messages).toggle(true);
|
||||
}
|
||||
} else{
|
||||
if(opts.btn) {
|
||||
$(opts.btn).addClass("btn-success");
|
||||
|
|
@ -286,6 +290,10 @@ $.extend(frappe, {
|
|||
$('[data-html-block]').each(function(i, section) {
|
||||
var $section = $(section);
|
||||
var stype = $section.attr("data-html-block");
|
||||
|
||||
// handle meta separately
|
||||
if (stype==="meta_block") return;
|
||||
|
||||
var block_data = data[stype] || "";
|
||||
|
||||
// NOTE: use frappe.ready instead of $.ready for reliable execution
|
||||
|
|
@ -307,6 +315,12 @@ $.extend(frappe, {
|
|||
});
|
||||
if(data.title) $("title").html(data.title);
|
||||
|
||||
// change meta tags
|
||||
$('[data-html-block="meta_block"]').remove();
|
||||
if(data.meta_block) {
|
||||
$("head").append(data.meta_block);
|
||||
}
|
||||
|
||||
// change id of current page
|
||||
$(".page-container").attr("id", "page-" + data.path);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,23 @@
|
|||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// MIT License. See license.txt"
|
||||
|
||||
frappe.pages['sitemap-browser'].onload = function(wrapper) {
|
||||
frappe.pages['sitemap-browser'].onload = function(wrapper) {
|
||||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: 'Sitemap Browser',
|
||||
});
|
||||
});
|
||||
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();
|
||||
});
|
||||
|
||||
$(wrapper)
|
||||
.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)
|
||||
.find(".layout-main-section")
|
||||
.css({
|
||||
|
|
@ -34,31 +33,13 @@ frappe.website.SitemapBrowser = Class.extend({
|
|||
$(parent).empty();
|
||||
var me = this;
|
||||
this.tree = new frappe.ui.Tree({
|
||||
parent: $(parent),
|
||||
parent: $(parent),
|
||||
label: "Sitemap",
|
||||
method: 'frappe.website.page.sitemap_browser.sitemap_browser.get_children',
|
||||
toolbar: [
|
||||
{
|
||||
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"),
|
||||
click: function(node, btn) {
|
||||
|
|
@ -66,7 +47,7 @@ frappe.website.SitemapBrowser = Class.extend({
|
|||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
// drop: function(dragged_node, dropped_node, dragged_element, dropped_element) {
|
||||
// frappe.website.sitemap.update_parent(dragged_node.label, dropped_node.label, function(r) {
|
||||
// if(!r.exc) {
|
||||
|
|
@ -78,7 +59,7 @@ frappe.website.SitemapBrowser = Class.extend({
|
|||
});
|
||||
this.tree.rootnode.$a
|
||||
.data('node-data', {value: "Sitemap", expandable:1})
|
||||
.click();
|
||||
.click();
|
||||
},
|
||||
selected_node: function() {
|
||||
return this.tree.$w.find('.tree-link.selected');
|
||||
|
|
@ -87,67 +68,4 @@ frappe.website.SitemapBrowser = Class.extend({
|
|||
var node = this.selected_node();
|
||||
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:
|
||||
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:
|
||||
for p in frappe.db.sql_list("""select name from `tabWebsite Route`"""):
|
||||
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())
|
||||
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.title = sitemap_options.page_title
|
||||
sitemap_options.pathname = sitemap_options.name
|
||||
|
||||
# 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:
|
||||
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 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
|
||||
`tabWebsite Route` t1, `tab{ref_doctype}` t2
|
||||
where ifnull(t1.parent_website_route,'')=%s
|
||||
and t1.public_read=1
|
||||
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)
|
||||
|
||||
children = [frappe.get_doc("Website Route", pathname)] + children
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@
|
|||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, os, time
|
||||
import frappe, os, time, sys
|
||||
|
||||
from frappe import _
|
||||
from frappe.utils import cint
|
||||
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):
|
||||
s = sync()
|
||||
s.verbose = True
|
||||
while True:
|
||||
s.start(rebuild)
|
||||
frappe.db.commit()
|
||||
|
|
@ -19,17 +19,19 @@ def sync_statics(rebuild=False):
|
|||
|
||||
class sync(object):
|
||||
def start(self, rebuild=False):
|
||||
self.verbose = False
|
||||
self.synced = []
|
||||
self.synced_paths = []
|
||||
self.to_insert = []
|
||||
self.to_update = []
|
||||
self.updated = 0
|
||||
self.rebuild = rebuild
|
||||
for app in frappe.get_installed_apps():
|
||||
self.sync_for_app(app)
|
||||
|
||||
self.insert_and_update()
|
||||
self.cleanup()
|
||||
|
||||
if self.updated:
|
||||
print str(self.updated) + " files updated"
|
||||
|
||||
def sync_for_app(self, app):
|
||||
self.statics_path = frappe.get_app_path(app, "templates", "statics")
|
||||
if os.path.exists(self.statics_path):
|
||||
|
|
@ -38,17 +40,15 @@ class sync(object):
|
|||
|
||||
|
||||
def sync_folder(self, basepath, folders, files):
|
||||
folder_route = os.path.relpath(basepath, self.statics_path)
|
||||
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
|
||||
return
|
||||
|
||||
if self.index:
|
||||
self.sync_using_given_index(basepath, folders, files)
|
||||
|
||||
else:
|
||||
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
|
||||
if fname in files:
|
||||
self.sync_file(fname, os.path.join(basepath, fname), None)
|
||||
return
|
||||
return True
|
||||
|
||||
def sync_using_given_index(self, basepath, folders, files):
|
||||
for i, page_name in enumerate(self.index):
|
||||
|
|
@ -108,12 +108,42 @@ class sync(object):
|
|||
["name", "idx", "static_file_timestamp", "docname"], as_dict=True)
|
||||
|
||||
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:
|
||||
# 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({
|
||||
"doctype":"Web Page",
|
||||
"idx": priority,
|
||||
|
|
@ -122,66 +152,75 @@ class sync(object):
|
|||
"parent_website_route": parent_website_route
|
||||
})
|
||||
|
||||
page.fpath = fpath
|
||||
page.route = route
|
||||
page.update(get_static_content(fpath, page_name, route))
|
||||
return page
|
||||
|
||||
def insert_web_page(self, page):
|
||||
try:
|
||||
page.insert()
|
||||
except frappe.NameError:
|
||||
except frappe.NameError, e:
|
||||
print e
|
||||
# page exists, if deleted static, delete it and try again
|
||||
old_route = frappe.get_doc("Website Route", {"ref_doctype":"Web Page",
|
||||
"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)
|
||||
page.insert() # retry
|
||||
|
||||
|
||||
# update timestamp
|
||||
route_doc = frappe.get_doc("Website Route", {"ref_doctype": "Web Page",
|
||||
"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()
|
||||
|
||||
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:
|
||||
print "Ignoring {0} because page found".format(route_details.name)
|
||||
return
|
||||
|
||||
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 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):
|
||||
if self.synced:
|
||||
# delete static web pages that are not in immediate list
|
||||
frappe.delete_doc("Web Page", frappe.db.sql_list("""select docname
|
||||
from `tabWebsite Route`
|
||||
where ifnull(static_file_timestamp,'')!='' and name not in ({})
|
||||
order by (rgt-lft) asc""".format(', '.join(["%s"]*len(self.synced))),
|
||||
tuple(self.synced)))
|
||||
else:
|
||||
# delete all static web pages
|
||||
frappe.delete_doc("Web Page", frappe.db.sql_list("""select docname
|
||||
from `tabWebsite Route`
|
||||
where ifnull(static_file_timestamp,'')!=''
|
||||
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):
|
||||
d = frappe._dict({})
|
||||
|
|
@ -197,21 +236,21 @@ def get_static_content(fpath, docname, route):
|
|||
d.title = first_line[2:]
|
||||
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)
|
||||
|
||||
|
|
|
|||
116
frappe/website/sync.py
Normal file
116
frappe/website/sync.py
Normal file
|
|
@ -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
|
||||
import re
|
||||
|
||||
from sitemap import get_next
|
||||
|
||||
def render_blocks(context):
|
||||
"""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():
|
||||
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
|
||||
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"):
|
||||
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>"
|
||||
|
||||
if "breadcrumbs" not in out:
|
||||
out["breadcrumbs"] = scrub_relative_urls(
|
||||
frappe.get_template("templates/includes/breadcrumbs.html").render(context))
|
||||
|
||||
if "meta_block" not in out:
|
||||
out["meta_block"] = frappe.get_template("templates/includes/meta_block.html").render(context)
|
||||
|
||||
|
||||
if "<!-- no-sidebar -->" in out.get("content", ""):
|
||||
out["no_sidebar"] = 1
|
||||
|
||||
if "<!-- title:" in out.get("content", ""):
|
||||
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"):
|
||||
out["sidebar"] = scrub_relative_urls(
|
||||
frappe.get_template("templates/includes/sidebar.html").render(context))
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ def find_first_image(html):
|
|||
def can_cache(no_cache=False):
|
||||
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():
|
||||
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.website.utils import cleanup_page_name
|
||||
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):
|
||||
def autoname(self):
|
||||
self.setup_generator()
|
||||
if not self.website_template: return
|
||||
|
||||
self.name = self.get_page_name()
|
||||
append_number_if_name_exists(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):
|
||||
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):
|
||||
self.update_sitemap()
|
||||
if getattr(self, "save_versions", False):
|
||||
frappe.add_version(self)
|
||||
|
||||
def get_route(self):
|
||||
parent = self.get_parent_website_route()
|
||||
return ((parent + "/") if parent else "") + self.get_page_name()
|
||||
|
||||
def get_route_docname(self, name=None):
|
||||
return frappe.db.get_value("Website Route",
|
||||
{"ref_doctype":self.doctype, "docname": name or self.name})
|
||||
|
||||
def after_rename(self, olddn, newdn, merge):
|
||||
self.setup_generator()
|
||||
if not self.website_template: return
|
||||
|
||||
frappe.db.sql("""update `tabWebsite Route`
|
||||
set docname=%s where ref_doctype=%s and docname=%s""", (newdn, self.doctype, olddn))
|
||||
|
||||
if merge:
|
||||
remove_sitemap(ref_doctype=self.doctype, docname=olddn)
|
||||
if self.is_condition_field_enabled():
|
||||
self.update_route(self.get_route_docname())
|
||||
|
||||
def on_trash(self):
|
||||
self.setup_generator()
|
||||
if not self.website_template: return
|
||||
|
||||
remove_sitemap(ref_doctype=self.doctype, docname=self.name)
|
||||
|
||||
def update_sitemap(self):
|
||||
self.setup_generator()
|
||||
if not self.website_template: return
|
||||
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
|
||||
|
||||
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)
|
||||
def update_sitemap(self):
|
||||
# update route of all descendants
|
||||
route_docname = self.get_route_docname()
|
||||
|
||||
if not self.is_condition_field_enabled():
|
||||
frappe.delete_doc("Website Route", route_docname, ignore_permissions=True)
|
||||
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:
|
||||
# for sitemap.xml
|
||||
lastmod = frappe.utils.get_datetime(self.modified).strftime("%Y-%m-%d")
|
||||
else:
|
||||
lastmod = now()
|
||||
|
||||
opts = frappe._dict({
|
||||
route = frappe.new_doc("Website Route")
|
||||
route.update({
|
||||
"page_or_generator": "Generator",
|
||||
"ref_doctype":self.doctype,
|
||||
"idx": self.idx,
|
||||
"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,
|
||||
"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)
|
||||
self.update_permissions(route)
|
||||
route.ignore_links = True
|
||||
route.insert(ignore_permissions=True)
|
||||
|
||||
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)
|
||||
|
||||
def update_permissions(self, opts):
|
||||
def update_permissions(self, route):
|
||||
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:
|
||||
opts.public_read = 1
|
||||
route.public_read = 1
|
||||
|
||||
def get_page_name(self):
|
||||
page_name = self._get_page_name()
|
||||
return self.get_or_make_page_name()
|
||||
|
||||
def get_page_name_field(self):
|
||||
return self.page_name_field if hasattr(self, "page_name_field") else "page_name"
|
||||
|
||||
def get_or_make_page_name(self):
|
||||
page_name = self.get(self.get_page_name_field())
|
||||
if not page_name:
|
||||
page_name = self.set_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 self._get_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())
|
||||
return page_name
|
||||
|
||||
def get_page_title(self):
|
||||
return self.get("title") or (self.name.replace("-", " ").replace("_", " ").title())
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe, os
|
||||
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
|
||||
|
||||
######
|
||||
|
|
@ -35,10 +35,11 @@ class FormMeta(Meta):
|
|||
self.add_code()
|
||||
self.load_print_formats()
|
||||
self.load_workflows()
|
||||
self.load_form_grid_templates()
|
||||
|
||||
def as_dict(self, no_nulls=False):
|
||||
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"):
|
||||
d[k] = self.get(k)
|
||||
|
||||
|
|
@ -153,6 +154,17 @@ class FormMeta(Meta):
|
|||
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):
|
||||
if "{% include" in content:
|
||||
content = frappe.get_jenv().from_string(content).render()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ from __future__ import unicode_literals
|
|||
|
||||
import frappe
|
||||
import os, json
|
||||
import types
|
||||
|
||||
from frappe import _
|
||||
from frappe.modules import scrub, get_module_path
|
||||
|
|
@ -31,11 +30,16 @@ def get_script(report_name):
|
|||
module_path = get_module_path(module)
|
||||
report_folder = os.path.join(module_path, "report", scrub(report.name))
|
||||
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):
|
||||
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:
|
||||
script = report.javascript
|
||||
|
|
@ -47,7 +51,10 @@ def get_script(report_name):
|
|||
if frappe.lang != "en":
|
||||
frappe.response["__messages"] = frappe.get_lang_dict("report", report_name)
|
||||
|
||||
return script
|
||||
return {
|
||||
"script": script,
|
||||
"html_format": html_format
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def run(report_name, filters=()):
|
||||
|
|
@ -148,8 +155,6 @@ def get_linked_doctypes(columns):
|
|||
|
||||
def get_user_match_filters(doctypes, ref_doctype):
|
||||
match_filters = {}
|
||||
doctypes_meta = {}
|
||||
tables = []
|
||||
|
||||
for dt in doctypes:
|
||||
match_filters.update(frappe.widgets.reportview.build_match_conditions(dt, False))
|
||||
|
|
|
|||
|
|
@ -150,6 +150,8 @@ def scrub_user_tags(tagcount):
|
|||
rdict = {}
|
||||
tagdict = dict(tagcount)
|
||||
for t in tagdict:
|
||||
if not t:
|
||||
continue
|
||||
alltags = t.split(',')
|
||||
for tag in alltags:
|
||||
if tag:
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ slugify
|
|||
termcolor
|
||||
werkzeug
|
||||
semantic_version
|
||||
lxml
|
||||
inlinestyler
|
||||
rauth>=0.6.2
|
||||
requests==1.2.3
|
||||
celery
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue