diff --git a/frappe/__init__.py b/frappe/__init__.py index b302a749e7..4235605026 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -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() diff --git a/frappe/build.py b/frappe/build.py index 3f49530dfa..fb057934b0 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -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 - + diff --git a/frappe/cli.py b/frappe/cli.py index 8270d43498..0116bed406 100755 --- a/frappe/cli.py +++ b/frappe/cli.py @@ -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) - 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() + sync_fixtures() + 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 diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index d55d89cdea..dc7f150909 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -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 diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 26bdba1ab0..12333f289a 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -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"): diff --git a/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.json b/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.json index c4460d34c9..34fbd02313 100644 --- a/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.json +++ b/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.json @@ -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": "[?]", - "fieldname": "use_ssl", - "fieldtype": "Check", - "label": "Use TLS", + "description": "[?]", + "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": "
Sent via \n\tFrappe
", + "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 } ] -} \ No newline at end of file +} diff --git a/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.py b/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.py index 28e972cc9f..65838ee466 100644 --- a/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.py +++ b/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.py @@ -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 "" diff --git a/frappe/core/page/desktop/desktop.css b/frappe/core/page/desktop/desktop.css index 710882b873..084053fe8a 100644 --- a/frappe/core/page/desktop/desktop.css +++ b/frappe/core/page/desktop/desktop.css @@ -20,7 +20,7 @@ @media (max-width: 768px) { .case-wrapper { - margin: 12px; + margin: 9px; width: 70px; height: 80px; } diff --git a/frappe/core/page/permission_manager/permission_manager.js b/frappe/core/page/permission_manager/permission_manager.js index be34e0335b..f9cadd6889 100644 --- a/frappe/core/page/permission_manager/permission_manager.js +++ b/frappe/core/page/permission_manager/permission_manager.js @@ -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 = $("
\ - \ -
").appendTo(cell) + " + + (d.help || "") + "").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 = '
\ + Show User Pemissions
'; 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 = { diff --git a/frappe/core/page/user_permissions/user_permissions.js b/frappe/core/page/user_permissions/user_permissions.js index 7d1392736b..c35c5574b5 100644 --- a/frappe/core/page/user_permissions/user_permissions.js +++ b/frappe/core/page/user_permissions/user_permissions.js @@ -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("
\ @@ -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", diff --git a/frappe/exceptions.py b/frappe/exceptions.py index 805d68315b..fbd3fc03be 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -44,3 +44,4 @@ class DocstatusTransitionError(ValidationError): pass class TimestampMismatchError(ValidationError): pass class EmptyTableError(ValidationError): pass class LinkExistsError(ValidationError): pass +class InvalidEmailAddressError(ValidationError): pass diff --git a/frappe/hooks.py b/frappe/hooks.py index 6866b4ad17..fd1377c7c0 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -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" diff --git a/frappe/installer.py b/frappe/installer.py index 7e94db953e..52dd8d482d 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -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() diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index fd3d66e001..1095edf4bb 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -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") diff --git a/frappe/model/mapper.py b/frappe/model/mapper.py index cdf65cad0a..485a0c0c23 100644 --- a/frappe/model/mapper.py +++ b/frappe/model/mapper.py @@ -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]) diff --git a/frappe/model/sync.py b/frappe/model/sync.py index b7ad94aa03..9ede198c1b 100644 --- a/frappe/model/sync.py +++ b/frappe/model/sync.py @@ -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): - """walk and sync all doctypes and pages""" - - modules = [] - - document_type = ['doctype', 'page', 'report', 'print_format'] + l = len(files) + if l: + for i, doc_path in enumerate(files): + if import_file_by_path(doc_path, force=force) and verbose: + complete = int(float(i+1) / l * 40) + sys.stdout.write("\rSyncing {0}: [{1}{2}]".format(app_name, "="*complete, " "*(40-complete))) + sys.stdout.flush() + #print module_name + ' | ' + doctype + ' | ' + name - for path, folders, files in os.walk(start_path): - # sort folders so that doctypes are synced before pages or reports + frappe.db.commit() - for dontwalk in (".git", "locale", "public"): - if dontwalk in folders: - folders.remove(dontwalk) + print "" - folders.sort() - if sync_everything or (os.path.basename(os.path.dirname(path)) in document_type): - for f in files: - f = cstr(f) - if f.endswith(".json"): - doc_name = f.split(".json")[0] - if doc_name == os.path.basename(path): +def get_doc_files(start_path, force=0, sync_everything = False, verbose=False): + """walk and sync all doctypes and pages""" - module_name = path.split(os.sep)[-3] - doctype = path.split(os.sep)[-2] - name = path.split(os.sep)[-1] + out = [] + document_type = ['doctype', 'page', 'report', 'print_format'] + for doctype in document_type: + doctype_path = os.path.join(start_path, doctype) + if os.path.exists(doctype_path): - if import_file_by_path(os.path.join(path, f), force=force) and verbose: - print module_name + ' | ' + doctype + ' | ' + name + # Note: sorted is a hack because custom* and doc* need + # be synced first - frappe.db.commit() + for docname in sorted(os.listdir(doctype_path)): + if os.path.isdir(os.path.join(doctype_path, docname)): + doc_path = os.path.join(doctype_path, docname, docname) + ".json" + if os.path.exists(doc_path): + out.append(doc_path) - return modules + return out diff --git a/frappe/modules/__init__.py b/frappe/modules/__init__.py index 9f8890c482..3735d5f2ec 100644 --- a/frappe/modules/__init__.py +++ b/frappe/modules/__init__.py @@ -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)] diff --git a/frappe/patches.txt b/frappe/patches.txt index 8962d1404b..14a584c4e6 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -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 + diff --git a/frappe/patches/v4_0/rename_sitemap_to_route.py b/frappe/patches/v4_0/rename_sitemap_to_route.py index 2ef8de6486..a0922874ee 100644 --- a/frappe/patches/v4_0/rename_sitemap_to_route.py +++ b/frappe/patches/v4_0/rename_sitemap_to_route.py @@ -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"): diff --git a/frappe/patches/v4_0/set_website_route_idx.py b/frappe/patches/v4_0/set_website_route_idx.py index eb5fc1ebd6..82f64756a4 100644 --- a/frappe/patches/v4_0/set_website_route_idx.py +++ b/frappe/patches/v4_0/set_website_route_idx.py @@ -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() \ No newline at end of file + 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() diff --git a/frappe/patches/v4_0/website_sitemap_hierarchy.py b/frappe/patches/v4_0/website_sitemap_hierarchy.py index 800a14f4e7..2ac591e415 100644 --- a/frappe/patches/v4_0/website_sitemap_hierarchy.py +++ b/frappe/patches/v4_0/website_sitemap_hierarchy.py @@ -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") diff --git a/frappe/public/build.json b/frappe/public/build.json index 53fb50072b..a033d26f14 100644 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -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", diff --git a/frappe/public/html/print_template.html b/frappe/public/html/print_template.html new file mode 100644 index 0000000000..aba7ff57dc --- /dev/null +++ b/frappe/public/html/print_template.html @@ -0,0 +1,17 @@ + + + + + + + + + {%= title %} + + + +
+ {%= content %} +
+ + diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index daefe582ac..163719b174 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -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,38 +266,27 @@ frappe.ui.form.GridRow = Class.extend({ }, make_static_display: function() { var me = this; - this.make_static_display_template(); this.row.empty(); - $('
' + (this.doc ? this.doc.idx : "#")+ '
') + $('
' + (this.doc ? this.doc.idx : "#")+ '
') .appendTo(this.row); - for(var ci in this.static_display_template) { - var df = this.static_display_template[ci][0]; - var colsize = this.static_display_template[ci][1]; - var txt = this.doc ? - frappe.format(this.doc[df.fieldname], df, null, this.doc) : - __(df.label); - if(this.doc && df.fieldtype === "Select") { - txt = __(txt); - } - var add_class = (["Text", "Small Text"].indexOf(df.fieldtype)===-1) ? - " grid-overflow-ellipsis" : " grid-overflow-no-ellipsis"; - add_class += (["Int", "Currency", "Float"].indexOf(df.fieldtype)!==-1) ? - " text-right": ""; - - $col = $('
') - .html(txt) - .attr("data-fieldname", df.fieldname) - .data("df", df) - .appendTo(this.row) - if(!this.doc) $col.css({"font-weight":"bold"}) + if(this.grid.template) { + $('
').appendTo(this.row) + .html(frappe.render(this.grid.template, {doc:this.doc, frm:this.frm, grid_row: this})); + } else { + this.add_visible_columns(); } - // TODO find a better solution - // append button column + this.add_buttons(); + + $(this.frm.wrapper).trigger("grid-row-render", [this]); + }, + + add_buttons: function() { + var me = this; if(this.doc && this.grid.is_editable()) { if(!this.grid.$row_actions) { - this.grid.$row_actions = $('
\ \ \ @@ -311,8 +305,34 @@ frappe.ui.form.GridRow = Class.extend({ } } - $(this.frm.wrapper).trigger("grid-row-render", [this]); }, + + add_visible_columns: function() { + this.make_static_display_template(); + for(var ci in this.static_display_template) { + var df = this.static_display_template[ci][0]; + var colsize = this.static_display_template[ci][1]; + var txt = this.doc ? + frappe.format(this.doc[df.fieldname], df, null, this.doc) : + __(df.label); + if(this.doc && df.fieldtype === "Select") { + txt = __(txt); + } + var add_class = (["Text", "Small Text"].indexOf(df.fieldtype)===-1) ? + " grid-overflow-ellipsis" : " grid-overflow-no-ellipsis"; + add_class += (["Int", "Currency", "Float"].indexOf(df.fieldtype)!==-1) ? + " text-right": ""; + + $col = $('
') + .html(txt) + .attr("data-fieldname", df.fieldname) + .data("df", df) + .appendTo(this.row) + if(!this.doc) $col.css({"font-weight":"bold"}) + } + + }, + make_static_display_template: function() { 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) - // $('

'+ label +'

\ - //
') - // .appendTo(me.form_area); - // - // var row = $('
') - // .appendTo(me.form_area); - // - // var col_spans = 6; - // if(row.parents(".form-column:first").hasClass("col-md-6")) - // col_spans = 12; - // - // var col1 = $('
').appendTo(row), - // col2 = $('
').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 = $('
') - // .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; diff --git a/frappe/public/js/frappe/misc/user.js b/frappe/public/js/frappe/misc/user.js index cfe614af37..6dcd55c959 100644 --- a/frappe/public/js/frappe/misc/user.js +++ b/frappe/public/js/frappe/misc/user.js @@ -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;" : ""})); } diff --git a/frappe/public/js/frappe/misc/utils.js b/frappe/public/js/frappe/misc/utils.js index 5096536c80..d4b418a7d2 100644 --- a/frappe/public/js/frappe/misc/utils.js +++ b/frappe/public/js/frappe/misc/utils.js @@ -247,5 +247,5 @@ frappe.utils = { var dataURL = canvas.toDataURL("image/jpeg"); setTimeout(function() { callback(dataURL); }, 10 ); } - } + }, }; diff --git a/frappe/public/js/frappe/provide.js b/frappe/public/js/frappe/provide.js index a6091ec58b..6438b46839 100644 --- a/frappe/public/js/frappe/provide.js +++ b/frappe/public/js/frappe/provide.js @@ -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"); \ No newline at end of file +frappe.provide("frappe.modules"); +frappe.provide("frappe.templates"); diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 2117ad8eb9..126a09a6fb 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -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(); diff --git a/frappe/public/js/frappe/views/doclistview.js b/frappe/public/js/frappe/views/doclistview.js index 7738606fda..28cc7ad794 100644 --- a/frappe/public/js/frappe/views/doclistview.js +++ b/frappe/public/js/frappe/views/doclistview.js @@ -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 diff --git a/frappe/public/js/frappe/views/query_report.js b/frappe/public/js/frappe/views/query_report.js index cdb3f67d19..c6f0b8feee 100644 --- a/frappe/public/js/frappe/views/query_report.js +++ b/frappe/public/js/frappe/views/query_report.js @@ -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; diff --git a/frappe/public/js/legacy/datatype.js b/frappe/public/js/legacy/datatype.js index 8bd282bd4f..ad3383a696 100644 --- a/frappe/public/js/legacy/datatype.js +++ b/frappe/public/js/legacy/datatype.js @@ -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); } diff --git a/frappe/public/js/lib/microtemplate.js b/frappe/public/js/lib/microtemplate.js new file mode 100644 index 0000000000..3da5dad3ce --- /dev/null +++ b/frappe/public/js/lib/microtemplate.js @@ -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); +}; diff --git a/frappe/templates/base.html b/frappe/templates/base.html index 6af5cae355..5853a6965d 100644 --- a/frappe/templates/base.html +++ b/frappe/templates/base.html @@ -14,12 +14,9 @@ Built on Frappe.io. Free and Open Source Framework for the Web. https://frappe.i {%- block head_include %}{% endblock -%} {%- block head -%} - - {%- if metatags -%} - {%- for name in metatags %} - - {%- endfor -%} - {%- endif -%} + {% if meta_block is defined %} + {{ meta_block }} + {% endif %} {%- for link in web_include_css %} @@ -50,15 +47,15 @@ Built on Frappe.io. Free and Open Source Framework for the Web. https://frappe.i
+ + +
{%- if header is defined -%}{{ header }}{%- endif -%}
-
diff --git a/frappe/templates/emails/new_message.html b/frappe/templates/emails/new_message.html index b51e7129f4..a4a2ea933d 100644 --- a/frappe/templates/emails/new_message.html +++ b/frappe/templates/emails/new_message.html @@ -2,4 +2,4 @@

You have a new message from: {{ from }}

{{ message }}


-

Login and view in Browser

\ No newline at end of file +

Login and view in Browser

diff --git a/frappe/templates/emails/new_user.html b/frappe/templates/emails/new_user.html index 8c6b099c29..ef46f02dc3 100644 --- a/frappe/templates/emails/new_user.html +++ b/frappe/templates/emails/new_user.html @@ -2,9 +2,9 @@

Dear {{ first_name }}{% if last_name %} {{ last_name}}{% endif %},

A new account has been created for you.

Your login id is: {{ user }} -

Click on the button below to complete your registration and set a new password.

-

Complete Registration

+

Click on the link below to complete your registration and set a new password.

+

Complete Registration


You can also copy-paste this link in your browser {{ link }}

Thank you,
-{{ user_fullname }}

\ No newline at end of file +{{ user_fullname }}

diff --git a/frappe/templates/emails/standard.html b/frappe/templates/emails/standard.html index a8f8ffd4de..d3a719a9b9 100644 --- a/frappe/templates/emails/standard.html +++ b/frappe/templates/emails/standard.html @@ -4,273 +4,16 @@ {{ subject or "" }} - - - -
- - - - - - - -
- - -
- - - - -
- {{ content }} -
-
- - -
- +
{{ content }}
- - - - - - - - -
- - diff --git a/frappe/templates/form_grid/fields.html b/frappe/templates/form_grid/fields.html new file mode 100644 index 0000000000..277f2de05b --- /dev/null +++ b/frappe/templates/form_grid/fields.html @@ -0,0 +1,41 @@ +{% if(doc) { %} +
+
+

+ {% if (doc.fieldtype==="Section Break") { %}{% } %} + {% if (doc.reqd) { %}{% } %} + {% if (doc.hidden) { %} + {% } %} + {% if(doc.fieldtype==="Link") { %}{% } %} + {% if(doc.fieldtype==="Table") { %}{% } %} + {%= doc.label %} + {% if (doc.hidden) { %}{% } %} + {% if (doc.reqd) { %}{% } %} + {% if (doc.fieldtype=="Section Break") { %}{% } %} +

+ {% if (doc.description) { %}

{%= doc.description %}

{% } %} +
+
+ {%= doc.fieldtype %} +
{%= doc.fieldname %} +
+
+ {%= doc.options && doc.options.split("\n").join("
") || "" %} + {% if(doc["default"]) { %} +
{%= __("Default") %}: {%= doc["default"] %} + {% } %} +
+
+{% } else { %} +
+
+ {%= __("Label") %} +
+
+ {%= __("Field Type") %} +
+
+ {%= __("Options") %} +
+
+{% } %} diff --git a/frappe/templates/generators/blog_category.py b/frappe/templates/generators/blog_category.py deleted file mode 100644 index 5c7eb90af6..0000000000 --- a/frappe/templates/generators/blog_category.py +++ /dev/null @@ -1,2 +0,0 @@ -doctype = "Blog Category" -no_cache = 1 \ No newline at end of file diff --git a/frappe/templates/generators/blog_post.py b/frappe/templates/generators/blog_post.py deleted file mode 100644 index 6ee75c6274..0000000000 --- a/frappe/templates/generators/blog_post.py +++ /dev/null @@ -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 diff --git a/frappe/templates/generators/web_page.py b/frappe/templates/generators/web_page.py deleted file mode 100644 index 87d9005278..0000000000 --- a/frappe/templates/generators/web_page.py +++ /dev/null @@ -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 diff --git a/frappe/templates/generators/website_group.py b/frappe/templates/generators/website_group.py deleted file mode 100644 index 0660d96abf..0000000000 --- a/frappe/templates/generators/website_group.py +++ /dev/null @@ -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": '
' - 'The page you are looking for does not exist.
' - } - except frappe.PermissionError: - return { - "content": '
' - 'You are not permitted to view this page.
' - } - -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 - } -} diff --git a/frappe/templates/includes/blog.js b/frappe/templates/includes/blog.js index de1eb250ec..00ef5f649d 100644 --- a/frappe/templates/includes/blog.js +++ b/frappe/templates/includes/blog.js @@ -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") diff --git a/frappe/templates/includes/comments.html b/frappe/templates/includes/comments.html index 06e789f086..ecb2c7f641 100644 --- a/frappe/templates/includes/comments.html +++ b/frappe/templates/includes/comments.html @@ -18,7 +18,7 @@


+ placeholder="Your Email Id" type="email"/>

@@ -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", diff --git a/frappe/templates/includes/comments.py b/frappe/templates/includes/comments.py index 2ee7858cda..6fe8840b33 100644 --- a/frappe/templates/includes/comments.py +++ b/frappe/templates/includes/comments.py @@ -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 diff --git a/frappe/templates/includes/login.js b/frappe/templates/includes/login.js index e79af5dfad..3b5dbec159 100644 --- a/frappe/templates/includes/login.js +++ b/frappe/templates/includes/login.js @@ -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'); }); diff --git a/frappe/templates/includes/meta_block.html b/frappe/templates/includes/meta_block.html new file mode 100644 index 0000000000..e311040606 --- /dev/null +++ b/frappe/templates/includes/meta_block.html @@ -0,0 +1,5 @@ +{%- if metatags -%} +{%- for name in metatags %} + +{%- endfor -%} +{%- endif -%} diff --git a/frappe/templates/includes/navbar.html b/frappe/templates/includes/navbar.html index b8097711bf..a2ad132ddd 100644 --- a/frappe/templates/includes/navbar.html +++ b/frappe/templates/includes/navbar.html @@ -39,7 +39,7 @@