Merge branch 'develop'

Conflicts:
	frappe/__init__.py
	setup.py
This commit is contained in:
Pratik Vyas 2014-05-20 16:21:22 +05:30
commit d9dcf132a1
129 changed files with 3269 additions and 1366 deletions

View file

@ -16,7 +16,7 @@ import json
from .exceptions import *
__version__ = "4.0.0"
__version__ = "4.0.1"
local = Local()
@ -215,12 +215,20 @@ def set_user(username):
def get_request_header(key, default=None):
return request.headers.get(key, default)
def sendmail(recipients=(), sender="", subject="No Subject", message="No Message", as_markdown=False):
import frappe.utils.email_lib
if as_markdown:
frappe.utils.email_lib.sendmail_md(recipients, sender=sender, subject=subject, msg=message)
def sendmail(recipients=(), sender="", subject="No Subject", message="No Message",
as_markdown=False, bulk=False):
if bulk:
import frappe.utils.email_lib.bulk
frappe.utils.email_lib.bulk.send(recipients=recipients, sender=sender,
subject=subject, message=message, add_unsubscribe_link=False)
else:
frappe.utils.email_lib.sendmail(recipients, sender=sender, subject=subject, msg=message)
import frappe.utils.email_lib
if as_markdown:
frappe.utils.email_lib.sendmail_md(recipients, sender=sender, subject=subject, msg=message)
else:
frappe.utils.email_lib.sendmail(recipients, sender=sender, subject=subject, msg=message)
logger = None
whitelisted = []
@ -540,13 +548,15 @@ def build_match_conditions(doctype, as_condition=True):
import frappe.widgets.reportview
return frappe.widgets.reportview.build_match_conditions(doctype, as_condition)
def get_list(doctype, filters=None, fields=None, docstatus=None,
def get_list(doctype, filters=None, fields=None, or_filters=None, docstatus=None,
group_by=None, order_by=None, limit_start=0, limit_page_length=None,
as_list=False, debug=False, ignore_permissions=False):
import frappe.model.db_query
return frappe.model.db_query.DatabaseQuery(doctype).execute(filters=filters, fields=fields, docstatus=docstatus,
group_by=group_by, order_by=order_by, limit_start=limit_start, limit_page_length=limit_page_length,
as_list=as_list, debug=debug, ignore_permissions=ignore_permissions)
return frappe.model.db_query.DatabaseQuery(doctype).execute(filters=filters,
fields=fields, docstatus=docstatus, or_filters=or_filters,
group_by=group_by, order_by=order_by, limit_start=limit_start,
limit_page_length=limit_page_length, as_list=as_list, debug=debug,
ignore_permissions=ignore_permissions)
run_query = get_list

View file

@ -44,11 +44,12 @@ def handle():
elif call=="resource":
if "run_method" in frappe.local.form_dict:
doc = frappe.get_doc(doctype, name)
doc.is_whitelisted(frappe.local.form_dict.run_method)
if frappe.local.request.method=="GET":
if not doc.has_permission("read"):
frappe.throw(_("Not permitted"), frappe.PermissionError)
doc.run_method(frappe.local.form_dict.run_method, **frappe.local.form_dict)
doc.run_method(frappe.local.form_dict.run_method, **frappe.local.form_dict)
if frappe.local.request.method=="POST":
if not doc.has_permission("write"):
@ -69,7 +70,7 @@ def handle():
doc = frappe.get_doc(doctype, name)
# Not checking permissions here because it's checked in doc.save
doc.update(data)
frappe.local.response.update({
frappe.local.response.update({
"data": doc.save().as_dict()
})
frappe.db.commit()

View file

@ -34,7 +34,7 @@ def get_bootinfo():
bootinfo.modules = {}
for app in frappe.get_installed_apps():
try:
bootinfo.modules.update(frappe.get_attr(app + ".config.desktop.data") or {})
bootinfo.modules.update(frappe.get_attr(app + ".config.desktop.get_data")() or {})
except ImportError:
pass

View file

@ -49,11 +49,8 @@ def setup_celery(app, conf):
if conf.celery_error_emails:
app.conf.CELERY_SEND_TASK_ERROR_EMAILS = True
app.conf.ADMINS = conf.celery_error_email_recepients
if conf.mail_port:
app.conf.EMAIL_PORT = conf.mail_port
if conf.mail_server:
app.conf.EMAIL_HOST = conf.mail_server
for k, v in conf.celery_error_emails.iteritems():
setattr(app.conf, k, v)
class SiteRouter(object):
def route_for_task(self, task, args=None, kwargs=None):

View file

@ -155,6 +155,7 @@ def setup_test(parser):
parser.add_argument("--tests", metavar="TEST FUNCTION", nargs="*",
help="Run one or more specific test functions")
parser.add_argument("--serve_test", action="store_true", help="Run development server for testing")
parser.add_argument("--driver", nargs="?", help="Run selenium using given driver")
def setup_utilities(parser):
@ -231,6 +232,8 @@ def setup_utilities(parser):
help="Clear cache, doctype cache and defaults")
parser.add_argument("--reset_perms", default=False, action="store_true",
help="Reset permissions for all doctypes")
parser.add_argument("--clear_all_sessions", default=False, action="store_true",
help="Clear sessions of all users (logs them out)")
# scheduler
parser.add_argument("--run_scheduler", default=False, action="store_true",
@ -307,7 +310,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)
add_to_installed_apps(each, rebuild_sitemap=False)
frappe.destroy()
@cmd
@ -364,11 +367,7 @@ def latest(rebuild_website_config=True, quiet=False):
try:
# run patches
frappe.local.patch_log_list = []
frappe.modules.patch_handler.run_all()
if verbose:
print "\n".join(frappe.local.patch_log_list)
# sync
frappe.model.sync.sync_all(verbose=verbose)
sync_fixtures()
@ -381,9 +380,6 @@ def latest(rebuild_website_config=True, quiet=False):
frappe.translate.clear_cache()
except frappe.modules.patch_handler.PatchError:
print "\n".join(frappe.local.patch_log_list)
raise
finally:
frappe.destroy()
@ -399,9 +395,7 @@ def sync_all(force=False, quiet=False):
def patch(patch_module, force=False):
import frappe.modules.patch_handler
frappe.connect()
frappe.local.patch_log_list = []
frappe.modules.patch_handler.run_single(patch_module, force=force)
print "\n".join(frappe.local.patch_log_list)
frappe.destroy()
@cmd
@ -447,7 +441,6 @@ def backup(with_files=False, backup_path_db=None, backup_path_files=None, quiet=
if with_files:
print "files backup taken -", odb.backup_path_files, "- on", now()
frappe.destroy()
return odb
@cmd
def move(dest_dir=None, site=None):
@ -509,6 +502,14 @@ def clear_web():
frappe.website.render.clear_cache()
frappe.destroy()
@cmd
def clear_all_sessions():
import frappe.sessions
frappe.connect()
frappe.sessions.clear_all_sessions()
frappe.db.commit()
frappe.destroy()
@cmd
def build_sitemap():
from frappe.website import rebuild_config
@ -705,11 +706,11 @@ def smtp_debug_server():
os.execv(python, [python, '-m', "smtpd", "-n", "-c", "DebuggingServer", "localhost:25"])
@cmd
def run_tests(app=None, module=None, doctype=None, verbose=False, tests=()):
def run_tests(app=None, module=None, doctype=None, verbose=False, tests=(), driver=None):
import frappe.test_runner
from frappe.utils import sel
sel.start(verbose)
sel.start(verbose, driver)
ret = 1
try:

View file

@ -1,43 +1,50 @@
from frappe import _
data = {
"Calendar": {
"color": "#2980b9",
"icon": "icon-calendar",
"label": _("Calendar"),
"link": "Calendar/Event",
"type": "view"
},
"Messages": {
"color": "#9b59b6",
"icon": "icon-comments",
"label": _("Messages"),
"link": "messages",
"type": "page"
},
"To Do": {
"color": "#f1c40f",
"icon": "icon-check",
"label": _("To Do"),
"link": "List/ToDo",
"doctype": "ToDo",
"type": "list"
},
"Website": {
"color": "#16a085",
"icon": "icon-globe",
"type": "module"
},
"Installer": {
"color": "#888",
"icon": "icon-download",
"link": "applications",
"type": "page",
"label": _("Installer")
},
"Setup": {
"color": "#bdc3c7",
"icon": "icon-wrench",
"type": "module"
},
}
def get_data():
return {
"Calendar": {
"color": "#2980b9",
"icon": "icon-calendar",
"label": _("Calendar"),
"link": "Calendar/Event",
"type": "view"
},
"Messages": {
"color": "#9b59b6",
"icon": "icon-comments",
"label": _("Messages"),
"link": "messages",
"type": "page"
},
"To Do": {
"color": "#f1c40f",
"icon": "icon-check",
"label": _("To Do"),
"link": "List/ToDo",
"doctype": "ToDo",
"type": "list"
},
"Website": {
"color": "#16a085",
"icon": "icon-globe",
"type": "module"
},
"Installer": {
"color": "#888",
"icon": "icon-download",
"link": "applications",
"type": "page",
"label": _("Installer")
},
"Setup": {
"color": "#bdc3c7",
"icon": "icon-wrench",
"type": "module"
},
"Core": {
"color": "#589494",
"icon": "icon-cog",
"type": "module",
"system_manager": 1
},
}

View file

@ -1,193 +1,191 @@
from frappe import _
from frappe.widgets.moduleview import add_setup_section
data = [
{
"label": _("Users and Permissions"),
"icon": "icon-group",
"items": [
{
"type": "doctype",
"name": "User",
"description": _("System and Website Users")
},
{
"type": "doctype",
"name": "Role",
"description": _("User Roles")
},
{
"type": "page",
"name": "permission-manager",
"label": "Permission Manager",
"icon": "icon-lock",
"description": _("Set Permissions on Document Types and Roles")
},
{
"type": "page",
"name": "user-properties",
"label": _("User Permission Restrictions"),
"icon": "icon-user",
"description": _("Set Defaults and Restrictions for Users")
},
]
},
{
"label": _("Settings"),
"icon": "icon-wrench",
"items": [
{
"type": "doctype",
"name": "System Settings",
"label": _("System Settings"),
"description": _("Language, Date and Time settings"),
"hide_count": True
},
{
"type": "page",
"name": "modules_setup",
"label": _("Show / Hide Modules"),
"icon": "icon-upload",
"description": _("Show or hide modules globally.")
},
{
"type": "doctype",
"name": "Naming Series",
"description": _("Set numbering series for transactions."),
"hide_count": True
},
]
},
{
"label": _("Data"),
"icon": "icon-th",
"items": [
{
"type": "page",
"name": "data-import-tool",
"label": _("Import / Export Data"),
"icon": "icon-upload",
"description": _("Import / Export Data from .csv files.")
},
{
"type": "doctype",
"name": "Rename Tool",
"description": _("Rename many items by uploading a .csv file."),
"hide_count": True
},
{
"type": "doctype",
"name": "File Data",
"description": _("Manage uploaded files.")
}
]
},
{
"label": _("Workflow"),
"icon": "icon-random",
"items": [
{
"type": "doctype",
"name": "Workflow",
"description": _("Define workflows for forms.")
},
{
"type": "doctype",
"name": "Workflow State",
"description": _("States for workflow (e.g. Draft, Approved, Cancelled).")
},
{
"type": "doctype",
"name": "Workflow Action",
"description": _("Actions for workflow (e.g. Approve, Cancel).")
},
]
},
{
"label": _("Email"),
"icon": "icon-envelope",
"items": [
{
"type": "doctype",
"name": "Outgoing Email Settings",
"description": _("Set outgoing mail server.")
},
]
},
{
"label": _("Printing and Branding"),
"icon": "icon-print",
"items": [
{
"type": "doctype",
"name": "Print Format",
"description": _("Customized HTML Templates for printing transctions.")
},
]
},
{
"label": _("Customize"),
"icon": "icon-glass",
"items": [
{
"type": "doctype",
"name": "Customize Form",
"description": _("Change field properties (hide, readonly, permission etc.)"),
"hide_count": True
},
{
"type": "doctype",
"name": "Custom Field",
"description": _("Add fields to forms.")
},
{
"type": "doctype",
"name": "Custom Script",
"description": _("Add custom javascript to forms.")
}
]
},
{
"label": _("System"),
"icon": "icon-cog",
"items": [
{
"type": "page",
"name": "applications",
"label": _("Application Installer"),
"description": _("Install Applications."),
"icon": "icon-download"
},
{
"type": "doctype",
"name": "Backup Manager",
"label": _("Download Backup"),
"onclick": "frappe.ui.toolbar.download_backup",
"icon": "icon-download-alt",
"description": _("Send download link of a recent backup to System Managers"),
"hide_count": True
},
{
"type": "doctype",
"name": "Social Login Keys",
"description": _("Enter keys to enable login via Facebook, Google, GitHub."),
},
{
"type": "doctype",
"name": "Backup Manager",
"description": _("Manage cloud backups on Dropbox"),
"hide_count": True
},
{
"type": "doctype",
"name": "Scheduler Log",
"description": _("Log of error on automated events (scheduler).")
},
]
}
]
def get_data():
out = list(data)
add_setup_section(out, "frappe", "website", _("Website"), "icon-globe")
return out
data = [
{
"label": _("Users and Permissions"),
"icon": "icon-group",
"items": [
{
"type": "doctype",
"name": "User",
"description": _("System and Website Users")
},
{
"type": "doctype",
"name": "Role",
"description": _("User Roles")
},
{
"type": "page",
"name": "permission-manager",
"label": "Permission Manager",
"icon": "icon-lock",
"description": _("Set Permissions on Document Types and Roles")
},
{
"type": "page",
"name": "user-properties",
"label": _("User Permission Restrictions"),
"icon": "icon-user",
"description": _("Set Defaults and Restrictions for Users")
},
]
},
{
"label": _("Settings"),
"icon": "icon-wrench",
"items": [
{
"type": "doctype",
"name": "System Settings",
"label": _("System Settings"),
"description": _("Language, Date and Time settings"),
"hide_count": True
},
{
"type": "page",
"name": "modules_setup",
"label": _("Show / Hide Modules"),
"icon": "icon-upload",
"description": _("Show or hide modules globally.")
},
{
"type": "doctype",
"name": "Naming Series",
"description": _("Set numbering series for transactions."),
"hide_count": True
},
]
},
{
"label": _("Data"),
"icon": "icon-th",
"items": [
{
"type": "page",
"name": "data-import-tool",
"label": _("Import / Export Data"),
"icon": "icon-upload",
"description": _("Import / Export Data from .csv files.")
},
{
"type": "doctype",
"name": "Rename Tool",
"description": _("Rename many items by uploading a .csv file."),
"hide_count": True
},
{
"type": "doctype",
"name": "File Data",
"description": _("Manage uploaded files.")
}
]
},
{
"label": _("Workflow"),
"icon": "icon-random",
"items": [
{
"type": "doctype",
"name": "Workflow",
"description": _("Define workflows for forms.")
},
{
"type": "doctype",
"name": "Workflow State",
"description": _("States for workflow (e.g. Draft, Approved, Cancelled).")
},
{
"type": "doctype",
"name": "Workflow Action",
"description": _("Actions for workflow (e.g. Approve, Cancel).")
},
]
},
{
"label": _("Email"),
"icon": "icon-envelope",
"items": [
{
"type": "doctype",
"name": "Outgoing Email Settings",
"description": _("Set outgoing mail server.")
},
]
},
{
"label": _("Printing and Branding"),
"icon": "icon-print",
"items": [
{
"type": "doctype",
"name": "Print Format",
"description": _("Customized HTML Templates for printing transctions.")
},
]
},
{
"label": _("Customize"),
"icon": "icon-glass",
"items": [
{
"type": "doctype",
"name": "Customize Form",
"description": _("Change field properties (hide, readonly, permission etc.)"),
"hide_count": True
},
{
"type": "doctype",
"name": "Custom Field",
"description": _("Add fields to forms.")
},
{
"type": "doctype",
"name": "Custom Script",
"description": _("Add custom javascript to forms.")
}
]
},
{
"label": _("System"),
"icon": "icon-cog",
"items": [
{
"type": "page",
"name": "applications",
"label": _("Application Installer"),
"description": _("Install Applications."),
"icon": "icon-download"
},
{
"type": "doctype",
"name": "Backup Manager",
"label": _("Download Backup"),
"onclick": "frappe.ui.toolbar.download_backup",
"icon": "icon-download-alt",
"description": _("Send download link of a recent backup to System Managers"),
"hide_count": True
},
{
"type": "doctype",
"name": "Social Login Keys",
"description": _("Enter keys to enable login via Facebook, Google, GitHub."),
},
{
"type": "doctype",
"name": "Backup Manager",
"description": _("Manage cloud backups on Dropbox"),
"hide_count": True
},
{
"type": "doctype",
"name": "Scheduler Log",
"description": _("Log of error on automated events (scheduler).")
},
]
}
]
add_setup_section(data, "frappe", "website", _("Website"), "icon-globe")
return data

View file

@ -1,93 +1,94 @@
from frappe import _
data = [
{
"label": _("Documents"),
"icon": "icon-star",
"items": [
{
"type": "doctype",
"name": "Web Page",
"description": _("Content web page."),
},
{
"type": "doctype",
"name": "Blog Post",
"description": _("Single Post (article)."),
},
{
"type": "doctype",
"name": "Blogger",
"description": _("User ID of a blog writer."),
},
{
"type": "doctype",
"name": "Website Group",
"description": _("Web Site Forum Page."),
},
{
"type": "doctype",
"name": "Post",
"description": _("List of Web Site Forum's Posts."),
},
{
"type": "doctype",
"name": "Website Slideshow",
"description": _("Embed image slideshows in website pages."),
},
]
},
{
"label": _("Setup"),
"icon": "icon-cog",
"items": [
{
"type": "doctype",
"name": "Website Settings",
"description": _("Setup of top navigation bar, footer and logo."),
},
{
"type": "page",
"name":"sitemap-browser",
"label": _("Sitemap Browser"),
"description": _("View or manage Website Route tree."),
"icon": "icon-sitemap"
},
{
"type": "doctype",
"name": "Style Settings",
"description": _("Setup of fonts and background."),
},
{
"type": "doctype",
"name": "Website Script",
"description": _("Javascript to append to the head section of the page."),
},
{
"type": "doctype",
"name": "Blog Settings",
"description": _("Write titles and introductions to your blog."),
},
{
"type": "doctype",
"name": "Blog Category",
"description": _("Categorize blog posts."),
},
{
"type": "doctype",
"name": "About Us Settings",
"description": _("Settings for About Us Page."),
},
{
"type": "doctype",
"name": "Contact Us Settings",
"description": _("Settings for Contact Us Page."),
},
{
"type": "doctype",
"name": "Website Page Permission",
"description": _("Define read, write, admin permissions for a Website Page."),
},
]
},
]
def get_data():
return [
{
"label": _("Documents"),
"icon": "icon-star",
"items": [
{
"type": "doctype",
"name": "Web Page",
"description": _("Content web page."),
},
{
"type": "doctype",
"name": "Blog Post",
"description": _("Single Post (article)."),
},
{
"type": "doctype",
"name": "Blogger",
"description": _("User ID of a blog writer."),
},
{
"type": "doctype",
"name": "Website Group",
"description": _("Web Site Forum Page."),
},
{
"type": "doctype",
"name": "Post",
"description": _("List of Web Site Forum's Posts."),
},
{
"type": "doctype",
"name": "Website Slideshow",
"description": _("Embed image slideshows in website pages."),
},
]
},
{
"label": _("Setup"),
"icon": "icon-cog",
"items": [
{
"type": "doctype",
"name": "Website Settings",
"description": _("Setup of top navigation bar, footer and logo."),
},
{
"type": "page",
"name":"sitemap-browser",
"label": _("Sitemap Browser"),
"description": _("View or manage Website Route tree."),
"icon": "icon-sitemap"
},
{
"type": "doctype",
"name": "Style Settings",
"description": _("Setup of fonts and background."),
},
{
"type": "doctype",
"name": "Website Script",
"description": _("Javascript to append to the head section of the page."),
},
{
"type": "doctype",
"name": "Blog Settings",
"description": _("Write titles and introductions to your blog."),
},
{
"type": "doctype",
"name": "Blog Category",
"description": _("Categorize blog posts."),
},
{
"type": "doctype",
"name": "About Us Settings",
"description": _("Settings for About Us Page."),
},
{
"type": "doctype",
"name": "Contact Us Settings",
"description": _("Settings for Contact Us Page."),
},
{
"type": "doctype",
"name": "Website Page Permission",
"description": _("Define read, write, admin permissions for a Website Page."),
},
]
},
]

View file

@ -10,7 +10,7 @@ from frappe.website.utils import is_signup_enabled
from frappe.utils import get_url, cstr
from frappe.utils.email_lib.email_body import get_email
from frappe.utils.email_lib.smtp import send
from frappe.utils import scrub_urls
from frappe.utils import scrub_urls, cint
from frappe import _
from frappe.model.document import Document
@ -78,6 +78,9 @@ def _make(doctype=None, name=None, content=None, subject=None, sent_or_received
d.communication_medium = communication_medium
d.idx = cint(frappe.db.sql("""select max(idx) from `tabCommunication`
where parenttype=%s and parent=%s""", (doctype, name))[0][0]) + 1
comm.ignore_permissions = True
comm.insert()
@ -118,7 +121,7 @@ def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]', s
footer = set_portal_link(sent_via, d)
send_print_in_body = frappe.db.get_value("Outgoing Email Settings", None, "send_print_in_body_and_attachment")
if not send_print_in_body:
if print_html and not send_print_in_body:
d.content += "<p>Please see attachment for document details.</p>"
mail = get_email(d.recipients, sender=d.sender, subject=d.subject,

View file

@ -48,7 +48,7 @@ cur_frm.fields_dict['dt'].get_query = function(doc, dt, dn) {
cur_frm.cscript.fieldtype = function(doc, dt, dn) {
if(doc.fieldtype == 'Link') cur_frm.fields_dict['options_help'].disp_area.innerHTML = 'Please enter name of the document you want this field to be linked to in <b>Options</b>.<br> Eg.: Customer';
else if(doc.fieldtype == 'Select') cur_frm.fields_dict['options_help'].disp_area.innerHTML = 'Please enter values in <b>Options</b> separated by enter. <br>Eg.: <b>Field:</b> Country <br><b>Options:</b><br>China<br>India<br>United States<br><br><b> OR </b><br>You can also link it to existing Documents.<br>Eg.: <b>link:</b>Customer';
else if(doc.fieldtype == 'Select') cur_frm.fields_dict['options_help'].disp_area.innerHTML = 'Please enter values in <b>Options</b>, with each option on a new line. <br>Eg.: <b>Field:</b> Country <br><b>Options:</b><br>China<br>India<br>United States<br><br><b>';
else cur_frm.fields_dict['options_help'].disp_area.innerHTML = '';
}
@ -64,9 +64,10 @@ cur_frm.cscript.dt = function(doc, dt, dn) {
args: { doctype: doc.dt, fieldname: doc.fieldname },
callback: function(r, rt) {
set_field_options('insert_after', r.message);
var fieldnames = $.map(r.message, function(v) { return v.value; });
if(insert_after==null || !in_list(r.message.split("\n"), insert_after)) {
insert_after = r.message.split("\n")[0];
if(insert_after==null || !in_list(fieldnames, insert_after)) {
insert_after = fieldnames[-1];
}
cur_frm.set_value('insert_after', insert_after);

View file

@ -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
import frappe
@ -9,7 +9,7 @@ from frappe import _
from frappe.model.document import Document
class CustomField(Document):
def autoname(self):
self.set_fieldname()
self.name = self.dt + "-" + self.fieldname
@ -19,16 +19,16 @@ class CustomField(Document):
if not self.label:
frappe.throw(_("Label is mandatory"))
# remove special characters from fieldname
self.fieldname = filter(lambda x: x.isdigit() or x.isalpha() or '_',
self.fieldname = filter(lambda x: x.isdigit() or x.isalpha() or '_',
cstr(self.label).lower().replace(' ','_'))
def validate(self):
if not self.idx:
self.idx = len(frappe.get_meta(self.dt).get("fields")) + 1
if not self.fieldname:
frappe.throw(_("Fieldname not set for Custom Field"))
def on_update(self):
# validate field
from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype
@ -36,7 +36,7 @@ class CustomField(Document):
validate_fields_for_doctype(self.dt)
frappe.clear_cache(doctype=self.dt)
# create property setter to emulate insert after
self.create_property_setter()
@ -44,7 +44,7 @@ class CustomField(Document):
if not frappe.flags.in_test:
from frappe.model.db_schema import updatedb
updatedb(self.dt)
def on_trash(self):
# delete property setter entries
frappe.db.sql("""\
@ -57,57 +57,33 @@ class CustomField(Document):
def create_property_setter(self):
if not self.insert_after: return
idx_label_list, field_list = get_fields_label(self.dt, 0)
label_index = idx_label_list.index(self.insert_after)
if label_index==-1: return
prev_field = field_list[label_index]
dt_meta = frappe.get_meta(self.dt)
if not dt_meta.get_field(self.insert_after):
frappe.throw(_("Insert After field '{0}' mentioned in Custom Field '{1}', does not exist")
.format(dt_meta.get_label(self.insert_after), self.label), frappe.DoesNotExistError)
frappe.db.sql("""\
DELETE FROM `tabProperty Setter`
WHERE doc_type = %s
AND field_name = %s
AND property = 'previous_field'""", (self.dt, self.fieldname))
frappe.make_property_setter({
"doctype":self.dt,
"fieldname": self.fieldname,
"doctype":self.dt,
"fieldname": self.fieldname,
"property": "previous_field",
"value": prev_field
"value": self.insert_after
})
@frappe.whitelist()
def get_fields_label(dt=None, form=1):
"""
if form=1: Returns a string of field labels separated by \n
if form=0: Returns lists of field labels and field names
"""
import frappe
from frappe.utils import cstr
fieldname = None
if not dt:
dt = frappe.form_dict.get('doctype')
fieldname = frappe.form_dict.get('fieldname')
if not dt: return ""
docfields = frappe.get_meta(dt).get("fields")
if fieldname:
idx_label_list = [cstr(d.label) or cstr(d.fieldname) or cstr(d.fieldtype)
for d in docfields if d.fieldname != fieldname]
else:
idx_label_list = [cstr(d.label) or cstr(d.fieldname) or cstr(d.fieldtype)
for d in docfields]
if form:
return "\n".join(idx_label_list)
else:
# return idx_label_list, field_list
field_list = [cstr(d.fieldname) for d in docfields]
return idx_label_list, field_list
def get_fields_label(doctype=None):
return [{"value": df.fieldname, "label": _(df.label)} for df in frappe.get_meta(doctype).get("fields")]
def create_custom_field_if_values_exist(doctype, df):
df = frappe._dict(df)
if df.fieldname in frappe.db.get_table_columns(doctype) and \
frappe.db.sql("""select count(*) from `tab{doctype}`
frappe.db.sql("""select count(*) from `tab{doctype}`
where ifnull({fieldname},'')!=''""".format(doctype=doctype, fieldname=df.fieldname))[0][0] and \
not frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df.fieldname}):
frappe.get_doc({
@ -120,4 +96,4 @@ def create_custom_field_if_values_exist(doctype, df):
"options": df.options,
"insert_after": df.insert_after
}).insert()

View file

@ -1,6 +1,6 @@
{
"autoname": "DL.####",
"creation": "2013-01-29 17:55:08.000000",
"creation": "2013-01-29 17:55:08",
"docstatus": 0,
"doctype": "DocType",
"fields": [
@ -8,6 +8,7 @@
"fieldname": "doc_type",
"fieldtype": "Link",
"hidden": 0,
"in_list_view": 1,
"label": "Enter Form Type",
"no_copy": 0,
"options": "DocType",
@ -25,23 +26,37 @@
"fieldtype": "Column Break",
"permlevel": 0
},
{
"fieldname": "default_print_format",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Default Print Format",
"no_copy": 0,
"options": "Print Format",
"permlevel": 0,
"search_index": 0
},
{
"description": "Fields separated by comma (,) will be included in the<br /><b>Search By</b> list of Search dialog box",
"fieldname": "search_fields",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Search Fields",
"no_copy": 0,
"permlevel": 0,
"search_index": 0
},
{
"fieldname": "default_print_format",
"fieldtype": "Link",
"label": "Default Print Format",
"no_copy": 0,
"options": "Print Format",
"permlevel": 0,
"search_index": 0
"fieldname": "sort_field",
"fieldtype": "Data",
"label": "Sort Field",
"permlevel": 0
},
{
"fieldname": "sort_order",
"fieldtype": "Data",
"label": "Sort Order",
"permlevel": 0
},
{
"fieldname": "column_break1",
@ -111,7 +126,7 @@
"icon": "icon-glass",
"idx": 1,
"issingle": 1,
"modified": "2014-01-15 16:16:22.000000",
"modified": "2014-05-08 09:27:44.167026",
"modified_by": "Administrator",
"module": "Core",
"name": "Customize Form",

View file

@ -14,6 +14,8 @@ from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype
class CustomizeForm(Document):
doctype_properties = {
'search_fields': 'Data',
'sort_field': 'Data',
'sort_order': 'Data',
'default_print_format': 'Data',
'read_only_onload': 'Check',
'allow_attach': 'Check',

View file

@ -138,6 +138,17 @@
"options": "\nTitle Case\nUPPER CASE",
"permlevel": 0
},
{
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
"label": "Description",
"oldfieldname": "description",
"oldfieldtype": "Text",
"permlevel": 0,
"reqd": 0,
"search_index": 0
},
{
"fieldname": "column_break_15",
"fieldtype": "Column Break",
@ -162,15 +173,20 @@
"search_index": 0
},
{
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
"label": "Description",
"oldfieldname": "description",
"oldfieldtype": "Text",
"permlevel": 0,
"reqd": 0,
"search_index": 0
"default": "modified",
"description": "",
"fieldname": "sort_field",
"fieldtype": "Data",
"label": "Sort Field",
"permlevel": 0
},
{
"default": "DESC",
"fieldname": "sort_order",
"fieldtype": "Select",
"label": "Sort Order",
"options": "ASC\nDESC",
"permlevel": 0
},
{
"depends_on": "eval:!doc.istable",
@ -329,7 +345,7 @@
"idx": 1,
"issingle": 0,
"istable": 0,
"modified": "2014-05-01 05:27:22.582492",
"modified": "2014-05-08 09:23:56.952829",
"modified_by": "Administrator",
"module": "Core",
"name": "DocType",
@ -361,5 +377,7 @@
}
],
"read_only": 0,
"search_fields": "module"
"search_fields": "module",
"sort_field": "name",
"sort_order": "ASC"
}

View file

@ -10,6 +10,7 @@ import os
from frappe.utils import now, cint
from frappe.model import no_value_fields
from frappe.model.document import Document
from frappe.model.db_schema import type_map
class DocType(Document):
def validate(self):
@ -30,7 +31,6 @@ class DocType(Document):
validate_permissions(self)
self.make_amendable()
self.check_link_replacement_error()
def change_modified_of_parent(self):
if frappe.flags.in_import:
@ -93,12 +93,6 @@ class DocType(Document):
module.on_doctype_update()
frappe.clear_cache(doctype=self.name)
def check_link_replacement_error(self):
for d in self.get("fields", {"fieldtype":"Select"}):
if (frappe.db.get_value("DocField", d.name, "options") or "").startswith("link:") \
and not d.options.startswith("link:"):
frappe.throw(_("'link:' type Select {0} getting replaced").format(d.label))
def on_trash(self):
frappe.db.sql("delete from `tabCustom Field` where dt = %s", self.name)
frappe.db.sql("delete from `tabCustom Script` where dt = %s", self.name)
@ -176,7 +170,7 @@ def validate_fields(fields):
def check_unique_fieldname(fieldname):
duplicates = filter(None, map(lambda df: df.fieldname==fieldname and str(df.idx) or None, fields))
if len(duplicates) > 1:
frappe.throw(_("Fieldname {0} appears multiple times in rows {1}").format(", ".join(duplicates)))
frappe.throw(_("Fieldname {0} appears multiple times in rows {1}").format(fieldname, ", ".join(duplicates)))
def check_illegal_mandatory(d):
if d.fieldtype in ('HTML', 'Button', 'Section Break', 'Column Break') and d.reqd:
@ -198,7 +192,8 @@ def validate_fields(fields):
def check_min_items_in_list(fields):
if len(filter(lambda d: d.in_list_view, fields))==0:
for d in fields[:5]:
d.in_list_view = 1
if d.fieldtype in type_map:
d.in_list_view = 1
def check_width(d):
if d.fieldtype == "Currency" and cint(d.width) < 100:

View file

@ -1,17 +1,17 @@
{
"autoname": "EVP.#####",
"creation": "2013-02-22 01:27:33.000000",
"creation": "2013-02-22 01:27:33",
"docstatus": 0,
"doctype": "DocType",
"fields": [
{
"fieldname": "person",
"fieldtype": "Select",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Person",
"oldfieldname": "person",
"oldfieldtype": "Select",
"options": "link:User",
"options": "User",
"permlevel": 0,
"print_width": "240px",
"search_index": 1,
@ -20,9 +20,12 @@
],
"idx": 1,
"istable": 1,
"modified": "2013-12-20 19:23:14.000000",
"modified": "2014-05-09 02:12:32.374008",
"modified_by": "Administrator",
"module": "Core",
"name": "Event User",
"owner": "Administrator"
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View file

@ -44,8 +44,8 @@ class FileData(Document):
pass
# if file not attached to any other record, delete it
if self.file_name and not frappe.db.count("File Data",
{"content_hash": self.content_hash, "name": ["!=", self.name]}):
if self.file_name and self.content_hash and (not frappe.db.count("File Data",
{"content_hash": self.content_hash, "name": ["!=", self.name]})):
delete_file_data_content(self)
def on_rollback(self):

View file

@ -1,7 +1,7 @@
{
"allow_attach": 1,
"autoname": "field:letter_head_name",
"creation": "2012-11-22 17:45:46.000000",
"creation": "2012-11-22 17:45:46",
"docstatus": 0,
"doctype": "DocType",
"fields": [
@ -9,6 +9,7 @@
"fieldname": "letter_head_name",
"fieldtype": "Data",
"in_filter": 0,
"in_list_view": 1,
"label": "Letter Head Name",
"oldfieldname": "letter_head_name",
"oldfieldtype": "Data",
@ -19,6 +20,7 @@
"depends_on": "letter_head_name",
"fieldname": "disabled",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Disabled",
"oldfieldname": "disabled",
"oldfieldtype": "Check",
@ -29,6 +31,7 @@
"description": "Check this to make this the default letter head in all prints",
"fieldname": "is_default",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Is Default",
"oldfieldname": "is_default",
"oldfieldtype": "Check",
@ -39,6 +42,7 @@
"description": "Letter Head in HTML",
"fieldname": "content",
"fieldtype": "Text Editor",
"in_list_view": 1,
"label": "Content",
"oldfieldname": "content",
"oldfieldtype": "Text Editor",
@ -48,7 +52,7 @@
"icon": "icon-font",
"idx": 1,
"max_attachments": 3,
"modified": "2014-01-20 17:48:56.000000",
"modified": "2014-05-07 06:03:07.760995",
"modified_by": "Administrator",
"module": "Core",
"name": "Letter Head",
@ -69,11 +73,11 @@
},
{
"delete": 0,
"email": 1,
"email": 0,
"permlevel": 0,
"print": 1,
"print": 0,
"read": 1,
"report": 1,
"report": 0,
"role": "All"
}
]

View file

@ -1,4 +1,5 @@
{
"autoname": "hash",
"creation": "2013-11-18 05:31:03.000000",
"docstatus": 0,
"doctype": "DocType",
@ -25,9 +26,9 @@
}
],
"idx": 1,
"modified": "2013-12-20 19:24:14.000000",
"modified": "2014-05-12 19:24:14.000000",
"modified_by": "Administrator",
"module": "Core",
"name": "Notification Count",
"owner": "Administrator"
}
}

View file

@ -71,19 +71,19 @@ class Page(Document):
fpath = os.path.join(path, scrub(self.name) + '.js')
if os.path.exists(fpath):
with open(fpath, 'r') as f:
self.script = f.read()
self.script = unicode(f.read(), "utf-8")
# css
fpath = os.path.join(path, scrub(self.name) + '.css')
if os.path.exists(fpath):
with open(fpath, 'r') as f:
self.style = f.read()
self.style = unicode(f.read(), "utf-8")
# html
fpath = os.path.join(path, scrub(self.name) + '.html')
if os.path.exists(fpath):
with open(fpath, 'r') as f:
self.content = f.read()
self.content = unicode(f.read(), "utf-8")
if frappe.lang != 'en':
from frappe.translate import get_lang_js

View file

@ -1,10 +1,13 @@
cur_frm.cscript.refresh = function(doc, dt, dn) {
if(user!="Administrator") {
if(doc.standard == 'Yes') {
cur_frm.cscript.refresh = function (doc) {
if (user!="Administrator") {
if (doc.standard == 'Yes') {
cur_frm.toggle_enable(["html", "doc_type", "module"], false);
cur_frm.disable_save();
} else {
cur_frm.toggle_enable(["html", "doc_type", "module"], true);
cur_frm.enable_save();
}
cur_frm.toggle_enable("standard", false);
}
}
}

View file

@ -3,21 +3,22 @@
"allow_copy": 0,
"allow_rename": 0,
"autoname": "Prompt",
"creation": "2013-01-23 19:54:43.000000",
"creation": "2013-01-23 19:54:43",
"docstatus": 0,
"doctype": "DocType",
"fields": [
{
"allow_on_submit": 0,
"fieldname": "module",
"fieldtype": "Select",
"fieldtype": "Link",
"hidden": 0,
"in_filter": 1,
"in_list_view": 1,
"label": "Module",
"no_copy": 0,
"oldfieldname": "module",
"oldfieldtype": "Select",
"options": "link:Module Def",
"options": "Module Def",
"permlevel": 0,
"print_hide": 0,
"report_hide": 0,
@ -27,10 +28,11 @@
{
"description": "Belongs to",
"fieldname": "doc_type",
"fieldtype": "Select",
"fieldtype": "Link",
"in_filter": 1,
"in_list_view": 1,
"label": "DocType",
"options": "link:DocType",
"options": "DocType",
"permlevel": 0,
"reqd": 0,
"search_index": 0
@ -47,6 +49,7 @@
"fieldtype": "Select",
"hidden": 0,
"in_filter": 1,
"in_list_view": 1,
"label": "Standard",
"no_copy": 1,
"oldfieldname": "standard",
@ -62,6 +65,7 @@
{
"fieldname": "print_format_type",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Print Format Type",
"options": "Client\nServer",
"permlevel": 0
@ -99,7 +103,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2014-01-20 17:49:00.000000",
"modified": "2014-05-09 02:12:39.540952",
"modified_by": "Administrator",
"module": "Core",
"name": "Print Format",
@ -158,5 +162,7 @@
}
],
"read_only": 0,
"read_only_onload": 0
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
}

View file

@ -2,7 +2,7 @@ cur_frm.cscript.refresh = function(doc) {
cur_frm.add_custom_button("Show Report", function() {
switch(doc.report_type) {
case "Report Builder":
frappe.set_route("Report", doc.name);
frappe.set_route("Report", doc.ref_doctype, doc.name);
break;
case "Query Report":
frappe.set_route("query-report", doc.name);
@ -12,7 +12,7 @@ cur_frm.cscript.refresh = function(doc) {
break;
}
}, "icon-table")
cur_frm.set_intro("");
switch(doc.report_type) {
case "Report Builder":
@ -30,4 +30,4 @@ cur_frm.cscript.refresh = function(doc) {
cur_frm.set_intro(__("Write a Python file in the same folder where this is saved and return column and result."))
break;
}
}
}

View file

@ -1,6 +1,6 @@
{
"autoname": "field:report_name",
"creation": "2013-03-09 15:45:57.000000",
"creation": "2013-03-09 15:45:57",
"docstatus": 0,
"doctype": "DocType",
"document_type": "System",
@ -101,7 +101,7 @@
],
"icon": "icon-table",
"idx": 1,
"modified": "2014-03-07 15:20:02.000000",
"modified": "2014-05-12 17:08:04.185601",
"modified_by": "Administrator",
"module": "Core",
"name": "Report",
@ -157,5 +157,7 @@
"role": "All",
"submit": 0
}
]
],
"sort_field": "modified",
"sort_order": "DESC"
}

View file

@ -1,4 +1,4 @@
frappe.pages['applications'].onload = function(wrapper) {
frappe.pages['applications'].onload = function(wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
title: __('Application Installer'),
@ -10,7 +10,7 @@ frappe.pages['applications'].onload = function(wrapper) {
method:"frappe.core.page.applications.applications.get_app_list",
callback: function(r) {
var $main = $(wrapper).find(".layout-main");
if(!keys(r.message).length) {
$main.html('<div class="alert alert-info">No Apps Installed</div>');
return;
@ -54,7 +54,7 @@ frappe.pages['applications'].onload = function(wrapper) {
</div>\
</div>', app))
$app.appendTo($main)
if(app.installed) {
$btn = $('<button class="btn btn-success" disabled=disabled>\
<i class="icon-ok"></i> Installed</button>');
@ -63,7 +63,7 @@ frappe.pages['applications'].onload = function(wrapper) {
.attr("data-app", app.app_name)
.on("click", function() {
frappe.call({
method:"frappe.installer.install_app",
method:"frappe.core.page.applications.applications.install_app",
args: {name: $(this).attr("data-app")},
callback: function(r) {
if(!r.exc) {

View file

@ -3,6 +3,8 @@
from __future__ import unicode_literals
import frappe
import frappe.installer
from frappe import _
@frappe.whitelist()
def get_app_list():
@ -19,8 +21,16 @@ def get_app_list():
"app_publisher", "app_version", "app_url", "app_color"):
val = app_hooks.get(key) or []
out[app][key] = val[0] if len(val) else ""
if app in installed:
out[app]["installed"] = 1
return out
@frappe.whitelist()
def install_app(name):
app_hooks = frappe.get_hooks(app_name=name)
if app_hooks.get('hide_in_installer'):
frappe.throw(_("You cannot install this app"))
frappe.installer.install_app(name)

View file

@ -108,8 +108,6 @@ def get_template(doctype=None, parent_doctype=None, all_doctypes="No", with_data
if docfield.fieldtype == 'Select':
if not docfield.options:
return ''
elif docfield.options.startswith('link:'):
return 'Valid %s' % docfield.options[5:]
else:
return 'One of: %s' % ', '.join(filter(None, docfield.options.split('\n')))
elif docfield.fieldtype == 'Link':

View file

@ -186,9 +186,6 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False,
doc = None
doc = get_doc(row_idx)
if doc.get("name"):
doc["_new_name_set"] = True
try:
frappe.local.message_log = []
if doc.get("parentfield"):

View file

@ -73,7 +73,7 @@ frappe.desktop.render = function() {
$.each(modules_list, function(i, m) {
var module = frappe.modules[m];
if(module) {
if(m!="Setup" && user_list.indexOf(m)!==-1)
if(!in_list(["Setup", "Core"], m) && user_list.indexOf(m)!==-1)
add_icon(m);
}
})
@ -82,6 +82,9 @@ frappe.desktop.render = function() {
if(user_roles.indexOf('System Manager')!=-1)
add_icon('Setup')
if(user_roles.indexOf('Administrator')!=-1)
add_icon('Core')
// all applications
frappe.modules["All Applications"] = {
icon: "icon-th",

View file

@ -18,17 +18,17 @@ def get_users_and_links():
def get_properties(parent=None, defkey=None, defvalue=None):
if defkey and not frappe.permissions.can_restrict(defkey, defvalue):
raise frappe.PermissionError
conditions, values = _build_conditions(locals())
properties = frappe.db.sql("""select name, parent, defkey, defvalue
properties = frappe.db.sql("""select name, parent, defkey, defvalue
from tabDefaultValue
where parent not in ('__default', '__global')
and substr(defkey,1,1)!='_'
and parenttype='Restriction'
{conditions}
order by parent, defkey""".format(conditions=conditions), values, as_dict=True)
if not defkey:
out = []
doctypes = get_restrictable_doctypes()
@ -36,9 +36,9 @@ def get_properties(parent=None, defkey=None, defvalue=None):
if p.defkey in doctypes:
out.append(p)
properties = out
return properties
def _build_conditions(filters):
conditions = []
values = {}
@ -46,7 +46,7 @@ def _build_conditions(filters):
if filters.get(key):
conditions.append("and `{key}`=%({key})s".format(key=key))
values[key] = value
return "\n".join(conditions), values
@frappe.whitelist()
@ -54,35 +54,35 @@ def remove(user, name, defkey, defvalue):
if not frappe.permissions.can_restrict_user(user, defkey, defvalue):
raise frappe.PermissionError("Cannot Remove Restriction for User: {user} on DocType: {doctype} and Name: {name}".format(
user=user, doctype=defkey, name=defvalue))
frappe.defaults.clear_default(name=name)
frappe.defaults.clear_default(key=defkey, value=defvalue, parent=user, name=name)
def clear_restrictions(doctype):
frappe.defaults.clear_default(parenttype="Restriction", key=doctype)
@frappe.whitelist()
def add(user, defkey, defvalue):
if not frappe.permissions.can_restrict_user(user, defkey, defvalue):
raise frappe.PermissionError("Cannot Restrict User: {user} for DocType: {doctype} and Name: {name}".format(
user=user, doctype=defkey, name=defvalue))
# check if already exists
d = frappe.db.sql("""select name from tabDefaultValue
d = frappe.db.sql("""select name from tabDefaultValue
where parent=%s and parenttype='Restriction' and defkey=%s and defvalue=%s""", (user, defkey, defvalue))
if not d:
frappe.defaults.add_default(defkey, defvalue, user, "Restriction")
def get_restrictable_doctypes():
user_roles = frappe.get_roles()
condition = ""
values = []
if "System Manager" not in user_roles:
condition = """and exists(select `tabDocPerm`.name from `tabDocPerm`
condition = """and exists(select `tabDocPerm`.name from `tabDocPerm`
where `tabDocPerm`.parent=`tabDocType`.name and `tabDocPerm`.`restrict`=1
and `tabDocPerm`.role in ({roles}))""".format(roles=", ".join(["%s"]*len(user_roles)))
values = user_roles
return frappe.db.sql_list("""select name from tabDocType
return frappe.db.sql_list("""select name from tabDocType
where ifnull(issingle,0)=0 and ifnull(istable,0)=0 {condition}""".format(condition=condition),
tuple(values))

View file

@ -8,21 +8,21 @@
DROP TABLE IF EXISTS `tabDocField`;
CREATE TABLE `tabDocField` (
`name` varchar(120) NOT NULL,
`name` varchar(255) NOT NULL,
`creation` datetime(6) DEFAULT NULL,
`modified` datetime(6) DEFAULT NULL,
`modified_by` varchar(40) DEFAULT NULL,
`owner` varchar(40) DEFAULT NULL,
`modified_by` varchar(255) DEFAULT NULL,
`owner` varchar(255) DEFAULT NULL,
`docstatus` int(1) DEFAULT '0',
`parent` varchar(120) DEFAULT NULL,
`parentfield` varchar(120) DEFAULT NULL,
`parenttype` varchar(120) DEFAULT NULL,
`parent` varchar(255) DEFAULT NULL,
`parentfield` varchar(255) DEFAULT NULL,
`parenttype` varchar(255) DEFAULT NULL,
`idx` int(8) DEFAULT NULL,
`fieldname` varchar(180) DEFAULT NULL,
`label` varchar(180) DEFAULT NULL,
`oldfieldname` varchar(180) DEFAULT NULL,
`fieldtype` varchar(180) DEFAULT NULL,
`oldfieldtype` varchar(180) DEFAULT NULL,
`fieldname` varchar(255) DEFAULT NULL,
`label` varchar(255) DEFAULT NULL,
`oldfieldname` varchar(255) DEFAULT NULL,
`fieldtype` varchar(255) DEFAULT NULL,
`oldfieldtype` varchar(255) DEFAULT NULL,
`options` text,
`search_index` int(1) DEFAULT NULL,
`hidden` int(1) DEFAULT NULL,
@ -32,12 +32,12 @@ CREATE TABLE `tabDocField` (
`reqd` int(1) DEFAULT NULL,
`no_copy` int(1) DEFAULT NULL,
`allow_on_submit` int(1) DEFAULT NULL,
`trigger` varchar(180) DEFAULT NULL,
`depends_on` varchar(180) DEFAULT NULL,
`trigger` varchar(255) DEFAULT NULL,
`depends_on` varchar(255) DEFAULT NULL,
`permlevel` int(11) DEFAULT '0',
`ignore_restrictions` int(1) DEFAULT NULL,
`width` varchar(180) DEFAULT NULL,
`print_width` varchar(180) DEFAULT NULL,
`width` varchar(255) DEFAULT NULL,
`print_width` varchar(255) DEFAULT NULL,
`default` text,
`description` text,
`in_filter` int(1) DEFAULT NULL,
@ -57,22 +57,20 @@ CREATE TABLE `tabDocField` (
--
DROP TABLE IF EXISTS `tabDocPerm`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tabDocPerm` (
`name` varchar(120) NOT NULL,
`name` varchar(255) NOT NULL,
`creation` datetime(6) DEFAULT NULL,
`modified` datetime(6) DEFAULT NULL,
`modified_by` varchar(40) DEFAULT NULL,
`owner` varchar(40) DEFAULT NULL,
`modified_by` varchar(255) DEFAULT NULL,
`owner` varchar(255) DEFAULT NULL,
`docstatus` int(1) DEFAULT '0',
`parent` varchar(120) DEFAULT NULL,
`parentfield` varchar(120) DEFAULT NULL,
`parenttype` varchar(120) DEFAULT NULL,
`parent` varchar(255) DEFAULT NULL,
`parentfield` varchar(255) DEFAULT NULL,
`parenttype` varchar(255) DEFAULT NULL,
`idx` int(8) DEFAULT NULL,
`permlevel` int(11) DEFAULT '0',
`role` varchar(180) DEFAULT NULL,
`match` varchar(180) DEFAULT NULL,
`role` varchar(255) DEFAULT NULL,
`match` varchar(255) DEFAULT NULL,
`read` int(1) DEFAULT NULL,
`write` int(1) DEFAULT NULL,
`create` int(1) DEFAULT NULL,
@ -89,43 +87,42 @@ CREATE TABLE `tabDocPerm` (
PRIMARY KEY (`name`),
KEY `parent` (`parent`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `tabDocType`
--
DROP TABLE IF EXISTS `tabDocType`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tabDocType` (
`name` varchar(180) NOT NULL DEFAULT '',
`name` varchar(255) NOT NULL DEFAULT '',
`creation` datetime(6) DEFAULT NULL,
`modified` datetime(6) DEFAULT NULL,
`modified_by` varchar(40) DEFAULT NULL,
`owner` varchar(180) DEFAULT NULL,
`modified_by` varchar(255) DEFAULT NULL,
`owner` varchar(255) DEFAULT NULL,
`docstatus` int(1) DEFAULT '0',
`parent` varchar(120) DEFAULT NULL,
`parentfield` varchar(120) DEFAULT NULL,
`parenttype` varchar(120) DEFAULT NULL,
`parent` varchar(255) DEFAULT NULL,
`parentfield` varchar(255) DEFAULT NULL,
`parenttype` varchar(255) DEFAULT NULL,
`idx` int(8) DEFAULT NULL,
`search_fields` varchar(180) DEFAULT NULL,
`search_fields` varchar(255) DEFAULT NULL,
`issingle` int(1) DEFAULT NULL,
`istable` int(1) DEFAULT NULL,
`version` int(11) DEFAULT NULL,
`module` varchar(180) DEFAULT NULL,
`plugin` varchar(180) DEFAULT NULL,
`autoname` varchar(180) DEFAULT NULL,
`name_case` varchar(180) DEFAULT NULL,
`title_field` varchar(180) DEFAULT NULL,
`module` varchar(255) DEFAULT NULL,
`plugin` varchar(255) DEFAULT NULL,
`autoname` varchar(255) DEFAULT NULL,
`name_case` varchar(255) DEFAULT NULL,
`title_field` varchar(255) DEFAULT NULL,
`sort_field` varchar(255) DEFAULT NULL,
`sort_order` varchar(255) DEFAULT NULL,
`description` text,
`colour` varchar(180) DEFAULT NULL,
`colour` varchar(255) DEFAULT NULL,
`read_only` int(1) DEFAULT NULL,
`in_create` int(1) DEFAULT NULL,
`show_in_menu` int(1) DEFAULT NULL,
`menu_index` int(11) DEFAULT NULL,
`parent_node` varchar(180) DEFAULT NULL,
`smallicon` varchar(180) DEFAULT NULL,
`parent_node` varchar(255) DEFAULT NULL,
`smallicon` varchar(255) DEFAULT NULL,
`allow_print` int(1) DEFAULT NULL,
`allow_email` int(1) DEFAULT NULL,
`allow_copy` int(1) DEFAULT NULL,
@ -136,47 +133,33 @@ CREATE TABLE `tabDocType` (
`allow_attach` int(1) DEFAULT NULL,
`use_template` int(1) DEFAULT NULL,
`max_attachments` int(11) DEFAULT NULL,
`section_style` varchar(180) DEFAULT NULL,
`client_script` mediumtext,
`client_script_core` mediumtext,
`server_code` mediumtext,
`server_code_core` mediumtext,
`server_code_compiled` mediumtext,
`client_string` mediumtext,
`server_code_error` varchar(180) DEFAULT NULL,
`print_outline` varchar(180) DEFAULT NULL,
`dt_template` mediumtext,
`print_outline` varchar(255) DEFAULT NULL,
`is_transaction_doc` int(1) DEFAULT NULL,
`change_log` mediumtext,
`read_only_onload` int(1) DEFAULT NULL,
`allow_trash` int(1) DEFAULT NULL,
`in_dialog` int(1) DEFAULT NULL,
`document_type` varchar(180) DEFAULT NULL,
`icon` varchar(180) DEFAULT NULL,
`tag_fields` varchar(180) DEFAULT NULL,
`subject` varchar(180) DEFAULT NULL,
`document_type` varchar(255) DEFAULT NULL,
`icon` varchar(255) DEFAULT NULL,
`tag_fields` varchar(255) DEFAULT NULL,
`subject` varchar(255) DEFAULT NULL,
`_last_update` varchar(32) DEFAULT NULL,
`default_print_format` varchar(180) DEFAULT NULL,
`default_print_format` varchar(255) DEFAULT NULL,
`is_submittable` int(1) DEFAULT NULL,
`_user_tags` varchar(180) DEFAULT NULL,
`_user_tags` varchar(255) DEFAULT NULL,
`custom` int(1) DEFAULT NULL,
PRIMARY KEY (`name`),
KEY `parent` (`parent`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `tabSeries`
--
DROP TABLE IF EXISTS `tabSeries`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tabSeries` (
`name` varchar(100) DEFAULT NULL,
`current` int(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@ -184,18 +167,15 @@ CREATE TABLE `tabSeries` (
--
DROP TABLE IF EXISTS `tabSessions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tabSessions` (
`user` varchar(255) DEFAULT NULL,
`sid` varchar(120) DEFAULT NULL,
`sid` varchar(255) DEFAULT NULL,
`sessiondata` longtext,
`ipaddress` varchar(16) DEFAULT NULL,
`lastupdate` datetime(6) DEFAULT NULL,
`status` varchar(20) DEFAULT NULL,
KEY `sid` (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@ -203,11 +183,9 @@ CREATE TABLE `tabSessions` (
--
DROP TABLE IF EXISTS `tabSingles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tabSingles` (
`doctype` varchar(40) DEFAULT NULL,
`field` varchar(40) DEFAULT NULL,
`doctype` varchar(255) DEFAULT NULL,
`field` varchar(255) DEFAULT NULL,
`value` text,
KEY `singles_doctype_field_index` (`doctype`, `field`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@ -217,11 +195,9 @@ CREATE TABLE `tabSingles` (
--
DROP TABLE IF EXISTS `__Auth`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `__Auth` (
`user` VARCHAR(180) NOT NULL PRIMARY KEY,
`password` VARCHAR(180) NOT NULL,
`user` VARCHAR(255) NOT NULL PRIMARY KEY,
`password` VARCHAR(255) NOT NULL,
KEY `user` (`user`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@ -231,22 +207,22 @@ CREATE TABLE `__Auth` (
DROP TABLE IF EXISTS `tabFile Data`;
CREATE TABLE `tabFile Data` (
`name` varchar(120) NOT NULL,
`name` varchar(255) NOT NULL,
`creation` datetime(6) DEFAULT NULL,
`modified` datetime(6) DEFAULT NULL,
`modified_by` varchar(40) DEFAULT NULL,
`owner` varchar(40) DEFAULT NULL,
`modified_by` varchar(255) DEFAULT NULL,
`owner` varchar(255) DEFAULT NULL,
`docstatus` int(1) DEFAULT '0',
`parent` varchar(120) DEFAULT NULL,
`parentfield` varchar(120) DEFAULT NULL,
`parenttype` varchar(120) DEFAULT NULL,
`parent` varchar(255) DEFAULT NULL,
`parentfield` varchar(255) DEFAULT NULL,
`parenttype` varchar(255) DEFAULT NULL,
`idx` int(8) DEFAULT NULL,
`file_name` varchar(180) DEFAULT NULL,
`file_url` varchar(180) DEFAULT NULL,
`module` varchar(180) DEFAULT NULL,
`attached_to_name` varchar(180) DEFAULT NULL,
`file_name` varchar(255) DEFAULT NULL,
`file_url` varchar(255) DEFAULT NULL,
`module` varchar(255) DEFAULT NULL,
`attached_to_name` varchar(255) DEFAULT NULL,
`file_size` int(11) DEFAULT NULL,
`attached_to_doctype` varchar(180) DEFAULT NULL,
`attached_to_doctype` varchar(255) DEFAULT NULL,
PRIMARY KEY (`name`),
KEY `parent` (`parent`),
KEY `attached_to_name` (`attached_to_name`),
@ -259,18 +235,18 @@ CREATE TABLE `tabFile Data` (
DROP TABLE IF EXISTS `tabDefaultValue`;
CREATE TABLE `tabDefaultValue` (
`name` varchar(120) NOT NULL,
`name` varchar(255) NOT NULL,
`creation` datetime(6) DEFAULT NULL,
`modified` datetime(6) DEFAULT NULL,
`modified_by` varchar(40) DEFAULT NULL,
`owner` varchar(40) DEFAULT NULL,
`modified_by` varchar(255) DEFAULT NULL,
`owner` varchar(255) DEFAULT NULL,
`docstatus` int(1) DEFAULT '0',
`parent` varchar(120) DEFAULT NULL,
`parentfield` varchar(120) DEFAULT NULL,
`parenttype` varchar(120) DEFAULT NULL,
`parent` varchar(255) DEFAULT NULL,
`parentfield` varchar(255) DEFAULT NULL,
`parenttype` varchar(255) DEFAULT NULL,
`idx` int(8) DEFAULT NULL,
`defvalue` text,
`defkey` varchar(180) DEFAULT NULL,
`defkey` varchar(255) DEFAULT NULL,
PRIMARY KEY (`name`),
KEY `parent` (`parent`),
KEY `defaultvalue_parent_defkey_index` (`parent`,`defkey`)

View file

@ -7,11 +7,12 @@ fr français
hi हिंदी
hr hrvatski
it italiano
kn ಕನ್ನಡ
nl nederlands
pt-BR português brasileiro
pt português
sr српски
ta தமிழ்
th ไทย
zh-cn 中国(简体
zh-tw 中國(繁體
zh-cn 中国(简体
zh-tw 中國(繁體

View file

@ -66,6 +66,7 @@ scheduler_events = {
"frappe.utils.email_lib.bulk.clear_outbox",
"frappe.core.doctype.notification_count.notification_count.delete_event_notification_count",
"frappe.core.doctype.event.event.send_event_digest",
"frappe.sessions.clear_expired_sessions",
],
"hourly": [
"frappe.templates.generators.website_group.clear_event_cache"

View file

@ -13,6 +13,7 @@ import getpass
from frappe import _
from frappe.model.db_schema import DbManager
from frappe.model.sync import sync_for
from frappe.utils.fixtures import sync_fixtures
def install_db(root_login="root", root_password=None, db_name=None, source_sql=None,
admin_password = 'admin', verbose=True, force=0, site_config=None, reinstall=False):
@ -81,7 +82,6 @@ def make_connection(root_login, root_password):
root_password = getpass.getpass("MySQL root password: ")
return frappe.database.Database(user=root_login, password=root_password)
@frappe.whitelist()
def install_app(name, verbose=False, set_as_patched=True):
frappe.flags.in_install_app = name
frappe.clear_cache()
@ -105,6 +105,7 @@ def install_app(name, verbose=False, set_as_patched=True):
if name != "frappe":
add_module_defs(name)
sync_for(name, force=True, sync_everything=True, verbose=verbose)
add_to_installed_apps(name)
@ -115,6 +116,8 @@ 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()
frappe.flags.in_install_app = False
def add_to_installed_apps(app_name, rebuild_sitemap=True):

View file

@ -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
@ -16,7 +16,7 @@ class StaticDataMiddleware(SharedDataMiddleware):
def get_directory_loader(self, directory):
def loader(path):
site = get_site_name(self.environ.get('HTTP_HOST'))
site = get_site_name(frappe.app._site or self.environ.get('HTTP_HOST'))
path = os.path.join(directory, site, 'public', 'files', cstr(path))
if os.path.isfile(path):
return os.path.basename(path), self._opener(path)

View file

@ -1,9 +1,11 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# MIT License. See license.txt
# model __init__.py
from __future__ import unicode_literals
import frappe
import json
no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'Button', 'Image']
default_fields = ['doctype','name','owner','creation','modified','modified_by','parent','parentfield','parenttype','idx','docstatus']
@ -18,10 +20,10 @@ def insert(doclist):
d["__islocal"] = 1
else:
d.set("__islocal", 1)
wrapper = frappe.get_doc(doclist)
wrapper.save()
return wrapper
def rename(doctype, old, new, debug=False):
@ -29,17 +31,17 @@ def rename(doctype, old, new, debug=False):
frappe.model.rename_doc.rename_doc(doctype, old, new, debug)
def copytables(srctype, src, srcfield, tartype, tar, tarfield, srcfields, tarfields=[]):
if not tarfields:
if not tarfields:
tarfields = srcfields
l = []
data = src.get(srcfield)
for d in data:
newrow = tar.append(tarfield)
newrow.idx = d.idx
for i in range(len(srcfields)):
newrow.set(tarfields[i], d.get(srcfields[i]))
l.append(newrow)
return l
@ -60,15 +62,15 @@ def delete_fields(args_dict, delete=0):
for dt in args_dict.keys():
fields = args_dict[dt]
if not fields: continue
frappe.db.sql("""\
DELETE FROM `tabDocField`
WHERE parent=%s AND fieldname IN (%s)
""" % ('%s', ", ".join(['"' + f + '"' for f in fields])), dt)
# Delete the data / column only if delete is specified
if not delete: continue
if frappe.db.get_value("DocType", dt, "issingle"):
frappe.db.sql("""\
DELETE FROM `tabSingles`
@ -84,13 +86,13 @@ def delete_fields(args_dict, delete=0):
def rename_field(doctype, old_fieldname, new_fieldname):
"""This functions assumes that doctype is already synced"""
meta = frappe.get_meta(doctype)
new_field = meta.get_field(new_fieldname)
if not new_field:
print "rename_field: " + (new_fieldname) + " not found in " + doctype
return
if new_field.fieldtype == "Table":
# change parentfield of table mentioned in options
frappe.db.sql("""update `tab%s` set parentfield=%s
@ -99,22 +101,82 @@ def rename_field(doctype, old_fieldname, new_fieldname):
elif new_field.fieldtype not in no_value_fields:
if meta.issingle:
frappe.db.sql("""update `tabSingles` set field=%s
where doctype=%s and field=%s""",
where doctype=%s and field=%s""",
(new_fieldname, doctype, old_fieldname))
else:
# copy field value
frappe.db.sql("""update `tab%s` set `%s`=`%s`""" % \
(doctype, new_fieldname, old_fieldname))
# update in property setter
frappe.db.sql("""update `tabProperty Setter` set field_name = %s
frappe.db.sql("""update `tabProperty Setter` set field_name = %s
where doc_type=%s and field_name=%s""", (new_fieldname, doctype, old_fieldname))
update_reports(doctype, old_fieldname, new_fieldname)
update_users_report_view_settings(doctype, old_fieldname)
def update_reports(doctype, old_fieldname, new_fieldname):
def _get_new_sort_by(report_dict, report, key):
sort_by = report_dict.get(key) or ""
if sort_by:
sort_by = sort_by.split(".")
if len(sort_by) > 1:
if sort_by[0]==doctype and sort_by[1]==old_fieldname:
sort_by = doctype + "." + new_fieldname
report_dict["updated"] = True
elif report.ref_doctype == doctype and sort_by[0]==old_fieldname:
sort_by = doctype + "." + new_fieldname
report_dict["updated"] = True
if isinstance(sort_by, list):
sort_by = '.'.join(sort_by)
return sort_by
reports = frappe.db.sql("""select name, ref_doctype, json from tabReport
where report_type = 'Report Builder' and ifnull(is_standard, 'No') = 'No'
and json like %s and json like %s""",
('%%%s%%' % old_fieldname , '%%%s%%' % doctype), as_dict=True)
for r in reports:
report_dict = json.loads(r.json)
# update filters
new_filters = []
for f in report_dict.get("filters"):
if f[0] == doctype and f[1] == old_fieldname:
new_filters.append([doctype, new_fieldname, f[2], f[3]])
report_dict["updated"] = True
else:
new_filters.append(f)
# update columns
new_columns = []
for c in report_dict.get("columns"):
if c[0] == old_fieldname and c[1] == doctype:
new_columns.append([new_fieldname, doctype])
report_dict["updated"] = True
else:
new_columns.append(c)
# update sort by
new_sort_by = _get_new_sort_by(report_dict, r, "sort_by")
new_sort_by_next = _get_new_sort_by(report_dict, r, "sort_by_next")
if report_dict.get("updated"):
new_val = json.dumps({
"filters": new_filters,
"columns": new_columns,
"sort_by": new_sort_by,
"sort_order": report_dict.get("sort_order"),
"sort_by_next": new_sort_by_next,
"sort_order_next": report_dict.get("sort_order_next")
})
frappe.db.sql("""update `tabReport` set `json`=%s where name=%s""", (new_val, r.name))
def update_users_report_view_settings(doctype, ref_fieldname):
import json
user_report_cols = frappe.db.sql("""select defkey, defvalue from `tabDefaultValue` where
user_report_cols = frappe.db.sql("""select defkey, defvalue from `tabDefaultValue` where
defkey like '_list_settings:%'""")
for key, value in user_report_cols:
new_columns = []
@ -124,5 +186,5 @@ def update_users_report_view_settings(doctype, ref_fieldname):
new_columns.append([field, field_doctype])
columns_modified=True
if columns_modified:
frappe.db.sql("""update `tabDefaultValue` set defvalue=%s
where defkey=%s""" % ('%s', '%s'), (json.dumps(new_columns), key))
frappe.db.sql("""update `tabDefaultValue` set defvalue=%s
where defkey=%s""" % ('%s', '%s'), (json.dumps(new_columns), key))

View file

@ -163,6 +163,9 @@ class BaseDocument(object):
if self.get("__islocal"):
doc["__islocal"] = 1
elif self.get("__onload"):
doc["__onload"] = self.get("__onload")
return doc
def as_json(self):
@ -189,6 +192,7 @@ class BaseDocument(object):
except Exception, e:
if e.args[0]==1062:
type, value, traceback = sys.exc_info()
frappe.msgprint(_("Duplicate name {0} {1}".format(self.doctype, self.name)))
raise frappe.NameError, (self.doctype, self.name, e), traceback
else:
raise
@ -259,9 +263,6 @@ class BaseDocument(object):
if not doctype:
frappe.throw(_("Options not set for link field {0}").format(df.fieldname))
elif doctype.lower().startswith("link:"):
doctype = doctype[5:]
docname = self.get(df.fieldname)
if docname and not frappe.db.get_value(doctype, docname):
invalid_links.append((df.fieldname, docname, get_msg(df, docname)))
@ -285,7 +286,7 @@ class BaseDocument(object):
current = frappe.db.get_value(self.doctype, self.name, "*", as_dict=True)
for key, value in current.iteritems():
df = self.meta.get_field(key)
if df and not df.allow_on_submit and self.get(key) != value:
if df and not df.allow_on_submit and (self.get(key) or value) and self.get(key) != value:
frappe.throw(_("Not allowed to change {0} after submission").format(df.label),
frappe.UpdateAfterSubmitError)

View file

@ -63,8 +63,7 @@ def get_new_doc(doctype, parent_doc = None, parentfield = None):
elif d.fieldtype == "Time":
doc.set(d.fieldname, nowtime())
elif (d.fieldtype == "Select" and d.options and not d.options.startswith("link:")
and d.options != "[Select]"):
elif (d.fieldtype == "Select" and d.options and d.options != "[Select]"):
doc.set(d.fieldname, d.options.split("\n")[0])
return doc

View file

@ -18,15 +18,17 @@ class DatabaseQuery(object):
self.ignore_permissions = False
self.fields = ["name"]
def execute(self, query=None, filters=None, fields=None, docstatus=None,
group_by=None, order_by=None, limit_start=0, limit_page_length=20,
as_list=False, with_childnames=False, debug=False, ignore_permissions=False):
def execute(self, query=None, filters=None, fields=None, or_filters=None,
docstatus=None, group_by=None, order_by=None, limit_start=0,
limit_page_length=20, as_list=False, with_childnames=False, debug=False,
ignore_permissions=False):
if not frappe.has_permission(self.doctype, "read"):
raise frappe.PermissionError
if fields:
self.fields = fields
self.filters = filters or []
self.or_filters = or_filters or []
self.docstatus = docstatus or []
self.group_by = group_by
self.order_by = order_by
@ -67,17 +69,19 @@ class DatabaseQuery(object):
# query dict
args.tables = ', '.join(self.tables)
if self.or_conditions:
self.conditions.append("({0})".format(" or ".join(self.or_conditions)))
args.conditions = ' and '.join(self.conditions)
args.fields = ', '.join(self.fields)
args.order_by = self.order_by or self.tables[0] + '.modified desc'
self.set_order_by(args)
args.group_by = self.group_by and (" group by " + self.group_by) or ""
self.check_sort_by_table(args.order_by)
return args
def parse_args(self):
if isinstance(self.filters, basestring):
self.filters = json.loads(self.filters)
@ -134,8 +138,10 @@ class DatabaseQuery(object):
def build_conditions(self):
self.conditions = []
self.or_conditions = []
self.add_docstatus_conditions()
self.build_filter_conditions()
self.build_filter_conditions(self.filters, self.conditions)
self.build_filter_conditions(self.or_filters, self.or_conditions)
# join parent, child tables
for tname in self.tables[1:]:
@ -153,11 +159,13 @@ class DatabaseQuery(object):
else:
self.conditions.append(self.tables[0] + '.docstatus < 2')
def build_filter_conditions(self):
def build_filter_conditions(self, filters, conditions):
"""build conditions from user filters"""
for f in self.filters:
if isinstance(filters, dict):
filters = [filters]
for f in filters:
if isinstance(f, basestring):
self.conditions.append(f)
conditions.append(f)
else:
f = self.get_filter_tuple(f)
@ -172,7 +180,7 @@ class DatabaseQuery(object):
opts = f[3].split(",")
opts = ["'" + t.strip().replace("'", "\\'") + "'" for t in opts]
f[3] = "(" + ', '.join(opts) + ")"
self.conditions.append('ifnull(' + tname + '.' + f[1] + ", '') " + f[2] + " " + f[3])
conditions.append('ifnull(' + tname + '.' + f[1] + ", '') " + f[2] + " " + f[3])
else:
df = frappe.get_meta(f[0]).get("fields", {"fieldname": f[1]})
@ -182,7 +190,7 @@ class DatabaseQuery(object):
else:
value, default_val = flt(f[3]), 0
self.conditions.append('ifnull({tname}.{fname}, {default_val}) {operator} {value}'.format(
conditions.append('ifnull({tname}.{fname}, {default_val}) {operator} {value}'.format(
tname=tname, fname=f[1], default_val=default_val, operator=f[2],
value=value))
@ -203,7 +211,7 @@ class DatabaseQuery(object):
"""add match conditions if applicable"""
self.match_filters = {}
self.match_conditions = []
self.or_conditions = []
self.match_or_conditions = []
if not self.tables: self.extract_tables()
@ -214,7 +222,7 @@ class DatabaseQuery(object):
restrictions = frappe.defaults.get_restrictions()
if restricted_by_user:
self.or_conditions.append('`tab{doctype}`.`owner`="{user}"'.format(doctype=self.doctype,
self.match_or_conditions.append('`tab{doctype}`.`owner`="{user}"'.format(doctype=self.doctype,
user=frappe.local.session.user))
self.match_filters["owner"] = frappe.session.user
@ -247,12 +255,12 @@ class DatabaseQuery(object):
if doctype_conditions:
conditions += ' and ' + doctype_conditions if conditions else doctype_conditions
if self.or_conditions:
if self.match_or_conditions:
if conditions:
conditions = '({conditions}) or {or_conditions}'.format(conditions=conditions,
or_conditions = ' or '.join(self.or_conditions))
or_conditions = ' or '.join(self.match_or_conditions))
else:
conditions = " or ".join(self.or_conditions)
conditions = " or ".join(self.match_or_conditions)
return conditions
@ -272,6 +280,14 @@ class DatabaseQuery(object):
query = query.replace('%(key)s', 'name')
return frappe.db.sql(query, as_dict = (not self.as_list))
def set_order_by(self, args):
meta = frappe.get_meta(self.doctype)
if self.order_by:
args.order_by = self.order_by
else:
args.order_by = "`tab{0}`.`{1}` {2}".format(self.doctype,
meta.sort_field or "modified", meta.sort_order or "desc")
def check_sort_by_table(self, order_by):
if "." in order_by:
tbl = order_by.split('.')[0]

View file

@ -175,27 +175,37 @@ class DbTable:
for col in self.columns.values():
col.check(self.current_columns.get(col.fieldname, None))
query = []
for col in self.add_column:
frappe.db.sql("alter table `%s` add column `%s` %s" % (self.name, col.fieldname, col.get_definition()))
query.append("add column `{}` {}".format(col.fieldname, col.get_definition()))
for col in self.change_type:
frappe.db.sql("alter table `%s` change `%s` `%s` %s" % (self.name, col.fieldname, col.fieldname, col.get_definition()))
query.append("change `{}` `{}` {}".format(col.fieldname, col.fieldname, col.get_definition()))
for col in self.add_index:
# if index key not exists
if not frappe.db.sql("show index from `%s` where key_name = %s" %
(self.name, '%s'), col.fieldname):
frappe.db.sql("alter table `%s` add index `%s`(`%s`)" % (self.name, col.fieldname, col.fieldname))
query.append("add index `{}`(`{}`)".format(col.fieldname, col.fieldname))
for col in self.drop_index:
if col.fieldname != 'name': # primary key
# if index key exists
if frappe.db.sql("show index from `%s` where key_name = %s" %
(self.name, '%s'), col.fieldname):
frappe.db.sql("alter table `%s` drop index `%s`" % (self.name, col.fieldname))
query.append("drop index `{}`".format(col.fieldname))
for col in self.set_default:
frappe.db.sql("alter table `%s` alter column `%s` set default %s" % (self.name, col.fieldname, '%s'), (col.default,))
for col in list(set(self.set_default).difference(set(self.change_type))):
if not col.default:
col_default = "null"
else:
col_default = '"{}"'.format(col.default.replace('"', '\\"'))
query.append('alter column `{}` set default {}'.format(col.fieldname, col_default))
if query:
frappe.db.sql("alter table `{}` {}".format(self.name, ", ".join(query)))
class DbColumn:
def __init__(self, table, fieldname, fieldtype, length, default, set_index, options):

View file

@ -8,6 +8,7 @@ from frappe.utils import flt, cint, cstr, now
from frappe.modules import load_doctype_module
from frappe.model.base_document import BaseDocument
from frappe.model.naming import set_new_name
from werkzeug.exceptions import NotFound, Forbidden
# once_only validation
# methods
@ -156,6 +157,8 @@ class Document(BaseDocument):
self.set_parent_in_children()
self.run_before_save_methods()
self._validate()
if self._action == "update_after_submit":
self.validate_update_after_submit()
# parent
if self.meta.issingle:
@ -283,7 +286,6 @@ class Document(BaseDocument):
elif docstatus==1:
if self.docstatus==1:
self._action = "update_after_submit"
self.validate_update_after_submit()
if not self.has_permission("submit"):
self.raise_no_permission_to("submit")
elif self.docstatus==2:
@ -387,6 +389,7 @@ class Document(BaseDocument):
elif self._action=="cancel":
self.run_method("before_cancel")
elif self._action=="update_after_submit":
self.run_method("validate")
self.run_method("before_update_after_submit")
def run_post_save_methods(self):
@ -400,6 +403,11 @@ class Document(BaseDocument):
elif self._action=="update_after_submit":
self.run_method("on_update_after_submit")
@staticmethod
def whitelist(f):
f.whitelisted = True
return f
@staticmethod
def hook(f):
def add_to_return_value(self, new_return_value):
@ -433,6 +441,13 @@ class Document(BaseDocument):
return composer
def is_whitelisted(self, method):
fn = getattr(self, method, None)
if not fn:
raise NotFound("Method {0} not found".format(method))
elif not getattr(fn, "whitelisted", False):
raise Forbidden("Method {0} not whitelisted".format(method))
def validate_value(self, fieldname, condition, val2, doc=None, raise_exception=None):
"""check that value of fieldname should be 'condition' val2
else throw exception"""

View file

@ -59,11 +59,7 @@ class Meta(Document):
raise
def get_link_fields(self):
tmp = self.get("fields", {"fieldtype":"Link", "options":["!=", "[Select]"]})
for df in self.get("fields", {"fieldtype":"Select", "options": "^link:"}):
tmp.append(frappe._dict({"fieldname":df.fieldname, "label":df.label,
"fieldtype":"Link", "options": df.options[5:]}))
return tmp
return self.get("fields", {"fieldtype": "Link", "options":["!=", "[Select]"]})
def get_table_fields(self):
if not hasattr(self, "_table_fields"):
@ -99,6 +95,21 @@ class Meta(Document):
def get_options(self, fieldname):
return self.get_field(fieldname).options
def get_search_fields(self):
search_fields = self.search_fields or "name"
search_fields = [d.strip() for d in search_fields.split(",")]
if "name" not in search_fields:
search_fields.append("name")
return search_fields
def get_list_fields(self):
list_fields = ["name"] + [d.fieldname \
for d in self.fields if (d.in_list_view and d.fieldtype in type_map)]
if self.title_field and self.title_field not in list_fields:
list_fields.append(self.title_field)
return list_fields
def process(self):
# don't process for special doctypes
# prevent's circular dependency
@ -203,7 +214,7 @@ doctype_table_fields = [
def is_single(doctype):
try:
return frappe.db.get_value("DocType", doctype, "issingle")
except IndexError, e:
except IndexError:
raise Exception, 'Cannot determine whether %s is single' % doctype
def get_parent_dt(dt):

View file

@ -7,13 +7,9 @@ import frappe
from frappe.utils import now_datetime, cint
def set_new_name(doc):
if getattr(doc, "_new_name_set", False):
# already set by doc
if doc.name:
return
doc._new_name_set = True
autoname = frappe.get_meta(doc.doctype).autoname
# amendments
if getattr(doc, "amended_from", None):
return _get_amended_name(doc)
@ -22,31 +18,35 @@ def set_new_name(doc):
if tmp and not isinstance(tmp, basestring):
# autoname in a function, not a property
doc.autoname()
if doc.name:
return
if doc.name:
return
autoname = frappe.get_meta(doc.doctype).autoname
# based on a field
if autoname and autoname.startswith('field:'):
n = doc.get(autoname[6:])
if not n:
raise Exception, 'Name is required'
doc.name = n.strip()
if autoname:
if autoname.startswith('field:'):
n = doc.get(autoname[6:])
if not n:
raise Exception, 'Name is required'
doc.name = n.strip()
elif autoname and autoname.startswith("naming_series:"):
if not doc.naming_series:
doc.naming_series = get_default_naming_series(doc.doctype)
elif autoname.startswith("naming_series:"):
if not doc.naming_series:
doc.naming_series = get_default_naming_series(doc.doctype)
if not doc.naming_series:
frappe.msgprint(frappe._("Naming Series mandatory"), raise_exception=True)
doc.name = make_autoname(doc.naming_series+'.#####')
if not doc.naming_series:
frappe.msgprint(frappe._("Naming Series mandatory"), raise_exception=True)
doc.name = make_autoname(doc.naming_series+'.#####')
# call the method!
elif autoname and autoname!='Prompt':
doc.name = make_autoname(autoname, doc.doctype)
# call the method!
elif autoname=='Prompt':
# set from __newname in save.py
if not doc.name:
frappe.throw(frappe._("Name not set via Prompt"))
# given
elif doc.get('__newname', None):
doc.name = doc.get('__newname')
else:
doc.name = make_autoname(autoname, doc.doctype)
# default name for table
elif doc.meta.istable:

View file

@ -11,7 +11,7 @@ from frappe.model.naming import validate_name
def rename_doc(doctype, old, new, force=False, merge=False, ignore_permissions=False):
"""
Renames a doc(dt, old) to doc(dt, new) and
updates all linked fields of type "Link" or "Select" with "link:"
updates all linked fields of type "Link"
"""
if not frappe.db.exists(doctype, old):
return
@ -138,10 +138,7 @@ def get_link_fields(doctype):
where dt.name = df.parent) as issingle
from tabDocField df
where
df.parent not like "old%%%%" and df.parent != '0' and
((df.options=%s and df.fieldtype='Link') or
(df.options='link:%s' and df.fieldtype='Select'))""" \
% ('%s', doctype), (doctype,), as_dict=1)
df.options=%s and df.fieldtype='Link'""", (doctype,), as_dict=1)
# get link fields from tabCustom Field
custom_link_fields = frappe.db.sql("""\
@ -150,10 +147,7 @@ def get_link_fields(doctype):
where dt.name = df.dt) as issingle
from `tabCustom Field` df
where
df.dt not like "old%%%%" and df.dt != '0' and
((df.options=%s and df.fieldtype='Link') or
(df.options='link:%s' and df.fieldtype='Select'))""" \
% ('%s', doctype), (doctype,), as_dict=1)
df.options=%s and df.fieldtype='Link'""", (doctype,), as_dict=1)
# add custom link fields list to link fields list
link_fields += custom_link_fields
@ -167,8 +161,7 @@ def get_link_fields(doctype):
where
ps.property_type='options' and
ps.field_name is not null and
(ps.value=%s or ps.value='link:%s')""" \
% ('%s', doctype), (doctype,), as_dict=1)
ps.value=%s""", (doctype,), as_dict=1)
link_fields += property_setter_link_fields
@ -199,10 +192,8 @@ def get_select_fields(old, new):
where dt.name = df.parent) as issingle
from tabDocField df
where
df.parent not like "old%%%%" and df.parent != '0' and
df.parent != %s and df.fieldtype = 'Select' and
df.options not like "link:%%%%" and
(df.options like "%%%%%s%%%%")""" \
df.options like "%%%%%s%%%%" """ \
% ('%s', old), (new,), as_dict=1)
# get link fields from tabCustom Field
@ -212,10 +203,8 @@ def get_select_fields(old, new):
where dt.name = df.dt) as issingle
from `tabCustom Field` df
where
df.dt not like "old%%%%" and df.dt != '0' and
df.dt != %s and df.fieldtype = 'Select' and
df.options not like "link:%%%%" and
(df.options like "%%%%%s%%%%")""" \
df.options like "%%%%%s%%%%" """ \
% ('%s', old), (new,), as_dict=1)
# add custom link fields list to link fields list
@ -231,8 +220,7 @@ def get_select_fields(old, new):
ps.doc_type != %s and
ps.property_type='options' and
ps.field_name is not null and
ps.value not like "link:%%%%" and
(ps.value like "%%%%%s%%%%")""" \
ps.value like "%%%%%s%%%%" """ \
% ('%s', old), (new,), as_dict=1)
select_fields += property_setter_select_fields
@ -243,16 +231,14 @@ def update_select_field_values(old, new):
frappe.db.sql("""\
update `tabDocField` set options=replace(options, %s, %s)
where
parent != %s and parent not like "old%%%%" and
fieldtype = 'Select' and options not like "link:%%%%" and
parent != %s and fieldtype = 'Select' and
(options like "%%%%\\n%s%%%%" or options like "%%%%%s\\n%%%%")""" % \
('%s', '%s', '%s', old, old), (old, new, new))
frappe.db.sql("""\
update `tabCustom Field` set options=replace(options, %s, %s)
where
dt != %s and dt not like "old%%%%" and
fieldtype = 'Select' and options not like "link:%%%%" and
dt != %s and fieldtype = 'Select' and
(options like "%%%%\\n%s%%%%" or options like "%%%%%s\\n%%%%")""" % \
('%s', '%s', '%s', old, old), (old, new, new))
@ -260,7 +246,7 @@ def update_select_field_values(old, new):
update `tabProperty Setter` set value=replace(value, %s, %s)
where
doc_type != %s and field_name is not null and
property='options' and value not like "link%%%%" and
property='options' and
(value like "%%%%\\n%s%%%%" or value like "%%%%%s\\n%%%%")""" % \
('%s', '%s', '%s', old, old), (old, new, new))

View file

@ -25,14 +25,14 @@ 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']
document_type = ['doctype', 'page', 'report', 'print_format']
for path, folders, files in os.walk(start_path):
# sort folders so that doctypes are synced before pages or reports
for dontwalk in (".git", "locale", "public"):
if dontwalk in folders:
if dontwalk in folders:
folders.remove(dontwalk)
folders.sort()
@ -47,10 +47,10 @@ def walk_and_sync(start_path, force=0, sync_everything = False, verbose=False):
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

View file

@ -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
"""
@ -8,12 +8,12 @@ from __future__ import unicode_literals
import frappe, os
import frappe.utils
lower_case_files_for = ['DocType', 'Page', 'Report',
"Workflow", 'Module Def', 'Desktop Item', 'Workflow State', 'Workflow Action']
lower_case_files_for = ['DocType', 'Page', 'Report',
"Workflow", 'Module Def', 'Desktop Item', 'Workflow State', 'Workflow Action', 'Print Format']
def scrub(txt):
return frappe.scrub(txt)
def scrub_dt_dn(dt, dn):
"""Returns in lowercase and code friendly names of doctype and name for certain types"""
ndt, ndn = dt, dn
@ -21,11 +21,11 @@ def scrub_dt_dn(dt, dn):
ndt, ndn = scrub(dt), scrub(dn)
return ndt, ndn
def get_module_path(module):
"""Returns path of the given module"""
return frappe.get_module_path(module)
def get_doc_path(module, doctype, name):
dt, dn = scrub_dt_dn(doctype, name)
return os.path.join(get_module_path(module), dt, dn)
@ -48,9 +48,9 @@ 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
return '{app}.{module}.doctype.{doctype}.{prefix}{doctype}'.format(\
app = scrub(frappe.local.module_app[scrub(module)]),
app = scrub(frappe.local.module_app[scrub(module)]),
module = scrub(module), doctype = scrub(doctype), prefix=prefix)

View file

@ -47,11 +47,11 @@ def import_file_by_path(path, force=False):
original_modified = doc.get("modified")
import_doc(doc)
import_doc(doc, force=force)
if original_modified:
# since there is a new timestamp on the file, update timestamp in
if doc["doctype"] == doc["name"]:
if doc["doctype"] == doc["name"] and doc["name"]!="DocType":
frappe.db.sql("""update tabSingles set value=%s where field="modified" and doctype=%s""",
(original_modified, doc["name"]))
else:
@ -78,7 +78,7 @@ ignore_values = {
ignore_doctypes = ["Page Role", "DocPerm"]
def import_doc(docdict):
def import_doc(docdict, force=False):
docdict["__islocal"] = 1
doc = frappe.get_doc(docdict)
@ -87,14 +87,14 @@ def import_doc(docdict):
if frappe.db.exists(doc.doctype, doc.name):
old_doc = frappe.get_doc(doc.doctype, doc.name)
if doc.doctype in ignore_values:
if doc.doctype in ignore_values and not force:
# update ignore values
for key in ignore_values.get(doc.doctype) or []:
doc.set(key, old_doc.get(key))
# update ignored docs into new doc
for df in doc.meta.get_table_fields():
if df.options in ignore_doctypes:
if df.options in ignore_doctypes and not force:
doc.set(df.fieldname, [])
ignore.append(df.options)

View file

@ -1,15 +1,15 @@
# 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
"""
Execute Patch Files
To run directly
python lib/wnf.py patch patch1, patch2 etc
python lib/wnf.py patch -f patch1, patch2 etc
where patch1, patch2 is module name
"""
import frappe, os
@ -19,21 +19,21 @@ class PatchError(Exception): pass
def run_all():
"""run all pending patches"""
executed = [p[0] for p in frappe.db.sql("""select patch from `tabPatch Log`""")]
for patch in get_all_patches():
if patch and (patch not in executed):
if not run_single(patchmodule = patch):
log(patch + ': failed: STOPPED')
raise PatchError(patch)
def get_all_patches():
patches = []
for app in frappe.get_installed_apps():
# 3-to-4 fix
if app=="webnotes":
if app=="webnotes":
app="frappe"
patches.extend(frappe.get_file_items(frappe.get_pymodule_path(app, "patches.txt")))
return patches
def reload_doc(args):
@ -42,22 +42,23 @@ def reload_doc(args):
def run_single(patchmodule=None, method=None, methodargs=None, force=False):
from frappe import conf
# don't write txt files
conf.developer_mode = 0
if force or method or not executed(patchmodule):
return execute_patch(patchmodule, method, methodargs)
else:
return True
def execute_patch(patchmodule, method=None, methodargs=None):
"""execute the patch"""
success = False
block_user(True)
frappe.db.begin()
try:
log('Executing %s in %s' % (patchmodule or str(methodargs), frappe.db.cur_db_name))
log('Executing {patch} in {site} ({db})'.format(patch=patchmodule or str(methodargs),
site=frappe.local.site, db=frappe.db.cur_db_name))
if patchmodule:
if patchmodule.startswith("execute:"):
exec patchmodule.split("execute:")[1] in globals()
@ -66,66 +67,43 @@ def execute_patch(patchmodule, method=None, methodargs=None):
update_patch_log(patchmodule)
elif method:
method(**methodargs)
frappe.db.commit()
success = True
except Exception, e:
frappe.db.rollback()
tb = frappe.get_traceback()
log(tb)
import os
if frappe.request:
add_to_patch_log(tb)
block_user(False)
if success:
log('Success')
return success
def add_to_patch_log(tb):
"""add error log to patches/patch.log"""
import conf, os
# TODO use get_site_base_path
with open(os.path.join(os.path.dirname(conf.__file__), 'app', 'patches','patch.log'),'a') as patchlog:
patchlog.write('\n\n' + tb)
def update_patch_log(patchmodule):
"""update patch_file in patch log"""
if frappe.db.table_exists("__PatchLog"):
frappe.db.sql("""INSERT INTO `__PatchLog` VALUES (%s, now())""", \
patchmodule)
else:
frappe.get_doc({"doctype": "Patch Log", "patch": patchmodule}).insert()
"""update patch_file in patch log"""
frappe.get_doc({"doctype": "Patch Log", "patch": patchmodule}).insert()
def executed(patchmodule):
"""return True if is executed"""
if frappe.db.table_exists("__PatchLog"):
done = frappe.db.sql("""select patch from __PatchLog where patch=%s""", patchmodule)
else:
done = frappe.db.get_value("Patch Log", {"patch": patchmodule})
if done:
print "Patch %s already executed in %s" % (patchmodule, frappe.db.cur_db_name)
done = frappe.db.get_value("Patch Log", {"patch": patchmodule})
# if done:
# print "Patch %s already executed in %s" % (patchmodule, frappe.db.cur_db_name)
return done
def block_user(block):
"""stop/start execution till patch is run"""
frappe.local.flags.in_patch = block
frappe.db.begin()
msg = "Patches are being executed in the system. Please try again in a few moments."
frappe.db.set_global('__session_status', block and 'stop' or None)
frappe.db.set_global('__session_status_message', block and msg or None)
frappe.db.commit()
def check_session_stopped():
if frappe.db.get_global("__session_status")=='stop':
frappe.msgprint(frappe.db.get_global("__session_status_message"))
raise frappe.SessionStopped('Session Stopped')
def setup():
frappe.db.sql("""CREATE TABLE IF NOT EXISTS `__PatchLog` (
patch TEXT, applied_on DATETIME) engine=InnoDB""")
def log(msg):
if getattr(frappe.local, "patch_log_list", None) is None:
frappe.local.patch_log_list = []
frappe.local.patch_log_list.append(msg)
print msg

View file

@ -1,5 +1,6 @@
execute:import inlinestyler # new requirement
execute:frappe.db.sql("""update `tabPatch Log` set patch=replace(patch, '.4_0.', '.v4_0.')""") #2014-05-12
execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2014-01-24
execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2014-03-01
execute:frappe.reload_doc('core', 'doctype', 'docperm') #2013-13-26
@ -8,21 +9,24 @@ execute:frappe.reload_doc('core', 'doctype', 'report') #2013-13-26
execute:frappe.reload_doc('core', 'doctype', 'version') #2014-02-21
execute:frappe.db.sql("alter table `tabSessions` modify `user` varchar(255), engine=InnoDB")
frappe.patches.4_0.remove_index_sitemap
frappe.patches.4_0.add_delete_permission
frappe.patches.4_0.move_match_to_restricted
frappe.patches.4_0.set_todo_checked_as_closed
frappe.patches.4_0.website_sitemap_hierarchy
frappe.patches.4_0.webnotes_to_frappe
frappe.patches.v4_0.remove_old_parent
frappe.patches.v4_0.remove_index_sitemap
frappe.patches.v4_0.add_delete_permission
frappe.patches.v4_0.move_match_to_restricted
frappe.patches.v4_0.set_todo_checked_as_closed
frappe.patches.v4_0.website_sitemap_hierarchy
frappe.patches.v4_0.webnotes_to_frappe
execute:frappe.reset_perms("Module Def")
frappe.patches.4_0.rename_sitemap_to_route
frappe.patches.4_0.rename_profile_to_user
frappe.patches.4_0.set_website_route_idx
frappe.patches.v4_0.rename_sitemap_to_route
frappe.patches.v4_0.rename_profile_to_user
frappe.patches.v4_0.set_website_route_idx
execute:import frappe.installer;frappe.installer.make_site_dirs() #2014-02-19
frappe.patches.4_0.private_backups
frappe.patches.4_0.set_module_in_report
frappe.patches.4_0.remove_old_parent
frappe.patches.4_0.update_datetime
frappe.patches.4_0.deprecate_control_panel
frappe.patches.4_0.file_manager_hooks
frappe.patches.v4_0.private_backups
frappe.patches.v4_0.set_module_in_report
frappe.patches.v4_0.update_datetime
frappe.patches.v4_0.deprecate_control_panel
frappe.patches.v4_0.file_manager_hooks
execute:frappe.get_doc("User", "Guest").save()
frappe.patches.v4_0.deprecate_link_selects
frappe.patches.v4_0.set_user_gravatar
frappe.patches.v4_0.update_custom_field_insert_after

View file

@ -0,0 +1,13 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
for name in frappe.db.sql_list("""select name from `tabCustom Field`
where fieldtype="Select" and options like "link:%" """):
custom_field = frappe.get_doc("Custom Field", name)
custom_field.fieldtype = "Link"
custom_field.options = custom_field.options[5:]
custom_field.save()

View file

@ -21,7 +21,16 @@ def execute():
b.file_url = os.path.normpath('/' + old_file_name)
else:
b.file_url = os.path.normpath('/files/' + old_file_name)
_file_name, content = get_file(name)
b.content_hash = get_content_hash(content)
b.save()
try:
_file_name, content = get_file(name)
b.content_hash = get_content_hash(content)
except IOError:
print 'Warning: Error processing ', name
_file_name = old_file_name
b.content_hash = None
try:
b.save()
except frappe.DuplicateEntryError:
frappe.delete_doc(b.doctype, b.name)

View file

@ -7,3 +7,4 @@ import frappe
def execute():
for doctype in frappe.db.sql_list("""select name from `tabDocType` where istable=1"""):
frappe.db.sql("""delete from `tab{0}` where parent like "old_par%:%" """.format(doctype))
frappe.db.sql("""delete from `tabDocField` where parent="0" """)

View file

@ -0,0 +1,11 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
for name in frappe.db.sql_list("select name from `tabUser` where ifnull(user_image, '')=''"):
user = frappe.get_doc("User", name)
user.update_gravatar()
user.db_set("user_image", user.user_image)

View file

@ -0,0 +1,18 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
for d in frappe.db.sql("""select name, dt, insert_after from `tabCustom Field`
where docstatus < 2""", as_dict=1):
dt_meta = frappe.get_meta(d.dt)
if not dt_meta.get_field(d.insert_after):
cf = frappe.get_doc("Custom Field", d.name)
df = dt_meta.get("fields", {"label": d.insert_after})
if df:
cf.insert_after = df[0].fieldname
else:
cf.insert_after = None
cf.save()

View file

@ -71,11 +71,12 @@ def has_unrestricted_access(doc, verbose=True):
restrictions = get_restrictions()
meta = frappe.get_meta(doc.get("doctype"))
user_perms = get_user_perms(meta)
if get_user_perms(meta).restricted:
if doc.owner == frappe.session.user:
# owner is always allowed for restricted permissions
return True
elif not restrictions:
elif not (restrictions and restrictions.get(doc.get("doctype"))):
return False
else:
if not restrictions:

View file

@ -106,6 +106,10 @@ div#freeze {
/*margin-top: -15px;*/
}
.form-control {
padding: 6px 8px;
}
.form-headline {
margin-bottom: 10px;
font-size: 120%;

View file

@ -1,5 +1,5 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
// MIT License. See license.txt
// assign to is lined to todo
// refresh - load todos
@ -15,9 +15,9 @@ frappe.ui.form.AssignTo = Class.extend({
this.wrapper = $('<div>\
<div class="alert-list" style="margin-bottom: 7px;"></div>\
</div>').appendTo(this.parent);
this.$list = this.wrapper.find(".alert-list");
this.parent.find(".btn").click(function() {
me.add();
});
@ -36,11 +36,11 @@ frappe.ui.form.AssignTo = Class.extend({
this.frm.get_docinfo().assignments = d;
this.$list.empty();
if(this.dialog) {
this.dialog.hide();
this.dialog.hide();
}
if(d && d.length) {
for(var i=0; i<d.length; i++) {
for(var i=0; i<d.length; i++) {
var info = frappe.user_info(d[i]);
info.owner = d[i];
info.avatar = frappe.avatar(d[i]);
@ -58,12 +58,12 @@ frappe.ui.form.AssignTo = Class.extend({
// set remove
this.$list.find('a.close').click(function() {
frappe.call({
method:'frappe.widgets.form.assign_to.remove',
method:'frappe.widgets.form.assign_to.remove',
args: {
doctype: me.frm.doctype,
name: me.frm.docname,
assign_to: $(this).attr('data-owner')
},
assign_to: $(this).attr('data-owner')
},
callback:function(r,rt) {
me.render(r.message);
me.frm.toolbar.show_infobar();
@ -81,16 +81,15 @@ frappe.ui.form.AssignTo = Class.extend({
if(!me.dialog) {
me.dialog = new frappe.ui.Dialog({
title: __('Add to To Do'),
width: 350,
fields: [
{fieldtype:'Link', fieldname:'assign_to', options:'User',
label:__("Assign To"),
{fieldtype:'Link', fieldname:'assign_to', options:'User',
label:__("Assign To"),
description:__("Add to To Do List of"), reqd:true},
{fieldtype:'Data', fieldname:'description', label:__("Comment")},
{fieldtype:'Date', fieldname:'date', label: __("Complete By")},
{fieldtype:'Data', fieldname:'description', label:__("Comment"), reqd:true},
{fieldtype:'Date', fieldname:'date', label: __("Complete By")},
{fieldtype:'Select', fieldname:'priority', label: __("Priority"),
options:'Low\nMedium\nHigh', 'default':'Medium'},
{fieldtype:'Check', fieldname:'notify',
{fieldtype:'Check', fieldname:'notify',
label:__("Notify By Email"), "default":1},
{fieldtype:'Check', fieldname:'restrict',
label:__("Add This To User's Restrictions")
@ -98,7 +97,7 @@ frappe.ui.form.AssignTo = Class.extend({
{fieldtype:'Button', label:__("Add"), fieldname:'add_btn'}
]
});
me.dialog.fields_dict.restrict.$wrapper
.find(".assign-user-properties")
.on("click", function() {
@ -108,14 +107,14 @@ frappe.ui.form.AssignTo = Class.extend({
};
frappe.set_route("user-properties");
});
me.dialog.fields_dict.add_btn.input.onclick = function() {
var assign_to = me.dialog.fields_dict.assign_to.get_value();
var args = me.dialog.get_values();
if(assign_to) {
return frappe.call({
method:'frappe.widgets.form.assign_to.add',
method:'frappe.widgets.form.assign_to.add',
args: $.extend(args, {
doctype: me.frm.doctype,
name: me.frm.docname,
@ -135,19 +134,19 @@ frappe.ui.form.AssignTo = Class.extend({
me.dialog.fields_dict.assign_to.get_query = "frappe.core.doctype.user.user.user_query";
}
me.dialog.clear();
(function toggle_restrict() {
var can_restrict = frappe.model.can_restrict(me.frm.doctype, me.frm);
me.dialog.fields_dict.restrict.$wrapper.toggle(can_restrict);
me.dialog.get_input("restrict").prop("checked", can_restrict);
})();
if(me.frm.meta.title_field) {
me.dialog.set_value("description", me.frm.doc[me.frm.meta.title_field])
}
me.dialog.show();
if(!frappe.perm.get_perm(me.frm.doctype)[0].restricted) {
me.dialog.fields_dict.restrict.set_input(0);
me.dialog.fields_dict.restrict.$wrapper.toggle(false);

View file

@ -103,14 +103,15 @@ frappe.ui.form.ControlImage = frappe.ui.form.Control.extend({
make: function() {
this._super();
var me = this;
this.$wrapper = $("<div></div>").appendTo(this.parent);
this.$body = $("<div>").appendTo(this.$wrapper)
.css({"margin-bottom": "10px", "margin-right": "15px", "float": "right",
"text-align": "right", "max-width": "100%"})
this.$wrapper = $("<div class='row'><div class='col-xs-4'></div></div>")
.appendTo(this.parent)
.css({"max-width": "600px", "margin": "0px"});
this.$body = $("<div class='col-xs-8'>").appendTo(this.$wrapper)
.css({"margin-bottom": "10px", "max-width": "100%"})
this.$wrapper.on("refresh", function() {
me.$body.empty();
if(me.df.options && me.frm.doc[me.df.options]) {
me.$img = $("<img src='"+me.frm.doc[me.df.options]+"' style='max-width: 70%;'>")
me.$img = $("<img src='"+me.frm.doc[me.df.options]+"' style='max-width: 100%;'>")
.appendTo(me.$body);
} else {
me.$buffer = $("<div class='missing-image'><i class='icon-camera'></i></div>")
@ -694,7 +695,7 @@ frappe.ui.form.ControlSelect = frappe.ui.form.ControlData.extend({
// set the default option (displayed)
var input_value = this.$input.val();
var model_value = frappe.model.get_value(this.doctype, this.docname, this.df.fieldname);
if(model_value == null && input_value != (model_value || "")) {
if(model_value == null && (input_value || "") != (model_value || "")) {
this.set_model_value(input_value);
} else {
this.last_value = value;
@ -784,16 +785,14 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
this.$input.on("focus", function() {
setTimeout(function() {
if(!me.$input.val()) {
me.$input.val("%").trigger("keydown");
me.$input.autocomplete("search", "");
}
}, 1000)
})
}, 500);
});
this.input = this.$input.get(0);
this.has_input = true;
//this.bind_change_event();
var me = this;
this.setup_buttons();
//this.setup_typeahead();
this.setup_autocomplete();
},
setup_buttons: function() {
@ -841,10 +840,19 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
if(value!==me.last_value) {
me.parse_validate_and_set_in_model(value);
}
}});
}
});
var cache = {};
this.$input.autocomplete({
minLength: 0,
source: function(request, response) {
if (cache[request.term]!=null) {
// from cache
response(cache[request.term]);
return;
}
var args = {
'txt': request.term,
'doctype': me.df.options,
@ -858,6 +866,7 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
no_spinner: true,
args: args,
callback: function(r) {
cache[request.term] = r.results;
response(r.results);
},
});
@ -879,17 +888,13 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
}
}
}).data('ui-autocomplete')._renderItem = function(ul, d) {
var html = "";
if(keys(d).length > 1) {
d.info = $.map(d, function(val, key) { return ["value", "label"].indexOf(key)!==-1 ? null : val }).join(", ") || "";
html = repl("<a>%(value)s<br><span class='text-muted'>%(info)s</span></a>", d);
} else {
html = "<a>" + d.value + "</a>";
var html = "<strong>" + d.value + "</strong>";
if(d.value!==d.description) {
html += '<br><span class="small">' + d.description + '</span>';
}
return $('<li></li>')
.data('item.autocomplete', d)
.append(html)
.html('<a><p>' + html + '</p></a>')
.appendTo(ul);
};
// remove accessibility span (for now)

View file

@ -109,13 +109,14 @@ frappe.ui.form.Grid = Class.extend({
var me =this;
$rows.sortable({
handle: ".data-row, .panel-heading",
helper: 'clone',
update: function(event, ui) {
me.frm.doc[me.df.parentfield] = [];
me.frm.doc[me.df.fieldname] = [];
$rows.find(".grid-row").each(function(i, item) {
var doc = $(item).data("doc");
doc.idx = i + 1;
$(this).find(".row-index").html(i + 1);
me.frm.doc[me.df.parentfield].push(doc);
me.frm.doc[me.df.fieldname].push(doc);
});
me.frm.dirty();
}

View file

@ -122,12 +122,20 @@ frappe.ui.form.Layout = Class.extend({
if(df && df.idx===1)
head.css({"margin-top": "0px"})
if(this.sections.length > 1)
this.section.css({
"margin-top": "15px",
"border-top": "1px solid #eee"
});
}
if(df.label || df.show_section_border) {
if(this.sections.length > 1) {
this.section.css("border-top", "1px solid #eee");
if (df.label) {
this.section.css("margin-top", "15px");
} else {
this.section.css("padding-top", "15px");
}
}
}
if(df.description) {
$('<div class="col-md-12 small text-muted">' + df.description + '</div>')
.css("padding-left", "40px")

View file

@ -26,7 +26,7 @@ frappe.ui.form.ScriptManager = Class.extend({
name = name || this.frm.docname;
handlers = this.get_handlers(event_name, doctype, name, callback);
if(callback) handlers.push(callback);
$.each(handlers, function(i, fn) {
fn();
})
@ -38,7 +38,7 @@ frappe.ui.form.ScriptManager = Class.extend({
$.each(frappe.ui.form.handlers[doctype][event_name], function(i, fn) {
handlers.push(function() { fn(me.frm, doctype, name) });
});
}
}
if(this.frm.cscript[event_name]) {
handlers.push(function() { me.frm.cscript[event_name](me.frm.doc, doctype, name); });
}
@ -49,6 +49,7 @@ frappe.ui.form.ScriptManager = Class.extend({
},
setup: function() {
var doctype = this.frm.meta;
var me = this;
// js
var cs = doctype.__js;
@ -56,6 +57,15 @@ frappe.ui.form.ScriptManager = Class.extend({
var tmp = eval(cs);
}
// setup add fetch
$.each(this.frm.fields, function(i, field) {
var df = field.df;
if(df.fieldtype==="Read Only" && df.options && df.options.indexOf(".")!=-1) {
var parts = df.options.split(".");
me.frm.add_fetch(parts[0], parts[1], df.fieldname);
}
});
// css
doctype.__css && frappe.dom.set_style(doctype.__css);
},
@ -72,25 +82,25 @@ frappe.ui.form.ScriptManager = Class.extend({
},
validate_link_and_fetch: function(df, docname, value, callback) {
var me = this;
if(value) {
var fetch = '';
if(this.frm && this.frm.fetch_dict[df.fieldname])
fetch = this.frm.fetch_dict[df.fieldname].columns.join(', ');
return frappe.call({
method:'frappe.widgets.form.utils.validate_link',
type: "GET",
args: {
'value': value,
'options': df.options,
'value': value,
'options': df.options,
'fetch': fetch
},
},
no_spinner: true,
callback: function(r) {
if(r.message=='Ok') {
if(r.fetch_values)
if(r.fetch_values)
me.set_fetch_values(df, docname, r.fetch_values);
if(callback) callback(value);
} else {
@ -111,9 +121,9 @@ frappe.ui.form.ScriptManager = Class.extend({
copy_from_first_row: function(parentfield, current_row, fieldnames) {
var doclist = this.frm.doc[parentfield];
if(doclist.length===1 || doclist[0]===current_row) return;
$.each(fieldnames, function(i, fieldname) {
current_row[fieldname] = doclist[0][fieldname];
});
}
});
});

View file

@ -207,7 +207,10 @@ frappe.ui.form.Toolbar = Class.extend({
current = this.appframe.get_title_right_text(),
status = null;
this.appframe.clear_primary_action();
if (!this.frm.doc.__unsaved) {
// don't clear actions menu if dirty
this.appframe.clear_primary_action();
}
if (this.can_submit()) {
status = "Submit";

View file

@ -8,17 +8,17 @@ frappe.tools.downloadify = function(data, roles, me) {
msgprint(__("Export not allowed. You need {0} role to export.", [frappe.utils.comma_or(roles)]));
return;
}
var _get_data = function() { return frappe.tools.to_csv(data); };
var flash_disabled = (navigator.mimeTypes["application/x-shockwave-flash"] == undefined);
var download_from_server = function() {
open_url_post("/", {
args: { data: data, filename: me.title },
cmd: "frappe.utils.datautils.send_csv_to_client"
}, true);
}
// save file > abt 200 kb using server call
if((_get_data().length > 200000) || flash_disabled) {
download_from_server();
@ -36,8 +36,8 @@ frappe.tools.downloadify = function(data, roles, me) {
return me.title + '.csv';
},
data: _get_data,
swf: 'lib/js/lib/downloadify/downloadify.swf',
downloadImage: 'lib/js/lib/downloadify/download.png',
swf: 'assets/frappe/js/lib/downloadify/downloadify.swf',
downloadImage: 'assets/frappe/js/lib/downloadify/download.png',
onComplete: function(){
$(msgobj.msg_area).html("<p>Saved</p>")
},
@ -46,7 +46,7 @@ frappe.tools.downloadify = function(data, roles, me) {
width: 100,
height: 30,
transparent: true,
append: false
append: false
});
}
};
@ -56,11 +56,11 @@ frappe.markdown = function(txt) {
frappe.require('assets/frappe/js/lib/markdown.js');
frappe.md2html = new Showdown.converter();
}
while(txt.substr(0,1)==="\n") {
txt = txt.substr(1);
}
// remove leading tab (if they exist in the first line)
var whitespace_len = 0,
first_line = txt.split("\n")[0];
@ -69,7 +69,7 @@ frappe.markdown = function(txt) {
whitespace_len++;
first_line = first_line.substr(1);
}
if(whitespace_len && whitespace_len != first_line.length) {
var txt1 = [];
$.each(txt.split("\n"), function(i, t) {
@ -77,7 +77,7 @@ frappe.markdown = function(txt) {
})
txt = txt1.join("\n");
}
return frappe.md2html.makeHtml(txt);
}
@ -109,9 +109,9 @@ frappe.slickgrid_tools = {
}
row.push(val);
});
if(!filter || filter(row, d)) {
res.push(row);
res.push(row);
}
}
return [col_row].concat(res);
@ -119,7 +119,7 @@ frappe.slickgrid_tools = {
add_property_setter_on_resize: function(grid) {
grid.onColumnsResized.subscribe(function(e, args) {
$.each(grid.getColumns(), function(i, col) {
if(col.docfield && col.previousWidth != col.width &&
if(col.docfield && col.previousWidth != col.width &&
!in_list(frappe.model.std_fields_list, col.docfield.fieldname) ) {
frappe.call({
method:"frappe.client.make_width_property_setter",
@ -131,7 +131,7 @@ frappe.slickgrid_tools = {
field_name: col.docfield.fieldname,
property: 'width',
value: col.width,
"__islocal": 1
"__islocal": 1
}]
}
});
@ -140,5 +140,5 @@ frappe.slickgrid_tools = {
}
});
});
}
}
};

View file

@ -66,7 +66,7 @@ $.extend(frappe.model, {
doc[f.fieldname] = v;
updated.push(f.fieldname);
} else if(f.fieldtype == "Select" && f.options
&& f.options.substr(0, 5)!="link:" && f.options!="[Select]") {
&& f.options!="[Select]") {
doc[f.fieldname] = f.options.split("\n")[0];
}
}
@ -166,6 +166,14 @@ $.extend(frappe.model, {
},
open_mapped_doc: function(opts) {
if (opts.frm && opts.frm.doc.__unsaved) {
frappe.throw(__("You have unsaved changes in this form. Please save before you continue."));
} else if (!opts.source_name && opts.frm) {
opts.source_name = opts.frm.doc.name;
}
return frappe.call({
type: "GET",
method: opts.method,

View file

@ -22,7 +22,7 @@ frappe.ui.Dialog = frappe.ui.FieldGroup.extend({
init: function(opts) {
this.display = false;
this.is_dialog = true;
if(!opts.width) opts.width = 600;
if(!opts.width) opts.width = "600px";
$.extend(this, opts);
this._super();

View file

@ -8,7 +8,7 @@
bsEditor = Class.extend({
init: function(options) {
this.options = $.extend(options || {}, this.default_options);
this.options = $.extend({}, this.default_options, options || {});
this.edit_mode = true;
if(this.options.editor) {
this.setup_editor(this.options.editor);
@ -100,7 +100,7 @@ bsEditor = Class.extend({
active_toolbar_class: 'btn-info',
selection_marker: 'edit-focus-marker',
selection_color: 'darkgrey',
remove_typography: true,
remove_typography: false,
max_file_size: 1,
},
@ -126,15 +126,18 @@ bsEditor = Class.extend({
var html = this.editor.html() || "";
if(!$.trim(this.editor.text()) && !(this.editor.find("img"))) html = "";
// html = html.replace(/(<br>|\s|<div><br><\/div>|&nbsp;)*$/, '');
// remove custom typography (use CSS!)
if(this.options.remove_typography) {
html = html.replace(/(font-family|font-size|line-height):[^;]*;/g, '');
html = html.replace(/<[^>]*(font=['"][^'"]*['"])>/g, function(a,b) { return a.replace(b, ''); });
html = html.replace(/\s*style\s*=\s*["']\s*["']/g, '');
return html;
var tmp = $("<div></div>").html(html);
// remove style attributes
tmp.find("*")
.removeAttr("style")
.removeAttr("font");
html = tmp.html();
}
return html;
},
init_file_drops: function () {

View file

@ -18,7 +18,7 @@ frappe.ui.FilterList = Class.extend({
me.listobj.run();
});
},
show_filters: function() {
this.$w.find('.show_filters').toggle();
if(!this.filters.length)
@ -29,7 +29,7 @@ frappe.ui.FilterList = Class.extend({
this.filters = [];
this.$w.find('.filter_area').empty();
},
add_filter: function(tablename, fieldname, condition, value) {
this.push_new_filter(tablename, fieldname, condition, value);
// list must be expanded
@ -37,7 +37,7 @@ frappe.ui.FilterList = Class.extend({
this.$w.find('.show_filters').toggle(true);
}
},
push_new_filter: function(tablename, fieldname, condition, value) {
this.filters.push(new frappe.ui.Filter({
flist: this,
@ -47,7 +47,7 @@ frappe.ui.FilterList = Class.extend({
value: value
}));
},
get_filters: function() {
// get filter values as dict
var values = [];
@ -57,7 +57,7 @@ frappe.ui.FilterList = Class.extend({
})
return values;
},
// remove hidden filters
update_filters: function() {
var fl = [];
@ -66,7 +66,7 @@ frappe.ui.FilterList = Class.extend({
})
this.filters = fl;
},
get_filter: function(fieldname) {
for(var i in this.filters) {
if(this.filters[i].field && this.filters[i].field.df.fieldname==fieldname)
@ -109,29 +109,30 @@ frappe.ui.Filter = Class.extend({
make_select: function() {
var me = this;
this.fieldselect = new frappe.ui.FieldSelect({
parent: this.$w.find('.fieldname_select_area'),
doctype: this.doctype,
filter_fields: this.filter_fields,
parent: this.$w.find('.fieldname_select_area'),
doctype: this.doctype,
filter_fields: this.filter_fields,
select: function(doctype, fieldname) {
me.set_field(doctype, fieldname);
}
});
this.fieldselect.set_value(this.doctype, this.fieldname);
},
set_events: function() {
var me = this;
this.$w.find('a.close').bind('click', function() {
this.$w.find('a.close').bind('click', function() {
me.$w.css('display','none');
var value = me.field.get_parsed_value();
var fieldname = me.field.df.fieldname;
me.field = null;
// hide filter section
if(!me.flist.get_filters().length) {
me.flist.$w.find('.set_filters').toggle(true);
me.flist.$w.find('.show_filters').toggle(false);
}
me.flist.update_filters();
me.flist.listobj.dirty = true;
me.flist.listobj.run();
@ -144,33 +145,33 @@ frappe.ui.Filter = Class.extend({
me.set_field(me.field.df.parent, me.field.df.fieldname, 'Data');
if(!me.field.desc_area)
me.field.desc_area = $a(me.field.wrapper, 'span', 'help', null,
'values separated by comma');
'values separated by comma');
} else {
me.set_field(me.field.df.parent, me.field.df.fieldname, null,
me.$w.find('.condition').val());
me.set_field(me.field.df.parent, me.field.df.fieldname, null,
me.$w.find('.condition').val());
}
});
// set the field
if(me.fieldname) {
// presents given (could be via tags!)
this.set_values(me.tablename, me.fieldname, me.condition, me.value);
} else {
me.set_field(me.doctype, 'name');
}
}
},
set_values: function(tablename, fieldname, condition, value) {
// presents given (could be via tags!)
this.set_field(tablename, fieldname);
if(condition) this.$w.find('.condition').val(condition).change();
if(value!=null) this.field.set_input(value);
},
set_field: function(doctype, fieldname, fieldtype, condition) {
var me = this;
// set in fieldname (again)
var cur = me.field ? {
fieldname: me.field.df.fieldname,
@ -185,13 +186,12 @@ frappe.ui.Filter = Class.extend({
return;
}
var df = copy_dict(me.fieldselect.fields_by_name[doctype][fieldname]);
this.set_fieldtype(df, fieldtype);
// called when condition is changed,
// called when condition is changed,
// don't change if all is well
if(me.field && cur.fieldname == fieldname && df.fieldtype == cur.fieldtype &&
if(me.field && cur.fieldname == fieldname && df.fieldtype == cur.fieldtype &&
df.parent == cur.parent) {
return;
}
@ -199,13 +199,13 @@ frappe.ui.Filter = Class.extend({
// clear field area and make field
me.fieldselect.selected_doctype = doctype;
me.fieldselect.selected_fieldname = fieldname;
// save old text
var old_text = null;
if(me.field) {
old_text = me.field.get_parsed_value();
}
var field_area = me.$w.find('.filter_field').empty().get(0);
var f = frappe.ui.form.make_control({
df: df,
@ -213,13 +213,13 @@ frappe.ui.Filter = Class.extend({
only_input: true,
})
f.refresh();
me.field = f;
if(old_text)
if(old_text && me.field.df.fieldtype===cur.fieldtype)
me.field.set_input(old_text);
if(!condition) this.set_default_condition(df, fieldtype);
// run on enter
$(me.field.wrapper).find(':input').keydown(function(ev) {
if(ev.which==13) {
@ -227,33 +227,33 @@ frappe.ui.Filter = Class.extend({
}
})
},
set_fieldtype: function(df, fieldtype) {
// reset
if(df.original_type)
df.fieldtype = df.original_type;
else
df.original_type = df.fieldtype;
df.description = ''; df.reqd = 0;
// given
if(fieldtype) {
df.fieldtype = fieldtype;
return;
}
}
// scrub
if(df.fieldtype=='Check') {
df.fieldtype='Select';
df.options='No\nYes';
} else if(['Text','Small Text','Text Editor','Code','Tags','Comments'].indexOf(df.fieldtype)!=-1) {
df.fieldtype = 'Data';
df.fieldtype = 'Data';
} else if(df.fieldtype=='Link' && this.$w.find('.condition').val()!="=") {
df.fieldtype = 'Data';
}
},
set_default_condition: function(df, fieldtype) {
if(!fieldtype) {
// set as "like" for data fields
@ -261,27 +261,27 @@ frappe.ui.Filter = Class.extend({
this.$w.find('.condition').val('like');
} else {
this.$w.find('.condition').val('=');
}
}
}
}
},
get_value: function() {
var me = this;
var val = me.field.get_parsed_value();
var cond = me.$w.find('.condition').val();
if(me.field.df.original_type == 'Check') {
val = (val=='Yes' ? 1 :0);
}
if(cond=='like') {
// add % only if not there at the end
if ((val.length === 0) || (val.lastIndexOf("%") !== (val.length - 1))) {
val = (val || "") + '%';
}
} else if(val === '%') val = null;
return [me.fieldselect.selected_doctype,
return [me.fieldselect.selected_doctype,
me.field.df.fieldname, me.$w.find('.condition').val(), val];
}
@ -311,10 +311,10 @@ frappe.ui.FieldSelect = Class.extend({
return false;
}
});
if(this.filter_fields) {
for(var i in this.filter_fields)
this.add_field_option(this.filter_fields[i])
this.add_field_option(this.filter_fields[i])
} else {
this.build_options();
}
@ -340,14 +340,14 @@ frappe.ui.FieldSelect = Class.extend({
var me = this;
this.clear();
if(!doctype) return;
// old style
if(doctype.indexOf(".")!==-1) {
parts = doctype.split(".");
doctype = parts[0];
fieldname = parts[1];
}
$.each(this.options, function(i, v) {
if(v.doctype===doctype && v.fieldname===fieldname) {
me.selected_doctype = doctype;
@ -365,7 +365,7 @@ frappe.ui.FieldSelect = Class.extend({
if(d.fieldname=="name") opts.options = me.doctype;
return $.extend(copy_dict(d), opts);
});
// add parenttype column
var doctype_obj = locals['DocType'][me.doctype];
if(doctype_obj && cint(doctype_obj.istable)) {
@ -376,7 +376,7 @@ frappe.ui.FieldSelect = Class.extend({
parent: me.doctype,
}]);
}
// blank
if(this.with_blank) {
this.options.push({
@ -414,7 +414,7 @@ frappe.ui.FieldSelect = Class.extend({
var label = df.label + ' (' + df.parent + ')';
var table = df.parent;
}
if(frappe.model.no_value_type.indexOf(df.fieldtype)==-1 &&
if(frappe.model.no_value_type.indexOf(df.fieldtype)==-1 &&
!(me.fields_by_name[df.parent] && me.fields_by_name[df.parent][df.fieldname])) {
this.options.push({
label: __(label),
@ -423,7 +423,7 @@ frappe.ui.FieldSelect = Class.extend({
doctype: df.parent
})
if(!me.fields_by_name[df.parent]) me.fields_by_name[df.parent] = {};
me.fields_by_name[df.parent][df.fieldname] = df;
me.fields_by_name[df.parent][df.fieldname] = df;
}
},
})

View file

@ -257,5 +257,8 @@ frappe.ui.toolbar.show_about = function() {
}
frappe.ui.toolbar.show_banner = function(msg) {
return $('<div class="toolbar-banner"></div>').html(msg).appendTo($('header'));
$banner = $('<div class="toolbar-banner">'+msg+'<a class="close">&times;</a></div>')
.appendTo($('header'));
$banner.find(".close").click(function() { $(".toolbar-banner").toggle(false); });
return $banner;
}

View file

@ -20,7 +20,7 @@ $.extend(frappe.report_dump, {
$.each(r.message, function(doctype, doctype_data) {
frappe.report_dump.set_data(doctype, doctype_data);
});
// reverse map names
$.each(r.message, function(doctype, doctype_data) {
// only if not pre-loaded
@ -38,7 +38,7 @@ $.extend(frappe.report_dump, {
}
}
});
callback();
},
progress_bar: progress_bar
@ -62,7 +62,7 @@ $.extend(frappe.report_dump, {
var row = make_row(d);
replace_dict[row.name] = row;
});
// replace old data
$.each(frappe.report_dump.data[doctype], function(i, d) {
if(replace_dict[d.name]) {
@ -75,13 +75,13 @@ $.extend(frappe.report_dump, {
data.push(d);
}
});
// add new records
$.each(replace_dict, function(name, d) {
data.push(d);
})
} else {
// first loading
$.each(doctype_data.data, function(i, d) {
data.push(make_row(d));
@ -96,20 +96,20 @@ frappe.provide("frappe.views");
frappe.views.GridReport = Class.extend({
init: function(opts) {
frappe.require("assets/js/slickgrid.min.js");
this.filter_inputs = {};
this.preset_checks = [];
this.tree_grid = {show: false};
var me = this;
$.extend(this, opts);
this.wrapper = $('<div>').appendTo(this.parent);
if(this.filters) {
this.make_filters();
}
this.make_waiting();
this.get_data_and_refresh();
},
bind_show: function() {
@ -118,14 +118,14 @@ frappe.views.GridReport = Class.extend({
// this must be called after init
// because "frappe.container.page" will only be set
// once "load" event is over.
var me = this;
$(this.page).bind('show', function() {
// reapply filters on show
frappe.cur_grid_report = me;
me.get_data_and_refresh();
});
},
get_data_and_refresh: function() {
var me = this;
@ -139,7 +139,7 @@ frappe.views.GridReport = Class.extend({
var progress_bar = null;
if(!this.setup_filters_done)
progress_bar = this.wrapper.find(".progress .progress-bar");
frappe.report_dump.with_data(this.doctypes, function() {
if(!me.setup_filters_done) {
me.setup_filters();
@ -160,28 +160,28 @@ frappe.views.GridReport = Class.extend({
function(d) { return d.name; });
me.set_autocomplete(v, opts.list);
}
});
});
// refresh
this.filter_inputs.refresh && this.filter_inputs.refresh.click(function() {
this.filter_inputs.refresh && this.filter_inputs.refresh.click(function() {
me.get_data(function() {
me.refresh();
});
});
// reset filters
this.filter_inputs.reset_filters && this.filter_inputs.reset_filters.click(function() {
me.init_filter_values();
this.filter_inputs.reset_filters && this.filter_inputs.reset_filters.click(function() {
me.init_filter_values();
me.refresh();
});
// range
this.filter_inputs.range && this.filter_inputs.range.on("change", function() {
me.refresh();
});
// plot check
if(this.setup_plot_check)
if(this.setup_plot_check)
this.setup_plot_check();
},
set_filter: function(key, value) {
@ -223,7 +223,7 @@ frappe.views.GridReport = Class.extend({
filter.val("");
}
});
this.set_default_values();
},
@ -232,7 +232,7 @@ frappe.views.GridReport = Class.extend({
from_date: dateutil.str_to_user(sys_defaults.year_start_date),
to_date: dateutil.str_to_user(sys_defaults.year_end_date)
}
var me = this;
$.each(values, function(i, v) {
if(me.filter_inputs[i] && !me.filter_inputs[i].val())
@ -281,7 +281,7 @@ frappe.views.GridReport = Class.extend({
});
},
make_waiting: function() {
this.waiting = frappe.messages.waiting(this.wrapper, __("Loading Report")+"...", '10');
this.waiting = frappe.messages.waiting(this.wrapper, __("Loading Report")+"...", '10');
},
load_filter_values: function() {
var me = this;
@ -298,12 +298,12 @@ frappe.views.GridReport = Class.extend({
}
}
});
if(this.filter_inputs.from_date && this.filter_inputs.to_date && (this.to_date < this.from_date)) {
msgprint(__("From Date must be before To Date"));
return;
}
},
make_name_map: function(data, key) {
@ -314,7 +314,7 @@ frappe.views.GridReport = Class.extend({
})
return map;
},
reset_item_values: function(item) {
var me = this;
$.each(this.columns, function(i, col) {
@ -323,7 +323,7 @@ frappe.views.GridReport = Class.extend({
}
});
},
round_item_values: function(item) {
var me = this;
$.each(this.columns, function(i, col) {
@ -332,17 +332,17 @@ frappe.views.GridReport = Class.extend({
}
});
},
round_off_data: function() {
var me = this;
$.each(this.data, function(i, d) {
me.round_item_values(d);
});
},
refresh: function() {
this.waiting.toggle(false);
if(!this.grid_wrapper)
if(!this.grid_wrapper)
this.make();
this.show_zero = $('.show-zero input:checked').length;
this.load_filter_values();
@ -361,15 +361,15 @@ frappe.views.GridReport = Class.extend({
setup_dataview_columns: function() {
this.dataview_columns = $.map(this.columns, function(col) {
return !col.hidden ? col : null;
});
});
},
make: function() {
var me = this;
// plot wrapper
this.plot_area = $('<div class="plot" style="margin-bottom: 15px; display: none; \
height: 300px; width: 100%;"></div>').appendTo(this.wrapper);
// print / export
$('<div style="text-align: right;"> \
<div class="processing" style="background-color: #fec; display: none; \
@ -377,9 +377,9 @@ frappe.views.GridReport = Class.extend({
<a href="#" class="grid-report-export"> \
<i class="icon icon-download-alt"></i> Export</a> \
</div>').appendTo(this.wrapper);
this.wrapper.find(".grid-report-export").click(function() { return me.export(); });
// grid wrapper
this.grid_wrapper = $("<div style='height: 500px; border: 1px solid #aaa; \
background-color: #eee; margin-top: 15px;'>")
@ -392,10 +392,10 @@ frappe.views.GridReport = Class.extend({
+'</div>').appendTo(this.wrapper);
this.bind_show();
frappe.cur_grid_report = this;
$(this.wrapper).trigger('make');
},
apply_filters_from_route: function() {
var me = this;
@ -432,12 +432,12 @@ frappe.views.GridReport = Class.extend({
me.grid.invalidateRows(args.rows);
me.grid.render();
});
this.dataView.onRowCountChanged.subscribe(function (e, args) {
me.grid.updateRowCount();
me.grid.render();
});
this.tree_grid.show && this.add_tree_grid_events();
},
prepare_data_view: function() {
@ -459,13 +459,13 @@ frappe.views.GridReport = Class.extend({
// from all filter_inputs
var filters = this.filter_inputs;
if(item._show) return true;
for (i in filters) {
if(!this.apply_filter(item, i)) {
return false;
}
}
return true;
},
apply_filter: function(item, fieldname) {
@ -485,11 +485,11 @@ frappe.views.GridReport = Class.extend({
if(col.formatter==me.currency_formatter && !col.hidden) {
if(flt(item[col.field]) > 0.001 || flt(item[col.field]) < -0.001) {
return true;
}
}
}
}
return false;
}
}
return true;
},
show_zero_check: function() {
@ -497,7 +497,7 @@ frappe.views.GridReport = Class.extend({
this.wrapper.bind('make', function() {
me.wrapper.find('.show-zero').toggle(true).find('input').click(function(){
me.refresh();
});
});
});
},
is_default: function(fieldname) {
@ -535,16 +535,16 @@ frappe.views.GridReport = Class.extend({
// link_formatter must have
// filter_input, open_btn (true / false), doctype (will be eval'd)
if(!value) return "";
var me = frappe.cur_grid_report;
if(dataContext._show) {
return repl('<span style="%(_style)s">%(value)s</span>', {
_style: dataContext._style || "",
value: value
});
}
// make link to add a filter
var link_formatter = me.dataview_columns[cell].link_formatter;
if (link_formatter.filter_input) {
@ -562,8 +562,8 @@ frappe.views.GridReport = Class.extend({
// make icon to open form
if(link_formatter.open_btn) {
var doctype = link_formatter.doctype
? eval(link_formatter.doctype)
var doctype = link_formatter.doctype
? eval(link_formatter.doctype)
: dataContext.doctype;
html += me.get_link_open_icon(doctype, value);
}
@ -576,21 +576,21 @@ frappe.views.GridReport = Class.extend({
return repl(' <a href="#Form/%(doctype)s/%(name)s">\
<i class="icon icon-share" style="cursor: pointer;"></i></a>', {
doctype: doctype,
name: encodeURIComponent(name)
name: encodeURIComponent(name)
});
},
make_date_range_columns: function() {
this.columns = [];
var me = this;
var range = this.filter_inputs.range.val();
this.from_date = dateutil.user_to_str(this.filter_inputs.from_date.val());
this.to_date = dateutil.user_to_str(this.filter_inputs.to_date.val());
var date_diff = dateutil.get_diff(this.to_date, this.from_date);
me.column_map = {};
me.last_date = null;
var add_column = function(date) {
me.columns.push({
id: date,
@ -600,22 +600,22 @@ frappe.views.GridReport = Class.extend({
width: 100
});
}
var build_columns = function(condition) {
// add column for each date range
for(var i=0; i <= date_diff; i++) {
var date = dateutil.add_days(me.from_date, i);
if(!condition) condition = function() { return true; }
if(condition(date)) add_column(date);
me.last_date = date;
if(me.columns.length) {
me.column_map[date] = me.columns[me.columns.length-1];
}
}
}
// make columns for all date ranges
if(range=='Daily') {
build_columns();
@ -623,7 +623,7 @@ frappe.views.GridReport = Class.extend({
build_columns(function(date) {
if(!me.last_date) return true;
return !(dateutil.get_diff(date, me.from_date) % 7)
});
});
} else if(range=='Monthly') {
build_columns(function(date) {
if(!me.last_date) return true;
@ -633,7 +633,7 @@ frappe.views.GridReport = Class.extend({
build_columns(function(date) {
if(!me.last_date) return true;
return dateutil.str_to_obj(date).getDate()==1 && in_list([0,3,6,9], dateutil.str_to_obj(date).getMonth())
});
});
} else if(range=='Yearly') {
build_columns(function(date) {
if(!me.last_date) return true;
@ -641,15 +641,15 @@ frappe.views.GridReport = Class.extend({
return date==v.year_start_date ? true : null;
}).length;
});
}
// set label as last date of period
$.each(this.columns, function(i, col) {
col.name = me.columns[i+1]
? dateutil.str_to_user(dateutil.add_days(me.columns[i+1].id, -1))
: dateutil.str_to_user(me.to_date);
});
: dateutil.str_to_user(me.to_date);
});
},
trigger_refresh_on_change: function(filters) {
var me = this;
@ -670,10 +670,10 @@ frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({
}
frappe.require('assets/frappe/js/lib/flot/jquery.flot.js');
frappe.require('assets/frappe/js/lib/flot/jquery.flot.downsample.js');
this.plot = $.plot(this.plot_area.toggle(true), plot_data,
this.get_plot_options());
this.setup_plot_hover();
},
setup_plot_check: function() {
@ -692,7 +692,7 @@ frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({
});
}
me.render_plot();
});
});
});
},
setup_plot_hover: function() {
@ -718,16 +718,16 @@ frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({
me.previousPoint = item.dataIndex;
$("#" + me.tooltip_id).remove();
showTooltip(item.pageX, item.pageY,
showTooltip(item.pageX, item.pageY,
me.get_tooltip_text(item.series.label, item.datapoint[0], item.datapoint[1]));
}
}
else {
$("#" + me.tooltip_id).remove();
me.previousPoint = null;
me.previousPoint = null;
}
});
},
get_tooltip_text: function(label, x, y) {
var date = dateutil.obj_to_user(new Date(x));
@ -749,19 +749,19 @@ frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({
points: {show: true},
lines: {show: true, fill: true},
});
// prepend opening
data[data.length-1].data = [[dateutil.str_to_obj(me.from_date).getTime(),
// prepend opening
data[data.length-1].data = [[dateutil.str_to_obj(me.from_date).getTime(),
item.opening]].concat(data[data.length-1].data);
}
});
return data.length ? data : false;
},
get_plot_options: function() {
return {
grid: { hoverable: true, clickable: true },
xaxis: { mode: "time",
xaxis: { mode: "time",
min: dateutil.str_to_obj(this.from_date).getTime(),
max: dateutil.str_to_obj(this.to_date).getTime() },
series: { downsample: { threshold: 1000 } }
@ -780,7 +780,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({
});
if (!this.tl) this.tl = {};
if (!this.tl[parent_doctype]) this.tl[parent_doctype] = [];
$.each(frappe.report_dump.data[parent_doctype], function(i, parent) {
if(tmap[parent.name]) {
$.each(tmap[parent.name], function(i, d) {
@ -811,15 +811,15 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({
var me = frappe.cur_grid_report;
value = value.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
var data = me.data;
var spacer = "<span style='display:inline-block;height:1px;width:" +
var spacer = "<span style='display:inline-block;height:1px;width:" +
(15 * dataContext["indent"]) + "px'></span>";
var idx = me.dataView.getIdxById(dataContext.id);
var link = me.tree_grid.formatter(dataContext);
if(dataContext.doctype) {
link += me.get_link_open_icon(dataContext.doctype, dataContext.name);
link += me.get_link_open_icon(dataContext.doctype, dataContext.name);
}
if (data[idx + 1] && data[idx + 1].indent > data[idx].indent) {
if (dataContext._collapsed) {
return spacer + " <span class='toggle expand'></span>&nbsp;" + link;
@ -833,7 +833,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({
tree_dataview_filter: function(item) {
var me = frappe.cur_grid_report;
if(!me.apply_filters(item)) return false;
var parent = item[me.tree_grid.parent_field];
while (parent) {
if (me.item_by_name[parent]._collapsed) {
@ -846,7 +846,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({
prepare_tree: function(item_dt, group_dt) {
var group_data = frappe.report_dump.data[group_dt];
var item_data = frappe.report_dump.data[item_dt];
// prepare map with child in respective group
var me = this;
var item_group_map = {};
@ -855,12 +855,12 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({
var parent = item[me.tree_grid.parent_field];
if(!item_group_map[parent]) item_group_map[parent] = [];
if(group_ids.indexOf(item.name)==-1) {
item_group_map[parent].push(item);
item_group_map[parent].push(item);
} else {
msgprint(__("Ignoring Item {0}, because a group exists with the same name!", [item.name.bold()]));
}
});
// arrange items besides their parent item groups
var items = [];
$.each(group_data, function(i, group){
@ -884,7 +884,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({
d.indent = indent;
});
},
export: function() {
var msgbox = msgprint($.format('<p>{0}</p>\
<p><input type="checkbox" name="with_groups" checked="checked"> {1}</p>\
@ -902,7 +902,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({
var with_groups = $(msgbox.body).find("[name='with_groups']").prop("checked");
var with_ledgers = $(msgbox.body).find("[name='with_ledgers']").prop("checked");
var data = frappe.slickgrid_tools.get_view_data(me.columns, me.dataView,
var data = frappe.slickgrid_tools.get_view_data(me.columns, me.dataView,
function(row, item) {
if(with_groups) {
// add row
@ -914,15 +914,15 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({
if(with_ledgers && (item.group_or_ledger != "Group" && !item.is_group)) {
return true;
}
return false;
});
frappe.tools.downloadify(data, ["Report Manager", "System Manager"], me);
return false;
})
return false;
},
});

View file

@ -216,15 +216,17 @@ frappe.views.moduleview.ModuleView = Class.extend({
});
} else {
var route = item.route;
if(item.type==="doctype") {
route = "List/" + encodeURIComponent(item.name);
} else if(item.type==="page") {
route = item.route || item.link || item.name;
} else if(item.type==="report") {
if(item.is_query_report) {
route = "query-report/" + encodeURIComponent(item.name);
} else {
route = "Report/" + encodeURIComponent(item.doctype) + "/" + encodeURIComponent(item.name);
if(!route) {
if(item.type==="doctype") {
route = "List/" + encodeURIComponent(item.name);
} else if(item.type==="page") {
route = item.route || item.link || item.name;
} else if(item.type==="report") {
if(item.is_query_report) {
route = "query-report/" + encodeURIComponent(item.name);
} else {
route = "Report/" + encodeURIComponent(item.doctype) + "/" + encodeURIComponent(item.name);
}
}
}

View file

@ -117,7 +117,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
if(!columns) {
var columns = [['name', this.doctype],];
$.each(frappe.meta.docfield_list[this.doctype], function(i, df) {
if(df.in_filter && df.fieldname!='naming_series'
if((df.in_filter || df.in_list_view) && df.fieldname!='naming_series'
&& !in_list(frappe.model.no_value_type, df.fieldname)) {
columns.push([df.fieldname, df.parent]);
}

View file

@ -719,6 +719,11 @@ _f.Frm.prototype.disable_save = function() {
this.appframe.set_title_right("", null);
}
_f.Frm.prototype.enable_save = function() {
this.save_disabled = false;
this.toolbar.set_title_right();
}
_f.Frm.prototype.save_or_update = function() {
if(this.save_disabled) return;

View file

@ -12,56 +12,56 @@
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible {
.ui-helper-hidden-accessible {
border: 0; clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.ui-helper-reset {
margin: 0;
padding: 0;
border: 0;
outline: 0;
line-height: 1.3;
text-decoration: none;
font-size: 100%;
list-style: none;
.ui-helper-reset {
margin: 0;
padding: 0;
border: 0;
outline: 0;
line-height: 1.3;
text-decoration: none;
font-size: 100%;
list-style: none;
}
.ui-helper-clearfix:before,
.ui-helper-clearfix:after {
content: "";
display: table;
}
.ui-helper-clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
.ui-helper-clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.ui-helper-clearfix {
.ui-helper-clearfix {
/*display: inline-block; */
display:block;
min-height: 0; /* support: IE7 */
}
/* required comment for clearfix to work in Opera \*/
* html .ui-helper-clearfix {
height:1%;
* html .ui-helper-clearfix {
height:1%;
}
/* end clearfix */
.ui-helper-zfix {
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
opacity: 0;
filter:Alpha(Opacity=0);
.ui-helper-zfix {
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
opacity: 0;
filter:Alpha(Opacity=0);
}
.ui-front {
z-index: 100;
@ -77,11 +77,11 @@
----------------------------------*/
/* states and images */
.ui-icon {
display: block;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
.ui-icon {
display: block;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
}
@ -89,12 +89,12 @@
----------------------------------*/
/* Overlays */
.ui-widget-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
.ui-widget-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
/*
@ -107,72 +107,72 @@
* http://api.jqueryui.com/resizable/
*/
.ui-resizable {
.ui-resizable {
position: relative;
}
.ui-resizable-handle {
.ui-resizable-handle {
position: absolute;
font-size: 0.1px;
z-index: 99999;
display: block;
z-index: 99999;
display: block;
}
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle {
display: none;
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle {
display: none;
}
.ui-resizable-n {
cursor: n-resize;
height: 7px;
width: 100%;
top: -5px;
left: 0;
.ui-resizable-n {
cursor: n-resize;
height: 7px;
width: 100%;
top: -5px;
left: 0;
}
.ui-resizable-s {
cursor: s-resize;
height: 7px;
width: 100%;
bottom: -5px;
left: 0;
.ui-resizable-s {
cursor: s-resize;
height: 7px;
width: 100%;
bottom: -5px;
left: 0;
}
.ui-resizable-e {
cursor: e-resize;
width: 7px;
right: -5px;
top: 0;
height: 100%;
.ui-resizable-e {
cursor: e-resize;
width: 7px;
right: -5px;
top: 0;
height: 100%;
}
.ui-resizable-w {
cursor: w-resize;
width: 7px;
left: -5px;
top: 0;
height: 100%;
.ui-resizable-w {
cursor: w-resize;
width: 7px;
left: -5px;
top: 0;
height: 100%;
}
.ui-resizable-se {
cursor: se-resize;
width: 12px;
height: 12px;
right: 1px;
bottom: 1px;
.ui-resizable-se {
cursor: se-resize;
width: 12px;
height: 12px;
right: 1px;
bottom: 1px;
}
.ui-resizable-sw {
cursor: sw-resize;
width: 9px;
height: 9px;
left: -5px;
bottom: -5px;
.ui-resizable-sw {
cursor: sw-resize;
width: 9px;
height: 9px;
left: -5px;
bottom: -5px;
}
.ui-resizable-nw {
cursor: nw-resize;
width: 9px;
height: 9px;
left: -5px;
top: -5px;
.ui-resizable-nw {
cursor: nw-resize;
width: 9px;
height: 9px;
left: -5px;
top: -5px;
}
.ui-resizable-ne {
cursor: ne-resize;
width: 9px;
height: 9px;
right: -5px;
.ui-resizable-ne {
cursor: ne-resize;
width: 9px;
height: 9px;
right: -5px;
top: -5px;
}
@ -185,10 +185,10 @@
*
* http://jqueryui.com/selectable/
*/
.ui-selectable-helper {
position: absolute;
z-index: 100;
border:1px dotted black;
.ui-selectable-helper {
position: absolute;
z-index: 100;
border:1px dotted black;
}
/*
@ -198,17 +198,18 @@
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
*
*
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/
*/
/* Component containers
----------------------------------*/
.ui-widget { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size:13px; }
/*.ui-widget { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size:13px; }*/
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(../frappe/js/lib/jquery/bootstrap_theme/images/ui-bg_glass_75_ffffff_1x400.png) 50% 50% repeat-x; color: #404040; }
/*.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 1em; }*/
/*.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(../frappe/js/lib/jquery/bootstrap_theme/images/ui-bg_glass_75_ffffff_1x400.png) 50% 50% repeat-x; color: #404040; }*/
.ui-widget-content { border: 1px solid #aaaaaa; background-color: #ffffff; color: #404040;}
.ui-widget-content a { color: #404040; }
.ui-widget-header {
font-weight:bold;
@ -514,41 +515,41 @@
----------------------------------*/
/* Corner radius */
.ui-corner-all,
.ui-corner-top,
.ui-corner-left,
.ui-corner-tl {
-moz-border-radius-topleft: 4px;
-webkit-border-top-left-radius: 4px;
-khtml-border-top-left-radius: 4px;
border-top-left-radius: 4px;
.ui-corner-all,
.ui-corner-top,
.ui-corner-left,
.ui-corner-tl {
-moz-border-radius-topleft: 4px;
-webkit-border-top-left-radius: 4px;
-khtml-border-top-left-radius: 4px;
border-top-left-radius: 4px;
}
.ui-corner-all,
.ui-corner-top,
.ui-corner-right,
.ui-corner-tr {
-moz-border-radius-topright: 4px;
-webkit-border-top-right-radius: 4px;
-khtml-border-top-right-radius: 4px;
border-top-right-radius: 4px;
.ui-corner-all,
.ui-corner-top,
.ui-corner-right,
.ui-corner-tr {
-moz-border-radius-topright: 4px;
-webkit-border-top-right-radius: 4px;
-khtml-border-top-right-radius: 4px;
border-top-right-radius: 4px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-left,
.ui-corner-bl {
-moz-border-radius-bottomleft: 4px;
-webkit-border-bottom-left-radius: 4px;
-khtml-border-bottom-left-radius: 4px;
border-bottom-left-radius: 4px;
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-left,
.ui-corner-bl {
-moz-border-radius-bottomleft: 4px;
-webkit-border-bottom-left-radius: 4px;
-khtml-border-bottom-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-right,
.ui-corner-br {
-moz-border-radius-bottomright: 4px;
-webkit-border-bottom-right-radius: 4px;
-khtml-border-bottom-right-radius: 4px;
border-bottom-right-radius: 4px;
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-right,
.ui-corner-br {
-moz-border-radius-bottomright: 4px;
-webkit-border-bottom-right-radius: 4px;
-khtml-border-bottom-right-radius: 4px;
border-bottom-right-radius: 4px;
}
@ -562,11 +563,11 @@
* http://jqueryui.com/autocomplete/
*/
.ui-autocomplete {
position: absolute;
.ui-autocomplete {
position: absolute;
top: 0;
left: 0;
cursor: default;
cursor: default;
}
/*
@ -685,9 +686,9 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad
.ui-menu .ui-menu-divider { margin: 5px -2px 5px -2px; height: 0; font-size: 0; line-height: 0; border-width: 1px 0 0 0; }
.ui-menu .ui-menu-item a { text-decoration: none; display: block; padding: 2px .4em; line-height: 1.5; zoom: 1; font-weight: normal; }
.ui-menu .ui-menu-item a.ui-state-focus,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
margin: 0;
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
margin: 0;
color: #ffffff;
background: #0064cd;
background-color: #0064cd;
@ -695,6 +696,11 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad
border-color: #0064cd #0064cd #003f81;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
}
.ui-menu .ui-menu-item a p { margin: 3px 0px; }
.ui-menu .ui-menu-item a .small { color: #555555; }
.ui-menu .ui-menu-item a.ui-state-focus .small,
.ui-menu .ui-menu-item a.ui-state-active .small { color: #ffffff; }
/* Fix problem with border in ui-state-active */
.ui-menu .ui-menu-item a.ui-state-active {
padding: 1px .4em;
@ -884,4 +890,4 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad
-moz-border-radius: 4px;
-khtml-border-radius: 4px;
border-radius: 4px;
}
}

View file

@ -32,7 +32,7 @@ def clear_cache(user=None):
"time_zone"])
def delete_user_cache(user):
for key in ("bootinfo", "lang", "roles", "restrictions"):
for key in ("bootinfo", "lang", "roles", "restrictions", "home_page"):
cache.delete_value(key + ":" + user)
def clear_notifications(user=None):
@ -60,6 +60,7 @@ def clear_cache(user=None):
delete_user_cache(sess.user)
cache.delete_value("session:" + sess.sid)
delete_user_cache("Guest")
clear_notifications()
frappe.defaults.clear_cache()
@ -70,13 +71,24 @@ def clear_sessions(user=None, keep_current=False):
if keep_current and frappe.session.sid==sid[0]:
continue
else:
frappe.cache().delete_value("session:" + sid[0])
frappe.db.sql("""delete from tabSessions where sid=%s""", (sid[0],))
delete_session(sid[0])
def delete_session(sid=None):
frappe.cache().delete_value("session:" + sid)
frappe.db.sql("""delete from tabSessions where sid=%s""", sid)
def clear_all_sessions():
"""This effectively logs out all users"""
frappe.only_for("Administrator")
for sid in frappe.db.sql_list("select sid from `tabSessions`"):
delete_session(sid)
def clear_expired_sessions():
"""This function is meant to be called from scheduler"""
for sid in frappe.db.sql_list("""select sid
from tabSessions where TIMEDIFF(NOW(), lastupdate) > TIME(%s)""", get_expiry_period()):
delete_session(sid)
def get():
"""get session boot info"""
from frappe.core.doctype.notification_count.notification_count import \
@ -139,7 +151,7 @@ class Session:
self.data['data']['session_ip'] = frappe.get_request_header('REMOTE_ADDR')
if self.user != "Guest":
self.data['data']['last_updated'] = frappe.utils.now()
self.data['data']['session_expiry'] = self.get_expiry_period()
self.data['data']['session_expiry'] = get_expiry_period()
self.data['data']['session_country'] = get_geo_ip_country(frappe.get_request_header('REMOTE_ADDR'))
# insert session
@ -212,7 +224,7 @@ class Session:
rec = frappe.db.sql("""select user, sessiondata
from tabSessions where sid=%s and
TIMEDIFF(NOW(), lastupdate) < TIME(%s)""", (self.sid,
self.get_expiry_period()))
get_expiry_period()))
if rec:
data = frappe._dict(eval(rec and rec[0][1] or '{}'))
data.user = rec[0][0]
@ -261,17 +273,14 @@ class Session:
frappe.utils.now())
frappe.cache().set_value("session:" + self.sid, self.data)
def get_expiry_period(self):
exp_sec = frappe.defaults.get_global_default("session_expiry") or "06:00:00"
def get_expiry_period():
exp_sec = frappe.defaults.get_global_default("session_expiry") or "06:00:00"
# incase seconds is missing
if exp_sec:
if len(exp_sec.split(':')) == 2:
exp_sec = exp_sec + ':00'
else:
exp_sec = "2:00:00"
# incase seconds is missing
if len(exp_sec.split(':')) == 2:
exp_sec = exp_sec + ':00'
return exp_sec
return exp_sec
def get_geo_ip_country(ip_addr):
try:

View file

@ -20,10 +20,6 @@ https://frappe.io/apps/frappe
<meta name="{{ name }}" content="{{ metatags[name]|striptags }}">
{%- endfor -%}
{%- endif -%}
{%- for link in web_include_js %}
<script type="text/javascript" src="{{ link }}"></script>
{%- endfor -%}
{%- for link in web_include_css %}
<link type="text/css" rel="stylesheet" href="{{ link }}">
{%- endfor -%}
@ -38,6 +34,7 @@ https://frappe.io/apps/frappe
<body>
<div id="wrap">
{%- block banner -%}
{% include "templates/includes/banner_extension.html" ignore missing %}
{% if banner_html -%}
<header class="container">{{ banner_html or "" }}</header>
{%- endif %}
@ -92,6 +89,10 @@ https://frappe.io/apps/frappe
{%- block footer -%}{% include "templates/includes/footer.html" %}{%- endblock -%}
</div>
{%- for link in web_include_js %}
<script type="text/javascript" src="{{ link }}"></script>
{%- endfor -%}
{%- block script %}
<script data-html-block="script">
{%- if script is defined -%}{{ script }}{%- endif -%}

View file

@ -5,15 +5,15 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{{ subject }}</title>
<style>
/* -------------------------------------
GLOBAL
/* -------------------------------------
GLOBAL
------------------------------------- */
img {
max-width: 100%;
img {
max-width: 100%;
}
body {
width: 100% !important;
width: 100% !important;
height: 100%;
}
@ -21,18 +21,18 @@ body {
background-color: #eee;
}
.wrapper * {
.wrapper * {
margin:0;
padding:0;
font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
font-size: 100%;
line-height: 1.6;
}
/* -------------------------------------
ELEMENTS
/* -------------------------------------
ELEMENTS
------------------------------------- */
.wrapper a {
.wrapper a {
color: #348eda;
}
@ -66,7 +66,7 @@ body {
border-radius: 25px;
}
.last {
.last {
margin-bottom: 0;
}
@ -92,10 +92,10 @@ body {
margin-right: 5px;
}
/* -------------------------------------
BODY
/* -------------------------------------
BODY
------------------------------------- */
table.body-wrap {
table.body-wrap {
width: 100%;
padding: 10px;
}
@ -105,18 +105,18 @@ table.body-wrap .container{
}
/* -------------------------------------
FOOTER
/* -------------------------------------
FOOTER
------------------------------------- */
table.footer-wrap {
width: 100%;
table.footer-wrap {
width: 100%;
clear:both!important;
}
.footer-wrap .container p {
font-size:12px;
color:#666;
}
table.footer-wrap a{
@ -124,19 +124,19 @@ table.footer-wrap a{
}
/* -------------------------------------
TYPOGRAPHY
/* -------------------------------------
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;
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;
font-weight:200;
}
.wrapper h1 {
@ -152,23 +152,23 @@ table.footer-wrap a{
margin: 20px 0;
border-top: 1px solid #eee;
}
.wrapper p,
.wrapper ul,
.wrapper ol {
margin-bottom: 10px;
font-weight: normal;
.wrapper p,
.wrapper ul,
.wrapper ol {
margin-bottom: 10px;
font-weight: normal;
font-size:14px;
}
.wrapper ul li,
.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.
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 */
@ -188,12 +188,12 @@ table.footer-wrap a{
.content {
max-width:600px;
margin:0 auto;
display:block;
display:block;
}
/* Let's make sure tables in the content area are 100% wide */
.content table {
width: 100%;
.content table {
width: 100%;
}
a.no-decoration {
@ -209,9 +209,13 @@ small, .small {
color: #999999;
}
pre, code {
font-family: monospace !important;
}
</style>
</head>
<body>
<div class="wrapper">
@ -233,7 +237,7 @@ small, .small {
</table>
</div>
<!-- /content -->
</td>
<td></td>
</tr>
@ -245,7 +249,7 @@ small, .small {
<tr>
<td></td>
<td class="container">
<!-- content -->
<div class="content">
<table>
@ -257,7 +261,7 @@ small, .small {
</table>
</div>
<!-- /content -->
</td>
<td></td>
</tr>
@ -269,4 +273,4 @@ small, .small {
<div class="print-html">{{ print_html or "" }}</div>
</body>
</html>
</html>

View file

@ -46,6 +46,8 @@ var blog = {
b.avatar = "/" + b.avatar;
}
b.month = b.month.toUpperCase().substr(0,3);
$(repl('<div class="row">\
<div class="col-md-2 text-center" style="color: #ccc">\
<h1 class="blog-day" style="margin: 0px;">%(day)s</h1>\
@ -61,7 +63,7 @@ var blog = {
<p>%(content)s</p>\
<p style="color: #aaa; font-size: 90%">\
<a href="/blog?by=%(blogger)s&by_name=%(full_name)s">\
%(full_name)s</a> wrote this on %(published)s / %(comment_text)s</p>\
%(full_name)s</a> / %(comment_text)s</p>\
</div>\
</div><hr>', b)).appendTo($wrap);
});

View file

@ -30,7 +30,6 @@
{%- endif -%}
{%- endfor %}
<!-- post login tools -->
{% if not disable_signup %}
<li class="dropdown logged-in" id="website-post-login"
data-label="website-post-login" style="display: none">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
@ -56,6 +55,7 @@
{%- endfor -%}
</ul>
</li>
{% if not disable_signup %}
<li class="btn-login-area"><a href="/login">Sign Up / Login</a></li>
{% endif %}
</ul>
@ -63,4 +63,4 @@
</div>
</nav>
</header>
<script>$('.dropdown-toggle').dropdown()</script>
<script>$(function() { $('.dropdown-toggle').dropdown(); });</script>

View file

@ -2,27 +2,27 @@
{% block content %}
<div class="about-content">
{{ obj.doc.company_introduction or """<p>Some Introduction about your company that you would
like your website visitor to know.
More people than you think will read your About page.
People always like to know who the are doing business with.
Be authentic and avoid using jargon like 'value added services' etc.
Be sure to update your company history and
{{ doc.company_introduction or """<p>Some Introduction about your company that you would
like your website visitor to know.
More people than you think will read your About page.
People always like to know who the are doing business with.
Be authentic and avoid using jargon like 'value added services' etc.
Be sure to update your company history and
list of key team members in Website > About Us Settings</p>""" }}
{% if obj.get({"doctype":"Company History"}) %}
<h3>{{ obj.doc.company_history_heading or "Company History" }}</h3>
{% for d in obj.get({"doctype":"Company History"}) %}
{% if doc.get({"doctype":"Company History"}) %}
<h3>{{ doc.company_history_heading or "Company History" }}</h3>
{% for d in doc.get({"doctype":"Company History"}) %}
<div class="row">
<span class="col-md-2"><h4 style="margin:0px;">{{ d.year }}</h4></span>
<span class="col-md-10"><p>{{ d.highlight }}</p></span>
</div>
{% endfor %}
{% endif %}
{% if obj.get({"doctype":"About Us Team Member"}) %}
<h3>{{ obj.doc.team_members_heading or "Team Members" }}</h3>
{% for d in obj.get({"doctype":"About Us Team Member"}) %}
{% if doc.get({"doctype":"About Us Team Member"}) %}
<h3>{{ doc.team_members_heading or "Team Members" }}</h3>
{% for d in doc.get({"doctype":"About Us Team Member"}) %}
<div class="row" itemscope itemtype="http://schema.org/Person">
<span class="col-md-2">
<div class="avatar avatar-large">
@ -35,6 +35,6 @@
</div>
{% endfor %}
{% endif %}
{{ obj.doc.footer or "" }}
{{ doc.footer or "" }}
</div>
{% endblock %}

View file

@ -1,8 +1,8 @@
# 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
import frappe
def get_context(context):
return { "obj": frappe.get_doc("About Us Settings", "About Us Settings") }
return { "doc": frappe.get_doc("About Us Settings", "About Us Settings") }

View file

@ -1,15 +1,17 @@
{% block title %}{{ heading or "Contact Us"}}{% endblock %}
{% block content %}
<!-- no-sidebar -->
<div class="contact-content">
<div class="row">
<div class="col-md-8">
<p id="contact-alert" class="alert alert-warning"
{{ introduction or "" }}<br>
<p id="contact-alert" class="alert alert-warning"
style="display: none;">&nbsp;</p>
<div class="form-group">
<select name="subject" class="form-control">
{% if query_options -%}
{% for option in query_options -%}
{% for option in query_options.split("\n") -%}
<option value="{{ option }}">{{ option }}</option>
{%- endfor %}
{% else %}
@ -18,7 +20,7 @@
</select>
</div>
<div class="form-group">
<input class="form-control" name="email" type="text"
<input class="form-control" name="email" type="text"
placeholder="Your Email Address" />
</div>
<div class="form-group">
@ -53,9 +55,8 @@
</div>
{% endif %}
</div>
{{ introduction or ""}}
</div>
<script>
{% include "templates/includes/contact.js" %}
</script>
{% endblock %}
{% endblock %}

View file

@ -0,0 +1,84 @@
{% block title %}{{ type }} {{ _("List") }}{% endblock %}
{% block header %}
<h2>{{ type }} {{ _("List") }}</h2>
{% endblock %}
{% block content %}
<!-- no-sidebar -->
<div class="row">
<div class=" col-sm-offset-8 col-sm-4">
<form class="form-inline form-search" action="/list">
<div class="input-group">
<input class="form-control" type="text" name="txt"
placeholder="Search..." value="{{ txt or '' }}">
<input type="hidden" name="type" value="{{ type }}">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">
<i class="icon-search"></i></button>
</span>
</div>
</form>
</div>
</div>
<br>
{% if txt %}
<div class="alert alert-warning">Results filtered by <b>{{ txt }}</b>. <a href="/list?type={{ type }}" class="close">&times;</a></div>
{% endif %}
<div class="list-group" data-type="{{ type }}" data-txt="{{ txt or '[notxt]' }}">
{% for item in items %}
<div class="list-group-item">
{{ item }}
</div>
{% endfor %}
</div>
<div class="more-block text-center hide">
<button class="btn btn-default btn-more">More</button>
</div>
{% endblock %}
{% block script %}
<script>
frappe.ready(function() {
// show more button if len is 20
$list_group = $(".list-group[data-type='{{ type }}'][data-txt='{{ txt or "[notxt]" }}']");
// more ajax
frappe.start = 20;
$(".btn-more").on("click", function() {
$.ajax({
url:"/api/method/frappe.templates.pages.list.get_items",
data: {
type: "{{ type }}",
txt: "{{ txt or '' }}",
limit_start: frappe.start
},
statusCode: {
200: function(data) {
frappe.start += 20;
$.each(data.message.items, function(i, d) {
$('<div class="list-group-item">')
.html(d)
.appendTo($list_group);
});
show_more();
}
}
})
})
var show_more = function() {
var $items = $list_group.find(".list-group-item")
if($items.length && ($items.length % 20 === 0)) {
if($(".more-block").hasClass("hide"))
$(".more-block").removeClass("hide");
} else {
if(!$(".more-block").hasClass("hide"))
$(".more-block").addClass("hide");
}
};
show_more();
})
</script>
{% endblock %}

View file

@ -0,0 +1,50 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import frappe, os
from frappe.modules import get_doc_path
from jinja2 import Environment, Template, FileSystemLoader
no_cache = 1
no_sitemap = 1
def get_context(context):
context.type = frappe.local.form_dict.type
context.txt = frappe.local.form_dict.txt
context.update(get_items(context.type, context.txt))
return context
@frappe.whitelist(allow_guest=True)
def get_items(type, txt, limit_start=0):
meta = frappe.get_meta(type)
filters, or_filters = [], []
out = frappe._dict()
if txt:
if meta.search_fields:
for f in meta.get_search_fields():
or_filters.append([type, f.strip(), "like", "%" + txt + "%"])
else:
filters.append([type, "name", "like", "%" + txt + "%"])
out.raw_items = frappe.get_list(type, fields = ["*"],
filters=filters, or_filters = or_filters, limit_start=limit_start,
limit_page_length = 20)
template_path = os.path.join(get_doc_path(meta.module, "DocType", meta.name), "list_item.html")
if os.path.exists(template_path):
env = Environment(loader = FileSystemLoader("/"))
#template_path = os.path.relpath(template_path)
template = env.get_template(template_path)
else:
template = Template("""<div><a href="/{{ doctype }}/{{ doc.name }}">
{{ doc[title_field] }}</a></div>""")
out.items = [template.render(doc=i, doctype=type,
title_field = meta.title_field or "name") for i in out.raw_items]
out.meta = meta
return out

View file

@ -7,4 +7,4 @@
<div class="message-content">
<p>{{ message }}</p>
</div>
{% endblock %}
{% endblock %}

View file

@ -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
import frappe
@ -11,11 +11,11 @@ base_template_path = "templates/pages/rss.xml"
def get_context(context):
"""generate rss feed"""
host = get_request_site_address()
blog_list = frappe.db.sql("""\
select page_name as name, published_on, modified, title, content from `tabBlog Post`
select page_name as name, published_on, modified, title, content from `tabBlog Post`
where ifnull(published,0)=1
order by published_on desc limit 20""", as_dict=1)
@ -23,22 +23,21 @@ def get_context(context):
blog_page = cstr(urllib.quote(blog.name.encode("utf-8"))) + ".html"
blog.link = urllib.basejoin(host, blog_page)
blog.content = escape_html(blog.content or "")
if blog_list:
modified = max((blog['modified'] for blog in blog_list))
else:
modified = now()
ws = frappe.get_doc('Website Settings', 'Website Settings')
blog_settings = frappe.get_doc('Blog Settings', 'Blog Settings')
context = {
'title': ws.title_prefix,
'description': ws.description or ((ws.title_prefix or "") + ' Blog'),
'title': blog_settings.blog_title or "Blog",
'description': blog_settings.blog_introduction or "",
'modified': modified,
'items': blog_list,
'link': host + '/blog'
}
# print context
return context

View file

@ -0,0 +1,78 @@
{% block title %}{{ doc.doctype }} / {{ doc[meta.title_field or "name"] }}{% endblock %}
{% block header %}
<h2>{{ doc[meta.title_field or "name"] }}</h2>
<p><a href="/{{ doc.doctype }}">{{ doc.doctype }} {{ _("List") }}</a></p>
{% endblock %}
{% block content %}
{% if custom_view %}
{{ custom_view }}
{% else %}
{% macro print_value(df, d, meta) -%}
{% if df.fieldtype=="Check" %}
<i class="{{ 'icon-check' if d[df.fieldname] else 'icon-check-empty' }}"></i>
{% elif df.fieldtype=="Image" %}
<img src="{{ doc[meta.get_field(df.fieldname).options] }}" class="img-responsive">
{% else %}
{{ d[df.fieldname] or "" }}
{% endif %}
{%- endmacro %}
{% macro get_width(fieldtype) -%}
{%- if fieldtype in ("Int", "Check") -%}{{ 60 }}
{%- else -%}{{ 150 }}{% endif -%}
{%- endmacro %}
{% for df in meta.fields %}
{% if not df.hidden and not df.permlevel and not df.print_hide %}
{% if df.fieldtype=="Section Break" %}
<h4>{{ df.label or "" }}</h4>
{% elif df.fieldtype=="Column Break" %}
{% elif df.fieldtype=="Table" %}
{% set table_meta = frappe.get_meta(df.options) %}
<div style="overflow-x: auto;">
<table class="table table-bordered" style="table-layout: fixed">
<thead>
<tr>
<th style="width: 40px">Sr</th>
{% for tdf in table_meta.fields %}
{% if tdf.fieldtype not in ("Column Break", "Section Break")
and tdf.label %}
<th style="width: {{ get_width(tdf.fieldtype) }}px">
{{ tdf.label }}</th>
{% endif %}
{% endfor %}
</tr>
</thead>
<tbody>
{% for d in doc[df.fieldname] %}
<tr>
<td>{{ d.idx }}</td>
{% for tdf in table_meta.fields %}
{% if tdf.fieldtype not in ("Column Break", "Section Break") %}
<td>{{ print_value(tdf, d, table_meta) }}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="row">
<div class="col-sm-4 text-right">
{% if df.fieldtype not in ("Image","HTML") %}
<label>{{ df.label }}</label>
{% endif %}
</div>
<div class="col-sm-8">
{{ print_value(df, doc, meta) }}
</div>
</div>
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endblock %}

Some files were not shown because too many files have changed in this diff Show more