diff --git a/MANIFEST.in b/MANIFEST.in index 815f6a7572..b10df4f0d0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,7 +3,6 @@ include requirements.txt include *.json include *.md include *.py -include *.txt recursive-include frappe *.css recursive-include frappe *.dat recursive-include frappe *.eot @@ -22,5 +21,4 @@ recursive-include frappe *.swf recursive-include frappe *.ttf recursive-include frappe *.woff recursive-include frappe *.xml -recursive-include frappe *.txt recursive-exclude * *.pyc \ No newline at end of file diff --git a/frappe/__init__.py b/frappe/__init__.py index ca7802c25f..8f70e2d3eb 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -15,7 +15,6 @@ import os, sys, importlib, inspect import json import semantic_version -from frappe.core.doctype.print_format.print_format import get_html as get_print_html from .exceptions import * local = Local() @@ -53,13 +52,13 @@ def get_lang_dict(fortype, name=None): return {} from frappe.translate import get_dict return get_dict(fortype, name) - + def set_user_lang(user, user_language=None): from frappe.translate import get_lang_dict - + if not user_language: user_language = db.get_value("User", user, "language") - + if user_language: lang_dict = get_lang_dict() if user_language in lang_dict: @@ -89,16 +88,16 @@ def init(site, sites_path=None): if not sites_path: sites_path = '.' - + local.error_log = [] local.site = site local.sites_path = sites_path local.site_path = os.path.join(sites_path, site) local.message_log = [] local.debug_log = [] - local.response = _dict({}) local.lang = "en" local.request_method = request.method if request else None + local.response = _dict({"docs":[]}) local.conf = _dict(get_site_config()) local.initialised = True local.flags = _dict({}) @@ -111,33 +110,42 @@ def init(site, sites_path=None): local.test_objects = {} local.jenv = None local.jloader =None - local.meta = {} + local.cache = {} setup_module_map() +def connect(site=None, db_name=None): + from database import Database + if site: + init(site) + local.db = Database(user=db_name or local.conf.db_name) + local.form_dict = _dict() + local.session = _dict() + set_user("Administrator") + def get_site_config(sites_path=None, site_path=None): config = {} - + sites_path = sites_path or getattr(local, "sites_path", None) site_path = site_path or getattr(local, "site_path", None) - + if sites_path: common_site_config = os.path.join(sites_path, "common_site_config.json") if os.path.exists(common_site_config): config.update(get_file_json(common_site_config)) - + if site_path: site_config = os.path.join(site_path, "site_config.json") if os.path.exists(site_config): config.update(get_file_json(site_config)) - + return _dict(config) def destroy(): """closes connection and releases werkzeug local""" if db: db.close() - + release_local(local) _memc = None @@ -158,14 +166,14 @@ def errprint(msg): from utils import cstr if not request: print cstr(msg) - + error_log.append(cstr(msg)) def log(msg): if not request: if conf.get("logging") or False: print repr(msg) - + from utils import cstr debug_log.append(cstr(msg)) @@ -179,18 +187,18 @@ def msgprint(msg, small=0, raise_exception=0, as_table=False): raise raise_exception, msg else: raise ValidationError, msg - + if flags.mute_messages: _raise_exception() return - + from utils import cstr if as_table and type(msg) in (list, tuple): msg = '' + ''.join([''+''.join(['' % c for c in r])+'' for r in msg]) + '
%s
' - + if flags.print_messages: print "Message: " + repr(msg) - + message_log.append((small and '__small:' or '')+cstr(msg or '')) _raise_exception() @@ -199,20 +207,13 @@ def throw(msg, exc=ValidationError): def create_folder(path): if not os.path.exists(path): os.makedirs(path) - -def connect(site=None, db_name=None): - from database import Database - if site: - init(site) - local.db = Database(user=db_name or local.conf.db_name) - local.response = _dict() - local.form_dict = _dict() - local.session = _dict() - set_user("Administrator") def set_user(username): from frappe.utils.user import User - local.session["user"] = username + local.session.user = username + local.session.sid = username + local.cache = {} + local.session.data = {} local.user = User(username) local.restrictions = None local.user_perms = {} @@ -233,23 +234,23 @@ guest_methods = [] def whitelist(allow_guest=False): """ decorator for whitelisting a function - + Note: if the function is allowed to be accessed by a guest user, it must explicitly be marked as allow_guest=True - + for specific roles, set allow_roles = ['Administrator'] etc. """ def innerfn(fn): global whitelisted, guest_methods whitelisted.append(fn) - + if allow_guest: guest_methods.append(fn) return fn - + return innerfn - + def only_for(roles): if not isinstance(roles, (tuple, list)): roles = (roles,) @@ -257,13 +258,13 @@ def only_for(roles): myroles = set(get_roles()) if not roles.intersection(myroles): raise PermissionError - + def clear_cache(user=None, doctype=None): """clear cache""" import frappe.sessions if doctype: - import frappe.model.doctype - frappe.model.doctype.clear_cache(doctype) + import frappe.model.meta + frappe.model.meta.clear_cache(doctype) reset_metadata_version() elif user: frappe.sessions.clear_cache(user) @@ -281,7 +282,7 @@ def get_roles(username=None): return local.user.get_roles() else: return User(username).get_roles() - + def has_permission(doctype, ptype="read", refdoc=None): import frappe.permissions return frappe.permissions.has_permission(doctype, ptype, refdoc) @@ -298,80 +299,46 @@ def clear_perms(doctype): def reset_perms(doctype): clear_perms(doctype) - reload_doc(db.get_value("DocType", doctype, "module"), + reload_doc(db.get_value("DocType", doctype, "module"), "DocType", doctype, force=True) def generate_hash(txt=None): """Generates random hash for session id""" import hashlib, time - return hashlib.sha224((txt or "") + str(time.time())).hexdigest() + return hashlib.sha224((txt or "") + repr(time.time())).hexdigest() def reset_metadata_version(): v = generate_hash() cache().set_value("metadata_version", v) return v -def get_obj(dt = None, dn = None, doc=None, doclist=None, with_children = True): - from frappe.model.code import get_obj - return get_obj(dt, dn, doc, doclist, with_children) - -def doc(doctype=None, name=None, fielddata=None): - from frappe.model.doc import Document - return Document(doctype, name, fielddata) - def new_doc(doctype, parent_doc=None, parentfield=None): from frappe.model.create_new import get_new_doc return get_new_doc(doctype, parent_doc, parentfield) -def new_bean(doctype): - from frappe.model.create_new import get_new_doc - return bean([get_new_doc(doctype)]) - -def doclist(lst=None): - from frappe.model.doclist import DocList - return DocList(lst) - -def bean(doctype=None, name=None, copy=None): - """return an instance of the object, wrapped as a Bean (frappe.model.bean)""" - from frappe.model.bean import Bean - if copy: - return Bean(copy_doclist(copy)) - else: - return Bean(doctype, name) - def set_value(doctype, docname, fieldname, value): import frappe.client return frappe.client.set_value(doctype, docname, fieldname, value) -def get_doclist(doctype, name=None): - return bean(doctype, name).doclist - def get_doc(arg1, arg2=None): import frappe.model.document return frappe.model.document.get_doc(arg1, arg2) -def get_doctype(doctype, processed=False): - import frappe.model.doctype - return frappe.model.doctype.get(doctype, processed) - def get_meta(doctype, cached=True): import frappe.model.meta return frappe.model.meta.get_meta(doctype, cached=cached) -def delete_doc(doctype=None, name=None, doclist = None, force=0, ignore_doctypes=None, - for_reload=False, ignore_permissions=False): +def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False, ignore_permissions=False): import frappe.model.delete_doc - + if not ignore_doctypes: ignore_doctypes = [] - + if isinstance(name, list): for n in name: - frappe.model.delete_doc.delete_doc(doctype, n, doclist, force, ignore_doctypes, - for_reload, ignore_permissions) + frappe.model.delete_doc.delete_doc(doctype, n, force, ignore_doctypes, for_reload, ignore_permissions) else: - frappe.model.delete_doc.delete_doc(doctype, name, doclist, force, ignore_doctypes, - for_reload, ignore_permissions) + frappe.model.delete_doc.delete_doc(doctype, name, force, ignore_doctypes, for_reload, ignore_permissions) def reload_doc(module, dt=None, dn=None, force=False): import frappe.modules @@ -397,21 +364,21 @@ def get_module_path(module, *joins): def get_app_path(app_name, *joins): return get_pymodule_path(app_name, *joins) - + def get_site_path(*joins): return os.path.join(local.site_path, *joins) def get_pymodule_path(modulename, *joins): joins = [scrub(part) for part in joins] return os.path.join(os.path.dirname(get_module(scrub(modulename)).__file__), *joins) - + def get_module_list(app_name): return get_file_items(os.path.join(os.path.dirname(get_module(app_name).__file__), "modules.txt")) def get_all_apps(with_frappe=False, with_internal_apps=True, sites_path=None): if not sites_path: sites_path = local.sites_path - + apps = get_file_items(os.path.join(sites_path, "apps.txt"), raise_not_found=True) if with_internal_apps: apps.extend(get_file_items(os.path.join(local.site_path, "apps.txt"))) @@ -440,7 +407,7 @@ def get_hooks(hook=None, app_name=None): hooks = _dict(load_app_hooks(app_name)) else: hooks = _dict(cache().get_value("app_hooks", load_app_hooks)) - + if hook: return hooks.get(hook) or [] else: @@ -448,11 +415,11 @@ def get_hooks(hook=None, app_name=None): def setup_module_map(): _cache = cache() - + if conf.db_name: local.app_modules = _cache.get_value("app_modules") local.module_app = _cache.get_value("module_app") - + if not local.app_modules: local.module_app, local.app_modules = {}, {} for app in get_all_apps(True): @@ -461,16 +428,16 @@ def setup_module_map(): for module in get_module_list(app): local.module_app[module] = app local.app_modules[app].append(module) - + if conf.db_name: _cache.set_value("app_modules", local.app_modules) _cache.set_value("module_app", local.module_app) - + def get_file_items(path, raise_not_found=False): content = read_file(path, raise_not_found=raise_not_found) if content: return [p.strip() for p in content.splitlines() if p.strip() and not p.startswith("#")] - else: + else: return [] def get_file_json(path): @@ -480,7 +447,7 @@ def get_file_json(path): def read_file(path, raise_not_found=False): from frappe.utils import cstr if os.path.exists(path): - with open(path, "r") as f: + with open(path, "r") as f: return cstr(f.read()) elif raise_not_found: raise IOError("{} Not Found".format(path)) @@ -491,7 +458,7 @@ def get_attr(method_string): modulename = '.'.join(method_string.split('.')[:-1]) methodname = method_string.split('.')[-1] return getattr(get_module(modulename), methodname) - + def call(fn, *args, **kwargs): if hasattr(fn, 'fnargs'): fnargs = fn.fnargs @@ -506,7 +473,7 @@ def call(fn, *args, **kwargs): def make_property_setter(args): args = _dict(args) - bean([{ + get_doc({ 'doctype': "Property Setter", 'doctype_or_field': args.doctype_or_field or "DocField", 'doc_type': args.doctype, @@ -515,43 +482,31 @@ def make_property_setter(args): 'value': args.value, 'property_type': args.property_type or "Data", '__islocal': 1 - }]).save() + }).save() -def get_application_home_page(user='Guest'): - """get home page for user""" - hpl = db.sql("""select home_page - from `tabDefault Home Page` - where parent='Control Panel' - and role in ('%s') order by idx asc limit 1""" % "', '".join(get_roles(user))) - if hpl: - return hpl[0][0] - else: - return db.get_value("Control Panel", None, "home_page") - -def import_doclist(path, ignore_links=False, ignore_insert=False, insert=False): +def import_doc(path, ignore_links=False, ignore_insert=False, insert=False): from frappe.core.page.data_import_tool import data_import_tool - data_import_tool.import_doclist(path, ignore_links=ignore_links, ignore_insert=ignore_insert, insert=insert) + data_import_tool.import_doc(path, ignore_links=ignore_links, ignore_insert=ignore_insert, insert=insert) -def copy_doclist(in_doclist): - new_doclist = [] - parent_doc = None - for i, d in enumerate(in_doclist): - is_dict = False - if isinstance(d, dict): - is_dict = True - values = _dict(d.copy()) - else: - values = _dict(d.fields.copy()) - - newd = new_doc(values.doctype, parent_doc=(None if i==0 else parent_doc), parentfield=values.parentfield) - newd.fields.update(values) - - if i==0: - parent_doc = newd - - new_doclist.append(newd.fields if is_dict else newd) - - return doclist(new_doclist) +def copy_doc(doc): + import copy + if not isinstance(doc, dict): + d = doc.as_dict() + else: + d = doc + + newdoc = get_doc(copy.deepcopy(d)) + newdoc.name = None + newdoc.set("__islocal", 1) + newdoc.owner = None + newdoc.creation = None + for d in newdoc.get_all_children(): + d.name = None + d.parent = None + d.set("__islocal", 1) + d.owner = None + d.creation = None + return newdoc def compare(val1, condition, val2): import frappe.utils @@ -565,7 +520,7 @@ def respond_as_web_page(title, html, success=None, http_status_code=None): local.response['page_name'] = 'message.html' if http_status_code: local.response['http_status_code'] = http_status_code - + def build_match_conditions(doctype, as_condition=True): import frappe.widgets.reportview return frappe.widgets.reportview.build_match_conditions(doctype, as_condition) @@ -577,7 +532,7 @@ def get_list(doctype, filters=None, fields=None, docstatus=None, 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) - + run_query = get_list def get_jenv(): @@ -594,34 +549,34 @@ def get_jenv(): "frappe.utils": frappe.utils, "_": _ }) - + local.jenv = jenv - + return local.jenv - + def get_jloader(): if not local.jloader: from jinja2 import ChoiceLoader, PackageLoader apps = get_installed_apps() apps.remove("frappe") - + local.jloader = ChoiceLoader([PackageLoader(app, ".") \ for app in apps + ["frappe"]]) - + return local.jloader - + def set_filters(jenv): from frappe.utils import global_date_format from frappe.website.utils import get_hex_shade from markdown2 import markdown from json import dumps - + jenv.filters["global_date_format"] = global_date_format jenv.filters["markdown"] = markdown jenv.filters["json"] = dumps jenv.filters["get_hex_shade"] = get_hex_shade - + # load jenv_filters from hooks.txt for app in get_all_apps(True): for jenv_filter in (get_hooks(app_name=app).jenv_filter or []): @@ -630,14 +585,23 @@ def set_filters(jenv): def get_template(path): return get_jenv().get_template(path) - + def get_website_route(doctype, name): return db.get_value("Website Route", {"ref_doctype": doctype, "docname": name}) -def add_version(doclist): - bean({ +def add_version(doc): + get_doc({ "doctype": "Version", - "ref_doctype": doclist[0].get("doctype"), - "docname": doclist[0].get("name"), - "doclist_json": json.dumps([d.fields for d in doclist]) + "ref_doctype": doc.doctype, + "docname": doc.name, + "doclist_json": json.dumps(doc.as_dict(), indent=1, sort_keys=True) }).insert(ignore_permissions=True) + +def get_test_records(doctype): + from frappe.modules import get_doctype_module, get_module_path + path = os.path.join(get_module_path(get_doctype_module(doctype)), "doctype", scrub(doctype), "test_records.json") + if os.path.exists(path): + with open(path, "r") as f: + return json.loads(f.read()) + else: + return [] diff --git a/frappe/api.py b/frappe/api.py index 2365cecb44..cbeddda742 100644 --- a/frappe/api.py +++ b/frappe/api.py @@ -41,17 +41,17 @@ def handle(): elif call=="resource": if "run_method" in frappe.local.form_dict: - bean = frappe.bean(doctype, name) + doc = frappe.get_doc(doctype, name) if frappe.local.request.method=="GET": - if not bean.has_permission("read"): + if not doc.has_permission("read"): frappe.throw("No Permission", frappe.PermissionError) - bean.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 bean.has_permission("write"): + if not doc.has_permission("write"): frappe.throw("No Permission", frappe.PermissionError) - bean.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) frappe.db.commit() else: @@ -63,7 +63,7 @@ def handle(): if frappe.local.request.method=="PUT": frappe.local.response.update({ - "doclist":frappe.client.save(frappe.local.form_dict.doclist)}) + "doclist":frappe.client.save(frappe.local.form_dict)}) frappe.db.commit() if frappe.local.request.method=="DELETE": @@ -79,7 +79,7 @@ def handle(): if frappe.local.request.method=="POST": frappe.local.response.update({ - "doclist": frappe.client.insert(frappe.local.form_dict.doclist)}) + "doclist": frappe.client.insert(frappe.local.form_dict)}) frappe.db.commit() else: raise frappe.DoesNotExistError diff --git a/frappe/app.py b/frappe/app.py index c45d360134..9860a924cc 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -68,7 +68,8 @@ def application(request): frappe.DoesNotExistError, frappe.DuplicateEntryError, frappe.OutgoingEmailError, - frappe.ValidationError), e: + frappe.ValidationError, + frappe.UnsupportedMediaType), e: if frappe.local.is_ajax: response = frappe.utils.response.report_error(e.http_status_code) @@ -118,7 +119,7 @@ def serve(port=8000, profile=False, site=None, sites_path='.'): from werkzeug.serving import run_simple if profile: - application = ProfilerMiddleware(application) + application = ProfilerMiddleware(application, sort_by=('tottime', 'calls')) if not os.environ.get('NO_STATICS'): application = SharedDataMiddleware(application, { diff --git a/frappe/boot.py b/frappe/boot.py index 2470edd941..443ea73771 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -1,5 +1,5 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt from __future__ import unicode_literals """ @@ -8,7 +8,6 @@ bootstrap client session import frappe import frappe.defaults -import frappe.model.doc import frappe.widgets.page import json @@ -20,21 +19,17 @@ def get_bootinfo(): # user get_user(bootinfo) - - # control panel - cp = frappe.model.doc.getsingle('Control Panel') - + # system info - bootinfo['control_panel'] = frappe._dict(cp.copy()) bootinfo['sysdefaults'] = frappe.defaults.get_defaults() bootinfo['server_date'] = frappe.utils.nowdate() - bootinfo["send_print_in_body_and_attachment"] = frappe.db.get_value("Outgoing Email Settings", + bootinfo["send_print_in_body_and_attachment"] = frappe.db.get_value("Outgoing Email Settings", None, "send_print_in_body_and_attachment") if frappe.session['user'] != 'Guest': bootinfo['user_info'] = get_fullnames() bootinfo['sid'] = frappe.session['sid']; - + # home page bootinfo.modules = {} for app in frappe.get_installed_apps(): @@ -45,11 +40,11 @@ def get_bootinfo(): bootinfo.module_app = frappe.local.module_app bootinfo.hidden_modules = frappe.db.get_global("hidden_modules") - bootinfo.doctype_icons = dict(frappe.db.sql("""select name, icon from + bootinfo.doctype_icons = dict(frappe.db.sql("""select name, icon from tabDocType where ifnull(icon,'')!=''""")) - bootinfo.doctype_icons.update(dict(frappe.db.sql("""select name, icon from + bootinfo.doctype_icons.update(dict(frappe.db.sql("""select name, icon from tabPage where ifnull(icon,'')!=''"""))) - + add_home_page(bootinfo, doclist) add_allowed_pages(bootinfo) load_translations(bootinfo) @@ -58,23 +53,16 @@ def get_bootinfo(): # ipinfo if frappe.session['data'].get('ipinfo'): bootinfo['ipinfo'] = frappe.session['data']['ipinfo'] - + # add docs bootinfo['docs'] = doclist - + for method in hooks.boot_session or []: frappe.get_attr(method)(bootinfo) - - from frappe.model.utils import compress - bootinfo['docs'] = compress(bootinfo['docs']) if bootinfo.lang: bootinfo.lang = unicode(bootinfo.lang) - - bootinfo.metadata_version = frappe.cache().get_value("metadata_version") - if not bootinfo.metadata_version: - bootinfo.metadata_version = frappe.reset_metadata_version() - + return bootinfo def load_conf_settings(bootinfo): @@ -86,25 +74,25 @@ def add_allowed_pages(bootinfo): roles = frappe.get_roles() bootinfo.page_info = dict(frappe.db.sql("""select distinct parent, modified from `tabPage Role` where role in (%s)""" % ', '.join(['%s']*len(roles)), roles)) - + # pages where role is not set are also allowed bootinfo.page_info.update(dict(frappe.db.sql("""select parent, modified - from `tabPage` where - (select count(*) from `tabPage Role` + from `tabPage` where + (select count(*) from `tabPage Role` where `tabPage Role`.parent=tabPage.name) = 0"""))) def load_translations(bootinfo): frappe.set_user_lang(frappe.session.user) - + if frappe.lang != 'en': bootinfo["__messages"] = frappe.get_lang_dict("include") bootinfo["lang"] = frappe.lang def get_fullnames(): """map of user fullnames""" - ret = frappe.db.sql("""select name, - concat(ifnull(first_name, ''), - if(ifnull(last_name, '')!='', ' ', ''), ifnull(last_name, '')), + ret = frappe.db.sql("""select name, + concat(ifnull(first_name, ''), + if(ifnull(last_name, '')!='', ' ', ''), ifnull(last_name, '')), user_image, gender, email from tabUser where ifnull(enabled, 0)=1""", as_list=1) d = {} @@ -113,7 +101,7 @@ def get_fullnames(): r[2] = '/assets/frappe/images/ui/avatar.png' else: r[2] = r[2] - + d[r[0]]= {'fullname': r[1], 'image': r[2], 'gender': r[3], 'email': r[4] or r[0]} @@ -124,24 +112,24 @@ def get_startup_js(): for method in frappe.get_hooks().startup_js or []: startup_js.append(frappe.get_attr(method)() or "") return "\n".join(startup_js) - + def get_user(bootinfo): """get user info""" bootinfo['user'] = frappe.user.load_user() - -def add_home_page(bootinfo, doclist): + +def add_home_page(bootinfo, docs): """load home page""" if frappe.session.user=="Guest": return - - home_page = frappe.get_application_home_page(frappe.session.user) + + home_page = frappe.db.get_default("desktop:home_page") try: - page_doclist = frappe.widgets.page.get(home_page) + page = frappe.widgets.page.get(home_page) except (frappe.DoesNotExistError, frappe.PermissionError), e: - page_doclist = frappe.widgets.page.get('desktop') - - bootinfo['home_page_html'] = page_doclist[0].content - bootinfo['home_page'] = page_doclist[0].name - doclist += page_doclist + frappe.message_log.pop() + page = frappe.widgets.page.get('desktop') + + bootinfo['home_page'] = page.name + docs.append(page) diff --git a/frappe/celery_app.py b/frappe/celery_app.py index 7032ad0dd8..7d03034d54 100644 --- a/frappe/celery_app.py +++ b/frappe/celery_app.py @@ -51,9 +51,9 @@ class SiteRouter(object): def route_for_task(self, task, args=None, kwargs=None): if hasattr(frappe.local, 'site'): if kwargs and kwargs.get("event", "").endswith("_long"): - get_queue(frappe.local.site, LONGJOBS_PREFIX) + return get_queue(frappe.local.site, LONGJOBS_PREFIX) else: - get_queue(frappe.local.site) + return get_queue(frappe.local.site) return None diff --git a/frappe/cli.py b/frappe/cli.py index d191de2397..01c3c48e36 100755 --- a/frappe/cli.py +++ b/frappe/cli.py @@ -25,9 +25,9 @@ def main(): else: parsed_args["sites_path"] = os.environ.get("SITES_PATH", ".") sites_path = parsed_args.get("sites_path") - + if not parsed_args.get("make_app"): - + if parsed_args.get("site")=="all": for site in get_sites(parsed_args["sites_path"]): print "\nRunning", fn, "for", site @@ -35,20 +35,20 @@ def main(): args = parsed_args.copy() args["site"] = site frappe.init(site, sites_path=sites_path) - run(fn, args) + return run(fn, args) else: site = get_site(parsed_args) if fn not in site_arg_optional and not site: print 'site argument required' - exit(1) + return 1 elif site: frappe.init(site, sites_path=sites_path) else: # site argument optional frappe.init("", sites_path=sites_path) - run(fn, parsed_args) + return run(fn, parsed_args) else: - run(fn, parsed_args) + return run(fn, parsed_args) def cmd(fn): def new_fn(*args, **kwargs): @@ -59,25 +59,39 @@ def cmd(fn): # should not pass an argument more than once if i >= len(args) and a in kwargs: new_kwargs[a] = kwargs.get(a) - + return fn(*args, **new_kwargs) - + return new_fn - + def run(fn, args): + import cProfile, pstats, StringIO + + use_profiler = args.get("profile") and fn!="serve" + if use_profiler: + pr = cProfile.Profile() + pr.enable() + if isinstance(args.get(fn), (list, tuple)): out = globals().get(fn)(*args.get(fn), **args) else: out = globals().get(fn)(**args) - + + if use_profiler: + pr.disable() + s = StringIO.StringIO() + ps = pstats.Stats(pr, stream=s).sort_stats('tottime', 'ncalls') + ps.print_stats() + print s.getvalue() + return out - + def get_function(args): for fn, val in args.items(): if (val or isinstance(val, list)) and globals().get(fn): return fn - + def get_sites(sites_path=None): import os if not sites_path: @@ -86,26 +100,28 @@ def get_sites(sites_path=None): if not os.path.islink(os.path.join(sites_path, site)) and os.path.isdir(os.path.join(sites_path, site)) and not site in ('assets',)] - + def setup_parser(): import argparse parser = argparse.ArgumentParser(description="Run frappe utility functions") - + setup_install(parser) setup_utilities(parser) setup_translation(parser) setup_test(parser) - + parser.add_argument("site", nargs="?") - + # common parser.add_argument("-f", "--force", default=False, action="store_true", help="Force execution where applicable (look for [-f] in help)") - parser.add_argument("--quiet", default=True, action="store_false", dest="verbose", + parser.add_argument("--verbose", default=False, action="store_true", + help="Show verbose output (where applicable)") + parser.add_argument("--quiet", default=True, action="store_false", help="Do not show verbose output (where applicable)") - + return parser.parse_args() - + def setup_install(parser): parser.add_argument("--make_app", default=False, action="store_true", help="Make a new application with boilerplate") @@ -119,11 +135,11 @@ def setup_install(parser): help="Add these app(s) to Installed Apps") parser.add_argument("--root-password", nargs=1, help="Root password for new app") - parser.add_argument("--reinstall", default=False, action="store_true", + parser.add_argument("--reinstall", default=False, action="store_true", help="Install a fresh app in db_name specified in conf.py") parser.add_argument("--restore", metavar=("DB-NAME", "SQL-FILE"), nargs=2, help="Restore from an sql file") - parser.add_argument("--add_system_manager", nargs="+", + parser.add_argument("--add_system_manager", nargs="+", metavar=("EMAIL", "[FIRST-NAME] [LAST-NAME]"), help="Add a user with all roles") def setup_test(parser): @@ -135,6 +151,8 @@ def setup_test(parser): help="Run command for specified doctype") parser.add_argument("-m", "--module", metavar="MODULE", nargs=1, help="Run command for specified module") + parser.add_argument("--tests", metavar="TEST FUNCTION", nargs="*", + help="Run one or more specific test functions") def setup_utilities(parser): # update @@ -149,10 +167,10 @@ def setup_utilities(parser): help="Reload all doctypes, pages, etc. using txt files [-f]") parser.add_argument("--update_all_sites", nargs="*", metavar=("REMOTE", "BRANCH"), help="Perform git pull, run patches, sync schema and rebuild files/translations") - - parser.add_argument("--reload_doc", nargs=3, + + parser.add_argument("--reload_doc", nargs=3, metavar=('"MODULE"', '"DOCTYPE"', '"DOCNAME"')) - + # build parser.add_argument("-b", "--build", default=False, action="store_true", help="Minify + concatenate JS and CSS files, build translations") @@ -160,7 +178,7 @@ def setup_utilities(parser): help="Make copy of assets instead of symlinks") parser.add_argument("-w", "--watch", default=False, action="store_true", help="Watch and concatenate JS and CSS files as and when they change") - + # misc parser.add_argument("--backup", default=False, action="store_true", help="Take backup of database in backup folder [--with_files]") @@ -189,12 +207,12 @@ def setup_utilities(parser): parser.add_argument("--ipython", action="store_true", help="get ipython shell for a site") parser.add_argument("--execute", help="execute a function", nargs=1, metavar="FUNCTION") parser.add_argument("--get_site_status", action="store_true", help="Get site details") - parser.add_argument("--update_site_config", nargs=1, - metavar="site-CONFIG-JSON", + parser.add_argument("--update_site_config", nargs=1, + metavar="site-CONFIG-JSON", help="Update site_config.json for a given site") parser.add_argument("--port", default=8000, type=int, help="port for development server") parser.add_argument("--use", action="store_true", help="Set current site for development.") - + # clear parser.add_argument("--clear_web", default=False, action="store_true", help="Clear website cache") @@ -206,37 +224,37 @@ 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") - + # scheduler parser.add_argument("--run_scheduler", default=False, action="store_true", help="Trigger scheduler") parser.add_argument("--celery", nargs="*", help="Run Celery Commands") - parser.add_argument("--run_scheduler_event", nargs=1, + parser.add_argument("--run_scheduler_event", nargs=1, metavar="all | daily | weekly | monthly", help="Run a scheduler event") - + # replace - parser.add_argument("--replace", nargs=3, + parser.add_argument("--replace", nargs=3, metavar=("SEARCH-REGEX", "REPLACE-BY", "FILE-EXTN"), help="Multi-file search-replace [-f]") - + # import/export parser.add_argument("--export_doc", nargs=2, metavar=('"DOCTYPE"', '"DOCNAME"')) - parser.add_argument("--export_doclist", nargs=3, metavar=("DOCTYPE", "NAME", "PATH"), + parser.add_argument("--export_doclist", nargs=3, metavar=("DOCTYPE", "NAME", "PATH"), help="""Export doclist as json to the given path, use '-' as name for Singles.""") - parser.add_argument("--export_csv", nargs=2, metavar=("DOCTYPE", "PATH"), + parser.add_argument("--export_csv", nargs=2, metavar=("DOCTYPE", "PATH"), help="""Dump DocType as csv""") - parser.add_argument("--export_fixtures", default=False, action="store_true", + parser.add_argument("--export_fixtures", default=False, action="store_true", help="""Export fixtures""") - parser.add_argument("--import_doclist", nargs=1, metavar="PATH", - help="""Import (insert/update) doclist. If the argument is a directory, all files ending with .json are imported""") - + parser.add_argument("--import_doc", nargs=1, metavar="PATH", + help="""Import (insert/update) doclist. If the argument is a directory, all files ending with .json are imported""") + def setup_translation(parser): parser.add_argument("--build_message_files", default=False, action="store_true", help="Build message files for translation.") parser.add_argument("--get_untranslated", nargs=2, metavar=("LANG-CODE", "TARGET-FILE-PATH"), help="""Get untranslated strings for lang.""") - parser.add_argument("--update_translations", nargs=3, + parser.add_argument("--update_translations", nargs=3, metavar=("LANG-CODE", "UNTRANSLATED-FILE-PATH", "TRANSLATED-FILE-PATH"), help="""Update translated strings.""") @@ -254,8 +272,10 @@ def use(sites_path): # install @cmd def install(db_name, root_login="root", root_password=None, source_sql=None, - admin_password = 'admin', verbose=True, force=False, site_config=None, reinstall=False): + admin_password = 'admin', verbose=True, force=False, site_config=None, reinstall=False, quiet=False): from frappe.installer import install_db, install_app, make_site_dirs + verbose = verbose or not quiet + install_db(root_login=root_login, root_password=root_password, db_name=db_name, source_sql=source_sql, admin_password = admin_password, verbose=verbose, force=force, site_config=site_config, reinstall=reinstall) make_site_dirs() @@ -263,7 +283,8 @@ def install(db_name, root_login="root", root_password=None, source_sql=None, frappe.destroy() @cmd -def install_app(app_name, verbose=False): +def install_app(app_name, verbose=True, quiet=False): + verbose = verbose or not quiet from frappe.installer import install_app frappe.connect() install_app(app_name, verbose=verbose) @@ -280,11 +301,21 @@ def add_to_installed_apps(*apps): frappe.destroy() @cmd -def reinstall(verbose=True): +def reinstall(verbose=True, quiet=False): + verbose = verbose or not quiet + try: + frappe.connect() + frappe.clear_cache() + except: + pass + finally: + frappe.db.close() + install(db_name=frappe.conf.db_name, verbose=verbose, force=True, reinstall=True) @cmd -def restore(db_name, source_sql, verbose=True, force=False): +def restore(db_name, source_sql, verbose=True, force=False, quiet=False): + verbose = verbose or not quiet install(db_name, source_sql=source_sql, verbose=verbose, force=force) @cmd @@ -294,7 +325,7 @@ def add_system_manager(email, first_name=None, last_name=None): frappe.utils.user.add_system_manager(email, first_name, last_name) frappe.db.commit() frappe.destroy() - + # utilities @cmd @@ -310,35 +341,37 @@ def update(remote=None, branch=None, reload_gunicorn=False): subprocess.check_output("killall -HUP gunicorn".split()) @cmd -def latest(verbose=True, rebuild_website_config=True): +def latest(verbose=True, rebuild_website_config=True, quiet=False): import frappe.modules.patch_handler import frappe.model.sync from frappe.website import rebuild_config from frappe.utils.fixtures import sync_fixtures import frappe.translate from frappe.website import statics - + + verbose = verbose or not quiet + frappe.connect() - + 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() - + frappe.model.sync.sync_all(verbose=verbose) + sync_fixtures() + + statics.sync().start() # build website config if any changes in templates etc. if rebuild_website_config: rebuild_config() - - statics.sync().start() - sync_fixtures() - + + frappe.translate.clear_cache() - + except frappe.modules.patch_handler.PatchError, e: print "\n".join(frappe.local.patch_log_list) raise @@ -346,10 +379,11 @@ def latest(verbose=True, rebuild_website_config=True): frappe.destroy() @cmd -def sync_all(force=False): +def sync_all(force=False, verbose=True, quiet=False): import frappe.model.sync + verbose = verbose or not quiet frappe.connect() - frappe.model.sync.sync_all(force=force) + frappe.model.sync.sync_all(force=force, verbose=verbose) frappe.destroy() @cmd @@ -360,14 +394,15 @@ def patch(patch_module, force=False): frappe.modules.patch_handler.run_single(patch_module, force=force) print "\n".join(frappe.local.patch_log_list) frappe.destroy() - + @cmd -def update_all_sites(remote=None, branch=None, verbose=True): +def update_all_sites(remote=None, branch=None, verbose=True, quiet=False): + verbose = verbose or not quiet pull(remote, branch) - + # maybe there are new framework changes, any consequences? reload(frappe) - + build() for site in get_sites(): frappe.init(site) @@ -392,8 +427,9 @@ def watch(): frappe.build.watch(True) @cmd -def backup(with_files=False, verbose=True, backup_path_db=None, backup_path_files=None): +def backup(with_files=False, verbose=True, backup_path_db=None, backup_path_files=None, quiet=False): from frappe.utils.backups import scheduled_backup + verbose = verbose or not quiet frappe.connect() odb = scheduled_backup(ignore_files=not with_files, backup_path_db=backup_path_db, backup_path_files=backup_path_files) if verbose: @@ -441,7 +477,7 @@ def domain(host_url=None): def make_conf(db_name=None, db_password=None, site_config=None): from frappe.install_lib.install import make_conf make_conf(db_name=db_name, db_password=db_password, site_config=site_config) - + @cmd def make_custom_server_script(doctype): from frappe.core.doctype.custom_script.custom_script import make_custom_server_script_file @@ -470,7 +506,7 @@ def build_sitemap(): frappe.connect() rebuild_config() frappe.destroy() - + @cmd def sync_statics(): from frappe.website import statics @@ -478,7 +514,7 @@ def sync_statics(): statics.sync_statics() frappe.db.commit() frappe.destroy() - + @cmd def reset_perms(): frappe.connect() @@ -499,7 +535,7 @@ def execute(method): @cmd def celery(arg): - import frappe + import frappe import commands, os python = commands.getoutput('which python') os.execv(python, [python, "-m", "frappe.celery_app"] + arg.split()) @@ -511,14 +547,14 @@ def run_scheduler_event(event, force=False): frappe.connect() frappe.utils.scheduler.trigger(frappe.local.site, event, now=force) frappe.destroy() - + # replace @cmd def replace(search_regex, replacement, extn, force=False): print search_regex, replacement, extn replace_code('.', search_regex, replacement, extn, force=force) - -# import/export + +# import/export @cmd def export_doc(doctype, docname): import frappe.modules @@ -532,7 +568,7 @@ def export_doclist(doctype, name, path): frappe.connect() data_import_tool.export_json(doctype, name, path) frappe.destroy() - + @cmd def export_csv(doctype, path): from frappe.core.page.data_import_tool import data_import_tool @@ -548,12 +584,12 @@ def export_fixtures(): frappe.destroy() @cmd -def import_doclist(path, force=False): +def import_doc(path, force=False): from frappe.core.page.data_import_tool import data_import_tool frappe.connect() - data_import_tool.import_doclist(path, overwrite=force) + data_import_tool.import_doc(path, overwrite=force) frappe.destroy() - + # translation @cmd def build_message_files(): @@ -589,11 +625,11 @@ def get_remote_and_branch(remote=None, branch=None): if not (remote and branch): if not frappe.conf.branch: raise Exception("Please specify remote and branch") - + remote = remote or "origin" branch = branch or frappe.conf.branch frappe.destroy() - + return remote, branch @cmd @@ -629,7 +665,7 @@ def set_admin_password(admin_password): @cmd def mysql(): - import frappe + import frappe import commands, os msq = commands.getoutput('which mysql') os.execv(msq, [msq, '-u', frappe.conf.db_name, '-p'+frappe.conf.db_password, frappe.conf.db_name, '-h', frappe.conf.db_host or "localhost", "-A"]) @@ -637,7 +673,7 @@ def mysql(): @cmd def python(site): - import frappe + import frappe import commands, os python = commands.getoutput('which python') if site: @@ -660,33 +696,21 @@ 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, profile=False): +def run_tests(app=None, module=None, doctype=None, verbose=False, quiet=True, tests=()): import frappe.test_runner + verbose = verbose or not quiet - def _run(): - ret = frappe.test_runner.main(app and app[0], module and module[0], doctype and doctype[0], verbose) - if len(ret.failures) > 0 or len(ret.errors) > 0: - exit(1) + ret = frappe.test_runner.main(app and app[0], module and module[0], doctype and doctype[0], verbose, + tests=tests) - if profile: - import cProfile, pstats, StringIO - pr = cProfile.Profile() - pr.enable() - _run() - pr.disable() - s = StringIO.StringIO() - sortby = 'cumulative' - ps = pstats.Stats(pr, stream=s).sort_stats(sortby) - ps.print_stats() - print s.getvalue() - else: - _run() + if len(ret.failures) > 0 or len(ret.errors) > 0: + return 1 @cmd def serve(port=8000, profile=False, sites_path='.', site=None): import frappe.app frappe.app.serve(port=port, profile=profile, site=frappe.local.site, sites_path=sites_path) - + @cmd def request(args): import frappe.handler @@ -696,12 +720,12 @@ def request(args): frappe.local.form_dict = frappe._dict([a.split("=") for a in args.split("?")[-1].split("&")]) else: frappe.local.form_dict = frappe._dict() - + if args.startswith("/api/method"): frappe.local.form_dict.cmd = args.split("?")[0].split("/")[-1] - + frappe.handler.execute_cmd(frappe.form_dict.cmd) - + print frappe.response frappe.destroy() @@ -722,7 +746,7 @@ def replace_code(start, txt1, txt2, extn, search=None, force=False): fpath = os.path.join(wt[0], fn) with open(fpath, 'r') as f: content = f.read() - + if re.search(search, content): res = search_replace_with_prompt(fpath, txt1, txt2, force) if res == 'skip': @@ -757,13 +781,15 @@ def search_replace_with_prompt(fpath, txt1, txt2, force=False): print colored('Updated', 'green') @cmd -def get_site_status(verbose=False): +def get_site_status(verbose=False, quiet=True): import frappe import frappe.utils from frappe.utils.user import get_system_managers from frappe.core.doctype.user.user import get_total_users, get_active_users, \ get_website_users, get_active_website_users - + + verbose = verbose or not quiet + import json frappe.connect() ret = { @@ -777,40 +803,39 @@ def get_site_status(verbose=False): 'disk_usage': frappe.utils.get_disk_usage(), 'working_directory': frappe.local.site_path } - + # country, timezone, industry - control_panel_details = frappe.db.get_value("Control Panel", "Control Panel", - ["country", "time_zone", "industry"], as_dict=True) - if control_panel_details: - ret.update(control_panel_details) - + for key in ["country", "time_zone", "industry"]: + ret[key] = frappe.db.get_default(key) + # basic usage/progress analytics for doctype in ("Company", "Customer", "Item", "Quotation", "Sales Invoice", "Journal Voucher", "Stock Ledger Entry"): key = doctype.lower().replace(" ", "_") + "_exists" ret[key] = 1 if frappe.db.count(doctype) else 0 - + frappe.destroy() - + if verbose: print json.dumps(ret, indent=1, sort_keys=True) - + return ret @cmd -def update_site_config(site_config, verbose=False): +def update_site_config(site_config, verbose=False, quiet=True): import json - + verbose = verbose or not quiet + if isinstance(site_config, basestring): site_config = json.loads(site_config) - + config = frappe.get_site_config() config.update(site_config) site_config_path = os.path.join(frappe.local.site_path, "site_config.json") - + with open(site_config_path, "w") as f: json.dump(config, f, indent=1, sort_keys=True) - + frappe.destroy() @cmd @@ -836,14 +861,14 @@ def bump(repo, bump_type): elif version_type == 'patch': v.patch += 1 return unicode(v) - + def add_tag(repo_path, version): import git repo = git.Repo(repo_path) repo.index.add(['config.json']) repo.index.commit('bumped to version {}'.format(version)) repo.create_tag('v' + version, repo.head) - + def update_framework_requirement(version): with open('app/config.json') as f: config = json.load(f) @@ -875,7 +900,9 @@ def bump(repo, bump_type): update_framework_requirement(new_version) bump('app', bump_type) - + if __name__=="__main__": - main() + out = main() + if out and out==1: + exit(1) diff --git a/frappe/client.py b/frappe/client.py index b6bde9e342..f11c55bcfd 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -14,7 +14,7 @@ def get(doctype, name=None, filters=None): name = frappe.db.get_value(doctype, json.loads(filters)) if not name: raise Exception, "No document found for given filters" - return [d.fields for d in frappe.bean(doctype, name).doclist] + return frappe.get_doc(doctype, name).as_dict() @frappe.whitelist() def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False): @@ -32,16 +32,16 @@ def set_value(doctype, name, fieldname, value): doc = frappe.db.get_value(doctype, name, ["parenttype", "parent"], as_dict=True) if doc and doc.parent: - bean = frappe.bean(doc.parenttype, doc.parent) - child = bean.doclist.getone({"doctype": doctype, "name": name}) - child.fields[fieldname] = value + doc = frappe.get_doc(doc.parenttype, doc.parent) + child = doc.getone({"doctype": doctype, "name": name}) + child.set(fieldname, value) else: - bean = frappe.bean(doctype, name) - bean.doc.fields[fieldname] = value + doc = frappe.get_doc(doctype, name) + doc.set(fieldname, value) - bean.save() + doc.save() - return [d.fields for d in bean.doclist] + return doc.as_dict() @frappe.whitelist() def insert(doclist): @@ -54,21 +54,21 @@ def insert(doclist): if doclist[0].get("parent") and doclist[0].get("parenttype"): # inserting a child record d = doclist[0] - bean = frappe.bean(d["parenttype"], d["parent"]) - bean.doclist.append(d) - bean.save() + doc = frappe.get_doc(d["parenttype"], d["parent"]) + doc.append(d) + doc.save() return [d] else: - bean = frappe.bean(doclist).insert() - return [d.fields for d in bean.doclist] + doc = frappe.get_doc(doclist).insert() + return doc.as_dict() @frappe.whitelist() def save(doclist): if isinstance(doclist, basestring): doclist = json.loads(doclist) - bean = frappe.bean(doclist).save() - return [d.fields for d in bean.doclist] + doc = frappe.get_doc(doclist).save() + return doc.as_dict() @frappe.whitelist() def rename_doc(doctype, old_name, new_name, merge=False): @@ -80,17 +80,17 @@ def submit(doclist): if isinstance(doclist, basestring): doclist = json.loads(doclist) - doclistobj = frappe.bean(doclist) + doclistobj = frappe.get_doc(doclist) doclistobj.submit() - return [d.fields for d in doclist] + return doclist.as_dict() @frappe.whitelist() def cancel(doctype, name): - wrapper = frappe.bean(doctype, name) + wrapper = frappe.get_doc(doctype, name) wrapper.cancel() - return [d.fields for d in wrapper.doclist] + return wrapper.as_dict() @frappe.whitelist() def delete(doctype, name): @@ -104,11 +104,9 @@ def set_default(key, value, parent=None): @frappe.whitelist() def make_width_property_setter(): - doclist = json.loads(frappe.form_dict.doclist) - if doclist[0]["doctype"]=="Property Setter" and doclist[0]["property"]=="width": - bean = frappe.bean(doclist) - bean.ignore_permissions = True - bean.insert() + doc = json.loads(frappe.form_dict) + if doc["doctype"]=="Property Setter" and doc["property"]=="width": + frappe.get_doc(doc).insert(ignore_permissions = True) @frappe.whitelist() def bulk_update(docs): @@ -119,9 +117,9 @@ def bulk_update(docs): ddoc = {key: val for key, val in doc.iteritems() if key not in ['doctype', 'docname']} doctype = doc['doctype'] docname = doc['docname'] - bean = frappe.bean(doctype, docname) - bean.doc.update(ddoc) - bean.save() + doc = frappe.get_doc(doctype, docname) + doc.update(ddoc) + doc.save() except: failed_docs.append({ 'doc': doc, diff --git a/frappe/config/setup.py b/frappe/config/setup.py index 3db8336266..5c5b98d75c 100644 --- a/frappe/config/setup.py +++ b/frappe/config/setup.py @@ -101,6 +101,17 @@ data = [ }, ] }, + { + "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", diff --git a/frappe/core/doctype/bulk_email/bulk_email.json b/frappe/core/doctype/bulk_email/bulk_email.json new file mode 100644 index 0000000000..782686f625 --- /dev/null +++ b/frappe/core/doctype/bulk_email/bulk_email.json @@ -0,0 +1,74 @@ +{ + "creation": "2012-08-02 15:17:28.000000", + "description": "Bulk Email records.", + "docstatus": 0, + "doctype": "DocType", + "document_type": "System", + "fields": [ + { + "fieldname": "sender", + "fieldtype": "Data", + "label": "Sender", + "permlevel": 0 + }, + { + "fieldname": "recipient", + "fieldtype": "Data", + "label": "Recipient", + "permlevel": 0 + }, + { + "fieldname": "message", + "fieldtype": "Long Text", + "label": "Message", + "permlevel": 0 + }, + { + "fieldname": "status", + "fieldtype": "Data", + "label": "Status", + "permlevel": 0 + }, + { + "fieldname": "error", + "fieldtype": "Text", + "label": "Error", + "permlevel": 0 + }, + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "label": "Reference DocType", + "options": "DocType", + "permlevel": 0, + "read_only": 1, + "reqd": 0 + }, + { + "fieldname": "ref_docname", + "fieldtype": "Data", + "label": "Reference DocName", + "permlevel": 0, + "read_only": 1, + "reqd": 0 + } + ], + "icon": "icon-envelope", + "idx": 1, + "in_create": 1, + "modified": "2014-02-12 21:11:05.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Bulk Email", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "System Manager" + } + ], + "read_only": 1 +} \ No newline at end of file diff --git a/frappe/core/doctype/bulk_email/bulk_email.py b/frappe/core/doctype/bulk_email/bulk_email.py index a48f49f2a7..3aa353233a 100644 --- a/frappe/core/doctype/bulk_email/bulk_email.py +++ b/frappe/core/doctype/bulk_email/bulk_email.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class BulkEmail(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/bulk_email/bulk_email.txt b/frappe/core/doctype/bulk_email/bulk_email.txt deleted file mode 100644 index c47da6d4d3..0000000000 --- a/frappe/core/doctype/bulk_email/bulk_email.txt +++ /dev/null @@ -1,93 +0,0 @@ -[ - { - "creation": "2012-08-02 15:17:28", - "docstatus": 0, - "modified": "2014-02-12 21:11:05", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "description": "Bulk Email records.", - "doctype": "DocType", - "document_type": "System", - "icon": "icon-envelope", - "in_create": 1, - "module": "Core", - "name": "__common__", - "read_only": 1 - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Bulk Email", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Bulk Email", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "role": "System Manager" - }, - { - "doctype": "DocType", - "name": "Bulk Email" - }, - { - "doctype": "DocField", - "fieldname": "sender", - "fieldtype": "Data", - "label": "Sender" - }, - { - "doctype": "DocField", - "fieldname": "recipient", - "fieldtype": "Data", - "label": "Recipient" - }, - { - "doctype": "DocField", - "fieldname": "message", - "fieldtype": "Long Text", - "label": "Message" - }, - { - "doctype": "DocField", - "fieldname": "status", - "fieldtype": "Data", - "label": "Status" - }, - { - "doctype": "DocField", - "fieldname": "error", - "fieldtype": "Text", - "label": "Error" - }, - { - "doctype": "DocField", - "fieldname": "ref_doctype", - "fieldtype": "Link", - "label": "Reference DocType", - "options": "DocType", - "read_only": 1, - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "ref_docname", - "fieldtype": "Data", - "label": "Reference DocName", - "read_only": 1, - "reqd": 1 - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/comment/comment.json b/frappe/core/doctype/comment/comment.json new file mode 100644 index 0000000000..9dcff41adb --- /dev/null +++ b/frappe/core/doctype/comment/comment.json @@ -0,0 +1,118 @@ +{ + "autoname": "CWR/.#####", + "creation": "2012-08-08 10:40:11.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "comment", + "fieldtype": "Text", + "label": "Comment", + "no_copy": 0, + "oldfieldname": "comment", + "oldfieldtype": "Text", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "comment_by", + "fieldtype": "Data", + "label": "Comment By", + "no_copy": 0, + "oldfieldname": "comment_by", + "oldfieldtype": "Data", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "comment_by_fullname", + "fieldtype": "Data", + "label": "Comment By Fullname", + "no_copy": 0, + "oldfieldname": "comment_by_fullname", + "oldfieldtype": "Data", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "comment_date", + "fieldtype": "Date", + "label": "Comment Date", + "no_copy": 0, + "oldfieldname": "comment_date", + "oldfieldtype": "Date", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "comment_time", + "fieldtype": "Data", + "label": "Comment Time", + "no_copy": 0, + "oldfieldname": "comment_time", + "oldfieldtype": "Data", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "comment_doctype", + "fieldtype": "Data", + "label": "Comment Doctype", + "no_copy": 0, + "oldfieldname": "comment_doctype", + "oldfieldtype": "Data", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "comment_docname", + "fieldtype": "Data", + "label": "Comment Docname", + "no_copy": 0, + "oldfieldname": "comment_docname", + "oldfieldtype": "Data", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "post_topic", + "fieldtype": "Data", + "label": "Post Topic", + "no_copy": 0, + "oldfieldname": "post_topic", + "oldfieldtype": "Data", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "unsubscribed", + "fieldtype": "Check", + "label": "Unsubscribed", + "permlevel": 0 + } + ], + "icon": "icon-comments", + "idx": 1, + "issingle": 0, + "modified": "2014-01-24 13:00:20.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Comment", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + } + ], + "title_field": "comment" +} \ No newline at end of file diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index 145625131a..536bc66425 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -5,39 +5,42 @@ from __future__ import unicode_literals import frappe, json from frappe.website.render import clear_cache -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class Comment(Document): def validate(self): if frappe.db.sql("""select count(*) from tabComment where comment_doctype=%s - and comment_docname=%s""", (self.doc.doctype, self.doc.name))[0][0] >= 50: + and comment_docname=%s""", (self.doctype, self.name))[0][0] >= 50: frappe.msgprint("Max Comments reached!", raise_exception=True) def on_update(self): self.update_comment_in_doc() def update_comment_in_doc(self): - if self.doc.comment_doctype and self.doc.comment_docname and self.doc.comment: + if self.comment_doctype and self.comment_docname and self.comment: try: _comments = self.get_comments_from_parent() updated = False for c in _comments: - if c.get("name")==self.doc.name: - c["comment"] = self.doc.comment + if c.get("name")==self.name: + c["comment"] = self.comment updated = True if not updated: _comments.append({ - "comment": self.doc.comment, - "by": self.doc.comment_by or self.doc.owner, - "name":self.doc.name + "comment": self.comment, + "by": self.comment_by or self.owner, + "name":self.name }) self.update_comments_in_parent(_comments) except Exception, e: if e.args[0]==1054: + if frappe.flags.in_test: + return + from frappe.model.db_schema import add_column - add_column(self.doc.comment_doctype, "_comments", "Text") + add_column(self.comment_doctype, "_comments", "Text") self.update_comment_in_doc() elif e.args[0]==1146: # no table @@ -46,18 +49,18 @@ class DocType: raise def get_comments_from_parent(self): - _comments = frappe.db.get_value(self.doc.comment_doctype, - self.doc.comment_docname, "_comments") or "[]" + _comments = frappe.db.get_value(self.comment_doctype, + self.comment_docname, "_comments") or "[]" return json.loads(_comments) def update_comments_in_parent(self, _comments): # use sql, so that we do not mess with the timestamp - frappe.db.sql("""update `tab%s` set `_comments`=%s where name=%s""" % (self.doc.comment_doctype, - "%s", "%s"), (json.dumps(_comments), self.doc.comment_docname)) + frappe.db.sql("""update `tab%s` set `_comments`=%s where name=%s""" % (self.comment_doctype, + "%s", "%s"), (json.dumps(_comments), self.comment_docname)) # clear parent cache if route exists: - route = frappe.db.get_value("Website Route", {"ref_doctype": self.doc.comment_doctype, - "docname": self.doc.comment_docname}) + route = frappe.db.get_value("Website Route", {"ref_doctype": self.comment_doctype, + "docname": self.comment_docname}) if route: clear_cache(route) @@ -65,7 +68,7 @@ class DocType: def on_trash(self): _comments = self.get_comments_from_parent() for c in _comments: - if c.get("name")==self.doc.name: + if c.get("name")==self.name: _comments.remove(c) self.update_comments_in_parent(_comments) diff --git a/frappe/core/doctype/comment/comment.txt b/frappe/core/doctype/comment/comment.txt deleted file mode 100644 index 2f44641886..0000000000 --- a/frappe/core/doctype/comment/comment.txt +++ /dev/null @@ -1,137 +0,0 @@ -[ - { - "creation": "2012-08-08 10:40:11", - "docstatus": 0, - "modified": "2014-01-24 13:00:20", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "CWR/.#####", - "doctype": "DocType", - "icon": "icon-comments", - "issingle": 0, - "module": "Core", - "name": "__common__", - "title_field": "comment" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Comment", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Comment", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Comment" - }, - { - "doctype": "DocField", - "fieldname": "comment", - "fieldtype": "Text", - "label": "Comment", - "no_copy": 0, - "oldfieldname": "comment", - "oldfieldtype": "Text", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "comment_by", - "fieldtype": "Data", - "label": "Comment By", - "no_copy": 0, - "oldfieldname": "comment_by", - "oldfieldtype": "Data", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "comment_by_fullname", - "fieldtype": "Data", - "label": "Comment By Fullname", - "no_copy": 0, - "oldfieldname": "comment_by_fullname", - "oldfieldtype": "Data", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "comment_date", - "fieldtype": "Date", - "label": "Comment Date", - "no_copy": 0, - "oldfieldname": "comment_date", - "oldfieldtype": "Date", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "comment_time", - "fieldtype": "Data", - "label": "Comment Time", - "no_copy": 0, - "oldfieldname": "comment_time", - "oldfieldtype": "Data", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "comment_doctype", - "fieldtype": "Data", - "label": "Comment Doctype", - "no_copy": 0, - "oldfieldname": "comment_doctype", - "oldfieldtype": "Data", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "comment_docname", - "fieldtype": "Data", - "label": "Comment Docname", - "no_copy": 0, - "oldfieldname": "comment_docname", - "oldfieldtype": "Data", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "post_topic", - "fieldtype": "Data", - "label": "Post Topic", - "no_copy": 0, - "oldfieldname": "post_topic", - "oldfieldtype": "Data", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "unsubscribed", - "fieldtype": "Check", - "label": "Unsubscribed" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/comment/test_comment.py b/frappe/core/doctype/comment/test_comment.py index 238ffc6513..7c6dc19543 100644 --- a/frappe/core/doctype/comment/test_comment.py +++ b/frappe/core/doctype/comment/test_comment.py @@ -9,7 +9,7 @@ import frappe, unittest, json # class TestComment(unittest.TestCase): # def setUp(self): # self.cleanup() -# self.test_rec = frappe.bean({ +# self.test_rec = frappe.get_doc({ # "doctype":"Event", # "subject":"__Comment Test Event", # "event_type": "Private", @@ -29,21 +29,21 @@ import frappe, unittest, json # frappe.db.sql("""alter table `tabEvent` drop column `_comments`""") # # def test_add_comment(self): -# self.comment = frappe.bean({ +# self.comment = frappe.get_doc({ # "doctype":"Comment", -# "comment_doctype": self.test_rec.doc.doctype, -# "comment_docname": self.test_rec.doc.name, +# "comment_doctype": self.test_rec.doctype, +# "comment_docname": self.test_rec.name, # "comment": "__Test Comment" # }).insert() # -# test_rec = frappe.doc(self.test_rec.doc.doctype, self.test_rec.doc.name) +# test_rec = frappe.get_doc(self.test_rec.doctype, self.test_rec.name) # _comments = json.loads(test_rec.get("_comments")) # self.assertTrue(_comments[0].get("comment")=="__Test Comment") # # def test_remove_comment(self): # self.test_add_comment() -# frappe.delete_doc("Comment", self.comment.doc.name) -# test_rec = frappe.doc(self.test_rec.doc.doctype, self.test_rec.doc.name) +# frappe.delete_doc("Comment", self.comment.name) +# test_rec = frappe.get_doc(self.test_rec.doctype, self.test_rec.name) # _comments = json.loads(test_rec.get("_comments")) # self.assertEqual(len(_comments), 0) # diff --git a/frappe/core/doctype/communication/communication.json b/frappe/core/doctype/communication/communication.json new file mode 100644 index 0000000000..508d44c62a --- /dev/null +++ b/frappe/core/doctype/communication/communication.json @@ -0,0 +1,233 @@ +{ + "allow_attach": 1, + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-01-29 10:47:14.000000", + "description": "Keep a track of all communications", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Master", + "fields": [ + { + "default": "COMM-", + "fieldname": "naming_series", + "fieldtype": "Select", + "hidden": 1, + "label": "Series", + "options": "COMM-", + "permlevel": 0 + }, + { + "fieldname": "sent_or_received", + "fieldtype": "Select", + "label": "Sent or Received", + "options": "Sent\nReceived", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "subject", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Subject", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "content", + "fieldtype": "Text Editor", + "label": "Content", + "permlevel": 0, + "reqd": 0, + "width": "400" + }, + { + "fieldname": "section_break1", + "fieldtype": "Section Break", + "options": "simple", + "permlevel": 0 + }, + { + "fieldname": "category", + "fieldtype": "Select", + "label": "Category", + "options": "\nSales\nComplaint\nHelp\nSuggestion\nMiscellaneous\nSent Mail", + "permlevel": 0, + "reqd": 0 + }, + { + "fieldname": "column_break2", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "next_communication_date", + "fieldtype": "Date", + "label": "Next Communcation On", + "permlevel": 0 + }, + { + "fieldname": "action", + "fieldtype": "Select", + "label": "Action", + "options": "\nCreated Opportunity\nSent Quotation\nCreated Support Ticket\nCreated Customer Issue\nNo Action\nSent Mail", + "permlevel": 0, + "reqd": 0 + }, + { + "fieldname": "additional_info", + "fieldtype": "Section Break", + "label": "Additional Info", + "permlevel": 0 + }, + { + "fieldname": "recipients", + "fieldtype": "Data", + "label": "Recipients", + "permlevel": 0 + }, + { + "fieldname": "sender", + "fieldtype": "Data", + "label": "Sender", + "permlevel": 0 + }, + { + "fieldname": "communication_medium", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Communication Medium", + "options": "\nChat\nPhone\nEmail\nSMS\nVisit\nOther", + "permlevel": 0 + }, + { + "fieldname": "phone_no", + "fieldtype": "Data", + "label": "Phone No.", + "permlevel": 0 + }, + { + "fieldname": "section_break2", + "fieldtype": "Section Break", + "options": "simple", + "permlevel": 0 + }, + { + "fieldname": "column_break4", + "fieldtype": "Column Break", + "label": "By", + "permlevel": 0 + }, + { + "default": "__user", + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "column_break5", + "fieldtype": "Column Break", + "label": "On", + "permlevel": 0 + }, + { + "default": "Today", + "fieldname": "communication_date", + "fieldtype": "Datetime", + "label": "Date", + "permlevel": 0 + }, + { + "fieldname": "_user_tags", + "fieldtype": "Data", + "hidden": 1, + "label": "User Tags", + "no_copy": 1, + "permlevel": 0, + "print_hide": 1 + } + ], + "icon": "icon-comment", + "idx": 1, + "in_dialog": 0, + "issingle": 0, + "modified": "2014-01-24 13:01:25.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Communication", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Support Team", + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Support Manager", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + } + ], + "title_field": "subject" +} \ No newline at end of file diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 42b1d8d43a..8c77a1f5bf 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -12,17 +12,15 @@ from frappe.utils.email_lib.email_body import get_email from frappe.utils.email_lib.smtp import send from frappe.utils import scrub_urls -class DocType(): - def __init__(self, doc, doclist=None): - self.doc = doc - self.doclist = doclist - - def get_parent_bean(self): - return frappe.bean(self.doc.parenttype, self.doc.parent) +from frappe.model.document import Document + +class Communication(Document): + def get_parent_doc(self): + return frappe.get_doc(self.parenttype, self.parent) def update_parent(self): """update status of parent Lead or Contact based on who is replying""" - observer = self.get_parent_bean().get_attr("on_communication") + observer = self.get_parent_doc().get_attr("on_communication") if observer: observer() @@ -60,8 +58,8 @@ def _make(doctype=None, name=None, content=None, subject=None, sent_or_received if isinstance(sender, (tuple, list)) and len(sender)==2: sender = formataddr(sender) - comm = frappe.new_bean('Communication') - d = comm.doc + comm = frappe.new_doc('Communication') + d = comm d.subject = subject d.content = content d.sent_or_received = sent_or_received @@ -69,7 +67,7 @@ def _make(doctype=None, name=None, content=None, subject=None, sent_or_received d.recipients = recipients # add as child - sent_via = frappe.get_obj(doctype, name) + sent_via = frappe.get_doc(doctype, name) d.parent = name d.parenttype = doctype d.parentfield = "communications" @@ -83,7 +81,7 @@ def _make(doctype=None, name=None, content=None, subject=None, sent_or_received comm.insert() if send_email: - d = comm.doc + d = comm send_comm_email(d, name, sent_via, print_html, attachments, send_me_a_copy) @frappe.whitelist() @@ -149,10 +147,10 @@ def set_portal_link(sent_via, comm): if is_signup_enabled() and hasattr(sent_via, "get_portal_page"): portal_page = sent_via.get_portal_page() if portal_page: - is_valid_recipient = cstr(sent_via.doc.email or sent_via.doc.email_id or - sent_via.doc.contact_email) in comm.recipients + is_valid_recipient = cstr(sent_via.email or sent_via.email_id or + sent_via.contact_email) in comm.recipients if is_valid_recipient: - url = "%s/%s?name=%s" % (get_url(), portal_page, urllib.quote(sent_via.doc.name)) + url = "%s/%s?name=%s" % (get_url(), portal_page, urllib.quote(sent_via.name)) footer = """
View this on our website""" % url diff --git a/frappe/core/doctype/communication/communication.txt b/frappe/core/doctype/communication/communication.txt deleted file mode 100644 index 2d6457e1df..0000000000 --- a/frappe/core/doctype/communication/communication.txt +++ /dev/null @@ -1,216 +0,0 @@ -[ - { - "creation": "2013-01-29 10:47:14", - "docstatus": 0, - "modified": "2014-01-24 13:01:25", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 1, - "allow_import": 1, - "autoname": "naming_series:", - "description": "Keep a track of all communications", - "doctype": "DocType", - "document_type": "Master", - "icon": "icon-comment", - "in_dialog": 0, - "issingle": 0, - "module": "Core", - "name": "__common__", - "title_field": "subject" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Communication", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Communication", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Communication" - }, - { - "default": "COMM-", - "doctype": "DocField", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 1, - "label": "Series", - "options": "COMM-" - }, - { - "doctype": "DocField", - "fieldname": "sent_or_received", - "fieldtype": "Select", - "label": "Sent or Received", - "options": "Sent\nReceived", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "subject", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Subject", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "content", - "fieldtype": "Text Editor", - "label": "Content", - "reqd": 0, - "width": "400" - }, - { - "doctype": "DocField", - "fieldname": "section_break1", - "fieldtype": "Section Break", - "options": "simple" - }, - { - "doctype": "DocField", - "fieldname": "category", - "fieldtype": "Select", - "label": "Category", - "options": "\nSales\nComplaint\nHelp\nSuggestion\nMiscellaneous\nSent Mail", - "reqd": 0 - }, - { - "doctype": "DocField", - "fieldname": "column_break2", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "next_communication_date", - "fieldtype": "Date", - "label": "Next Communcation On" - }, - { - "doctype": "DocField", - "fieldname": "action", - "fieldtype": "Select", - "label": "Action", - "options": "\nCreated Opportunity\nSent Quotation\nCreated Support Ticket\nCreated Customer Issue\nNo Action\nSent Mail", - "reqd": 0 - }, - { - "doctype": "DocField", - "fieldname": "additional_info", - "fieldtype": "Section Break", - "label": "Additional Info" - }, - { - "doctype": "DocField", - "fieldname": "recipients", - "fieldtype": "Data", - "label": "Recipients" - }, - { - "doctype": "DocField", - "fieldname": "sender", - "fieldtype": "Data", - "label": "Sender" - }, - { - "doctype": "DocField", - "fieldname": "communication_medium", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Communication Medium", - "options": "\nChat\nPhone\nEmail\nSMS\nVisit\nOther" - }, - { - "doctype": "DocField", - "fieldname": "phone_no", - "fieldtype": "Data", - "label": "Phone No." - }, - { - "doctype": "DocField", - "fieldname": "section_break2", - "fieldtype": "Section Break", - "options": "simple" - }, - { - "doctype": "DocField", - "fieldname": "column_break4", - "fieldtype": "Column Break", - "label": "By" - }, - { - "default": "__user", - "doctype": "DocField", - "fieldname": "user", - "fieldtype": "Link", - "label": "User", - "options": "User", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "column_break5", - "fieldtype": "Column Break", - "label": "On" - }, - { - "default": "Today", - "doctype": "DocField", - "fieldname": "communication_date", - "fieldtype": "Datetime", - "label": "Date" - }, - { - "doctype": "DocField", - "fieldname": "_user_tags", - "fieldtype": "Data", - "hidden": 1, - "label": "User Tags", - "no_copy": 1, - "print_hide": 1 - }, - { - "amend": 0, - "doctype": "DocPerm", - "role": "Support Team" - }, - { - "amend": 0, - "doctype": "DocPerm", - "role": "Sales Manager" - }, - { - "amend": 0, - "doctype": "DocPerm", - "role": "Sales User" - }, - { - "doctype": "DocPerm", - "role": "Support Manager" - }, - { - "doctype": "DocPerm", - "role": "System Manager" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/control_panel/README.md b/frappe/core/doctype/control_panel/README.md deleted file mode 100644 index ab8383486c..0000000000 --- a/frappe/core/doctype/control_panel/README.md +++ /dev/null @@ -1 +0,0 @@ -(deprecated) Single DocType where global variables used to be stored. \ No newline at end of file diff --git a/frappe/core/doctype/control_panel/__init__.py b/frappe/core/doctype/control_panel/__init__.py deleted file mode 100644 index e61caaf193..0000000000 --- a/frappe/core/doctype/control_panel/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals diff --git a/frappe/core/doctype/control_panel/control_panel.py b/frappe/core/doctype/control_panel/control_panel.py deleted file mode 100644 index 673b1a33be..0000000000 --- a/frappe/core/doctype/control_panel/control_panel.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals -import frappe - -from frappe import form, msgprint -import frappe.defaults - -class DocType: - def __init__(self, doc, doclist): - self.doc = doc - self.doclist = doclist - - def on_update(self): - # clear cache on save - frappe.cache().delete_value('time_zone') - frappe.clear_cache() diff --git a/frappe/core/doctype/control_panel/control_panel.txt b/frappe/core/doctype/control_panel/control_panel.txt deleted file mode 100644 index 6bdad0d86c..0000000000 --- a/frappe/core/doctype/control_panel/control_panel.txt +++ /dev/null @@ -1,226 +0,0 @@ -[ - { - "creation": "2013-01-10 16:34:01", - "docstatus": 0, - "modified": "2013-12-20 19:23:02", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_copy": 0, - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "in_create": 1, - "issingle": 1, - "istable": 0, - "module": "Core", - "name": "__common__", - "read_only": 0 - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Control Panel", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "doctype": "DocPerm", - "email": 1, - "export": 0, - "import": 0, - "name": "__common__", - "parent": "Control Panel", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Administrator", - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Control Panel" - }, - { - "doctype": "DocField", - "fieldname": "general", - "fieldtype": "Section Break", - "label": "General" - }, - { - "doctype": "DocField", - "fieldname": "date_format", - "fieldtype": "Select", - "label": "Date Format", - "options": "yyyy-mm-dd\ndd-mm-yyyy\ndd/mm/yyyy\nmm/dd/yyyy\nmm-dd-yyyy" - }, - { - "doctype": "DocField", - "fieldname": "currency_format", - "fieldtype": "Select", - "label": "Currency Format", - "options": "Millions\nLacs" - }, - { - "doctype": "DocField", - "fieldname": "password_expiry_days", - "fieldtype": "Int", - "label": "Password Expires in (days)" - }, - { - "description": "Format: hh:mm example for one hour expiry set as 01:00. \nMax expiry will be 72 hours. Default is 24 hours", - "doctype": "DocField", - "fieldname": "session_expiry", - "fieldtype": "Data", - "label": "Session Expires in (time)" - }, - { - "doctype": "DocField", - "fieldname": "column_break0", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "title", - "fieldtype": "Data", - "label": "Title" - }, - { - "doctype": "DocField", - "fieldname": "account_id", - "fieldtype": "Data", - "label": "Account Id" - }, - { - "doctype": "DocField", - "fieldname": "company_name", - "fieldtype": "Data", - "label": "Company Name" - }, - { - "doctype": "DocField", - "fieldname": "industry", - "fieldtype": "Data", - "label": "Industry" - }, - { - "doctype": "DocField", - "fieldname": "time_zone", - "fieldtype": "Data", - "label": "Time Zone" - }, - { - "doctype": "DocField", - "fieldname": "country", - "fieldtype": "Data", - "label": "Country" - }, - { - "doctype": "DocField", - "fieldname": "total_sms_sent", - "fieldtype": "Int", - "label": "Total SMS Sent", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "mail_server", - "fieldtype": "Section Break", - "label": "Mail Server" - }, - { - "description": "Email Id where users will send support request e.g. support@example.com", - "doctype": "DocField", - "fieldname": "support_email_id", - "fieldtype": "Data", - "label": "Support Email Id" - }, - { - "doctype": "DocField", - "fieldname": "letter_head_image", - "fieldtype": "Data", - "label": "Letter Head Image" - }, - { - "doctype": "DocField", - "fieldname": "column_break1", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "mail_footer", - "fieldtype": "Text", - "label": "Mail Footer" - }, - { - "doctype": "DocField", - "fieldname": "defaults", - "fieldtype": "Section Break", - "label": "Defaults" - }, - { - "doctype": "DocField", - "fieldname": "system_defaults", - "fieldtype": "Table", - "hidden": 0, - "label": "System Defaults", - "options": "DefaultValue", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "banner", - "fieldtype": "Section Break", - "label": "Banner" - }, - { - "doctype": "DocField", - "fieldname": "client_name", - "fieldtype": "Text", - "label": "Banner HTML" - }, - { - "doctype": "DocField", - "fieldname": "startup", - "fieldtype": "Section Break", - "label": "Startup" - }, - { - "doctype": "DocField", - "fieldname": "custom_startup_code", - "fieldtype": "Code", - "label": "Custom Startup Code" - }, - { - "doctype": "DocField", - "fieldname": "home_pages", - "fieldtype": "Section Break", - "label": "Home Pages" - }, - { - "doctype": "DocField", - "fieldname": "home_page", - "fieldtype": "Link", - "label": "Home Page", - "options": "Page" - }, - { - "doctype": "DocField", - "fieldname": "default_home_pages", - "fieldtype": "Table", - "label": "Default Home Pages", - "options": "Default Home Page" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/custom_field/custom_field.json b/frappe/core/doctype/custom_field/custom_field.json new file mode 100644 index 0000000000..cb0c3ad2c1 --- /dev/null +++ b/frappe/core/doctype/custom_field/custom_field.json @@ -0,0 +1,294 @@ +{ + "creation": "2013-01-10 16:34:01.000000", + "description": "Adds a custom field to a DocType", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "dt", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "label": "Document", + "no_copy": 0, + "oldfieldname": "dt", + "oldfieldtype": "Link", + "options": "DocType", + "permlevel": 0, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_filter": 1, + "label": "Label", + "no_copy": 1, + "oldfieldname": "label", + "oldfieldtype": "Data", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "label_help", + "fieldtype": "HTML", + "label": "Label Help", + "oldfieldtype": "HTML", + "permlevel": 0 + }, + { + "description": "Select the label after which you want to insert new field.", + "fieldname": "insert_after", + "fieldtype": "Select", + "label": "Insert After", + "no_copy": 1, + "oldfieldname": "insert_after", + "oldfieldtype": "Select", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "fieldtype", + "fieldtype": "Select", + "in_filter": 1, + "in_list_view": 1, + "label": "Field Type", + "no_copy": 0, + "oldfieldname": "fieldtype", + "oldfieldtype": "Select", + "options": "Button\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime", + "permlevel": 0, + "reqd": 1, + "search_index": 0 + }, + { + "fieldname": "options_help", + "fieldtype": "HTML", + "label": "Options Help", + "oldfieldtype": "HTML", + "permlevel": 0 + }, + { + "fieldname": "options", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Options", + "no_copy": 0, + "oldfieldname": "options", + "oldfieldtype": "Text", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "description", + "fieldtype": "Text", + "label": "Field Description", + "no_copy": 0, + "oldfieldname": "description", + "oldfieldtype": "Text", + "permlevel": 0, + "print_width": "300px", + "search_index": 0, + "width": "300px" + }, + { + "fieldname": "fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Fieldname", + "no_copy": 1, + "oldfieldname": "fieldname", + "oldfieldtype": "Data", + "permlevel": 0, + "read_only": 1, + "search_index": 0 + }, + { + "fieldname": "properties", + "fieldtype": "Column Break", + "label": "Properties", + "oldfieldtype": "Column Break", + "permlevel": 0, + "print_width": "50%", + "width": "50%" + }, + { + "default": "0", + "fieldname": "permlevel", + "fieldtype": "Int", + "label": "Permission Level", + "no_copy": 0, + "oldfieldname": "permlevel", + "oldfieldtype": "Int", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "reqd", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Mandatory Field", + "no_copy": 0, + "oldfieldname": "reqd", + "oldfieldtype": "Check", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "read_only", + "fieldtype": "Check", + "label": "Read Only", + "permlevel": 0 + }, + { + "depends_on": "eval:doc.fieldtype===\"Link\"", + "fieldname": "ignore_restrictions", + "fieldtype": "Check", + "label": "Ignore Restrictions", + "permlevel": 0 + }, + { + "fieldname": "width", + "fieldtype": "Data", + "label": "Width", + "no_copy": 0, + "oldfieldname": "width", + "oldfieldtype": "Data", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "default", + "fieldtype": "Text", + "label": "Default Value", + "no_copy": 0, + "oldfieldname": "default", + "oldfieldtype": "Text", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "hidden", + "fieldtype": "Check", + "label": "Hidden", + "permlevel": 0 + }, + { + "fieldname": "depends_on", + "fieldtype": "Data", + "label": "Depends On", + "permlevel": 0 + }, + { + "fieldname": "print_hide", + "fieldtype": "Check", + "label": "Print Hide", + "no_copy": 0, + "oldfieldname": "print_hide", + "oldfieldtype": "Check", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "print_width", + "fieldtype": "Data", + "hidden": 1, + "label": "Print Width", + "no_copy": 1, + "permlevel": 0, + "print_hide": 1 + }, + { + "fieldname": "no_copy", + "fieldtype": "Check", + "label": "No Copy", + "no_copy": 0, + "oldfieldname": "no_copy", + "oldfieldtype": "Check", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "allow_on_submit", + "fieldtype": "Check", + "label": "Allow on Submit", + "no_copy": 0, + "oldfieldname": "allow_on_submit", + "oldfieldtype": "Check", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "in_filter", + "fieldtype": "Check", + "label": "In Report Filter", + "no_copy": 0, + "oldfieldname": "in_filter", + "oldfieldtype": "Check", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "in_list_view", + "fieldtype": "Check", + "label": "In List View", + "permlevel": 0 + }, + { + "fieldname": "report_hide", + "fieldtype": "Check", + "label": "Report Hide", + "no_copy": 0, + "oldfieldname": "report_hide", + "oldfieldtype": "Check", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "search_index", + "fieldtype": "Check", + "hidden": 1, + "label": "Index", + "no_copy": 1, + "permlevel": 0, + "print_hide": 1 + } + ], + "icon": "icon-glass", + "idx": 1, + "modified": "2014-01-20 17:48:31.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Custom Field", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + } + ], + "search_fields": "dt,label,fieldtype,options" +} \ No newline at end of file diff --git a/frappe/core/doctype/custom_field/custom_field.py b/frappe/core/doctype/custom_field/custom_field.py index e2520f0425..e83c7df6ad 100644 --- a/frappe/core/doctype/custom_field/custom_field.py +++ b/frappe/core/doctype/custom_field/custom_field.py @@ -6,60 +6,59 @@ import frappe from frappe.utils import cint, cstr from frappe import _ -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class CustomField(Document): def autoname(self): self.set_fieldname() - self.doc.name = self.doc.dt + "-" + self.doc.fieldname + self.name = self.dt + "-" + self.fieldname def set_fieldname(self): - if not self.doc.fieldname: - if not self.doc.label: + if not self.fieldname: + if not self.label: frappe.throw(_("Label is mandatory")) # remove special characters from fieldname - self.doc.fieldname = filter(lambda x: x.isdigit() or x.isalpha() or '_', - cstr(self.doc.label).lower().replace(' ','_')) + self.fieldname = filter(lambda x: x.isdigit() or x.isalpha() or '_', + cstr(self.label).lower().replace(' ','_')) def validate(self): - from frappe.model.doctype import get - temp_doclist = get(self.doc.dt).get_parent_doclist() - - # set idx - if not self.doc.idx: - max_idx = max(d.idx for d in temp_doclist if d.doctype=='DocField') - self.doc.idx = cint(max_idx) + 1 + 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 - validate_fields_for_doctype(self.doc.dt) + validate_fields_for_doctype(self.dt) - frappe.clear_cache(doctype=self.doc.dt) + frappe.clear_cache(doctype=self.dt) # create property setter to emulate insert after self.create_property_setter() # update the schema - from frappe.model.db_schema import updatedb - updatedb(self.doc.dt) - + 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("""\ DELETE FROM `tabProperty Setter` WHERE doc_type = %s AND field_name = %s""", - (self.doc.dt, self.doc.fieldname)) + (self.dt, self.fieldname)) - frappe.clear_cache(doctype=self.doc.dt) + frappe.clear_cache(doctype=self.dt) def create_property_setter(self): - if not self.doc.insert_after: return - idx_label_list, field_list = get_fields_label(self.doc.dt, 0) - label_index = idx_label_list.index(self.doc.insert_after) + 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] @@ -67,11 +66,11 @@ class DocType: DELETE FROM `tabProperty Setter` WHERE doc_type = %s AND field_name = %s - AND property = 'previous_field'""", (self.doc.dt, self.doc.fieldname)) + AND property = 'previous_field'""", (self.dt, self.fieldname)) frappe.make_property_setter({ - "doctype":self.doc.dt, - "fieldname": self.doc.fieldname, + "doctype":self.dt, + "fieldname": self.fieldname, "property": "previous_field", "value": prev_field }) @@ -84,16 +83,13 @@ def get_fields_label(dt=None, form=1): """ import frappe from frappe.utils import cstr - import frappe.model.doctype fieldname = None if not dt: dt = frappe.form_dict.get('doctype') fieldname = frappe.form_dict.get('fieldname') if not dt: return "" - doclist = frappe.model.doctype.get(dt) - docfields = sorted(doclist.get({"parent": dt, "doctype": "DocField"}), - key=lambda d: d.idx) + docfields = frappe.get_meta(dt).get("fields") if fieldname: idx_label_list = [cstr(d.label) or cstr(d.fieldname) or cstr(d.fieldtype) @@ -114,7 +110,7 @@ def create_custom_field_if_values_exist(doctype, df): 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.bean({ + frappe.get_doc({ "doctype":"Custom Field", "dt": doctype, "permlevel": df.permlevel or 0, diff --git a/frappe/core/doctype/custom_field/custom_field.txt b/frappe/core/doctype/custom_field/custom_field.txt deleted file mode 100644 index 75ae2da1ea..0000000000 --- a/frappe/core/doctype/custom_field/custom_field.txt +++ /dev/null @@ -1,304 +0,0 @@ -[ - { - "creation": "2013-01-10 16:34:01", - "docstatus": 0, - "modified": "2014-01-20 17:48:31", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "description": "Adds a custom field to a DocType", - "doctype": "DocType", - "icon": "icon-glass", - "module": "Core", - "name": "__common__", - "search_fields": "dt,label,fieldtype,options" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Custom Field", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Custom Field", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Custom Field" - }, - { - "doctype": "DocField", - "fieldname": "dt", - "fieldtype": "Link", - "in_filter": 1, - "in_list_view": 1, - "label": "Document", - "no_copy": 0, - "oldfieldname": "dt", - "oldfieldtype": "Link", - "options": "DocType", - "reqd": 1, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "label", - "fieldtype": "Data", - "in_filter": 1, - "label": "Label", - "no_copy": 1, - "oldfieldname": "label", - "oldfieldtype": "Data", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "label_help", - "fieldtype": "HTML", - "label": "Label Help", - "oldfieldtype": "HTML" - }, - { - "description": "Select the label after which you want to insert new field.", - "doctype": "DocField", - "fieldname": "insert_after", - "fieldtype": "Select", - "label": "Insert After", - "no_copy": 1, - "oldfieldname": "insert_after", - "oldfieldtype": "Select", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "fieldtype", - "fieldtype": "Select", - "in_filter": 1, - "in_list_view": 1, - "label": "Field Type", - "no_copy": 0, - "oldfieldname": "fieldtype", - "oldfieldtype": "Select", - "options": "Button\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime", - "reqd": 1, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "options_help", - "fieldtype": "HTML", - "label": "Options Help", - "oldfieldtype": "HTML" - }, - { - "doctype": "DocField", - "fieldname": "options", - "fieldtype": "Text", - "in_list_view": 1, - "label": "Options", - "no_copy": 0, - "oldfieldname": "options", - "oldfieldtype": "Text", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "description", - "fieldtype": "Text", - "label": "Field Description", - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Text", - "print_width": "300px", - "search_index": 0, - "width": "300px" - }, - { - "doctype": "DocField", - "fieldname": "fieldname", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Fieldname", - "no_copy": 1, - "oldfieldname": "fieldname", - "oldfieldtype": "Data", - "read_only": 1, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "properties", - "fieldtype": "Column Break", - "label": "Properties", - "oldfieldtype": "Column Break", - "print_width": "50%", - "width": "50%" - }, - { - "default": "0", - "doctype": "DocField", - "fieldname": "permlevel", - "fieldtype": "Int", - "label": "Permission Level", - "no_copy": 0, - "oldfieldname": "permlevel", - "oldfieldtype": "Int", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "reqd", - "fieldtype": "Check", - "in_list_view": 1, - "label": "Is Mandatory Field", - "no_copy": 0, - "oldfieldname": "reqd", - "oldfieldtype": "Check", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "read_only", - "fieldtype": "Check", - "label": "Read Only" - }, - { - "depends_on": "eval:doc.fieldtype===\"Link\"", - "doctype": "DocField", - "fieldname": "ignore_restrictions", - "fieldtype": "Check", - "label": "Ignore Restrictions" - }, - { - "doctype": "DocField", - "fieldname": "width", - "fieldtype": "Data", - "label": "Width", - "no_copy": 0, - "oldfieldname": "width", - "oldfieldtype": "Data", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "default", - "fieldtype": "Text", - "label": "Default Value", - "no_copy": 0, - "oldfieldname": "default", - "oldfieldtype": "Text", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "hidden", - "fieldtype": "Check", - "label": "Hidden" - }, - { - "doctype": "DocField", - "fieldname": "depends_on", - "fieldtype": "Data", - "label": "Depends On" - }, - { - "doctype": "DocField", - "fieldname": "print_hide", - "fieldtype": "Check", - "label": "Print Hide", - "no_copy": 0, - "oldfieldname": "print_hide", - "oldfieldtype": "Check", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "print_width", - "fieldtype": "Data", - "hidden": 1, - "label": "Print Width", - "no_copy": 1, - "print_hide": 1 - }, - { - "doctype": "DocField", - "fieldname": "no_copy", - "fieldtype": "Check", - "label": "No Copy", - "no_copy": 0, - "oldfieldname": "no_copy", - "oldfieldtype": "Check", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "allow_on_submit", - "fieldtype": "Check", - "label": "Allow on Submit", - "no_copy": 0, - "oldfieldname": "allow_on_submit", - "oldfieldtype": "Check", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "in_filter", - "fieldtype": "Check", - "label": "In Report Filter", - "no_copy": 0, - "oldfieldname": "in_filter", - "oldfieldtype": "Check", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "in_list_view", - "fieldtype": "Check", - "label": "In List View" - }, - { - "doctype": "DocField", - "fieldname": "report_hide", - "fieldtype": "Check", - "label": "Report Hide", - "no_copy": 0, - "oldfieldname": "report_hide", - "oldfieldtype": "Check", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "search_index", - "fieldtype": "Check", - "hidden": 1, - "label": "Index", - "no_copy": 1, - "print_hide": 1 - }, - { - "doctype": "DocPerm", - "role": "Administrator" - }, - { - "doctype": "DocPerm", - "role": "System Manager" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/custom_script/custom_script.json b/frappe/core/doctype/custom_script/custom_script.json new file mode 100644 index 0000000000..95c302f1f3 --- /dev/null +++ b/frappe/core/doctype/custom_script/custom_script.json @@ -0,0 +1,74 @@ +{ + "autoname": "CustomScript.####", + "creation": "2013-01-10 16:34:01.000000", + "description": "Adds a custom script (client or server) to a DocType", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "dt", + "fieldtype": "Link", + "label": "DocType", + "oldfieldname": "dt", + "oldfieldtype": "Link", + "options": "DocType", + "permlevel": 0 + }, + { + "default": "Client", + "fieldname": "script_type", + "fieldtype": "Select", + "hidden": 1, + "label": "Script Type", + "oldfieldname": "script_type", + "oldfieldtype": "Select", + "options": "Client", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "script", + "fieldtype": "Code", + "label": "Script", + "oldfieldname": "script", + "oldfieldtype": "Code", + "options": "Script", + "permlevel": 0 + } + ], + "icon": "icon-glass", + "idx": 1, + "modified": "2014-01-20 17:48:31.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Custom Script", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "submit": 0, + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/custom_script/custom_script.py b/frappe/core/doctype/custom_script/custom_script.py index 6907e986b7..4cc3954d29 100644 --- a/frappe/core/doctype/custom_script/custom_script.py +++ b/frappe/core/doctype/custom_script/custom_script.py @@ -4,16 +4,16 @@ from __future__ import unicode_literals import frappe from frappe.utils import cstr -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class CustomScript(Document): def autoname(self): - self.doc.name = self.doc.dt + "-" + self.doc.script_type + self.name = self.dt + "-" + self.script_type def on_update(self): - frappe.clear_cache(doctype=self.doc.dt) + frappe.clear_cache(doctype=self.dt) def on_trash(self): - frappe.clear_cache(doctype=self.doc.dt) + frappe.clear_cache(doctype=self.dt) diff --git a/frappe/core/doctype/custom_script/custom_script.txt b/frappe/core/doctype/custom_script/custom_script.txt deleted file mode 100644 index 72689ff9e4..0000000000 --- a/frappe/core/doctype/custom_script/custom_script.txt +++ /dev/null @@ -1,84 +0,0 @@ -[ - { - "creation": "2013-01-10 16:34:01", - "docstatus": 0, - "modified": "2014-01-20 17:48:31", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "CustomScript.####", - "description": "Adds a custom script (client or server) to a DocType", - "doctype": "DocType", - "icon": "icon-glass", - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Custom Script", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Custom Script", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Custom Script" - }, - { - "doctype": "DocField", - "fieldname": "dt", - "fieldtype": "Link", - "label": "DocType", - "oldfieldname": "dt", - "oldfieldtype": "Link", - "options": "DocType" - }, - { - "default": "Client", - "doctype": "DocField", - "fieldname": "script_type", - "fieldtype": "Select", - "hidden": 1, - "label": "Script Type", - "oldfieldname": "script_type", - "oldfieldtype": "Select", - "options": "Client", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "script", - "fieldtype": "Code", - "label": "Script", - "oldfieldname": "script", - "oldfieldtype": "Code", - "options": "Script" - }, - { - "doctype": "DocPerm", - "role": "System Manager" - }, - { - "doctype": "DocPerm", - "role": "Administrator" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/customize_form/customize_form.js b/frappe/core/doctype/customize_form/customize_form.js index 7c762bea25..fbe186376e 100644 --- a/frappe/core/doctype/customize_form/customize_form.js +++ b/frappe/core/doctype/customize_form/customize_form.js @@ -1,102 +1,87 @@ // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors // MIT License. See license.txt -$(cur_frm.wrapper).on("grid-row-render", function(e, grid_row) { - if(grid_row.doc && grid_row.doc.fieldtype=="Section Break") { - $(grid_row.row).css({"font-weight": "bold"}); - } -}) +frappe.provide("frappe.customize_form"); -cur_frm.cscript.doc_type = function() { - return cur_frm.call({ - method: "get", - doc: cur_frm.doc, - callback: function(r) { - cur_frm.refresh(); +frappe.ui.form.on("Customize Form", "onload", function(frm) { + frappe.customize_form.add_fields_help(frm); + + frm.set_query("doc_type", function() { + return { + filters: [ + ['DocType', 'issingle', '=', 0], + ['DocType', 'in_create', '=', 0], + ['DocType', 'name', 'not in', 'DocType, DocField, DocPerm, User, Role, UserRole, \ + Page, Page Role, Module Def, Print Format, Report, Customize Form, \ + Customize Form Field'] + ] + }; + }); + + $(frm.wrapper).on("grid-row-render", function(e, grid_row) { + if(grid_row.doc && grid_row.doc.fieldtype=="Section Break") { + $(grid_row.row).css({"font-weight": "bold"}); } }); -} +}); -cur_frm.cscript.onload = function(doc, dt, dn) { - cur_frm.fields_dict.fields.grid.cannot_add_rows = true; - cur_frm.add_fields_help(); -} - -cur_frm.fields_dict.doc_type.get_query = function(doc, dt, dn) { - return{ - filters:[ - ['DocType', 'issingle', '=', 0], - ['DocType', 'in_create', '=', 0], - ['DocType', 'name', 'not in', 'DocType, DocField, DocPerm, User, Role, UserRole,\ - Page, Page Role, Module Def, Print Format, Report, Customize Form, Customize Form Field'] - ] +frappe.ui.form.on("Customize Form", "doc_type", function(frm) { + if(frm.doc.doc_type) { + return frm.call({ + method: "fetch_to_customize", + doc: frm.doc, + callback: function(r) { + frm.refresh(); + } + }); } -} +}); -cur_frm.cscript.refresh = function() { - cur_frm.disable_save(); - cur_frm.frm_head.appframe.iconbar.clear("1"); - - cur_frm.appframe.set_title_right("Update", function() { - if(cur_frm.doc.doc_type) { - return cur_frm.call({ - doc: cur_frm.doc, - method: "post", - callback: function(r) { - if(!r.exc && r.server_messages) { - cur_frm.script_manager.trigger("doc_type"); - cur_frm.frm_head.set_label(['Updated', 'label-success']); +frappe.ui.form.on("Customize Form", "refresh", function(frm) { + frm.disable_save(); + frm.frm_head.appframe.iconbar.clear("1"); + + if(frm.doc.doc_type) { + frm.appframe.set_title_right("Update", function() { + if(frm.doc.doc_type) { + return frm.call({ + doc: frm.doc, + method: "save_customization", + callback: function(r) { + if(!r.exc) { + frappe.customize_form.clear_locals_and_refresh(frm); + } } - } - }); - } - }); + }); + } + }); - cur_frm.add_custom_button('Refresh Form', function() { - cur_frm.script_manager.trigger("doc_type"); - }, "icon-refresh"); + frm.add_custom_button('Refresh Form', function() { + frm.script_manager.trigger("doc_type"); + }, "icon-refresh"); - cur_frm.add_custom_button('Reset to defaults', function() { - cur_frm.confirm('This will remove the customizations defined for this form.

' - + 'Are you sure you want to reset to defaults?', cur_frm.doc, cur_frm.doctype, cur_frm.docname); - }, "icon-eraser"); - - if(!cur_frm.doc.doc_type) { - var frm_head = cur_frm.frm_head.appframe; - $(frm_head.buttons['Update']).prop('disabled', true); - $(frm_head.buttons['Refresh Form']).prop('disabled', true); - $(frm_head.buttons['Reset to defaults']).prop('disabled', true); + frm.add_custom_button('Reset to defaults', function() { + frappe.customize_form.confirm('This will remove the customizations defined for this form.

' + + 'Are you sure you want to reset to defaults?', frm); + }, "icon-eraser"); } - cur_frm.cscript.hide_allow_attach(cur_frm.doc); - + // if(!frm.doc.doc_type) { + // var frm_head = frm.frm_head.appframe; + // $(frm_head.buttons['Update']).prop('disabled', true); + // $(frm_head.buttons['Refresh Form']).prop('disabled', true); + // $(frm_head.buttons['Reset to defaults']).prop('disabled', true); + // } + if(frappe.route_options) { frappe.model.set_value("Customize Form", null, "doc_type", frappe.route_options.doctype) frappe.route_options = null; } -} +}); -cur_frm.cscript.hide_allow_attach = function(doc) { - var allow_attach_list = ['Website Settings', 'Web Page', 'Timesheet', 'Ticket', - 'Support Ticket', 'Supplier', 'Style Settings', 'Stock Reconciliation', - 'Stock Entry', 'Serial No', 'Sales Order', 'Sales Invoice', - 'Quotation', 'Question', 'Purchase Receipt', 'Purchase Order', - 'Project', 'User', 'Production Order', 'Product', 'Print Format', - 'Price List', 'Purchase Invoice', 'Page', 'Module Def', - 'Maintenance Visit', 'Maintenance Schedule', 'Letter Head', - 'Leave Application', 'Lead', 'Journal Voucher', 'Item', 'Material Request', - 'Expense Claim', 'Opportunity', 'Employee', 'Delivery Note', - 'Customer Issue', 'Customer', 'Contact Us Settings', 'Company', - 'Blog Post', 'BOM', 'About Us Settings', 'Batch']; +frappe.customize_form.confirm = function(msg, frm) { + if(!frm.doc.doc_type) return; - if(inList(allow_attach_list, doc.doc_type)) { - unhide_field('allow_attach'); - } else { - hide_field('allow_attach'); - } -} - -cur_frm.confirm = function(msg, doc, dt, dn) { var d = new frappe.ui.Dialog({ title: 'Reset To Defaults', width: 500 @@ -110,16 +95,15 @@ cur_frm.confirm = function(msg, doc, dt, dn) { $y(button_wrapper, {paddingTop: '15px'}); var proceed_btn = $btn(button_wrapper, 'Proceed', function() { - return cur_frm.call({ - doc: cur_frm.doc, - method: "delete", + return frm.call({ + doc: frm.doc, + method: "reset_to_defaults", callback: function(r) { if(r.exc) { msgprint(r.exc); } else { - cur_frm.confirm.dialog.hide(); - cur_frm.refresh(); - cur_frm.frm_head.set_label(['Saved', 'label-success']); + frappe.customize_form.confirm.dialog.hide(); + frappe.customize_form.clear_locals_and_refresh(frm); } } }); @@ -128,19 +112,26 @@ cur_frm.confirm = function(msg, doc, dt, dn) { $y(proceed_btn, {marginRight: '20px', fontWeight: 'bold'}); var cancel_btn = $btn(button_wrapper, 'Cancel', function() { - cur_frm.confirm.dialog.hide(); + frappe.customize_form.confirm.dialog.hide(); }); $(cancel_btn).addClass('btn-small btn-info'); $y(cancel_btn, {fontWeight: 'bold'}); - cur_frm.confirm.dialog = d; + frappe.customize_form.confirm.dialog = d; d.show(); } +frappe.customize_form.clear_locals_and_refresh = function(frm) { + // clear doctype from locals + frappe.model.clear_doc("DocType", frm.doc.doc_type); + delete frappe.meta.docfield_copy[frm.doc.doc_type]; + + frm.refresh(); +} -cur_frm.add_fields_help = function() { - $(cur_frm.grids[0].parent).before( +frappe.customize_form.add_fields_help = function(frm) { + $(frm.grids[0].parent).before( '
\ Help\
'); @@ -223,7 +214,7 @@ cur_frm.add_fields_help = function() { \ \ Press Esc to close\ \ \ @@ -235,7 +226,7 @@ cur_frm.add_fields_help = function() { d.show(); - cur_frm.fields_help_dialog = d; + frappe.customize_form.fields_help_dialog = d; }); } diff --git a/frappe/core/doctype/customize_form/customize_form.json b/frappe/core/doctype/customize_form/customize_form.json new file mode 100644 index 0000000000..5db77d1bbf --- /dev/null +++ b/frappe/core/doctype/customize_form/customize_form.json @@ -0,0 +1,133 @@ +{ + "autoname": "DL.####", + "creation": "2013-01-29 17:55:08.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "doc_type", + "fieldtype": "Link", + "hidden": 0, + "label": "Enter Form Type", + "no_copy": 0, + "options": "DocType", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "properties", + "fieldtype": "Section Break", + "label": "Properties", + "permlevel": 0 + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "description": "Fields separated by comma (,) will be included in the
Search By list of Search dialog box", + "fieldname": "search_fields", + "fieldtype": "Data", + "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": "column_break1", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "allow_print", + "fieldtype": "Check", + "label": "Hide Print", + "no_copy": 0, + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "allow_email", + "fieldtype": "Check", + "label": "Hide Email", + "no_copy": 0, + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "allow_copy", + "fieldtype": "Check", + "label": "Hide Copy", + "no_copy": 0, + "permlevel": 0, + "search_index": 0 + }, + { + "description": "Note: maximum attachment size = 1mb", + "fieldname": "allow_attach", + "fieldtype": "Check", + "label": "Allow Attach", + "no_copy": 0, + "permlevel": 0, + "search_index": 0 + }, + { + "depends_on": "eval:cint(doc.allow_attach)", + "fieldname": "max_attachments", + "fieldtype": "Int", + "label": "Max Attachments", + "no_copy": 0, + "permlevel": 0, + "search_index": 0 + }, + { + "description": "Customize Label, Print Hide, Default etc.", + "fieldname": "fields_section_break", + "fieldtype": "Section Break", + "label": "Fields", + "permlevel": 0 + }, + { + "fieldname": "customize_form_fields", + "fieldtype": "Table", + "label": "Fields", + "no_copy": 0, + "options": "Customize Form Field", + "permlevel": 0, + "search_index": 0 + } + ], + "hide_toolbar": 1, + "icon": "icon-glass", + "idx": 1, + "issingle": 1, + "modified": "2014-01-15 16:16:22.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Customize Form", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "System Manager", + "submit": 0, + "write": 1 + } + ], + "search_fields": "doc_type" +} \ No newline at end of file diff --git a/frappe/core/doctype/customize_form/customize_form.py b/frappe/core/doctype/customize_form/customize_form.py index 5401135212..675e60be92 100644 --- a/frappe/core/doctype/customize_form/customize_form.py +++ b/frappe/core/doctype/customize_form/customize_form.py @@ -7,345 +7,237 @@ from __future__ import unicode_literals Thus providing a better UI from user perspective """ import frappe, json +from frappe import _ from frappe.utils import cstr -class DocType: - def __init__(self, doc, doclist=[]): - self.doc, self.doclist = doc, doclist - self.doctype_properties = [ - 'search_fields', - 'default_print_format', - 'read_only_onload', - 'allow_print', - 'allow_email', - 'allow_copy', - 'allow_attach', - 'max_attachments' - ] - self.docfield_properties = [ - 'idx', - 'label', - 'fieldtype', - 'fieldname', - 'options', - 'permlevel', - 'width', - 'print_width', - 'reqd', - 'ignore_restrictions', - 'in_filter', - 'in_list_view', - 'hidden', - 'print_hide', - 'report_hide', - 'allow_on_submit', - 'depends_on', - 'description', - 'default', - 'name', - ] +from frappe.model.document import Document +from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype - self.property_restrictions = { - 'fieldtype': [['Currency', 'Float'], ['Small Text', 'Data'], ['Text', 'Text Editor', 'Code']], - } - - - def get(self): - """ - Gets DocFields applied with Property Setter customizations via Customize Form Field - """ - self.clear() - - if self.doc.doc_type: - from frappe.model.doc import addchild - - for d in self.get_ref_doclist(): - if d.doctype=='DocField': - new = addchild(self.doc, 'fields', 'Customize Form Field', - self.doclist) - self.set( - { - 'list': self.docfield_properties, - 'doc' : d, - 'doc_to_set': new - } - ) - elif d.doctype=='DocType': - self.set({ 'list': self.doctype_properties, 'doc': d }) - - - def get_ref_doclist(self): - """ - * Gets doclist of type self.doc.doc_type - * Applies property setter properties on the doclist - * returns the modified doclist - """ - from frappe.model.doctype import get - - ref_doclist = get(self.doc.doc_type) - ref_doclist = frappe.doclist([ref_doclist[0]] - + ref_doclist.get({"parent": self.doc.doc_type})) - - return ref_doclist - - - def clear(self): - """ - Clear fields in the doc - """ - # Clear table before adding new doctype's fields - self.doclist = self.doc.clear_table(self.doclist, 'fields') - self.set({ 'list': self.doctype_properties, 'value': None }) +class CustomizeForm(Document): + doctype_properties = { + 'search_fields': 'Data', + 'default_print_format': 'Data', + 'read_only_onload': 'Check', + 'allow_print': 'Check', + 'allow_email': 'Check', + 'allow_copy': 'Check', + 'allow_attach': 'Check', + 'max_attachments': 'Int' + } + docfield_properties = { + 'idx': 'Int', + 'label': 'Data', + 'fieldtype': 'Select', + 'options': 'Text', + 'permlevel': 'Int', + 'width': 'Data', + 'print_width': 'Data', + 'reqd': 'Check', + 'ignore_restrictions': 'Check', + 'in_filter': 'Check', + 'in_list_view': 'Check', + 'hidden': 'Check', + 'print_hide': 'Check', + 'report_hide': 'Check', + 'allow_on_submit': 'Check', + 'depends_on': 'Data', + 'description': 'Text', + 'default': 'Text' + } + + allowed_fieldtype_change = (('Currency', 'Float'), ('Small Text', 'Data'), + ('Text', 'Text Editor', 'Code')) + + def on_update(self): + frappe.db.sql("delete from tabSingles where doctype='Customize Form'") + frappe.db.sql("delete from `tabCustomize Form Field`") + + def fetch_to_customize(self): + self.clear_existing_doc() + if not self.doc_type: + return + + meta = frappe.get_meta(self.doc_type) - def set(self, args): - """ - Set a list of attributes of a doc to a value - or to attribute values of a doc passed - - args can contain: - * list --> list of attributes to set - * doc_to_set --> defaults to self.doc - * value --> to set all attributes to one value eg. None - * doc --> copy attributes from doc to doc_to_set - """ - if not 'doc_to_set' in args: - args['doc_to_set'] = self.doc - - if 'list' in args: - if 'value' in args: - for f in args['list']: - args['doc_to_set'].fields[f] = None - elif 'doc' in args: - for f in args['list']: - args['doc_to_set'].fields[f] = args['doc'].fields.get(f) - else: - frappe.msgprint("Please specify args['list'] to set", raise_exception=1) - - - def post(self): - """ - Save diff between Customize Form Bean and DocType Bean as property setter entries - """ - if self.doc.doc_type: - from frappe.model import doc - from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype - - this_doclist = frappe.doclist([self.doc] + self.doclist) - ref_doclist = self.get_ref_doclist() - dt_doclist = doc.get('DocType', self.doc.doc_type) - - # get a list of property setter docs - self.idx_dirty = False - diff_list = self.diff(this_doclist, ref_doclist, dt_doclist) - - if self.idx_dirty: - self.make_idx_property_setter(this_doclist, diff_list) - - self.set_properties(diff_list) - - validate_fields_for_doctype(self.doc.doc_type) - - frappe.clear_cache(doctype=self.doc.doc_type) - frappe.msgprint("Updated") - - - def diff(self, new_dl, ref_dl, dt_dl): - """ - Get difference between new_dl doclist and ref_dl doclist - then check how it differs from dt_dl i.e. default doclist - """ - import re - self.defaults = self.get_defaults() - diff_list = [] - for new_d in new_dl: - for ref_d in ref_dl: - if ref_d.doctype == 'DocField' and new_d.name == ref_d.name: - for prop in self.docfield_properties: - # do not set forbidden properties like idx - if prop=="idx": - if ref_d.idx != new_d.idx: - self.idx_dirty = True - continue - - # check if its custom field: - if ref_d.get("__custom_field"): - # update custom field - if self.has_property_changed(ref_d, new_d, prop): - # using set_value not bean because validations are called - # in the end anyways - frappe.db.set_value("Custom Field", ref_d.name, prop, new_d.get(prop)) - else: - d = self.prepare_to_set(prop, new_d, ref_d, dt_dl) - if d: diff_list.append(d) - break + # doctype properties + for property in self.doctype_properties: + self.set(property, meta.get(property)) + + for d in meta.get("fields"): + new_d = {"fieldname": d.fieldname, "is_custom_field": d.get("is_custom_field"), "name": d.name} + for property in self.docfield_properties: + new_d[property] = d.get(property) + self.append("customize_form_fields", new_d) - elif ref_d.doctype == 'DocType' and new_d.doctype == 'Customize Form': - for prop in self.doctype_properties: - d = self.prepare_to_set(prop, new_d, ref_d, dt_dl) - if d: diff_list.append(d) - break - - return diff_list - - - def get_defaults(self): - """ - Get fieldtype and default value for properties of a field - """ - df_defaults = frappe.db.sql(""" - SELECT fieldname, fieldtype, `default`, label - FROM `tabDocField` - WHERE parent='DocField' or parent='DocType'""", as_dict=1) + # NOTE doc is sent to clientside by run_method + + def clear_existing_doc(self): + doc_type = self.doc_type - defaults = {} - for d in df_defaults: - defaults[d['fieldname']] = d - defaults['idx'] = {'fieldname' : 'idx', 'fieldtype' : 'Int', 'default' : 1, 'label' : 'idx'} - defaults['previous_field'] = {'fieldname' : 'previous_field', 'fieldtype' : 'Data', 'default' : None, 'label' : 'Previous Field'} - return defaults - - - def has_property_changed(self, ref_d, new_d, prop): - return new_d.fields.get(prop) != ref_d.fields.get(prop) \ - and not \ - ( \ - new_d.fields.get(prop) in [None, 0] \ - and ref_d.fields.get(prop) in [None, 0] \ - ) and not \ - ( \ - new_d.fields.get(prop) in [None, ''] \ - and ref_d.fields.get(prop) in [None, ''] \ - ) + for fieldname in self.meta.get_valid_columns(): + self.set(fieldname, None) + + for df in self.meta.get_table_fields(): + self.set(df.fieldname, []) + + self.doc_type = doc_type + self.name = "Customize Form" - def prepare_to_set(self, prop, new_d, ref_d, dt_doclist, delete=0): - """ - Prepares docs of property setter - sets delete property if it is required to be deleted - """ - # Check if property has changed compared to when it was loaded - if self.has_property_changed(ref_d, new_d, prop): - #frappe.msgprint("new: " + str(new_d.fields[prop]) + " | old: " + str(ref_d.fields[prop])) - # Check if the new property is same as that in original doctype - # If yes, we need to delete the property setter entry - for dt_d in dt_doclist: - if dt_d.name == ref_d.name \ - and (new_d.fields.get(prop) == dt_d.fields.get(prop) \ - or \ - ( \ - new_d.fields.get(prop) in [None, 0] \ - and dt_d.fields.get(prop) in [None, 0] \ - ) or \ - ( \ - new_d.fields.get(prop) in [None, ''] \ - and dt_d.fields.get(prop) in [None, ''] \ - )): - delete = 1 - break + def save_customization(self): + if not self.doc_type: + return - value = new_d.fields.get(prop) - - if prop in self.property_restrictions: - allow_change = False - for restrict_list in self.property_restrictions.get(prop): - if value in restrict_list and \ - ref_d.fields.get(prop) in restrict_list: - allow_change = True - break - if not allow_change: - frappe.msgprint("""\ - You cannot change '%s' of '%s' from '%s' to '%s'. - %s can only be changed among %s. - Ignoring this change and saving.""" % \ - (self.defaults.get(prop, {}).get("label") or prop, - new_d.fields.get("label") or new_d.fields.get("idx"), - ref_d.fields.get(prop), value, - self.defaults.get(prop, {}).get("label") or prop, - " -or- ".join([", ".join(r) for r in \ - self.property_restrictions.get(prop)])), raise_exception=True) - return None - - # If the above conditions are fulfilled, - # create a property setter doc, but dont save it yet. - from frappe.model.doc import Document - d = Document('Property Setter') - d.doctype_or_field = ref_d.doctype=='DocField' and 'DocField' or 'DocType' - d.doc_type = self.doc.doc_type - d.field_name = ref_d.fieldname - d.property = prop - d.value = value - d.property_type = self.defaults[prop]['fieldtype'] - #d.default_value = self.defaults[prop]['default'] - if delete: d.delete = 1 - - if d.select_item: - d.select_item = self.remove_forbidden(d.select_item) - - # return the property setter doc - return d - - else: return None - - - def make_idx_property_setter(self, doclist, diff_list): - fields = [] - doclist.sort(lambda a, b: a.idx < b.idx) - for d in doclist: - if d.doctype=="Customize Form Field": - fields.append(d.fieldname) - - d = frappe.doc('Property Setter') - d.doctype_or_field = 'DocType' - d.doc_type = self.doc.doc_type - d.property = "_idx" - d.value = json.dumps(fields) - d.property_type = "Text" - diff_list.append(d) - - def set_properties(self, ps_doclist): - """ - * Delete a property setter entry - + if it already exists - + if marked for deletion - * Save the property setter doc in the list - """ - for d in ps_doclist: - # Delete existing property setter entry - if not d.fields.get("field_name"): - frappe.db.sql(""" - DELETE FROM `tabProperty Setter` - WHERE doc_type = %(doc_type)s - AND property = %(property)s""", d.fields) - else: - frappe.db.sql(""" - DELETE FROM `tabProperty Setter` - WHERE doc_type = %(doc_type)s - AND field_name = %(field_name)s - AND property = %(property)s""", d.fields) - - # Save the property setter doc if not marked for deletion i.e. delete=0 - if not d.delete: - d.insert() + self.set_property_setters() + self.update_custom_fields() + self.set_idx_property_setter() + validate_fields_for_doctype(self.doc_type) + frappe.msgprint("{}: {} {}.".format(_("DocType"), _(self.doc_type), _("updated"))) + frappe.clear_cache(doctype=self.doc_type) + self.fetch_to_customize() - def delete(self): - """ - Deletes all property setter entries for the selected doctype - and resets it to standard - """ - if self.doc.doc_type: - frappe.db.sql(""" - DELETE FROM `tabProperty Setter` - WHERE doc_type = %s""", self.doc.doc_type) + def set_property_setters(self): + meta = frappe.get_meta(self.doc_type) + # doctype property setters + for property in self.doctype_properties: + if self.get(property) != meta.get(property): + self.make_property_setter(property=property, value=self.get(property), + property_type=self.doctype_properties[property]) - frappe.clear_cache(doctype=self.doc.doc_type) + for df in self.get("customize_form_fields"): + if df.get("__islocal"): + continue + + meta_df = meta.get("fields", {"fieldname": df.fieldname}) + + if not meta_df or meta_df[0].get("is_custom_field"): + continue + + for property in self.docfield_properties: + if df.get(property) != meta_df[0].get(property): + if property == "fieldtype": + self.validate_fieldtype_change(df, meta_df[0].get(property), df.get(property)) + + self.make_property_setter(property=property, value=df.get(property), + property_type=self.docfield_properties[property], fieldname=df.fieldname) + + def update_custom_fields(self): + for df in self.get("customize_form_fields"): + if df.get("__islocal"): + self.add_custom_field(df) + else: + self.update_in_custom_field(df) + + self.delete_custom_fields() + + def add_custom_field(self, df): + d = frappe.new_doc("Custom Field") + d.dt = self.doc_type + for property in self.docfield_properties: + d.set(property, df.get(property)) + d.insert() + df.fieldname = d.fieldname + + def update_in_custom_field(self, df): + meta = frappe.get_meta(self.doc_type) + meta_df = meta.get("fields", {"fieldname": df.fieldname}) + if not (meta_df and meta_df[0].get("is_custom_field")): + return + + custom_field = frappe.get_doc("Custom Field", meta_df[0].name) + changed = False + for property in self.docfield_properties: + if df.get(property) != custom_field.get(property): + if property == "fieldtype": + self.validate_fieldtype_change(df, meta_df[0].get(property), df.get(property)) + + custom_field.set(property, df.get(property)) + changed = True + + if changed: + custom_field.save() + + def delete_custom_fields(self): + meta = frappe.get_meta(self.doc_type) + fields_to_remove = (set([df.fieldname for df in meta.get("fields")]) + - set(df.fieldname for df in self.get("customize_form_fields"))) - self.get() + for fieldname in fields_to_remove: + df = meta.get("fields", {"fieldname": fieldname})[0] + if df.get("is_custom_field"): + frappe.delete_doc("Custom Field", df.name) + + def set_idx_property_setter(self): + meta = frappe.get_meta(self.doc_type) + field_order_has_changed = [df.fieldname for df in meta.get("fields")] != \ + [d.fieldname for d in self.get("customize_form_fields")] + + if field_order_has_changed: + _idx = [] + for df in sorted(self.get("customize_form_fields"), key=lambda x: x.idx): + _idx.append(df.fieldname) - def remove_forbidden(self, string): - """ - Replace forbidden characters with a space - """ - forbidden = ['%', "'", '"', '#', '*', '?', '`'] - for f in forbidden: - string.replace(f, ' ') + self.make_property_setter(property="_idx", value=json.dumps(_idx), property_type="Text") + + def make_property_setter(self, property, value, property_type, fieldname=None): + self.delete_existing_property_setter(property, fieldname) + + property_value = self.get_existing_property_value(property, fieldname) + + if property_value==value: + return + + # create a new property setter + frappe.make_property_setter({ + "doctype": self.doc_type, + "doctype_or_field": "DocField" if fieldname else "DocType", + "fieldname": fieldname, + "property": property, + "value": value, + "property_type": property_type + }) + + def delete_existing_property_setter(self, property, fieldname=None): + # first delete existing property setter + existing_property_setter = frappe.db.get_value("Property Setter", {"doc_type": self.doc_type, + "property": property, "field_name['']": fieldname or ''}) + + if existing_property_setter: + frappe.delete_doc("Property Setter", existing_property_setter) + + def get_existing_property_value(self, property, fieldname=None): + # check if there is any need to make property setter! + if fieldname: + property_value = frappe.db.get_value("DocField", {"parent": self.doc_type, + "fieldname": fieldname}, property) + else: + try: + property_value = frappe.db.get_value("DocType", self.doc_type, property) + except Exception, e: + if e.args[0]==1054: + property_value = None + else: + raise + + return property_value + + def validate_fieldtype_change(self, df, old_value, new_value): + for allowed_changes in self.allowed_fieldtype_change: + if ((old_value in allowed_changes and new_value in allowed_changes) + or (old_value not in allowed_changes and new_value not in allowed_changes)): + continue + else: + frappe.throw("{row} # {num}, {label}: {msg} {allowed_changes}".format( + row=_("Row"), num=df.idx, label=_(df.label), msg=_("Field Type can be one of"), + allowed_changes=", ".join([_(fieldtype) for fieldtype in allowed_changes]))) + + def reset_to_defaults(self): + if not self.doc_type: + return + + frappe.db.sql("""delete from `tabProperty Setter` where doc_type=%s""", self.doc_type) + frappe.clear_cache(doctype=self.doc_type) + self.fetch_to_customize() + + \ No newline at end of file diff --git a/frappe/core/doctype/customize_form/customize_form.txt b/frappe/core/doctype/customize_form/customize_form.txt deleted file mode 100644 index dbffb3887d..0000000000 --- a/frappe/core/doctype/customize_form/customize_form.txt +++ /dev/null @@ -1,153 +0,0 @@ -[ - { - "creation": "2013-01-29 17:54:08", - "docstatus": 0, - "modified": "2014-01-15 16:16:23", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "DL.####", - "doctype": "DocType", - "hide_toolbar": 1, - "icon": "icon-glass", - "issingle": 1, - "module": "Core", - "name": "__common__", - "search_fields": "doc_type" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Customize Form", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "create": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Customize Form", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Customize Form" - }, - { - "doctype": "DocField", - "fieldname": "doc_type", - "fieldtype": "Link", - "hidden": 0, - "label": "Enter Form Type", - "no_copy": 0, - "options": "DocType", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "properties", - "fieldtype": "Section Break", - "label": "Properties" - }, - { - "doctype": "DocField", - "fieldname": "column_break0", - "fieldtype": "Column Break" - }, - { - "description": "Fields separated by comma (,) will be included in the
Search By list of Search dialog box", - "doctype": "DocField", - "fieldname": "search_fields", - "fieldtype": "Data", - "label": "Search Fields", - "no_copy": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "default_print_format", - "fieldtype": "Link", - "label": "Default Print Format", - "no_copy": 0, - "options": "Print Format", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "column_break1", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "allow_print", - "fieldtype": "Check", - "label": "Hide Print", - "no_copy": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "allow_email", - "fieldtype": "Check", - "label": "Hide Email", - "no_copy": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "allow_copy", - "fieldtype": "Check", - "label": "Hide Copy", - "no_copy": 0, - "search_index": 0 - }, - { - "description": "Note: maximum attachment size = 1mb", - "doctype": "DocField", - "fieldname": "allow_attach", - "fieldtype": "Check", - "label": "Allow Attach", - "no_copy": 0, - "search_index": 0 - }, - { - "depends_on": "eval:cint(doc.allow_attach)", - "doctype": "DocField", - "fieldname": "max_attachments", - "fieldtype": "Int", - "label": "Max Attachments", - "no_copy": 0, - "search_index": 0 - }, - { - "description": "Customize Label, Print Hide, Default etc.", - "doctype": "DocField", - "fieldname": "fields_section_break", - "fieldtype": "Section Break", - "label": "Fields" - }, - { - "default": "no add rows", - "doctype": "DocField", - "fieldname": "fields", - "fieldtype": "Table", - "label": "Fields", - "no_copy": 0, - "options": "Customize Form Field", - "search_index": 0 - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/customize_form/test_customize_form.py b/frappe/core/doctype/customize_form/test_customize_form.py new file mode 100644 index 0000000000..0dfab42c6e --- /dev/null +++ b/frappe/core/doctype/customize_form/test_customize_form.py @@ -0,0 +1,162 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe, unittest, json +from frappe.test_runner import make_test_records_for_doctype + +test_dependencies = ["Custom Field", "Property Setter"] +class TestCustomizeForm(unittest.TestCase): + def insert_custom_field(self): + frappe.get_doc({ + "doctype": "Custom Field", + "dt": "User", + "label": "Test Custom Field", + "description": "A Custom Field for Testing", + "fieldtype": "Select", + "in_list_view": 1, + "options": "\nCustom 1\nCustom 2\nCustom 3", + "default": "Custom 3" + }).insert() + + def delete_custom_field(self): + frappe.delete_doc("Custom Field", "User-test_custom_field") + + def setUp(self): + self.insert_custom_field() + frappe.clear_cache(doctype="User") + + def tearDown(self): + self.delete_custom_field() + frappe.clear_cache(doctype="User") + + def get_customize_form(self, doctype=None): + d = frappe.get_doc("Customize Form") + if doctype: + d.doc_type = doctype + d.run_method("fetch_to_customize") + return d + + def test_fetch_to_customize(self): + d = self.get_customize_form() + self.assertEquals(d.doc_type, None) + self.assertEquals(len(d.get("customize_form_fields")), 0) + + d = self.get_customize_form("Event") + self.assertEquals(d.doc_type, "Event") + self.assertEquals(len(d.get("customize_form_fields")), 30) + + d = self.get_customize_form("User") + self.assertEquals(d.doc_type, "User") + self.assertEquals(len(d.get("customize_form_fields")), 53) + self.assertEquals(d.get("customize_form_fields")[-1].fieldname, "test_custom_field") + self.assertEquals(d.get("customize_form_fields", {"fieldname": "location"})[0].in_list_view, 1) + + return d + + def test_save_customization_idx(self): + d = self.get_customize_form("User") + original_sequence = [df.fieldname for df in d.get("customize_form_fields")] + + # move field to last + location_field = d.get("customize_form_fields", {"fieldname": "location"})[0] + d.get("customize_form_fields").remove(location_field) + d.append("customize_form_fields", location_field) + d.run_method("save_customization") + frappe.clear_cache(doctype=d.doc_type) + + property_setter_name, _idx = frappe.db.get_value("Property Setter", + {"doc_type": d.doc_type, "property": "_idx"}, ("name", "value")) + self.assertTrue(_idx) + + _idx = json.loads(_idx) + for i, df in enumerate(frappe.get_meta(d.doc_type).get("fields")): + self.assertEquals(_idx[i], df.fieldname) + + frappe.delete_doc("Property Setter", property_setter_name) + frappe.clear_cache(doctype=d.doc_type) + + for i, df in enumerate(frappe.get_meta(d.doc_type).get("fields")): + self.assertEquals(original_sequence[i], df.fieldname) + + def test_save_customization_property(self): + d = self.get_customize_form("User") + self.assertEquals(frappe.db.get_value("Property Setter", + {"doc_type": "User", "property": "allow_copy"}, "value"), None) + + d.allow_copy = 1 + d.run_method("save_customization") + self.assertEquals(frappe.db.get_value("Property Setter", + {"doc_type": "User", "property": "allow_copy"}, "value"), '1') + + d.allow_copy = 0 + d.run_method("save_customization") + self.assertEquals(frappe.db.get_value("Property Setter", + {"doc_type": "User", "property": "allow_copy"}, "value"), None) + + def test_save_customization_field_property(self): + d = self.get_customize_form("User") + self.assertEquals(frappe.db.get_value("Property Setter", + {"doc_type": "User", "property": "reqd", "field_name": "location"}, "value"), None) + + location_field = d.get("customize_form_fields", {"fieldname": "location"})[0] + location_field.reqd = 1 + d.run_method("save_customization") + self.assertEquals(frappe.db.get_value("Property Setter", + {"doc_type": "User", "property": "reqd", "field_name": "location"}, "value"), '1') + + location_field = d.get("customize_form_fields", {"fieldname": "location"})[0] + location_field.reqd = 0 + d.run_method("save_customization") + self.assertEquals(frappe.db.get_value("Property Setter", + {"doc_type": "User", "property": "reqd", "field_name": "location"}, "value"), '0') + + def test_save_customization_custom_field_property(self): + d = self.get_customize_form("User") + self.assertEquals(frappe.db.get_value("Custom Field", "User-test_custom_field", "reqd"), None) + + custom_field = d.get("customize_form_fields", {"fieldname": "test_custom_field"})[0] + custom_field.reqd = 1 + d.run_method("save_customization") + self.assertEquals(frappe.db.get_value("Custom Field", "User-test_custom_field", "reqd"), 1) + + custom_field = d.get("customize_form_fields", {"is_custom_field": True})[0] + custom_field.reqd = 0 + d.run_method("save_customization") + self.assertEquals(frappe.db.get_value("Custom Field", "User-test_custom_field", "reqd"), 0) + + def test_save_customization_new_field(self): + d = self.get_customize_form("User") + d.append("customize_form_fields", { + "label": "Test Add Custom Field Via Customize Form", + "fieldtype": "Data", + "__islocal": 1 + }) + d.run_method("save_customization") + self.assertEquals(frappe.db.get_value("Custom Field", + "User-test_add_custom_field_via_customize_form", "fieldtype"), "Data") + + frappe.delete_doc("Custom Field", "User-test_add_custom_field_via_customize_form") + self.assertEquals(frappe.db.get_value("Custom Field", + "User-test_add_custom_field_via_customize_form"), None) + + def test_save_customization_remove_field(self): + d = self.get_customize_form("User") + custom_field = d.get("customize_form_fields", {"fieldname": "test_custom_field"})[0] + d.get("customize_form_fields").remove(custom_field) + d.run_method("save_customization") + + self.assertEquals(frappe.db.get_value("Custom Field", custom_field.name), None) + + frappe.local.test_objects["Custom Field"] = [] + make_test_records_for_doctype("Custom Field") + + def test_reset_to_defaults(self): + d = frappe.get_doc("Customize Form") + d.doc_type = "User" + d.run_method('reset_to_defaults') + + self.assertEquals(d.get("customize_form_fields", {"fieldname": "location"})[0].in_list_view, None) + + frappe.local.test_objects["Property Setter"] = [] + make_test_records_for_doctype("Property Setter") diff --git a/frappe/core/doctype/customize_form_field/customize_form_field.json b/frappe/core/doctype/customize_form_field/customize_form_field.json new file mode 100644 index 0000000000..e1cf79090f --- /dev/null +++ b/frappe/core/doctype/customize_form_field/customize_form_field.json @@ -0,0 +1,236 @@ +{ + "allow_copy": 0, + "autoname": "DLF.#####", + "creation": "2013-02-22 01:27:32.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "label", + "fieldtype": "Data", + "hidden": 0, + "in_list_view": 1, + "label": "Label", + "oldfieldname": "label", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "reqd": 0, + "search_index": 1 + }, + { + "fieldname": "fieldtype", + "fieldtype": "Select", + "hidden": 0, + "in_list_view": 1, + "label": "Type", + "oldfieldname": "fieldtype", + "oldfieldtype": "Select", + "options": "Button\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime", + "permlevel": 0, + "print_hide": 0, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "fieldname", + "fieldtype": "Data", + "hidden": 0, + "in_list_view": 1, + "label": "Name", + "oldfieldname": "fieldname", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "read_only": 1, + "reqd": 0, + "search_index": 1 + }, + { + "description": "For Links, enter the DocType as range\nFor Select, enter list of Options separated by comma", + "fieldname": "options", + "fieldtype": "Text", + "hidden": 0, + "in_list_view": 1, + "label": "Options", + "oldfieldname": "options", + "oldfieldtype": "Text", + "permlevel": 0, + "print_hide": 0, + "reqd": 0, + "search_index": 0 + }, + { + "default": "0", + "fieldname": "permlevel", + "fieldtype": "Int", + "hidden": 0, + "in_list_view": 1, + "label": "Perm Level", + "oldfieldname": "permlevel", + "oldfieldtype": "Int", + "permlevel": 0, + "print_hide": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "width", + "fieldtype": "Data", + "hidden": 0, + "in_list_view": 1, + "label": "Width", + "oldfieldname": "width", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "print_width": "50px", + "reqd": 0, + "search_index": 0, + "width": "50px" + }, + { + "fieldname": "print_width", + "fieldtype": "Data", + "label": "Print Width", + "permlevel": 0, + "print_width": "50px", + "width": "50px" + }, + { + "fieldname": "reqd", + "fieldtype": "Check", + "hidden": 0, + "label": "Reqd", + "oldfieldname": "reqd", + "oldfieldtype": "Check", + "permlevel": 0, + "print_hide": 0, + "print_width": "50px", + "reqd": 0, + "search_index": 0, + "width": "50px" + }, + { + "fieldname": "ignore_restrictions", + "fieldtype": "Check", + "label": "Ignore Restrictions", + "permlevel": 0 + }, + { + "fieldname": "in_filter", + "fieldtype": "Check", + "hidden": 0, + "label": "In Filter", + "oldfieldname": "in_filter", + "oldfieldtype": "Check", + "permlevel": 0, + "print_hide": 0, + "print_width": "50px", + "reqd": 0, + "width": "50px" + }, + { + "fieldname": "in_list_view", + "fieldtype": "Check", + "label": "In List View", + "permlevel": 0 + }, + { + "fieldname": "hidden", + "fieldtype": "Check", + "hidden": 0, + "label": "Hidden", + "oldfieldname": "hidden", + "oldfieldtype": "Check", + "permlevel": 0, + "print_hide": 0, + "print_width": "50px", + "reqd": 0, + "search_index": 0, + "width": "50px" + }, + { + "fieldname": "print_hide", + "fieldtype": "Check", + "hidden": 0, + "label": "Print Hide", + "oldfieldname": "print_hide", + "oldfieldtype": "Check", + "permlevel": 0, + "print_hide": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "report_hide", + "fieldtype": "Check", + "hidden": 0, + "label": "Report Hide", + "oldfieldname": "report_hide", + "oldfieldtype": "Check", + "permlevel": 0, + "print_hide": 0, + "reqd": 0 + }, + { + "fieldname": "allow_on_submit", + "fieldtype": "Check", + "hidden": 0, + "label": "Allow on Submit", + "oldfieldname": "allow_on_submit", + "oldfieldtype": "Check", + "permlevel": 0, + "print_hide": 0, + "reqd": 0 + }, + { + "description": "This field will appear only if the fieldname defined here has value OR the rules are true (examples):
\nmyfield\neval:doc.myfield=='My Value'
\neval:doc.age>18", + "fieldname": "depends_on", + "fieldtype": "Data", + "hidden": 0, + "label": "Depends On", + "oldfieldname": "depends_on", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "reqd": 0 + }, + { + "fieldname": "description", + "fieldtype": "Text", + "hidden": 0, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "permlevel": 0, + "print_hide": 0, + "print_width": "300px", + "reqd": 0, + "width": "300px" + }, + { + "fieldname": "default", + "fieldtype": "Text", + "hidden": 0, + "label": "Default", + "oldfieldname": "default", + "oldfieldtype": "Text", + "permlevel": 0, + "print_hide": 0, + "reqd": 0, + "search_index": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 1, + "issingle": 0, + "istable": 1, + "modified": "2013-12-23 16:12:45.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Customize Form Field", + "owner": "Administrator", + "read_only": 0 +} \ No newline at end of file diff --git a/frappe/core/doctype/customize_form_field/customize_form_field.py b/frappe/core/doctype/customize_form_field/customize_form_field.py index a48f49f2a7..4d2118a4a8 100644 --- a/frappe/core/doctype/customize_form_field/customize_form_field.py +++ b/frappe/core/doctype/customize_form_field/customize_form_field.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class CustomizeFormField(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/customize_form_field/customize_form_field.txt b/frappe/core/doctype/customize_form_field/customize_form_field.txt deleted file mode 100644 index 35af5f5ec3..0000000000 --- a/frappe/core/doctype/customize_form_field/customize_form_field.txt +++ /dev/null @@ -1,249 +0,0 @@ -[ - { - "creation": "2013-02-22 01:27:32", - "docstatus": 0, - "modified": "2013-12-23 16:12:45", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_copy": 0, - "autoname": "DLF.#####", - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "issingle": 0, - "istable": 1, - "module": "Core", - "name": "__common__", - "read_only": 0 - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Customize Form Field", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocType", - "name": "Customize Form Field" - }, - { - "doctype": "DocField", - "fieldname": "label", - "fieldtype": "Data", - "hidden": 0, - "in_list_view": 1, - "label": "Label", - "oldfieldname": "label", - "oldfieldtype": "Data", - "print_hide": 0, - "reqd": 0, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "fieldtype", - "fieldtype": "Select", - "hidden": 0, - "in_list_view": 1, - "label": "Type", - "oldfieldname": "fieldtype", - "oldfieldtype": "Select", - "options": "Button\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime", - "print_hide": 0, - "reqd": 1, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "fieldname", - "fieldtype": "Data", - "hidden": 0, - "in_list_view": 1, - "label": "Name", - "oldfieldname": "fieldname", - "oldfieldtype": "Data", - "print_hide": 0, - "read_only": 1, - "reqd": 0, - "search_index": 1 - }, - { - "description": "For Links, enter the DocType as range\nFor Select, enter list of Options separated by comma", - "doctype": "DocField", - "fieldname": "options", - "fieldtype": "Text", - "hidden": 0, - "in_list_view": 1, - "label": "Options", - "oldfieldname": "options", - "oldfieldtype": "Text", - "print_hide": 0, - "reqd": 0, - "search_index": 0 - }, - { - "default": "0", - "doctype": "DocField", - "fieldname": "permlevel", - "fieldtype": "Int", - "hidden": 0, - "in_list_view": 1, - "label": "Perm Level", - "oldfieldname": "permlevel", - "oldfieldtype": "Int", - "print_hide": 0, - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "width", - "fieldtype": "Data", - "hidden": 0, - "in_list_view": 1, - "label": "Width", - "oldfieldname": "width", - "oldfieldtype": "Data", - "print_hide": 0, - "print_width": "50px", - "reqd": 0, - "search_index": 0, - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "print_width", - "fieldtype": "Data", - "label": "Print Width", - "print_width": "50px", - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "reqd", - "fieldtype": "Check", - "hidden": 0, - "label": "Reqd", - "oldfieldname": "reqd", - "oldfieldtype": "Check", - "print_hide": 0, - "print_width": "50px", - "reqd": 0, - "search_index": 0, - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "ignore_restrictions", - "fieldtype": "Check", - "label": "Ignore Restrictions" - }, - { - "doctype": "DocField", - "fieldname": "in_filter", - "fieldtype": "Check", - "hidden": 0, - "label": "In Filter", - "oldfieldname": "in_filter", - "oldfieldtype": "Check", - "print_hide": 0, - "print_width": "50px", - "reqd": 0, - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "in_list_view", - "fieldtype": "Check", - "label": "In List View" - }, - { - "doctype": "DocField", - "fieldname": "hidden", - "fieldtype": "Check", - "hidden": 0, - "label": "Hidden", - "oldfieldname": "hidden", - "oldfieldtype": "Check", - "print_hide": 0, - "print_width": "50px", - "reqd": 0, - "search_index": 0, - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "print_hide", - "fieldtype": "Check", - "hidden": 0, - "label": "Print Hide", - "oldfieldname": "print_hide", - "oldfieldtype": "Check", - "print_hide": 0, - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "report_hide", - "fieldtype": "Check", - "hidden": 0, - "label": "Report Hide", - "oldfieldname": "report_hide", - "oldfieldtype": "Check", - "print_hide": 0, - "reqd": 0 - }, - { - "doctype": "DocField", - "fieldname": "allow_on_submit", - "fieldtype": "Check", - "hidden": 0, - "label": "Allow on Submit", - "oldfieldname": "allow_on_submit", - "oldfieldtype": "Check", - "print_hide": 0, - "reqd": 0 - }, - { - "description": "This field will appear only if the fieldname defined here has value OR the rules are true (examples):
\nmyfield\neval:doc.myfield=='My Value'
\neval:doc.age>18", - "doctype": "DocField", - "fieldname": "depends_on", - "fieldtype": "Data", - "hidden": 0, - "label": "Depends On", - "oldfieldname": "depends_on", - "oldfieldtype": "Data", - "print_hide": 0, - "reqd": 0 - }, - { - "doctype": "DocField", - "fieldname": "description", - "fieldtype": "Text", - "hidden": 0, - "label": "Description", - "oldfieldname": "description", - "oldfieldtype": "Text", - "print_hide": 0, - "print_width": "300px", - "reqd": 0, - "width": "300px" - }, - { - "doctype": "DocField", - "fieldname": "default", - "fieldtype": "Text", - "hidden": 0, - "label": "Default", - "oldfieldname": "default", - "oldfieldtype": "Text", - "print_hide": 0, - "reqd": 0, - "search_index": 0 - } -] \ No newline at end of file diff --git a/frappe/core/doctype/default_home_page/__init__.py b/frappe/core/doctype/default_home_page/__init__.py deleted file mode 100644 index e61caaf193..0000000000 --- a/frappe/core/doctype/default_home_page/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals diff --git a/frappe/core/doctype/default_home_page/default_home_page.py b/frappe/core/doctype/default_home_page/default_home_page.py deleted file mode 100644 index a48f49f2a7..0000000000 --- a/frappe/core/doctype/default_home_page/default_home_page.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals -import frappe - -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file diff --git a/frappe/core/doctype/default_home_page/default_home_page.txt b/frappe/core/doctype/default_home_page/default_home_page.txt deleted file mode 100644 index 5b014b4c16..0000000000 --- a/frappe/core/doctype/default_home_page/default_home_page.txt +++ /dev/null @@ -1,44 +0,0 @@ -[ - { - "creation": "2013-02-22 01:27:32", - "docstatus": 0, - "modified": "2013-12-20 19:23:05", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "DocType", - "istable": 1, - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "fieldtype": "Link", - "in_list_view": 1, - "name": "__common__", - "oldfieldtype": "Link", - "parent": "Default Home Page", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocType", - "name": "Default Home Page" - }, - { - "doctype": "DocField", - "fieldname": "role", - "label": "Role", - "oldfieldname": "role", - "options": "Role" - }, - { - "doctype": "DocField", - "fieldname": "home_page", - "label": "Home Page", - "oldfieldname": "home_page", - "options": "Page" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/defaultvalue/defaultvalue.json b/frappe/core/doctype/defaultvalue/defaultvalue.json new file mode 100644 index 0000000000..e7f3373268 --- /dev/null +++ b/frappe/core/doctype/defaultvalue/defaultvalue.json @@ -0,0 +1,48 @@ +{ + "allow_copy": 0, + "autoname": "DEF.######", + "creation": "2013-02-22 01:27:32.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "defkey", + "fieldtype": "Data", + "hidden": 0, + "in_list_view": 1, + "label": "Key", + "oldfieldname": "defkey", + "oldfieldtype": "Data", + "permlevel": 0, + "print_width": "200px", + "reqd": 1, + "search_index": 0, + "width": "200px" + }, + { + "fieldname": "defvalue", + "fieldtype": "Text", + "hidden": 0, + "in_list_view": 1, + "label": "Value", + "oldfieldname": "defvalue", + "oldfieldtype": "Text", + "permlevel": 0, + "print_width": "200px", + "reqd": 0, + "search_index": 0, + "width": "200px" + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 1, + "issingle": 0, + "istable": 1, + "modified": "2013-12-20 19:23:05.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "DefaultValue", + "owner": "Administrator", + "read_only": 0 +} \ No newline at end of file diff --git a/frappe/core/doctype/defaultvalue/defaultvalue.py b/frappe/core/doctype/defaultvalue/defaultvalue.py index ed3b873f7d..b2669077b7 100644 --- a/frappe/core/doctype/defaultvalue/defaultvalue.py +++ b/frappe/core/doctype/defaultvalue/defaultvalue.py @@ -4,9 +4,10 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class DefaultValue(Document): + pass def on_doctype_update(): if not frappe.db.sql("""show index from `tabDefaultValue` diff --git a/frappe/core/doctype/defaultvalue/defaultvalue.txt b/frappe/core/doctype/defaultvalue/defaultvalue.txt deleted file mode 100644 index d7019194a8..0000000000 --- a/frappe/core/doctype/defaultvalue/defaultvalue.txt +++ /dev/null @@ -1,56 +0,0 @@ -[ - { - "creation": "2013-02-22 01:27:32", - "docstatus": 0, - "modified": "2013-12-20 19:23:05", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_copy": 0, - "autoname": "DEF.######", - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "issingle": 0, - "istable": 1, - "module": "Core", - "name": "__common__", - "read_only": 0 - }, - { - "doctype": "DocField", - "hidden": 0, - "in_list_view": 1, - "name": "__common__", - "parent": "DefaultValue", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "print_width": "200px", - "search_index": 0, - "width": "200px" - }, - { - "doctype": "DocType", - "name": "DefaultValue" - }, - { - "doctype": "DocField", - "fieldname": "defkey", - "fieldtype": "Data", - "label": "Key", - "oldfieldname": "defkey", - "oldfieldtype": "Data", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "defvalue", - "fieldtype": "Text", - "label": "Value", - "oldfieldname": "defvalue", - "oldfieldtype": "Text", - "reqd": 0 - } -] \ No newline at end of file diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json new file mode 100644 index 0000000000..5f7152efb0 --- /dev/null +++ b/frappe/core/doctype/docfield/docfield.json @@ -0,0 +1,312 @@ +{ + "allow_copy": 0, + "autoname": "FL.#####", + "creation": "2013-02-22 01:27:33.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "label_and_type", + "fieldtype": "Section Break", + "label": "Label and Type", + "permlevel": 0 + }, + { + "fieldname": "label", + "fieldtype": "Data", + "hidden": 0, + "in_list_view": 1, + "label": "Label", + "oldfieldname": "label", + "oldfieldtype": "Data", + "permlevel": 0, + "print_width": "163", + "reqd": 0, + "search_index": 1, + "width": "163" + }, + { + "fieldname": "fieldtype", + "fieldtype": "Select", + "hidden": 0, + "in_list_view": 1, + "label": "Type", + "oldfieldname": "fieldtype", + "oldfieldtype": "Select", + "options": "Attach\nButton\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime", + "permlevel": 0, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "fieldname", + "fieldtype": "Data", + "hidden": 0, + "in_list_view": 1, + "label": "Name", + "oldfieldname": "fieldname", + "oldfieldtype": "Data", + "permlevel": 0, + "reqd": 0, + "search_index": 1 + }, + { + "fieldname": "reqd", + "fieldtype": "Check", + "hidden": 0, + "in_list_view": 1, + "label": "Mandatory", + "oldfieldname": "reqd", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "50px", + "reqd": 0, + "search_index": 0, + "width": "50px" + }, + { + "fieldname": "search_index", + "fieldtype": "Check", + "hidden": 0, + "label": "Index", + "oldfieldname": "search_index", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "50px", + "reqd": 0, + "search_index": 0, + "width": "50px" + }, + { + "fieldname": "in_list_view", + "fieldtype": "Check", + "label": "In List View", + "permlevel": 0, + "print_width": "70px", + "width": "70px" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "description": "For Links, enter the DocType as range\nFor Select, enter list of Options separated by comma", + "fieldname": "options", + "fieldtype": "Text", + "hidden": 0, + "in_list_view": 1, + "label": "Options", + "oldfieldname": "options", + "oldfieldtype": "Text", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "permissions", + "fieldtype": "Section Break", + "label": "Permissions", + "permlevel": 0 + }, + { + "fieldname": "depends_on", + "fieldtype": "Data", + "label": "Depends On", + "oldfieldname": "depends_on", + "oldfieldtype": "Data", + "permlevel": 0 + }, + { + "default": "0", + "fieldname": "permlevel", + "fieldtype": "Int", + "hidden": 0, + "label": "Perm Level", + "oldfieldname": "permlevel", + "oldfieldtype": "Int", + "permlevel": 0, + "print_width": "50px", + "reqd": 0, + "search_index": 0, + "width": "50px" + }, + { + "fieldname": "hidden", + "fieldtype": "Check", + "hidden": 0, + "label": "Hidden", + "oldfieldname": "hidden", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "50px", + "reqd": 0, + "search_index": 0, + "width": "50px" + }, + { + "fieldname": "read_only", + "fieldtype": "Check", + "label": "Read Only", + "permlevel": 0, + "print_width": "50px", + "width": "50px" + }, + { + "description": "Do not allow user to change after set the first time", + "fieldname": "set_only_once", + "fieldtype": "Check", + "label": "Set Only Once", + "permlevel": 0 + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "description": "User restrictions should not apply for this Link", + "fieldname": "ignore_restrictions", + "fieldtype": "Check", + "label": "Ignore Restrictions", + "permlevel": 0 + }, + { + "fieldname": "allow_on_submit", + "fieldtype": "Check", + "label": "Allow on Submit", + "oldfieldname": "allow_on_submit", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "50px", + "width": "50px" + }, + { + "fieldname": "report_hide", + "fieldtype": "Check", + "label": "Report Hide", + "oldfieldname": "report_hide", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "50px", + "width": "50px" + }, + { + "fieldname": "display", + "fieldtype": "Section Break", + "label": "Display", + "permlevel": 0 + }, + { + "fieldname": "default", + "fieldtype": "Text", + "hidden": 0, + "label": "Default", + "oldfieldname": "default", + "oldfieldtype": "Text", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "in_filter", + "fieldtype": "Check", + "label": "In Filter", + "oldfieldname": "in_filter", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "50px", + "width": "50px" + }, + { + "fieldname": "no_copy", + "fieldtype": "Check", + "label": "No Copy", + "oldfieldname": "no_copy", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "50px", + "width": "50px" + }, + { + "fieldname": "column_break_22", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "description", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "permlevel": 0, + "print_width": "300px", + "width": "300px" + }, + { + "fieldname": "print_hide", + "fieldtype": "Check", + "hidden": 0, + "label": "Print Hide", + "oldfieldname": "print_hide", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "50px", + "reqd": 0, + "search_index": 0, + "width": "50px" + }, + { + "fieldname": "print_width", + "fieldtype": "Data", + "label": "Print Width", + "permlevel": 0 + }, + { + "fieldname": "width", + "fieldtype": "Data", + "hidden": 0, + "label": "Width", + "oldfieldname": "width", + "oldfieldtype": "Data", + "permlevel": 0, + "print_width": "50px", + "reqd": 0, + "search_index": 0, + "width": "50px" + }, + { + "fieldname": "oldfieldname", + "fieldtype": "Data", + "hidden": 1, + "oldfieldname": "oldfieldname", + "oldfieldtype": "Data", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "oldfieldtype", + "fieldtype": "Data", + "hidden": 1, + "oldfieldname": "oldfieldtype", + "oldfieldtype": "Data", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 1, + "in_dialog": 1, + "issingle": 0, + "istable": 1, + "modified": "2014-03-05 14:58:43.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "DocField", + "owner": "Administrator", + "read_only": 0 +} \ No newline at end of file diff --git a/frappe/core/doctype/docfield/docfield.py b/frappe/core/doctype/docfield/docfield.py index a48f49f2a7..118a032b23 100644 --- a/frappe/core/doctype/docfield/docfield.py +++ b/frappe/core/doctype/docfield/docfield.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class DocField(Document): + pass diff --git a/frappe/core/doctype/docfield/docfield.txt b/frappe/core/doctype/docfield/docfield.txt deleted file mode 100644 index 2dd3a6e819..0000000000 --- a/frappe/core/doctype/docfield/docfield.txt +++ /dev/null @@ -1,325 +0,0 @@ -[ - { - "creation": "2013-02-22 01:27:33", - "docstatus": 0, - "modified": "2014-03-05 14:58:43", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_copy": 0, - "autoname": "FL.#####", - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "in_dialog": 1, - "issingle": 0, - "istable": 1, - "module": "Core", - "name": "__common__", - "read_only": 0 - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "DocField", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocType", - "name": "DocField" - }, - { - "doctype": "DocField", - "fieldname": "label_and_type", - "fieldtype": "Section Break", - "label": "Label and Type" - }, - { - "doctype": "DocField", - "fieldname": "label", - "fieldtype": "Data", - "hidden": 0, - "in_list_view": 1, - "label": "Label", - "oldfieldname": "label", - "oldfieldtype": "Data", - "print_width": "163", - "reqd": 0, - "search_index": 1, - "width": "163" - }, - { - "doctype": "DocField", - "fieldname": "fieldtype", - "fieldtype": "Select", - "hidden": 0, - "in_list_view": 1, - "label": "Type", - "oldfieldname": "fieldtype", - "oldfieldtype": "Select", - "options": "Attach\nButton\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime", - "reqd": 1, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "fieldname", - "fieldtype": "Data", - "hidden": 0, - "in_list_view": 1, - "label": "Name", - "oldfieldname": "fieldname", - "oldfieldtype": "Data", - "reqd": 0, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "reqd", - "fieldtype": "Check", - "hidden": 0, - "in_list_view": 1, - "label": "Mandatory", - "oldfieldname": "reqd", - "oldfieldtype": "Check", - "print_width": "50px", - "reqd": 0, - "search_index": 0, - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "search_index", - "fieldtype": "Check", - "hidden": 0, - "label": "Index", - "oldfieldname": "search_index", - "oldfieldtype": "Check", - "print_width": "50px", - "reqd": 0, - "search_index": 0, - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "in_list_view", - "fieldtype": "Check", - "label": "In List View", - "print_width": "70px", - "width": "70px" - }, - { - "doctype": "DocField", - "fieldname": "column_break_6", - "fieldtype": "Column Break" - }, - { - "description": "For Links, enter the DocType as range\nFor Select, enter list of Options separated by comma", - "doctype": "DocField", - "fieldname": "options", - "fieldtype": "Text", - "hidden": 0, - "in_list_view": 1, - "label": "Options", - "oldfieldname": "options", - "oldfieldtype": "Text", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "permissions", - "fieldtype": "Section Break", - "label": "Permissions" - }, - { - "doctype": "DocField", - "fieldname": "depends_on", - "fieldtype": "Data", - "label": "Depends On", - "oldfieldname": "depends_on", - "oldfieldtype": "Data" - }, - { - "default": "0", - "doctype": "DocField", - "fieldname": "permlevel", - "fieldtype": "Int", - "hidden": 0, - "label": "Perm Level", - "oldfieldname": "permlevel", - "oldfieldtype": "Int", - "print_width": "50px", - "reqd": 0, - "search_index": 0, - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "hidden", - "fieldtype": "Check", - "hidden": 0, - "label": "Hidden", - "oldfieldname": "hidden", - "oldfieldtype": "Check", - "print_width": "50px", - "reqd": 0, - "search_index": 0, - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "read_only", - "fieldtype": "Check", - "label": "Read Only", - "print_width": "50px", - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "set_only_once", - "fieldtype": "Check", - "label": "Set Only Once", - "description": "Do not allow user to change after set the first time" - }, - { - "doctype": "DocField", - "fieldname": "column_break_13", - "fieldtype": "Column Break" - }, - { - "description": "User restrictions should not apply for this Link", - "doctype": "DocField", - "fieldname": "ignore_restrictions", - "fieldtype": "Check", - "label": "Ignore Restrictions" - }, - { - "doctype": "DocField", - "fieldname": "allow_on_submit", - "fieldtype": "Check", - "label": "Allow on Submit", - "oldfieldname": "allow_on_submit", - "oldfieldtype": "Check", - "print_width": "50px", - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "report_hide", - "fieldtype": "Check", - "label": "Report Hide", - "oldfieldname": "report_hide", - "oldfieldtype": "Check", - "print_width": "50px", - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "display", - "fieldtype": "Section Break", - "label": "Display" - }, - { - "doctype": "DocField", - "fieldname": "default", - "fieldtype": "Text", - "hidden": 0, - "label": "Default", - "oldfieldname": "default", - "oldfieldtype": "Text", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "in_filter", - "fieldtype": "Check", - "label": "In Filter", - "oldfieldname": "in_filter", - "oldfieldtype": "Check", - "print_width": "50px", - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "no_copy", - "fieldtype": "Check", - "label": "No Copy", - "oldfieldname": "no_copy", - "oldfieldtype": "Check", - "print_width": "50px", - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "column_break_22", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "description", - "fieldtype": "Text", - "in_list_view": 1, - "label": "Description", - "oldfieldname": "description", - "oldfieldtype": "Text", - "print_width": "300px", - "width": "300px" - }, - { - "doctype": "DocField", - "fieldname": "print_hide", - "fieldtype": "Check", - "hidden": 0, - "label": "Print Hide", - "oldfieldname": "print_hide", - "oldfieldtype": "Check", - "print_width": "50px", - "reqd": 0, - "search_index": 0, - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "print_width", - "fieldtype": "Data", - "label": "Print Width" - }, - { - "doctype": "DocField", - "fieldname": "width", - "fieldtype": "Data", - "hidden": 0, - "label": "Width", - "oldfieldname": "width", - "oldfieldtype": "Data", - "print_width": "50px", - "reqd": 0, - "search_index": 0, - "width": "50px" - }, - { - "doctype": "DocField", - "fieldname": "oldfieldname", - "fieldtype": "Data", - "hidden": 1, - "oldfieldname": "oldfieldname", - "oldfieldtype": "Data", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "oldfieldtype", - "fieldtype": "Data", - "hidden": 1, - "oldfieldname": "oldfieldtype", - "oldfieldtype": "Data", - "reqd": 0, - "search_index": 0 - } -] \ No newline at end of file diff --git a/frappe/core/doctype/docperm/docperm.json b/frappe/core/doctype/docperm/docperm.json new file mode 100644 index 0000000000..6cb159a495 --- /dev/null +++ b/frappe/core/doctype/docperm/docperm.json @@ -0,0 +1,216 @@ +{ + "allow_copy": 0, + "autoname": "PERM.#####", + "creation": "2013-02-22 01:27:33.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "role_and_level", + "fieldtype": "Section Break", + "label": "Role and Level", + "permlevel": 0 + }, + { + "fieldname": "role", + "fieldtype": "Link", + "hidden": 0, + "in_list_view": 1, + "label": "Role", + "oldfieldname": "role", + "oldfieldtype": "Link", + "options": "Role", + "permlevel": 0, + "print_width": "150px", + "reqd": 1, + "search_index": 0, + "width": "150px" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "default": "0", + "fieldname": "permlevel", + "fieldtype": "Int", + "hidden": 0, + "in_list_view": 1, + "label": "Level", + "oldfieldname": "permlevel", + "oldfieldtype": "Int", + "permlevel": 0, + "print_width": "40px", + "reqd": 0, + "search_index": 0, + "width": "40px" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "label": "Permissions", + "permlevel": 0 + }, + { + "fieldname": "read", + "fieldtype": "Check", + "hidden": 0, + "in_list_view": 1, + "label": "Read", + "oldfieldname": "read", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "32px", + "reqd": 0, + "search_index": 0, + "width": "32px" + }, + { + "description": "Only restricted users can access", + "fieldname": "restricted", + "fieldtype": "Check", + "label": "Restricted", + "permlevel": 0 + }, + { + "fieldname": "write", + "fieldtype": "Check", + "hidden": 0, + "in_list_view": 1, + "label": "Write", + "oldfieldname": "write", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "32px", + "reqd": 0, + "search_index": 0, + "width": "32px" + }, + { + "fieldname": "create", + "fieldtype": "Check", + "hidden": 0, + "in_list_view": 1, + "label": "Create", + "oldfieldname": "create", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "32px", + "reqd": 0, + "search_index": 0, + "width": "32px" + }, + { + "fieldname": "delete", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Delete", + "permlevel": 0 + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "submit", + "fieldtype": "Check", + "hidden": 0, + "in_list_view": 1, + "label": "Submit", + "oldfieldname": "submit", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "32px", + "reqd": 0, + "search_index": 0, + "width": "32px" + }, + { + "fieldname": "cancel", + "fieldtype": "Check", + "hidden": 0, + "in_list_view": 1, + "label": "Cancel", + "oldfieldname": "cancel", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "32px", + "reqd": 0, + "search_index": 0, + "width": "32px" + }, + { + "fieldname": "amend", + "fieldtype": "Check", + "label": "Amend", + "oldfieldname": "amend", + "oldfieldtype": "Check", + "permlevel": 0, + "print_width": "32px", + "width": "32px" + }, + { + "fieldname": "additional_permissions", + "fieldtype": "Section Break", + "label": "Additional Permissions", + "permlevel": 0 + }, + { + "fieldname": "report", + "fieldtype": "Check", + "label": "Report", + "permlevel": 0, + "print_width": "32px", + "width": "32px" + }, + { + "fieldname": "export", + "fieldtype": "Check", + "label": "Export", + "permlevel": 0 + }, + { + "fieldname": "import", + "fieldtype": "Check", + "label": "Import", + "permlevel": 0 + }, + { + "fieldname": "column_break_19", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "print", + "fieldtype": "Check", + "label": "Print", + "permlevel": 0 + }, + { + "fieldname": "email", + "fieldtype": "Check", + "label": "Email", + "permlevel": 0 + }, + { + "description": "This role can restrict users for accessing the record.", + "fieldname": "restrict", + "fieldtype": "Check", + "label": "Can Restrict", + "permlevel": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 1, + "issingle": 0, + "istable": 1, + "modified": "2014-01-22 14:32:34.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "DocPerm", + "owner": "Administrator", + "read_only": 0 +} \ No newline at end of file diff --git a/frappe/core/doctype/docperm/docperm.py b/frappe/core/doctype/docperm/docperm.py index a48f49f2a7..00bffbfe52 100644 --- a/frappe/core/doctype/docperm/docperm.py +++ b/frappe/core/doctype/docperm/docperm.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class DocPerm(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/docperm/docperm.txt b/frappe/core/doctype/docperm/docperm.txt deleted file mode 100644 index f03bba0ba8..0000000000 --- a/frappe/core/doctype/docperm/docperm.txt +++ /dev/null @@ -1,229 +0,0 @@ -[ - { - "creation": "2013-02-22 01:27:33", - "docstatus": 0, - "modified": "2014-01-22 14:32:34", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_copy": 0, - "autoname": "PERM.#####", - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "issingle": 0, - "istable": 1, - "module": "Core", - "name": "__common__", - "read_only": 0 - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "DocPerm", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocType", - "name": "DocPerm" - }, - { - "doctype": "DocField", - "fieldname": "role_and_level", - "fieldtype": "Section Break", - "label": "Role and Level" - }, - { - "doctype": "DocField", - "fieldname": "role", - "fieldtype": "Link", - "hidden": 0, - "in_list_view": 1, - "label": "Role", - "oldfieldname": "role", - "oldfieldtype": "Link", - "options": "Role", - "print_width": "150px", - "reqd": 1, - "search_index": 0, - "width": "150px" - }, - { - "doctype": "DocField", - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "default": "0", - "doctype": "DocField", - "fieldname": "permlevel", - "fieldtype": "Int", - "hidden": 0, - "in_list_view": 1, - "label": "Level", - "oldfieldname": "permlevel", - "oldfieldtype": "Int", - "print_width": "40px", - "reqd": 0, - "search_index": 0, - "width": "40px" - }, - { - "doctype": "DocField", - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "label": "Permissions" - }, - { - "doctype": "DocField", - "fieldname": "read", - "fieldtype": "Check", - "hidden": 0, - "in_list_view": 1, - "label": "Read", - "oldfieldname": "read", - "oldfieldtype": "Check", - "print_width": "32px", - "reqd": 0, - "search_index": 0, - "width": "32px" - }, - { - "description": "Only restricted users can access", - "doctype": "DocField", - "fieldname": "restricted", - "fieldtype": "Check", - "label": "Restricted" - }, - { - "doctype": "DocField", - "fieldname": "write", - "fieldtype": "Check", - "hidden": 0, - "in_list_view": 1, - "label": "Write", - "oldfieldname": "write", - "oldfieldtype": "Check", - "print_width": "32px", - "reqd": 0, - "search_index": 0, - "width": "32px" - }, - { - "doctype": "DocField", - "fieldname": "create", - "fieldtype": "Check", - "hidden": 0, - "in_list_view": 1, - "label": "Create", - "oldfieldname": "create", - "oldfieldtype": "Check", - "print_width": "32px", - "reqd": 0, - "search_index": 0, - "width": "32px" - }, - { - "doctype": "DocField", - "fieldname": "delete", - "fieldtype": "Check", - "in_list_view": 1, - "label": "Delete" - }, - { - "doctype": "DocField", - "fieldname": "column_break_8", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "submit", - "fieldtype": "Check", - "hidden": 0, - "in_list_view": 1, - "label": "Submit", - "oldfieldname": "submit", - "oldfieldtype": "Check", - "print_width": "32px", - "reqd": 0, - "search_index": 0, - "width": "32px" - }, - { - "doctype": "DocField", - "fieldname": "cancel", - "fieldtype": "Check", - "hidden": 0, - "in_list_view": 1, - "label": "Cancel", - "oldfieldname": "cancel", - "oldfieldtype": "Check", - "print_width": "32px", - "reqd": 0, - "search_index": 0, - "width": "32px" - }, - { - "doctype": "DocField", - "fieldname": "amend", - "fieldtype": "Check", - "label": "Amend", - "oldfieldname": "amend", - "oldfieldtype": "Check", - "print_width": "32px", - "width": "32px" - }, - { - "doctype": "DocField", - "fieldname": "additional_permissions", - "fieldtype": "Section Break", - "label": "Additional Permissions" - }, - { - "doctype": "DocField", - "fieldname": "report", - "fieldtype": "Check", - "label": "Report", - "print_width": "32px", - "width": "32px" - }, - { - "doctype": "DocField", - "fieldname": "export", - "fieldtype": "Check", - "label": "Export" - }, - { - "doctype": "DocField", - "fieldname": "import", - "fieldtype": "Check", - "label": "Import" - }, - { - "doctype": "DocField", - "fieldname": "column_break_19", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "print", - "fieldtype": "Check", - "label": "Print" - }, - { - "doctype": "DocField", - "fieldname": "email", - "fieldtype": "Check", - "label": "Email" - }, - { - "description": "This role can restrict users for accessing the record.", - "doctype": "DocField", - "fieldname": "restrict", - "fieldtype": "Check", - "label": "Can Restrict" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json new file mode 100644 index 0000000000..09e8476af7 --- /dev/null +++ b/frappe/core/doctype/doctype/doctype.json @@ -0,0 +1,362 @@ +{ + "allow_attach": 0, + "allow_copy": 0, + "autoname": "Prompt", + "creation": "2013-02-18 13:36:19.000000", + "custom": 0, + "description": "DocType is a Table / Form in the application.", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "sb0", + "fieldtype": "Section Break", + "hidden": 0, + "label": "DocType Details", + "oldfieldtype": "Section Break", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "module", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Module", + "oldfieldname": "module", + "oldfieldtype": "Link", + "options": "Module Def", + "permlevel": 0, + "reqd": 1, + "search_index": 1 + }, + { + "description": "Child Tables are shown as a Grid in other DocTypes.", + "fieldname": "istable", + "fieldtype": "Check", + "hidden": 0, + "label": "Is Child Table", + "oldfieldname": "istable", + "oldfieldtype": "Check", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "description": "Single Types have only one record no tables associated. Values are stored in tabSingles", + "fieldname": "issingle", + "fieldtype": "Check", + "hidden": 0, + "label": "Is Single", + "oldfieldname": "issingle", + "oldfieldtype": "Check", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "cb01", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "document_type", + "fieldtype": "Select", + "label": "Document Type", + "oldfieldname": "document_type", + "oldfieldtype": "Select", + "options": "\nMaster\nTransaction\nSystem\nOther", + "permlevel": 0 + }, + { + "fieldname": "icon", + "fieldtype": "Data", + "label": "Icon", + "permlevel": 0 + }, + { + "fieldname": "custom", + "fieldtype": "Check", + "label": "Custom?", + "no_copy": 0, + "permlevel": 0 + }, + { + "fieldname": "plugin", + "fieldtype": "Data", + "hidden": 1, + "label": "Plugin", + "permlevel": 0, + "read_only": 0 + }, + { + "fieldname": "fields_section_break", + "fieldtype": "Section Break", + "hidden": 0, + "label": "Fields", + "oldfieldtype": "Section Break", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "fields", + "fieldtype": "Table", + "hidden": 0, + "label": "Fields", + "oldfieldname": "fields", + "oldfieldtype": "Table", + "options": "DocField", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "sb1", + "fieldtype": "Section Break", + "label": "Naming", + "permlevel": 0 + }, + { + "description": "\\\n
  • field:[fieldname] - By Field\\\n
  • naming_series: - By Naming Series (field called naming_series must be present\\\n
  • Prompt - Prompt user for a name\\\n
  • [series] - Series by prefix (separated by a dot); for example PRE.#####\\\n')\">Naming Options", + "fieldname": "autoname", + "fieldtype": "Data", + "hidden": 0, + "label": "Auto Name", + "oldfieldname": "autoname", + "oldfieldtype": "Data", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "name_case", + "fieldtype": "Select", + "label": "Name Case", + "oldfieldname": "name_case", + "oldfieldtype": "Select", + "options": "\nTitle Case\nUPPER CASE", + "permlevel": 0 + }, + { + "fieldname": "column_break_15", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "description": "Show this field as title", + "fieldname": "title_field", + "fieldtype": "Data", + "label": "Title Field", + "permlevel": 0 + }, + { + "fieldname": "search_fields", + "fieldtype": "Data", + "hidden": 0, + "label": "Search Fields", + "oldfieldname": "search_fields", + "oldfieldtype": "Data", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "sb2", + "fieldtype": "Section Break", + "label": "Permission Rules", + "permlevel": 0 + }, + { + "fieldname": "permissions", + "fieldtype": "Table", + "hidden": 0, + "label": "Permissions", + "oldfieldname": "permissions", + "oldfieldtype": "Table", + "options": "DocPerm", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "sb3", + "fieldtype": "Section Break", + "permlevel": 0 + }, + { + "fieldname": "cb30", + "fieldtype": "Column Break", + "label": "Permissions Settings", + "permlevel": 0 + }, + { + "fieldname": "in_create", + "fieldtype": "Check", + "label": "User Cannot Create", + "oldfieldname": "in_create", + "oldfieldtype": "Check", + "permlevel": 0 + }, + { + "fieldname": "read_only", + "fieldtype": "Check", + "hidden": 0, + "label": "User Cannot Search", + "oldfieldname": "read_only", + "oldfieldtype": "Check", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "is_submittable", + "fieldtype": "Check", + "label": "Is Submittable", + "permlevel": 0 + }, + { + "description": "Allow Import via Data Import Tool", + "fieldname": "allow_import", + "fieldtype": "Check", + "label": "Allow Import", + "permlevel": 0 + }, + { + "fieldname": "allow_rename", + "fieldtype": "Check", + "label": "Allow Rename", + "oldfieldname": "allow_rename", + "oldfieldtype": "Check", + "permlevel": 0 + }, + { + "fieldname": "in_dialog", + "fieldtype": "Check", + "label": "In Dialog", + "oldfieldname": "in_dialog", + "oldfieldtype": "Check", + "permlevel": 0 + }, + { + "fieldname": "read_only_onload", + "fieldtype": "Check", + "label": "Show Print First", + "oldfieldname": "read_only_onload", + "oldfieldtype": "Check", + "permlevel": 0 + }, + { + "fieldname": "allow_attach", + "fieldtype": "Check", + "label": "Allow Attach", + "oldfieldname": "allow_attach", + "oldfieldtype": "Check", + "permlevel": 0 + }, + { + "fieldname": "max_attachments", + "fieldtype": "Int", + "hidden": 1, + "label": "Max Attachments", + "oldfieldname": "max_attachments", + "oldfieldtype": "Int", + "permlevel": 0 + }, + { + "fieldname": "cb31", + "fieldtype": "Column Break", + "label": "Hide Actions", + "permlevel": 0 + }, + { + "fieldname": "hide_heading", + "fieldtype": "Check", + "hidden": 0, + "label": "Hide Heading", + "oldfieldname": "hide_heading", + "oldfieldtype": "Check", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "hide_toolbar", + "fieldtype": "Check", + "hidden": 0, + "label": "Hide Toolbar", + "oldfieldname": "hide_toolbar", + "oldfieldtype": "Check", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "allow_copy", + "fieldtype": "Check", + "hidden": 0, + "label": "Hide Copy", + "oldfieldname": "allow_copy", + "oldfieldtype": "Check", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "default_print_format", + "fieldtype": "Data", + "label": "Default Print Format", + "permlevel": 0 + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-bolt", + "idx": 1, + "issingle": 0, + "istable": 0, + "modified": "2014-01-27 11:57:14.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "DocType", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0 + }, + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "submit": 0, + "write": 1 + } + ], + "read_only": 0, + "search_fields": "module" +} \ No newline at end of file diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 8bac02bc72..614183ecee 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1,5 +1,5 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt from __future__ import unicode_literals @@ -9,60 +9,59 @@ import os from frappe.utils import now, cint from frappe.model import no_value_fields +from frappe.model.document import Document -class DocType: - def __init__(self, doc=None, doclist=[]): - self.doc = doc - self.doclist = doclist - +class DocType(Document): def validate(self): if not frappe.conf.get("developer_mode"): frappe.throw("Not in Developer Mode! Set in site_config.json") for c in [".", "/", "#", "&", "=", ":", "'", '"']: - if c in self.doc.name: + if c in self.name: frappe.msgprint(c + " not allowed in name", raise_exception=1) + for d in self.get("fields"): + d.parent = self.name self.validate_series() self.scrub_field_names() self.validate_title_field() - validate_fields(self.doclist.get({"doctype":"DocField"})) - validate_permissions(self.doclist.get({"doctype":"DocPerm"})) + validate_fields(self.get("fields")) + validate_permissions(self.get("permissions")) self.make_amendable() self.check_link_replacement_error() def change_modified_of_parent(self): if frappe.flags.in_import: return - parent_list = frappe.db.sql("""SELECT parent - from tabDocField where fieldtype="Table" and options=%s""", self.doc.name) + parent_list = frappe.db.sql("""SELECT parent + from tabDocField where fieldtype="Table" and options=%s""", self.name) for p in parent_list: frappe.db.sql('UPDATE tabDocType SET modified=%s WHERE `name`=%s', (now(), p[0])) def scrub_field_names(self): restricted = ('name','parent','idx','owner','creation','modified','modified_by', 'parentfield','parenttype',"file_list") - for d in self.doclist: - if d.parent and d.fieldtype: - if (not d.fieldname): + for d in self.get("fields"): + if d.fieldtype: + if (not getattr(d, "fieldname", None)): if d.label: d.fieldname = d.label.strip().lower().replace(' ','_') if d.fieldname in restricted: d.fieldname = d.fieldname + '1' else: d.fieldname = d.fieldtype.lower().replace(" ","_") + "_" + str(d.idx) - - + + def validate_title_field(self): - if self.doc.title_field and \ - self.doc.title_field not in [d.fieldname for d in self.doclist.get({"doctype":"DocField"})]: + if self.title_field and \ + self.title_field not in [d.fieldname for d in self.get("fields")]: frappe.throw(_("Title field must be a valid fieldname")) - + def validate_series(self, autoname=None, name=None): - if not autoname: autoname = self.doc.autoname - if not name: name = self.doc.name - - if not autoname and self.doclist.get({"fieldname":"naming_series"}): - self.doc.autoname = "naming_series:" - + if not autoname: autoname = self.autoname + if not name: name = self.name + + if not autoname and self.get("fields", {"fieldname":"naming_series"}): + self.autoname = "naming_series:" + if autoname and (not autoname.startswith('field:')) and (not autoname.startswith('eval:')) \ and (not autoname=='Prompt') and (not autoname.startswith('naming_series:')): prefix = autoname.split('.')[0] @@ -72,140 +71,138 @@ class DocType: def on_update(self): from frappe.model.db_schema import updatedb - updatedb(self.doc.name) + updatedb(self.name) self.change_modified_of_parent() - make_module_and_roles(self.doclist) - + make_module_and_roles(self) + from frappe import conf if (not frappe.flags.in_import) and conf.get('developer_mode') or 0: self.export_doc() self.make_controller_template() - + # update index - if not self.doc.custom: - from frappe.model.code import load_doctype_module - module = load_doctype_module( self.doc.name, self.doc.module) + if not getattr(self, "custom", False): + from frappe.modules import load_doctype_module + module = load_doctype_module(self.name, self.module) if hasattr(module, "on_doctype_update"): module.on_doctype_update() - frappe.clear_cache(doctype=self.doc.name) + frappe.clear_cache(doctype=self.name) def check_link_replacement_error(self): - for d in self.doclist.get({"doctype":"DocField", "fieldtype":"Select"}): + 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.msgprint("link: type Select fields are getting replaced. Please check for %s" % d.label, raise_exception=True) def on_trash(self): - frappe.db.sql("delete from `tabCustom Field` where dt = %s", self.doc.name) - frappe.db.sql("delete from `tabCustom Script` where dt = %s", self.doc.name) - frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", self.doc.name) - frappe.db.sql("delete from `tabReport` where ref_doctype=%s", self.doc.name) - + frappe.db.sql("delete from `tabCustom Field` where dt = %s", self.name) + frappe.db.sql("delete from `tabCustom Script` where dt = %s", self.name) + frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", self.name) + frappe.db.sql("delete from `tabReport` where ref_doctype=%s", self.name) + def before_rename(self, old, new, merge=False): if merge: frappe.throw(_("DocType can not be merged")) - + def after_rename(self, old, new, merge=False): - if self.doc.issingle: + if self.issingle: frappe.db.sql("""update tabSingles set doctype=%s where doctype=%s""", (new, old)) else: frappe.db.sql("rename table `tab%s` to `tab%s`" % (old, new)) - + def export_doc(self): from frappe.modules.export_file import export_to_files - export_to_files(record_list=[['DocType', self.doc.name]]) - + export_to_files(record_list=[['DocType', self.name]]) + def import_doc(self): from frappe.modules.import_module import import_from_files - import_from_files(record_list=[[self.doc.module, 'doctype', self.doc.name]]) + import_from_files(record_list=[[self.module, 'doctype', self.name]]) def make_controller_template(self): from frappe.modules import get_doc_path, get_module_path, scrub - - pypath = os.path.join(get_doc_path(self.doc.module, - self.doc.doctype, self.doc.name), scrub(self.doc.name) + '.py') + + pypath = os.path.join(get_doc_path(self.module, + self.doctype, self.name), scrub(self.name) + '.py') if not os.path.exists(pypath): # get app publisher for copyright - app = frappe.local.module_app[frappe.scrub(self.doc.module)] + app = frappe.local.module_app[frappe.scrub(self.module)] if not app: frappe.throw("App not found!") app_publisher = frappe.get_hooks(hook="app_publisher", app_name=app)[0] - + with open(pypath, 'w') as pyfile: - with open(os.path.join(get_module_path("core"), "doctype", "doctype", + with open(os.path.join(get_module_path("core"), "doctype", "doctype", "doctype_template.py"), 'r') as srcfile: - pyfile.write(srcfile.read().format(app_publisher=app_publisher)) - + pyfile.write(srcfile.read().format(app_publisher=app_publisher, classname=self.name.replace(" ", ""))) + def make_amendable(self): """ if is_submittable is set, add amended_from docfields """ - if self.doc.is_submittable: - if not frappe.db.sql("""select name from tabDocField - where fieldname = 'amended_from' and parent = %s""", self.doc.name): - new = self.doc.addchild('fields', 'DocField', self.doclist) - new.label = 'Amended From' - new.fieldtype = 'Link' - new.fieldname = 'amended_from' - new.options = self.doc.name - new.permlevel = 0 - new.read_only = 1 - new.print_hide = 1 - new.no_copy = 1 - new.idx = self.get_max_idx() + 1 - + if self.is_submittable: + if not frappe.db.sql("""select name from tabDocField + where fieldname = 'amended_from' and parent = %s""", self.name): + self.append("fields", { + "label": "Amended From", + "fieldtype": "Link", + "fieldname": "amended_from", + "options": self.name, + "read_only": 1, + "print_hide": 1, + "no_copy": 1 + }) + def get_max_idx(self): - max_idx = frappe.db.sql("""select max(idx) from `tabDocField` where parent = %s""", - self.doc.name) + max_idx = frappe.db.sql("""select max(idx) from `tabDocField` where parent = %s""", + self.name) return max_idx and max_idx[0][0] or 0 def validate_fields_for_doctype(doctype): - from frappe.model.doctype import get - validate_fields(get(doctype, cached=False).get({"parent":doctype, - "doctype":"DocField"})) - + validate_fields(frappe.get_meta(doctype).get("fields")) + def validate_fields(fields): def check_illegal_characters(fieldname): - for c in ['.', ',', ' ', '-', '&', '%', '=', '"', "'", '*', '$', + for c in ['.', ',', ' ', '-', '&', '%', '=', '"', "'", '*', '$', '(', ')', '[', ']', '/']: if c in fieldname: frappe.msgprint("'%s' not allowed in fieldname (%s)" % (c, fieldname)) - + 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.msgprint('Fieldname %s appears more than once in rows (%s). Please rectify' \ % (fieldname, ', '.join(duplicates)), raise_exception=1) - + def check_illegal_mandatory(d): if d.fieldtype in ('HTML', 'Button', 'Section Break', 'Column Break') and d.reqd: - frappe.msgprint('%(label)s [%(fieldtype)s] cannot be mandatory' % d.fields, + print d.fieldname, d.reqd + frappe.msgprint('%(parent)s, %(label)s [%(fieldtype)s] cannot be mandatory' % d.as_dict(), raise_exception=1) - + def check_link_table_options(d): if d.fieldtype in ("Link", "Table"): if not d.options: - frappe.msgprint("""#%(idx)s %(label)s: Options must be specified for Link and Table type fields""" % d.fields, + frappe.msgprint("""#%(idx)s %(label)s: Options must be specified for Link and Table type fields""" % d.as_dict(), raise_exception=1) - if d.options=="[Select]": + if d.options=="[Select]" or d.options==d.parent: return if d.options != d.parent and not frappe.db.exists("DocType", d.options): - frappe.msgprint("""#%(idx)s %(label)s: Options %(options)s must be a valid "DocType" for Link and Table type fields""" % d.fields, + frappe.msgprint("""#%(idx)s %(label)s: Options %(options)s must be a valid "DocType" for Link and Table type fields""" % d.as_dict(), raise_exception=1) def check_hidden_and_mandatory(d): if d.hidden and d.reqd and not d.default: - frappe.msgprint("""#%(idx)s %(label)s: Cannot be hidden and mandatory (reqd) without default""" % d.fields, + frappe.msgprint("""#%(idx)s %(label)s: Cannot be hidden and mandatory (reqd) without default""" % d.as_dict(), raise_exception=True) 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 - + def check_width(d): if d.fieldtype == "Currency" and cint(d.width) < 100: frappe.msgprint("Minimum width for FieldType 'Currency' is 100px", raise_exception=1) @@ -228,49 +225,53 @@ def validate_fields(fields): check_min_items_in_list(fields) def validate_permissions_for_doctype(doctype, for_remove=False): - from frappe.model.doctype import get - validate_permissions(get(doctype, cached=False).get({"parent":doctype, - "doctype":"DocPerm"}), for_remove) + validate_permissions(frappe.get_meta(doctype, cached=False).get("permissions"), for_remove) def validate_permissions(permissions, for_remove=False): doctype = permissions and permissions[0].parent issingle = issubmittable = isimportable = False - if doctype and not doctype.startswith("New DocType"): - values = frappe.db.get_value("DocType", doctype, - ["issingle", "is_submittable", "allow_import"], as_dict=True) + values = frappe.db.get_value("DocType", doctype, + ["issingle", "is_submittable", "allow_import"], as_dict=True) + if values: issingle = cint(values.issingle) issubmittable = cint(values.is_submittable) isimportable = cint(values.allow_import) def get_txt(d): return "For %s (level %s) in %s, row #%s:" % (d.role, d.permlevel, d.parent, d.idx) - + def check_atleast_one_set(d): if not d.read and not d.write and not d.submit and not d.cancel and not d.create: frappe.msgprint(get_txt(d) + " Atleast one of Read, Write, Create, Submit, Cancel must be set.", raise_exception=True) - + def check_double(d): - similar = permissions.get({ - "role":d.role, - "permlevel":d.permlevel, - "match": d.match - }) - - if len(similar) > 1: - frappe.msgprint(get_txt(d) + " Only one rule allowed for a particular Role and Level.", + has_similar = False + for p in permissions: + if p.role==d.role and p.permlevel==d.permlevel and p.match==d.match and p!=d: + has_similar = True + break + + if has_similar: + frappe.msgprint(get_txt(d) + " Only one rule allowed for a particular Role and Level.", raise_exception=True) - + def check_level_zero_is_set(d): if cint(d.permlevel) > 0 and d.role != 'All': - if not permissions.get({"role": d.role, "permlevel": 0}): + has_zero_perm = False + for p in permissions: + if p.role==d.role and (p.permlevel or 0)==0 and p!=d: + has_zero_perm = True + break + + if not has_zero_perm: frappe.msgprint(get_txt(d) + " Higher level permissions are meaningless if level 0 permission is not set.", raise_exception=True) - - if d.create or d.submit or d.cancel or d.amend or d.match: + + if d.create or d.submit or d.cancel or d.amend or d.match: frappe.msgprint("Create, Submit, Cancel, Amend, Match has no meaning at level " + d.permlevel, raise_exception=True) - + def check_permission_dependency(d): if d.cancel and not d.submit: frappe.msgprint(get_txt(d) + " Cannot set Cancel permission if Submit is not set.", @@ -281,29 +282,29 @@ def validate_permissions(permissions, for_remove=False): if d.amend and not d.write: frappe.msgprint(get_txt(d) + " Cannot set Amend if Cancel is not set.", raise_exception=True) - if (d.fields.get("import") or d.export) and not d.report: + if (d.get("import") or d.export) and not d.report: frappe.msgprint(get_txt(d) + " Cannot set Import or Export permission if Report is not set.", raise_exception=True) - if d.fields.get("import") and not d.create: + if d.get("import") and not d.create: frappe.msgprint(get_txt(d) + " Cannot set Import if Create is not set.", raise_exception=True) - + def remove_rights_for_single(d): if not issingle: return - + if d.report: frappe.msgprint("{doctype} {meaningless}".format(doctype=doctype, meaningless=_("is a single DocType, permission of type Report is meaningless."))) d.report = 0 - d.fields["import"] = 0 - d.fields["export"] = 0 - + d.set("import", 0) + d.set("export", 0) + if d.restrict: frappe.msgprint("{doctype} {meaningless}".format(doctype=doctype, meaningless=_("is a single DocType, permission of type Restrict is meaningless."))) d.restrict = 0 - + def check_if_submittable(d): if d.submit and not issubmittable: frappe.msgprint(doctype + " is not Submittable, cannot assign submit rights.", @@ -311,14 +312,14 @@ def validate_permissions(permissions, for_remove=False): elif d.amend and not issubmittable: frappe.msgprint(doctype + " is not Submittable, cannot assign amend rights.", raise_exception=True) - + def check_if_importable(d): - if d.fields.get("import") and not isimportable: + if d.get("import") and not isimportable: frappe.throw("{doctype}: {not_importable}".format(doctype=doctype, not_importable=_("is not allowed to be imported, cannot assign import rights."))) - + for d in permissions: - if not d.permlevel: + if not d.permlevel: d.permlevel=0 check_atleast_one_set(d) if not for_remove: @@ -329,19 +330,22 @@ def validate_permissions(permissions, for_remove=False): check_level_zero_is_set(d) remove_rights_for_single(d) -def make_module_and_roles(doclist, perm_doctype="DocPerm"): +def make_module_and_roles(doc, perm_fieldname="permissions"): try: - if not frappe.db.exists("Module Def", doclist[0].module): - m = frappe.bean({"doctype": "Module Def", "module_name": doclist[0].module}) + if not frappe.db.exists("Module Def", doc.module): + m = frappe.get_doc({"doctype": "Module Def", "module_name": doc.module}) + m.app_name = frappe.local.module_app[frappe.scrub(doc.module)] + m.ignore_mandatory = m.ignore_permissions = True m.insert() - + default_roles = ["Administrator", "Guest", "All"] - roles = [p.role for p in doclist.get({"doctype": perm_doctype})] + default_roles - + roles = [p.role for p in doc.get("permissions") or []] + default_roles + for role in list(set(roles)): if not frappe.db.exists("Role", role): - r = frappe.bean({"doctype": "Role", "role_name": role}) - r.doc.role_name = role + r = frappe.get_doc({"doctype": "Role", "role_name": role}) + r.role_name = role + r.ignore_mandatory = r.ignore_permissions = True r.insert() except frappe.DoesNotExistError, e: pass diff --git a/frappe/core/doctype/doctype/doctype.txt b/frappe/core/doctype/doctype/doctype.txt deleted file mode 100644 index a155cf9ace..0000000000 --- a/frappe/core/doctype/doctype/doctype.txt +++ /dev/null @@ -1,375 +0,0 @@ -[ - { - "creation": "2013-02-18 13:36:19", - "docstatus": 0, - "modified": "2014-01-27 11:57:14", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 0, - "allow_copy": 0, - "autoname": "Prompt", - "custom": 0, - "description": "DocType is a Table / Form in the application.", - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-bolt", - "issingle": 0, - "istable": 0, - "module": "Core", - "name": "__common__", - "read_only": 0, - "search_fields": "module" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "DocType", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "DocType", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "submit": 0 - }, - { - "doctype": "DocType", - "name": "DocType" - }, - { - "doctype": "DocField", - "fieldname": "sb0", - "fieldtype": "Section Break", - "hidden": 0, - "label": "DocType Details", - "oldfieldtype": "Section Break", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "module", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Module", - "oldfieldname": "module", - "oldfieldtype": "Link", - "options": "Module Def", - "reqd": 1, - "search_index": 1 - }, - { - "description": "Child Tables are shown as a Grid in other DocTypes.", - "doctype": "DocField", - "fieldname": "istable", - "fieldtype": "Check", - "hidden": 0, - "label": "Is Child Table", - "oldfieldname": "istable", - "oldfieldtype": "Check", - "reqd": 0, - "search_index": 0 - }, - { - "description": "Single Types have only one record no tables associated. Values are stored in tabSingles", - "doctype": "DocField", - "fieldname": "issingle", - "fieldtype": "Check", - "hidden": 0, - "label": "Is Single", - "oldfieldname": "issingle", - "oldfieldtype": "Check", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "cb01", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "document_type", - "fieldtype": "Select", - "label": "Document Type", - "oldfieldname": "document_type", - "oldfieldtype": "Select", - "options": "\nMaster\nTransaction\nSystem\nOther" - }, - { - "doctype": "DocField", - "fieldname": "icon", - "fieldtype": "Data", - "label": "Icon" - }, - { - "doctype": "DocField", - "fieldname": "custom", - "fieldtype": "Check", - "label": "Custom?", - "no_copy": 0 - }, - { - "doctype": "DocField", - "fieldname": "plugin", - "fieldtype": "Data", - "hidden": 1, - "label": "Plugin", - "read_only": 0 - }, - { - "doctype": "DocField", - "fieldname": "fields_section_break", - "fieldtype": "Section Break", - "hidden": 0, - "label": "Fields", - "oldfieldtype": "Section Break", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "fields", - "fieldtype": "Table", - "hidden": 0, - "label": "Fields", - "oldfieldname": "fields", - "oldfieldtype": "Table", - "options": "DocField", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "sb1", - "fieldtype": "Section Break", - "label": "Naming" - }, - { - "description": "\\\n
  • field:[fieldname] - By Field\\\n
  • naming_series: - By Naming Series (field called naming_series must be present\\\n
  • Prompt - Prompt user for a name\\\n
  • [series] - Series by prefix (separated by a dot); for example PRE.#####\\\n')\">Naming Options", - "doctype": "DocField", - "fieldname": "autoname", - "fieldtype": "Data", - "hidden": 0, - "label": "Auto Name", - "oldfieldname": "autoname", - "oldfieldtype": "Data", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "name_case", - "fieldtype": "Select", - "label": "Name Case", - "oldfieldname": "name_case", - "oldfieldtype": "Select", - "options": "\nTitle Case\nUPPER CASE" - }, - { - "doctype": "DocField", - "fieldname": "column_break_15", - "fieldtype": "Column Break" - }, - { - "description": "Show this field as title", - "doctype": "DocField", - "fieldname": "title_field", - "fieldtype": "Data", - "label": "Title Field" - }, - { - "doctype": "DocField", - "fieldname": "search_fields", - "fieldtype": "Data", - "hidden": 0, - "label": "Search Fields", - "oldfieldname": "search_fields", - "oldfieldtype": "Data", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "sb2", - "fieldtype": "Section Break", - "label": "Permission Rules" - }, - { - "doctype": "DocField", - "fieldname": "permissions", - "fieldtype": "Table", - "hidden": 0, - "label": "Permissions", - "oldfieldname": "permissions", - "oldfieldtype": "Table", - "options": "DocPerm", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "sb3", - "fieldtype": "Section Break" - }, - { - "doctype": "DocField", - "fieldname": "cb30", - "fieldtype": "Column Break", - "label": "Permissions Settings" - }, - { - "doctype": "DocField", - "fieldname": "in_create", - "fieldtype": "Check", - "label": "User Cannot Create", - "oldfieldname": "in_create", - "oldfieldtype": "Check" - }, - { - "doctype": "DocField", - "fieldname": "read_only", - "fieldtype": "Check", - "hidden": 0, - "label": "User Cannot Search", - "oldfieldname": "read_only", - "oldfieldtype": "Check", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "is_submittable", - "fieldtype": "Check", - "label": "Is Submittable" - }, - { - "description": "Allow Import via Data Import Tool", - "doctype": "DocField", - "fieldname": "allow_import", - "fieldtype": "Check", - "label": "Allow Import" - }, - { - "doctype": "DocField", - "fieldname": "allow_rename", - "fieldtype": "Check", - "label": "Allow Rename", - "oldfieldname": "allow_rename", - "oldfieldtype": "Check" - }, - { - "doctype": "DocField", - "fieldname": "in_dialog", - "fieldtype": "Check", - "label": "In Dialog", - "oldfieldname": "in_dialog", - "oldfieldtype": "Check" - }, - { - "doctype": "DocField", - "fieldname": "read_only_onload", - "fieldtype": "Check", - "label": "Show Print First", - "oldfieldname": "read_only_onload", - "oldfieldtype": "Check" - }, - { - "doctype": "DocField", - "fieldname": "allow_attach", - "fieldtype": "Check", - "label": "Allow Attach", - "oldfieldname": "allow_attach", - "oldfieldtype": "Check" - }, - { - "doctype": "DocField", - "fieldname": "max_attachments", - "fieldtype": "Int", - "hidden": 1, - "label": "Max Attachments", - "oldfieldname": "max_attachments", - "oldfieldtype": "Int" - }, - { - "doctype": "DocField", - "fieldname": "cb31", - "fieldtype": "Column Break", - "label": "Hide Actions" - }, - { - "doctype": "DocField", - "fieldname": "hide_heading", - "fieldtype": "Check", - "hidden": 0, - "label": "Hide Heading", - "oldfieldname": "hide_heading", - "oldfieldtype": "Check", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "hide_toolbar", - "fieldtype": "Check", - "hidden": 0, - "label": "Hide Toolbar", - "oldfieldname": "hide_toolbar", - "oldfieldtype": "Check", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "allow_copy", - "fieldtype": "Check", - "hidden": 0, - "label": "Hide Copy", - "oldfieldname": "allow_copy", - "oldfieldtype": "Check", - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "default_print_format", - "fieldtype": "Data", - "label": "Default Print Format" - }, - { - "doctype": "DocField", - "fieldname": "description", - "fieldtype": "Small Text", - "hidden": 0, - "label": "Description", - "oldfieldname": "description", - "oldfieldtype": "Text", - "reqd": 0, - "search_index": 0 - }, - { - "delete": 0, - "doctype": "DocPerm", - "role": "System Manager" - }, - { - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "role": "Administrator", - "write": 1 - } -] \ No newline at end of file diff --git a/frappe/core/doctype/doctype/doctype_template.py b/frappe/core/doctype/doctype/doctype_template.py index bb4f563ecf..369bd592d8 100644 --- a/frappe/core/doctype/doctype/doctype_template.py +++ b/frappe/core/doctype/doctype/doctype_template.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe.model.document import Document -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +class {classname}(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/event/event.json b/frappe/core/doctype/event/event.json new file mode 100644 index 0000000000..7c32762656 --- /dev/null +++ b/frappe/core/doctype/event/event.json @@ -0,0 +1,282 @@ +{ + "autoname": "EV.#####", + "creation": "2013-06-10 13:17:47.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "details", + "fieldtype": "Section Break", + "label": "Details", + "oldfieldtype": "Section Break", + "permlevel": 0 + }, + { + "fieldname": "subject", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Subject", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "event_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Event Type", + "no_copy": 0, + "oldfieldname": "event_type", + "oldfieldtype": "Select", + "options": "Private\nPublic\nCancel", + "permlevel": 0, + "reqd": 1, + "search_index": 1 + }, + { + "default": "1", + "fieldname": "send_reminder", + "fieldtype": "Check", + "label": "Send an email reminder in the morning", + "permlevel": 0 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "all_day", + "fieldtype": "Check", + "label": "All Day", + "permlevel": 0 + }, + { + "fieldname": "starts_on", + "fieldtype": "Datetime", + "label": "Starts on", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "ends_on", + "fieldtype": "Datetime", + "label": "Ends on", + "permlevel": 0, + "reqd": 0 + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "permlevel": 0 + }, + { + "fieldname": "repeat_this_event", + "fieldtype": "Check", + "label": "Repeat this Event", + "permlevel": 0 + }, + { + "depends_on": "repeat_this_event", + "fieldname": "section_break_11", + "fieldtype": "Section Break", + "permlevel": 0 + }, + { + "depends_on": "repeat_this_event", + "fieldname": "repeat_on", + "fieldtype": "Select", + "label": "Repeat On", + "options": "\nEvery Day\nEvery Week\nEvery Month\nEvery Year", + "permlevel": 0 + }, + { + "depends_on": "repeat_this_event", + "description": "Leave blank to repeat always", + "fieldname": "repeat_till", + "fieldtype": "Date", + "label": "Repeat Till", + "permlevel": 0 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "fieldname": "monday", + "fieldtype": "Check", + "label": "Monday", + "permlevel": 0 + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "fieldname": "tuesday", + "fieldtype": "Check", + "label": "Tuesday", + "permlevel": 0 + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "fieldname": "wednesday", + "fieldtype": "Check", + "label": "Wednesday", + "permlevel": 0 + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "fieldname": "thursday", + "fieldtype": "Check", + "label": "Thursday", + "permlevel": 0 + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "fieldname": "friday", + "fieldtype": "Check", + "label": "Friday", + "permlevel": 0 + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "fieldname": "saturday", + "fieldtype": "Check", + "label": "Saturday", + "permlevel": 0 + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "fieldname": "sunday", + "fieldtype": "Check", + "label": "Sunday", + "permlevel": 0 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "permlevel": 0 + }, + { + "fieldname": "description", + "fieldtype": "Text Editor", + "in_list_view": 0, + "label": "Description", + "no_copy": 0, + "oldfieldname": "description", + "oldfieldtype": "Text", + "permlevel": 0, + "print_width": "300px", + "search_index": 0, + "width": "300px" + }, + { + "fieldname": "participants", + "fieldtype": "Section Break", + "label": "Participants", + "oldfieldtype": "Section Break", + "permlevel": 0 + }, + { + "fieldname": "individuals", + "fieldtype": "Column Break", + "label": "Individuals", + "oldfieldtype": "Column Break", + "permlevel": 0, + "print_width": "50%", + "width": "50%" + }, + { + "fieldname": "event_individuals", + "fieldtype": "Table", + "label": "Event Individuals", + "no_copy": 0, + "oldfieldname": "event_individuals", + "oldfieldtype": "Table", + "options": "Event User", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "groups", + "fieldtype": "Column Break", + "label": "Groups", + "oldfieldtype": "Column Break", + "permlevel": 0, + "print_width": "50%", + "width": "50%" + }, + { + "fieldname": "event_roles", + "fieldtype": "Table", + "label": "Event Roles", + "no_copy": 0, + "oldfieldname": "event_roles", + "oldfieldtype": "Table", + "options": "Event Role", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "ref_type", + "fieldtype": "Data", + "hidden": 0, + "label": "Ref Type", + "no_copy": 0, + "oldfieldname": "ref_type", + "oldfieldtype": "Data", + "permlevel": 0, + "read_only": 1, + "search_index": 0 + }, + { + "fieldname": "ref_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Ref Name", + "no_copy": 0, + "oldfieldname": "ref_name", + "oldfieldtype": "Data", + "permlevel": 0, + "read_only": 1, + "search_index": 0 + } + ], + "icon": "icon-calendar", + "idx": 1, + "in_create": 1, + "modified": "2014-01-24 13:00:01.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Event", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + } + ], + "read_only": 1, + "title_field": "subject" +} \ No newline at end of file diff --git a/frappe/core/doctype/event/event.py b/frappe/core/doctype/event/event.py index 9e99b08f0a..2923a778ea 100644 --- a/frappe/core/doctype/event/event.py +++ b/frappe/core/doctype/event/event.py @@ -14,14 +14,6 @@ class Event(Document): def validate(self): if self.starts_on and self.ends_on and self.starts_on > self.ends_on: frappe.msgprint(frappe._("Event End must be after Start"), raise_exception=True) - -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl - - def validate(self): - if self.doc.starts_on and self.doc.ends_on and self.doc.starts_on > self.doc.ends_on: - frappe.msgprint(frappe._("Event End must be after Start"), raise_exception=True) def get_permission_query_conditions(): return """(tabEvent.event_type='Public' or tabEvent.owner='%(user)s' @@ -35,21 +27,14 @@ def get_permission_query_conditions(): "roles": "', '".join(frappe.get_roles(frappe.session.user)) } -def has_permission(doc, bean=None): +def has_permission(doc): if doc.event_type=="Public" or doc.owner==frappe.session.user: return True - - # need full doclist to check roles and users - if not bean: - bean = frappe.bean("Event", doc.name) - - if len(bean.doclist)==1: - return False - - if bean.doclist.get({"doctype":"Event User", "person":frappe.session.user}): + + if doc.get("event_individuals", {"person":frappe.session.user}): return True - if bean.doclist.get({"doctype":"Event Role", "role":("in", frappe.get_roles())}): + if doc.get("event_roles", {"role":("in", frappe.get_roles())}): return True return False diff --git a/frappe/core/doctype/event/event.txt b/frappe/core/doctype/event/event.txt deleted file mode 100644 index 77f8fdd6ad..0000000000 --- a/frappe/core/doctype/event/event.txt +++ /dev/null @@ -1,293 +0,0 @@ -[ - { - "creation": "2013-06-10 13:17:47", - "docstatus": 0, - "modified": "2014-01-24 13:00:01", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "EV.#####", - "doctype": "DocType", - "icon": "icon-calendar", - "in_create": 1, - "module": "Core", - "name": "__common__", - "read_only": 1, - "title_field": "subject" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Event", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Event", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Event" - }, - { - "doctype": "DocField", - "fieldname": "details", - "fieldtype": "Section Break", - "label": "Details", - "oldfieldtype": "Section Break" - }, - { - "doctype": "DocField", - "fieldname": "subject", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Subject", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "event_type", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Event Type", - "no_copy": 0, - "oldfieldname": "event_type", - "oldfieldtype": "Select", - "options": "Private\nPublic\nCancel", - "reqd": 1, - "search_index": 1 - }, - { - "default": "1", - "doctype": "DocField", - "fieldname": "send_reminder", - "fieldtype": "Check", - "label": "Send an email reminder in the morning" - }, - { - "doctype": "DocField", - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "all_day", - "fieldtype": "Check", - "label": "All Day" - }, - { - "doctype": "DocField", - "fieldname": "starts_on", - "fieldtype": "Datetime", - "label": "Starts on", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "ends_on", - "fieldtype": "Datetime", - "label": "Ends on", - "reqd": 0 - }, - { - "doctype": "DocField", - "fieldname": "section_break_8", - "fieldtype": "Section Break" - }, - { - "doctype": "DocField", - "fieldname": "repeat_this_event", - "fieldtype": "Check", - "label": "Repeat this Event" - }, - { - "depends_on": "repeat_this_event", - "doctype": "DocField", - "fieldname": "section_break_11", - "fieldtype": "Section Break" - }, - { - "depends_on": "repeat_this_event", - "doctype": "DocField", - "fieldname": "repeat_on", - "fieldtype": "Select", - "label": "Repeat On", - "options": "\nEvery Day\nEvery Week\nEvery Month\nEvery Year" - }, - { - "depends_on": "repeat_this_event", - "description": "Leave blank to repeat always", - "doctype": "DocField", - "fieldname": "repeat_till", - "fieldtype": "Date", - "label": "Repeat Till" - }, - { - "doctype": "DocField", - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, - { - "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", - "doctype": "DocField", - "fieldname": "monday", - "fieldtype": "Check", - "label": "Monday" - }, - { - "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", - "doctype": "DocField", - "fieldname": "tuesday", - "fieldtype": "Check", - "label": "Tuesday" - }, - { - "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", - "doctype": "DocField", - "fieldname": "wednesday", - "fieldtype": "Check", - "label": "Wednesday" - }, - { - "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", - "doctype": "DocField", - "fieldname": "thursday", - "fieldtype": "Check", - "label": "Thursday" - }, - { - "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", - "doctype": "DocField", - "fieldname": "friday", - "fieldtype": "Check", - "label": "Friday" - }, - { - "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", - "doctype": "DocField", - "fieldname": "saturday", - "fieldtype": "Check", - "label": "Saturday" - }, - { - "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", - "doctype": "DocField", - "fieldname": "sunday", - "fieldtype": "Check", - "label": "Sunday" - }, - { - "doctype": "DocField", - "fieldname": "section_break_6", - "fieldtype": "Section Break" - }, - { - "doctype": "DocField", - "fieldname": "description", - "fieldtype": "Text Editor", - "in_list_view": 0, - "label": "Description", - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Text", - "print_width": "300px", - "search_index": 0, - "width": "300px" - }, - { - "doctype": "DocField", - "fieldname": "participants", - "fieldtype": "Section Break", - "label": "Participants", - "oldfieldtype": "Section Break" - }, - { - "doctype": "DocField", - "fieldname": "individuals", - "fieldtype": "Column Break", - "label": "Individuals", - "oldfieldtype": "Column Break", - "print_width": "50%", - "width": "50%" - }, - { - "doctype": "DocField", - "fieldname": "event_individuals", - "fieldtype": "Table", - "label": "Event Individuals", - "no_copy": 0, - "oldfieldname": "event_individuals", - "oldfieldtype": "Table", - "options": "Event User", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "groups", - "fieldtype": "Column Break", - "label": "Groups", - "oldfieldtype": "Column Break", - "print_width": "50%", - "width": "50%" - }, - { - "doctype": "DocField", - "fieldname": "event_roles", - "fieldtype": "Table", - "label": "Event Roles", - "no_copy": 0, - "oldfieldname": "event_roles", - "oldfieldtype": "Table", - "options": "Event Role", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "ref_type", - "fieldtype": "Data", - "hidden": 0, - "label": "Ref Type", - "no_copy": 0, - "oldfieldname": "ref_type", - "oldfieldtype": "Data", - "read_only": 1, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "ref_name", - "fieldtype": "Data", - "hidden": 0, - "label": "Ref Name", - "no_copy": 0, - "oldfieldname": "ref_name", - "oldfieldtype": "Data", - "read_only": 1, - "search_index": 0 - }, - { - "delete": 0, - "doctype": "DocPerm", - "role": "All" - }, - { - "delete": 1, - "doctype": "DocPerm", - "role": "System Manager" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/event/test_event.py b/frappe/core/doctype/event/test_event.py index a866a85049..4f922a0d3a 100644 --- a/frappe/core/doctype/event/test_event.py +++ b/frappe/core/doctype/event/test_event.py @@ -3,57 +3,33 @@ """Use blog post test to test permission restriction logic""" -test_records = [ - [{ - "doctype": "Event", - "subject":"_Test Event 1", - "starts_on": "2014-01-01", - "event_type": "Public", - }], - [{ - "doctype": "Event", - "starts_on": "2014-01-01", - "subject":"_Test Event 2", - "event_type": "Private", - }], - [{ - "doctype": "Event", - "starts_on": "2014-01-01", - "subject":"_Test Event 3", - "event_type": "Private", - }, { - "doctype": "Event User", - "parentfield": "event_individuals", - "person": "test1@example.com" - }], - -] - import frappe import frappe.defaults import unittest +test_records = frappe.get_test_records('Event') + class TestEvent(unittest.TestCase): # def setUp(self): - # user = frappe.bean("User", "test1@example.com") - # user.get_controller().add_roles("Website Manager") + # user = frappe.get_doc("User", "test1@example.com") + # user.add_roles("Website Manager") def tearDown(self): frappe.set_user("Administrator") def test_allowed_public(self): frappe.set_user("test1@example.com") - doc = frappe.doc("Event", frappe.db.get_value("Event", {"subject":"_Test Event 1"})) + doc = frappe.get_doc("Event", frappe.db.get_value("Event", {"subject":"_Test Event 1"})) self.assertTrue(frappe.has_permission("Event", refdoc=doc)) def test_not_allowed_private(self): frappe.set_user("test1@example.com") - doc = frappe.doc("Event", frappe.db.get_value("Event", {"subject":"_Test Event 2"})) + doc = frappe.get_doc("Event", frappe.db.get_value("Event", {"subject":"_Test Event 2"})) self.assertFalse(frappe.has_permission("Event", refdoc=doc)) def test_allowed_private_if_in_event_user(self): frappe.set_user("test1@example.com") - doc = frappe.doc("Event", frappe.db.get_value("Event", {"subject":"_Test Event 3"})) + doc = frappe.get_doc("Event", frappe.db.get_value("Event", {"subject":"_Test Event 3"})) self.assertTrue(frappe.has_permission("Event", refdoc=doc)) def test_event_list(self): @@ -63,5 +39,4 @@ class TestEvent(unittest.TestCase): subjects = [r.subject for r in res] self.assertTrue("_Test Event 1" in subjects) self.assertTrue("_Test Event 3" in subjects) - self.assertFalse("_Test Event 2" in subjects) - \ No newline at end of file + self.assertFalse("_Test Event 2" in subjects) \ No newline at end of file diff --git a/frappe/core/doctype/event/test_records.json b/frappe/core/doctype/event/test_records.json new file mode 100644 index 0000000000..be094475f1 --- /dev/null +++ b/frappe/core/doctype/event/test_records.json @@ -0,0 +1,23 @@ +[ + { + "doctype": "Event", + "subject":"_Test Event 1", + "starts_on": "2014-01-01", + "event_type": "Public" + }, + { + "doctype": "Event", + "starts_on": "2014-01-01", + "subject":"_Test Event 2", + "event_type": "Private" + }, + { + "doctype": "Event", + "starts_on": "2014-01-01", + "subject":"_Test Event 3", + "event_type": "Private", + "event_individuals": [{ + "person": "test1@example.com" + }] + } +] diff --git a/frappe/core/doctype/event_role/event_role.json b/frappe/core/doctype/event_role/event_role.json new file mode 100644 index 0000000000..906027a61c --- /dev/null +++ b/frappe/core/doctype/event_role/event_role.json @@ -0,0 +1,27 @@ +{ + "autoname": "__EVR.#####", + "creation": "2013-02-22 01:27:33.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "role", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Role", + "oldfieldname": "role", + "oldfieldtype": "Link", + "options": "Role", + "permlevel": 0, + "print_width": "240px", + "width": "240px" + } + ], + "idx": 1, + "istable": 1, + "modified": "2013-12-20 19:23:12.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Event Role", + "owner": "Administrator" +} \ No newline at end of file diff --git a/frappe/core/doctype/event_role/event_role.py b/frappe/core/doctype/event_role/event_role.py index a48f49f2a7..cb24e5f51e 100644 --- a/frappe/core/doctype/event_role/event_role.py +++ b/frappe/core/doctype/event_role/event_role.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class EventRole(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/event_role/event_role.txt b/frappe/core/doctype/event_role/event_role.txt deleted file mode 100644 index 1ef3da2cab..0000000000 --- a/frappe/core/doctype/event_role/event_role.txt +++ /dev/null @@ -1,40 +0,0 @@ -[ - { - "creation": "2013-02-22 01:27:33", - "docstatus": 0, - "modified": "2013-12-20 19:23:12", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "__EVR.#####", - "doctype": "DocType", - "istable": 1, - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "fieldname": "role", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Role", - "name": "__common__", - "oldfieldname": "role", - "oldfieldtype": "Link", - "options": "Role", - "parent": "Event Role", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "print_width": "240px", - "width": "240px" - }, - { - "doctype": "DocType", - "name": "Event Role" - }, - { - "doctype": "DocField" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/event_user/event_user.json b/frappe/core/doctype/event_user/event_user.json new file mode 100644 index 0000000000..8ed3cfe2e0 --- /dev/null +++ b/frappe/core/doctype/event_user/event_user.json @@ -0,0 +1,28 @@ +{ + "autoname": "EVP.#####", + "creation": "2013-02-22 01:27:33.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "person", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Person", + "oldfieldname": "person", + "oldfieldtype": "Select", + "options": "link:User", + "permlevel": 0, + "print_width": "240px", + "search_index": 1, + "width": "240px" + } + ], + "idx": 1, + "istable": 1, + "modified": "2013-12-20 19:23:14.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Event User", + "owner": "Administrator" +} \ No newline at end of file diff --git a/frappe/core/doctype/event_user/event_user.py b/frappe/core/doctype/event_user/event_user.py index a48f49f2a7..3d4591739b 100644 --- a/frappe/core/doctype/event_user/event_user.py +++ b/frappe/core/doctype/event_user/event_user.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class EventUser(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/event_user/event_user.txt b/frappe/core/doctype/event_user/event_user.txt deleted file mode 100644 index d69d954284..0000000000 --- a/frappe/core/doctype/event_user/event_user.txt +++ /dev/null @@ -1,41 +0,0 @@ -[ - { - "creation": "2013-02-22 01:27:33", - "docstatus": 0, - "modified": "2013-12-20 19:23:14", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "EVP.#####", - "doctype": "DocType", - "istable": 1, - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "fieldname": "person", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Person", - "name": "__common__", - "oldfieldname": "person", - "oldfieldtype": "Select", - "options": "link:User", - "parent": "Event User", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "print_width": "240px", - "search_index": 1, - "width": "240px" - }, - { - "doctype": "DocType", - "name": "Event User" - }, - { - "doctype": "DocField" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/file_data/file_data.json b/frappe/core/doctype/file_data/file_data.json new file mode 100644 index 0000000000..cb42e57ad1 --- /dev/null +++ b/frappe/core/doctype/file_data/file_data.json @@ -0,0 +1,255 @@ +{ + "_last_update": null, + "_user_tags": null, + "allow_attach": null, + "allow_copy": null, + "allow_email": null, + "allow_import": null, + "allow_print": null, + "allow_rename": null, + "allow_trash": null, + "autoname": "File.######", + "change_log": null, + "client_script": null, + "client_script_core": null, + "client_string": null, + "colour": null, + "creation": "2012-12-12 11:19:22", + "custom": null, + "default_print_format": null, + "description": null, + "docstatus": 0, + "doctype": "DocType", + "document_type": null, + "dt_template": null, + "fields": [ + { + "allow_on_submit": null, + "default": null, + "depends_on": null, + "description": null, + "fieldname": "file_name", + "fieldtype": "Data", + "hidden": null, + "ignore_restrictions": null, + "in_filter": null, + "in_list_view": 1, + "label": "File Name", + "no_column": null, + "no_copy": null, + "oldfieldname": "file_name", + "oldfieldtype": "Data", + "options": null, + "permlevel": 0, + "print_hide": null, + "print_width": null, + "read_only": 1, + "report_hide": null, + "reqd": null, + "search_index": null, + "set_only_once": null, + "trigger": null, + "width": null + }, + { + "allow_on_submit": null, + "default": null, + "depends_on": null, + "description": null, + "fieldname": "file_url", + "fieldtype": "Data", + "hidden": null, + "ignore_restrictions": null, + "in_filter": null, + "in_list_view": 1, + "label": "File URL", + "no_column": null, + "no_copy": null, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "permlevel": 0, + "print_hide": null, + "print_width": null, + "read_only": 1, + "report_hide": null, + "reqd": null, + "search_index": null, + "set_only_once": null, + "trigger": null, + "width": null + }, + { + "allow_on_submit": null, + "default": null, + "depends_on": null, + "description": null, + "fieldname": "attached_to_doctype", + "fieldtype": "Link", + "hidden": null, + "ignore_restrictions": null, + "in_filter": null, + "in_list_view": 1, + "label": "Attached To DocType", + "no_column": null, + "no_copy": null, + "oldfieldname": null, + "oldfieldtype": null, + "options": "DocType", + "permlevel": 0, + "print_hide": null, + "print_width": null, + "read_only": 1, + "report_hide": null, + "reqd": null, + "search_index": 1, + "set_only_once": null, + "trigger": null, + "width": null + }, + { + "allow_on_submit": null, + "default": null, + "depends_on": null, + "description": null, + "fieldname": "attached_to_name", + "fieldtype": "Data", + "hidden": null, + "ignore_restrictions": null, + "in_filter": null, + "in_list_view": 1, + "label": "Attached To Name", + "no_column": null, + "no_copy": null, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "permlevel": 0, + "print_hide": null, + "print_width": null, + "read_only": 1, + "report_hide": null, + "reqd": null, + "search_index": 1, + "set_only_once": null, + "trigger": null, + "width": null + }, + { + "allow_on_submit": null, + "default": null, + "depends_on": null, + "description": null, + "fieldname": "file_size", + "fieldtype": "Int", + "hidden": null, + "ignore_restrictions": null, + "in_filter": null, + "in_list_view": 1, + "label": "File Size", + "no_column": null, + "no_copy": null, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "permlevel": 0, + "print_hide": null, + "print_width": null, + "read_only": 1, + "report_hide": null, + "reqd": null, + "search_index": null, + "set_only_once": null, + "trigger": null, + "width": null + }, + { + "allow_on_submit": null, + "default": null, + "depends_on": null, + "description": null, + "fieldname": "content_hash", + "fieldtype": "Data", + "hidden": null, + "ignore_restrictions": null, + "in_filter": null, + "in_list_view": null, + "label": "Content Hash", + "no_column": null, + "no_copy": null, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "permlevel": 0, + "print_hide": null, + "print_width": null, + "read_only": null, + "report_hide": null, + "reqd": null, + "search_index": 1, + "set_only_once": null, + "trigger": null, + "width": null + } + ], + "hide_heading": null, + "hide_toolbar": null, + "icon": "icon-file", + "idx": 1, + "in_create": null, + "in_dialog": null, + "is_submittable": null, + "is_transaction_doc": null, + "issingle": null, + "istable": null, + "max_attachments": null, + "menu_index": null, + "modified": "2014-04-07 17:01:13.295614", + "modified_by": "Administrator", + "module": "Core", + "name": "File Data", + "name_case": null, + "owner": "Administrator", + "parent": null, + "parent_node": null, + "parentfield": null, + "parenttype": null, + "permissions": [ + { + "amend": null, + "cancel": 0, + "create": null, + "delete": 1, + "email": 1, + "export": null, + "import": null, + "match": null, + "permlevel": 0, + "print": 1, + "read": 1, + "report": null, + "restrict": null, + "restricted": null, + "role": "System Manager", + "submit": null, + "write": 1 + } + ], + "plugin": null, + "print_outline": null, + "read_only": 0, + "read_only_onload": null, + "search_fields": null, + "section_style": null, + "server_code": null, + "server_code_compiled": null, + "server_code_core": null, + "server_code_error": null, + "show_in_menu": null, + "smallicon": null, + "subject": null, + "tag_fields": null, + "title_field": null, + "use_template": null, + "version": null +} \ No newline at end of file diff --git a/frappe/core/doctype/file_data/file_data.py b/frappe/core/doctype/file_data/file_data.py index 242f32d8e7..a457a3da5b 100644 --- a/frappe/core/doctype/file_data/file_data.py +++ b/frappe/core/doctype/file_data/file_data.py @@ -10,34 +10,33 @@ naming for same name files: file.gif, file-1.gif, file-2.gif etc import frappe, frappe.utils, os from frappe import conf +from frappe.model.document import Document +from frappe.utils.file_manager import delete_file_data_content -class DocType(): - def __init__(self, d, dl): - self.doc, self.doclist = d, dl - +class FileData(Document): def before_insert(self): frappe.local.rollback_observers.append(self) def on_update(self): # check duplicate assignement n_records = frappe.db.sql("""select name from `tabFile Data` - where file_name=%s + where content_hash=%s and name!=%s and attached_to_doctype=%s - and attached_to_name=%s""", (self.doc.file_name, self.doc.name, self.doc.attached_to_doctype, - self.doc.attached_to_name)) + and attached_to_name=%s""", (self.content_hash, self.name, self.attached_to_doctype, + self.attached_to_name)) if len(n_records) > 0: - self.doc.duplicate_entry = n_records[0][0] + self.duplicate_entry = n_records[0][0] frappe.msgprint(frappe._("Same file has already been attached to the record")) frappe.db.rollback() raise frappe.DuplicateEntryError def on_trash(self): - if self.doc.attached_to_name: + if self.attached_to_name: # check persmission try: - if not self.bean.ignore_permissions and \ - not frappe.has_permission(self.doc.attached_to_doctype, "write", self.doc.attached_to_name): + if not getattr(self, 'ignore_permissions', False) and \ + not frappe.has_permission(self.attached_to_doctype, "write", self.attached_to_name): frappe.msgprint(frappe._("No permission to write / remove."), raise_exception=True) @@ -45,14 +44,9 @@ class DocType(): pass # if file not attached to any other record, delete it - if self.doc.file_name and not frappe.db.count("File Data", - {"file_name": self.doc.file_name, "name": ["!=", self.doc.name]}): - if self.doc.file_name.startswith("files/"): - path = frappe.utils.get_site_path("public", self.doc.file_name) - else: - path = frappe.utils.get_site_path("public", "files", self.doc.file_name) - if os.path.exists(path): - os.remove(path) + if self.file_name and not frappe.db.count("File Data", + {"content_hash": self.content_hash, "name": ["!=", self.name]}): + delete_file_data_content(self) def on_rollback(self): - self.on_trash() \ No newline at end of file + self.on_trash() diff --git a/frappe/core/doctype/file_data/file_data.txt b/frappe/core/doctype/file_data/file_data.txt deleted file mode 100644 index e22a6a595e..0000000000 --- a/frappe/core/doctype/file_data/file_data.txt +++ /dev/null @@ -1,87 +0,0 @@ -[ - { - "creation": "2012-12-12 11:19:22", - "docstatus": 0, - "modified": "2014-01-20 17:48:46", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "File.######", - "doctype": "DocType", - "icon": "icon-file", - "module": "Core", - "name": "__common__", - "read_only": 0 - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "File Data", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "read_only": 1 - }, - { - "cancel": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "File Data", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "role": "System Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "File Data" - }, - { - "doctype": "DocField", - "fieldname": "file_name", - "fieldtype": "Data", - "label": "File Name", - "oldfieldname": "file_name", - "oldfieldtype": "Data" - }, - { - "doctype": "DocField", - "fieldname": "file_url", - "fieldtype": "Data", - "in_list_view": 1, - "label": "File URL" - }, - { - "doctype": "DocField", - "fieldname": "attached_to_doctype", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Attached To DocType", - "options": "DocType", - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "attached_to_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Attached To Name", - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "file_size", - "fieldtype": "Int", - "in_list_view": 1, - "label": "File Size" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/letter_head/letter_head.json b/frappe/core/doctype/letter_head/letter_head.json new file mode 100644 index 0000000000..6ba72a5b6d --- /dev/null +++ b/frappe/core/doctype/letter_head/letter_head.json @@ -0,0 +1,80 @@ +{ + "allow_attach": 1, + "autoname": "field:letter_head_name", + "creation": "2012-11-22 17:45:46.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "letter_head_name", + "fieldtype": "Data", + "in_filter": 0, + "label": "Letter Head Name", + "oldfieldname": "letter_head_name", + "oldfieldtype": "Data", + "permlevel": 0, + "reqd": 1 + }, + { + "depends_on": "letter_head_name", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled", + "oldfieldname": "disabled", + "oldfieldtype": "Check", + "permlevel": 0 + }, + { + "depends_on": "letter_head_name", + "description": "Check this to make this the default letter head in all prints", + "fieldname": "is_default", + "fieldtype": "Check", + "label": "Is Default", + "oldfieldname": "is_default", + "oldfieldtype": "Check", + "permlevel": 0 + }, + { + "depends_on": "letter_head_name", + "description": "Letter Head in HTML", + "fieldname": "content", + "fieldtype": "Text Editor", + "label": "Content", + "oldfieldname": "content", + "oldfieldtype": "Text Editor", + "permlevel": 0 + } + ], + "icon": "icon-font", + "idx": 1, + "max_attachments": 3, + "modified": "2014-01-20 17:48:56.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Letter Head", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + }, + { + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "All" + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/letter_head/letter_head.py b/frappe/core/doctype/letter_head/letter_head.py index 9e1c0a6f32..1ddc219e02 100755 --- a/frappe/core/doctype/letter_head/letter_head.py +++ b/frappe/core/doctype/letter_head/letter_head.py @@ -5,10 +5,9 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, doc, doclist=[]): - self.doc = doc - self.doclist = doclist +from frappe.model.document import Document + +class LetterHead(Document): def validate(self): self.set_as_default() @@ -18,13 +17,13 @@ class DocType: def set_as_default(self): from frappe.utils import set_default - if not self.doc.is_default: + if not self.is_default: if not frappe.db.sql("""select count(*) from `tabLetter Head` where ifnull(is_default,0)=1"""): - self.doc.is_default = 1 - if self.doc.is_default: + self.is_default = 1 + if self.is_default: frappe.db.sql("update `tabLetter Head` set is_default=0 where name != %s", - self.doc.name) - set_default('letter_head', self.doc.name) + self.name) + set_default('letter_head', self.name) # update control panel - so it loads new letter directly - frappe.db.set_value('Control Panel', None, 'letter_head', self.doc.content) \ No newline at end of file + frappe.db.set_default("letter_head", self.content) diff --git a/frappe/core/doctype/letter_head/letter_head.txt b/frappe/core/doctype/letter_head/letter_head.txt deleted file mode 100644 index 1a6e0a7fb6..0000000000 --- a/frappe/core/doctype/letter_head/letter_head.txt +++ /dev/null @@ -1,95 +0,0 @@ -[ - { - "creation": "2012-11-22 17:45:46", - "docstatus": 0, - "modified": "2014-01-20 17:48:56", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 1, - "autoname": "field:letter_head_name", - "doctype": "DocType", - "icon": "icon-font", - "max_attachments": 3, - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Letter Head", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Letter Head", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1 - }, - { - "doctype": "DocType", - "name": "Letter Head" - }, - { - "doctype": "DocField", - "fieldname": "letter_head_name", - "fieldtype": "Data", - "in_filter": 0, - "label": "Letter Head Name", - "oldfieldname": "letter_head_name", - "oldfieldtype": "Data", - "reqd": 1 - }, - { - "depends_on": "letter_head_name", - "doctype": "DocField", - "fieldname": "disabled", - "fieldtype": "Check", - "label": "Disabled", - "oldfieldname": "disabled", - "oldfieldtype": "Check" - }, - { - "depends_on": "letter_head_name", - "description": "Check this to make this the default letter head in all prints", - "doctype": "DocField", - "fieldname": "is_default", - "fieldtype": "Check", - "label": "Is Default", - "oldfieldname": "is_default", - "oldfieldtype": "Check" - }, - { - "depends_on": "letter_head_name", - "description": "Letter Head in HTML", - "doctype": "DocField", - "fieldname": "content", - "fieldtype": "Text Editor", - "label": "Content", - "oldfieldname": "content", - "oldfieldtype": "Text Editor" - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "role": "System Manager", - "submit": 0, - "write": 1 - }, - { - "delete": 0, - "doctype": "DocPerm", - "role": "All" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/letter_head/test_letter_head.py b/frappe/core/doctype/letter_head/test_letter_head.py index a6b6086caf..15899e2b27 100644 --- a/frappe/core/doctype/letter_head/test_letter_head.py +++ b/frappe/core/doctype/letter_head/test_letter_head.py @@ -1,4 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -test_records = [] \ No newline at end of file +import frappe + +test_records = frappe.get_test_records('Letter Head') \ No newline at end of file diff --git a/frappe/core/doctype/letter_head/test_records.json b/frappe/core/doctype/letter_head/test_records.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/frappe/core/doctype/letter_head/test_records.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/frappe/core/doctype/module_def/module_def.json b/frappe/core/doctype/module_def/module_def.json new file mode 100644 index 0000000000..b046c7057b --- /dev/null +++ b/frappe/core/doctype/module_def/module_def.json @@ -0,0 +1,162 @@ +{ + "_last_update": null, + "_user_tags": null, + "allow_attach": null, + "allow_copy": null, + "allow_email": null, + "allow_import": null, + "allow_print": null, + "allow_rename": 1, + "allow_trash": null, + "autoname": "field:module_name", + "change_log": null, + "client_script": null, + "client_script_core": null, + "client_string": null, + "colour": null, + "creation": "2013-01-10 16:34:03", + "custom": null, + "default_print_format": null, + "description": null, + "docstatus": 0, + "doctype": "DocType", + "document_type": null, + "dt_template": null, + "fields": [ + { + "allow_on_submit": null, + "default": null, + "depends_on": null, + "description": null, + "fieldname": "module_name", + "fieldtype": "Data", + "hidden": null, + "ignore_restrictions": null, + "in_filter": null, + "in_list_view": 1, + "label": "Module Name", + "no_column": null, + "no_copy": null, + "oldfieldname": "module_name", + "oldfieldtype": "Data", + "options": null, + "permlevel": 0, + "print_hide": null, + "print_width": null, + "read_only": null, + "report_hide": null, + "reqd": null, + "search_index": null, + "set_only_once": null, + "trigger": null, + "width": null + }, + { + "allow_on_submit": null, + "default": null, + "depends_on": null, + "description": null, + "fieldname": "app_name", + "fieldtype": "Data", + "hidden": null, + "ignore_restrictions": null, + "in_filter": null, + "in_list_view": 1, + "label": "App Name", + "no_column": null, + "no_copy": null, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "permlevel": 0, + "print_hide": null, + "print_width": null, + "read_only": null, + "report_hide": null, + "reqd": 1, + "search_index": null, + "set_only_once": null, + "trigger": null, + "width": null + } + ], + "hide_heading": null, + "hide_toolbar": null, + "icon": "icon-sitemap", + "idx": 1, + "in_create": null, + "in_dialog": null, + "is_submittable": null, + "is_transaction_doc": null, + "issingle": null, + "istable": null, + "max_attachments": null, + "menu_index": null, + "modified": "2014-04-07 13:00:27.894115", + "modified_by": "Administrator", + "module": "Core", + "name": "Module Def", + "name_case": null, + "owner": "Administrator", + "parent": null, + "parent_node": null, + "parentfield": null, + "parenttype": null, + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": null, + "import": null, + "match": null, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "restrict": null, + "restricted": null, + "role": "Administrator", + "submit": 0, + "write": 1 + }, + { + "amend": null, + "cancel": null, + "create": 1, + "delete": 1, + "email": null, + "export": null, + "import": null, + "match": null, + "permlevel": 0, + "print": null, + "read": 1, + "report": null, + "restrict": null, + "restricted": null, + "role": "System Manager", + "submit": null, + "write": 1 + } + ], + "plugin": null, + "print_outline": null, + "read_only": null, + "read_only_onload": null, + "search_fields": null, + "section_style": null, + "server_code": null, + "server_code_compiled": null, + "server_code_core": null, + "server_code_error": null, + "show_in_menu": null, + "smallicon": null, + "subject": null, + "tag_fields": null, + "title_field": null, + "use_template": null, + "version": null +} \ No newline at end of file diff --git a/frappe/core/doctype/module_def/module_def.py b/frappe/core/doctype/module_def/module_def.py index a48f49f2a7..6758a4ae0d 100644 --- a/frappe/core/doctype/module_def/module_def.py +++ b/frappe/core/doctype/module_def/module_def.py @@ -2,8 +2,32 @@ # MIT License. See license.txt from __future__ import unicode_literals -import frappe +import frappe, os + +from frappe.model.document import Document + +class ModuleDef(Document): + def validate(self): + modules = None + if not frappe.local.module_app.get(self.name): + with open(frappe.get_app_path(self.app_name, "modules.txt"), "r") as f: + content = f.read() + if not frappe.scrub(self.name) in content.splitlines(): + modules = filter(None, content.splitlines()) + modules.append(frappe.scrub(self.name)) + + if modules: + with open(frappe.get_app_path(self.app_name, "modules.txt"), "w") as f: + f.write("\n".join(modules)) + + frappe.clear_cache() + frappe.setup_module_map() + + module_path = frappe.get_app_path(self.app_name, self.name) + if not os.path.exists(module_path): + os.mkdir(module_path) + with open(os.path.join(module_path, "__init__.py"), "w") as f: + f.write("") + + -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file diff --git a/frappe/core/doctype/module_def/module_def.txt b/frappe/core/doctype/module_def/module_def.txt deleted file mode 100644 index 10bafefbc2..0000000000 --- a/frappe/core/doctype/module_def/module_def.txt +++ /dev/null @@ -1,63 +0,0 @@ -[ - { - "creation": "2013-01-10 16:34:03", - "docstatus": 0, - "modified": "2014-02-18 15:32:49", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_rename": 1, - "autoname": "field:module_name", - "doctype": "DocType", - "icon": "icon-sitemap", - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "fieldname": "module_name", - "fieldtype": "Data", - "label": "Module Name", - "name": "__common__", - "oldfieldname": "module_name", - "oldfieldtype": "Data", - "parent": "Module Def", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "name": "__common__", - "parent": "Module Def", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "read": 1, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Module Def" - }, - { - "doctype": "DocField" - }, - { - "amend": 0, - "cancel": 0, - "doctype": "DocPerm", - "email": 1, - "print": 1, - "report": 1, - "role": "Administrator", - "submit": 0 - }, - { - "doctype": "DocPerm", - "role": "System Manager" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/notification_count/notification_count.json b/frappe/core/doctype/notification_count/notification_count.json new file mode 100644 index 0000000000..53cd755ee0 --- /dev/null +++ b/frappe/core/doctype/notification_count/notification_count.json @@ -0,0 +1,33 @@ +{ + "creation": "2013-11-18 05:31:03.000000", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Other", + "fields": [ + { + "fieldname": "for_doctype", + "fieldtype": "Data", + "label": "For DocType", + "permlevel": 0, + "search_index": 1 + }, + { + "fieldname": "count", + "fieldtype": "Int", + "label": "Count", + "permlevel": 0 + }, + { + "fieldname": "open_count", + "fieldtype": "Int", + "label": "Open Count", + "permlevel": 0 + } + ], + "idx": 1, + "modified": "2013-12-20 19:24:14.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Notification Count", + "owner": "Administrator" +} \ No newline at end of file diff --git a/frappe/core/doctype/notification_count/notification_count.py b/frappe/core/doctype/notification_count/notification_count.py index 21d81c4247..36c624a5f6 100644 --- a/frappe/core/doctype/notification_count/notification_count.py +++ b/frappe/core/doctype/notification_count/notification_count.py @@ -6,12 +6,15 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class NotificationCount(Document): + pass @frappe.whitelist() def get_notifications(): + if frappe.flags.in_install_app: + return config = get_notification_config() can_read = frappe.user.get_can_read() open_count_doctype = {} @@ -31,7 +34,7 @@ def get_notifications(): result = frappe.get_list(d, fields=["count(*)"], filters=[[d, key, "=", condition[key]]], as_list=True, limit_page_length=1)[0][0] - frappe.doc({"doctype":"Notification Count", "for_doctype":d, + frappe.get_doc({"doctype":"Notification Count", "for_doctype":d, "open_count":result}).insert() open_count_doctype[d] = result @@ -41,7 +44,7 @@ def get_notifications(): open_count_module[m] = notification_count[m] else: open_count_module[m] = frappe.get_attr(config.for_module[m])() - frappe.doc({"doctype":"Notification Count", "for_doctype":m, + frappe.get_doc({"doctype":"Notification Count", "for_doctype":m, "open_count":open_count_module[m]}).insert() return { @@ -56,12 +59,12 @@ def delete_notification_count_for(doctype): def delete_event_notification_count(): delete_notification_count_for("Event") -def clear_doctype_notifications(bean, method=None): +def clear_doctype_notifications(doc, method=None): if frappe.flags.in_import: return config = get_notification_config() - doctype = bean.doc.doctype + doctype = doc.doctype if doctype in config.for_doctype: delete_notification_count_for(doctype) diff --git a/frappe/core/doctype/notification_count/notification_count.txt b/frappe/core/doctype/notification_count/notification_count.txt deleted file mode 100644 index 9042721822..0000000000 --- a/frappe/core/doctype/notification_count/notification_count.txt +++ /dev/null @@ -1,46 +0,0 @@ -[ - { - "creation": "2013-11-18 05:31:03", - "docstatus": 0, - "modified": "2013-12-20 19:24:14", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "DocType", - "document_type": "Other", - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Notification Count", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocType", - "name": "Notification Count" - }, - { - "doctype": "DocField", - "fieldname": "for_doctype", - "fieldtype": "Data", - "label": "For DocType", - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "count", - "fieldtype": "Int", - "label": "Count" - }, - { - "doctype": "DocField", - "fieldname": "open_count", - "fieldtype": "Int", - "label": "Open Count" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.json b/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.json new file mode 100644 index 0000000000..c4460d34c9 --- /dev/null +++ b/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.json @@ -0,0 +1,88 @@ +{ + "allow_copy": 1, + "creation": "2014-03-03 19:48:01.000000", + "description": "Email Settings for Outgoing and Incoming Emails.", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "description": "SMTP Server (e.g. smtp.gmail.com)", + "fieldname": "mail_server", + "fieldtype": "Data", + "label": "Outgoing Mail Server", + "permlevel": 0 + }, + { + "description": "[?]", + "fieldname": "use_ssl", + "fieldtype": "Check", + "label": "Use TLS", + "permlevel": 0 + }, + { + "description": "If non standard port (e.g. 587)", + "fieldname": "mail_port", + "fieldtype": "Int", + "label": "Port", + "permlevel": 0 + }, + { + "fieldname": "cb0", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "description": "Set Login and Password if authentication is required.", + "fieldname": "mail_login", + "fieldtype": "Data", + "label": "Login Id", + "permlevel": 0 + }, + { + "description": "Check this if you want to send emails as this id only (in case of restriction by your email provider).", + "fieldname": "always_use_login_id_as_sender", + "fieldtype": "Check", + "label": "Always use above Login Id as sender", + "permlevel": 0 + }, + { + "fieldname": "mail_password", + "fieldtype": "Password", + "label": "Mail Password", + "permlevel": 0 + }, + { + "description": "System generated mails will be sent from this email id.", + "fieldname": "auto_email_id", + "fieldtype": "Data", + "label": "Auto Email Id", + "permlevel": 0 + }, + { + "default": "1", + "description": "If checked, an email with an attached HTML format will be added to part of the EMail body as well as attachment. To only send as attachment, uncheck this.", + "fieldname": "send_print_in_body_and_attachment", + "fieldtype": "Check", + "label": "Send Print in Body and Attachment", + "permlevel": 0 + } + ], + "icon": "icon-cog", + "idx": 1, + "in_create": 1, + "issingle": 1, + "modified": "2014-03-03 20:20:09.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Outgoing Email Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "permlevel": 0, + "read": 1, + "role": "System Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.py b/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.py index e37bbe22a5..5ee88d31a8 100644 --- a/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.py +++ b/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.py @@ -6,20 +6,19 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class OutgoingEmailSettings(Document): def validate(self): - self.doc.encode() - if self.doc.mail_server: + if self.mail_server: from frappe.utils import cint from frappe.utils.email_lib.smtp import SMTPServer - smtpserver = SMTPServer(login = self.doc.mail_login, - password = self.doc.mail_password, - server = self.doc.mail_server, - port = cint(self.doc.mail_port), - use_ssl = self.doc.use_ssl + smtpserver = SMTPServer(login = self.mail_login, + password = self.mail_password, + server = self.mail_server, + port = cint(self.mail_port), + use_ssl = self.use_ssl ) # exceptions are handled in session connect diff --git a/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.txt b/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.txt deleted file mode 100644 index c50616c786..0000000000 --- a/frappe/core/doctype/outgoing_email_settings/outgoing_email_settings.txt +++ /dev/null @@ -1,107 +0,0 @@ -[ - { - "creation": "2014-03-03 19:48:01", - "docstatus": 0, - "modified": "2014-03-03 20:20:09", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_copy": 1, - "description": "Email Settings for Outgoing and Incoming Emails.", - "doctype": "DocType", - "icon": "icon-cog", - "in_create": 1, - "issingle": 1, - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Outgoing Email Settings", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "create": 1, - "doctype": "DocPerm", - "name": "__common__", - "parent": "Outgoing Email Settings", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "read": 1, - "role": "System Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Outgoing Email Settings" - }, - { - "description": "SMTP Server (e.g. smtp.gmail.com)", - "doctype": "DocField", - "fieldname": "mail_server", - "fieldtype": "Data", - "label": "Outgoing Mail Server" - }, - { - "description": "[?]", - "doctype": "DocField", - "fieldname": "use_ssl", - "fieldtype": "Check", - "label": "Use TLS" - }, - { - "description": "If non standard port (e.g. 587)", - "doctype": "DocField", - "fieldname": "mail_port", - "fieldtype": "Int", - "label": "Port" - }, - { - "doctype": "DocField", - "fieldname": "cb0", - "fieldtype": "Column Break" - }, - { - "description": "Set Login and Password if authentication is required.", - "doctype": "DocField", - "fieldname": "mail_login", - "fieldtype": "Data", - "label": "Login Id" - }, - { - "description": "Check this if you want to send emails as this id only (in case of restriction by your email provider).", - "doctype": "DocField", - "fieldname": "always_use_login_id_as_sender", - "fieldtype": "Check", - "label": "Always use above Login Id as sender" - }, - { - "doctype": "DocField", - "fieldname": "mail_password", - "fieldtype": "Password", - "label": "Mail Password" - }, - { - "description": "System generated mails will be sent from this email id.", - "doctype": "DocField", - "fieldname": "auto_email_id", - "fieldtype": "Data", - "label": "Auto Email Id" - }, - { - "default": "1", - "description": "If checked, an email with an attached HTML format will be added to part of the EMail body as well as attachment. To only send as attachment, uncheck this.", - "doctype": "DocField", - "fieldname": "send_print_in_body_and_attachment", - "fieldtype": "Check", - "label": "Send Print in Body and Attachment" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/page/page.json b/frappe/core/doctype/page/page.json new file mode 100644 index 0000000000..1ea08691f7 --- /dev/null +++ b/frappe/core/doctype/page/page.json @@ -0,0 +1,113 @@ +{ + "allow_copy": 0, + "allow_rename": 1, + "autoname": "field:page_name", + "creation": "2012-12-20 17:16:49.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "page_html", + "fieldtype": "Section Break", + "label": "Page HTML", + "oldfieldtype": "Section Break", + "permlevel": 0 + }, + { + "fieldname": "page_name", + "fieldtype": "Data", + "label": "Page Name", + "oldfieldname": "page_name", + "oldfieldtype": "Data", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "permlevel": 0 + }, + { + "fieldname": "icon", + "fieldtype": "Data", + "label": "icon", + "permlevel": 0 + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "module", + "fieldtype": "Link", + "label": "Module", + "oldfieldname": "module", + "oldfieldtype": "Select", + "options": "Module Def", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "standard", + "fieldtype": "Select", + "label": "Standard", + "oldfieldname": "standard", + "oldfieldtype": "Select", + "options": "\nYes\nNo", + "permlevel": 0, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "section_break0", + "fieldtype": "Section Break", + "permlevel": 0 + }, + { + "fieldname": "roles", + "fieldtype": "Table", + "hidden": 0, + "label": "Roles", + "oldfieldname": "roles", + "oldfieldtype": "Table", + "options": "Page Role", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-file", + "idx": 1, + "issingle": 0, + "istable": 0, + "modified": "2013-12-30 13:48:02.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Page", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "Administrator", + "submit": 0, + "write": 1 + }, + { + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "All" + } + ], + "read_only": 0 +} \ No newline at end of file diff --git a/frappe/core/doctype/page/page.py b/frappe/core/doctype/page/page.py index 3bb1cc587e..1a71c9ad00 100644 --- a/frappe/core/doctype/page/page.py +++ b/frappe/core/doctype/page/page.py @@ -3,11 +3,9 @@ from __future__ import unicode_literals import frappe +from frappe.model.document import Document -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d,dl - +class Page(Document): def autoname(self): """ Creates a url friendly name for this page. @@ -15,17 +13,17 @@ class DocType: it will add name-1, name-2 etc. """ from frappe.utils import cint - if (self.doc.name and self.doc.name.startswith('New Page')) or not self.doc.name: - self.doc.name = self.doc.page_name.lower().replace('"','').replace("'",'').\ + if (self.name and self.name.startswith('New Page')) or not self.name: + self.name = self.page_name.lower().replace('"','').replace("'",'').\ replace(' ', '-')[:20] - if frappe.db.exists('Page',self.doc.name): + if frappe.db.exists('Page',self.name): cnt = frappe.db.sql("""select name from tabPage - where name like "%s-%%" order by name desc limit 1""" % self.doc.name) + where name like "%s-%%" order by name desc limit 1""" % self.name) if cnt: cnt = cint(cnt[0][0].split('-')[-1]) + 1 else: cnt = 1 - self.doc.name += '-' + str(cnt) + self.name += '-' + str(cnt) # export def on_update(self): @@ -35,16 +33,16 @@ class DocType: """ from frappe import conf from frappe.core.doctype.doctype.doctype import make_module_and_roles - make_module_and_roles(self.doclist, "Page Role") + make_module_and_roles(self, "roles") - if not frappe.flags.in_import and getattr(conf,'developer_mode', 0) and self.doc.standard=='Yes': + if not frappe.flags.in_import and getattr(conf,'developer_mode', 0) and self.standard=='Yes': from frappe.modules.export_file import export_to_files from frappe.modules import get_module_path, scrub import os - export_to_files(record_list=[['Page', self.doc.name]]) + export_to_files(record_list=[['Page', self.name]]) # write files - path = os.path.join(get_module_path(self.doc.module), 'page', scrub(self.doc.name), scrub(self.doc.name)) + path = os.path.join(get_module_path(self.module), 'page', scrub(self.name), scrub(self.name)) # js if not os.path.exists(path + '.js'): @@ -55,35 +53,39 @@ class DocType: title: '%s', single_column: true }); -}""" % (self.doc.name, self.doc.title)) +}""" % (self.name, self.title)) - def get_from_files(self): - """ - Loads page info from files in module - """ + def as_dict(self): + d = super(Page, self).as_dict() + for key in ("script", "style", "content"): + d[key] = self.get(key) + return d + + def load_assets(self): from frappe.modules import get_module_path, scrub import os - - path = os.path.join(get_module_path(self.doc.module), 'page', scrub(self.doc.name)) + + path = os.path.join(get_module_path(self.module), 'page', scrub(self.name)) # script - fpath = os.path.join(path, scrub(self.doc.name) + '.js') + fpath = os.path.join(path, scrub(self.name) + '.js') if os.path.exists(fpath): with open(fpath, 'r') as f: - self.doc.script = f.read() + self.script = f.read() # css - fpath = os.path.join(path, scrub(self.doc.name) + '.css') + fpath = os.path.join(path, scrub(self.name) + '.css') if os.path.exists(fpath): with open(fpath, 'r') as f: - self.doc.style = f.read() + self.style = f.read() # html - fpath = os.path.join(path, scrub(self.doc.name) + '.html') + fpath = os.path.join(path, scrub(self.name) + '.html') if os.path.exists(fpath): with open(fpath, 'r') as f: - self.doc.content = f.read() + self.content = f.read() if frappe.lang != 'en': from frappe.translate import get_lang_js - self.doc.script += get_lang_js("page", self.doc.name) + self.script += get_lang_js("page", self.name) + \ No newline at end of file diff --git a/frappe/core/doctype/page/page.txt b/frappe/core/doctype/page/page.txt deleted file mode 100644 index 812c69050c..0000000000 --- a/frappe/core/doctype/page/page.txt +++ /dev/null @@ -1,129 +0,0 @@ -[ - { - "creation": "2012-12-20 17:16:49", - "docstatus": 0, - "modified": "2013-12-30 13:48:02", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_copy": 0, - "allow_rename": 1, - "autoname": "field:page_name", - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-file", - "issingle": 0, - "istable": 0, - "module": "Core", - "name": "__common__", - "read_only": 0 - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Page", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Page", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1 - }, - { - "doctype": "DocType", - "name": "Page" - }, - { - "doctype": "DocField", - "fieldname": "page_html", - "fieldtype": "Section Break", - "label": "Page HTML", - "oldfieldtype": "Section Break" - }, - { - "doctype": "DocField", - "fieldname": "page_name", - "fieldtype": "Data", - "label": "Page Name", - "oldfieldname": "page_name", - "oldfieldtype": "Data", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "title", - "fieldtype": "Data", - "label": "Title" - }, - { - "doctype": "DocField", - "fieldname": "icon", - "fieldtype": "Data", - "label": "icon" - }, - { - "doctype": "DocField", - "fieldname": "column_break0", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "module", - "fieldtype": "Link", - "label": "Module", - "oldfieldname": "module", - "oldfieldtype": "Select", - "options": "Module Def", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "standard", - "fieldtype": "Select", - "label": "Standard", - "oldfieldname": "standard", - "oldfieldtype": "Select", - "options": "\nYes\nNo", - "reqd": 1, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "section_break0", - "fieldtype": "Section Break" - }, - { - "doctype": "DocField", - "fieldname": "roles", - "fieldtype": "Table", - "hidden": 0, - "label": "Roles", - "oldfieldname": "roles", - "oldfieldtype": "Table", - "options": "Page Role", - "reqd": 0, - "search_index": 0 - }, - { - "cancel": 0, - "create": 1, - "doctype": "DocPerm", - "role": "Administrator", - "submit": 0, - "write": 1 - }, - { - "doctype": "DocPerm", - "role": "All" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/page_role/page_role.json b/frappe/core/doctype/page_role/page_role.json new file mode 100644 index 0000000000..268872bcf3 --- /dev/null +++ b/frappe/core/doctype/page_role/page_role.json @@ -0,0 +1,33 @@ +{ + "allow_copy": 0, + "autoname": "PR.######", + "creation": "2013-02-22 01:27:34.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "role", + "fieldtype": "Link", + "hidden": 0, + "in_list_view": 1, + "label": "Role", + "oldfieldname": "role", + "oldfieldtype": "Link", + "options": "Role", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 1, + "issingle": 0, + "istable": 1, + "modified": "2013-12-20 19:23:24.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Page Role", + "owner": "Administrator", + "read_only": 0 +} \ No newline at end of file diff --git a/frappe/core/doctype/page_role/page_role.py b/frappe/core/doctype/page_role/page_role.py index a48f49f2a7..9752238bb2 100644 --- a/frappe/core/doctype/page_role/page_role.py +++ b/frappe/core/doctype/page_role/page_role.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class PageRole(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/page_role/page_role.txt b/frappe/core/doctype/page_role/page_role.txt deleted file mode 100644 index 266e8d5136..0000000000 --- a/frappe/core/doctype/page_role/page_role.txt +++ /dev/null @@ -1,46 +0,0 @@ -[ - { - "creation": "2013-02-22 01:27:34", - "docstatus": 0, - "modified": "2013-12-20 19:23:24", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_copy": 0, - "autoname": "PR.######", - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "issingle": 0, - "istable": 1, - "module": "Core", - "name": "__common__", - "read_only": 0 - }, - { - "doctype": "DocField", - "fieldname": "role", - "fieldtype": "Link", - "hidden": 0, - "in_list_view": 1, - "label": "Role", - "name": "__common__", - "oldfieldname": "role", - "oldfieldtype": "Link", - "options": "Role", - "parent": "Page Role", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocType", - "name": "Page Role" - }, - { - "doctype": "DocField" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/patch_log/patch_log.json b/frappe/core/doctype/patch_log/patch_log.json new file mode 100644 index 0000000000..f7e6b65297 --- /dev/null +++ b/frappe/core/doctype/patch_log/patch_log.json @@ -0,0 +1,33 @@ +{ + "autoname": "PATCHLOG.#####", + "creation": "2013-01-17 11:36:45.000000", + "description": "List of patches executed", + "docstatus": 0, + "doctype": "DocType", + "document_type": "System", + "fields": [ + { + "fieldname": "patch", + "fieldtype": "Data", + "label": "Patch", + "permlevel": 0 + } + ], + "icon": "icon-cog", + "idx": 1, + "modified": "2013-12-20 19:24:15.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Patch Log", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator" + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/patch_log/patch_log.py b/frappe/core/doctype/patch_log/patch_log.py index cb42e2eea2..18a3dc2aed 100644 --- a/frappe/core/doctype/patch_log/patch_log.py +++ b/frappe/core/doctype/patch_log/patch_log.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class PatchLog(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/patch_log/patch_log.txt b/frappe/core/doctype/patch_log/patch_log.txt deleted file mode 100644 index dd774d7de4..0000000000 --- a/frappe/core/doctype/patch_log/patch_log.txt +++ /dev/null @@ -1,52 +0,0 @@ -[ - { - "creation": "2013-01-17 11:36:45", - "docstatus": 0, - "modified": "2013-12-20 19:24:15", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "PATCHLOG.#####", - "description": "List of patches executed", - "doctype": "DocType", - "document_type": "System", - "icon": "icon-cog", - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "fieldname": "patch", - "fieldtype": "Data", - "label": "Patch", - "name": "__common__", - "parent": "Patch Log", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Patch Log", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Administrator" - }, - { - "doctype": "DocType", - "name": "Patch Log" - }, - { - "doctype": "DocField" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/print_format/print_format.json b/frappe/core/doctype/print_format/print_format.json new file mode 100644 index 0000000000..987023bf04 --- /dev/null +++ b/frappe/core/doctype/print_format/print_format.json @@ -0,0 +1,162 @@ +{ + "allow_attach": 0, + "allow_copy": 0, + "allow_rename": 0, + "autoname": "Prompt", + "creation": "2013-01-23 19:54:43.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "allow_on_submit": 0, + "fieldname": "module", + "fieldtype": "Select", + "hidden": 0, + "in_filter": 1, + "label": "Module", + "no_copy": 0, + "oldfieldname": "module", + "oldfieldtype": "Select", + "options": "link:Module Def", + "permlevel": 0, + "print_hide": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 1 + }, + { + "description": "Belongs to", + "fieldname": "doc_type", + "fieldtype": "Select", + "in_filter": 1, + "label": "DocType", + "options": "link:DocType", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "allow_on_submit": 0, + "default": "No", + "fieldname": "standard", + "fieldtype": "Select", + "hidden": 0, + "in_filter": 1, + "label": "Standard", + "no_copy": 1, + "oldfieldname": "standard", + "oldfieldtype": "Select", + "options": "No\nYes", + "permlevel": 1, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "print_format_type", + "fieldtype": "Select", + "label": "Print Format Type", + "options": "Client\nServer", + "permlevel": 0 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "permlevel": 0 + }, + { + "allow_on_submit": 0, + "depends_on": "eval:doc.print_format_type!=\"Server\"", + "fieldname": "html", + "fieldtype": "Code", + "hidden": 0, + "in_filter": 0, + "label": "HTML", + "no_copy": 0, + "oldfieldname": "html", + "oldfieldtype": "Text Editor", + "options": "HTML", + "permlevel": 0, + "print_hide": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-print", + "idx": 1, + "in_create": 0, + "in_dialog": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2014-01-20 17:49:00.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Print Format", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "permlevel": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "permlevel": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "submit": 0, + "write": 1 + } + ], + "read_only": 0, + "read_only_onload": 0 +} \ No newline at end of file diff --git a/frappe/core/doctype/print_format/print_format.py b/frappe/core/doctype/print_format/print_format.py index 637adb3d11..97eb9a7b71 100644 --- a/frappe/core/doctype/print_format/print_format.py +++ b/frappe/core/doctype/print_format/print_format.py @@ -1,5 +1,5 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt from __future__ import unicode_literals import frappe, os @@ -8,36 +8,35 @@ from frappe.modules import get_doc_path standard_format = "templates/print_formats/standard.html" -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d,dl +from frappe.model.document import Document +class PrintFormat(Document): def validate(self): - if self.doc.standard=="Yes" and frappe.session.user != "Administrator": + if self.standard=="Yes" and frappe.session.user != "Administrator": frappe.msgprint("Standard Print Format cannot be updated.", raise_exception=1) - + # old_doc_type is required for clearing item cache self.old_doc_type = frappe.db.get_value('Print Format', - self.doc.name, 'doc_type') + self.name, 'doc_type') def on_update(self): if hasattr(self, 'old_doc_type') and self.old_doc_type: - frappe.clear_cache(doctype=self.old_doc_type) - if self.doc.doc_type: - frappe.clear_cache(doctype=self.doc.doc_type) + frappe.clear_cache(doctype=self.old_doc_type) + if self.doc_type: + frappe.clear_cache(doctype=self.doc_type) self.export_doc() - + def export_doc(self): # export - if self.doc.standard == 'Yes' and (frappe.conf.get('developer_mode') or 0) == 1: + if self.standard == 'Yes' and (frappe.conf.get('developer_mode') or 0) == 1: from frappe.modules.export_file import export_to_files - export_to_files(record_list=[['Print Format', self.doc.name]], - record_module=self.doc.module) - + export_to_files(record_list=[['Print Format', self.name]], + record_module=self.module) + def on_trash(self): - if self.doc.doc_type: - frappe.clear_cache(doctype=self.doc.doc_type) + if self.doc_type: + frappe.clear_cache(doctype=self.doc_type) def get_args(): if not frappe.form_dict.format: @@ -48,37 +47,34 @@ def get_args():

    Parameters doctype, name and format required

    %s
    """ % repr(frappe.form_dict) } - - bean = frappe.bean(frappe.form_dict.doctype, frappe.form_dict.name) + + doc = frappe.get_doc(frappe.form_dict.doctype, frappe.form_dict.name) for ptype in ("read", "print"): - if not frappe.has_permission(bean.doc.doctype, ptype, bean.doc): + if not frappe.has_permission(doc.doctype, ptype, doc): return { "body": """

    Error

    No {ptype} permission

    """.format(ptype=ptype) } - + return { - "body": get_html(bean.doc, bean.doclist), + "body": get_html(doc), "css": get_print_style(frappe.form_dict.style), "comment": frappe.session.user } -def get_html(doc, doclist, print_format=None): +def get_html(doc, name=None, print_format=None): from jinja2 import Environment - - if isinstance(doc, basestring) and isinstance(doclist, basestring): - bean = frappe.bean(doc, doclist) - doc = bean.doc - doclist = bean.doclist - template = Environment().from_string(get_print_format_name(doc.doctype, + if isinstance(doc, basestring) and isinstance(name, basestring): + doc = frappe.get_doc(doc, name) + + template = Environment().from_string(get_print_format_name(doc.doctype, print_format or frappe.form_dict.format)) - doctype = frappe.get_doctype(doc.doctype) - + meta = frappe.get_meta(doc.doctype) + args = { "doc": doc, - "doclist": doclist, - "doctype": doctype, + "meta": meta, "frappe": frappe, "utils": frappe.utils } @@ -88,9 +84,9 @@ def get_html(doc, doclist, print_format=None): def get_print_format_name(doctype, format_name): if format_name==standard_format: return format_name - + # server, find template - path = os.path.join(get_doc_path(frappe.db.get_value("DocType", doctype, "module"), + path = os.path.join(get_doc_path(frappe.db.get_value("DocType", doctype, "module"), "Print Format", format_name), format_name + ".html") if os.path.exists(path): with open(path, "r") as pffile: @@ -105,7 +101,7 @@ def get_print_format_name(doctype, format_name): def get_print_style(style=None): if not style: style = frappe.db.get_default("print_style") or "Standard" - path = os.path.join(get_doc_path("Core", "DocType", "Print Format"), "styles", + path = os.path.join(get_doc_path("Core", "DocType", "Print Format"), "styles", style.lower() + ".css") if not os.path.exists(path): if style!="Standard": @@ -115,4 +111,3 @@ def get_print_style(style=None): else: with open(path, 'r') as sfile: return sfile.read() - \ No newline at end of file diff --git a/frappe/core/doctype/print_format/print_format.txt b/frappe/core/doctype/print_format/print_format.txt deleted file mode 100644 index 7ec7470066..0000000000 --- a/frappe/core/doctype/print_format/print_format.txt +++ /dev/null @@ -1,175 +0,0 @@ -[ - { - "creation": "2013-01-23 19:54:43", - "docstatus": 0, - "modified": "2014-01-20 17:49:00", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 0, - "allow_copy": 0, - "allow_rename": 0, - "autoname": "Prompt", - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-print", - "in_create": 0, - "in_dialog": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "module": "Core", - "name": "__common__", - "read_only": 0, - "read_only_onload": 0 - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Print Format", - "parentfield": "fields", - "parenttype": "DocType" - }, - { - "amend": 0, - "cancel": 0, - "doctype": "DocPerm", - "name": "__common__", - "parent": "Print Format", - "parentfield": "permissions", - "parenttype": "DocType", - "read": 1, - "report": 1, - "submit": 0 - }, - { - "doctype": "DocType", - "name": "Print Format" - }, - { - "allow_on_submit": 0, - "doctype": "DocField", - "fieldname": "module", - "fieldtype": "Select", - "hidden": 0, - "in_filter": 1, - "label": "Module", - "no_copy": 0, - "oldfieldname": "module", - "oldfieldtype": "Select", - "options": "link:Module Def", - "permlevel": 0, - "print_hide": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1 - }, - { - "description": "Belongs to", - "doctype": "DocField", - "fieldname": "doc_type", - "fieldtype": "Select", - "in_filter": 1, - "label": "DocType", - "options": "link:DocType", - "permlevel": 0, - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "permlevel": 0 - }, - { - "allow_on_submit": 0, - "default": "No", - "doctype": "DocField", - "fieldname": "standard", - "fieldtype": "Select", - "hidden": 0, - "in_filter": 1, - "label": "Standard", - "no_copy": 1, - "oldfieldname": "standard", - "oldfieldtype": "Select", - "options": "No\nYes", - "permlevel": 1, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "print_format_type", - "fieldtype": "Select", - "label": "Print Format Type", - "options": "Client\nServer", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "permlevel": 0 - }, - { - "allow_on_submit": 0, - "depends_on": "eval:doc.print_format_type!=\"Server\"", - "doctype": "DocField", - "fieldname": "html", - "fieldtype": "Code", - "hidden": 0, - "in_filter": 0, - "label": "HTML", - "no_copy": 0, - "oldfieldname": "html", - "oldfieldtype": "Text Editor", - "options": "HTML", - "permlevel": 0, - "print_hide": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0 - }, - { - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "permlevel": 0, - "print": 1, - "role": "Administrator", - "write": 1 - }, - { - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "permlevel": 0, - "print": 1, - "role": "System Manager", - "write": 1 - }, - { - "create": 0, - "delete": 0, - "doctype": "DocPerm", - "permlevel": 1, - "role": "System Manager" - }, - { - "create": 0, - "delete": 0, - "doctype": "DocPerm", - "permlevel": 1, - "role": "Administrator", - "write": 1 - } -] \ No newline at end of file diff --git a/frappe/core/doctype/property_setter/property_setter.json b/frappe/core/doctype/property_setter/property_setter.json new file mode 100644 index 0000000000..3606bc325a --- /dev/null +++ b/frappe/core/doctype/property_setter/property_setter.json @@ -0,0 +1,120 @@ +{ + "creation": "2013-01-10 16:34:04.000000", + "description": "Property Setter overrides a standard DocType or Field property", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "help", + "fieldtype": "HTML", + "label": "Help", + "options": "
    Please don't update it as it can mess up your form. Use the Customize Form View and Custom Fields to set properties!
    ", + "permlevel": 0 + }, + { + "fieldname": "sb0", + "fieldtype": "Section Break", + "permlevel": 0 + }, + { + "depends_on": "eval:doc.__islocal", + "fieldname": "doctype_or_field", + "fieldtype": "Select", + "label": "DocType or Field", + "options": "\nDocField\nDocType", + "permlevel": 0, + "reqd": 1 + }, + { + "description": "New value to be set", + "fieldname": "value", + "fieldtype": "Text", + "label": "Set Value", + "permlevel": 0 + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "doc_type", + "fieldtype": "Link", + "in_filter": 0, + "label": "DocType", + "options": "DocType", + "permlevel": 0, + "reqd": 1, + "search_index": 1 + }, + { + "depends_on": "eval:doc.doctype_or_field=='DocField'", + "description": "ID (name) of the entity whose property is to be set", + "fieldname": "field_name", + "fieldtype": "Data", + "in_filter": 0, + "label": "Field Name", + "permlevel": 0, + "reqd": 0, + "search_index": 1 + }, + { + "fieldname": "property", + "fieldtype": "Data", + "in_filter": 0, + "label": "Property", + "permlevel": 0, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "property_type", + "fieldtype": "Data", + "label": "Property Type", + "permlevel": 0 + }, + { + "fieldname": "default_value", + "fieldtype": "Data", + "label": "Default Value", + "permlevel": 0 + } + ], + "icon": "icon-glass", + "idx": 1, + "modified": "2014-01-20 17:49:03.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Property Setter", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + } + ], + "search_fields": "doc_name,property" +} \ No newline at end of file diff --git a/frappe/core/doctype/property_setter/property_setter.py b/frappe/core/doctype/property_setter/property_setter.py index 9e19f63ec5..af529b84d6 100644 --- a/frappe/core/doctype/property_setter/property_setter.py +++ b/frappe/core/doctype/property_setter/property_setter.py @@ -4,26 +4,26 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class PropertySetter(Document): def autoname(self): - self.doc.name = self.doc.doc_type + "-" \ - + (self.doc.field_name and (self.doc.field_name + "-") or "") \ - + self.doc.property + self.name = self.doc_type + "-" \ + + (self.field_name and (self.field_name + "-") or "") \ + + self.property def validate(self): """delete other property setters on this, if this is new""" - if self.doc.fields['__islocal']: + if self.get('__islocal'): frappe.db.sql("""delete from `tabProperty Setter` where doctype_or_field = %(doctype_or_field)s and doc_type = %(doc_type)s and ifnull(field_name,'') = ifnull(%(field_name)s, '') - and property = %(property)s""", self.doc.fields) + and property = %(property)s""", self.get_valid_dict()) # clear cache - frappe.clear_cache(doctype = self.doc.doc_type) + frappe.clear_cache(doctype = self.doc_type) def get_property_list(self, dt): return frappe.db.sql("""select fieldname, label, fieldtype @@ -41,21 +41,21 @@ class DocType: } def get_field_ids(self): - return frappe.db.sql("select name, fieldtype, label, fieldname from tabDocField where parent=%s", self.doc.doc_type, as_dict = 1) + return frappe.db.sql("select name, fieldtype, label, fieldname from tabDocField where parent=%s", self.doc_type, as_dict = 1) def get_defaults(self): - if not self.doc.field_name: - return frappe.db.sql("select * from `tabDocType` where name=%s", self.doc.doc_type, as_dict = 1)[0] + if not self.field_name: + return frappe.db.sql("select * from `tabDocType` where name=%s", self.doc_type, as_dict = 1)[0] else: return frappe.db.sql("select * from `tabDocField` where fieldname=%s and parent=%s", - (self.doc.field_name, self.doc.doc_type), as_dict = 1)[0] + (self.field_name, self.doc_type), as_dict = 1)[0] def on_update(self): from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype - validate_fields_for_doctype(self.doc.doc_type) + validate_fields_for_doctype(self.doc_type) def make_property_setter(doctype, fieldname, property, value, property_type, for_doctype = False): - return frappe.bean({ + return frappe.get_doc({ "doctype":"Property Setter", "doctype_or_field": for_doctype and "DocType" or "DocField", "doc_type": doctype, diff --git a/frappe/core/doctype/property_setter/property_setter.txt b/frappe/core/doctype/property_setter/property_setter.txt deleted file mode 100644 index 305e574690..0000000000 --- a/frappe/core/doctype/property_setter/property_setter.txt +++ /dev/null @@ -1,130 +0,0 @@ -[ - { - "creation": "2013-01-10 16:34:04", - "docstatus": 0, - "modified": "2014-01-20 17:49:03", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "description": "Property Setter overrides a standard DocType or Field property", - "doctype": "DocType", - "icon": "icon-glass", - "module": "Core", - "name": "__common__", - "search_fields": "doc_name,property" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Property Setter", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Property Setter", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Property Setter" - }, - { - "doctype": "DocField", - "fieldname": "help", - "fieldtype": "HTML", - "label": "Help", - "options": "
    Please don't update it as it can mess up your form. Use the Customize Form View and Custom Fields to set properties!
    " - }, - { - "doctype": "DocField", - "fieldname": "sb0", - "fieldtype": "Section Break" - }, - { - "depends_on": "eval:doc.__islocal", - "doctype": "DocField", - "fieldname": "doctype_or_field", - "fieldtype": "Select", - "label": "DocType or Field", - "options": "\nDocField\nDocType", - "reqd": 1 - }, - { - "description": "New value to be set", - "doctype": "DocField", - "fieldname": "value", - "fieldtype": "Text", - "label": "Set Value" - }, - { - "doctype": "DocField", - "fieldname": "column_break0", - "fieldtype": "Column Break", - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "doc_type", - "fieldtype": "Link", - "in_filter": 0, - "label": "DocType", - "options": "DocType", - "reqd": 0, - "search_index": 1 - }, - { - "depends_on": "eval:doc.doctype_or_field=='DocField'", - "description": "ID (name) of the entity whose property is to be set", - "doctype": "DocField", - "fieldname": "field_name", - "fieldtype": "Data", - "in_filter": 0, - "label": "Field Name", - "reqd": 0, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "property", - "fieldtype": "Data", - "in_filter": 0, - "label": "Property", - "reqd": 1, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "property_type", - "fieldtype": "Data", - "label": "Property Type" - }, - { - "doctype": "DocField", - "fieldname": "default_value", - "fieldtype": "Data", - "label": "Default Value" - }, - { - "doctype": "DocPerm", - "role": "Administrator" - }, - { - "doctype": "DocPerm", - "role": "System Manager" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/property_setter/test_records.json b/frappe/core/doctype/property_setter/test_records.json new file mode 100644 index 0000000000..3c084b424e --- /dev/null +++ b/frappe/core/doctype/property_setter/test_records.json @@ -0,0 +1,10 @@ +[ + { + "doc_type": "User", + "doctype_or_field": "DocField", + "field_name": "location", + "property": "in_list_view", + "property_type": "Check", + "value": "1" + } +] \ No newline at end of file diff --git a/frappe/core/doctype/report/report.json b/frappe/core/doctype/report/report.json new file mode 100644 index 0000000000..f4a5bde8b2 --- /dev/null +++ b/frappe/core/doctype/report/report.json @@ -0,0 +1,161 @@ +{ + "autoname": "field:report_name", + "creation": "2013-03-09 15:45:57.000000", + "docstatus": 0, + "doctype": "DocType", + "document_type": "System", + "fields": [ + { + "fieldname": "report_name", + "fieldtype": "Data", + "label": "Report Name", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Ref DocType", + "options": "DocType", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "is_standard", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Is Standard", + "options": "No\nYes", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "add_total_row", + "fieldtype": "Check", + "label": "Add Total Row", + "permlevel": 0 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "permlevel": 0, + "read_only": 0 + }, + { + "fieldname": "report_type", + "fieldtype": "Select", + "label": "Report Type", + "options": "Report Builder\nQuery Report\nScript Report", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled", + "permlevel": 0, + "read_only": 0 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "eval:doc.report_type==\"Query Report\"", + "fieldname": "query", + "fieldtype": "Code", + "label": "Query", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "eval:doc.report_type==\"Query Report\"", + "description": "JavaScript Format: frappe.query_reports['REPORTNAME'] = {}", + "fieldname": "javascript", + "fieldtype": "Code", + "label": "Javascript", + "permlevel": 0 + }, + { + "depends_on": "eval:doc.report_type==\"Report Builder\"", + "fieldname": "json", + "fieldtype": "Text", + "label": "JSON", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "module", + "fieldtype": "Link", + "label": "Module", + "options": "Module Def", + "permlevel": 0 + } + ], + "icon": "icon-table", + "idx": 1, + "modified": "2014-03-07 15:20:02.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Report", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Report Manager", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "submit": 0 + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/report/report.py b/frappe/core/doctype/report/report.py index 881949ff0e..26c646529c 100644 --- a/frappe/core/doctype/report/report.py +++ b/frappe/core/doctype/report/report.py @@ -5,25 +5,24 @@ from __future__ import unicode_literals import frappe from frappe import conf, _ -class DocType: - def __init__(self, doc, doclist): - self.doc, self.doclist = doc, doclist - +from frappe.model.document import Document + +class Report(Document): def validate(self): """only administrator can save standard report""" - if not self.doc.module: - self.doc.module = frappe.db.get_value("DocType", self.doc.ref_doctype, "module") + if not self.module: + self.module = frappe.db.get_value("DocType", self.ref_doctype, "module") - if not self.doc.is_standard: - self.doc.is_standard = "No" + if not self.is_standard: + self.is_standard = "No" if frappe.session.user=="Administrator" and getattr(conf, 'developer_mode',0)==1: - self.doc.is_standard = "Yes" + self.is_standard = "Yes" - if self.doc.is_standard == "Yes" and frappe.session.user!="Administrator": + if self.is_standard == "Yes" and frappe.session.user!="Administrator": frappe.msgprint(_("Only Administrator can save a standard report. Please rename and save."), raise_exception=True) - if self.doc.report_type in ("Query Report", "Script Report") \ + if self.report_type in ("Query Report", "Script Report") \ and frappe.session.user!="Administrator": frappe.msgprint(_("Only Administrator allowed to create Query / Script Reports"), raise_exception=True) @@ -33,6 +32,6 @@ class DocType: def export_doc(self): from frappe.modules.export_file import export_to_files - if self.doc.is_standard == 'Yes' and (conf.get('developer_mode') or 0) == 1: - export_to_files(record_list=[['Report', self.doc.name]], - record_module=self.doc.module) + if self.is_standard == 'Yes' and (conf.get('developer_mode') or 0) == 1: + export_to_files(record_list=[['Report', self.name]], + record_module=self.module) diff --git a/frappe/core/doctype/report/report.txt b/frappe/core/doctype/report/report.txt deleted file mode 100644 index 75051dcbb0..0000000000 --- a/frappe/core/doctype/report/report.txt +++ /dev/null @@ -1,162 +0,0 @@ -[ - { - "creation": "2013-03-09 15:45:57", - "docstatus": 0, - "modified": "2014-03-07 15:20:02", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "field:report_name", - "doctype": "DocType", - "document_type": "System", - "icon": "icon-table", - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Report", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Report", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "submit": 0 - }, - { - "doctype": "DocType", - "name": "Report" - }, - { - "doctype": "DocField", - "fieldname": "report_name", - "fieldtype": "Data", - "label": "Report Name", - "read_only": 0, - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "ref_doctype", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Ref DocType", - "options": "DocType", - "read_only": 0, - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "is_standard", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Is Standard", - "options": "No\nYes", - "read_only": 0, - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "add_total_row", - "fieldtype": "Check", - "label": "Add Total Row" - }, - { - "doctype": "DocField", - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "read_only": 0 - }, - { - "doctype": "DocField", - "fieldname": "report_type", - "fieldtype": "Select", - "label": "Report Type", - "options": "Report Builder\nQuery Report\nScript Report", - "read_only": 0, - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "disabled", - "fieldtype": "Check", - "label": "Disabled", - "read_only": 0 - }, - { - "doctype": "DocField", - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "read_only": 0 - }, - { - "depends_on": "eval:doc.report_type==\"Query Report\"", - "doctype": "DocField", - "fieldname": "query", - "fieldtype": "Code", - "label": "Query", - "read_only": 0 - }, - { - "depends_on": "eval:doc.report_type==\"Query Report\"", - "description": "JavaScript Format: frappe.query_reports['REPORTNAME'] = {}", - "doctype": "DocField", - "fieldname": "javascript", - "fieldtype": "Code", - "label": "Javascript" - }, - { - "depends_on": "eval:doc.report_type==\"Report Builder\"", - "doctype": "DocField", - "fieldname": "json", - "fieldtype": "Text", - "label": "JSON", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "module", - "fieldtype": "Link", - "label": "Module", - "options": "Module Def" - }, - { - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "role": "Administrator", - "write": 1 - }, - { - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "role": "System Manager", - "write": 1 - }, - { - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "role": "Report Manager", - "write": 1 - }, - { - "delete": 0, - "doctype": "DocPerm", - "role": "All" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/role/role.json b/frappe/core/doctype/role/role.json new file mode 100644 index 0000000000..cc08cbcc2a --- /dev/null +++ b/frappe/core/doctype/role/role.json @@ -0,0 +1,58 @@ +{ + "allow_copy": 0, + "allow_rename": 1, + "autoname": "field:role_name", + "creation": "2013-01-08 15:50:01.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "role_name", + "fieldtype": "Data", + "label": "Role Name", + "oldfieldname": "role_name", + "oldfieldtype": "Data", + "permlevel": 0, + "reqd": 1 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-bookmark", + "idx": 1, + "issingle": 0, + "istable": 0, + "modified": "2013-12-20 19:24:27.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Role", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "submit": 0, + "write": 1 + } + ], + "read_only": 0 +} \ No newline at end of file diff --git a/frappe/core/doctype/role/role.py b/frappe/core/doctype/role/role.py index a48f49f2a7..219bed3579 100644 --- a/frappe/core/doctype/role/role.py +++ b/frappe/core/doctype/role/role.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class Role(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/role/role.txt b/frappe/core/doctype/role/role.txt deleted file mode 100644 index 57915c2fdd..0000000000 --- a/frappe/core/doctype/role/role.txt +++ /dev/null @@ -1,69 +0,0 @@ -[ - { - "creation": "2013-01-08 15:50:01", - "docstatus": 0, - "modified": "2013-12-20 19:24:27", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_copy": 0, - "allow_rename": 1, - "autoname": "field:role_name", - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-bookmark", - "issingle": 0, - "istable": 0, - "module": "Core", - "name": "__common__", - "read_only": 0 - }, - { - "doctype": "DocField", - "fieldname": "role_name", - "fieldtype": "Data", - "label": "Role Name", - "name": "__common__", - "oldfieldname": "role_name", - "oldfieldtype": "Data", - "parent": "Role", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "reqd": 1 - }, - { - "cancel": 0, - "create": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Role", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Role" - }, - { - "doctype": "DocField" - }, - { - "amend": 0, - "doctype": "DocPerm", - "role": "System Manager" - }, - { - "doctype": "DocPerm", - "role": "Administrator" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/role/test_records.json b/frappe/core/doctype/role/test_records.json new file mode 100644 index 0000000000..ee1f29f423 --- /dev/null +++ b/frappe/core/doctype/role/test_records.json @@ -0,0 +1,10 @@ +[ + { + "doctype": "Role", + "role_name": "_Test Role" + }, + { + "doctype": "Role", + "role_name": "_Test Role 2" + } +] \ No newline at end of file diff --git a/frappe/core/doctype/role/test_role.py b/frappe/core/doctype/role/test_role.py index b2c9fca743..cda62d7a2a 100644 --- a/frappe/core/doctype/role/test_role.py +++ b/frappe/core/doctype/role/test_role.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -test_records = [ - [{"doctype": "Role", "role_name":"_Test Role"}], - [{"doctype": "Role", "role_name":"_Test Role 2"}] -] \ No newline at end of file +import frappe + +test_records = frappe.get_test_records('Role') \ No newline at end of file diff --git a/frappe/core/doctype/scheduler_log/scheduler_log.json b/frappe/core/doctype/scheduler_log/scheduler_log.json new file mode 100644 index 0000000000..af8f5acc9b --- /dev/null +++ b/frappe/core/doctype/scheduler_log/scheduler_log.json @@ -0,0 +1,45 @@ +{ + "autoname": "SCHLOG.#####", + "creation": "2013-01-16 13:09:40.000000", + "description": "Log of Scheduler Errors", + "docstatus": 0, + "doctype": "DocType", + "document_type": "System", + "fields": [ + { + "fieldname": "method", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Method", + "permlevel": 0 + }, + { + "fieldname": "error", + "fieldtype": "Text", + "label": "Error", + "permlevel": 0 + } + ], + "icon": "icon-warning-sign", + "idx": 1, + "modified": "2014-01-20 17:49:26.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Scheduler Log", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/scheduler_log/scheduler_log.py b/frappe/core/doctype/scheduler_log/scheduler_log.py index cb42e2eea2..58185eb867 100644 --- a/frappe/core/doctype/scheduler_log/scheduler_log.py +++ b/frappe/core/doctype/scheduler_log/scheduler_log.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class SchedulerLog(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/scheduler_log/scheduler_log.txt b/frappe/core/doctype/scheduler_log/scheduler_log.txt deleted file mode 100644 index 2be5cd2c24..0000000000 --- a/frappe/core/doctype/scheduler_log/scheduler_log.txt +++ /dev/null @@ -1,64 +0,0 @@ -[ - { - "creation": "2013-01-16 13:09:40", - "docstatus": 0, - "modified": "2014-01-20 17:49:26", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "SCHLOG.#####", - "description": "Log of Scheduler Errors", - "doctype": "DocType", - "document_type": "System", - "icon": "icon-warning-sign", - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Scheduler Log", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Scheduler Log", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Scheduler Log" - }, - { - "doctype": "DocField", - "fieldname": "method", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Method" - }, - { - "doctype": "DocField", - "fieldname": "error", - "fieldtype": "Text", - "label": "Error" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/social_login_keys/social_login_keys.json b/frappe/core/doctype/social_login_keys/social_login_keys.json new file mode 100644 index 0000000000..98c0190754 --- /dev/null +++ b/frappe/core/doctype/social_login_keys/social_login_keys.json @@ -0,0 +1,79 @@ +{ + "creation": "2014-03-04 08:29:52.000000", + "docstatus": 0, + "doctype": "DocType", + "document_type": "System", + "fields": [ + { + "fieldname": "facebook", + "fieldtype": "Section Break", + "label": "Facebook", + "permlevel": 0 + }, + { + "fieldname": "facebook_client_id", + "fieldtype": "Data", + "label": "Facebook Client ID", + "permlevel": 0 + }, + { + "fieldname": "facebook_client_secret", + "fieldtype": "Data", + "label": "Facebook Client Secret", + "permlevel": 0 + }, + { + "fieldname": "google", + "fieldtype": "Section Break", + "label": "Google", + "permlevel": 0 + }, + { + "fieldname": "google_client_id", + "fieldtype": "Data", + "label": "Google Client ID", + "permlevel": 0 + }, + { + "fieldname": "google_client_secret", + "fieldtype": "Data", + "label": "Google Client Secret", + "permlevel": 0 + }, + { + "fieldname": "github", + "fieldtype": "Section Break", + "label": "GitHub", + "permlevel": 0 + }, + { + "fieldname": "github_client_id", + "fieldtype": "Data", + "label": "GitHub Client ID", + "permlevel": 0 + }, + { + "fieldname": "github_client_secret", + "fieldtype": "Data", + "label": "GitHub Client Secret", + "permlevel": 0 + } + ], + "icon": "icon-signin", + "idx": 1, + "issingle": 1, + "modified": "2014-03-04 08:47:32.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Social Login Keys", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "permlevel": 0, + "read": 1, + "role": "System Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/social_login_keys/social_login_keys.py b/frappe/core/doctype/social_login_keys/social_login_keys.py index cb42e2eea2..1a6b2389e0 100644 --- a/frappe/core/doctype/social_login_keys/social_login_keys.py +++ b/frappe/core/doctype/social_login_keys/social_login_keys.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class SocialLoginKeys(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/social_login_keys/social_login_keys.txt b/frappe/core/doctype/social_login_keys/social_login_keys.txt deleted file mode 100644 index cffedba804..0000000000 --- a/frappe/core/doctype/social_login_keys/social_login_keys.txt +++ /dev/null @@ -1,98 +0,0 @@ -[ - { - "creation": "2014-03-04 08:29:52", - "docstatus": 0, - "modified": "2014-03-04 08:47:32", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "DocType", - "document_type": "System", - "icon": "icon-signin", - "issingle": 1, - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Social Login Keys", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "create": 1, - "doctype": "DocPerm", - "name": "__common__", - "parent": "Social Login Keys", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "read": 1, - "role": "System Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Social Login Keys" - }, - { - "doctype": "DocField", - "fieldname": "facebook", - "fieldtype": "Section Break", - "label": "Facebook" - }, - { - "doctype": "DocField", - "fieldname": "facebook_client_id", - "fieldtype": "Data", - "label": "Facebook Client ID" - }, - { - "doctype": "DocField", - "fieldname": "facebook_client_secret", - "fieldtype": "Data", - "label": "Facebook Client Secret" - }, - { - "doctype": "DocField", - "fieldname": "google", - "fieldtype": "Section Break", - "label": "Google" - }, - { - "doctype": "DocField", - "fieldname": "google_client_id", - "fieldtype": "Data", - "label": "Google Client ID" - }, - { - "doctype": "DocField", - "fieldname": "google_client_secret", - "fieldtype": "Data", - "label": "Google Client Secret" - }, - { - "doctype": "DocField", - "fieldname": "github", - "fieldtype": "Section Break", - "label": "GitHub" - }, - { - "doctype": "DocField", - "fieldname": "github_client_id", - "fieldtype": "Data", - "label": "GitHub Client ID" - }, - { - "doctype": "DocField", - "fieldname": "github_client_secret", - "fieldtype": "Data", - "label": "GitHub Client Secret" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/tag/tag.json b/frappe/core/doctype/tag/tag.json new file mode 100644 index 0000000000..9d243129fe --- /dev/null +++ b/frappe/core/doctype/tag/tag.json @@ -0,0 +1,35 @@ +{ + "autoname": "field:tag_name", + "creation": "2012-07-09 11:17:17.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "tag_name", + "fieldtype": "Data", + "label": "Tag Name", + "oldfieldname": "tag_name", + "oldfieldtype": "Data", + "permlevel": 0 + } + ], + "icon": "icon-tag", + "idx": 1, + "modified": "2013-12-20 19:24:38.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Tag", + "name_case": "Title Case", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "System Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/tag/tag.py b/frappe/core/doctype/tag/tag.py index a48f49f2a7..668d620f78 100644 --- a/frappe/core/doctype/tag/tag.py +++ b/frappe/core/doctype/tag/tag.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class Tag(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/tag/tag.txt b/frappe/core/doctype/tag/tag.txt deleted file mode 100644 index a342874d9c..0000000000 --- a/frappe/core/doctype/tag/tag.txt +++ /dev/null @@ -1,54 +0,0 @@ -[ - { - "creation": "2012-07-09 11:17:17", - "docstatus": 0, - "modified": "2013-12-20 19:24:38", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "field:tag_name", - "doctype": "DocType", - "icon": "icon-tag", - "module": "Core", - "name": "__common__", - "name_case": "Title Case" - }, - { - "doctype": "DocField", - "fieldname": "tag_name", - "fieldtype": "Data", - "label": "Tag Name", - "name": "__common__", - "oldfieldname": "tag_name", - "oldfieldtype": "Data", - "parent": "Tag", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "create": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Tag", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "role": "System Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Tag" - }, - { - "doctype": "DocField" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/todo/todo.json b/frappe/core/doctype/todo/todo.json new file mode 100644 index 0000000000..c525a98347 --- /dev/null +++ b/frappe/core/doctype/todo/todo.json @@ -0,0 +1,198 @@ +{ + "allow_attach": 0, + "allow_copy": 0, + "allow_rename": 0, + "autoname": "TDI.########", + "creation": "2012-07-03 13:30:35.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "description_and_status", + "fieldtype": "Section Break", + "label": "Description and Status", + "permlevel": 0 + }, + { + "allow_on_submit": 0, + "fieldname": "description", + "fieldtype": "Text", + "hidden": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Description", + "no_copy": 0, + "oldfieldname": "description", + "oldfieldtype": "Text", + "permlevel": 0, + "print_hide": 0, + "print_width": "300px", + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "width": "300px" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Open\nClosed", + "permlevel": 0 + }, + { + "allow_on_submit": 0, + "default": "Medium", + "fieldname": "priority", + "fieldtype": "Select", + "hidden": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Priority", + "no_copy": 0, + "oldfieldname": "priority", + "oldfieldtype": "Data", + "options": "High\nMedium\nLow", + "permlevel": 0, + "print_hide": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0 + }, + { + "allow_on_submit": 0, + "fieldname": "date", + "fieldtype": "Date", + "hidden": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Due Date", + "no_copy": 0, + "oldfieldname": "date", + "oldfieldtype": "Date", + "permlevel": 0, + "print_hide": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Reference", + "permlevel": 0 + }, + { + "allow_on_submit": 0, + "fieldname": "reference_type", + "fieldtype": "Data", + "hidden": 0, + "in_filter": 0, + "label": "Reference Type", + "no_copy": 0, + "oldfieldname": "reference_type", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0 + }, + { + "allow_on_submit": 0, + "fieldname": "reference_name", + "fieldtype": "Data", + "hidden": 0, + "in_filter": 0, + "label": "Reference Name", + "no_copy": 0, + "oldfieldname": "reference_name", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "allow_on_submit": 0, + "fieldname": "role", + "fieldtype": "Link", + "hidden": 0, + "in_filter": 0, + "label": "Role", + "no_copy": 0, + "oldfieldname": "role", + "oldfieldtype": "Link", + "options": "Role", + "permlevel": 0, + "print_hide": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "assigned_by", + "fieldtype": "Link", + "label": "Assigned By", + "options": "User", + "permlevel": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-check", + "idx": 1, + "in_create": 0, + "in_dialog": 0, + "issingle": 0, + "max_attachments": 0, + "modified": "2014-03-12 17:06:46.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "ToDo", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "restricted": 1, + "role": "All", + "submit": 0, + "write": 1 + }, + { + "create": 1, + "delete": 0, + "email": 1, + "export": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "write": 1 + } + ], + "read_only": 0, + "read_only_onload": 0, + "title_field": "description" +} \ No newline at end of file diff --git a/frappe/core/doctype/todo/todo.py b/frappe/core/doctype/todo/todo.py index 2203546c0a..936e676c07 100644 --- a/frappe/core/doctype/todo/todo.py +++ b/frappe/core/doctype/todo/todo.py @@ -4,32 +4,31 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl - +from frappe.model.document import Document + +class ToDo(Document): def validate(self): - if self.doc.is_new(): + if self.is_new(): self.add_comment(frappe._("Assignment Added")) else: - cur_status = frappe.db.get_value("ToDo", self.doc.name, "status") - if cur_status != self.doc.status: + cur_status = frappe.db.get_value("ToDo", self.name, "status") + if cur_status != self.status: self.add_comment(frappe._("Assignment Status Changed")) def add_comment(self, text): - if not self.doc.reference_type and self.doc.reference_name: + if not self.reference_type and self.reference_name: return - comment = frappe.bean({ + comment = frappe.get_doc({ "doctype":"Comment", "comment_by": frappe.session.user, - "comment_doctype": self.doc.reference_type, - "comment_docname": self.doc.reference_name, + "comment_doctype": self.reference_type, + "comment_docname": self.reference_name, "comment": """
    {text}: {status}: {description}
    """.format(text=text, - status = frappe._(self.doc.status), - name = self.doc.name, - description = self.doc.description) + status = frappe._(self.status), + name = self.name, + description = self.description) }).insert(ignore_permissions=True) diff --git a/frappe/core/doctype/todo/todo.txt b/frappe/core/doctype/todo/todo.txt deleted file mode 100644 index 7f1b344ad3..0000000000 --- a/frappe/core/doctype/todo/todo.txt +++ /dev/null @@ -1,210 +0,0 @@ -[ - { - "creation": "2012-07-03 13:30:35", - "docstatus": 0, - "modified": "2014-03-12 17:06:46", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 0, - "allow_copy": 0, - "allow_rename": 0, - "autoname": "TDI.########", - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-check", - "in_create": 0, - "in_dialog": 0, - "issingle": 0, - "max_attachments": 0, - "module": "Core", - "name": "__common__", - "read_only": 0, - "read_only_onload": 0, - "title_field": "description" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "ToDo", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "create": 1, - "delete": 0, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "ToDo", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "write": 1 - }, - { - "doctype": "DocType", - "name": "ToDo" - }, - { - "doctype": "DocField", - "fieldname": "description_and_status", - "fieldtype": "Section Break", - "label": "Description and Status" - }, - { - "allow_on_submit": 0, - "doctype": "DocField", - "fieldname": "description", - "fieldtype": "Text", - "hidden": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Description", - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Text", - "print_hide": 0, - "print_width": "300px", - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "width": "300px" - }, - { - "doctype": "DocField", - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "default": "Open", - "doctype": "DocField", - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Status", - "options": "Open\nClosed" - }, - { - "allow_on_submit": 0, - "default": "Medium", - "doctype": "DocField", - "fieldname": "priority", - "fieldtype": "Select", - "hidden": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Priority", - "no_copy": 0, - "oldfieldname": "priority", - "oldfieldtype": "Data", - "options": "High\nMedium\nLow", - "print_hide": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0 - }, - { - "allow_on_submit": 0, - "doctype": "DocField", - "fieldname": "date", - "fieldtype": "Date", - "hidden": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Due Date", - "no_copy": 0, - "oldfieldname": "date", - "oldfieldtype": "Date", - "print_hide": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "label": "Reference" - }, - { - "allow_on_submit": 0, - "doctype": "DocField", - "fieldname": "reference_type", - "fieldtype": "Data", - "hidden": 0, - "in_filter": 0, - "label": "Reference Type", - "no_copy": 0, - "oldfieldname": "reference_type", - "oldfieldtype": "Data", - "print_hide": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0 - }, - { - "allow_on_submit": 0, - "doctype": "DocField", - "fieldname": "reference_name", - "fieldtype": "Data", - "hidden": 0, - "in_filter": 0, - "label": "Reference Name", - "no_copy": 0, - "oldfieldname": "reference_name", - "oldfieldtype": "Data", - "print_hide": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "column_break_10", - "fieldtype": "Column Break" - }, - { - "allow_on_submit": 0, - "doctype": "DocField", - "fieldname": "role", - "fieldtype": "Link", - "hidden": 0, - "in_filter": 0, - "label": "Role", - "no_copy": 0, - "oldfieldname": "role", - "oldfieldtype": "Link", - "options": "Role", - "print_hide": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "assigned_by", - "fieldtype": "Link", - "label": "Assigned By", - "options": "User" - }, - { - "cancel": 0, - "doctype": "DocPerm", - "export": 0, - "restricted": 1, - "role": "All", - "submit": 0 - }, - { - "doctype": "DocPerm", - "export": 1, - "role": "System Manager" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/user/test_records.json b/frappe/core/doctype/user/test_records.json new file mode 100644 index 0000000000..a8ffe46fbd --- /dev/null +++ b/frappe/core/doctype/user/test_records.json @@ -0,0 +1,52 @@ +[ + { + "doctype": "User", + "email": "test@example.com", + "enabled": 1, + "first_name": "_Test", + "new_password": "testpassword", + "user_roles": [ + { + "doctype": "UserRole", + "parentfield": "user_roles", + "role": "_Test Role" + }, + { + "doctype": "UserRole", + "parentfield": "user_roles", + "role": "System Manager" + } + ] + }, + { + "doctype": "User", + "email": "test1@example.com", + "first_name": "_Test1", + "new_password": "testpassword" + }, + { + "doctype": "User", + "email": "test2@example.com", + "first_name": "_Test2", + "new_password": "testpassword" + }, + { + "doctype": "User", + "email": "testdelete@example.com", + "enabled": 1, + "first_name": "_Test", + "new_password": "testpassword", + "user_roles": [ + { + "doctype": "UserRole", + "parentfield": "user_roles", + "role": "_Test Role 2" + }, + { + "doctype": "UserRole", + "parentfield": "user_roles", + "role": "System Manager" + } + ] + } +] \ No newline at end of file diff --git a/frappe/core/doctype/user/test_user.py b/frappe/core/doctype/user/test_user.py index ca5cfe8470..d5dda982a8 100644 --- a/frappe/core/doctype/user/test_user.py +++ b/frappe/core/doctype/user/test_user.py @@ -5,17 +5,19 @@ import frappe, unittest from frappe.model.delete_doc import delete_doc, LinkExistsError +test_records = frappe.get_test_records('User') + class TestUser(unittest.TestCase): def test_delete(self): self.assertRaises(LinkExistsError, delete_doc, "Role", "_Test Role 2") frappe.db.sql("""delete from tabUserRole where role='_Test Role 2'""") delete_doc("Role","_Test Role 2") - user = frappe.bean(copy=test_records[1]) - user.doc.email = "_test@example.com" + user = frappe.copy_doc(test_records[1]) + user.email = "_test@example.com" user.insert() - frappe.bean({"doctype": "ToDo", "description": "_Test"}).insert() + frappe.get_doc({"doctype": "ToDo", "description": "_Test"}).insert() delete_doc("User", "_test@example.com") @@ -23,7 +25,7 @@ class TestUser(unittest.TestCase): ("_test@example.com",))) from frappe.core.doctype.role.test_role import test_records as role_records - frappe.bean(copy=role_records[1]).insert() + frappe.copy_doc(role_records[1]).insert() def test_get_value(self): self.assertEquals(frappe.db.get_value("User", "test@example.com"), "test@example.com") @@ -43,73 +45,7 @@ class TestUser(unittest.TestCase): self.assertEquals(frappe.db.get_value("User", "xxxtest@example.com"), None) - frappe.db.set_value("Control Panel", "Control Panel", "_test", "_test_val") - self.assertEquals(frappe.db.get_value("Control Panel", None, "_test"), "_test_val") - self.assertEquals(frappe.db.get_value("Control Panel", "Control Panel", "_test"), "_test_val") - - def test_doclist(self): - p_meta = frappe.get_doctype("User") - - self.assertEquals(len(p_meta.get({"doctype": "DocField", "parent": "User", "fieldname": "first_name"})), 1) - self.assertEquals(len(p_meta.get({"doctype": "DocField", "parent": "User", "fieldname": "^first"})), 1) - self.assertEquals(len(p_meta.get({"fieldname": ["!=", "first_name"]})), len(p_meta) - 1) - self.assertEquals(len(p_meta.get({"fieldname": ["in", ["first_name", "last_name"]]})), 2) - self.assertEquals(len(p_meta.get({"fieldname": ["not in", ["first_name", "last_name"]]})), len(p_meta) - 2) - - -test_records = [ - [ - { - "doctype":"User", - "email": "test@example.com", - "first_name": "_Test", - "new_password": "testpassword", - "enabled": 1 - }, - { - "doctype":"UserRole", - "parentfield":"user_roles", - "role": "_Test Role" - }, - { - "doctype":"UserRole", - "parentfield":"user_roles", - "role": "System Manager" - } - ], - [ - { - "doctype":"User", - "email": "test1@example.com", - "first_name": "_Test1", - "new_password": "testpassword" - } - ], - [ - { - "doctype":"User", - "email": "test2@example.com", - "first_name": "_Test2", - "new_password": "testpassword" - } - ], - [ - { - "doctype":"User", - "email": "testdelete@example.com", - "first_name": "_Test", - "new_password": "testpassword", - "enabled": 1 - }, - { - "doctype":"UserRole", - "parentfield":"user_roles", - "role": "_Test Role 2" - }, - { - "doctype":"UserRole", - "parentfield":"user_roles", - "role": "System Manager" - } - ], -] \ No newline at end of file + frappe.db.set_value("Website Settings", "Website Settings", "_test", "_test_val") + self.assertEquals(frappe.db.get_value("Website Settings", None, "_test"), "_test_val") + self.assertEquals(frappe.db.get_value("Website Settings", "Website Settings", "_test"), "_test_val") + \ No newline at end of file diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js index d7de077f58..a611a4d122 100644 --- a/frappe/core/doctype/user/user.js +++ b/frappe/core/doctype/user/user.js @@ -151,8 +151,7 @@ frappe.RoleEditor = Class.extend({ .each(function(i, checkbox) { checkbox.checked = false; }); // set user roles as checked - $.each(frappe.model.get("UserRole", {parent: cur_frm.doc.name, - parentfield: "user_roles"}), function(i, user_role) { + $.each((cur_frm.doc.user_roles || []), function(i, user_role) { var checkbox = $(me.wrapper) .find('[data-user-role="'+user_role.role+'"] input[type="checkbox"]').get(0); if(checkbox) checkbox.checked = true; @@ -163,8 +162,7 @@ frappe.RoleEditor = Class.extend({ var existing_roles_map = {}; var existing_roles_list = []; - $.each(frappe.model.get("UserRole", {parent: cur_frm.doc.name, - parentfield: "user_roles"}), function(i, user_role) { + $.each((cur_frm.doc.user_roles || []), function(i, user_role) { existing_roles_map[user_role.role] = user_role.name; existing_roles_list.push(user_role.role); }); diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json new file mode 100644 index 0000000000..4003b57872 --- /dev/null +++ b/frappe/core/doctype/user/user.json @@ -0,0 +1,471 @@ +{ + "allow_attach": 1, + "allow_copy": 0, + "allow_import": 1, + "allow_rename": 1, + "creation": "2014-03-11 14:55:00.000000", + "description": "Represents a User in the system.", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Master", + "fields": [ + { + "fieldname": "sb0_5", + "fieldtype": "Section Break", + "label": "Personal Info", + "permlevel": 0 + }, + { + "default": "1", + "fieldname": "enabled", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Enabled", + "oldfieldname": "enabled", + "oldfieldtype": "Check", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "unsubscribed", + "fieldtype": "Check", + "hidden": 1, + "label": "Unsubscribed", + "permlevel": 0 + }, + { + "fieldname": "email", + "fieldtype": "Data", + "hidden": 0, + "label": "Email", + "oldfieldname": "email", + "oldfieldtype": "Data", + "permlevel": 0, + "reqd": 1, + "search_index": 0 + }, + { + "fieldname": "first_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "First Name", + "oldfieldname": "first_name", + "oldfieldtype": "Data", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "middle_name", + "fieldtype": "Data", + "label": "Middle Name (Optional)", + "oldfieldname": "middle_name", + "oldfieldtype": "Data", + "permlevel": 0 + }, + { + "fieldname": "last_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Last Name", + "oldfieldname": "last_name", + "oldfieldtype": "Data", + "permlevel": 0 + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "permlevel": 0, + "print_width": "50%", + "width": "50%" + }, + { + "description": "Language preference for user interface (only if available).", + "fieldname": "language", + "fieldtype": "Select", + "label": "Language", + "options": "Loading...", + "permlevel": 0 + }, + { + "fieldname": "change_password", + "fieldtype": "Section Break", + "label": "Set Password", + "permlevel": 0 + }, + { + "fieldname": "new_password", + "fieldtype": "Password", + "label": "New Password", + "permlevel": 0 + }, + { + "fieldname": "reset_password_key", + "fieldtype": "Data", + "hidden": 1, + "label": "Reset Password Key", + "permlevel": 0, + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "display_settings", + "fieldtype": "Section Break", + "label": "Display Settings", + "permlevel": 0 + }, + { + "fieldname": "user_image", + "fieldtype": "Attach", + "hidden": 0, + "label": "User Image", + "permlevel": 0 + }, + { + "fieldname": "background_image", + "fieldtype": "Attach", + "hidden": 0, + "label": "Background Image", + "permlevel": 0 + }, + { + "fieldname": "cb21", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "user_image_show", + "fieldtype": "Image", + "label": "user_image_show", + "options": "user_image", + "permlevel": 0 + }, + { + "fieldname": "short_bio", + "fieldtype": "Section Break", + "label": "Short Bio", + "permlevel": 0 + }, + { + "fieldname": "gender", + "fieldtype": "Select", + "label": "Gender", + "oldfieldname": "gender", + "oldfieldtype": "Select", + "options": "\nMale\nFemale\nOther", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "birth_date", + "fieldtype": "Date", + "label": "Birth Date", + "oldfieldname": "birth_date", + "oldfieldtype": "Date", + "permlevel": 0 + }, + { + "fieldname": "location", + "fieldtype": "Data", + "label": "Location", + "permlevel": 0 + }, + { + "fieldname": "column_break_22", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "bio", + "fieldtype": "Small Text", + "label": "Bio", + "permlevel": 0 + }, + { + "description": "Check / Uncheck roles assigned to the User. Click on the Role to find out what permissions that Role has.", + "fieldname": "sb1", + "fieldtype": "Section Break", + "label": "Roles", + "permlevel": 1, + "read_only": 1 + }, + { + "fieldname": "roles_html", + "fieldtype": "HTML", + "label": "Roles HTML", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "incoming_email_settings", + "fieldtype": "Section Break", + "label": "Email Settings", + "permlevel": 1 + }, + { + "fieldname": "cb18", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "email_signature", + "fieldtype": "Small Text", + "label": "Email Signature", + "permlevel": 0 + }, + { + "fieldname": "cb20", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "description": "Pull Emails from the Inbox and attach them as Communication records (for known contacts).", + "fieldname": "sync_inbox", + "fieldtype": "Check", + "hidden": 1, + "label": "Sync Inbox", + "permlevel": 0 + }, + { + "description": "POP3 Mail Server (e.g. pop.gmail.com)", + "fieldname": "email_host", + "fieldtype": "Data", + "hidden": 1, + "label": "Email Host", + "permlevel": 0 + }, + { + "fieldname": "email_use_ssl", + "fieldtype": "Check", + "hidden": 1, + "label": "Email Use SSL", + "permlevel": 0 + }, + { + "fieldname": "email_login", + "fieldtype": "Data", + "hidden": 1, + "label": "Email Login", + "permlevel": 0 + }, + { + "fieldname": "email_password", + "fieldtype": "Password", + "hidden": 1, + "label": "Email Password", + "permlevel": 0 + }, + { + "description": "These values will be automatically updated in transactions and also will be useful to restrict permissions for this user on transactions containing these values.", + "fieldname": "sb2", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Defaults", + "oldfieldtype": "Column Break", + "permlevel": 1, + "print_width": "50%", + "read_only": 1, + "width": "50%" + }, + { + "description": "Enter default value fields (keys) and values. If you add multiple values for a field, the first one will be picked. These defaults are also used to set \"match\" permission rules. To see list of fields, go to Customize Form.", + "fieldname": "defaults", + "fieldtype": "Table", + "hidden": 1, + "label": "User Defaults", + "options": "DefaultValue", + "permlevel": 0 + }, + { + "fieldname": "sb3", + "fieldtype": "Section Break", + "label": "Security Settings", + "oldfieldtype": "Section Break", + "permlevel": 0, + "read_only": 1 + }, + { + "default": "System User", + "description": "User Type \"System User\" can access Desktop. \"Website User\" can only be logged into the website and portal pages. ", + "fieldname": "user_type", + "fieldtype": "Select", + "label": "User Type", + "oldfieldname": "user_type", + "oldfieldtype": "Select", + "options": "System User\nWebsite User", + "permlevel": 0, + "read_only": 1, + "reqd": 1 + }, + { + "description": "Allow user to login only after this hour (0-24)", + "fieldname": "login_after", + "fieldtype": "Int", + "label": "Login After", + "permlevel": 0, + "read_only": 1 + }, + { + "description": "Allow user to login only before this hour (0-24)", + "fieldname": "login_before", + "fieldtype": "Int", + "label": "Login Before", + "permlevel": 0, + "read_only": 1 + }, + { + "description": "Restrict user from this IP address only. Multiple IP addresses can be added by separating with commas. Also accepts partial IP addresses like (111.111.111)", + "fieldname": "restrict_ip", + "fieldtype": "Data", + "label": "Restrict IP", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "permlevel": 0, + "print_width": "50%", + "width": "50%" + }, + { + "fieldname": "last_login", + "fieldtype": "Read Only", + "hidden": 0, + "label": "Last Login", + "oldfieldname": "last_login", + "oldfieldtype": "Read Only", + "permlevel": 0, + "read_only": 1, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "last_ip", + "fieldtype": "Read Only", + "label": "Last IP", + "oldfieldname": "last_ip", + "oldfieldtype": "Read Only", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "roles_assigned_to_user", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Roles Assigned To User", + "no_copy": 0, + "permlevel": 0, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "user_roles", + "fieldtype": "Table", + "hidden": 1, + "label": "Roles Assigned", + "options": "UserRole", + "permlevel": 0, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "third_party_authentication", + "fieldtype": "Section Break", + "label": "Third Party Authentication", + "permlevel": 0 + }, + { + "fieldname": "fb_username", + "fieldtype": "Data", + "label": "Facebook Username", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "fb_userid", + "fieldtype": "Data", + "label": "Facebook User ID", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "google_userid", + "fieldtype": "Data", + "label": "Google User ID", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "github_userid", + "fieldtype": "Data", + "label": "Github User ID", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "github_username", + "fieldtype": "Data", + "label": "Github Username", + "permlevel": 0, + "read_only": 1 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-user", + "idx": 1, + "issingle": 0, + "istable": 0, + "max_attachments": 5, + "modified": "2014-03-11 16:04:13.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "User", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "restricted": 1, + "role": "All", + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "permlevel": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "submit": 0, + "write": 1 + } + ], + "read_only": 0, + "search_fields": "first_name, last_name" +} \ No newline at end of file diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 1f8b9be91e..f4897581db 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -9,81 +9,79 @@ from frappe.auth import _update_password STANDARD_USERS = ("Guest", "Administrator") -class DocType: - def __init__(self, doc, doclist): - self.doc = doc - self.doclist = doclist +from frappe.model.document import Document + +class User(Document): def autoname(self): """set name as email id""" - if self.doc.name not in STANDARD_USERS: - self.doc.email = self.doc.email.strip() - self.doc.name = self.doc.email + if self.name not in STANDARD_USERS: + self.email = self.email.strip() + self.name = self.email - if frappe.db.exists("User", self.doc.name): + if frappe.db.exists("User", self.name): throw(_("Name Exists")) def validate(self): - self.in_insert = self.doc.fields.get("__islocal") - if self.doc.name not in STANDARD_USERS: - self.validate_email_type(self.doc.email) + self.in_insert = self.get("__islocal") + if self.name not in STANDARD_USERS: + self.validate_email_type(self.email) self.add_system_manager_role() self.check_enable_disable() self.update_gravatar() def check_enable_disable(self): # do not allow disabling administrator/guest - if not cint(self.doc.enabled) and self.doc.name in STANDARD_USERS: + if not cint(self.enabled) and self.name in STANDARD_USERS: throw("{msg}: {name}".format(**{ "msg": _("Hey! You cannot disable user"), - "name": self.doc.name + "name": self.name })) - if not cint(self.doc.enabled): + if not cint(self.enabled): self.a_system_manager_should_exist() # clear sessions if disabled - if not cint(self.doc.enabled) and getattr(frappe, "login_manager", None): - frappe.local.login_manager.logout(user=self.doc.name) + if not cint(self.enabled) and getattr(frappe, "login_manager", None): + frappe.local.login_manager.logout(user=self.name) def add_system_manager_role(self): # if adding system manager, do nothing - if not cint(self.doc.enabled) or ("System Manager" in [user_role.role for user_role in - self.doclist.get({"parentfield": "user_roles"})]): + if not cint(self.enabled) or ("System Manager" in [user_role.role for user_role in + self.get("user_roles")]): return - if self.doc.user_type == "System User" and not self.get_other_system_managers(): + if self.user_type == "System User" and not self.get_other_system_managers(): msgprint("""Adding System Manager Role as there must be atleast one 'System Manager'.""") - self.doclist.append({ + self.append("user_roles", { "doctype": "UserRole", - "parentfield": "user_roles", "role": "System Manager" }) def email_new_password(self, new_password=None): if new_password and not self.in_insert: - _update_password(self.doc.name, new_password) + _update_password(self.name, new_password) self.password_update_mail(new_password) frappe.msgprint("New Password Emailed.") def on_update(self): # owner is always name - frappe.db.set(self.doc, 'owner', self.doc.name) + frappe.db.set(self, 'owner', self.name) # clear new password - new_password = self.doc.new_password - self.doc.set("new_password", "") + new_password = self.new_password + self.set("new_password", "") - frappe.clear_cache(user=self.doc.name) + frappe.clear_cache(user=self.name) try: if self.in_insert: - if self.doc.name not in STANDARD_USERS: + if self.name not in STANDARD_USERS: if new_password: # new password given, no email required - _update_password(self.doc.name, new_password) + _update_password(self.name, new_password) if not getattr(self, "no_welcome_mail", False): self.send_welcome_mail() msgprint(_("Welcome Email Sent")) @@ -96,15 +94,15 @@ class DocType: def update_gravatar(self): import md5 - if not self.doc.user_image: - self.doc.user_image = "https://secure.gravatar.com/avatar/" + md5.md5(self.doc.name).hexdigest() \ + if not self.user_image: + self.user_image = "https://secure.gravatar.com/avatar/" + md5.md5(self.name).hexdigest() \ + "?d=retro" def reset_password(self): from frappe.utils import random_string, get_url key = random_string(32) - frappe.db.set_value("User", self.doc.name, "reset_password_key", key) + frappe.db.set_value("User", self.name, "reset_password_key", key) self.password_reset_mail(get_url("/update-password?key=" + key)) def get_other_system_managers(self): @@ -112,12 +110,12 @@ class DocType: where role='System Manager' and docstatus<2 and parent not in ('Administrator', %s) and exists (select * from `tabUser` user - where user.name=user_role.parent and enabled=1)""", (self.doc.name,)) + where user.name=user_role.parent and enabled=1)""", (self.name,)) def get_fullname(self): """get first_name space last_name""" - return (self.doc.first_name or '') + \ - (self.doc.first_name and " " or '') + (self.doc.last_name or '') + return (self.first_name or '') + \ + (self.first_name and " " or '') + (self.last_name or '') def password_reset_mail(self, link): self.send_login_mail("Password Reset", "templates/emails/password_reset.html", {"link": link}) @@ -128,8 +126,8 @@ class DocType: def send_welcome_mail(self): from frappe.utils import random_string, get_url - self.doc.reset_password_key = random_string(32) - link = get_url("/update-password?key=" + self.doc.reset_password_key) + self.reset_password_key = random_string(32) + link = get_url("/update-password?key=" + self.reset_password_key) self.send_login_mail("Verify Your Account", "templates/emails/new_user.html", {"link": link}) @@ -146,8 +144,8 @@ class DocType: full_name = "Administrator" args = { - 'first_name': self.doc.first_name or self.doc.last_name or "user", - 'user': self.doc.name, + 'first_name': self.first_name or self.last_name or "user", + 'user': self.name, 'title': title, 'login_url': get_url(), 'user_fullname': full_name @@ -157,7 +155,7 @@ class DocType: sender = frappe.session.user not in STANDARD_USERS and frappe.session.user or None - frappe.sendmail(recipients=self.doc.email, sender=sender, subject=subject, + frappe.sendmail(recipients=self.email, sender=sender, subject=subject, message=frappe.get_template(template).render(args)) def a_system_manager_should_exist(self): @@ -165,36 +163,36 @@ class DocType: throw(_("Hey! There should remain at least one System Manager")) def on_trash(self): - frappe.clear_cache(user=self.doc.name) - if self.doc.name in STANDARD_USERS: + frappe.clear_cache(user=self.name) + if self.name in STANDARD_USERS: throw("{msg}: {name}".format(**{ "msg": _("Hey! You cannot delete user"), - "name": self.doc.name + "name": self.name })) self.a_system_manager_should_exist() # disable the user and log him/her out - self.doc.enabled = 0 + self.enabled = 0 if getattr(frappe.local, "login_manager", None): - frappe.local.login_manager.logout(user=self.doc.name) + frappe.local.login_manager.logout(user=self.name) # delete their password - frappe.db.sql("""delete from __Auth where user=%s""", (self.doc.name,)) + frappe.db.sql("""delete from __Auth where user=%s""", (self.name,)) # delete todos - frappe.db.sql("""delete from `tabToDo` where owner=%s""", (self.doc.name,)) + frappe.db.sql("""delete from `tabToDo` where owner=%s""", (self.name,)) frappe.db.sql("""update tabToDo set assigned_by=null where assigned_by=%s""", - (self.doc.name,)) + (self.name,)) # delete events frappe.db.sql("""delete from `tabEvent` where owner=%s - and event_type='Private'""", (self.doc.name,)) - frappe.db.sql("""delete from `tabEvent User` where person=%s""", (self.doc.name,)) + and event_type='Private'""", (self.name,)) + frappe.db.sql("""delete from `tabEvent User` where person=%s""", (self.name,)) # delete messages frappe.db.sql("""delete from `tabComment` where comment_doctype='Message' - and (comment_docname=%s or owner=%s)""", (self.doc.name, self.doc.name)) + and (comment_docname=%s or owner=%s)""", (self.name, self.name)) def before_rename(self, olddn, newdn, merge=False): frappe.clear_cache(user=olddn) @@ -245,15 +243,14 @@ class DocType: def add_roles(self, *roles): for role in roles: - if role in [d.role for d in self.doclist.get({"doctype":"UserRole"})]: + if role in [d.role for d in self.get("user_roles")]: continue - self.bean.doclist.append({ + self.append("user_roles", { "doctype": "UserRole", - "parentfield": "user_roles", "role": role }) - self.bean.save() + self.save() @frappe.whitelist() def get_languages(): @@ -315,7 +312,7 @@ def sign_up(email, full_name): TIMEDIFF(%s, modified) > '1:00:00' """, now())[0][0] > 200: raise Exception, "Too Many New Users" from frappe.utils import random_string - user = frappe.bean({ + user = frappe.get_doc({ "doctype":"User", "email": email, "first_name": full_name, @@ -336,8 +333,8 @@ def reset_password(user): if frappe.db.sql("""select name from tabUser where name=%s""", (user,)): # Hack! frappe.session["user"] = "Administrator" - user = frappe.bean("User", user) - user.get_controller().reset_password() + user = frappe.get_doc("User", user) + user.reset_password() return "Password reset details sent to your email." else: return "No such user (%s)" % user diff --git a/frappe/core/doctype/user/user.txt b/frappe/core/doctype/user/user.txt deleted file mode 100644 index 49ba8436e7..0000000000 --- a/frappe/core/doctype/user/user.txt +++ /dev/null @@ -1,533 +0,0 @@ -[ - { - "creation": "2014-03-11 14:55:00", - "docstatus": 0, - "modified": "2014-03-11 16:04:13", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 1, - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 1, - "description": "Represents a User in the system.", - "doctype": "DocType", - "document_type": "Master", - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-user", - "issingle": 0, - "istable": 0, - "max_attachments": 5, - "module": "Core", - "name": "__common__", - "read_only": 0, - "search_fields": "first_name, last_name" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "User", - "parentfield": "fields", - "parenttype": "DocType" - }, - { - "cancel": 0, - "delete": 0, - "doctype": "DocPerm", - "name": "__common__", - "parent": "User", - "parentfield": "permissions", - "parenttype": "DocType", - "read": 1, - "report": 1, - "submit": 0 - }, - { - "doctype": "DocType", - "name": "User" - }, - { - "doctype": "DocField", - "fieldname": "sb0_5", - "fieldtype": "Section Break", - "label": "Personal Info", - "permlevel": 0 - }, - { - "default": "1", - "doctype": "DocField", - "fieldname": "enabled", - "fieldtype": "Check", - "in_list_view": 1, - "label": "Enabled", - "oldfieldname": "enabled", - "oldfieldtype": "Check", - "permlevel": 0, - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "unsubscribed", - "fieldtype": "Check", - "hidden": 1, - "label": "Unsubscribed", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "email", - "fieldtype": "Data", - "hidden": 0, - "label": "Email", - "oldfieldname": "email", - "oldfieldtype": "Data", - "permlevel": 0, - "reqd": 1, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "first_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "First Name", - "oldfieldname": "first_name", - "oldfieldtype": "Data", - "permlevel": 0, - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "middle_name", - "fieldtype": "Data", - "label": "Middle Name (Optional)", - "oldfieldname": "middle_name", - "oldfieldtype": "Data", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "last_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Last Name", - "oldfieldname": "last_name", - "oldfieldtype": "Data", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "column_break0", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_width": "50%", - "width": "50%" - }, - { - "description": "Language preference for user interface (only if available).", - "doctype": "DocField", - "fieldname": "language", - "fieldtype": "Select", - "label": "Language", - "options": "Loading...", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "change_password", - "fieldtype": "Section Break", - "label": "Set Password", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "new_password", - "fieldtype": "Password", - "label": "New Password", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "reset_password_key", - "fieldtype": "Data", - "hidden": 1, - "label": "Reset Password Key", - "permlevel": 0, - "print_hide": 1, - "read_only": 1 - }, - { - "depends_on": "eval:!doc.__islocal", - "doctype": "DocField", - "fieldname": "display_settings", - "fieldtype": "Section Break", - "label": "Display Settings", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "user_image", - "fieldtype": "Attach", - "hidden": 0, - "label": "User Image", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "background_image", - "fieldtype": "Attach", - "hidden": 0, - "label": "Background Image", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "cb21", - "fieldtype": "Column Break", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "user_image_show", - "fieldtype": "Image", - "label": "user_image_show", - "options": "user_image", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "short_bio", - "fieldtype": "Section Break", - "label": "Short Bio", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "gender", - "fieldtype": "Select", - "label": "Gender", - "oldfieldname": "gender", - "oldfieldtype": "Select", - "options": "\nMale\nFemale\nOther", - "permlevel": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "birth_date", - "fieldtype": "Date", - "label": "Birth Date", - "oldfieldname": "birth_date", - "oldfieldtype": "Date", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "location", - "fieldtype": "Data", - "label": "Location", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "column_break_22", - "fieldtype": "Column Break", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "bio", - "fieldtype": "Small Text", - "label": "Bio", - "permlevel": 0 - }, - { - "description": "Check / Uncheck roles assigned to the User. Click on the Role to find out what permissions that Role has.", - "doctype": "DocField", - "fieldname": "sb1", - "fieldtype": "Section Break", - "label": "Roles", - "permlevel": 1, - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "roles_html", - "fieldtype": "HTML", - "label": "Roles HTML", - "permlevel": 0, - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "incoming_email_settings", - "fieldtype": "Section Break", - "label": "Email Settings", - "permlevel": 1 - }, - { - "doctype": "DocField", - "fieldname": "cb18", - "fieldtype": "Column Break", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "email_signature", - "fieldtype": "Small Text", - "label": "Email Signature", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "cb20", - "fieldtype": "Column Break", - "permlevel": 0 - }, - { - "description": "Pull Emails from the Inbox and attach them as Communication records (for known contacts).", - "doctype": "DocField", - "fieldname": "sync_inbox", - "fieldtype": "Check", - "hidden": 1, - "label": "Sync Inbox", - "permlevel": 0 - }, - { - "description": "POP3 Mail Server (e.g. pop.gmail.com)", - "doctype": "DocField", - "fieldname": "email_host", - "fieldtype": "Data", - "hidden": 1, - "label": "Email Host", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "email_use_ssl", - "fieldtype": "Check", - "hidden": 1, - "label": "Email Use SSL", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "email_login", - "fieldtype": "Data", - "hidden": 1, - "label": "Email Login", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "email_password", - "fieldtype": "Password", - "hidden": 1, - "label": "Email Password", - "permlevel": 0 - }, - { - "description": "These values will be automatically updated in transactions and also will be useful to restrict permissions for this user on transactions containing these values.", - "doctype": "DocField", - "fieldname": "sb2", - "fieldtype": "Section Break", - "hidden": 1, - "label": "Defaults", - "oldfieldtype": "Column Break", - "permlevel": 1, - "print_width": "50%", - "read_only": 1, - "width": "50%" - }, - { - "description": "Enter default value fields (keys) and values. If you add multiple values for a field, the first one will be picked. These defaults are also used to set \"match\" permission rules. To see list of fields, go to Customize Form.", - "doctype": "DocField", - "fieldname": "defaults", - "fieldtype": "Table", - "hidden": 1, - "label": "User Defaults", - "options": "DefaultValue", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "sb3", - "fieldtype": "Section Break", - "label": "Security Settings", - "oldfieldtype": "Section Break", - "permlevel": 0, - "read_only": 1 - }, - { - "default": "System User", - "description": "User Type \"System User\" can access Desktop. \"Website User\" can only be logged into the website and portal pages. ", - "doctype": "DocField", - "fieldname": "user_type", - "fieldtype": "Select", - "label": "User Type", - "oldfieldname": "user_type", - "oldfieldtype": "Select", - "options": "System User\nWebsite User", - "permlevel": 0, - "read_only": 1, - "reqd": 1 - }, - { - "description": "Allow user to login only after this hour (0-24)", - "doctype": "DocField", - "fieldname": "login_after", - "fieldtype": "Int", - "label": "Login After", - "permlevel": 0, - "read_only": 1 - }, - { - "description": "Allow user to login only before this hour (0-24)", - "doctype": "DocField", - "fieldname": "login_before", - "fieldtype": "Int", - "label": "Login Before", - "permlevel": 0, - "read_only": 1 - }, - { - "description": "Restrict user from this IP address only. Multiple IP addresses can be added by separating with commas. Also accepts partial IP addresses like (111.111.111)", - "doctype": "DocField", - "fieldname": "restrict_ip", - "fieldtype": "Data", - "label": "Restrict IP", - "permlevel": 0, - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "column_break1", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_width": "50%", - "width": "50%" - }, - { - "doctype": "DocField", - "fieldname": "last_login", - "fieldtype": "Read Only", - "hidden": 0, - "label": "Last Login", - "oldfieldname": "last_login", - "oldfieldtype": "Read Only", - "permlevel": 0, - "read_only": 1, - "reqd": 0, - "search_index": 0 - }, - { - "doctype": "DocField", - "fieldname": "last_ip", - "fieldtype": "Read Only", - "label": "Last IP", - "oldfieldname": "last_ip", - "oldfieldtype": "Read Only", - "permlevel": 0, - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "roles_assigned_to_user", - "fieldtype": "Section Break", - "hidden": 1, - "label": "Roles Assigned To User", - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "user_roles", - "fieldtype": "Table", - "hidden": 1, - "label": "Roles Assigned", - "options": "UserRole", - "permlevel": 0, - "print_hide": 1, - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "third_party_authentication", - "fieldtype": "Section Break", - "label": "Third Party Authentication", - "permlevel": 0 - }, - { - "doctype": "DocField", - "fieldname": "fb_username", - "fieldtype": "Data", - "label": "Facebook Username", - "permlevel": 0, - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "fb_userid", - "fieldtype": "Data", - "label": "Facebook User ID", - "permlevel": 0, - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "google_userid", - "fieldtype": "Data", - "label": "Google User ID", - "permlevel": 0, - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "github_userid", - "fieldtype": "Data", - "label": "Github User ID", - "permlevel": 0, - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "github_username", - "fieldtype": "Data", - "label": "Github Username", - "permlevel": 0, - "read_only": 1 - }, - { - "create": 1, - "doctype": "DocPerm", - "email": 1, - "permlevel": 0, - "print": 1, - "role": "System Manager", - "write": 1 - }, - { - "create": 0, - "doctype": "DocPerm", - "email": 1, - "permlevel": 0, - "print": 1, - "restricted": 1, - "role": "All", - "write": 0 - }, - { - "amend": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 1, - "role": "System Manager", - "write": 1 - } -] \ No newline at end of file diff --git a/frappe/core/doctype/userrole/userrole.json b/frappe/core/doctype/userrole/userrole.json new file mode 100644 index 0000000000..fb9094cb33 --- /dev/null +++ b/frappe/core/doctype/userrole/userrole.json @@ -0,0 +1,35 @@ +{ + "allow_copy": 0, + "autoname": "UR.#####", + "creation": "2013-02-06 11:30:13.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "role", + "fieldtype": "Link", + "hidden": 0, + "in_list_view": 1, + "label": "Role", + "oldfieldname": "role", + "oldfieldtype": "Link", + "options": "Role", + "permlevel": 0, + "print_width": "200px", + "reqd": 0, + "search_index": 0, + "width": "200px" + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 1, + "issingle": 0, + "istable": 1, + "modified": "2013-12-20 19:21:54.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "UserRole", + "owner": "Administrator", + "read_only": 0 +} \ No newline at end of file diff --git a/frappe/core/doctype/userrole/userrole.py b/frappe/core/doctype/userrole/userrole.py index 20f5e77cce..b9ff4b704f 100644 --- a/frappe/core/doctype/userrole/userrole.py +++ b/frappe/core/doctype/userrole/userrole.py @@ -5,12 +5,11 @@ from __future__ import unicode_literals import frappe from frappe.utils import cint -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl - +from frappe.model.document import Document + +class UserRole(Document): def validate(self): - if cint(self.doc.fields.get("__islocal")) and frappe.db.exists("UserRole", { - "parent": self.doc.parent, "role": self.doc.role}): + if cint(self.get("__islocal")) and frappe.db.exists("UserRole", { + "parent": self.parent, "role": self.role}): frappe.msgprint("Role Already Exists", raise_exception=True) \ No newline at end of file diff --git a/frappe/core/doctype/userrole/userrole.txt b/frappe/core/doctype/userrole/userrole.txt deleted file mode 100644 index 9a0846d63e..0000000000 --- a/frappe/core/doctype/userrole/userrole.txt +++ /dev/null @@ -1,48 +0,0 @@ -[ - { - "creation": "2013-02-06 11:30:13", - "docstatus": 0, - "modified": "2013-12-20 19:21:54", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_copy": 0, - "autoname": "UR.#####", - "doctype": "DocType", - "hide_heading": 0, - "hide_toolbar": 0, - "issingle": 0, - "istable": 1, - "module": "Core", - "name": "__common__", - "read_only": 0 - }, - { - "doctype": "DocField", - "fieldname": "role", - "fieldtype": "Link", - "hidden": 0, - "in_list_view": 1, - "label": "Role", - "name": "__common__", - "oldfieldname": "role", - "oldfieldtype": "Link", - "options": "Role", - "parent": "UserRole", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "print_width": "200px", - "reqd": 0, - "search_index": 0, - "width": "200px" - }, - { - "doctype": "DocType", - "name": "UserRole" - }, - { - "doctype": "DocField" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/version/version.json b/frappe/core/doctype/version/version.json new file mode 100644 index 0000000000..87a967e28d --- /dev/null +++ b/frappe/core/doctype/version/version.json @@ -0,0 +1,46 @@ +{ + "autoname": "_VER.######", + "creation": "2014-02-20 17:22:37.000000", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Master", + "fields": [ + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "label": "Ref DocType", + "options": "DocType", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "docname", + "fieldtype": "Data", + "label": "Docname", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "doclist_json", + "fieldtype": "Code", + "label": "Doclist JSON", + "permlevel": 0, + "reqd": 1 + } + ], + "idx": 1, + "modified": "2014-02-20 17:22:38.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Version", + "owner": "Administrator", + "permissions": [ + { + "export": 1, + "permlevel": 0, + "read": 1, + "report": 1, + "role": "System Manager" + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/version/version.py b/frappe/core/doctype/version/version.py index edb6e2e185..5ddb2996d6 100644 --- a/frappe/core/doctype/version/version.py +++ b/frappe/core/doctype/version/version.py @@ -6,25 +6,24 @@ from __future__ import unicode_literals import frappe, json -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class Version(Document): + pass @frappe.whitelist() def restore(version): if not "System Manager" in frappe.get_roles(): raise frappe.PermissionError - version = frappe.doc("Version", version) - doclist = json.loads(version.doclist_json) + version = frappe.get_doc("Version", version) + docdict = json.loads(version.doclist_json) # check if renamed - if doclist[0].get("name") != version.docname: - doclist[0]["name"] = version.docname - for d in doclist[1:]: - d["parent"] = version.docname + if docdict.get("name") != version.docname: + docdict["name"] = version.docname - doclist[0]["modified"] = frappe.db.get_value(version.ref_doctype, version.docname, "modified") + docdict["modified"] = frappe.db.get_value(version.ref_doctype, version.docname, "modified") # overwrite - frappe.bean(doclist).save() \ No newline at end of file + frappe.get_doc(docdict).save() \ No newline at end of file diff --git a/frappe/core/doctype/version/version.txt b/frappe/core/doctype/version/version.txt deleted file mode 100644 index f7a1a046dc..0000000000 --- a/frappe/core/doctype/version/version.txt +++ /dev/null @@ -1,63 +0,0 @@ -[ - { - "creation": "2014-02-20 17:22:37", - "docstatus": 0, - "modified": "2014-02-20 17:22:38", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "_VER.######", - "doctype": "DocType", - "document_type": "Master", - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Version", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "reqd": 1 - }, - { - "doctype": "DocPerm", - "export": 1, - "name": "__common__", - "parent": "Version", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "read": 1, - "report": 1, - "role": "System Manager" - }, - { - "doctype": "DocType", - "name": "Version" - }, - { - "doctype": "DocField", - "fieldname": "ref_doctype", - "fieldtype": "Link", - "label": "Ref DocType", - "options": "DocType" - }, - { - "doctype": "DocField", - "fieldname": "docname", - "fieldtype": "Data", - "label": "Docname" - }, - { - "doctype": "DocField", - "fieldname": "doclist_json", - "fieldtype": "Code", - "label": "Doclist JSON" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/workflow/workflow.js b/frappe/core/doctype/workflow/workflow.js index d1f25446d5..7cbfd6b715 100644 --- a/frappe/core/doctype/workflow/workflow.js +++ b/frappe/core/doctype/workflow/workflow.js @@ -20,11 +20,9 @@ frappe.core.Workflow = frappe.ui.form.Controller.extend({ } }, update_field_options: function() { - var fields = $.map(frappe.model.get("DocField", { - parent: this.frm.doc.document_type, - fieldtype: ["not in", frappe.model.no_value_type] - }), - function(d) { return d.fieldname; }); + var fields = $.map(frappe.get_doc("DocType", this.frm.doc.document_type).fields, function(d) { + return frappe.model.no_value_type.indexOf(d.fieldtype)===-1 ? d.fieldname : null; + }) frappe.meta.get_docfield("Workflow Document State", "update_field", this.frm.doc.name).options = [""].concat(fields); } diff --git a/frappe/core/doctype/workflow/workflow.json b/frappe/core/doctype/workflow/workflow.json new file mode 100644 index 0000000000..741a613d43 --- /dev/null +++ b/frappe/core/doctype/workflow/workflow.json @@ -0,0 +1,94 @@ +{ + "autoname": "field:workflow_name", + "creation": "2012-12-28 10:49:55.000000", + "description": "Defines workflow states and rules for a document.", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Transaction", + "fields": [ + { + "fieldname": "workflow_name", + "fieldtype": "Data", + "label": "Workflow Name", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "description": "DocType on which this Workflow is applicable.", + "fieldname": "document_type", + "fieldtype": "Link", + "label": "Document Type", + "options": "DocType", + "permlevel": 0, + "reqd": 1 + }, + { + "description": "If checked, all other workflows become inactive.", + "fieldname": "is_active", + "fieldtype": "Check", + "label": "Is Active", + "permlevel": 0 + }, + { + "description": "Different \"States\" this document can exist in. Like \"Open\", \"Pending Approval\" etc.", + "fieldname": "states", + "fieldtype": "Section Break", + "label": "States", + "permlevel": 0 + }, + { + "description": "All possible Workflow States and roles of the workflow.
    Docstatus Options: 0 is\"Saved\", 1 is \"Submitted\" and 2 is \"Cancelled\"", + "fieldname": "workflow_document_states", + "fieldtype": "Table", + "label": "Workflow Document States", + "options": "Workflow Document State", + "permlevel": 0, + "reqd": 1 + }, + { + "description": "Rules for how states are transitions, like next state and which role is allowed to change state etc.", + "fieldname": "transition_rules", + "fieldtype": "Section Break", + "label": "Transition Rules", + "permlevel": 0 + }, + { + "description": "Rules defining transition of state in the workflow.", + "fieldname": "workflow_transitions", + "fieldtype": "Table", + "label": "Workflow Transitions", + "options": "Workflow Transition", + "permlevel": 0, + "reqd": 1 + }, + { + "default": "workflow_state", + "description": "Field that represents the Workflow State of the transaction (if field is not present, a new hidden Custom Field will be created)", + "fieldname": "workflow_state_field", + "fieldtype": "Data", + "label": "Workflow State Field", + "permlevel": 0 + } + ], + "icon": "icon-random", + "idx": 1, + "modified": "2014-01-20 17:49:35.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Workflow", + "owner": "Administrator", + "permissions": [ + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "System Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/workflow/workflow.py b/frappe/core/doctype/workflow/workflow.py index 9550d667d8..ab76cf5205 100644 --- a/frappe/core/doctype/workflow/workflow.py +++ b/frappe/core/doctype/workflow/workflow.py @@ -4,9 +4,9 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class Workflow(Document): def validate(self): self.set_active() @@ -14,44 +14,40 @@ class DocType: self.update_default_workflow_status() def on_update(self): - frappe.clear_cache(doctype=self.doc.document_type) + frappe.clear_cache(doctype=self.document_type) def create_custom_field_for_workflow_state(self): - frappe.clear_cache(doctype=self.doc.document_type) - doctypeobj = frappe.get_doctype(self.doc.document_type) - if not len(doctypeobj.get({"doctype":"DocField", - "fieldname":self.doc.workflow_state_field})): - + frappe.clear_cache(doctype=self.document_type) + meta = frappe.get_meta(self.document_type) + if not meta.get_field(self.workflow_state_field): # create custom field - frappe.bean([{ + frappe.get_doc({ "doctype":"Custom Field", - "dt": self.doc.document_type, + "dt": self.document_type, "__islocal": 1, - "fieldname": self.doc.workflow_state_field, - "label": self.doc.workflow_state_field.replace("_", " ").title(), + "fieldname": self.workflow_state_field, + "label": self.workflow_state_field.replace("_", " ").title(), "hidden": 1, "fieldtype": "Link", "options": "Workflow State", - #"insert_after": doctypeobj.get({"doctype":"DocField"})[-1].fieldname - }]).save() + }).save() - frappe.msgprint("Created Custom Field '%s' in '%s'" % (self.doc.workflow_state_field, - self.doc.document_type)) + frappe.msgprint("Created Custom Field '%s' in '%s'" % (self.workflow_state_field, + self.document_type)) def update_default_workflow_status(self): docstatus_map = {} - states = self.doclist.get({"doctype": "Workflow Document State"}) - states.sort(lambda x, y: x.idx - y.idx) - for d in self.doclist.get({"doctype": "Workflow Document State"}): + states = self.get("workflow_document_states") + for d in states: if not d.doc_status in docstatus_map: frappe.db.sql("""update `tab%s` set `%s` = %s where \ - ifnull(`%s`, '')='' and docstatus=%s""" % (self.doc.document_type, self.doc.workflow_state_field, - '%s', self.doc.workflow_state_field, "%s"), (d.state, d.doc_status)) + ifnull(`%s`, '')='' and docstatus=%s""" % (self.document_type, self.workflow_state_field, + '%s', self.workflow_state_field, "%s"), (d.state, d.doc_status)) docstatus_map[d.doc_status] = d.state def set_active(self): - if int(self.doc.is_active or 0): + if int(self.is_active or 0): # clear all other frappe.db.sql("""update tabWorkflow set is_active=0 where document_type=%s""", - self.doc.document_type) + self.document_type) diff --git a/frappe/core/doctype/workflow/workflow.txt b/frappe/core/doctype/workflow/workflow.txt deleted file mode 100644 index 731c8c7dbe..0000000000 --- a/frappe/core/doctype/workflow/workflow.txt +++ /dev/null @@ -1,113 +0,0 @@ -[ - { - "creation": "2012-12-28 10:49:55", - "docstatus": 0, - "modified": "2014-01-20 17:49:35", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "field:workflow_name", - "description": "Defines workflow states and rules for a document.", - "doctype": "DocType", - "document_type": "Transaction", - "icon": "icon-random", - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Workflow", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Workflow", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "role": "System Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Workflow" - }, - { - "doctype": "DocField", - "fieldname": "workflow_name", - "fieldtype": "Data", - "label": "Workflow Name", - "read_only": 0, - "reqd": 1 - }, - { - "description": "DocType on which this Workflow is applicable.", - "doctype": "DocField", - "fieldname": "document_type", - "fieldtype": "Link", - "label": "Document Type", - "options": "DocType", - "reqd": 1 - }, - { - "description": "If checked, all other workflows become inactive.", - "doctype": "DocField", - "fieldname": "is_active", - "fieldtype": "Check", - "label": "Is Active" - }, - { - "description": "Different \"States\" this document can exist in. Like \"Open\", \"Pending Approval\" etc.", - "doctype": "DocField", - "fieldname": "states", - "fieldtype": "Section Break", - "label": "States" - }, - { - "description": "All possible Workflow States and roles of the workflow.
    Docstatus Options: 0 is\"Saved\", 1 is \"Submitted\" and 2 is \"Cancelled\"", - "doctype": "DocField", - "fieldname": "workflow_document_states", - "fieldtype": "Table", - "label": "Workflow Document States", - "options": "Workflow Document State", - "reqd": 1 - }, - { - "description": "Rules for how states are transitions, like next state and which role is allowed to change state etc.", - "doctype": "DocField", - "fieldname": "transition_rules", - "fieldtype": "Section Break", - "label": "Transition Rules" - }, - { - "description": "Rules defining transition of state in the workflow.", - "doctype": "DocField", - "fieldname": "workflow_transitions", - "fieldtype": "Table", - "label": "Workflow Transitions", - "options": "Workflow Transition", - "reqd": 1 - }, - { - "default": "workflow_state", - "description": "Field that represents the Workflow State of the transaction (if field is not present, a new hidden Custom Field will be created)", - "doctype": "DocField", - "fieldname": "workflow_state_field", - "fieldtype": "Data", - "label": "Workflow State Field" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/workflow_action/workflow_action.json b/frappe/core/doctype/workflow_action/workflow_action.json new file mode 100644 index 0000000000..958555f541 --- /dev/null +++ b/frappe/core/doctype/workflow_action/workflow_action.json @@ -0,0 +1,36 @@ +{ + "autoname": "field:workflow_action_name", + "creation": "2012-12-28 10:49:56.000000", + "description": "Workflow Action Master", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "workflow_action_name", + "fieldtype": "Data", + "label": "Workflow Action Name", + "permlevel": 0, + "reqd": 1 + } + ], + "icon": "icon-flag", + "idx": 1, + "modified": "2014-01-20 17:49:35.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Workflow Action", + "owner": "Administrator", + "permissions": [ + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "System Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/workflow_action/workflow_action.py b/frappe/core/doctype/workflow_action/workflow_action.py index a48f49f2a7..3bf0ee4977 100644 --- a/frappe/core/doctype/workflow_action/workflow_action.py +++ b/frappe/core/doctype/workflow_action/workflow_action.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class WorkflowAction(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/workflow_action/workflow_action.txt b/frappe/core/doctype/workflow_action/workflow_action.txt deleted file mode 100644 index bce86a07e4..0000000000 --- a/frappe/core/doctype/workflow_action/workflow_action.txt +++ /dev/null @@ -1,55 +0,0 @@ -[ - { - "creation": "2012-12-28 10:49:56", - "docstatus": 0, - "modified": "2014-01-20 17:49:35", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "field:workflow_action_name", - "description": "Workflow Action Master", - "doctype": "DocType", - "icon": "icon-flag", - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "fieldname": "workflow_action_name", - "fieldtype": "Data", - "label": "Workflow Action Name", - "name": "__common__", - "parent": "Workflow Action", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "reqd": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Workflow Action", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "role": "System Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Workflow Action" - }, - { - "doctype": "DocField" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/workflow_document_state/workflow_document_state.json b/frappe/core/doctype/workflow_document_state/workflow_document_state.json new file mode 100644 index 0000000000..67fb37028a --- /dev/null +++ b/frappe/core/doctype/workflow_document_state/workflow_document_state.json @@ -0,0 +1,73 @@ +{ + "allow_import": 1, + "creation": "2013-02-22 01:27:36.000000", + "description": "Represents the states allowed in one document and role assigned to change the state.", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Master", + "fields": [ + { + "fieldname": "state", + "fieldtype": "Link", + "in_list_view": 1, + "label": "State", + "options": "Workflow State", + "permlevel": 0, + "print_width": "160px", + "reqd": 1, + "width": "160px" + }, + { + "fieldname": "doc_status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Doc Status", + "options": "0\n1\n2", + "permlevel": 0, + "print_width": "80px", + "width": "80px" + }, + { + "fieldname": "update_field", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Update Field", + "permlevel": 0 + }, + { + "fieldname": "update_value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Update Value", + "permlevel": 0 + }, + { + "fieldname": "allow_edit", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Only Allow Edit For", + "options": "Role", + "permlevel": 0, + "print_width": "160px", + "reqd": 1, + "width": "160px" + }, + { + "fieldname": "message", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Message", + "permlevel": 0, + "print_width": "160px", + "reqd": 0, + "width": "160px" + } + ], + "idx": 1, + "istable": 1, + "modified": "2013-12-20 19:21:55.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Workflow Document State", + "owner": "Administrator" +} \ No newline at end of file diff --git a/frappe/core/doctype/workflow_document_state/workflow_document_state.py b/frappe/core/doctype/workflow_document_state/workflow_document_state.py index a48f49f2a7..fc491ac941 100644 --- a/frappe/core/doctype/workflow_document_state/workflow_document_state.py +++ b/frappe/core/doctype/workflow_document_state/workflow_document_state.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class WorkflowDocumentState(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/workflow_document_state/workflow_document_state.txt b/frappe/core/doctype/workflow_document_state/workflow_document_state.txt deleted file mode 100644 index 859d6e3020..0000000000 --- a/frappe/core/doctype/workflow_document_state/workflow_document_state.txt +++ /dev/null @@ -1,81 +0,0 @@ -[ - { - "creation": "2013-02-22 01:27:36", - "docstatus": 0, - "modified": "2013-12-20 19:21:55", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_import": 1, - "description": "Represents the states allowed in one document and role assigned to change the state.", - "doctype": "DocType", - "document_type": "Master", - "istable": 1, - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "in_list_view": 1, - "name": "__common__", - "parent": "Workflow Document State", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocType", - "name": "Workflow Document State" - }, - { - "doctype": "DocField", - "fieldname": "state", - "fieldtype": "Link", - "label": "State", - "options": "Workflow State", - "print_width": "160px", - "reqd": 1, - "width": "160px" - }, - { - "doctype": "DocField", - "fieldname": "doc_status", - "fieldtype": "Select", - "label": "Doc Status", - "options": "0\n1\n2", - "print_width": "80px", - "width": "80px" - }, - { - "doctype": "DocField", - "fieldname": "update_field", - "fieldtype": "Select", - "label": "Update Field" - }, - { - "doctype": "DocField", - "fieldname": "update_value", - "fieldtype": "Data", - "label": "Update Value" - }, - { - "doctype": "DocField", - "fieldname": "allow_edit", - "fieldtype": "Link", - "label": "Only Allow Edit For", - "options": "Role", - "print_width": "160px", - "reqd": 1, - "width": "160px" - }, - { - "doctype": "DocField", - "fieldname": "message", - "fieldtype": "Text", - "label": "Message", - "print_width": "160px", - "reqd": 0, - "width": "160px" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/workflow_state/test_records.json b/frappe/core/doctype/workflow_state/test_records.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/frappe/core/doctype/workflow_state/test_records.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/frappe/core/doctype/workflow_state/test_workflow_state.py b/frappe/core/doctype/workflow_state/test_workflow_state.py index a6b6086caf..2bbe87dfed 100644 --- a/frappe/core/doctype/workflow_state/test_workflow_state.py +++ b/frappe/core/doctype/workflow_state/test_workflow_state.py @@ -1,4 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -test_records = [] \ No newline at end of file +import frappe + +test_records = frappe.get_test_records('Workflow State') \ No newline at end of file diff --git a/frappe/core/doctype/workflow_state/workflow_state.json b/frappe/core/doctype/workflow_state/workflow_state.json new file mode 100644 index 0000000000..f34a63269e --- /dev/null +++ b/frappe/core/doctype/workflow_state/workflow_state.json @@ -0,0 +1,56 @@ +{ + "allow_import": 1, + "autoname": "field:workflow_state_name", + "creation": "2012-12-28 10:49:56.000000", + "description": "Workflow state represents the current state of a document.", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Master", + "fields": [ + { + "fieldname": "workflow_state_name", + "fieldtype": "Data", + "label": "State", + "permlevel": 0, + "reqd": 1 + }, + { + "description": "Icon will appear on the button", + "fieldname": "icon", + "fieldtype": "Select", + "label": "Icon", + "options": "\nglass\nmusic\nsearch\nenvelope\nheart\nstar\nstar-empty\nuser\nfilm\nth-large\nth\nth-list\nok\nremove\nzoom-in\nzoom-out\noff\nsignal\ncog\ntrash\nhome\nfile\ntime\nroad\ndownload-alt\ndownload\nupload\ninbox\nplay-circle\nrepeat\nrefresh\nlist-alt\nlock\nflag\nheadphones\nvolume-off\nvolume-down\nvolume-up\nqrcode\nbarcode\ntag\ntags\nbook\nbookmark\nprint\ncamera\nfont\nbold\nitalic\ntext-height\ntext-width\nalign-left\nalign-center\nalign-right\nalign-justify\nlist\nindent-left\nindent-right\nfacetime-video\npicture\npencil\nmap-marker\nadjust\ntint\nedit\nshare\ncheck\nmove\nstep-backward\nfast-backward\nbackward\nplay\npause\nstop\nforward\nfast-forward\nstep-forward\neject\nchevron-left\nchevron-right\nplus-sign\nminus-sign\nremove-sign\nok-sign\nquestion-sign\ninfo-sign\nscreenshot\nremove-circle\nok-circle\nban-circle\narrow-left\narrow-right\narrow-up\narrow-down\nshare-alt\nresize-full\nresize-small\nplus\nminus\nasterisk\nexclamation-sign\ngift\nleaf\nfire\neye-open\neye-close\nwarning-sign\nplane\ncalendar\nrandom\ncomment\nmagnet\nchevron-up\nchevron-down\nretweet\nshopping-cart\nfolder-close\nfolder-open\nresize-vertical\nresize-horizontal\nhdd\nbullhorn\nbell\ncertificate\nthumbs-up\nthumbs-down\nhand-right\nhand-left\nhand-up\nhand-down\ncircle-arrow-right\ncircle-arrow-left\ncircle-arrow-up\ncircle-arrow-down\nglobe\nwrench\ntasks\nfilter\nbriefcase\nfullscreen", + "permlevel": 0, + "reqd": 0 + }, + { + "description": "Style represents the button color: Success - Green, Danger - Red, Inverse - Black, Primary - Dark Blue, Info - Light Blue, Warning - Orange", + "fieldname": "style", + "fieldtype": "Select", + "label": "Style", + "options": "\nPrimary\nInfo\nSuccess\nWarning\nDanger\nInverse", + "permlevel": 0, + "reqd": 0 + } + ], + "icon": "icon-flag", + "idx": 1, + "modified": "2014-01-20 17:49:35.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Workflow State", + "owner": "Administrator", + "permissions": [ + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "System Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/core/doctype/workflow_state/workflow_state.py b/frappe/core/doctype/workflow_state/workflow_state.py index a48f49f2a7..83eb8a4dc9 100644 --- a/frappe/core/doctype/workflow_state/workflow_state.py +++ b/frappe/core/doctype/workflow_state/workflow_state.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class WorkflowState(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/workflow_state/workflow_state.txt b/frappe/core/doctype/workflow_state/workflow_state.txt deleted file mode 100644 index b4a502394f..0000000000 --- a/frappe/core/doctype/workflow_state/workflow_state.txt +++ /dev/null @@ -1,75 +0,0 @@ -[ - { - "creation": "2012-12-28 10:49:56", - "docstatus": 0, - "modified": "2014-01-20 17:49:35", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_import": 1, - "autoname": "field:workflow_state_name", - "description": "Workflow state represents the current state of a document.", - "doctype": "DocType", - "document_type": "Master", - "icon": "icon-flag", - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Workflow State", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Workflow State", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "role": "System Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Workflow State" - }, - { - "doctype": "DocField", - "fieldname": "workflow_state_name", - "fieldtype": "Data", - "label": "State", - "reqd": 1 - }, - { - "description": "Icon will appear on the button", - "doctype": "DocField", - "fieldname": "icon", - "fieldtype": "Select", - "label": "Icon", - "options": "\nglass\nmusic\nsearch\nenvelope\nheart\nstar\nstar-empty\nuser\nfilm\nth-large\nth\nth-list\nok\nremove\nzoom-in\nzoom-out\noff\nsignal\ncog\ntrash\nhome\nfile\ntime\nroad\ndownload-alt\ndownload\nupload\ninbox\nplay-circle\nrepeat\nrefresh\nlist-alt\nlock\nflag\nheadphones\nvolume-off\nvolume-down\nvolume-up\nqrcode\nbarcode\ntag\ntags\nbook\nbookmark\nprint\ncamera\nfont\nbold\nitalic\ntext-height\ntext-width\nalign-left\nalign-center\nalign-right\nalign-justify\nlist\nindent-left\nindent-right\nfacetime-video\npicture\npencil\nmap-marker\nadjust\ntint\nedit\nshare\ncheck\nmove\nstep-backward\nfast-backward\nbackward\nplay\npause\nstop\nforward\nfast-forward\nstep-forward\neject\nchevron-left\nchevron-right\nplus-sign\nminus-sign\nremove-sign\nok-sign\nquestion-sign\ninfo-sign\nscreenshot\nremove-circle\nok-circle\nban-circle\narrow-left\narrow-right\narrow-up\narrow-down\nshare-alt\nresize-full\nresize-small\nplus\nminus\nasterisk\nexclamation-sign\ngift\nleaf\nfire\neye-open\neye-close\nwarning-sign\nplane\ncalendar\nrandom\ncomment\nmagnet\nchevron-up\nchevron-down\nretweet\nshopping-cart\nfolder-close\nfolder-open\nresize-vertical\nresize-horizontal\nhdd\nbullhorn\nbell\ncertificate\nthumbs-up\nthumbs-down\nhand-right\nhand-left\nhand-up\nhand-down\ncircle-arrow-right\ncircle-arrow-left\ncircle-arrow-up\ncircle-arrow-down\nglobe\nwrench\ntasks\nfilter\nbriefcase\nfullscreen", - "reqd": 0 - }, - { - "description": "Style represents the button color: Success - Green, Danger - Red, Inverse - Black, Primary - Dark Blue, Info - Light Blue, Warning - Orange", - "doctype": "DocField", - "fieldname": "style", - "fieldtype": "Select", - "label": "Style", - "options": "\nPrimary\nInfo\nSuccess\nWarning\nDanger\nInverse", - "reqd": 0 - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/core/doctype/workflow_transition/workflow_transition.json b/frappe/core/doctype/workflow_transition/workflow_transition.json new file mode 100644 index 0000000000..9b62473bb5 --- /dev/null +++ b/frappe/core/doctype/workflow_transition/workflow_transition.json @@ -0,0 +1,59 @@ +{ + "creation": "2013-02-22 01:27:36.000000", + "description": "Defines actions on states and the next step and allowed roles.", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "state", + "fieldtype": "Link", + "in_list_view": 1, + "label": "State", + "options": "Workflow State", + "permlevel": 0, + "print_width": "200px", + "reqd": 1, + "width": "200px" + }, + { + "fieldname": "action", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Action", + "options": "Workflow Action", + "permlevel": 0, + "print_width": "200px", + "reqd": 1, + "width": "200px" + }, + { + "fieldname": "next_state", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Next State", + "options": "Workflow State", + "permlevel": 0, + "print_width": "200px", + "reqd": 1, + "width": "200px" + }, + { + "fieldname": "allowed", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Allowed", + "options": "Role", + "permlevel": 0, + "print_width": "200px", + "reqd": 1, + "width": "200px" + } + ], + "idx": 1, + "istable": 1, + "modified": "2013-12-20 19:21:55.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "Workflow Transition", + "owner": "Administrator" +} \ No newline at end of file diff --git a/frappe/core/doctype/workflow_transition/workflow_transition.py b/frappe/core/doctype/workflow_transition/workflow_transition.py index a48f49f2a7..69a04db9ee 100644 --- a/frappe/core/doctype/workflow_transition/workflow_transition.py +++ b/frappe/core/doctype/workflow_transition/workflow_transition.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class WorkflowTransition(Document): + pass \ No newline at end of file diff --git a/frappe/core/doctype/workflow_transition/workflow_transition.txt b/frappe/core/doctype/workflow_transition/workflow_transition.txt deleted file mode 100644 index 519c2e2865..0000000000 --- a/frappe/core/doctype/workflow_transition/workflow_transition.txt +++ /dev/null @@ -1,57 +0,0 @@ -[ - { - "creation": "2013-02-22 01:27:36", - "docstatus": 0, - "modified": "2013-12-20 19:21:55", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "description": "Defines actions on states and the next step and allowed roles.", - "doctype": "DocType", - "istable": 1, - "module": "Core", - "name": "__common__" - }, - { - "doctype": "DocField", - "fieldtype": "Link", - "in_list_view": 1, - "name": "__common__", - "parent": "Workflow Transition", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "print_width": "200px", - "reqd": 1, - "width": "200px" - }, - { - "doctype": "DocType", - "name": "Workflow Transition" - }, - { - "doctype": "DocField", - "fieldname": "state", - "label": "State", - "options": "Workflow State" - }, - { - "doctype": "DocField", - "fieldname": "action", - "label": "Action", - "options": "Workflow Action" - }, - { - "doctype": "DocField", - "fieldname": "next_state", - "label": "Next State", - "options": "Workflow State" - }, - { - "doctype": "DocField", - "fieldname": "allowed", - "label": "Allowed", - "options": "Role" - } -] \ No newline at end of file diff --git a/frappe/core/page/applications/applications.json b/frappe/core/page/applications/applications.json new file mode 100644 index 0000000000..3aed1361c3 --- /dev/null +++ b/frappe/core/page/applications/applications.json @@ -0,0 +1,20 @@ +{ + "creation": "2013-12-23 11:01:52.000000", + "docstatus": 0, + "doctype": "Page", + "icon": "icon-magic", + "idx": 1, + "modified": "2013-12-23 11:01:52.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "applications", + "owner": "Administrator", + "page_name": "applications", + "roles": [ + { + "role": "System Manager" + } + ], + "standard": "Yes", + "title": "Applications" +} \ No newline at end of file diff --git a/frappe/core/page/applications/applications.txt b/frappe/core/page/applications/applications.txt deleted file mode 100644 index 110b471af7..0000000000 --- a/frappe/core/page/applications/applications.txt +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "creation": "2013-12-23 11:01:52", - "docstatus": 0, - "modified": "2013-12-23 11:01:52", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "Page", - "icon": "icon-magic", - "module": "Core", - "name": "__common__", - "page_name": "applications", - "standard": "Yes", - "title": "Applications" - }, - { - "doctype": "Page Role", - "name": "__common__", - "parent": "applications", - "parentfield": "roles", - "parenttype": "Page", - "role": "System Manager" - }, - { - "doctype": "Page", - "name": "applications" - }, - { - "doctype": "Page Role" - } -] \ No newline at end of file diff --git a/frappe/core/page/data_import_tool/data_import_tool.js b/frappe/core/page/data_import_tool/data_import_tool.js index 3be83eebad..2a823d02f4 100644 --- a/frappe/core/page/data_import_tool/data_import_tool.js +++ b/frappe/core/page/data_import_tool/data_import_tool.js @@ -1,27 +1,33 @@ -frappe.pages['data-import-tool'].onload = function(wrapper) { +frappe.pages['data-import-tool'].onload = function(wrapper) { wrapper.app_page = frappe.ui.make_app_page({ parent: wrapper, - title: "Data Import Tool", + title: __("Data Import / Export Tool"), icon: "data-import-tool" }); - + // check permission for import if(!((frappe.boot.user.can_import && frappe.boot.user.can_import.length) || user_roles.indexOf("System Manager")!==-1)) { frappe.show_not_permitted("data-import-tool"); return false; } - + $(wrapper).find('.layout-main-section').append('

    1. Download Template

    \
    \

    Download a template for importing a table.

    \ -

    \ -

    \ - \ - Download with data\ -

    \ -

    \ +
    \ +
    \ +

    \ + \ +

    Export all rows in CSV fields for re-upload. This is ideal for bulk-editing.

    \ +
    \ +
    \ +
    \ +
    \ +
    \
    \
    \

    2. Import Data

    \ @@ -29,29 +35,27 @@ frappe.pages['data-import-tool'].onload = function(wrapper) {

    \ \

    \ - '); - - $(wrapper).find('.layout-side-section').append('

    Help

    \ -

    Importing non-English data:

    \ -

    While uploading non English files ensure that the encoding is UTF-8.

    \ -

    Microsoft Excel Users:\ -

      \ -
    1. In Excel, save the file in CSV (Comma Delimited) format
    2. \ -
    3. Open this saved file in Notepad
    4. \ -
    5. Click on File -> Save As
    6. \ -
    7. File Name: <your filename>.csv
      \ - Save as type: Text Documents (*.txt)
      \ - Encoding: UTF-8\ -
    8. \ -
    9. Click on Save
    10. \ -
    \ -

    ') - +
    \ +

    Help: Importing non-English data in Microsoft Excel

    \ +

    While uploading non English files ensure that the encoding is UTF-8.

    \ +
      \ +
    1. In Excel, save the file in CSV (Comma Delimited) format
    2. \ +
    3. Open this saved file in Notepad
    4. \ +
    5. Click on File -> Save As
    6. \ +
    7. File Name: <your filename>.csv
      \ + Save as type: Text Documents (*.txt)
      \ + Encoding: UTF-8\ +
    8. \ +
    9. Click on Save
    10. \ +
    \ +

    \ +
    '); + $select = $(wrapper).find('[name="dit-doctype"]'); - - frappe.messages.waiting($(wrapper).find(".dit-progress-area").toggle(false), + + frappe.messages.waiting($(wrapper).find(".dit-progress-area").toggle(false), "Performing hardcore import process....", 100); - + // load doctypes frappe.call({ method: 'frappe.core.page.data_import_tool.data_import_tool.get_doctypes', @@ -61,7 +65,7 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { wrapper.set_route_options(); } }); - + wrapper.set_route_options = function() { if(frappe.route_options && frappe.route_options.doctype @@ -70,7 +74,7 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { frappe.route_options = null; } } - + // check if template with_data is allowed var validate_download_with_data = function(doctype, verbose) { // if no export permission, uncheck with data @@ -78,7 +82,7 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { if(with_data && !frappe.model.can_export(doctype)) { $('[name="dit-with-data"]').prop("checked", false); with_data = false; - + if(verbose) { msgprint(frappe._("You are not allowed to export the data of") + ": " + frappe._(doctype) + ". " + frappe._("Downloading empty template") + "."); @@ -86,7 +90,7 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { } return with_data; }; - + wrapper.add_template_download_link = function(doctype) { return $('') .html(doctype) @@ -96,13 +100,13 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { var doctype = $(this).data('doctype'); var parent_doctype = $('[name="dit-doctype"]').val(); var with_data = validate_download_with_data(parent_doctype || doctype, true); - window.location.href = repl(frappe.request.url + window.location.href = repl(frappe.request.url + '?cmd=%(cmd)s&doctype=%(doctype)s' + '&parent_doctype=%(parent_doctype)s' + '&with_data=%(with_data)s' + '&all_doctypes=%(all_doctypes)s', - { - cmd: 'frappe.core.page.data_import_tool.data_import_tool.get_template', + { + cmd: 'frappe.core.page.data_import_tool.exporter.get_template', doctype: doctype, parent_doctype: parent_doctype, with_data: with_data ? 'Yes' : 'No', @@ -111,22 +115,22 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { }) .appendTo('#dit-download'); } - + // load options $select.change(function() { var val = $(this).val() if(val!='Select...') { - $('#dit-download').empty(); - + $('#dit-download').empty().removeClass("hide"); + frappe.model.with_doctype(val, function() { validate_download_with_data(val); - + // get options return frappe.call({ method: 'frappe.core.page.data_import_tool.data_import_tool.get_doctype_options', args: {doctype: val}, callback: function(r) { - $('

    Select Template:

    ').appendTo('#dit-download'); + $('

    Download

    ').appendTo('#dit-download'); var with_data = $('[name="dit-with-data"]:checked').length ? 'Yes' : 'No'; // download link $.each(r.message, function(i, v) { @@ -134,11 +138,11 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { $('Main Table:
    ').appendTo('#dit-download'); if(i==1) $('
    Child Tables:
    ').appendTo('#dit-download'); - + wrapper.add_template_download_link(v); $('#dit-download').append('
    '); }); - + if(r.message.length > 1) { $('
    All Tables (Main + Child Tables):
    ').appendTo('#dit-download'); var link = wrapper @@ -150,7 +154,7 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { }); } }); - + var write_messages = function(r) { $(wrapper).find(".dit-progress-area").toggle(false); $("#dit-output").empty(); @@ -168,7 +172,7 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { } }); } - + var onerror = function(r) { r.messages = $.map(r.message.messages, function(v) { var msg = v.replace("Inserted", "Valid") @@ -180,18 +184,18 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { } return v; }); - + r.messages = ["

    Import Failed!

    "] .concat(r.messages); - + write_messages(r); }; - + // upload frappe.upload.make({ parent: $('#dit-upload-area'), args: { - method: 'frappe.core.page.data_import_tool.data_import_tool.upload' + method: 'frappe.core.page.data_import_tool.importer.upload' }, onerror: onerror, callback: function(fid, filename, r) { @@ -201,33 +205,30 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { // replace links if error has occured r.messages = ["

    Import Successful!

    "]. concat(r.message.messages) - + write_messages(r); } } }); - + // add overwrite option var $submit_btn = $('#dit-upload-area button.btn-upload') .html(' ' + frappe._("Upload and Import")); - - $('\ - Overwrite\ -

    If you are uploading a child table (for example Item Price), the all the entries of that table will be deleted (for that parent record) and new entries will be made.


    ') + + $('\ +

    If you are uploading a child table (for example Item Price), the all the entries of that table will be deleted (for that parent record) and new entries will be made.


    ') .insertBefore($submit_btn); - + // add submit option - $('\ - Submit\ -

    If you are inserting new records (overwrite not checked) \ + $('\ +

    If you are inserting new records (overwrite not checked) \ and if you have submit permission, the record will be submitted.


    ') .insertBefore($submit_btn); // add ignore option - $('\ - Ignore Encoding Errors

    ') + $('

    ') .insertBefore($submit_btn); - + // rename button $('#dit-upload-area button.btn-upload') .click(function() { @@ -236,6 +237,6 @@ frappe.pages['data-import-tool'].onload = function(wrapper) { }); } -frappe.pages['data-import-tool'].onshow = function(wrapper) { +frappe.pages['data-import-tool'].onshow = function(wrapper) { wrapper.set_route_options && wrapper.set_route_options(); -} \ No newline at end of file +} diff --git a/frappe/core/page/data_import_tool/data_import_tool.json b/frappe/core/page/data_import_tool/data_import_tool.json new file mode 100644 index 0000000000..37525e54e4 --- /dev/null +++ b/frappe/core/page/data_import_tool/data_import_tool.json @@ -0,0 +1,20 @@ +{ + "creation": "2012-06-14 15:07:25.000000", + "docstatus": 0, + "doctype": "Page", + "icon": "icon-upload", + "idx": 1, + "modified": "2014-02-13 16:09:27.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "data-import-tool", + "owner": "Administrator", + "page_name": "data-import-tool", + "roles": [ + { + "role": "System Manager" + } + ], + "standard": "Yes", + "title": "Data Import Tool" +} \ No newline at end of file diff --git a/frappe/core/page/data_import_tool/data_import_tool.py b/frappe/core/page/data_import_tool/data_import_tool.py index f6f00fb1d1..53109dfc4f 100644 --- a/frappe/core/page/data_import_tool/data_import_tool.py +++ b/frappe/core/page/data_import_tool/data_import_tool.py @@ -4,13 +4,9 @@ from __future__ import unicode_literals import frappe, json, os -import frappe.permissions -import frappe.model.doctype -from frappe.model.meta import get_table_fields -from frappe.utils import cstr -from frappe.utils.datautils import UnicodeWriter, check_record, import_doc, getlink, cint, flt -from frappe import _ -import frappe.permissions +import frappe.modules.import_file +from frappe.utils import cstr, cint, flt +from frappe.utils.datautils import check_record, import_doc data_keys = frappe._dict({ "data_separator": 'Start entering data below this line', @@ -23,477 +19,27 @@ data_keys = frappe._dict({ @frappe.whitelist() def get_doctypes(): if "System Manager" in frappe.get_roles(): - return [r[0] for r in frappe.db.sql("""select name from `tabDocType` + return [r[0] for r in frappe.db.sql("""select name from `tabDocType` where allow_import = 1""")] else: return frappe.user._get("can_import") - + @frappe.whitelist() def get_doctype_options(): doctype = frappe.form_dict['doctype'] - return [doctype] + filter(None, map(lambda d: \ - d.doctype=='DocField' and d.fieldtype=='Table' and d.options or None, - frappe.model.doctype.get(doctype))) + return [doctype] + [d.options for d in frappe.get_meta(doctype).get_table_fields()] -@frappe.whitelist() -def get_template(doctype=None, parent_doctype=None, all_doctypes="No", with_data="No"): - import frappe.permissions - all_doctypes = all_doctypes=="Yes" - if not parent_doctype: - parent_doctype = doctype - - column_start_end = {} - - if all_doctypes: - doctype_parentfield = {} - child_doctypes = [] - for d in get_table_fields(doctype): - child_doctypes.append(d[0]) - doctype_parentfield[d[0]] = d[1] - - def add_main_header(): - w.writerow(['Data Import Template']) - w.writerow([data_keys.main_table, doctype]) - - if parent_doctype != doctype: - w.writerow([data_keys.parent_table, parent_doctype]) - else: - w.writerow(['']) - w.writerow(['']) - w.writerow(['Notes:']) - w.writerow(['Please do not change the template headings.']) - w.writerow(['First data column must be blank.']) - w.writerow(['If you are uploading new records, leave the "name" (ID) column blank.']) - w.writerow(['If you are uploading new records, "Naming Series" becomes mandatory, if present.']) - w.writerow(['Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.']) - w.writerow(['For updating, you can update only selective columns.']) - w.writerow(['You can only upload upto 5000 records in one go. (may be less in some cases)']) - if key == "parent": - w.writerow(['"Parent" signifies the parent table in which this row must be added']) - w.writerow(['If you are updating, please select "Overwrite" else existing rows will not be deleted.']) - - def build_field_columns(dt): - doctype_dl = frappe.model.doctype.get(dt) - - tablecolumns = filter(None, - [doctype_dl.get_field(f[0]) for f in frappe.db.sql('desc `tab%s`' % dt)]) - - tablecolumns.sort(lambda a, b: a.idx - b.idx) - - if dt==doctype: - column_start_end[dt] = frappe._dict({"start": 0}) - else: - column_start_end[dt] = frappe._dict({"start": len(columns)}) - - append_field_column(frappe._dict({ - "fieldname": "name", - "label": "ID", - "fieldtype": "Data", - "reqd": 1, - "idx": 0, - "info": "Leave blank for new records" - }), True) - - for docfield in tablecolumns: - append_field_column(docfield, True) - - # all non mandatory fields - for docfield in tablecolumns: - append_field_column(docfield, False) - - # append DocType name - tablerow[column_start_end[dt].start + 1] = dt - if dt!=doctype: - tablerow[column_start_end[dt].start + 2] = doctype_parentfield[dt] - - column_start_end[dt].end = len(columns) + 1 - - def append_field_column(docfield, mandatory): - if docfield and ((mandatory and docfield.reqd) or not (mandatory or docfield.reqd)) \ - and (docfield.fieldname not in ('parenttype', 'trash_reason')) and not docfield.hidden: - tablerow.append("") - fieldrow.append(docfield.fieldname) - labelrow.append(docfield.label) - mandatoryrow.append(docfield.reqd and 'Yes' or 'No') - typerow.append(docfield.fieldtype) - inforow.append(getinforow(docfield)) - columns.append(docfield.fieldname) - - def append_empty_field_column(): - tablerow.append("~") - fieldrow.append("~") - labelrow.append("") - mandatoryrow.append("") - typerow.append("") - inforow.append("") - columns.append("") - - def getinforow(docfield): - """make info comment for options, links etc.""" - 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': - return 'Valid %s' % docfield.options - elif docfield.fieldtype in ('Int'): - return 'Integer' - elif docfield.fieldtype == "Check": - return "0 or 1" - elif docfield.info: - return docfield.info - else: - return '' - - def add_field_headings(): - w.writerow(tablerow) - w.writerow(labelrow) - w.writerow(fieldrow) - w.writerow(mandatoryrow) - w.writerow(typerow) - w.writerow(inforow) - w.writerow([data_keys.data_separator]) - - def add_data(): - def add_data_row(row_group, dt, doc, rowidx): - d = doc.copy() - if all_doctypes: - d.name = '"'+ d.name+'"' - - if len(row_group) < rowidx + 1: - row_group.append([""] * (len(columns) + 1)) - row = row_group[rowidx] - for i, c in enumerate(columns[column_start_end[dt].start:column_start_end[dt].end]): - row[column_start_end[dt].start + i + 1] = d.get(c, "") - - if with_data=='Yes': - frappe.permissions.can_export(parent_doctype, raise_exception=True) - - # get permitted data only - data = frappe.get_list(doctype, fields=["*"], limit_page_length=None) - for doc in data: - # add main table - row_group = [] - - add_data_row(row_group, doctype, doc, 0) - - if all_doctypes: - # add child tables - for child_doctype in child_doctypes: - for ci, child in enumerate(frappe.db.sql("""select * from `tab%s` - where parent=%s order by idx""" % (child_doctype, "%s"), doc.name, as_dict=1)): - add_data_row(row_group, child_doctype, child, ci) - - for row in row_group: - w.writerow(row) - - w = UnicodeWriter() - key = 'parent' if parent_doctype != doctype else 'name' - - add_main_header() - - w.writerow(['']) - tablerow = [data_keys.doctype, ""] - labelrow = ["Column Labels:", "ID"] - fieldrow = [data_keys.columns, key] - mandatoryrow = ['Mandatory:', 'Yes'] - typerow = ['Type:', 'Data (text)'] - inforow = ['Info:', ''] - columns = [key] - - build_field_columns(doctype) - if all_doctypes: - for d in child_doctypes: - append_empty_field_column() - build_field_columns(d) - - add_field_headings() - add_data() - - # write out response as a type csv - frappe.response['result'] = cstr(w.getvalue()) - frappe.response['type'] = 'csv' - frappe.response['doctype'] = doctype - -@frappe.whitelist() -def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, overwrite=False, ignore_links=False): - """upload data""" - frappe.flags.mute_emails = True - # extra input params - params = json.loads(frappe.form_dict.get("params") or '{}') - - if params.get("_submit"): - submit_after_import = True - if params.get("ignore_encoding_errors"): - ignore_encoding_errors = True - - from frappe.utils.datautils import read_csv_content_from_uploaded_file - - def bad_template(): - frappe.msgprint("Please do not change the rows above '%s'" % data_keys.data_separator, - raise_exception=1) - - def check_data_length(): - max_rows = 5000 - if not data: - frappe.msgprint("No data found", raise_exception=True) - elif len(data) > max_rows: - frappe.msgprint("Please upload only upto %d %ss at a time" % \ - (max_rows, doctype), raise_exception=True) - - def get_start_row(): - for i, row in enumerate(rows): - if row and row[0]==data_keys.data_separator: - return i+1 - bad_template() - - def get_header_row(key): - return get_header_row_and_idx(key)[0] - - def get_header_row_and_idx(key): - for i, row in enumerate(header): - if row and row[0]==key: - return row, i - return [], -1 - - def filter_empty_columns(columns): - empty_cols = filter(lambda x: x in ("", None), columns) - - if empty_cols: - if columns[-1*len(empty_cols):] == empty_cols: - # filter empty columns if they exist at the end - columns = columns[:-1*len(empty_cols)] - else: - frappe.msgprint(_("Please make sure that there are no empty columns in the file."), - raise_exception=1) - - return columns - - def make_column_map(): - doctype_row, row_idx = get_header_row_and_idx(data_keys.doctype) - if row_idx == -1: # old style - return - - dt = None - for i, d in enumerate(doctype_row[1:]): - if d not in ("~", "-"): - if d: # value in doctype_row - if doctype_row[i]==dt: - # prev column is doctype (in case of parentfield) - doctype_parentfield[dt] = doctype_row[i+1] - else: - dt = d - doctypes.append(d) - column_idx_to_fieldname[dt] = {} - column_idx_to_fieldtype[dt] = {} - if dt: - column_idx_to_fieldname[dt][i+1] = rows[row_idx + 2][i+1] - column_idx_to_fieldtype[dt][i+1] = rows[row_idx + 4][i+1] - - def get_doclist(start_idx): - if doctypes: - doclist = [] - for idx in xrange(start_idx, len(rows)): - if (not len(doclist)) or main_doc_empty(rows[idx]): - for dt in doctypes: - d = {} - for column_idx in column_idx_to_fieldname[dt]: - try: - fieldname = column_idx_to_fieldname[dt][column_idx] - fieldtype = column_idx_to_fieldtype[dt][column_idx] - - d[fieldname] = rows[idx][column_idx] - if fieldtype in ("Int", "Check"): - d[fieldname] = cint(d[fieldname]) - elif fieldtype in ("Float", "Currency"): - d[fieldname] = flt(d[fieldname]) - except IndexError, e: - pass - - # scrub quotes from name and modified - if d.get("name") and d["name"].startswith('"'): - d["name"] = d["name"][1:-1] - - if sum([0 if not val else 1 for val in d.values()]): - d['doctype'] = dt - if dt != doctype: - if not overwrite: - d['parent'] = doclist[0]["name"] - d['parenttype'] = doctype - d['parentfield'] = doctype_parentfield[dt] - doclist.append(d) - else: - break - - return doclist - else: - d = frappe._dict(zip(columns, rows[start_idx][1:])) - d['doctype'] = doctype - return [d] - - def main_doc_empty(row): - return not (row and ((len(row) > 1 and row[1]) or (len(row) > 2 and row[2]))) - - # header - if not rows: - rows = read_csv_content_from_uploaded_file(ignore_encoding_errors) - start_row = get_start_row() - header = rows[:start_row] - data = rows[start_row:] - doctype = get_header_row(data_keys.main_table)[1] - columns = filter_empty_columns(get_header_row(data_keys.columns)[1:]) - doctypes = [] - doctype_parentfield = {} - column_idx_to_fieldname = {} - column_idx_to_fieldtype = {} - - if submit_after_import and not cint(frappe.db.get_value("DocType", - doctype, "is_submittable")): - submit_after_import = False - - parenttype = get_header_row(data_keys.parent_table) - - if len(parenttype) > 1: - parenttype = parenttype[1] - parentfield = get_parent_field(doctype, parenttype) - - # check permissions - if not frappe.permissions.can_import(parenttype or doctype): - frappe.flags.mute_emails = False - return {"messages": [_("Not allowed to Import") + ": " + _(doctype)], "error": True} - - # allow limit rows to be uploaded - check_data_length() - make_column_map() - - frappe.db.begin() - if not overwrite: - overwrite = params.get('overwrite') - doctype_dl = frappe.model.doctype.get(doctype) - - # delete child rows (if parenttype) - if parenttype and overwrite: - delete_child_rows(data, doctype) - - ret = [] - error = False - parent_list = [] - for i, row in enumerate(data): - # bypass empty rows - if main_doc_empty(row): - continue - - row_idx = i + start_row - bean = None - - doclist = get_doclist(row_idx) - try: - frappe.local.message_log = [] - if len(doclist) > 1: - for d in doclist: - # ignoring parent check as it will be automatically added - check_record(d, None, doctype_dl) - - if overwrite and frappe.db.exists(doctype, doclist[0]["name"]): - bean = frappe.bean(doctype, doclist[0]["name"]) - bean.ignore_links = ignore_links - bean.doclist.update(doclist) - bean.save() - ret.append('Updated row (#%d) %s' % (row_idx + 1, getlink(bean.doc.doctype, bean.doc.name))) - else: - bean = frappe.bean(doclist) - bean.ignore_links = ignore_links - bean.insert() - ret.append('Inserted row (#%d) %s' % (row_idx + 1, getlink(bean.doc.doctype, bean.doc.name))) - if submit_after_import: - bean.submit() - ret.append('Submitted row (#%d) %s' % (row_idx + 1, getlink(bean.doc.doctype, bean.doc.name))) - else: - check_record(doclist[0], parenttype, doctype_dl) - - if parenttype: - # child doc - doc = frappe.doc(doctype) - doc.fields.update(doclist[0]) - if parenttype: - doc.parenttype = parenttype - doc.parentfield = parentfield - doc.save() - ret.append('Inserted row for %s at #%s' % (getlink(parenttype, - doc.parent), unicode(doc.idx))) - parent_list.append(doc.parent) - else: - ret.append(import_doc(doclist[0], doctype, overwrite, row_idx, submit_after_import, ignore_links)) - - except Exception, e: - error = True - if bean: - frappe.errprint(bean.doclist) - err_msg = frappe.local.message_log and "
    ".join(frappe.local.message_log) or cstr(e) - ret.append('Error for row (#%d) %s : %s' % (row_idx + 1, - len(row)>1 and row[1] or "", err_msg)) - frappe.errprint(frappe.get_traceback()) - - ret, error = validate_parent(parent_list, parenttype, ret, error) - - if error: - frappe.db.rollback() - else: - frappe.db.commit() - - frappe.flags.mute_emails = False - - return {"messages": ret, "error": error} - -def validate_parent(parent_list, parenttype, ret, error): - if parent_list: - parent_list = list(set(parent_list)) - for p in parent_list: - try: - obj = frappe.bean(parenttype, p) - obj.run_method("validate") - obj.run_method("on_update") - except Exception, e: - error = True - ret.append('Validation Error for %s %s: %s' % (parenttype, p, cstr(e))) - frappe.errprint(frappe.get_traceback()) - - return ret, error - -def get_parent_field(doctype, parenttype): - parentfield = None - - # get parentfield - if parenttype: - for d in frappe.model.doctype.get(parenttype): - if d.fieldtype=='Table' and d.options==doctype: - parentfield = d.fieldname - break - - if not parentfield: - frappe.msgprint("Did not find parentfield for %s (%s)" % \ - (parenttype, doctype)) - raise Exception - - return parentfield - -def delete_child_rows(rows, doctype): - """delete child rows for all parents""" - for p in list(set([r[1] for r in rows])): - frappe.db.sql("""delete from `tab%s` where parent=%s""" % (doctype, '%s'), p) - import csv -def import_file_by_path(path, ignore_links=False, overwrite=False): +def import_file_by_path(path, ignore_links=False, overwrite=False, submit=False): from frappe.utils.datautils import read_csv_content + from frappe.core.page.data_import_tool.importer import upload print "Importing " + path with open(path, "r") as infile: - upload(rows = read_csv_content(infile), ignore_links=ignore_links, overwrite=overwrite) + upload(rows = read_csv_content(infile.read()), ignore_links=ignore_links, overwrite=overwrite, submit_after_import=submit) -def export_csv(doctype, path): +def export_csv(doctype, path): + from frappe.core.page.data_import_tool.exporter import get_template with open(path, "w") as csvfile: get_template(doctype=doctype, all_doctypes="Yes", with_data="Yes") csvfile.write(frappe.response.result.encode("utf-8")) @@ -503,55 +49,34 @@ def export_json(doctype, name, path): if not name or name=="-": name = doctype with open(path, "w") as outfile: - doclist = [d.fields for d in frappe.bean(doctype, name).doclist] - for d in doclist: - if d.get("parent"): - del d["parent"] - del d["name"] - d["__islocal"] = 1 - outfile.write(json.dumps([doclist], default=json_handler, indent=1, sort_keys=True)) + doc = frappe.get_doc(doctype, name) + for d in doc.get_all_children(): + d.set("parent", None) + d.set("name", None) + d.set("__islocal", 1) + outfile.write(json.dumps([d], default=json_handler, indent=1, sort_keys=True)) @frappe.whitelist() def export_fixture(doctype, name, app): if frappe.session.user != "Administrator": raise frappe.PermissionError - + if not os.path.exists(frappe.get_app_path(app, "fixtures")): os.mkdir(frappe.get_app_path(app, "fixtures")) - - export_json(doctype, name, frappe.get_app_path(app, "fixtures", frappe.scrub(name) + ".json")) - -def import_doclist(path, overwrite=False, ignore_links=False, ignore_insert=False, insert=False): + export_json(doctype, name, frappe.get_app_path(app, "fixtures", frappe.scrub(name) + ".json")) + + +def import_doc(path, overwrite=False, ignore_links=False, ignore_insert=False, insert=False, submit=False): if os.path.isdir(path): files = [os.path.join(path, f) for f in os.listdir(path)] else: files = [path] - - def _import_doclist(d): - b = frappe.bean(d) - b.ignore_links = ignore_links - if insert: - b.doc.fields["__islocal"] = True - try: - b.insert_or_update() - except NameError: - if ignore_insert: - pass - else: - raise - print "Imported: " + b.doc.doctype + " / " + b.doc.name - + + for f in files: if f.endswith(".json"): - with open(f, "r") as infile: - data = json.loads(infile.read()) - if isinstance(data, list): - for doc in data: - _import_doclist(doc) - else: - _import_doclist(data) - frappe.db.commit() - if f.endswith(".csv"): - import_file_by_path(f, ignore_links=True, overwrite=overwrite) + frappe.modules.import_file.import_file_by_path(f) + elif f.endswith(".csv"): + import_file_by_path(f, ignore_links=ignore_links, overwrite=overwrite, submit=submit) frappe.db.commit() diff --git a/frappe/core/page/data_import_tool/data_import_tool.txt b/frappe/core/page/data_import_tool/data_import_tool.txt deleted file mode 100644 index 4c62dbff7d..0000000000 --- a/frappe/core/page/data_import_tool/data_import_tool.txt +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "creation": "2012-06-14 15:07:25", - "docstatus": 0, - "modified": "2014-02-13 16:09:27", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "Page", - "icon": "icon-upload", - "module": "Core", - "name": "__common__", - "page_name": "data-import-tool", - "standard": "Yes", - "title": "Data Import Tool" - }, - { - "doctype": "Page Role", - "name": "__common__", - "parent": "data-import-tool", - "parentfield": "roles", - "parenttype": "Page", - "role": "System Manager" - }, - { - "doctype": "Page", - "name": "data-import-tool" - }, - { - "doctype": "Page Role" - } -] \ No newline at end of file diff --git a/frappe/core/page/data_import_tool/exporter.py b/frappe/core/page/data_import_tool/exporter.py new file mode 100644 index 0000000000..fc32aa05f1 --- /dev/null +++ b/frappe/core/page/data_import_tool/exporter.py @@ -0,0 +1,194 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals + +import frappe, json, os +import frappe.permissions +from frappe.utils.datautils import UnicodeWriter +from frappe.utils import cstr, cint, flt + +from frappe.core.page.data_import_tool.data_import_tool import data_keys + +@frappe.whitelist() +def get_template(doctype=None, parent_doctype=None, all_doctypes="No", with_data="No"): + all_doctypes = all_doctypes=="Yes" + if not parent_doctype: + parent_doctype = doctype + + column_start_end = {} + + if all_doctypes: + doctype_parentfield = {} + child_doctypes = [] + for df in frappe.get_meta(doctype).get_table_fields(): + child_doctypes.append(df.options) + doctype_parentfield[df.options] = df.fieldname + + def add_main_header(): + w.writerow(['Data Import Template']) + w.writerow([data_keys.main_table, doctype]) + + if parent_doctype != doctype: + w.writerow([data_keys.parent_table, parent_doctype]) + else: + w.writerow(['']) + + w.writerow(['']) + w.writerow(['Notes:']) + w.writerow(['Please do not change the template headings.']) + w.writerow(['First data column must be blank.']) + w.writerow(['If you are uploading new records, leave the "name" (ID) column blank.']) + w.writerow(['If you are uploading new records, "Naming Series" becomes mandatory, if present.']) + w.writerow(['Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.']) + w.writerow(['For updating, you can update only selective columns.']) + w.writerow(['You can only upload upto 5000 records in one go. (may be less in some cases)']) + if key == "parent": + w.writerow(['"Parent" signifies the parent table in which this row must be added']) + w.writerow(['If you are updating, please select "Overwrite" else existing rows will not be deleted.']) + + def build_field_columns(dt): + meta = frappe.get_meta(dt) + + tablecolumns = filter(None, + [(meta.get_field(f[0]) or None) for f in frappe.db.sql('desc `tab%s`' % dt)]) + + tablecolumns.sort(lambda a, b: a.idx - b.idx) + + if dt==doctype: + column_start_end[dt] = frappe._dict({"start": 0}) + else: + column_start_end[dt] = frappe._dict({"start": len(columns)}) + + append_field_column(frappe._dict({ + "fieldname": "name", + "label": "ID", + "fieldtype": "Data", + "reqd": 1, + "idx": 0, + "info": "Leave blank for new records" + }), True) + + for docfield in tablecolumns: + append_field_column(docfield, True) + + # all non mandatory fields + for docfield in tablecolumns: + append_field_column(docfield, False) + + # append DocType name + tablerow[column_start_end[dt].start + 1] = dt + if dt!=doctype: + tablerow[column_start_end[dt].start + 2] = doctype_parentfield[dt] + + column_start_end[dt].end = len(columns) + 1 + + def append_field_column(docfield, mandatory): + if docfield and ((mandatory and docfield.reqd) or not (mandatory or docfield.reqd)) \ + and (docfield.fieldname not in ('parenttype', 'trash_reason')) and not docfield.hidden: + tablerow.append("") + fieldrow.append(docfield.fieldname) + labelrow.append(docfield.label) + mandatoryrow.append(docfield.reqd and 'Yes' or 'No') + typerow.append(docfield.fieldtype) + inforow.append(getinforow(docfield)) + columns.append(docfield.fieldname) + + def append_empty_field_column(): + tablerow.append("~") + fieldrow.append("~") + labelrow.append("") + mandatoryrow.append("") + typerow.append("") + inforow.append("") + columns.append("") + + def getinforow(docfield): + """make info comment for options, links etc.""" + 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': + return 'Valid %s' % docfield.options + elif docfield.fieldtype in ('Int'): + return 'Integer' + elif docfield.fieldtype == "Check": + return "0 or 1" + elif hasattr(docfield, "info"): + return docfield.info + else: + return '' + + def add_field_headings(): + w.writerow(tablerow) + w.writerow(labelrow) + w.writerow(fieldrow) + w.writerow(mandatoryrow) + w.writerow(typerow) + w.writerow(inforow) + w.writerow([data_keys.data_separator]) + + def add_data(): + def add_data_row(row_group, dt, doc, rowidx): + d = doc.copy() + if all_doctypes: + d.name = '"'+ d.name+'"' + + if len(row_group) < rowidx + 1: + row_group.append([""] * (len(columns) + 1)) + row = row_group[rowidx] + for i, c in enumerate(columns[column_start_end[dt].start:column_start_end[dt].end]): + row[column_start_end[dt].start + i + 1] = d.get(c, "") + + if with_data=='Yes': + frappe.permissions.can_export(parent_doctype, raise_exception=True) + + # get permitted data only + data = frappe.get_list(doctype, fields=["*"], limit_page_length=None) + for doc in data: + # add main table + row_group = [] + + add_data_row(row_group, doctype, doc, 0) + + if all_doctypes: + # add child tables + for child_doctype in child_doctypes: + for ci, child in enumerate(frappe.db.sql("""select * from `tab%s` + where parent=%s order by idx""" % (child_doctype, "%s"), doc.name, as_dict=1)): + add_data_row(row_group, child_doctype, child, ci) + + for row in row_group: + w.writerow(row) + + w = UnicodeWriter() + key = 'parent' if parent_doctype != doctype else 'name' + + add_main_header() + + w.writerow(['']) + tablerow = [data_keys.doctype, ""] + labelrow = ["Column Labels:", "ID"] + fieldrow = [data_keys.columns, key] + mandatoryrow = ['Mandatory:', 'Yes'] + typerow = ['Type:', 'Data (text)'] + inforow = ['Info:', ''] + columns = [key] + + build_field_columns(doctype) + if all_doctypes: + for d in child_doctypes: + append_empty_field_column() + build_field_columns(d) + + add_field_headings() + add_data() + + # write out response as a type csv + frappe.response['result'] = cstr(w.getvalue()) + frappe.response['type'] = 'csv' + frappe.response['doctype'] = doctype diff --git a/frappe/core/page/data_import_tool/importer.py b/frappe/core/page/data_import_tool/importer.py new file mode 100644 index 0000000000..f791487c70 --- /dev/null +++ b/frappe/core/page/data_import_tool/importer.py @@ -0,0 +1,249 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals + +import frappe, json, os +import frappe.permissions + +from frappe.utils.datautils import check_record, import_doc, getlink +from frappe.utils import cint, cstr, flt +from frappe.core.page.data_import_tool.data_import_tool import data_keys + + +@frappe.whitelist() +def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, overwrite=None, ignore_links=False): + """upload data""" + frappe.flags.mute_emails = True + # extra input params + params = json.loads(frappe.form_dict.get("params") or '{}') + + if params.get("_submit"): + submit_after_import = True + if params.get("ignore_encoding_errors"): + ignore_encoding_errors = True + + from frappe.utils.datautils import read_csv_content_from_uploaded_file + + def bad_template(): + frappe.msgprint("Please do not change the rows above '%s'" % data_keys.data_separator, + raise_exception=1) + + def check_data_length(): + max_rows = 5000 + if not data: + frappe.msgprint("No data found", raise_exception=True) + elif len(data) > max_rows: + frappe.msgprint("Please upload only upto %d %ss at a time" % \ + (max_rows, doctype), raise_exception=True) + + def get_start_row(): + for i, row in enumerate(rows): + if row and row[0]==data_keys.data_separator: + return i+1 + bad_template() + + def get_header_row(key): + return get_header_row_and_idx(key)[0] + + def get_header_row_and_idx(key): + for i, row in enumerate(header): + if row and row[0]==key: + return row, i + return [], -1 + + def filter_empty_columns(columns): + empty_cols = filter(lambda x: x in ("", None), columns) + + if empty_cols: + if columns[-1*len(empty_cols):] == empty_cols: + # filter empty columns if they exist at the end + columns = columns[:-1*len(empty_cols)] + else: + frappe.msgprint(_("Please make sure that there are no empty columns in the file."), + raise_exception=1) + + return columns + + def make_column_map(): + doctype_row, row_idx = get_header_row_and_idx(data_keys.doctype) + if row_idx == -1: # old style + return + + dt = None + for i, d in enumerate(doctype_row[1:]): + if d not in ("~", "-"): + if d: # value in doctype_row + if doctype_row[i]==dt: + # prev column is doctype (in case of parentfield) + doctype_parentfield[dt] = doctype_row[i+1] + else: + dt = d + doctypes.append(d) + column_idx_to_fieldname[dt] = {} + column_idx_to_fieldtype[dt] = {} + if dt: + column_idx_to_fieldname[dt][i+1] = rows[row_idx + 2][i+1] + column_idx_to_fieldtype[dt][i+1] = rows[row_idx + 4][i+1] + + def get_doc(start_idx): + if doctypes: + doc = {} + for idx in xrange(start_idx, len(rows)): + if (not doc) or main_doc_empty(rows[idx]): + for dt in doctypes: + d = {} + for column_idx in column_idx_to_fieldname[dt]: + try: + fieldname = column_idx_to_fieldname[dt][column_idx] + fieldtype = column_idx_to_fieldtype[dt][column_idx] + + d[fieldname] = rows[idx][column_idx] + if fieldtype in ("Int", "Check"): + d[fieldname] = cint(d[fieldname]) + elif fieldtype in ("Float", "Currency"): + d[fieldname] = flt(d[fieldname]) + except IndexError, e: + pass + + # scrub quotes from name and modified + if d.get("name") and d["name"].startswith('"'): + d["name"] = d["name"][1:-1] + + if sum([0 if not val else 1 for val in d.values()]): + d['doctype'] = dt + if dt == doctype: + doc.update(d) + else: + if not overwrite: + d['parent'] = doc["name"] + d['parenttype'] = doctype + d['parentfield'] = doctype_parentfield[dt] + doc.setdefault(d['parentfield'], []).append(d) + else: + break + + return doc + else: + doc = frappe._dict(zip(columns, rows[start_idx][1:])) + doc['doctype'] = doctype + return doc + + def main_doc_empty(row): + return not (row and ((len(row) > 1 and row[1]) or (len(row) > 2 and row[2]))) + + # header + if not rows: + rows = read_csv_content_from_uploaded_file(ignore_encoding_errors) + start_row = get_start_row() + header = rows[:start_row] + data = rows[start_row:] + doctype = get_header_row(data_keys.main_table)[1] + columns = filter_empty_columns(get_header_row(data_keys.columns)[1:]) + doctypes = [] + doctype_parentfield = {} + column_idx_to_fieldname = {} + column_idx_to_fieldtype = {} + + if submit_after_import and not cint(frappe.db.get_value("DocType", + doctype, "is_submittable")): + submit_after_import = False + + parenttype = get_header_row(data_keys.parent_table) + + if len(parenttype) > 1: + parenttype = parenttype[1] + parentfield = get_parent_field(doctype, parenttype) + + # check permissions + if not frappe.permissions.can_import(parenttype or doctype): + frappe.flags.mute_emails = False + return {"messages": [_("Not allowed to Import") + ": " + _(doctype)], "error": True} + + # allow limit rows to be uploaded + check_data_length() + make_column_map() + + frappe.db.begin() + if overwrite==None: + overwrite = params.get('overwrite') + + # delete child rows (if parenttype) + if parenttype and overwrite: + delete_child_rows(data, doctype) + + ret = [] + error = False + parent_list = [] + for i, row in enumerate(data): + # bypass empty rows + if main_doc_empty(row): + continue + + row_idx = i + start_row + doc = None + + doc = get_doc(row_idx) + try: + frappe.local.message_log = [] + if doc.get("parentfield"): + parent = frappe.get_doc(doc["parenttype"], doc["parentfield"]) + parent.append(doc) + parent.save() + ret.append('Inserted row for %s at #%s' % (getlink(parenttype, + doc.parent), unicode(doc.idx))) + + else: + if overwrite and frappe.db.exists(doctype, doc["name"]): + original = frappe.get_doc(doctype, doc["name"]) + original.update(doc) + original.ignore_links = ignore_links + original.save() + ret.append('Updated row (#%d) %s' % (row_idx + 1, getlink(original.doctype, original.name))) + else: + doc = frappe.get_doc(doc) + doc.ignore_links = ignore_links + doc.insert() + ret.append('Inserted row (#%d) %s' % (row_idx + 1, getlink(doc.doctype, doc.name))) + if submit_after_import: + doc.submit() + ret.append('Submitted row (#%d) %s' % (row_idx + 1, getlink(doc.doctype, doc.name))) + except Exception, e: + error = True + if doc: + frappe.errprint(doc if isinstance(doc, dict) else doc.as_dict()) + err_msg = frappe.local.message_log and "
    ".join(frappe.local.message_log) or cstr(e) + ret.append('Error for row (#%d) %s : %s' % (row_idx + 1, + len(row)>1 and row[1] or "", err_msg)) + frappe.errprint(frappe.get_traceback()) + + if error: + frappe.db.rollback() + else: + frappe.db.commit() + + frappe.flags.mute_emails = False + + return {"messages": ret, "error": error} + +def get_parent_field(doctype, parenttype): + parentfield = None + + # get parentfield + if parenttype: + for d in frappe.get_meta(parenttype).get_table_fields(): + if d.options==doctype: + parentfield = d.fieldname + break + + if not parentfield: + frappe.msgprint("Did not find parentfield for %s (%s)" % \ + (parenttype, doctype)) + raise Exception + + return parentfield + +def delete_child_rows(rows, doctype): + """delete child rows for all parents""" + for p in list(set([r[1] for r in rows])): + frappe.db.sql("""delete from `tab%s` where parent=%s""" % (doctype, '%s'), p) diff --git a/frappe/core/page/desktop/desktop.json b/frappe/core/page/desktop/desktop.json new file mode 100644 index 0000000000..cec645a911 --- /dev/null +++ b/frappe/core/page/desktop/desktop.json @@ -0,0 +1,20 @@ +{ + "creation": "2013-02-14 17:37:37.000000", + "docstatus": 0, + "doctype": "Page", + "icon": "icon-th", + "idx": 1, + "modified": "2013-07-11 14:41:56.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "desktop", + "owner": "Administrator", + "page_name": "desktop", + "roles": [ + { + "role": "All" + } + ], + "standard": "Yes", + "title": "Desktop" +} \ No newline at end of file diff --git a/frappe/core/page/desktop/desktop.txt b/frappe/core/page/desktop/desktop.txt deleted file mode 100644 index c415bd8dae..0000000000 --- a/frappe/core/page/desktop/desktop.txt +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "creation": "2013-02-14 17:37:37", - "docstatus": 0, - "modified": "2013-07-11 14:41:56", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "Page", - "icon": "icon-th", - "module": "Core", - "name": "__common__", - "page_name": "desktop", - "standard": "Yes", - "title": "Desktop" - }, - { - "doctype": "Page Role", - "name": "__common__", - "parent": "desktop", - "parentfield": "roles", - "parenttype": "Page", - "role": "All" - }, - { - "doctype": "Page", - "name": "desktop" - }, - { - "doctype": "Page Role" - } -] \ No newline at end of file diff --git a/frappe/core/page/messages/messages.json b/frappe/core/page/messages/messages.json new file mode 100644 index 0000000000..850fe76bfe --- /dev/null +++ b/frappe/core/page/messages/messages.json @@ -0,0 +1,20 @@ +{ + "creation": "2012-06-14 18:44:56.000000", + "docstatus": 0, + "doctype": "Page", + "icon": "icon-envelope", + "idx": 1, + "modified": "2013-07-11 14:43:32.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "messages", + "owner": "Administrator", + "page_name": "messages", + "roles": [ + { + "role": "All" + } + ], + "standard": "Yes", + "title": "Messages" +} \ No newline at end of file diff --git a/frappe/core/page/messages/messages.py b/frappe/core/page/messages/messages.py index 9c9363915e..620e3a0344 100644 --- a/frappe/core/page/messages/messages.py +++ b/frappe/core/page/messages/messages.py @@ -66,13 +66,12 @@ def post(arg=None): import json arg = json.loads(arg) - from frappe.model.doc import Document - d = Document('Comment') + d = frappe.new_doc('Comment') d.parenttype = arg.get("parenttype") d.comment = arg['txt'] d.comment_docname = arg['contact'] d.comment_doctype = 'Message' - d.save() + d.insert(ignore_permissions=True) delete_notification_count_for("Messages") @@ -88,13 +87,16 @@ def delete(arg=None): def notify(arg=None): from frappe.utils import cstr, get_fullname, get_url - frappe.sendmail(\ - recipients=[frappe.db.get_value("User", arg["contact"], "email") or arg["contact"]], - sender= frappe.db.get_value("User", frappe.session.user, "email"), - subject="New Message from " + get_fullname(frappe.user.name), - message=frappe.get_template("templates/emails/new_message.html").render({ - "from": get_fullname(frappe.user.name), - "message": arg['txt'], - "link": get_url() - }) - ) \ No newline at end of file + try: + frappe.sendmail(\ + recipients=[frappe.db.get_value("User", arg["contact"], "email") or arg["contact"]], + sender= frappe.db.get_value("User", frappe.session.user, "email"), + subject="New Message from " + get_fullname(frappe.user.name), + message=frappe.get_template("templates/emails/new_message.html").render({ + "from": get_fullname(frappe.user.name), + "message": arg['txt'], + "link": get_url() + }) + ) + except frappe.OutgoingEmailError, e: + pass \ No newline at end of file diff --git a/frappe/core/page/messages/messages.txt b/frappe/core/page/messages/messages.txt deleted file mode 100644 index 347a9eaedc..0000000000 --- a/frappe/core/page/messages/messages.txt +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "creation": "2012-06-14 18:44:56", - "docstatus": 0, - "modified": "2013-07-11 14:43:32", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "Page", - "icon": "icon-envelope", - "module": "Core", - "name": "__common__", - "page_name": "messages", - "standard": "Yes", - "title": "Messages" - }, - { - "doctype": "Page Role", - "name": "__common__", - "parent": "messages", - "parentfield": "roles", - "parenttype": "Page", - "role": "All" - }, - { - "doctype": "Page", - "name": "messages" - }, - { - "doctype": "Page Role" - } -] \ No newline at end of file diff --git a/frappe/core/page/modules_setup/modules_setup.json b/frappe/core/page/modules_setup/modules_setup.json new file mode 100644 index 0000000000..6d9f609ad7 --- /dev/null +++ b/frappe/core/page/modules_setup/modules_setup.json @@ -0,0 +1,20 @@ +{ + "creation": "2012-10-04 18:45:29.000000", + "docstatus": 0, + "doctype": "Page", + "icon": "icon-cog", + "idx": 1, + "modified": "2013-07-11 14:43:37.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "modules_setup", + "owner": "Administrator", + "page_name": "modules_setup", + "roles": [ + { + "role": "System Manager" + } + ], + "standard": "Yes", + "title": "Modules Setup" +} \ No newline at end of file diff --git a/frappe/core/page/modules_setup/modules_setup.txt b/frappe/core/page/modules_setup/modules_setup.txt deleted file mode 100644 index 4faf9caa2b..0000000000 --- a/frappe/core/page/modules_setup/modules_setup.txt +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "creation": "2012-10-04 18:45:29", - "docstatus": 0, - "modified": "2013-07-11 14:43:37", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "Page", - "icon": "icon-cog", - "module": "Core", - "name": "__common__", - "page_name": "modules_setup", - "standard": "Yes", - "title": "Modules Setup" - }, - { - "doctype": "Page Role", - "name": "__common__", - "parent": "modules_setup", - "parentfield": "roles", - "parenttype": "Page", - "role": "System Manager" - }, - { - "doctype": "Page", - "name": "modules_setup" - }, - { - "doctype": "Page Role" - } -] \ No newline at end of file diff --git a/frappe/core/page/permission_manager/permission_manager.js b/frappe/core/page/permission_manager/permission_manager.js index 95ec254578..015a8d4640 100644 --- a/frappe/core/page/permission_manager/permission_manager.js +++ b/frappe/core/page/permission_manager/permission_manager.js @@ -428,17 +428,15 @@ frappe.PermissionEngine = Class.extend({ }); }, get_user_fields: function(doctype) { - var user_fields = frappe.model.get("DocField", {parent:doctype, - fieldtype:"Link", options:"User"}); - - user_fields = user_fields.concat(frappe.model.get("DocField", {parent:doctype, - fieldtype:"Select", link_doctype:"User"})) - + var user_fields = frappe.get_children("DocType", doctype, "fields", {fieldtype:"Link", options:"User"}) + user_fields = user_fields.concat(frappe.get_children("DocType", doctype, "fields", + {fieldtype:"Select", link_doctype:"User"})) + return user_fields }, get_link_fields: function(doctype) { - return link_fields = frappe.model.get("DocField", {parent:doctype, - fieldtype:"Link", options:["not in", ["User", '[Select]']]}); + return frappe.get_children("DocType", doctype, "fields", + {fieldtype:"Link", options:["not in", ["User", '[Select]']]}); } }) diff --git a/frappe/core/page/permission_manager/permission_manager.json b/frappe/core/page/permission_manager/permission_manager.json new file mode 100644 index 0000000000..db0a3129e3 --- /dev/null +++ b/frappe/core/page/permission_manager/permission_manager.json @@ -0,0 +1,20 @@ +{ + "creation": "2013-01-01 11:00:01.000000", + "docstatus": 0, + "doctype": "Page", + "icon": "icon-lock", + "idx": 1, + "modified": "2013-07-11 14:43:43.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "permission-manager", + "owner": "Administrator", + "page_name": "permission-manager", + "roles": [ + { + "role": "System Manager" + } + ], + "standard": "Yes", + "title": "Permission Manager" +} \ No newline at end of file diff --git a/frappe/core/page/permission_manager/permission_manager.py b/frappe/core/page/permission_manager/permission_manager.py index 8d0b491865..9602f2cbd5 100644 --- a/frappe/core/page/permission_manager/permission_manager.py +++ b/frappe/core/page/permission_manager/permission_manager.py @@ -12,7 +12,7 @@ def get_roles_and_doctypes(): return { "doctypes": [d[0] for d in frappe.db.sql("""select name from `tabDocType` dt where ifnull(istable,0)=0 and - name not in ('DocType', 'Control Panel') and + name not in ('DocType') and exists(select * from `tabDocField` where parent=dt.name)""")], "roles": [d[0] for d in frappe.db.sql("""select name from tabRole where name not in ('Guest', 'Administrator')""")] @@ -36,7 +36,7 @@ def remove(doctype, name): @frappe.whitelist() def add(parent, role, permlevel): frappe.only_for("System Manager") - frappe.doc(fielddata={ + frappe.get_doc({ "doctype":"DocPerm", "__islocal": 1, "parent": parent, diff --git a/frappe/core/page/permission_manager/permission_manager.txt b/frappe/core/page/permission_manager/permission_manager.txt deleted file mode 100644 index e6f239be70..0000000000 --- a/frappe/core/page/permission_manager/permission_manager.txt +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "creation": "2013-01-01 11:00:01", - "docstatus": 0, - "modified": "2013-07-11 14:43:43", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "Page", - "icon": "icon-lock", - "module": "Core", - "name": "__common__", - "page_name": "permission-manager", - "standard": "Yes", - "title": "Permission Manager" - }, - { - "doctype": "Page Role", - "name": "__common__", - "parent": "permission-manager", - "parentfield": "roles", - "parenttype": "Page", - "role": "System Manager" - }, - { - "doctype": "Page", - "name": "permission-manager" - }, - { - "doctype": "Page Role" - } -] \ No newline at end of file diff --git a/frappe/core/page/user_properties/user_properties.json b/frappe/core/page/user_properties/user_properties.json new file mode 100644 index 0000000000..414b7a5b8d --- /dev/null +++ b/frappe/core/page/user_properties/user_properties.json @@ -0,0 +1,20 @@ +{ + "creation": "2013-01-01 18:50:55.000000", + "docstatus": 0, + "doctype": "Page", + "icon": "icon-user", + "idx": 1, + "modified": "2014-03-13 11:11:56.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "user-properties", + "owner": "Administrator", + "page_name": "user-properties", + "roles": [ + { + "role": "System Manager" + } + ], + "standard": "Yes", + "title": "User Properties" +} \ No newline at end of file diff --git a/frappe/core/page/user_properties/user_properties.py b/frappe/core/page/user_properties/user_properties.py index e4aa0799a8..47f58cec35 100644 --- a/frappe/core/page/user_properties/user_properties.py +++ b/frappe/core/page/user_properties/user_properties.py @@ -23,7 +23,7 @@ def get_properties(parent=None, defkey=None, defvalue=None): properties = frappe.db.sql("""select name, parent, defkey, defvalue from tabDefaultValue - where parent not in ('Control Panel', '__global') + where parent not in ('__default', '__global') and substr(defkey,1,1)!='_' and parenttype='Restriction' {conditions} diff --git a/frappe/core/page/user_properties/user_properties.txt b/frappe/core/page/user_properties/user_properties.txt deleted file mode 100644 index 12271ee239..0000000000 --- a/frappe/core/page/user_properties/user_properties.txt +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "creation": "2013-01-01 18:50:55", - "docstatus": 0, - "modified": "2014-03-13 11:11:56", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "Page", - "icon": "icon-user", - "module": "Core", - "name": "__common__", - "page_name": "user-properties", - "standard": "Yes", - "title": "User Properties" - }, - { - "doctype": "Page Role", - "name": "__common__", - "parent": "user-properties", - "parentfield": "roles", - "parenttype": "Page", - "role": "System Manager" - }, - { - "doctype": "Page", - "name": "user-properties" - }, - { - "doctype": "Page Role" - } -] \ No newline at end of file diff --git a/frappe/core/report/todo/todo.json b/frappe/core/report/todo/todo.json new file mode 100644 index 0000000000..768162f126 --- /dev/null +++ b/frappe/core/report/todo/todo.json @@ -0,0 +1,15 @@ +{ + "creation": "2013-02-25 14:26:30.000000", + "docstatus": 0, + "doctype": "Report", + "idx": 1, + "is_standard": "Yes", + "modified": "2014-03-07 15:30:27.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "ToDo", + "owner": "Administrator", + "ref_doctype": "ToDo", + "report_name": "ToDo", + "report_type": "Script Report" +} \ No newline at end of file diff --git a/frappe/core/report/todo/todo.txt b/frappe/core/report/todo/todo.txt deleted file mode 100644 index 7c208e5139..0000000000 --- a/frappe/core/report/todo/todo.txt +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "creation": "2013-02-25 14:26:30", - "docstatus": 0, - "modified": "2014-03-07 15:30:27", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "Report", - "is_standard": "Yes", - "module": "Core", - "name": "__common__", - "ref_doctype": "ToDo", - "report_name": "ToDo", - "report_type": "Script Report" - }, - { - "doctype": "Report", - "name": "ToDo" - } -] \ No newline at end of file diff --git a/frappe/data/Framework.sql b/frappe/data/Framework.sql index f6b04d5d26..b7c78719cc 100644 --- a/frappe/data/Framework.sql +++ b/frappe/data/Framework.sql @@ -9,8 +9,8 @@ DROP TABLE IF EXISTS `tabDocField`; CREATE TABLE `tabDocField` ( `name` varchar(120) NOT NULL, - `creation` datetime DEFAULT NULL, - `modified` datetime DEFAULT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, `modified_by` varchar(40) DEFAULT NULL, `owner` varchar(40) DEFAULT NULL, `docstatus` int(1) DEFAULT '0', @@ -61,8 +61,8 @@ DROP TABLE IF EXISTS `tabDocPerm`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `tabDocPerm` ( `name` varchar(120) NOT NULL, - `creation` datetime DEFAULT NULL, - `modified` datetime DEFAULT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, `modified_by` varchar(40) DEFAULT NULL, `owner` varchar(40) DEFAULT NULL, `docstatus` int(1) DEFAULT '0', @@ -100,8 +100,8 @@ DROP TABLE IF EXISTS `tabDocType`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `tabDocType` ( `name` varchar(180) NOT NULL DEFAULT '', - `creation` datetime DEFAULT NULL, - `modified` datetime DEFAULT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, `modified_by` varchar(40) DEFAULT NULL, `owner` varchar(180) DEFAULT NULL, `docstatus` int(1) DEFAULT '0', @@ -191,7 +191,7 @@ CREATE TABLE `tabSessions` ( `sid` varchar(120) DEFAULT NULL, `sessiondata` longtext, `ipaddress` varchar(16) DEFAULT NULL, - `lastupdate` datetime DEFAULT NULL, + `lastupdate` datetime(6) DEFAULT NULL, `status` varchar(20) DEFAULT NULL, KEY `sid` (`sid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -232,8 +232,8 @@ CREATE TABLE `__Auth` ( DROP TABLE IF EXISTS `tabFile Data`; CREATE TABLE `tabFile Data` ( `name` varchar(120) NOT NULL, - `creation` datetime DEFAULT NULL, - `modified` datetime DEFAULT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, `modified_by` varchar(40) DEFAULT NULL, `owner` varchar(40) DEFAULT NULL, `docstatus` int(1) DEFAULT '0', @@ -260,8 +260,8 @@ CREATE TABLE `tabFile Data` ( DROP TABLE IF EXISTS `tabDefaultValue`; CREATE TABLE `tabDefaultValue` ( `name` varchar(120) NOT NULL, - `creation` datetime DEFAULT NULL, - `modified` datetime DEFAULT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, `modified_by` varchar(40) DEFAULT NULL, `owner` varchar(40) DEFAULT NULL, `docstatus` int(1) DEFAULT '0', diff --git a/frappe/database.py b/frappe/database.py index 749dfee446..127d2d0bf0 100644 --- a/frappe/database.py +++ b/frappe/database.py @@ -9,6 +9,7 @@ import MySQLdb import warnings import frappe import datetime +from frappe.utils import now, get_datetime, get_datetime_str class Database: """ @@ -43,6 +44,8 @@ class Database: self._conn = MySQLdb.connect(user=self.user, host=self.host, passwd=self.password, use_unicode=True, charset='utf8') self._conn.converter[246]=float + self._conn.converter[12]=get_datetime + self._cursor = self._conn.cursor() if self.user != 'root': self.use(self.user) @@ -156,7 +159,8 @@ class Database: self.sql(query) def check_transaction_status(self, query): - if self.transaction_writes and query and query.strip().split()[0].lower() in ['start', 'alter', 'drop', 'create', "begin"]: + if not frappe.flags.in_test and self.transaction_writes and \ + query and query.strip().split()[0].lower() in ['start', 'alter', 'drop', 'create', "begin"]: raise Exception, 'This statement can cause implicit commit' if query and query.strip().lower() in ('commit', 'rollback'): @@ -297,7 +301,7 @@ class Database: return ((len(ret[0]) > 1 or as_dict) and ret[0] or ret[0][0]) if ret else None - def get_values(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False, debug=False): + def get_values(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False, debug=False, order_by=None, update=None): if isinstance(filters, list): return self.get_value_for_many_names(doctype, filters, fieldname, debug=debug) @@ -310,7 +314,7 @@ class Database: if (filters is not None) and (filters!=doctype or doctype=="DocType"): try: - return self.get_values_from_table(fields, filters, doctype, as_dict, debug) + return self.get_values_from_table(fields, filters, doctype, as_dict, debug, order_by, update) except Exception, e: if ignore and e.args[0] in (1146, 1054): # table or column not found, return None @@ -321,9 +325,9 @@ class Database: else: raise - return self.get_values_from_single(fields, filters, doctype, as_dict, debug) + return self.get_values_from_single(fields, filters, doctype, as_dict, debug, update) - def get_values_from_single(self, fields, filters, doctype, as_dict=False, debug=False): + def get_values_from_single(self, fields, filters, doctype, as_dict=False, debug=False, update=None): if fields=="*" or isinstance(filters, dict): # check if single doc matches with filters values = self.get_singles_dict(doctype) @@ -345,7 +349,13 @@ class Database: tuple(fields) + (doctype,), as_dict=False, debug=debug) if as_dict: - return r and [frappe._dict(r)] or [] + if r: + r = frappe._dict(r) + if update: + r.update(update) + return [r] + else: + return [] else: return r and [[i[1] for i in r]] or [] @@ -354,7 +364,7 @@ class Database: tabSingles where doctype=%s""", doctype)) - def get_values_from_table(self, fields, filters, doctype, as_dict, debug): + def get_values_from_table(self, fields, filters, doctype, as_dict, debug, order_by=None, update=None): fl = [] if isinstance(fields, (list, tuple)): for f in fields: @@ -369,9 +379,11 @@ class Database: as_dict = True conditions, filters = self.build_conditions(filters) + + order_by = ("order by " + order_by) if order_by else "" - r = self.sql("select %s from `tab%s` where %s" % (fl, doctype, - conditions), filters, as_dict=as_dict, debug=debug) + r = self.sql("select %s from `tab%s` where %s %s" % (fl, doctype, + conditions, order_by), filters, as_dict=as_dict, debug=debug, update=update) return r @@ -385,24 +397,32 @@ class Database: return {} def set_value(self, dt, dn, field, val, modified=None, modified_by=None): - from frappe.utils import now + if not modified: + modified = now() + if not modified_by: + modified_by = frappe.session.user + if dn and dt!=dn: self.sql("""update `tab%s` set `%s`=%s, modified=%s, modified_by=%s where name=%s""" % (dt, field, "%s", "%s", "%s", "%s"), - (val, modified or now(), modified_by or frappe.session["user"], dn)) + (val, modified, modified_by, dn)) else: if self.sql("select value from tabSingles where field=%s and doctype=%s", (field, dt)): self.sql("""update tabSingles set value=%s where field=%s and doctype=%s""", (val, field, dt)) else: self.sql("""insert into tabSingles(doctype, field, value) - values (%s, %s, %s)""", (dt, field, val, )) + values (%s, %s, %s)""", (dt, field, val)) - if field!="modified": - self.set_value(dt, dn, "modified", modified or now()) + if field not in ("modified", "modified_by"): + self.set_value(dt, dn, "modified", modified) + self.set_value(dt, dn, "modified_by", modified_by) def set(self, doc, field, val): doc.set(field, val) + doc.set("modified", now()) + doc.set("modified_by", frappe.session.user) + frappe.db.set_value(doc.doctype, doc.name, field, val, doc.modified, doc.modified_by) def touch(self, doctype, docname): from frappe.utils import now @@ -417,27 +437,26 @@ class Database: def get_global(self, key, user='__global'): return self.get_default(key, user) - def set_default(self, key, val, parent="Control Panel", parenttype=None): - """set control panel default (tabDefaultVal)""" + def set_default(self, key, val, parent="__default", parenttype=None): import frappe.defaults frappe.defaults.set_default(key, val, parent, parenttype) - def add_default(self, key, val, parent="Control Panel", parenttype=None): + def add_default(self, key, val, parent="__default", parenttype=None): import frappe.defaults frappe.defaults.add_default(key, val, parent, parenttype) - def get_default(self, key, parent="Control Panel"): + def get_default(self, key, parent="__default"): """get default value""" import frappe.defaults d = frappe.defaults.get_defaults(parent).get(key) return isinstance(d, list) and d[0] or d - def get_defaults_as_list(self, key, parent="Control Panel"): + def get_defaults_as_list(self, key, parent="__default"): import frappe.defaults d = frappe.defaults.get_default(key, parent) return isinstance(d, basestring) and [d] or d - def get_defaults(self, key=None, parent="Control Panel"): + def get_defaults(self, key=None, parent="__default"): """get all defaults""" import frappe.defaults if key: @@ -468,7 +487,7 @@ class Database: def exists(self, dt, dn=None): if isinstance(dt, basestring): - if dt==dn: + if dt!="DocType" and dt==dn: return True # single always exists (!) try: return self.sql('select name from `tab%s` where name=%s' % (dt, '%s'), (dn,)) diff --git a/frappe/defaults.py b/frappe/defaults.py index 36840da232..22843937e0 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -common_keys = ["Control Panel", "__global"] +common_keys = ["__default", "__global"] def set_user_default(key, value, user=None, parenttype=None): set_default(key, value, user or frappe.session.user, parenttype) @@ -60,10 +60,10 @@ def clear_user_default(key, user=None): # Global def set_global_default(key, value): - set_default(key, value, "Control Panel") + set_default(key, value, "__default") def add_global_default(key, value): - add_default(key, value, "Control Panel") + add_default(key, value, "__default") def get_global_default(key): d = get_defaults().get(key, None) @@ -71,7 +71,7 @@ def get_global_default(key): # Common -def set_default(key, value, parent, parenttype="Control Panel"): +def set_default(key, value, parent, parenttype="__default"): if frappe.db.sql("""select defkey from `tabDefaultValue` where defkey=%s and parent=%s """, (key, parent)): # update @@ -82,15 +82,15 @@ def set_default(key, value, parent, parenttype="Control Panel"): add_default(key, value, parent) def add_default(key, value, parent, parenttype=None): - d = frappe.doc({ + d = frappe.get_doc({ "doctype": "DefaultValue", "parent": parent, - "parenttype": parenttype or "Control Panel", + "parenttype": parenttype or "__default", "parentfield": "system_defaults", "defkey": key, "defvalue": value }) - d.insert() + d.db_insert() if parenttype=="Restriction": frappe.local.restrictions = None _clear_cache(parent) @@ -116,7 +116,7 @@ def clear_default(key=None, value=None, parent=None, name=None, parenttype=None) clear_cache(parent) values.append(parent) else: - clear_cache("Control Panel") + clear_cache("__default") clear_cache("__global") if parenttype: @@ -132,7 +132,7 @@ def clear_default(key=None, value=None, parent=None, name=None, parenttype=None) tuple(values)) _clear_cache(parent) -def get_defaults_for(parent="Control Panel"): +def get_defaults_for(parent="__default"): """get all defaults""" defaults = frappe.cache().get_value("__defaults:" + parent) if defaults==None: diff --git a/frappe/exceptions.py b/frappe/exceptions.py index 3a15f913d4..e0954b4080 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -26,7 +26,10 @@ class OutgoingEmailError(Exception): class SessionStopped(Exception): http_status_code = 503 -class DataError(Exception): pass +class UnsupportedMediaType(Exception): + http_status_code = 415 + +class DataError(ValidationError): pass class UnknownDomainError(Exception): pass class MappingMismatchError(ValidationError): pass class InvalidStatusError(ValidationError): pass diff --git a/frappe/handler.py b/frappe/handler.py index 4076e6fecd..2e3f39602e 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -33,10 +33,9 @@ def web_logout(): @frappe.whitelist(allow_guest=True) def run_custom_method(doctype, name, custom_method): """cmd=run_custom_method&doctype={doctype}&name={name}&custom_method={custom_method}""" - bean = frappe.bean(doctype, name) - controller = bean.get_controller() - if getattr(controller, custom_method, frappe._dict()).is_whitelisted: - frappe.call(getattr(controller, custom_method), **frappe.local.form_dict) + doc = frappe.get_doc(doctype, name) + if getattr(doc, custom_method, frappe._dict()).is_whitelisted: + frappe.call(getattr(doc, custom_method), **frappe.local.form_dict) else: frappe.throw("Not Allowed") @@ -65,7 +64,7 @@ def handle(): if cmd!='login': execute_cmd(cmd) - + return build_response("json") def execute_cmd(cmd): diff --git a/frappe/hooks.txt b/frappe/hooks.txt index 45cc4912da..5feb017e48 100644 --- a/frappe/hooks.txt +++ b/frappe/hooks.txt @@ -42,9 +42,12 @@ has_permission:ToDo = frappe.core.doctype.todo.todo.has_permission # bean -bean_event:User Vote:after_insert = frappe.templates.generators.website_group.clear_cache_on_bean_event -bean_event:Website Route Permission:on_update = frappe.templates.generators.website_group.clear_cache_on_bean_event +doc_event:User Vote:after_insert = frappe.templates.generators.website_group.clear_cache_on_doc_event +doc_event:Website Route Permission:on_update = frappe.templates.generators.website_group.clear_cache_on_doc_event -bean_event:*:on_update = frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications -bean_event:*:on_cancel = frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications -bean_event:*:on_trash = frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications +doc_event:*:on_update = frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications +doc_event:*:on_cancel = frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications +doc_event:*:on_trash = frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications + +write_file_keys = file_url +write_file_keys = file_name diff --git a/frappe/installer.py b/frappe/installer.py index ed5c921799..b02b6077ac 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -130,7 +130,7 @@ def set_all_patches_as_completed(app): patch_path = os.path.join(frappe.get_pymodule_path(app), "patches.txt") if os.path.exists(patch_path): for patch in frappe.get_file_items(patch_path): - frappe.doc({ + frappe.get_doc({ "doctype": "Patch Log", "patch": patch }).insert() diff --git a/frappe/memc.py b/frappe/memc.py index 3d03d1ac12..360a468d54 100644 --- a/frappe/memc.py +++ b/frappe/memc.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import memcache, frappe -from frappe.model.doc import Document class MClient(memcache.Client): """memcache client that will automatically prefix conf.db_name""" @@ -11,13 +10,18 @@ class MClient(memcache.Client): return (frappe.conf.db_name + ":" + key.replace(" ", "_")).encode('utf-8') def set_value(self, key, val): + frappe.local.cache[key] = val self.set(self.n(key), val) def get_value(self, key, builder=None): - val = self.get(self.n(key)) - if not val and builder: - val = builder() - self.set_value(key, val) + val = frappe.local.cache.get(key) + if val is None: + val = self.get(self.n(key)) + if val is None and builder: + val = builder() + self.set_value(key, val) + else: + frappe.local.cache[key] = val return val def delete_value(self, keys): @@ -25,3 +29,5 @@ class MClient(memcache.Client): keys = (keys,) for key in keys: self.delete(self.n(key)) + if key in frappe.local.cache: + del frappe.local.cache[key] diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 7dd9487f05..5beafb2936 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -17,9 +17,9 @@ def insert(doclist): if isinstance(d, dict): d["__islocal"] = 1 else: - d.fields["__islocal"] = 1 + d.set("__islocal", 1) - wrapper = frappe.bean(doclist) + wrapper = frappe.get_doc(doclist) wrapper.save() return wrapper @@ -29,18 +29,16 @@ 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=[]): - import frappe.model.doc - if not tarfields: tarfields = srcfields l = [] - data = frappe.model.doc.getchildren(src.name, srctype, srcfield) + data = src.get(srcfield) for d in data: - newrow = frappe.model.doc.addchild(tar, tarfield, tartype) + newrow = tar.append(tarfield) newrow.idx = d.idx for i in range(len(srcfields)): - newrow.fields[tarfields[i]] = d.fields[srcfields[i]] + newrow.set(tarfields[i], d.get(srcfields[i])) l.append(newrow) return l @@ -87,8 +85,8 @@ def delete_fields(args_dict, delete=0): def rename_field(doctype, old_fieldname, new_fieldname): """This functions assumes that doctype is already synced""" - doctype_list = frappe.get_doctype(doctype) - new_field = doctype_list.get_field(new_fieldname) + 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 @@ -99,7 +97,7 @@ def rename_field(doctype, old_fieldname, new_fieldname): where parentfield=%s""" % (new_field.options.split("\n")[0], "%s", "%s"), (new_fieldname, old_fieldname)) elif new_field.fieldtype not in no_value_fields: - if doctype_list[0].issingle: + if meta.issingle: frappe.db.sql("""update `tabSingles` set field=%s where doctype=%s and field=%s""", (new_fieldname, doctype, old_fieldname)) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 65c012bcef..dd3e49b063 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -1,198 +1,238 @@ # 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 from frappe import _, msgprint from frappe.utils import cint, flt, cstr, now from frappe.model import default_fields -from frappe.model.db_schema import type_map from frappe.model.naming import set_new_name class BaseDocument(object): - def __init__(self, d, valid_columns=None): - self.update(d, valid_columns=valid_columns) + ignore_in_getter = ("doctype", "_meta", "meta", "_table_fields", "_valid_columns") - def __getattr__(self, key): - if self.__dict__.has_key(key): - return self.__dict__[key] + def __init__(self, d): + self.update(d) - if key!= "_valid_columns" and key in self.get_valid_columns(): - return None + @property + def meta(self): + if not hasattr(self, "_meta"): + self._meta = frappe.get_meta(self.doctype) - raise AttributeError(key) + return self._meta - def update(self, d, valid_columns=None): - if valid_columns: - self.__dict__["_valid_columns"] = valid_columns + def update(self, d): if "doctype" in d: self.set("doctype", d.get("doctype")) for key, value in d.iteritems(): self.set(key, value) - + + def update_if_missing(self, d): + if isinstance(d, BaseDocument): + d = d.get_valid_dict() + + if "doctype" in d: + self.set("doctype", d.get("doctype")) + for key, value in d.iteritems(): + if self.get(key) is None: + self.set(key, value) + + def get_db_value(self, key): + return frappe.db.get_value(self.doctype, self.name, key) + def get(self, key=None, filters=None, limit=None, default=None): if key: + if isinstance(key, dict): + return _filter(self.get_all_children(), key, limit=limit) if filters: - return _filter(self.__dict__.get(key), filters, limit=limit) + if isinstance(filters, dict): + value = _filter(self.__dict__.get(key), filters, limit=limit) + else: + default = filters + filters = None + value = self.__dict__.get(key, default) else: - return self.__dict__.get(key, default) + value = self.__dict__.get(key, default) + + if value is None and key not in self.ignore_in_getter \ + and key in (d.fieldname for d in self.meta.get_table_fields()): + self.set(key, []) + value = self.__dict__.get(key) + + return value else: return self.__dict__ - - def set(self, key, value, valid_columns=None): - if isinstance(value, list): - tmp = [] - for v in value: - tmp.append(self._init_child(v, key, valid_columns)) - value = tmp - self.__dict__[key] = value - - def append(self, key, value): - if isinstance(value, dict): - if not self.get(key): + def getone(self, key, filters=None): + return self.get(key, filters=filters, limit=1)[0] + + def set(self, key, value): + if isinstance(value, list): + self.__dict__[key] = [] + self.extend(key, value) + else: + self.__dict__[key] = value + + def append(self, key, value=None): + if value==None: + value={} + if isinstance(value, (dict, BaseDocument)): + if not self.__dict__.get(key): self.__dict__[key] = [] - self.get(key).append(self._init_child(value, key)) + value = self._init_child(value, key) + self.__dict__[key].append(value) + return value else: raise ValueError - + def extend(self, key, value): if isinstance(value, list): for v in value: - self.append(v) + self.append(key, v) else: raise ValueError - - def _init_child(self, value, key, valid_columns=None): + + def remove(self, doc): + self.get(doc.parentfield).remove(doc) + + def _init_child(self, value, key): if not self.doctype: return value if not isinstance(value, BaseDocument): - if not value.get("doctype"): + if "doctype" not in value: value["doctype"] = self.get_table_field_doctype(key) - if not value.get("doctype"): - raise AttributeError, key - value = BaseDocument(value, valid_columns=valid_columns) - + if not value["doctype"]: + raise AttributeError, key + value = BaseDocument(value) + value.init_valid_columns() + value.parent = self.name value.parenttype = self.doctype value.parentfield = key - if not value.idx: + if not getattr(value, "idx", None): value.idx = len(self.get(key) or []) + 1 - + return value - @property - def doc(self): - return self - - @property - def meta(self): - if not self.get("_meta"): - self._meta = frappe.get_meta(self.doctype) - return self._meta - def get_valid_dict(self): d = {} - for fieldname in self.valid_columns: + for fieldname in self.meta.get_valid_columns(): d[fieldname] = self.get(fieldname) return d - - @property - def valid_columns(self): - return self.get_valid_columns() - def get_valid_columns(self): - if not hasattr(self, "_valid_columns"): - doctype = self.__dict__.get("doctype") - self._valid_columns = default_fields[1:] + \ - [df.fieldname for df in frappe.get_meta(doctype).get("fields") - if df.fieldtype in type_map] - - return self._valid_columns - + def init_valid_columns(self): + for key in default_fields: + if key not in self.__dict__: + self.__dict__[key] = None + + if self.doctype in ("DocField", "DocPerm") and self.parent in ("DocType", "DocField", "DocPerm"): + from frappe.model.meta import get_table_columns + valid = get_table_columns(self.doctype) + else: + valid = self.meta.get_valid_columns() + + for key in valid: + if key not in self.__dict__: + self.__dict__[key] = None + + def is_new(self): + return self.get("__islocal") + + def as_dict(self): + doc = self.get_valid_dict() + doc["doctype"] = self.doctype + for df in self.meta.get_table_fields(): + children = self.get(df.fieldname) or [] + doc[df.fieldname] = [d.as_dict() for d in children] + return doc + def get_table_field_doctype(self, fieldname): - return self.meta.get("fields", {"fieldname":fieldname})[0].options - + return self.meta.get_field(fieldname).options + + def get_parentfield_of_doctype(self, doctype): + fieldname = [df.fieldname for df in self.meta.get_table_fields() if df.options==doctype] + return fieldname[0] if fieldname else None + def db_insert(self): set_new_name(self) d = self.get_valid_dict() columns = d.keys() - frappe.db.sql("""insert into `tab{doctype}` + frappe.db.sql("""insert into `tab{doctype}` ({columns}) values ({values})""".format( doctype = self.doctype, columns = ", ".join(["`"+c+"`" for c in columns]), values = ", ".join(["%s"] * len(columns)) ), d.values()) + self.set("__islocal", False) def db_update(self): + if self.get("__islocal") or not self.name: + self.db_insert() + return + d = self.get_valid_dict() columns = d.keys() - frappe.db.sql("""update `tab{doctype}` + frappe.db.sql("""update `tab{doctype}` set {values} where name=%s""".format( doctype = self.doctype, values = ", ".join(["`"+c+"`=%s" for c in columns]) ), d.values() + [d.get("name")]) - + def _fix_numeric_types(self): for df in self.meta.get("fields"): if df.fieldtype in ("Int", "Check"): self.set(df.fieldname, cint(self.get(df.fieldname))) elif df.fieldtype in ("Float", "Currency"): self.set(df.fieldname, flt(self.get(df.fieldname))) - - if self.docstatus is not None: + + if self.docstatus is not None: self.docstatus = cint(self.docstatus) - - def set_missing_values(self, d): - for key, value in d.iteritems(): - if self.get(key) is None: - self.set(key, value) - + def _get_missing_mandatory_fields(self): """Get mandatory fields that do not have any values""" def get_msg(df): if df.fieldtype == "Table": return "{}: {}: {}".format(_("Error"), _("Data missing in table"), _(df.label)) - + elif self.parentfield: return "{}: {} #{}: {}: {}".format(_("Error"), _("Row"), self.idx, _("Value missing for"), _(df.label)) else: return "{}: {}: {}".format(_("Error"), _("Value missing for"), _(df.label)) - + missing = [] - + for df in self.meta.get("fields", {"reqd": 1}): if self.get(df.fieldname) in (None, []): missing.append((df.fieldname, get_msg(df))) - + return missing - + def get_invalid_links(self): def get_msg(df, docname): if self.parentfield: return "{} #{}: {}: {}".format(_("Row"), self.idx, _(df.label), docname) else: return "{}: {}".format(_(df.label), docname) - + invalid_links = [] for df in self.meta.get_link_fields(): doctype = df.options - + if not doctype: frappe.throw("Options not set for link field: {}".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))) - + return invalid_links - + def _validate_constants(self): if frappe.flags.in_import: return @@ -203,8 +243,8 @@ class BaseDocument(object): for fieldname in constants: if self.get(fieldname) != values.get(fieldname): - frappe.throw("{0}: {1}".format(_("Value cannot be changed for"), - _(meta.get("fields", {"fieldname":fieldname})[0].label)), + frappe.throw("{0}: {1}".format(_("Value cannot be changed for"), + _(self.meta.get_label(fieldname))), frappe.CannotChangeConstantError) def _filter(data, filters, limit=None): @@ -214,23 +254,23 @@ def _filter(data, filters, limit=None): "key" : True (exists), "key": False (does not exist) }""" out = [] - + for d in data: add = True for f in filters: fval = filters[f] - + if fval is True: - fval = ["not None", fval] + fval = ("not None", fval) elif fval is False: - fval = ["None", fval] + fval = ("None", fval) elif not isinstance(fval, (tuple, list)): if isinstance(fval, basestring) and fval.startswith("^"): - fval = ["^", fval[1:]] + fval = ("^", fval[1:]) else: - fval = ["=", fval] - - if not frappe.compare(d.get(f), fval[0], fval[1]): + fval = ("=", fval) + + if not frappe.compare(getattr(d, f, None), fval[0], fval[1]): add = False break @@ -238,5 +278,5 @@ def _filter(data, filters, limit=None): out.append(d) if limit and (len(out)-1)==limit: break - + return out diff --git a/frappe/model/bean.py b/frappe/model/bean.py deleted file mode 100644 index c7e8ec9411..0000000000 --- a/frappe/model/bean.py +++ /dev/null @@ -1,524 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals -""" -Transactions are defined as collection of classes, a Bean represents collection of Document -objects for a transaction with main and children. - -Group actions like save, etc are performed on doclists -""" - -import frappe -from frappe import _, msgprint -from frappe.utils import cint, cstr, flt -from frappe.model.doc import Document -import frappe.permissions - -class DocstatusTransitionError(frappe.ValidationError): pass -class BeanPermissionError(frappe.ValidationError): pass -class TimestampMismatchError(frappe.ValidationError): pass - -class Bean: - """ - Collection of Documents with one parent and multiple children - """ - def __init__(self, dt=None, dn=None): - self.obj = None - self.ignore_permissions = False - self.ignore_children_type = [] - self.ignore_links = False - self.ignore_validate = False - self.ignore_fields = False - self.ignore_mandatory = False - self.ignore_restrictions = False - - if isinstance(dt, basestring) and not dn: - dn = dt - if dt and dn: - if isinstance(dn, dict): - dn = frappe.db.get_value(dt, dn, "name") - if dn is None: - raise frappe.DoesNotExistError - - self.load_from_db(dt, dn) - elif isinstance(dt, list): - self.set_doclist(dt) - elif isinstance(dt, dict): - self.set_doclist([dt]) - - def load_from_db(self, dt=None, dn=None): - """ - Load doclist from dt - """ - from frappe.model.doc import getchildren - - if not dt: dt = self.doc.doctype - if not dn: dn = self.doc.name - - doc = Document(dt, dn) - - # get all children types - tablefields = frappe.model.meta.get_table_fields(dt) - - # load chilren - doclist = frappe.doclist([doc,]) - for t in tablefields: - doclist += getchildren(doc.name, t[0], t[1], dt) - - self.set_doclist(doclist) - - def __iter__(self): - return self.doclist.__iter__() - - @property - def meta(self): - if not hasattr(self, "_meta"): - self._meta = frappe.get_doctype(self.doc.doctype) - return self._meta - - def from_compressed(self, data, docname): - from frappe.model.utils import expand - self.set_doclist(expand(data)) - - def set_doclist(self, doclist): - for i, d in enumerate(doclist): - if isinstance(d, dict): - doclist[i] = Document(fielddata=d) - - self.doclist = frappe.doclist(doclist) - self.doc = self.doclist[0] - - if self.doc.meta.issingle: - self.doc.cast_floats_and_ints() - - if self.obj: - self.obj.doclist = self.doclist - self.obj.doc = self.doc - - def make_controller(self): - if not self.doc.doctype: - raise frappe.DataError("Bean doctype not specified") - if self.obj: - # update doclist before running any method - self.obj.doclist = self.doclist - return self.obj - - self.obj = frappe.get_obj(doc=self.doc, doclist=self.doclist) - self.obj.bean = self - self.controller = self.obj - return self.obj - - def get_controller(self): - return self.make_controller() - - def to_dict(self): - return [d.fields for d in self.doclist] - - def check_if_latest(self, method="save"): - from frappe.model.meta import is_single - - conflict = False - if not cint(self.doc.fields.get('__islocal')): - if is_single(self.doc.doctype): - modified = frappe.db.get_value(self.doc.doctype, self.doc.name, "modified") - if isinstance(modified, list): - modified = modified[0] - if cstr(modified) and cstr(modified) != cstr(self.doc.modified): - conflict = True - else: - tmp = frappe.db.sql("""select modified, docstatus from `tab%s` - where name=%s for update""" - % (self.doc.doctype, '%s'), self.doc.name, as_dict=True) - - if not tmp: - frappe.msgprint("""This record does not exist. Please refresh.""", raise_exception=1) - - modified = cstr(tmp[0].modified) - if modified and modified != cstr(self.doc.modified): - conflict = True - - self.check_docstatus_transition(tmp[0].docstatus, method) - - if conflict: - frappe.msgprint(_("Error: Document has been modified after you have opened it") \ - + (" (%s, %s). " % (modified, self.doc.modified)) \ - + _("Please refresh to get the latest document."), raise_exception=TimestampMismatchError) - - def check_docstatus_transition(self, db_docstatus, method): - valid = { - "save": [0,0], - "submit": [0,1], - "cancel": [1,2], - "update_after_submit": [1,1] - } - - labels = { - 0: _("Draft"), - 1: _("Submitted"), - 2: _("Cancelled") - } - - if not hasattr(self, "to_docstatus"): - self.to_docstatus = 0 - - if method != "runserverobj" and [db_docstatus, self.to_docstatus] != valid[method]: - frappe.msgprint(_("Cannot change from") + ": " + labels[db_docstatus] + " > " + \ - labels[self.to_docstatus], raise_exception=DocstatusTransitionError) - - def update_timestamps_and_docstatus(self): - from frappe.utils import now - ts = now() - user = frappe.__dict__.get('session', {}).get('user') or 'Administrator' - - for d in self.doclist: - if self.doc.fields.get('__islocal'): - if not d.owner: - d.owner = user - if not d.creation: - d.creation = ts - - d.modified_by = user - d.modified = ts - if d.docstatus != 2 and self.to_docstatus >= int(d.docstatus): # don't update deleted - d.docstatus = self.to_docstatus - - def prepare_for_save(self, method): - self.check_if_latest(method) - - self.update_timestamps_and_docstatus() - self.update_parent_info() - - if self.doc.fields.get("__islocal"): - # set name before validate - self.run_method('before_set_name') - self.doc.set_new_name(self) - self.run_method('before_insert') - - if method != "cancel": - self.extract_images_from_text_editor() - - def update_parent_info(self): - idx_map = {} - is_local = cint(self.doc.fields.get("__islocal")) - - if not frappe.flags.in_import: - parentfields = [d.fieldname for d in self.meta.get({"doctype": "DocField", "fieldtype": "Table"})] - - for i, d in enumerate(self.doclist[1:]): - if d.parentfield: - if not frappe.flags.in_import: - if not d.parentfield in parentfields: - frappe.msgprint("Bad parentfield %s" % d.parentfield, - raise_exception=True) - d.parenttype = self.doc.doctype - d.parent = self.doc.name - if not d.idx: - d.idx = idx_map.setdefault(d.parentfield, 0) + 1 - else: - d.idx = cint(d.idx) - if is_local: - # if parent is new, all children should be new - d.fields["__islocal"] = 1 - d.name = None - - idx_map[d.parentfield] = d.idx - - def run_method(self, method, *args, **kwargs): - if not args: - args = [] - self.make_controller() - - def add_to_response(out, new_response): - if isinstance(new_response, dict): - out.update(new_response) - - if hasattr(self.controller, method): - add_to_response(frappe.local.response, - frappe.call(getattr(self.controller, method), *args, **kwargs)) - - self.set_doclist(self.controller.doclist) - - args = [self, method] + list(args) - for handler in frappe.get_hooks("bean_event:" + self.doc.doctype + ":" + method) \ - + frappe.get_hooks("bean_event:*:" + method): - add_to_response(frappe.local.response, frappe.call(frappe.get_attr(handler), *args, **kwargs)) - - return frappe.local.response - - def get_attr(self, method): - self.make_controller() - return getattr(self.controller, method, None) - - def insert(self, ignore_permissions=None): - if ignore_permissions: - self.ignore_permissions = True - - self.doc.fields["__islocal"] = 1 - - self.set_defaults() - - if frappe.flags.in_test: - if self.meta.get_field("naming_series"): - self.doc.naming_series = "_T-" + self.doc.doctype + "-" - - return self.save() - - def insert_or_update(self): - if self.doc.name and frappe.db.exists(self.doc.doctype, self.doc.name): - return self.save() - else: - return self.insert() - - def set_defaults(self): - if frappe.flags.in_import: - return - - new_docs = {} - new_doclist = [] - - for d in self.doclist: - if not d.doctype in new_docs: - new_docs[d.doctype] = frappe.new_doc(d.doctype) - - newd = frappe.doc(new_docs[d.doctype].fields.copy()) - newd.fields.update(d.fields) - new_doclist.append(newd) - - self.set_doclist(new_doclist) - - def has_read_perm(self): - return self.has_permission("read") - - def has_permission(self, permtype): - if self.ignore_permissions: - return True - if self.doc.parent and self.doc.parenttype: - return frappe.has_permission(self.doc.parenttype, permtype, - frappe.doc(self.doc.parenttype, self.doc.parent)) - else: - return frappe.has_permission(self.doc.doctype, permtype, self.doc) - - def save(self, check_links=1, ignore_permissions=None): - if ignore_permissions!=None: - self.ignore_permissions = ignore_permissions - perm_to_check = "write" - if self.doc.fields.get("__islocal"): - perm_to_check = "create" - if not self.doc.owner: - self.doc.owner = frappe.session.user - - self.to_docstatus = 0 - self.prepare_for_save("save") - - # check permissions after preparing for save, since name might be required - if self.has_permission(perm_to_check): - if not self.ignore_validate: - self.run_method('validate') - self.validate_doclist() - self.save_main() - self.save_children() - self.run_method('on_update') - if perm_to_check=="create": - self.run_method("after_insert") - else: - self.no_permission_to(_(perm_to_check.title())) - - return self - - def submit(self): - if self.has_permission("submit"): - self.to_docstatus = 1 - self.prepare_for_save("submit") - self.run_method('validate') - self.validate_doclist() - self.save_main() - self.save_children() - self.run_method('on_update') - self.run_method('on_submit') - else: - self.no_permission_to(_("Submit")) - - return self - - def cancel(self): - if self.has_permission("cancel"): - self.to_docstatus = 2 - self.prepare_for_save("cancel") - self.run_method('before_cancel') - self.save_main() - self.save_children() - self.run_method('on_cancel') - self.check_no_back_links_exist() - else: - self.no_permission_to(_("Cancel")) - - return self - - def update_after_submit(self): - if self.doc.docstatus != 1: - frappe.msgprint("Only to called after submit", raise_exception=1) - if self.has_permission("write"): - self.to_docstatus = 1 - self.prepare_for_save("update_after_submit") - self.run_method('validate') - self.run_method('before_update_after_submit') - self.validate_doclist() - self.save_main() - self.save_children() - self.run_method('on_update_after_submit') - else: - self.no_permission_to(_("Update")) - - return self - - def save_main(self): - try: - self.doc.save(check_links = False, ignore_fields = self.ignore_fields) - except NameError, e: - frappe.msgprint('%s "%s" already exists' % (self.doc.doctype, self.doc.name)) - - # prompt if cancelled - if frappe.db.get_value(self.doc.doctype, self.doc.name, 'docstatus')==2: - frappe.msgprint('[%s "%s" has been cancelled]' % (self.doc.doctype, self.doc.name)) - frappe.errprint(frappe.utils.get_traceback()) - raise - - def save_children(self): - child_map = {} - for d in self.doclist[1:]: - if d.fields.get("parent") or d.fields.get("parentfield"): - d.parent = self.doc.name # rename if reqd - d.parenttype = self.doc.doctype - - d.save(check_links=False, ignore_fields = self.ignore_fields) - - child_map.setdefault(d.doctype, []).append(d.name) - - # delete all children in database that are not in the child_map - - # get all children types - tablefields = frappe.model.meta.get_table_fields(self.doc.doctype) - - for dt in tablefields: - if dt[0] not in self.ignore_children_type: - cnames = child_map.get(dt[0]) or [] - if cnames: - frappe.db.sql("""delete from `tab%s` where parent=%s and parenttype=%s and - name not in (%s)""" % (dt[0], '%s', '%s', ','.join(['%s'] * len(cnames))), - tuple([self.doc.name, self.doc.doctype] + cnames)) - else: - frappe.db.sql("""delete from `tab%s` where parent=%s and parenttype=%s""" \ - % (dt[0], '%s', '%s'), (self.doc.name, self.doc.doctype)) - - def delete(self): - frappe.delete_doc(self.doc.doctype, self.doc.name) - - def no_permission_to(self, ptype): - frappe.msgprint(("%s (%s): " % (self.doc.name, _(self.doc.doctype))) + \ - _("No Permission to ") + ptype, raise_exception=BeanPermissionError) - - def check_no_back_links_exist(self): - from frappe.model.delete_doc import check_if_doc_is_linked - check_if_doc_is_linked(self.doc.doctype, self.doc.name, method="Cancel") - - def check_mandatory(self): - if self.ignore_mandatory: - return - - missing = [] - for doc in self.doclist: - for df in self.meta: - if df.doctype=="DocField" and df.reqd and df.parent==doc.doctype and df.fieldname!="naming_series": - msg = "" - if df.fieldtype == "Table": - if not self.doclist.get({"parentfield": df.fieldname}): - msg = _("Error") + ": " + _("Data missing in table") + ": " + _(df.label) - - elif doc.fields.get(df.fieldname) is None: - msg = _("Error") + ": " - if doc.parentfield: - msg += _("Row") + (" # %s: " % (doc.idx,)) - - msg += _("Value missing for") + ": " + _(df.label) - - if msg: - missing.append([msg, df.fieldname]) - - if missing: - for msg, fieldname in missing: - msgprint(msg) - - raise frappe.MandatoryError, ", ".join([fieldname for msg, fieldname in missing]) - - def extract_images_from_text_editor(self): - from frappe.utils.file_manager import extract_images_from_html - if self.doc.doctype != "DocType": - for df in self.meta.get({"doctype": "DocField", "parent": self.doc.doctype, "fieldtype":"Text Editor"}): - extract_images_from_html(self.doc, df.fieldname) - - def validate_doclist(self): - self.check_mandatory() - self.validate_restrictions() - self.check_links() - - def check_links(self): - if self.ignore_links: - return - ref, err_list = {}, [] - for d in self.doclist: - if not ref.get(d.doctype): - ref[d.doctype] = d.make_link_list() - - err_list += d.validate_links(ref[d.doctype]) - - if err_list: - frappe.msgprint("""[Link Validation] Could not find the following values: %s. - Please correct and resave. Document Not Saved.""" % ', '.join(err_list), raise_exception=1) - - def validate_restrictions(self): - if self.ignore_restrictions: - return - - has_restricted_data = False - for d in self.doclist: - if not frappe.permissions.has_unrestricted_access(d): - has_restricted_data = True - - if has_restricted_data: - raise BeanPermissionError - -def clone(source_wrapper): - """ make a clone of a document""" - if isinstance(source_wrapper, list): - source_wrapper = Bean(source_wrapper) - - new_wrapper = Bean(source_wrapper.doclist.copy()) - - if new_wrapper.doc.fields.get("amended_from"): - new_wrapper.doc.fields["amended_from"] = None - - if new_wrapper.doc.fields.get("amendment_date"): - new_wrapper.doc.fields["amendment_date"] = None - - for d in new_wrapper.doclist: - d.fields.update({ - "name": None, - "__islocal": 1, - "docstatus": 0, - }) - - return new_wrapper - -# for bc -def getlist(doclist, parentfield): - import frappe.model.utils - return frappe.model.utils.getlist(doclist, parentfield) - -def copy_doclist(doclist, no_copy = []): - """ - Make a copy of the doclist - """ - import frappe.model.utils - return frappe.model.utils.copy_doclist(doclist, no_copy) - diff --git a/frappe/model/code.py b/frappe/model/code.py deleted file mode 100644 index 9972cbfc62..0000000000 --- a/frappe/model/code.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals -""" -This is where all the plug-in code is executed. The standard method for DocTypes is declaration of a -standardized `DocType` class that has the methods of any DocType. When an object is instantiated using the -`get_obj` method, it creates an instance of the `DocType` class of that particular DocType and sets the -`doc` and `doclist` attributes that represent the fields (properties) of that record. - -methods in following modules are imported for backward compatibility - - * frappe.* - * frappe.utils.* - * frappe.model.doc.* - * frappe.model.bean.* -""" - -import frappe -from frappe.modules import get_doctype_module, load_doctype_module, get_module_name -import frappe.model.doc - -def get_obj(dt = None, dn = None, doc=None, doclist=None, with_children = 0): - import frappe.model.doc - if not doclist: doclist = [] - if dt: - if isinstance(dt, list): - return get_server_obj(dt[0], dt) - if isinstance(dt, frappe.model.doc.Document): - return get_server_obj(dt, [dt]) - if not dn: - dn = dt - if with_children: - doclist = frappe.model.doc.get(dt, dn, from_controller=1) - else: - doclist = frappe.model.doc.get(dt, dn, with_children = 0, from_controller=1) - return get_server_obj(doclist[0], doclist) - else: - return get_server_obj(doc, doclist) - -def get_server_obj(doc, doclist = [], basedoctype = ''): - # for test - module = get_doctype_module(doc.doctype) - return load_doctype_module(doc.doctype, module).DocType(doc, doclist) - -def run_server_obj(server_obj, method_name, arg=None): - """ - Executes a method (`method_name`) from the given object (`server_obj`) - """ - if server_obj and hasattr(server_obj, method_name): - if arg: - return getattr(server_obj, method_name)(arg) - else: - return getattr(server_obj, method_name)() - else: - raise Exception, 'No method %s' % method_name diff --git a/frappe/model/controller.py b/frappe/model/controller.py index a023809fc8..3a8ed70990 100644 --- a/frappe/model/controller.py +++ b/frappe/model/controller.py @@ -6,22 +6,16 @@ import frappe from frappe import msgprint, _ from frappe.utils import flt, cint, cstr from frappe.model.meta import get_field_precision +from frappe.model.document import Document class EmptyTableError(frappe.ValidationError): pass -class DocListController(object): - def __init__(self, doc, doclist): - self.doc, self.doclist = doc, doclist - +class DocListController(Document): + def __init__(self, arg1, arg2=None): + super(DocListController, self).__init__(arg1, arg2) if hasattr(self, "setup"): self.setup() - - @property - def meta(self): - if not hasattr(self, "_meta"): - self._meta = frappe.get_doctype(self.doc.doctype) - return self._meta - + def validate_value(self, fieldname, condition, val2, doc=None, raise_exception=None): """check that value of fieldname should be 'condition' val2 else throw exception""" @@ -38,11 +32,11 @@ class DocListController(object): } if not doc: - doc = self.doc + doc = self - df = self.meta.get_field(fieldname, parent=doc.doctype) + df = doc.meta.get_field(fieldname) - val1 = doc.fields.get(fieldname) + val1 = doc.get(fieldname) if df.fieldtype in ("Currency", "Float"): val1 = flt(val1, self.precision(df.fieldname, doc.parentfield or None)) @@ -60,50 +54,42 @@ class DocListController(object): if doc.parentfield: msg += _("Row") + (" # %d: " % doc.idx) - msg += _(self.meta.get_label(fieldname, parent=doc.doctype)) \ + msg += _(doc.meta.get_label(fieldname)) \ + " " + error_condition_map.get(condition, "") + " " + cstr(val2) # raise passed exception or True msgprint(msg, raise_exception=raise_exception or True) def validate_table_has_rows(self, parentfield, raise_exception=None): - if not self.doclist.get({"parentfield": parentfield}): + if not self.get(parentfield): label = self.meta.get_label(parentfield) msgprint(_("Error") + ": " + _(label) + " " + _("cannot be empty"), raise_exception=raise_exception or EmptyTableError) def round_floats_in(self, doc, fieldnames=None): if not fieldnames: - fieldnames = [df.fieldname for df in self.meta.get({"doctype": "DocField", "parent": doc.doctype, - "fieldtype": ["in", ["Currency", "Float"]]})] + fieldnames = [df.fieldname for df in doc.meta.get("fields", + {"fieldtype": ["in", ["Currency", "Float"]]})] for fieldname in fieldnames: - doc.fields[fieldname] = flt(doc.fields.get(fieldname), self.precision(fieldname, doc.parentfield)) + doc.set(fieldname, flt(doc.get(fieldname), self.precision(fieldname, doc.parentfield))) - def _process(self, parentfield): - from frappe.model.doc import Document - if isinstance(parentfield, Document): - parentfield = parentfield.parentfield - - elif isinstance(parentfield, dict): - parentfield = parentfield.get("parentfield") - - return parentfield - def precision(self, fieldname, parentfield=None): - parentfield = self._process(parentfield) + if parentfield and not isinstance(parentfield, basestring): + parentfield = parentfield.parentfield if not hasattr(self, "_precision"): self._precision = frappe._dict({ "default": cint(frappe.db.get_default("float_precision")) or 3, "options": {} }) - + if self._precision.setdefault(parentfield or "main", {}).get(fieldname) is None: - df = self.meta.get_field(fieldname, parentfield=parentfield) + meta = frappe.get_meta(self.meta.get_field(parentfield).options if parentfield else self.doctype) + df = meta.get_field(fieldname) if df.fieldtype == "Currency" and df.options and not self._precision.options.get(df.options): - self._precision.options[df.options] = get_field_precision(df, self.doc) + self._precision.options[df.options] = get_field_precision(df, self) if df.fieldtype == "Currency": self._precision[parentfield or "main"][fieldname] = cint(self._precision.options.get(df.options)) or \ diff --git a/frappe/model/create_new.py b/frappe/model/create_new.py index 4e909bd56e..6d8842e038 100644 --- a/frappe/model/create_new.py +++ b/frappe/model/create_new.py @@ -11,15 +11,13 @@ from frappe.utils import nowdate, nowtime, cint, flt import frappe.defaults def get_new_doc(doctype, parent_doc = None, parentfield = None): - doc = frappe.doc({ + doc = frappe.get_doc({ "doctype": doctype, "__islocal": 1, "owner": frappe.session.user, "docstatus": 0 }) - meta = frappe.get_doctype(doctype) - restrictions = frappe.defaults.get_restrictions() if parent_doc: @@ -31,39 +29,38 @@ def get_new_doc(doctype, parent_doc = None, parentfield = None): defaults = frappe.defaults.get_defaults() - for d in meta.get({"doctype":"DocField", "parent": doctype}): + for d in doc.meta.get("fields"): default = defaults.get(d.fieldname) if (d.fieldtype=="Link") and d.ignore_restrictions != 1 and (d.options in restrictions)\ and len(restrictions[d.options])==1: - doc.fields[d.fieldname] = restrictions[d.options][0] + doc.set(d.fieldname, restrictions[d.options][0]) elif default: - doc.fields[d.fieldname] = default - elif d.fields.get("default"): + doc.set(d.fieldname, default) + elif d.get("default"): if d.default == "__user": - doc.fields[d.fieldname] = frappe.session.user + doc.set(d.fieldname, frappe.session.user) elif d.default == "Today": - doc.fields[d.fieldname] = nowdate() + doc.set(d.fieldname, nowdate()) elif d.default.startswith(":"): - ref_fieldname = d.default[1:].lower().replace(" ", "_") + ref_doctype = d.default[1:] + ref_fieldname = ref_doctype.lower().replace(" ", "_") if parent_doc: - ref_docname = parent_doc.fields[ref_fieldname] + ref_docname = parent_doc.get(ref_fieldname) else: ref_docname = frappe.db.get_default(ref_fieldname) - doc.fields[d.fieldname] = frappe.db.get_value(d.default[1:], - ref_docname, d.fieldname) - + doc.set(d.fieldname, frappe.db.get_value(ref_doctype, ref_docname, d.fieldname)) else: - doc.fields[d.fieldname] = d.default + doc.set(d.fieldname, d.default) # convert type of default if d.fieldtype in ("Int", "Check"): - doc.fields[d.fieldname] = cint(doc.fields[d.fieldname]) + doc.set(d.fieldname, cint(doc.get(d.fieldname))) elif d.fieldtype in ("Float", "Currency"): - doc.fields[d.fieldname] = flt(doc.fields[d.fieldname]) + doc.set(d.fieldname, flt(doc.get(d.fieldname))) elif d.fieldtype == "Time": - doc.fields[d.fieldname] = nowtime() + doc.set(d.fieldname, nowtime()) return doc diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 96a13b97e8..1c9c263c6a 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -7,7 +7,6 @@ from __future__ import unicode_literals import frappe, json import frappe.defaults import frappe.permissions -import frappe.model.doctype from frappe.utils import cstr, flt class DatabaseQuery(object): diff --git a/frappe/model/db_schema.py b/frappe/model/db_schema.py index b948c07c88..19a6e4085b 100644 --- a/frappe/model/db_schema.py +++ b/frappe/model/db_schema.py @@ -1,5 +1,5 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt from __future__ import unicode_literals """ @@ -24,7 +24,7 @@ type_map = { ,'Text Editor': ('text', '') ,'Date': ('date', '') ,'Datetime': ('datetime', '6') - ,'Time': ('time', '') + ,'Time': ('time', '6') ,'Text': ('text', '') ,'Data': ('varchar', '255') ,'Link': ('varchar', '255') @@ -53,42 +53,42 @@ class DbTable: self.name = prefix + doctype self.columns = {} self.current_columns = {} - + # lists for change self.add_column = [] self.change_type = [] self.add_index = [] self.drop_index = [] self.set_default = [] - + # load self.get_columns_from_docfields() def create(self): add_text = '' - + # columns column_defs = self.get_column_definitions() if column_defs: add_text += ',\n'.join(column_defs) + ',\n' - + # index index_defs = self.get_index_definitions() if index_defs: add_text += ',\n'.join(index_defs) + ',\n' - + # create table frappe.db.sql("""create table `%s` ( - name varchar(255) not null primary key, + name varchar(255) not null primary key, creation datetime(6), modified datetime(6), - modified_by varchar(40), + modified_by varchar(40), owner varchar(60), - docstatus int(1) default '0', + docstatus int(1) default '0', parent varchar(255), - parentfield varchar(255), - parenttype varchar(255), + parentfield varchar(255), + parenttype varchar(255), idx int(8), - %sindex parent(parent)) - ENGINE=InnoDB + %sindex parent(parent)) + ENGINE=InnoDB CHARACTER SET=utf8""" % (self.name, add_text)) def get_columns_from_docfields(self): @@ -96,7 +96,7 @@ class DbTable: get columns from docfields and custom fields """ fl = frappe.db.sql("SELECT * FROM tabDocField WHERE parent = %s", self.doctype, as_dict = 1) - + try: custom_fl = frappe.db.sql("""\ SELECT * FROM `tabCustom Field` @@ -110,12 +110,12 @@ class DbTable: self.columns[f['fieldname']] = DbColumn(self, f['fieldname'], f['fieldtype'], f.get('length'), f.get('default'), f.get('search_index'), f.get('options')) - + def get_columns_from_db(self): self.show_columns = frappe.db.sql("desc `%s`" % self.name) for c in self.show_columns: self.current_columns[c[0]] = {'name': c[0], 'type':c[1], 'index':c[3], 'default':c[4]} - + def get_column_definitions(self): column_list = [] + default_columns ret = [] @@ -126,7 +126,7 @@ class DbTable: ret.append('`'+ k+ '` ' + d) column_list.append(k) return ret - + def get_index_definitions(self): ret = [] for key, col in self.columns.items(): @@ -155,24 +155,24 @@ class DbTable: return fk_list = self.get_foreign_keys() - + # make dictionary of constraint names fk_dict = {} for f in fk_list: fk_dict[f[0]] = f[1] - + # drop for col in self.drop_foreign_key: frappe.db.sql("set foreign_key_checks=0") frappe.db.sql("alter table `%s` drop foreign key `%s`" % (self.name, fk_dict[col.fieldname])) frappe.db.sql("set foreign_key_checks=1") - + def sync(self): if not self.name in DbManager(frappe.db).get_tables_list(frappe.db.cur_db_name): self.create() else: self.alter() - + def alter(self): self.get_columns_from_db() for col in self.columns.values(): @@ -186,14 +186,14 @@ class DbTable: for col in self.add_index: # if index key not exists - if not frappe.db.sql("show index from `%s` where key_name = %s" % + 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)) 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" % + 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)) @@ -216,16 +216,16 @@ class DbColumn: if with_default and self.default and (self.default not in default_shortcuts) \ and not self.default.startswith(":") and ret not in ['text', 'longtext']: ret += ' default "' + self.default.replace('"', '\"') + '"' - + return ret - + def check(self, current_def): column_def = self.get_definition(0) # no columns if not column_def: return - + # to add? if not current_def: self.fieldname = validate_column_name(self.fieldname) @@ -235,15 +235,15 @@ class DbColumn: # type if current_def['type'] != column_def: self.table.change_type.append(self) - + # index else: if (current_def['index'] and not self.set_index): self.table.drop_index.append(self) - + if (not current_def['index'] and self.set_index and not (column_def in ['text', 'longtext'])): self.table.add_index.append(self) - + # default if (self.default_changed(current_def) and (self.default not in default_shortcuts) and not cstr(self.default).startswith(":") and not (column_def in ['text','longtext'])): self.table.set_default.append(self) @@ -260,40 +260,40 @@ class DbColumn: class DbManager: """ - Basically, a wrapper for oft-used mysql commands. like show tables,databases, variables etc... + Basically, a wrapper for oft-used mysql commands. like show tables,databases, variables etc... #TODO: - 0. Simplify / create settings for the restore database source folder + 0. Simplify / create settings for the restore database source folder 0a. Merge restore database and extract_sql(from frappe_server_tools). 1. Setter and getter for different mysql variables. 2. Setter and getter for mysql variables at global level?? - """ + """ def __init__(self,db): """ Pass root_conn here for access to all databases. """ if db: self.db = db - + def get_variables(self,regex): """ Get variables that match the passed pattern regex """ return list(self.db.sql("SHOW VARIABLES LIKE '%s'"%regex)) - + def get_table_schema(self,table): """ Just returns the output of Desc tables. """ return list(self.db.sql("DESC `%s`"%table)) - - + + def get_tables_list(self,target=None): """get list of tables""" if target: self.db.use(target) - + return [t[0] for t in self.db.sql("SHOW TABLES")] def create_user(self,user,password): @@ -357,7 +357,7 @@ class DbManager: def restore_database(self,target,source,user,password): from frappe.utils import make_esc esc = make_esc('$ ') - + try: ret = os.system("mysql -u %s -p%s %s < %s" % \ (esc(user), esc(password), esc(target), source)) @@ -371,7 +371,7 @@ class DbManager: try: self.db.sql("DROP TABLE IF EXISTS %s "%(table_name)) except Exception,e: - raise + raise def set_transaction_isolation_level(self,scope='SESSION',level='READ COMMITTED'): #Sets the transaction isolation level. scope = global/session @@ -399,7 +399,7 @@ def updatedb(dt): res = frappe.db.sql("select ifnull(issingle, 0) from tabDocType where name=%s", (dt,)) if not res: raise Exception, 'Wrong doctype "%s" in updatedb' % dt - + if not res[0][0]: frappe.db.commit() tab = DbTable(dt, 'tab') @@ -418,7 +418,7 @@ def remove_all_foreign_keys(): fklist = [] else: raise - + for f in fklist: frappe.db.sql("alter table `tab%s` drop foreign key `%s`" % (t[0], f[1])) @@ -427,7 +427,7 @@ def get_definition(fieldtype): if not d: return - + ret = d[0] if d[1]: ret += '(' + d[1] + ')' @@ -436,6 +436,6 @@ def get_definition(fieldtype): def add_column(doctype, column_name, fieldtype): frappe.db.commit() - frappe.db.sql("alter table `tab%s` add column %s %s" % (doctype, + frappe.db.sql("alter table `tab%s` add column %s %s" % (doctype, column_name, get_definition(fieldtype))) - + diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 902837b33b..875e6f4fbf 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -9,7 +9,7 @@ import frappe.defaults from frappe.utils.file_manager import remove_all from frappe import _ -def delete_doc(doctype=None, name=None, doclist = None, force=0, ignore_doctypes=None, for_reload=False, ignore_permissions=False): +def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False, ignore_permissions=False): """ Deletes a doc(dt, dn) and validates if it is not submitted and not linked in a live record """ @@ -19,7 +19,7 @@ def delete_doc(doctype=None, name=None, doclist = None, force=0, ignore_doctypes if not doctype: doctype = frappe.form_dict.get('dt') name = frappe.form_dict.get('dn') - + if not doctype: frappe.msgprint('Nothing to delete!', raise_exception =1) @@ -27,71 +27,67 @@ def delete_doc(doctype=None, name=None, doclist = None, force=0, ignore_doctypes if not frappe.db.exists(doctype, name): return + doc = frappe.get_doc(doctype, name) + if not for_reload: - check_permission_and_not_submitted(doctype, name, ignore_permissions) - - run_on_trash(doctype, name, doclist) + check_permission_and_not_submitted(doc, ignore_permissions) + doc.run_method("on_trash") # check if links exist if not force: - check_if_doc_is_linked(doctype, name) - + check_if_doc_is_linked(doc) + try: - tablefields = frappe.model.meta.get_table_fields(doctype) - frappe.db.sql("delete from `tab%s` where name=%s" % (doctype, "%s"), (name,)) - for t in tablefields: - if t[0] not in ignore_doctypes: - frappe.db.sql("delete from `tab%s` where parent = %s" % (t[0], '%s'), (name,)) + if doctype!="DocType" and doctype==name: + frappe.db.sql("delete from `tabSingles` where doctype=%s", name) + else: + frappe.db.sql("delete from `tab%s` where name=%s" % (doctype, "%s"), (name,)) + + for t in doc.meta.get_table_fields(): + if t.options not in ignore_doctypes: + frappe.db.sql("delete from `tab%s` where parent = %s" % (t.options, '%s'), (name,)) + except Exception, e: if e.args[0]==1451: frappe.msgprint("Cannot delete %s '%s' as it is referenced in another record. You must delete the referred record first" % (doctype, name)) - + raise - + # delete attachments remove_all(doctype, name) - + # delete restrictions frappe.defaults.clear_default(parenttype="Restriction", key=doctype, value=name) - + return 'okay' -def check_permission_and_not_submitted(doctype, name, ignore_permissions=False): +def check_permission_and_not_submitted(doc, ignore_permissions=False): # permission - if not ignore_permissions and frappe.session.user!="Administrator" and not frappe.has_permission(doctype, "cancel"): + if not ignore_permissions and frappe.session.user!="Administrator" and not doc.has_permission("cancel"): frappe.msgprint(_("User not allowed to delete."), raise_exception=True) # check if submitted - if frappe.db.get_value(doctype, name, "docstatus") == 1: - frappe.msgprint(_("Submitted Record cannot be deleted")+": "+name+"("+doctype+")", + if doc.docstatus == 1: + frappe.msgprint(_("Submitted Record cannot be deleted")+": "+doc.name+"("+doc.doctype+")", raise_exception=True) -def run_on_trash(doctype, name, doclist): - # call on_trash if required - if doclist: - bean = frappe.bean(doclist) - else: - bean = frappe.bean(doctype, name) - - bean.run_method("on_trash") - class LinkExistsError(frappe.ValidationError): pass -def check_if_doc_is_linked(dt, dn, method="Delete"): +def check_if_doc_is_linked(doc, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ from frappe.model.rename_doc import get_link_fields - link_fields = get_link_fields(dt) + link_fields = get_link_fields(doc.doctype) link_fields = [[lf['parent'], lf['fieldname'], lf['issingle']] for lf in link_fields] for link_dt, link_field, issingle in link_fields: if not issingle: - item = frappe.db.get_value(link_dt, {link_field:dn}, + item = frappe.db.get_value(link_dt, {link_field:doc.name}, ["name", "parent", "parenttype", "docstatus"], as_dict=True) - - if item and item.parent != dn and ((method=="Delete" and item.docstatus<2) or + + if item and item.parent != doc.name and ((method=="Delete" and item.docstatus<2) or (method=="Cancel" and item.docstatus==1)): frappe.msgprint(method + " " + _("Error") + ":"+\ - ("%s (%s) " % (dn, dt)) + _("is linked in") + (" %s (%s)") % + ("%s (%s) " % (doc.name, doc.doctype)) + _("is linked in") + (" %s (%s)") % (item.parent or item.name, item.parent and item.parenttype or link_dt), raise_exception=LinkExistsError) diff --git a/frappe/model/doc.py b/frappe/model/doc.py deleted file mode 100755 index 144e3b6700..0000000000 --- a/frappe/model/doc.py +++ /dev/null @@ -1,772 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals -""" -Contains the Document class representing an object / record -""" - -import frappe -from frappe import _ -import frappe.model.meta -from frappe.utils import * - -class Document: - """ - The frappe(meta-data)framework equivalent of a Database Record. - Stores,Retrieves,Updates the record in the corresponding table. - Runs the triggers required. - - The `Document` class represents the basic Object-Relational Mapper (ORM). The object type is defined by - `DocType` and the object ID is represented by `name`:: - - Please note the anamoly in the Web Notes Framework that `ID` is always called as `name` - - If both `doctype` and `name` are specified in the constructor, then the object is loaded from the database. - If only `doctype` is given, then the object is not loaded - If `fielddata` is specfied, then the object is created from the given dictionary. - - **Note 1:** - - The getter and setter of the object are overloaded to map to the fields of the object that - are loaded when it is instantiated. - - For example: doc.name will be the `name` field and doc.owner will be the `owner` field - - **Note 2 - Standard Fields:** - - * `name`: ID / primary key - * `owner`: creator of the record - * `creation`: datetime of creation - * `modified`: datetime of last modification - * `modified_by` : last updating user - * `docstatus` : Status 0 - Saved, 1 - Submitted, 2- Cancelled - * `parent` : if child (table) record, this represents the parent record - * `parenttype` : type of parent record (if any) - * `parentfield` : table fieldname of parent record (if any) - * `idx` : Index (sequence) of the child record - """ - - def __init__(self, doctype = None, name = None, fielddata = None): - self._roles = [] - self._perms = [] - self._user_defaults = {} - self._new_name_set = False - self._meta = None - - if isinstance(doctype, dict): - fielddata = doctype - doctype = None - if doctype and isinstance(name, dict): - name = frappe.db.get_value(doctype, name, "name") or None - - if fielddata: - self.fields = fielddata - else: - self.fields = {} - - if not self.fields.has_key('name'): - self.fields['name']='' # required on save - if not self.fields.has_key('doctype'): - self.fields['doctype']='' # required on save - if not self.fields.has_key('owner'): - self.fields['owner']='' # required on save - - if doctype: - self.fields['doctype'] = doctype - if name: - self.fields['name'] = name - self.__initialized = 1 - - if (doctype and name): - self._loadfromdb(doctype, name) - else: - if not fielddata: - self.fields['__islocal'] = 1 - - if not self.fields.get("docstatus"): - self.fields["docstatus"] = 0 - - def __nonzero__(self): - return True - - def __str__(self): - return str(self.fields) - - def __repr__(self): - return repr(self.fields) - - def __unicode__(self): - return unicode(self.fields) - - def __eq__(self, other): - if isinstance(other, Document): - return self.fields == other.fields - else: - return False - - def __getstate__(self): - return self.fields - - def __setstate__(self, d): - self.fields = d - - def encode(self, encoding='utf-8'): - """convert all unicode values to utf-8""" - from frappe.utils import encode_dict - encode_dict(self.fields) - - def _loadfromdb(self, doctype = None, name = None): - if name: self.name = name - if doctype: self.doctype = doctype - - is_single = False - try: - is_single = frappe.model.meta.is_single(self.doctype) - except Exception, e: - pass - - if is_single: - self._loadsingle() - else: - try: - dataset = frappe.db.sql('select * from `tab%s` where name=%s' % - (self.doctype, "%s"), self.name) - except frappe.SQLError, e: - if e.args[0]==1146: - dataset = None - else: - raise - - if not dataset: - raise frappe.DoesNotExistError, '[WNF] %s %s does not exist' % (self.doctype, self.name) - self._load_values(dataset[0], frappe.db.get_description()) - - def is_new(self): - return self.fields.get("__islocal") - - def _load_values(self, data, description): - if '__islocal' in self.fields: - del self.fields['__islocal'] - for i in range(len(description)): - v = data[i] - self.fields[description[i][0]] = frappe.db.convert_to_simple_type(v) - - def _merge_values(self, data, description): - for i in range(len(description)): - v = data[i] - if v: # only if value, over-write - self.fields[description[i][0]] = frappe.db.convert_to_simple_type(v) - - def _loadsingle(self): - self.name = self.doctype - self.fields.update(getsingle(self.doctype)) - self.cast_floats_and_ints() - - def cast_floats_and_ints(self): - for df in frappe.get_doctype(self.doctype).get_docfields(): - if df.fieldtype in ("Int", "Check"): - self.fields[df.fieldname] = cint(self.fields.get(df.fieldname)) - elif df.fieldtype in ("Float", "Currency"): - self.fields[df.fieldname] = flt(self.fields.get(df.fieldname)) - - if self.docstatus is not None: - self.docstatus = cint(self.docstatus) - - def __setattr__(self, name, value): - # normal attribute - if not self.__dict__.has_key('_Document__initialized'): - self.__dict__[name] = value - elif self.__dict__.has_key(name): - self.__dict__[name] = value - else: - # field attribute - f = self.__dict__['fields'] - f[name] = value - - def __getattr__(self, name): - if self.__dict__.has_key(name): - return self.__dict__[name] - elif self.fields.has_key(name): - return self.fields[name] - else: - if name.startswith("__"): - raise AttributeError() - return None - - def get(self, name, value=None): - return self.fields.get(name, value) - - def update(self, d): - self.fields.update(d) - return self - - def update_if_missing(self, d): - fields = self.get_valid_fields() - for key, value in d.iteritems(): - if (key in fields) and (not self.fields.get(key)): - self.fields[key] = value - - def insert(self): - self.fields['__islocal'] = 1 - self.save() - return self - - def save(self, new=0, check_links=1, ignore_fields=0, make_autoname=1, - keep_timestamps=False): - - if new: - self.fields["__islocal"] = 1 - - # add missing parentinfo (if reqd) - if self.parent and not (self.parenttype and self.parentfield): - self.update_parentinfo() - - if self.parent and not self.idx: - self.set_idx() - - # if required, make new - if not self.meta.issingle: - if self.is_new(): - r = self._insert(make_autoname=make_autoname, keep_timestamps = keep_timestamps) - if r: - return r - else: - if not frappe.db.exists(self.doctype, self.name): - frappe.msgprint(frappe._("Cannot update a non-exiting record, try inserting.") + ": " + self.doctype + " / " + self.name, - raise_exception=1) - - - # save the values - self._update_values(self.meta.issingle, - check_links and self.make_link_list() or {}, ignore_fields=ignore_fields, - keep_timestamps=keep_timestamps) - self._clear_temp_fields() - - - def _get_amended_name(self): - am_id = 1 - am_prefix = self.amended_from - if frappe.db.get_value(self.doctype, self.amended_from, "amended_from"): - am_id = cint(self.amended_from.split('-')[-1]) + 1 - am_prefix = '-'.join(self.amended_from.split('-')[:-1]) # except the last hyphen - - self.name = am_prefix + '-' + str(am_id) - - def set_new_name(self, bean=None): - if self._new_name_set: - # already set by bean - return - - self._new_name_set = True - - autoname = self.meta.autoname - - self.localname = self.name - - - # amendments - if self.amended_from: - return self._get_amended_name() - - # by method - else: - # get my object - if not bean: - bean = frappe.bean([self]) - - bean.run_method("autoname") - if self.name and self.localname != self.name: - return - - # based on a field - if autoname and autoname.startswith('field:'): - n = self.fields[autoname[6:]] - if not n: - raise Exception, 'Name is required' - self.name = n.strip() - - elif autoname and autoname.startswith("naming_series:"): - self.set_naming_series() - if not self.naming_series: - frappe.msgprint(frappe._("Naming Series mandatory"), raise_exception=True) - self.name = make_autoname(self.naming_series+'.#####') - - # call the method! - elif autoname and autoname!='Prompt': - self.name = make_autoname(autoname, self.doctype) - - # given - elif self.fields.get('__newname',''): - self.name = self.fields['__newname'] - - # default name for table - elif self.meta.istable: - self.name = make_autoname('#########', self.doctype) - - # unable to determine a name, use global series - if not self.name: - self.name = make_autoname('#########', self.doctype) - - def set_naming_series(self): - if not self.naming_series: - # pick default naming series - self.naming_series = get_default_naming_series(self.doctype) - - def append_number_if_name_exists(self): - if frappe.db.exists(self.doctype, self.name): - last = frappe.db.sql("""select name from `tab{}` - where name regexp '{}-[[:digit:]]+' - order by name desc limit 1""".format(self.doctype, self.name)) - - if last: - count = str(cint(last[0][0].rsplit("-", 1)[1]) + 1) - else: - count = "1" - - self.name = "{0}-{1}".format(self.name, count) - - def set(self, key, value): - self.modified = now() - self.modified_by = frappe.session["user"] - frappe.db.set_value(self.doctype, self.name, key, value, self.modified, self.modified_by) - self.fields[key] = value - - def _insert(self, make_autoname=True, keep_timestamps=False): - # set name - if make_autoname: - self.set_new_name() - - # validate name - self.name = validate_name(self.doctype, self.name, self.meta.name_case) - - # insert! - if not keep_timestamps: - if not self.owner: - self.owner = frappe.session['user'] - self.modified_by = frappe.session['user'] - if not self.creation: - self.creation = self.modified = now() - else: - self.modified = now() - - frappe.db.sql("insert into `tab%(doctype)s`" % self.fields \ - + """ (name, owner, creation, modified, modified_by) - values (%(name)s, %(owner)s, %(creation)s, %(modified)s, - %(modified_by)s)""", self.fields) - - def _update_single(self, link_list): - self.modified = now() - update_str, values = [], [] - - frappe.db.sql("delete from tabSingles where doctype=%s", self.doctype) - for f in self.fields.keys(): - if not (f in ('modified', 'doctype', 'name', 'perm', 'localname', 'creation'))\ - and (not f.startswith('__')): # fields not saved - - # validate links - if link_list and link_list.get(f): - self.fields[f] = self._validate_link(link_list, f) - - if self.fields[f]==None: - update_str.append("(%s,%s,NULL)") - values.append(self.doctype) - values.append(f) - else: - update_str.append("(%s,%s,%s)") - values.append(self.doctype) - values.append(f) - values.append(self.fields[f]) - frappe.db.sql("insert into tabSingles(doctype, field, value) values %s" % (', '.join(update_str)), values) - - def validate_links(self, link_list): - err_list = [] - for f in self.fields.keys(): - # validate links - old_val = self.fields[f] - if link_list and link_list.get(f): - self.fields[f] = self._validate_link(link_list, f) - - if old_val and not self.fields[f]: - err_list.append("{}: {}".format(link_list[f][1], old_val)) - - return err_list - - def make_link_list(self): - res = frappe.model.meta.get_link_fields(self.doctype) - - link_list = {} - for i in res: link_list[i[0]] = (i[1], i[2]) # options, label - return link_list - - def _validate_link(self, link_list, f): - dt = link_list[f][0] - dn = self.fields.get(f) - - if not dt: - frappe.throw("Options not set for link field: " + f) - - if not dt: return dn - if not dn: return None - if dt=="[Select]": return dn - if dt.lower().startswith('link:'): - dt = dt[5:] - if '\n' in dt: - dt = dt.split('\n')[0] - tmp = frappe.db.sql("""SELECT name FROM `tab%s` - WHERE name = %s""" % (dt, '%s'), (dn,)) - return tmp and tmp[0][0] or ''# match case - - def _update_values(self, issingle, link_list, ignore_fields=0, keep_timestamps=False): - self.validate_constants() - if issingle: - self._update_single(link_list) - else: - update_str, values = [], [] - # set modified timestamp - if self.modified and not keep_timestamps: - self.modified = now() - self.modified_by = frappe.session['user'] - - fields_list = ignore_fields and self.get_valid_fields() or self.fields.keys() - - for f in fields_list: - if (not (f in ('doctype', 'name', 'perm', 'localname', - 'creation','_user_tags', "file_list", "_comments"))) and (not f.startswith('__')): - # fields not saved - - # validate links - if link_list and link_list.get(f): - self.fields[f] = self._validate_link(link_list, f) - - if self.fields.get(f) is None or self.fields.get(f)=='': - update_str.append("`%s`=NULL" % f) - else: - values.append(self.fields.get(f)) - update_str.append("`%s`=%s" % (f, '%s')) - if values: - values.append(self.name) - r = frappe.db.sql("update `tab%s` set %s where name=%s" % \ - (self.doctype, ', '.join(update_str), "%s"), values) - - def get_valid_fields(self): - import frappe.model.doctype - - if getattr(frappe.local, "valid_fields_map", None) is None: - frappe.local.valid_fields_map = {} - - valid_fields_map = frappe.local.valid_fields_map - - if not valid_fields_map.get(self.doctype): - if cint(self.meta.issingle): - doctypelist = frappe.model.doctype.get(self.doctype) - valid_fields_map[self.doctype] = doctypelist.get_fieldnames({ - "fieldtype": ["not in", frappe.model.no_value_fields]}) - else: - valid_fields_map[self.doctype] = \ - frappe.db.get_table_columns(self.doctype) - - return valid_fields_map.get(self.doctype) - - def validate_constants(self): - if frappe.flags.in_import: - return - - meta = frappe.get_doctype(self.doctype) - constants = [d.fieldname for d in meta.get({"set_only_once": 1})] - if constants: - values = frappe.db.get_value(self.doctype, self.name, constants, as_dict=True) - - for fieldname in constants: - if self.fields.get(fieldname) != values.get(fieldname): - frappe.throw("{0}: {1}".format(_("Value cannot be changed for"), - _(meta.get_field(fieldname).label)), frappe.CannotChangeConstantError) - - @property - def meta(self): - if not self._meta: - self._meta = frappe.db.get_value("DocType", self.doctype, "*", as_dict=True) \ - or frappe._dict() - - return self._meta - - def update_parentinfo(self): - """update parent type and parent field, if not explicitly specified""" - - tmp = frappe.db.sql("""select parent, fieldname from tabDocField - where fieldtype='Table' and options=%s""", (self.doctype,)) - - if len(tmp)==0: - raise Exception, 'Incomplete parent info in child table (%s, %s)' \ - % (self.doctype, self.fields.get('name', '[new]')) - - elif len(tmp)>1: - raise Exception, 'Ambiguous parent info (%s, %s)' \ - % (self.doctype, self.fields.get('name', '[new]')) - - else: - self.parenttype = tmp[0][0] - self.parentfield = tmp[0][1] - - def set_idx(self): - """set idx""" - self.idx = (frappe.db.sql("""select max(idx) from `tab%s` - where parent=%s and parentfield=%s""" % (self.doctype, '%s', '%s'), - (self.parent, self.parentfield))[0][0] or 0) + 1 - - def _clear_temp_fields(self): - # clear temp stuff - keys = self.fields.keys() - for f in keys: - if f.startswith('__'): - del self.fields[f] - - def clear_table(self, doclist, tablefield, save=0): - """ - Clears the child records from the given `doclist` for a particular `tablefield` - """ - from frappe.model.utils import getlist - - table_list = getlist(doclist, tablefield) - - delete_list = [d.name for d in table_list] - - if delete_list: - #filter doclist - doclist = filter(lambda d: d.name not in delete_list, doclist) - - # delete from db - frappe.db.sql("""\ - delete from `tab%s` - where parent=%s and parenttype=%s""" - % (table_list[0].doctype, '%s', '%s'), - (self.name, self.doctype)) - - self.fields['__unsaved'] = 1 - - return frappe.doclist(doclist) - - def addchild(self, fieldname, childtype = '', doclist=None): - """ - Returns a child record of the give `childtype`. - - * if local is set, it does not save the record - * if doclist is passed, it append the record to the doclist - """ - from frappe.model.doc import Document - d = Document() - d.parent = self.name - d.parenttype = self.doctype - d.parentfield = fieldname - d.doctype = childtype - d.docstatus = 0; - d.name = '' - d.owner = frappe.session['user'] - d.fields['__islocal'] = 1 # for Client to identify unsaved doc - - if doclist != None: - doclist.append(d) - - return d - - def get_values(self): - """get non-null fields dict withouth standard fields""" - from frappe.model import default_fields - ret = {} - for key in self.fields: - if key not in default_fields and self.fields[key]: - ret[key] = self.fields[key] - - return ret - - def get_db_value(self, key): - return frappe.db.get_value(self.doctype, self.name, key) - -def addchild(parent, fieldname, childtype = '', doclist=None): - """ - - Create a child record to the parent doc. - - Example:: - - c = Document('Contact','ABC') - d = addchild(c, 'contact_updates', 'Contact Update') - d.last_updated = 'Phone call' - d.save(1) - """ - return parent.addchild(fieldname, childtype, doclist) - -def make_autoname(key, doctype=''): - """ - Creates an autoname from the given key: - - **Autoname rules:** - - * The key is separated by '.' - * '####' represents a series. The string before this part becomes the prefix: - Example: ABC.#### creates a series ABC0001, ABC0002 etc - * 'MM' represents the current month - * 'YY' and 'YYYY' represent the current year - - - *Example:* - - * DE/./.YY./.MM./.##### will create a series like - DE/09/01/0001 where 09 is the year, 01 is the month and 0001 is the series - """ - if key=="hash": - return frappe.generate_hash(doctype) - - if not "#" in key: - key = key + ".#####" - - n = '' - l = key.split('.') - series_set = False - today = now_datetime() - - for e in l: - en = '' - if e.startswith('#'): - if not series_set: - digits = len(e) - en = getseries(n, digits, doctype) - series_set = True - elif e=='YY': - en = today.strftime('%y') - elif e=='MM': - en = today.strftime('%m') - elif e=='DD': - en = today.strftime("%d") - elif e=='YYYY': - en = today.strftime('%Y') - else: en = e - n+=en - return n - -def getseries(key, digits, doctype=''): - # series created ? - current = frappe.db.sql("select `current` from `tabSeries` where name=%s for update", (key,)) - if current and current[0][0] is not None: - current = current[0][0] - # yes, update it - frappe.db.sql("update tabSeries set current = current+1 where name=%s", (key,)) - current = cint(current) + 1 - else: - # no, create it - frappe.db.sql("insert into tabSeries (name, current) values (%s, 1)", (key,)) - current = 1 - return ('%0'+str(digits)+'d') % current - -def getchildren(name, childtype, field='', parenttype='', from_doctype=0): - import frappe - from frappe.model.doclist import DocList - - condition = "" - values = [] - - if field: - condition += ' and parentfield=%s ' - values.append(field) - if parenttype: - condition += ' and parenttype=%s ' - values.append(parenttype) - - dataset = frappe.db.sql("""select * from `tab%s` where parent=%s %s order by idx""" \ - % (childtype, "%s", condition), tuple([name]+values)) - desc = frappe.db.get_description() - - l = DocList() - - for i in dataset: - d = Document() - d.doctype = childtype - d._load_values(i, desc) - l.append(d) - - return l - -def check_page_perm(doc): - if doc.name=='Login Page': - return - if doc.publish: - return - - if not frappe.db.sql("select name from `tabPage Role` where parent=%s and role='Guest'", (doc.name,)): - frappe.response['403'] = 1 - raise frappe.PermissionError, '[WNF] No read permission for %s %s' % ('Page', doc.name) - -def get(dt, dn='', with_children = 1, from_controller = 0): - """ - Returns a doclist containing the main record and all child records - """ - import frappe - import frappe.model - from frappe.model.doclist import DocList - - dn = dn or dt - - # load the main doc - doc = Document(dt, dn) - - if dt=='Page' and frappe.session['user'] == 'Guest': - check_page_perm(doc) - - if not with_children: - # done - return DocList([doc,]) - - # get all children types - tablefields = frappe.model.meta.get_table_fields(dt) - - # load chilren - doclist = DocList([doc,]) - for t in tablefields: - doclist += getchildren(doc.name, t[0], t[1], dt) - - return doclist - -def getsingle(doctype): - """get single doc as dict""" - dataset = frappe.db.sql("select field, value from tabSingles where doctype=%s", (doctype,)) - return dict(dataset) - -def copy_common_fields(from_doc, to_doc): - from frappe.model import default_fields - doctype_list = frappe.get_doctype(to_doc.doctype) - - for fieldname, value in from_doc.fields.items(): - if fieldname in default_fields: - continue - - if doctype_list.get_field(fieldname) and to_doc.fields[fieldname] != value: - to_doc.fields[fieldname] = value - -def validate_name(doctype, name, case=None, merge=False): - if not merge: - if frappe.db.sql('select name from `tab%s` where name=%s' % (doctype,'%s'), (name,)): - raise NameError, 'Name %s already exists' % name - - # no name - if not name: return 'No Name Specified for %s' % doctype - - # new.. - if name.startswith('New '+doctype): - raise NameError, 'There were some errors setting the name, please contact the administrator' - - if case=='Title Case': name = name.title() - if case=='UPPER CASE': name = name.upper() - - name = name.strip() # no leading and trailing blanks - - return name - -def get_default_naming_series(doctype): - """get default value for `naming_series` property""" - from frappe.model.doctype import get_property - naming_series = get_property(doctype, "options", "naming_series") - if naming_series: - naming_series = naming_series.split("\n") - return naming_series[0] or naming_series[1] - else: - return None diff --git a/frappe/model/doclist.py b/frappe/model/doclist.py deleted file mode 100644 index d04f3f9ea6..0000000000 --- a/frappe/model/doclist.py +++ /dev/null @@ -1,143 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe -import frappe.model -from frappe.model.doc import Document -from frappe import _ - -class DocList(list): - """DocList object as a wrapper around a list""" - def get(self, filters, limit=0): - """pass filters as: - {"key": "val", "key": ["!=", "val"], - "key": ["in", "val"], "key": ["not in", "val"], "key": "^val", - "key" : True (exists), "key": False (does not exist) }""" - - out = [] - - for doc in self: - d = isinstance(getattr(doc, "fields", None), dict) and doc.fields or doc - add = True - for f in filters: - fval = filters[f] - - if fval is True: - fval = ["not None", fval] - elif fval is False: - fval = ["None", fval] - elif not isinstance(fval, (tuple, list)): - if isinstance(fval, basestring) and fval.startswith("^"): - fval = ["^", fval[1:]] - else: - fval = ["=", fval] - - if not frappe.compare(d.get(f), fval[0], fval[1]): - add = False - break - - if add: - out.append(doc) - if limit and (len(out)-1)==limit: - break - - return DocList(out) - - def get_distinct_values(self, fieldname): - return filter(None, list(set(map(lambda d: d.fields.get(fieldname), self)))) - - def remove_items(self, filters): - for d in self.get(filters): - self.remove(d) - - def getone(self, filters): - return self.get(filters, limit=1)[0] - - def copy(self): - out = [] - for d in self: - if isinstance(d, dict): - fielddata = d - else: - fielddata = d.fields - fielddata.update({"name": None}) - out.append(Document(fielddata=fielddata)) - return DocList(out) - - def get_item_value(self, d, name): - if isinstance(d, dict): - return d.get(name) - else: - return d.fields.get(name) - - def filter_valid_fields(self): - import frappe.model - fieldnames = {} - for d in self: - remove = [] - for f in d: - if f not in fieldnames.setdefault(d.doctype, - frappe.model.get_fieldnames(d.doctype)): - remove.append(f) - for f in remove: - del d[f] - - def append(self, doc): - if not isinstance(doc, Document): - doc = Document(fielddata=doc) - - self._prepare_doc(doc) - - super(DocList, self).append(doc) - - def extend(self, doclist): - doclist = objectify(doclist) - for doc in doclist: - self._prepare_doc(doc) - - super(DocList, self).extend(doclist) - - return self - - def _prepare_doc(self, doc): - if not doc.name: - doc.fields["__islocal"] = 1 - doc.docstatus = 0 - if doc.parentfield: - if not doc.parenttype: - doc.parenttype = self[0].doctype - if not doc.parent: - doc.parent = self[0].name - if not doc.idx: - siblings = [int(self.get_item_value(d, "idx") or 0) for d in self.get({"parentfield": doc.parentfield})] - doc.idx = (max(siblings) + 1) if siblings else 1 - - def update(self, doclist): - for i, d in enumerate(self): - if d.get("parent") and d.get("name") not in [t.get("name") for t in doclist]: - del self[i] - - for d in doclist: - if not d["name"]: - d["__islocal"] = 1 - self.append(d) - else: - # child - found_in_existing = False - - for ref in self: - if d["name"] and ref.name and ref.name == d["name"]: - ref.fields.update(d) - found_in_existing = True - break - - if not found_in_existing: - d["__islocal"] = 1 - d["name"] = None - self.append(d) - - return self - -def objectify(doclist): - from frappe.model.doc import Document - return map(lambda d: isinstance(d, Document) and d or Document(d), doclist) diff --git a/frappe/model/doctype.py b/frappe/model/doctype.py deleted file mode 100644 index 4e77263e24..0000000000 --- a/frappe/model/doctype.py +++ /dev/null @@ -1,423 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -""" -Get metadata (main doctype with fields and permissions with all table doctypes) - -- if exists in cache, get it from cache -- add custom fields -- override properties from PropertySetter -- sort based on prev_field -- optionally, post process (add js, css, select fields), or without - -""" -from __future__ import unicode_literals - -# imports -import json, os -import frappe -import frappe.model -import frappe.model.doc -import frappe.model.doclist -from frappe.utils import cint, cstr - -doctype_cache = frappe.local('doctype_doctype_cache') -docfield_types = frappe.local('doctype_docfield_types') - -# doctype_cache = {} -# docfield_types = None - -def get(doctype, processed=False, cached=True): - """return doclist""" - if cached: - doclist = from_cache(doctype, processed) - if doclist: - if processed: - update_language(doclist) - return DocTypeDocList(doclist) - - load_docfield_types() - - # main doctype doclist - doclist = get_doctype_doclist(doctype) - - # add doctypes of table fields - table_types = [d.options for d in doclist \ - if d.doctype=='DocField' and d.fieldtype=='Table'] - - for table_doctype in table_types: - doclist += get_doctype_doclist(table_doctype) - - if processed: - add_code(doctype, doclist) - expand_selects(doclist) - add_print_formats(doclist) - add_search_fields(doclist) - add_workflows(doclist) - add_linked_with(doclist) - - to_cache(doctype, processed, doclist) - - if processed: - update_language(doclist) - - return DocTypeDocList(doclist) - -def load_docfield_types(): - frappe.local.doctype_docfield_types = dict(frappe.db.sql("""select fieldname, fieldtype from tabDocField - where parent='DocField'""")) - -def add_workflows(doclist): - from frappe.model.workflow import get_workflow_name - doctype = doclist[0].name - - # get active workflow - workflow_name = get_workflow_name(doctype) - - if workflow_name and frappe.db.exists("Workflow", workflow_name): - doclist += frappe.get_doclist("Workflow", workflow_name) - - # add workflow states (for icons and style) - for state in map(lambda d: d.state, doclist.get({"doctype":"Workflow Document State"})): - doclist += frappe.get_doclist("Workflow State", state) - -def get_doctype_doclist(doctype): - """get doclist of single doctype""" - doclist = frappe.get_doclist('DocType', doctype) - add_custom_fields(doctype, doclist) - apply_property_setters(doctype, doclist) - sort_fields(doclist) - return doclist - -def sort_fields(doclist): - """sort on basis of previous_field""" - - from frappe.model.doclist import DocList - newlist = DocList([]) - pending = doclist.get({"doctype":"DocField"}) - - if doclist[0].get("_idx"): - for fieldname in json.loads(doclist[0].get("_idx")): - d = doclist.get({"fieldname": fieldname}) - if d: - newlist.append(d[0]) - pending.remove(d[0]) - else: - maxloops = 20 - while (pending and maxloops>0): - maxloops -= 1 - for d in pending[:]: - if d.previous_field: - # field already added - for n in newlist: - if n.fieldname==d.previous_field: - newlist.insert(newlist.index(n)+1, d) - pending.remove(d) - break - else: - newlist.append(d) - pending.remove(d) - - # recurring at end - if pending: - newlist += pending - - # renum - idx = 1 - for d in newlist: - d.idx = idx - idx += 1 - - doclist.get({"doctype":["!=", "DocField"]}).extend(newlist) - -def apply_property_setters(doctype, doclist): - for ps in frappe.db.sql("""select * from `tabProperty Setter` where - doc_type=%s""", (doctype,), as_dict=1): - if ps['doctype_or_field']=='DocType': - if ps.get('property_type', None) in ('Int', 'Check'): - ps['value'] = cint(ps['value']) - doclist[0].fields[ps['property']] = ps['value'] - else: - docfield = filter(lambda d: d.doctype=="DocField" and d.fieldname==ps['field_name'], - doclist) - if not docfield: continue - if docfield_types.get(ps['property'], None) in ('Int', 'Check'): - ps['value'] = cint(ps['value']) - - docfield[0].fields[ps['property']] = ps['value'] - -def add_custom_fields(doctype, doclist): - try: - res = frappe.db.sql("""SELECT * FROM `tabCustom Field` - WHERE dt = %s AND docstatus < 2""", (doctype,), as_dict=1) - except Exception, e: - if e.args[0]==1146: - return doclist - else: - raise - - for r in res: - custom_field = frappe.model.doc.Document(fielddata=r) - - # convert to DocField - custom_field.fields.update({ - 'doctype': 'DocField', - 'parent': doctype, - 'parentfield': 'fields', - 'parenttype': 'DocType', - '__custom_field': 1 - }) - doclist.append(custom_field) - - return doclist - -def add_linked_with(doclist): - """add list of doctypes this doctype is 'linked' with""" - doctype = doclist[0].name - links = frappe.db.sql("""select parent, fieldname from tabDocField - where (fieldtype="Link" and options=%s) - or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype)) - links += frappe.db.sql("""select dt as parent, fieldname from `tabCustom Field` - where (fieldtype="Link" and options=%s) - or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype)) - - links = dict(links) - - if not links: - return {} - - ret = {} - - for dt in links: - ret[dt] = { "fieldname": links[dt] } - - for grand_parent, options in frappe.db.sql("""select parent, options from tabDocField - where fieldtype="Table" - and options in (select name from tabDocType - where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)): - - ret[grand_parent] = {"child_doctype": options, "fieldname": links[options] } - if options in ret: - del ret[options] - - doclist[0].fields["__linked_with"] = ret - -def from_cache(doctype, processed): - """ load doclist from cache. - sets flag __from_cache in first doc of doclist if loaded from cache""" - - # from memory - if doctype_cache and not processed and doctype in doctype_cache: - doclist = doctype_cache[doctype] - doclist[0].fields["__from_cache"] = 1 - return doclist - - doclist = frappe.cache().get_value(cache_name(doctype, processed)) - if doclist: - from frappe.model.doclist import DocList - doclist = DocList([frappe.model.doc.Document(fielddata=d) - for d in doclist]) - doclist[0].fields["__from_cache"] = 1 - return doclist - -def to_cache(doctype, processed, doclist): - - if not doctype_cache: - frappe.local.doctype_doctype_cache = {} - - frappe.cache().set_value(cache_name(doctype, processed), - [d.fields for d in doclist]) - - if not processed: - doctype_cache[doctype] = doclist - -def cache_name(doctype, processed): - """returns cache key""" - suffix = "" - if processed: - suffix = ":Raw" - return "doctype:" + doctype + suffix - -def clear_cache(doctype=None): - def clear_single(dt): - frappe.cache().delete_value(cache_name(dt, False)) - frappe.cache().delete_value(cache_name(dt, True)) - - if doctype_cache and (dt in doctype_cache): - del doctype_cache[dt] - - if doctype: - clear_single(doctype) - - # clear all parent doctypes - for dt in frappe.db.sql("""select parent from tabDocField - where fieldtype="Table" and options=%s""", (doctype,)): - clear_single(dt[0]) - - # clear all notifications - from frappe.core.doctype.notification_count.notification_count import delete_notification_count_for - delete_notification_count_for(doctype) - - else: - # clear all - for dt in frappe.db.sql("""select name from tabDocType"""): - clear_single(dt[0]) - - frappe.cache().delete_value("is_table") - -def add_code(doctype, doclist): - import os - from frappe.modules import scrub, get_module_path - - doc = doclist[0] - - path = os.path.join(get_module_path(doc.module), 'doctype', scrub(doc.name)) - def _get_path(fname): - return os.path.join(path, scrub(fname)) - - _add_code(doc, _get_path(doc.name + '.js'), '__js') - _add_code(doc, _get_path(doc.name + '.css'), "__css") - _add_code(doc, _get_path(doc.name + '_list.js'), '__list_js') - _add_code(doc, _get_path(doc.name + '_calendar.js'), '__calendar_js') - _add_code(doc, _get_path(doc.name + '_map.js'), '__map_js') - - add_custom_script(doc) - add_code_via_hook(doc, "doctype_js", "__js") - -def _add_code(doc, path, fieldname): - js = frappe.read_file(path) - if js: - doc.fields[fieldname] = (doc.fields.get(fieldname) or "") + "\n\n" + render_jinja(js) - -def add_code_via_hook(doc, hook, fieldname): - hook = "{}:{}".format(hook, doc.name) - for app_name in frappe.get_installed_apps(): - for file in frappe.get_hooks(hook, app_name=app_name): - path = frappe.get_app_path(app_name, *file.strip("/").split("/")) - _add_code(doc, path, fieldname) - -def add_custom_script(doc): - """embed all require files""" - # custom script - custom = frappe.db.get_value("Custom Script", {"dt": doc.name, - "script_type": "Client"}, "script") or "" - - doc.fields["__js"] = (doc.fields.get('__js') or '') + "\n\n" + custom - -def render_jinja(content): - if "{% include" in content: - content = frappe.get_jenv().from_string(content).render() - return content - -def expand_selects(doclist): - for d in filter(lambda d: d.fieldtype=='Select' \ - and (d.options or '').startswith('link:'), doclist): - doctype = d.options.split("\n")[0][5:] - d.link_doctype = doctype - d.options = '\n'.join([''] + [o.name for o in frappe.db.sql("""select - name from `tab%s` where docstatus<2 order by name asc""" % doctype, as_dict=1)]) - -def add_print_formats(doclist): - print_formats = frappe.db.sql("""select * FROM `tabPrint Format` - WHERE doc_type=%s AND docstatus<2""", (doclist[0].name,), as_dict=1) - for pf in print_formats: - doclist.append(frappe.model.doc.Document('Print Format', fielddata=pf)) - -def get_property(dt, prop, fieldname=None): - """get a doctype property""" - doctypelist = get(dt) - if fieldname: - field = doctypelist.get_field(fieldname) - return field and field.fields.get(prop) or None - else: - return doctypelist[0].fields.get(prop) - -def get_link_fields(doctype): - """get docfields of links and selects with "link:" - for main doctype and child doctypes""" - doctypelist = get(doctype) - return doctypelist.get({"fieldtype":"Link"}).extend(doctypelist.get({"fieldtype":"Select", - "options": "^link:"})) - -def add_validators(doctype, doclist): - for validator in frappe.db.sql("""select name from `tabDocType Validator` where - for_doctype=%s""", (doctype,), as_dict=1): - doclist.extend(frappe.get_doclist('DocType Validator', validator.name)) - -def add_search_fields(doclist): - """add search fields found in the doctypes indicated by link fields' options""" - for lf in doclist.get({"fieldtype": "Link", "options":["!=", "[Select]"]}): - if lf.options: - search_fields = get(lf.options)[0].search_fields - if search_fields: - lf.search_fields = map(lambda sf: sf.strip(), search_fields.split(",")) - -def update_language(doclist): - """update language""" - if frappe.local.lang != 'en': - doclist[0].fields["__messages"] = frappe.get_lang_dict("doctype", doclist[0].name) - -class DocTypeDocList(frappe.model.doclist.DocList): - def get_field(self, fieldname, parent=None, parentfield=None): - filters = {"doctype":"DocField"} - if isinstance(fieldname, dict): - filters.update(fieldname) - else: - filters["fieldname"] = fieldname - - # if parentfield, get the name of the parent table - if parentfield: - parent = self.get_options(parentfield) - - if parent: - filters["parent"] = parent - else: - filters["parent"] = self[0].name - - fields = self.get(filters) - if fields: - return fields[0] - - def has_field(self, fieldname): - return fieldname in self.get_fieldnames() - - def get_fieldnames(self, filters=None): - return map(lambda df: df.fieldname, self.get_docfields(filters)) - - def get_docfields(self, filters=None): - if not filters: filters = {} - filters.update({"doctype": "DocField", "parent": self[0].name}) - return self.get(filters) - - def get_options(self, fieldname, parent=None, parentfield=None): - return self.get_field(fieldname, parent, parentfield).options - - def get_label(self, fieldname, parent=None, parentfield=None): - return self.get_field(fieldname, parent, parentfield).label - - def get_table_fields(self): - return self.get({"doctype": "DocField", "fieldtype": "Table"}) - - def get_parent_doclist(self): - return frappe.doclist([self[0]] + self.get({"parent": self[0].name})) - - def get_restricted_fields(self, restricted_types): - restricted_fields = self.get({ - "doctype":"DocField", - "fieldtype":"Link", - "parent": self[0].name, - "ignore_restrictions":("!=", 1), - "options":("in", restricted_types) - }) - if self[0].name in restricted_types: - restricted_fields.append(frappe._dict({"label":"Name", "fieldname":"name", "options": self[0].name})) - return restricted_fields - - def get_permissions(self, user=None): - user_roles = frappe.get_roles(user) - return [p for p in self.get({"doctype": "DocPerm"}) - if cint(p.permlevel)==0 and (p.role=="All" or p.role in user_roles)] - - def get_link_fields(self): - return self.get({"fieldtype":"Link", "parent": self[0].name})\ - .extend(self.get({"fieldtype":"Select", "options": "^link:", "parent": self[0].name})) diff --git a/frappe/model/document.py b/frappe/model/document.py index b3169da79d..d070fa4899 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -1,5 +1,5 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt from __future__ import unicode_literals import frappe @@ -7,24 +7,42 @@ from frappe import _, msgprint from frappe.utils import cint, flt, cstr, now from frappe.modules import load_doctype_module from frappe.model.base_document import BaseDocument +from frappe.model.naming import set_new_name # once_only validation # methods def get_doc(arg1, arg2=None): - if isinstance(arg1, basestring): + if isinstance(arg1, BaseDocument): + return arg1 + elif isinstance(arg1, basestring): doctype = arg1 else: doctype = arg1.get("doctype") - - module = load_doctype_module(doctype) - classname = doctype.replace(" ", "") - if hasattr(module, classname): - _class = getattr(module, classname) - if issubclass(_class, Document): - return getattr(module, classname)(arg1, arg2) - - return Document(arg1, arg2) + + controller = get_controller(doctype) + if controller: + return controller(arg1, arg2) + + raise ImportError, arg1 + +_classes = {} + +def get_controller(doctype): + if not doctype in _classes: + module = load_doctype_module(doctype) + classname = doctype.replace(" ", "") + if hasattr(module, classname): + _class = getattr(module, classname) + if issubclass(_class, Document): + _class = getattr(module, classname) + else: + raise ImportError, doctype + else: + raise ImportError, doctype + _classes[doctype] = _class + + return _classes[doctype] class Document(BaseDocument): def __init__(self, arg1, arg2=None): @@ -42,12 +60,13 @@ class Document(BaseDocument): raise frappe.DoesNotExistError else: self.name = arg2 - + self.load_from_db() elif isinstance(arg1, dict): super(Document, self).__init__(arg1) - + self.init_valid_columns() + else: # incorrect arguments. let's not proceed. raise frappe.DataError("Document({0}, {1})".format(arg1, arg2)) @@ -55,51 +74,60 @@ class Document(BaseDocument): def load_from_db(self): if not getattr(self, "_metaclass", False) and self.meta.issingle: self.update(frappe.db.get_singles_dict(self.doctype)) + self.init_valid_columns() self._fix_numeric_types() - + else: d = frappe.db.get_value(self.doctype, self.name, "*", as_dict=1) - self.update(d, valid_columns = d.keys()) + if not d: + frappe.throw("{}: {}, {}".format(_("Not Found"), + self.doctype, self.name), frappe.DoesNotExistError) + self.update(d) - for df in self.get_table_fields(): - children = frappe.db.get_values(df.options, - {"parent": self.name, "parenttype": self.doctype, "parentfield": df.fieldname}, - "*", as_dict=True) - if children: - self.set(df.fieldname, children, children[0].keys()) - else: - self.set(df.fieldname, []) - - def get_table_fields(self): - return self.meta.get('fields', {"fieldtype":"Table"}) + if self.name=="DocType" and self.doctype=="DocType": + from frappe.model.meta import doctype_table_fields + table_fields = doctype_table_fields + else: + table_fields = self.meta.get_table_fields() + + for df in table_fields: + children = frappe.db.get_values(df.options, + {"parent": self.name, "parenttype": self.doctype, "parentfield": df.fieldname}, + "*", as_dict=True, order_by="idx asc") + if children: + self.set(df.fieldname, children) + else: + self.set(df.fieldname, []) def has_permission(self, permtype): - if getattr(self, "_ignore_permissions", False): + if getattr(self, "ignore_permissions", False): return True return frappe.has_permission(self.doctype, permtype, self) - - def insert(self): - # check links - # check permissions + + def insert(self, ignore_permissions=None): + if ignore_permissions!=None: + self.ignore_permissions = ignore_permissions + self.set("__islocal", True) - + if not self.has_permission("create"): raise frappe.PermissionError self._set_defaults() self._set_docstatus_user_and_timestamp() - self._check_if_latest() + self.check_if_latest() + set_new_name(self) self.run_method("before_insert") self.run_before_save_methods() self._validate() - + # run validate, on update etc. - + # parent - if self.meta.issingle: + if getattr(self.meta, "issingle", 0): self.update_single(self.get_valid_dict()) else: self.db_insert() - + # children for d in self.get_all_children(): d.parent = self.name @@ -107,7 +135,12 @@ class Document(BaseDocument): self.run_method("after_insert") self.run_post_save_methods() - def save(self): + return self + + def save(self, ignore_permissions=None): + if ignore_permissions!=None: + self.ignore_permissions = ignore_permissions + if self.get("__islocal") or not self.get("name"): self.insert() return @@ -116,7 +149,7 @@ class Document(BaseDocument): raise frappe.PermissionError self._set_docstatus_user_and_timestamp() - self._check_if_latest() + self.check_if_latest() self.run_before_save_methods() self._validate() @@ -127,19 +160,35 @@ class Document(BaseDocument): self.db_update() # children - ignore_children_type = self.get("_ignore_children_type", []) + child_map = {} + ignore_children_type = self.get("ignore_children_type") or [] + for d in self.get_all_children(): - if d.doctype not in _ignore_children_type: - d.parent = self.name - d.db_update() - + d.parent = self.name # rename if reqd + d.parenttype = self.doctype + d.db_update() + child_map.setdefault(d.doctype, []).append(d.name) + + for df in self.meta.get_table_fields(): + if df.options not in ignore_children_type: + cnames = child_map.get(df.options) or [] + if cnames: + frappe.db.sql("""delete from `tab%s` where parent=%s and parenttype=%s and + name not in (%s)""" % (df.options, '%s', '%s', ','.join(['%s'] * len(cnames))), + tuple([self.name, self.doctype] + cnames)) + else: + frappe.db.sql("""delete from `tab%s` where parent=%s and parenttype=%s""" \ + % (df.options, '%s', '%s'), (self.name, self.doctype)) + self.run_post_save_methods() - + + return self + def update_single(self, d): frappe.db.sql("""delete from tabSingles where doctype=%s""", self.doctype) for field, value in d.iteritems(): if field not in ("doctype"): - frappe.db.sql("""insert into tabSingles(doctype, field, value) + frappe.db.sql("""insert into tabSingles(doctype, field, value) values (%s, %s, %s)""", (self.doctype, field, value)) def _set_docstatus_user_and_timestamp(self): @@ -152,7 +201,7 @@ class Document(BaseDocument): self.owner = self.modified_by if self.docstatus==None: self.docstatus=0 - + for d in self.get_all_children(): d.docstatus = self.docstatus d.modified = self.modified @@ -169,23 +218,23 @@ class Document(BaseDocument): for d in self.get_all_children(): d._validate_constants() self._extract_images_from_text_editor() - + def _set_defaults(self): if frappe.flags.in_import: return - - new_doc = frappe.new_doc(self.doctype).fields - self.set_missing_values(new_doc) + + new_doc = frappe.new_doc(self.doctype) + self.update_if_missing(new_doc) # children - for df in self.meta.get("fields", {"fieldtype":"Table"}): - new_doc = frappe.new_doc(df.options).fields + for df in self.meta.get_table_fields(): + new_doc = frappe.new_doc(df.options) value = self.get(df.fieldname) if isinstance(value, list): for d in value: - d.set_missing_values(new_doc) - - def _check_if_latest(self): + d.update_if_missing(new_doc) + + def check_if_latest(self): conflict = False self._action = "save" if not self.get('__islocal'): @@ -194,24 +243,26 @@ class Document(BaseDocument): if cstr(modified) and cstr(modified) != cstr(self._original_modified): conflict = True else: - tmp = frappe.db.get_value(self.doctype, self.name, + tmp = frappe.db.get_value(self.doctype, self.name, ["modified", "docstatus"], as_dict=True) if not tmp: frappe.msgprint("""This record does not exist. Please refresh.""", raise_exception=1) modified = cstr(tmp.modified) - + if modified and modified != cstr(self._original_modified): conflict = True - + self.check_docstatus_transition(tmp.docstatus) - + if conflict: frappe.msgprint(_("Error: Document has been modified after you have opened it") \ + (" (%s, %s). " % (modified, self.modified)) \ - + _("Please refresh to get the latest document."), + + _("Please refresh to get the latest document."), raise_exception=frappe.TimestampMismatchError) + else: + self.check_docstatus_transition(0) def check_docstatus_transition(self, docstatus): if not self.docstatus: @@ -224,8 +275,8 @@ class Document(BaseDocument): if not self.has_permission("submit"): raise frappe.PermissionError else: - raise frappe.DocstatusTransitionError - + raise frappe.DocstatusTransitionError("Cannot change docstatus from 0 to 2") + elif docstatus==1: if self.docstatus==1: self._action = "update_after_submit" @@ -237,49 +288,54 @@ class Document(BaseDocument): if not self.has_permission("cancel"): raise frappe.PermissionError else: - raise frappe.DocstatusTransitionError - + raise frappe.DocstatusTransitionError("Cannot change docstatus from 1 to 0") + elif docstatus==2: raise frappe.ValidationError - + def validate_update_after_submit(self): - # check only allowed values are updated - pass - + if getattr(self, "ignore_validate_update_after_submit", False): + return + + # TODO check only allowed values are updated + def _validate_mandatory(self): if self.get("ignore_mandatory"): return - + missing = self._get_missing_mandatory_fields() for d in self.get_all_children(): missing.extend(d._get_missing_mandatory_fields()) - + if not missing: return - + for fieldname, msg in missing: msgprint(msg) - + raise frappe.MandatoryError(", ".join((each[0] for each in missing))) - + def _validate_links(self): if self.get("ignore_links"): return - + invalid_links = self.get_invalid_links() for d in self.get_all_children(): invalid_links.extend(d.get_invalid_links()) - + if not invalid_links: return - + msg = ", ".join((each[2] for each in invalid_links)) frappe.throw("{}: {}".format(_("Could not find the following documents"), msg), frappe.LinkValidationError) - - def get_all_children(self): + + def get_all_children(self, parenttype=None): ret = [] for df in self.meta.get("fields", {"fieldtype": "Table"}): + if parenttype: + if df.options==parenttype: + return self.get(df.fieldname) value = self.get(df.fieldname) if isinstance(value, list): ret.extend(value) @@ -290,27 +346,26 @@ class Document(BaseDocument): if self.doctype != "DocType": for df in self.meta.get("fields", {"fieldtype":"Text Editor"}): extract_images_from_html(self, df.fieldname) - + def run_method(self, method, *args, **kwargs): """run standard triggers, plus those in frappe""" - def add_to_response(out, new_response): - if isinstance(new_response, dict): - out.update(new_response) - if hasattr(self, method): - add_to_response(frappe.local.response, - getattr(self, method)(*args, **kwargs)) + fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs) + fn.__name__ = method.encode("utf-8") + return Document.hook(fn)(self, *args, **kwargs) - args = [self, method] + list(args or []) + def submit(self): + self.docstatus = 1 + self.save() - for handler in frappe.get_hooks("bean_event:" + self.doctype + ":" + method) \ - + frappe.get_hooks("bean_event:*:" + method): - add_to_response(frappe.local.response, - frappe.call(frappe.get_attr(handler), *args, **kwargs)) - - return frappe.local.response + def cancel(self): + self.docstatus = 2 + self.save() def run_before_save_methods(self): + if getattr(self, "ignore_validate", False): + return + if self._action=="save": self.run_method("validate") self.run_method("before_save") @@ -332,3 +387,36 @@ class Document(BaseDocument): self.run_method("on_cancel") elif self._action=="update_after_submit": self.run_method("on_update_after_submit") + + @staticmethod + def hook(f): + def add_to_return_value(self, new_return_value): + if isinstance(new_return_value, dict): + if not self.get("_return_value"): + self._return_value = {} + self._return_value.update(new_return_value) + else: + self._return_value = new_return_value or self.get("_return_value") + + def compose(fn, *hooks): + def runner(self, method, *args, **kwargs): + add_to_return_value(self, fn(self, *args, **kwargs)) + for f in hooks: + add_to_return_value(self, f(self, method, *args, **kwargs)) + + return self._return_value + + return runner + + def composer(self, *args, **kwargs): + hooks = [] + method = f.__name__ + for handler in frappe.get_hooks("doc_event:" + self.doctype + ":" + method) \ + + frappe.get_hooks("doc_event:*:" + method): + hooks.append(frappe.get_attr(handler)) + + composed = compose(f, *hooks) + return composed(self, method, *args, **kwargs) + + return composer + diff --git a/frappe/model/mapper.py b/frappe/model/mapper.py index 388673b3d5..344af7fb5a 100644 --- a/frappe/model/mapper.py +++ b/frappe/model/mapper.py @@ -7,117 +7,92 @@ from frappe import _ from frappe.utils import cstr from frappe.model import default_fields -def get_mapped_doclist(from_doctype, from_docname, table_maps, target_doclist=None, +def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None, postprocess=None, ignore_permissions=False): - if target_doclist is None: - target_doclist = [] - - if isinstance(target_doclist, basestring): - target_doclist = json.loads(target_doclist) + if isinstance(target_doc, basestring): + target_doc = json.loads(target_doc) - source = frappe.bean(from_doctype, from_docname) + source_doc = frappe.get_doc(from_doctype, from_docname) - if not ignore_permissions and not frappe.has_permission(from_doctype, "read", source.doc): + if not ignore_permissions and not source_doc.has_permission("read"): frappe.msgprint("No Permission", raise_exception=frappe.PermissionError) - source_meta = frappe.get_doctype(from_doctype) - target_meta = frappe.get_doctype(table_maps[from_doctype]["doctype"]) - # main - if target_doclist: - if isinstance(target_doclist[0], dict): - target_doc = frappe.doc(fielddata=target_doclist[0]) - else: - target_doc = target_doclist[0] - else: + if not target_doc: target_doc = frappe.new_doc(table_maps[from_doctype]["doctype"]) - map_doc(source.doc, target_doc, table_maps[source.doc.doctype], source_meta, target_meta) - if target_doclist: - target_doclist[0] = target_doc - else: - target_doclist = [target_doc] - - target_doclist = frappe.doclist(target_doclist) - + map_doc(source_doc, target_doc, table_maps[source_doc.doctype]) + row_exists_for_parentfield = {} # children - for source_d in source.doclist[1:]: - table_map = table_maps.get(source_d.doctype) + for df in source_doc.meta.get_table_fields(): + source_child_doctype = df.options + table_map = table_maps.get(source_child_doctype) if table_map: - if "condition" in table_map: - if not table_map["condition"](source_d): - continue - target_doctype = table_map["doctype"] - parentfield = target_meta.get({ - "parent": target_doc.doctype, - "doctype": "DocField", - "fieldtype": "Table", - "options": target_doctype - })[0].fieldname - - # does row exist for a parentfield? - if parentfield not in row_exists_for_parentfield: - row_exists_for_parentfield[parentfield] = True if \ - frappe.doclist(target_doclist).get({"parentfield": parentfield}) else False - - if table_map.get("add_if_empty") and row_exists_for_parentfield.get(parentfield): - continue - - target_d = frappe.new_doc(target_doctype, target_doc, parentfield) - map_doc(source_d, target_d, table_map, source_meta, target_meta, source.doclist[0]) - target_d.idx = None - target_doclist.append(target_d) + for source_d in source_doc.get(df.fieldname): + if "condition" in table_map: + if not table_map["condition"](source_d): + continue + + target_child_doctype = table_map["doctype"] + target_parentfield = target_doc.get_parentfield_of_doctype(target_child_doctype) - target_doclist = frappe.doclist(target_doclist) + # does row exist for a parentfield? + if df.fieldname not in row_exists_for_parentfield: + row_exists_for_parentfield[target_parentfield] = (True + if target_doc.get(target_parentfield) else False) + + if table_map.get("add_if_empty") and row_exists_for_parentfield.get(target_parentfield): + continue + + if table_map.get("filter") and table_map.get("filter")(source_d): + continue + + target_d = frappe.new_doc(target_child_doctype, target_doc, target_parentfield) + map_doc(source_d, target_d, table_map, source_doc) + target_d.idx = None + target_doc.append(target_parentfield, target_d) if postprocess: - new_target_doclist = postprocess(source, target_doclist) - if new_target_doclist: - target_doclist = new_target_doclist + postprocess(source_doc, target_doc) - return target_doclist + return target_doc -def map_doc(source_doc, target_doc, table_map, source_meta, target_meta, source_parent=None): - no_copy_fields = set(\ - [d.fieldname for d in source_meta.get({"no_copy": 1, - "parent": source_doc.doctype})] \ - + [d.fieldname for d in target_meta.get({"no_copy": 1, - "parent": target_doc.doctype})] \ +def map_doc(source_doc, target_doc, table_map, source_parent=None): + no_copy_fields = set([d.fieldname for d in source_doc.meta.get("fields", {"no_copy": 1})] + + [d.fieldname for d in target_doc.meta.get("fields", {"no_copy": 1})] + default_fields + table_map.get("field_no_map", [])) if table_map.get("validation"): for key, condition in table_map["validation"].items(): if condition[0]=="=": - if source_doc.fields.get(key) != condition[1]: - frappe.msgprint(_("Cannot map because following condition fails: ") - + key + "=" + cstr(condition[1]), raise_exception=frappe.ValidationError) + if source_doc.get(key) != condition[1]: + frappe.throw(_("Cannot map because following condition fails: ") + + key + "=" + cstr(condition[1])) # map same fields - target_fields = target_meta.get({"doctype": "DocField", "parent": target_doc.doctype}) - for key in [d.fieldname for d in target_fields]: - if key not in no_copy_fields: - val = source_doc.fields.get(key) + for df in target_doc.meta.get("fields"): + if df.fieldname not in no_copy_fields: + val = source_doc.get(df.fieldname) if val not in (None, ""): - target_doc.fields[key] = val - - + target_doc.set(df.fieldname, val) + # map other fields field_map = table_map.get("field_map") if field_map: if isinstance(field_map, dict): for source_key, target_key in field_map.items(): - val = source_doc.fields.get(source_key) + val = source_doc.get(source_key) if val not in (None, ""): - target_doc.fields[target_key] = val + target_doc.set(target_key, val) else: for fmap in field_map: - val = source_doc.fields.get(fmap[0]) + val = source_doc.get(fmap[0]) if val not in (None, ""): - target_doc.fields[fmap[1]] = val + target_doc.set(fmap[1], val) # map idx if source_doc.idx: diff --git a/frappe/model/meta.py b/frappe/model/meta.py index cf694e750d..f4f2c099a8 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -1,82 +1,145 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt # metadata from __future__ import unicode_literals -import frappe +import frappe, json from frappe.utils import cstr, cint -from frappe.model import integer_docfield_properties +from frappe.model import integer_docfield_properties, default_fields from frappe.model.document import Document +from frappe.model.base_document import BaseDocument +from frappe.model.db_schema import type_map ###### def get_meta(doctype, cached=True): if cached: - if doctype not in frappe.local.meta: - frappe.local.meta[doctype] = frappe.cache().get_value("meta:" + doctype, lambda: Meta(doctype)) - return frappe.local.meta.get(doctype) + return frappe.cache().get_value("meta:" + doctype, lambda: Meta(doctype)) else: return Meta(doctype) +def get_table_columns(doctype): + return frappe.cache().get_value("table_columns:" + doctype, lambda: frappe.db.get_table_columns(doctype)) + +def load_doctype_from_file(doctype): + fname = frappe.scrub(doctype) + with open(frappe.get_app_path("frappe", "core", "doctype", fname, fname + ".json"), "r") as f: + txt = json.loads(f.read()) + + for d in txt.get("fields", []): + d["doctype"] = "DocField" + + for d in txt.get("permissions", []): + d["doctype"] = "DocPerm" + + txt["fields"] = [BaseDocument(d) for d in txt["fields"]] + if "permissions" in txt: + txt["permissions"] = [BaseDocument(d) for d in txt["permissions"]] + + return txt + class Meta(Document): _metaclass = True + default_fields = default_fields[1:] + special_doctypes = ("DocField", "DocPerm", "Role", "DocType", "Module Def") + def __init__(self, doctype): + self._fields = {} super(Meta, self).__init__("DocType", doctype) - + self.process() + + def load_from_db(self): + try: + super(Meta, self).load_from_db() + except frappe.DoesNotExistError: + if self.doctype=="DocType" and self.name in self.special_doctypes: + self.__dict__.update(load_doctype_from_file(self.name)) + else: + raise + def get_link_fields(self): - tmp = self.get("fields", {"fieldtype":"Link"}) - tmp.extend(self.get("fields", {"fieldtype":"Select", "options": "^link:"})) + 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 - + def get_table_fields(self): - return [ - frappe._dict({"fieldname": "fields", "options": "DocField"}), - frappe._dict({"fieldname": "permissions", "options": "DocPerm"}) - ] - + if not hasattr(self, "_table_fields"): + if self.name!="DocType": + self._table_fields = self.get('fields', {"fieldtype":"Table"}) + else: + self._table_fields = doctype_table_fields + + return self._table_fields + def get_valid_columns(self): if not hasattr(self, "_valid_columns"): - doctype = self.__dict__.get("doctype") - self._valid_columns = frappe.db.get_table_columns(doctype) - + if self.name in ("DocType", "DocField", "DocPerm", "Property Setter"): + self._valid_columns = get_table_columns(self.name) + else: + self._valid_columns = self.default_fields + \ + [df.fieldname for df in self.get("fields") if df.fieldtype in type_map] + return self._valid_columns - + def get_table_field_doctype(self, fieldname): return { "fields": "DocField", "permissions": "DocPerm"}.get(fieldname) - + + def get_field(self, fieldname): + if not fieldname in self._fields: + fields = self.get("fields", {"fieldname":fieldname}) + self._fields[fieldname] = fields[0] if fields else frappe._dict() + return self._fields[fieldname] + + def get_label(self, fieldname): + return self.get_field(fieldname).label + + def get_options(self, fieldname): + return self.get_field(fieldname).options + def process(self): + # don't process for special doctypes + # prevent's circular dependency + if self.name in self.special_doctypes: + return + self.add_custom_fields() self.apply_property_setters() self.sort_fields() - + def add_custom_fields(self): try: self.extend("fields", frappe.db.sql("""SELECT * FROM `tabCustom Field` - WHERE dt = %s AND docstatus < 2""", (doctype,), as_dict=1)) + WHERE dt = %s AND docstatus < 2""", (self.name,), as_dict=1, + update={"is_custom_field": True})) except Exception, e: if e.args[0]==1146: return else: raise - + def apply_property_setters(self): for ps in frappe.db.sql("""select * from `tabProperty Setter` where - doc_type=%s""", (doctype,), as_dict=1): - if ps['doctype_or_field']=='DocType': - if ps.get('property_type', None) in ('Int', 'Check'): - ps['value'] = cint(ps['value']) + doc_type=%s""", (self.name,), as_dict=1): + if ps.doctype_or_field=='DocType': + if ps.property_type in ('Int', 'Check'): + ps.value = cint(ps.value) - self.set(ps["property"], ps["value"]) + self.set(ps.property, ps.value) else: - docfield = self.get("fields", {"fieldname":ps["fieldname"]}, limit=1)[0] + docfield = self.get("fields", {"fieldname":ps.field_name}, limit=1) + if docfield: + docfield = docfield[0] + else: + continue - if not docfield: continue - if ps["property"] in integer_docfield_properties: - ps['value'] = cint(ps['value']) + if ps.property in integer_docfield_properties: + ps.value = cint(ps.value) + + docfield.set(ps.property, ps.value) - docfield.set(ps["property"], ps["value"]) - def sort_fields(self): """sort on basis of previous_field""" newlist = [] @@ -93,7 +156,7 @@ class Meta(Document): while (pending and maxloops>0): maxloops -= 1 for d in pending[:]: - if d.previous_field: + if d.get("previous_field"): # field already added for n in newlist: if n.fieldname==d.previous_field: @@ -104,7 +167,7 @@ class Meta(Document): newlist.append(d) pending.remove(d) - # recurring at end + # recurring at end if pending: newlist += pending @@ -115,12 +178,12 @@ class Meta(Document): idx += 1 self.set("fields", newlist) - + def get_restricted_fields(self, restricted_types): restricted_fields = self.get("fields", { - "fieldtype":"Link", - "parent": self.name, - "ignore_restrictions":("!=", 1), + "fieldtype":"Link", + "parent": self.name, + "ignore_restrictions":("!=", 1), "options":("in", restricted_types) }) if self.name in restricted_types: @@ -130,6 +193,11 @@ class Meta(Document): return restricted_fields +doctype_table_fields = [ + frappe._dict({"fieldname": "fields", "options": "DocField"}), + frappe._dict({"fieldname": "permissions", "options": "DocPerm"}) +] + ####### def is_single(doctype): @@ -139,74 +207,69 @@ def is_single(doctype): raise Exception, 'Cannot determine whether %s is single' % doctype def get_parent_dt(dt): - parent_dt = frappe.db.sql("""select parent from tabDocField - where fieldtype="Table" and options=%s and (parent not like "old_parent:%%") + parent_dt = frappe.db.sql("""select parent from tabDocField + where fieldtype="Table" and options=%s and (parent not like "old_parent:%%") limit 1""", dt) return parent_dt and parent_dt[0][0] or '' def set_fieldname(field_id, fieldname): frappe.db.set_value('DocField', field_id, 'fieldname', fieldname) -def get_link_fields(doctype): - """ - Returns list of link fields for a doctype in tuple (fieldname, options, label) - """ - import frappe.model.doctype - doclist = frappe.model.doctype.get(doctype) - return [(d.fields.get('fieldname'), d.fields.get('options'), d.fields.get('label')) - for d in doclist.get_link_fields() if d.fields.get('fieldname')!='owner'] - -def get_table_fields(doctype): - child_tables = [[d[0], d[1]] for d in frappe.db.sql("""select options, fieldname - from tabDocField where parent=%s and fieldtype='Table'""", doctype, as_list=1)] - - try: - custom_child_tables = [[d[0], d[1]] for d in frappe.db.sql("""select options, fieldname - from `tabCustom Field` where dt=%s and fieldtype='Table'""", doctype, as_list=1)] - except Exception, e: - if e.args[0]!=1146: - raise - custom_child_tables = [] - - return child_tables + custom_child_tables - -def has_field(doctype, fieldname, parent=None, parentfield=None): - return get_field(doctype, fieldname, parent=None, parentfield=None) and True or False - -def get_field(doctype, fieldname, parent=None, parentfield=None): - doclist = frappe.get_doctype(doctype) - return doclist.get_field(fieldname, parent, parentfield) - def get_field_currency(df, doc): """get currency based on DocField options and fieldvalue in doc""" currency = None - + if ":" in cstr(df.options): split_opts = df.options.split(":") if len(split_opts)==3: - currency = frappe.db.get_value(split_opts[0], doc.fields.get(split_opts[1]), + currency = frappe.db.get_value(split_opts[0], doc.get(split_opts[1]), split_opts[2]) else: - currency = doc.fields.get(df.options) + currency = doc.get(df.options) return currency - + def get_field_precision(df, doc): """get precision based on DocField options and fieldvalue in doc""" from frappe.utils import get_number_format_info - + number_format = None if df.fieldtype == "Currency": currency = get_field_currency(df, doc) if currency: number_format = frappe.db.get_value("Currency", currency, "number_format") - + if not number_format: number_format = frappe.db.get_default("number_format") or "#,###.##" - + decimal_str, comma_str, precision = get_number_format_info(number_format) if df.fieldtype == "Float": precision = cint(frappe.db.get_default("float_precision")) or 3 - return precision \ No newline at end of file + return precision + +def clear_cache(doctype=None): + def clear_single(dt): + frappe.cache().delete_value("meta:" + dt) + frappe.cache().delete_value("form_meta:" + dt) + frappe.cache().delete_value("table_columns:" + dt) + + if doctype: + clear_single(doctype) + + # clear all parent doctypes + for dt in frappe.db.sql("""select parent from tabDocField + where fieldtype="Table" and options=%s""", (doctype,)): + clear_single(dt[0]) + + # clear all notifications + from frappe.core.doctype.notification_count.notification_count import delete_notification_count_for + delete_notification_count_for(doctype) + + else: + # clear all + for dt in frappe.db.sql("""select name from tabDocType"""): + clear_single(dt[0]) + + frappe.cache().delete_value("is_table") diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 1540c80deb..4e36156ae4 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -8,18 +8,20 @@ from frappe.utils import now_datetime, cint def set_new_name(doc): if getattr(doc, "_new_name_set", False): - # already set by bean + # already set by doc return doc._new_name_set = True - autoname = frappe.get_doctype(doc.doctype)[0].autoname + autoname = frappe.get_meta(doc.doctype).autoname doc.localname = doc.name # for passing back to client # amendments if getattr(doc, "amended_from", None): return doc._get_amended_name() else: - if hasattr(doc, "autoname"): + tmp = getattr(doc, "autoname", None) + if tmp and not isinstance(tmp, basestring): + # autoname in a function, not a property doc.autoname() if doc.name and doc.localname != doc.name: return @@ -121,8 +123,7 @@ def getseries(key, digits, doctype=''): def get_default_naming_series(doctype): """get default value for `naming_series` property""" - from frappe.model.doctype import get_property - naming_series = get_property(doctype, "options", "naming_series") + naming_series = frappe.get_meta(doctype).get_field("naming_series").options or "" if naming_series: naming_series = naming_series.split("\n") return naming_series[0] or naming_series[1] @@ -148,3 +149,16 @@ def _get_amended_name(doc): doc.name = am_prefix + '-' + str(am_id) return doc.name +def append_number_if_name_exists(doc): + if frappe.db.exists(doc.doctype, doc.name): + last = frappe.db.sql("""select name from `tab{}` + where name regexp '{}-[[:digit:]]+' + order by name desc limit 1""".format(doc.doctype, doc.name)) + + if last: + count = str(cint(last[0][0].rsplit("-", 1)[1]) + 1) + else: + count = "1" + + doc.name = "{0}-{1}".format(doc.name, count) + diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index ca782e9bbd..b177708681 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -4,54 +4,52 @@ from __future__ import unicode_literals import frappe from frappe.utils import cint -import frappe.model.doctype -from frappe.model.doc import validate_name +from frappe.model.naming import validate_name @frappe.whitelist() def rename_doc(doctype, old, new, force=False, merge=False, ignore_permissions=False): """ - Renames a doc(dt, old) to doc(dt, new) and + Renames a doc(dt, old) to doc(dt, new) and updates all linked fields of type "Link" or "Select" with "link:" """ if not frappe.db.exists(doctype, old): return - + force = cint(force) merge = cint(merge) - - # get doclist of given doctype - doclist = frappe.model.doctype.get(doctype) - + + meta = frappe.get_meta(doctype) + # call before_rename - out = frappe.bean(doctype, old).run_method("before_rename", old, new, merge) or {} - new = out.get("new") or new - new = validate_rename(doctype, new, doclist, merge, force, ignore_permissions) - + out = frappe.get_doc(doctype, old).run_method("before_rename", old, new, merge) or {} + new = (out.get("new") or new) if isinstance(out, dict) else new + new = validate_rename(doctype, new, meta, merge, force, ignore_permissions) + if not merge: - rename_parent_and_child(doctype, old, new, doclist) - + rename_parent_and_child(doctype, old, new, meta) + # update link fields' values link_fields = get_link_fields(doctype) update_link_field_values(link_fields, old, new, doctype) - + if doctype=='DocType': rename_doctype(doctype, old, new, force) - + update_attachments(doctype, old, new) - + if merge: frappe.delete_doc(doctype, old) - + # call after_rename - frappe.bean(doctype, new).run_method("after_rename", old, new, merge) - + frappe.get_doc(doctype, new).run_method("after_rename", old, new, merge) + rename_versions(doctype, old, new) - + # update restrictions - frappe.db.sql("""update tabDefaultValue set defvalue=%s where parenttype='Restriction' + frappe.db.sql("""update tabDefaultValue set defvalue=%s where parenttype='Restriction' and defkey=%s and defvalue=%s""", (new, doctype, old)) frappe.clear_cache() - + return new def update_attachments(doctype, old, new): @@ -60,34 +58,34 @@ def update_attachments(doctype, old, new): where attached_to_name=%s and attached_to_doctype=%s""", (new, old, doctype)) except Exception, e: if e.args[0]!=1054: # in patch? - raise + raise def rename_versions(doctype, old, new): - frappe.db.sql("""update tabVersion set docname=%s where ref_doctype=%s and docname=%s""", + frappe.db.sql("""update tabVersion set docname=%s where ref_doctype=%s and docname=%s""", (new, doctype, old)) -def rename_parent_and_child(doctype, old, new, doclist): +def rename_parent_and_child(doctype, old, new, meta): # rename the doc frappe.db.sql("update `tab%s` set name=%s where name=%s" \ % (doctype, '%s', '%s'), (new, old)) - update_child_docs(old, new, doclist) + update_child_docs(old, new, meta) -def validate_rename(doctype, new, doclist, merge, force, ignore_permissions): +def validate_rename(doctype, new, meta, merge, force, ignore_permissions): exists = frappe.db.exists(doctype, new) if merge and not exists: frappe.msgprint("%s: %s does not exist, select a new target to merge." % (doctype, new), raise_exception=1) - + if (not merge) and exists: frappe.msgprint("%s: %s exists, select a new, new name." % (doctype, new), raise_exception=1) if not (ignore_permissions or frappe.has_permission(doctype, "write")): frappe.msgprint("You need write permission to rename", raise_exception=1) - if not force and not doclist[0].allow_rename: + if not force and not meta.allow_rename: frappe.msgprint("%s cannot be renamed" % doctype, raise_exception=1) - + # validate naming like it's done in doc.py new = validate_name(doctype, new, merge=merge) @@ -96,51 +94,41 @@ def validate_rename(doctype, new, doclist, merge, force, ignore_permissions): def rename_doctype(doctype, old, new, force=False): # change options for fieldtype Table update_parent_of_fieldtype_table(old, new) - + # change options where select options are hardcoded i.e. listed select_fields = get_select_fields(old, new) update_link_field_values(select_fields, old, new, doctype) update_select_field_values(old, new) - + # change parenttype for fieldtype Table update_parenttype_values(old, new) - + # rename comments frappe.db.sql("""update tabComment set comment_doctype=%s where comment_doctype=%s""", (new, old)) -def update_child_docs(old, new, doclist): +def update_child_docs(old, new, meta): # update "parent" - child_doctypes = (d.options for d in doclist - if d.doctype=='DocField' and d.fieldtype=='Table') - - for child in child_doctypes: + for df in meta.get_table_fields(): frappe.db.sql("update `tab%s` set parent=%s where parent=%s" \ - % (child, '%s', '%s'), (new, old)) + % (df.options, '%s', '%s'), (new, old)) def update_link_field_values(link_fields, old, new, doctype): - update_list = [] - - # update values for field in link_fields: - # if already updated, do not do it again - if [field['parent'], field['fieldname']] in update_list: - continue - update_list.append([field['parent'], field['fieldname']]) if field['issingle']: frappe.db.sql("""\ update `tabSingles` set value=%s where doctype=%s and field=%s and value=%s""", (new, field['parent'], field['fieldname'], old)) else: - if doctype!='DocType' and field['parent']!=new: + if field['parent']!=new: frappe.db.sql("""\ update `tab%s` set `%s`=%s where `%s`=%s""" \ % (field['parent'], field['fieldname'], '%s', field['fieldname'], '%s'), (new, old)) - + def get_link_fields(doctype): # get link fields from tabDocField link_fields = frappe.db.sql("""\ @@ -153,7 +141,7 @@ def get_link_fields(doctype): ((df.options=%s and df.fieldtype='Link') or (df.options='link:%s' and df.fieldtype='Select'))""" \ % ('%s', doctype), (doctype,), as_dict=1) - + # get link fields from tabCustom Field custom_link_fields = frappe.db.sql("""\ select dt as parent, fieldname, @@ -165,10 +153,10 @@ def get_link_fields(doctype): ((df.options=%s and df.fieldtype='Link') or (df.options='link:%s' and df.fieldtype='Select'))""" \ % ('%s', doctype), (doctype,), as_dict=1) - + # add custom link fields list to link fields list link_fields += custom_link_fields - + # remove fields whose options have been changed using property setter property_setter_link_fields = frappe.db.sql("""\ select ps.doc_type as parent, ps.field_name as fieldname, @@ -180,24 +168,24 @@ def get_link_fields(doctype): ps.field_name is not null and (ps.value=%s or ps.value='link:%s')""" \ % ('%s', doctype), (doctype,), as_dict=1) - + link_fields += property_setter_link_fields - + return link_fields - + def update_parent_of_fieldtype_table(old, new): frappe.db.sql("""\ update `tabDocField` set options=%s where fieldtype='Table' and options=%s""", (new, old)) - + frappe.db.sql("""\ update `tabCustom Field` set options=%s where fieldtype='Table' and options=%s""", (new, old)) - + frappe.db.sql("""\ update `tabProperty Setter` set value=%s where property='options' and value=%s""", (new, old)) - + def get_select_fields(old, new): """ get select type fields where doctype's name is hardcoded as @@ -215,7 +203,7 @@ def get_select_fields(old, new): df.options not like "link:%%%%" and (df.options like "%%%%%s%%%%")""" \ % ('%s', old), (new,), as_dict=1) - + # get link fields from tabCustom Field custom_select_fields = frappe.db.sql("""\ select dt as parent, fieldname, @@ -228,10 +216,10 @@ def get_select_fields(old, new): df.options not like "link:%%%%" and (df.options like "%%%%%s%%%%")""" \ % ('%s', old), (new,), as_dict=1) - + # add custom link fields list to link fields list select_fields += custom_select_fields - + # remove fields whose options have been changed using property setter property_setter_select_fields = frappe.db.sql("""\ select ps.doc_type as parent, ps.field_name as fieldname, @@ -245,11 +233,11 @@ def get_select_fields(old, new): ps.value not like "link:%%%%" and (ps.value like "%%%%%s%%%%")""" \ % ('%s', old), (new,), as_dict=1) - + select_fields += property_setter_select_fields - + return select_fields - + def update_select_field_values(old, new): frappe.db.sql("""\ update `tabDocField` set options=replace(options, %s, %s) @@ -274,7 +262,7 @@ def update_select_field_values(old, new): property='options' and value not like "link%%%%" and (value like "%%%%\\n%s%%%%" or value like "%%%%%s\\n%%%%")""" % \ ('%s', '%s', '%s', old, old), (old, new, new)) - + def update_parenttype_values(old, new): child_doctypes = frappe.db.sql("""\ select options, fieldname from `tabDocField` @@ -286,18 +274,18 @@ def update_parenttype_values(old, new): child_doctypes += custom_child_doctypes fields = [d['fieldname'] for d in child_doctypes] - + property_setter_child_doctypes = frappe.db.sql("""\ select value as options from `tabProperty Setter` where doc_type=%s and property='options' and field_name in ("%s")""" % ('%s', '", "'.join(fields)), (new,)) - + child_doctypes += property_setter_child_doctypes child_doctypes = (d['options'] for d in child_doctypes) - + for doctype in child_doctypes: frappe.db.sql("""\ update `tab%s` set parenttype=%s where parenttype=%s""" % (doctype, '%s', '%s'), - (new, old)) \ No newline at end of file + (new, old)) diff --git a/frappe/model/sync.py b/frappe/model/sync.py index 747b3d3b4a..1ad92d78ec 100644 --- a/frappe/model/sync.py +++ b/frappe/model/sync.py @@ -11,9 +11,9 @@ import os, sys from frappe.modules.import_file import import_file_by_path from frappe.utils import get_path, cstr -def sync_all(force=0): +def sync_all(force=0, verbose=False): for app in frappe.get_installed_apps(): - sync_for(app, force) + sync_for(app, force, verbose=verbose) frappe.clear_cache() def sync_for(app_name, force=0, sync_everything = False, verbose=False): @@ -40,8 +40,8 @@ def walk_and_sync(start_path, force=0, sync_everything = False, verbose=False): if sync_everything or (os.path.basename(os.path.dirname(path)) in document_type): for f in files: f = cstr(f) - if f.endswith(".txt"): - doc_name = f.split(".txt")[0] + if f.endswith(".json"): + doc_name = f.split(".json")[0] if doc_name == os.path.basename(path): module_name = path.split(os.sep)[-3] diff --git a/frappe/model/utils.py b/frappe/model/utils.py index 88d6039157..a9929815e9 100644 --- a/frappe/model/utils.py +++ b/frappe/model/utils.py @@ -4,117 +4,14 @@ from __future__ import unicode_literals import frappe, json from frappe import _ -from frappe.model.doc import Document """ Model utilities, unclassified functions """ -def expand(docs): - """ - Expand a doclist sent from the client side. (Internally used by the request handler) - """ - def xzip(a,b): - d = {} - for i in range(len(a)): - d[a[i]] = b[i] - return d - - docs = json.loads(docs) - clist = [] - for d in docs['_vl']: - doc = xzip(docs['_kl'][d[0]], d); - clist.append(doc) - return clist - -def compress(doclist): - """ - Compress a doclist before sending it to the client side. (Internally used by the request handler) - - """ - docs = [isinstance(d, Document) and d.fields or d for d in doclist] - - kl, vl = {}, [] - forbidden = ['server_code_compiled'] - - # scan for keys & values - for d in docs: - dt = d['doctype'] - if not (dt in kl.keys()): - kl[dt] = ['doctype','localname','__oldparent','__unsaved'] - - # add client script for doctype, doctype due to ambiguity - if dt=='DocType' and '__client_script' not in kl[dt]: - kl[dt].append('__client_script') - - for f in d.keys(): - if not (f in kl[dt]) and not (f in forbidden): - # if key missing, then append - kl[dt].append(f) - - # build values - tmp = [] - for f in kl[dt]: - v = d.get(f) - if type(v)==long: - v=int(v) - tmp.append(v) - - vl.append(tmp) - return {'_vl':vl,'_kl':kl} - - -def getlist(doclist, field): - from frappe.utils import cint - l = [] - for d in doclist: - if d.parentfield == field: - l.append(d) - l.sort(lambda a, b: cint(a.idx) - cint(b.idx)) - return l - -def copy_doclist(doclist, no_copy = []): - """ - Save & return a copy of the given doclist - Pass fields that are not to be copied in `no_copy` - """ - - cl = [] - - # main doc - c = Document(fielddata = doclist[0].fields.copy()) - - # clear no_copy fields - for f in no_copy: - if c.fields.has_key(f): - c.fields[f] = None - - c.name = None - c.save(1) - cl.append(c) - - # new parent name - parent = c.name - - # children - for d in doclist[1:]: - c = Document(fielddata = d.fields.copy()) - c.name = None - - # clear no_copy fields - for f in no_copy: - if c.fields.has_key(f): - c.fields[f] = None - - c.parent = parent - c.save(1) - cl.append(c) - - return cl - def set_default(doc, key): if not doc.is_default: frappe.db.set(doc, "is_default", 1) frappe.db.sql("""update `tab%s` set `is_default`=0 where `%s`=%s and name!=%s""" % (doc.doctype, key, "%s", "%s"), - (doc.fields.get(key), doc.name)) + (doc.get(key), doc.name)) diff --git a/frappe/modules/__init__.py b/frappe/modules/__init__.py index f9f706133c..50950de9dc 100644 --- a/frappe/modules/__init__.py +++ b/frappe/modules/__init__.py @@ -37,10 +37,9 @@ def reload_doc(module, dt=None, dn=None, force=True): def export_doc(doctype, name, module=None): """write out a doc""" from frappe.modules.export_file import write_document_file - import frappe.model.doc if not module: module = frappe.db.get_value(doctype, name, 'module') - write_document_file(frappe.model.doc.get(doctype, name), module) + write_document_file(frappe.get_doc(doctype, name), module) def get_doctype_module(doctype): return frappe.db.get_value('DocType', doctype, 'module') or "core" diff --git a/frappe/modules/export_file.py b/frappe/modules/export_file.py index 2044988d94..ea68ef2f5a 100644 --- a/frappe/modules/export_file.py +++ b/frappe/modules/export_file.py @@ -3,8 +3,8 @@ from __future__ import unicode_literals -import frappe, os -import frappe.model.doc +import frappe, os, json +import frappe.model from frappe.modules import scrub, get_module_path, lower_case_files_for, scrub_dt_dn def export_doc(doc): @@ -17,58 +17,39 @@ def export_to_files(record_list=None, record_module=None, verbose=0, create_init if frappe.flags.in_import: return - module_doclist =[] if record_list: for record in record_list: - write_document_file(frappe.model.doc.get(record[0], record[1]), - record_module, create_init=create_init) + write_document_file(frappe.get_doc(record[0], record[1]), record_module, create_init=create_init) -def write_document_file(doclist, record_module=None, create_init=None): - from frappe.modules.utils import pprint_doclist +def write_document_file(doc, record_module=None, create_init=None): + newdoc = doc.as_dict() - doclist = [filter_fields(d.fields) for d in doclist] - - module = record_module or get_module_name(doclist) + # strip out default fields from children + for df in doc.meta.get_table_fields(): + for d in newdoc.get(df.fieldname): + for fieldname in frappe.model.default_fields: + if fieldname in d: + del d[fieldname] + + module = record_module or get_module_name(doc) if create_init is None: - create_init = doclist[0]['doctype'] in lower_case_files_for + create_init = doc.doctype in lower_case_files_for # create folder - folder = create_folder(module, doclist[0]['doctype'], doclist[0]['name'], create_init) + folder = create_folder(module, doc.doctype, doc.name, create_init) # write the data file - fname = (doclist[0]['doctype'] in lower_case_files_for and scrub(doclist[0]['name'])) or doclist[0]['name'] - with open(os.path.join(folder, fname +'.txt'),'w+') as txtfile: - txtfile.write(pprint_doclist(doclist)) + fname = (doc.doctype in lower_case_files_for and scrub(doc.name)) or doc.name + with open(os.path.join(folder, fname +".json"),'w+') as txtfile: + txtfile.write(json.dumps(newdoc, indent=1, sort_keys=True)) -def filter_fields(doc): - from frappe.model.doctype import get - from frappe.model import default_fields - - doctypelist = get(doc.get("doctype"), False) - valid_fields = [d.fieldname for d in doctypelist.get({"parent":doc.get("doctype"), - "doctype":"DocField"})] - to_remove = [] - - for key in doc: - if (not key in default_fields) and (not key in valid_fields): - to_remove.append(key) - elif doc[key]==None: - to_remove.append(key) - - for key in to_remove: - del doc[key] - - return doc - -def get_module_name(doclist): - if doclist[0]['doctype'] == 'Module Def': - module = doclist[0]['name'] - elif doclist[0]['doctype']=='Control Panel': - module = 'Core' - elif doclist[0]['doctype']=="Workflow": - module = frappe.db.get_value("DocType", doclist[0]["document_type"], "module") +def get_module_name(doc): + if doc.doctype == 'Module Def': + module = doc.name + elif doc.doctype=="Workflow": + module = frappe.db.get_value("DocType", doc.document_type, "module") else: - module = doclist[0]['module'] + module = doc.module return module diff --git a/frappe/modules/import_file.py b/frappe/modules/import_file.py index 05c02ada0a..b8db9180d8 100644 --- a/frappe/modules/import_file.py +++ b/frappe/modules/import_file.py @@ -1,10 +1,11 @@ # 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, os +import frappe, os, json from frappe.modules import scrub, get_module_path, scrub_dt_dn +from frappe.utils import get_datetime_str def import_files(module, dt=None, dn=None, force=False): if type(module) is list: @@ -14,113 +15,92 @@ def import_files(module, dt=None, dn=None, force=False): return out else: return import_file(module, dt, dn, force=force) - + def import_file(module, dt, dn, force=False): """Sync a file from txt if modifed, return false if not updated""" path = get_file_path(module, dt, dn) ret = import_file_by_path(path, force) return ret - + def get_file_path(module, dt, dn): dt, dn = scrub_dt_dn(dt, dn) - - path = os.path.join(get_module_path(module), - os.path.join(dt, dn, dn + '.txt')) - + + path = os.path.join(get_module_path(module), + os.path.join(dt, dn, dn + ".json")) + return path - + def import_file_by_path(path, force=False): frappe.flags.in_import = True - doclist = read_doclist_from_file(path) - - if doclist: - doc = doclist[0] - - if not force: - # check if timestamps match - if doc['modified']==str(frappe.db.get_value(doc['doctype'], doc['name'], 'modified')): - return False - - original_modified = doc["modified"] - - import_doclist(doclist) + docs = read_doc_from_file(path) + + if docs: + if not isinstance(docs, list): + docs = [docs] + + for doc in docs: + if not force: + # check if timestamps match + db_modified = frappe.db.get_value(doc['doctype'], doc['name'], 'modified') + if db_modified and doc.get('modified')==get_datetime_str(db_modified): + return False + + original_modified = doc.get("modified") + + import_doc(doc) + + if original_modified: + # since there is a new timestamp on the file, update timestamp in + frappe.db.sql("update `tab%s` set modified=%s where name=%s" % \ + (doc['doctype'], '%s', '%s'), + (original_modified, doc['name'])) - # since there is a new timestamp on the file, update timestamp in - frappe.db.sql("update `tab%s` set modified=%s where name=%s" % \ - (doc['doctype'], '%s', '%s'), - (original_modified, doc['name'])) - frappe.flags.in_import = False return True - -def read_doclist_from_file(path): - doclist = None + +def read_doc_from_file(path): + doc = None if os.path.exists(path): - from frappe.modules.utils import peval_doclist - with open(path, 'r') as f: - doclist = peval_doclist(f.read()) + doc = json.loads(f.read()) else: raise Exception, '%s missing' % path - - return doclist -ignore_values = { - "Report": ["disabled"], + return doc + +ignore_values = { + "Report": ["disabled"], } ignore_doctypes = ["Page Role", "DocPerm"] -def import_doclist(doclist): - doctype = doclist[0]["doctype"] - name = doclist[0]["name"] - old_doc = None - - doctypes = set([d["doctype"] for d in doclist]) - ignore = list(doctypes.intersection(set(ignore_doctypes))) - - if doctype in ignore_values: - if frappe.db.exists(doctype, name): - old_doc = frappe.doc(doctype, name) +def import_doc(docdict): + docdict["__islocal"] = 1 + doc = frappe.get_doc(docdict) - # delete old - frappe.delete_doc(doctype, name, force=1, ignore_doctypes=ignore, for_reload=True) - - # don't overwrite ignored docs - doclist1 = remove_ignored_docs_if_they_already_exist(doclist, ignore, name) + ignore = [] - # update old values (if not to be overwritten) - if doctype in ignore_values and old_doc: - update_original_values(doclist1, doctype, old_doc) - - # reload_new - new_bean = frappe.bean(doclist1) - new_bean.ignore_children_type = ignore - new_bean.ignore_links = True - new_bean.ignore_validate = True - new_bean.ignore_permissions = True - new_bean.ignore_mandatory = True - new_bean.ignore_restrictions = True - - if doctype=="DocType" and name in ["DocField", "DocType"]: - new_bean.ignore_fields = True - - new_bean.insert() + if frappe.db.exists(doc.doctype, doc.name): + old_doc = frappe.get_doc(doc.doctype, doc.name) -def remove_ignored_docs_if_they_already_exist(doclist, ignore, name): - doclist1 = doclist - if ignore: - has_records = [] - for d in ignore: - if frappe.db.get_value(d, {"parent":name}): - has_records.append(d) - - if has_records: - doclist1 = filter(lambda d: d["doctype"] not in has_records, doclist) - - return doclist1 + if doc.doctype in ignore_values: + # update ignore values + for key in ignore_values.get(doc.doctype) or []: + doc.set(key, old_doc.get(key)) -def update_original_values(doclist, doctype, old_doc): - for key in ignore_values[doctype]: - doclist[0][key] = old_doc.fields[key] - \ No newline at end of file + # update ignored docs into new doc + for df in doc.meta.get_table_fields(): + if df.options in ignore_doctypes: + doc.set(df.fieldname, []) + ignore.append(df.options) + + # delete old + frappe.delete_doc(doc.doctype, doc.name, force=1, ignore_doctypes=ignore, for_reload=True) + + doc.ignore_children_type = ignore + doc.ignore_links = True + doc.ignore_validate = True + doc.ignore_permissions = True + doc.ignore_mandatory = True + doc.ignore_restrictions = True + doc.insert() diff --git a/frappe/modules/patch_handler.py b/frappe/modules/patch_handler.py index 851bf7b7b5..bd2b18941f 100644 --- a/frappe/modules/patch_handler.py +++ b/frappe/modules/patch_handler.py @@ -95,7 +95,7 @@ def update_patch_log(patchmodule): frappe.db.sql("""INSERT INTO `__PatchLog` VALUES (%s, now())""", \ patchmodule) else: - frappe.doc({"doctype": "Patch Log", "patch": patchmodule}).insert() + frappe.get_doc({"doctype": "Patch Log", "patch": patchmodule}).insert() def executed(patchmodule): """return True if is executed""" diff --git a/frappe/patches.txt b/frappe/patches.txt index f87f01ebfa..b1ac2d88f6 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -16,10 +16,12 @@ frappe.patches.4_0.website_sitemap_hierarchy frappe.patches.4_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 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.rename_profile_to_user frappe.patches.4_0.update_datetime +frappe.patches.4_0.deprecate_control_panel +frappe.patches.4_0.file_manager_hooks diff --git a/frappe/patches/4_0/deprecate_control_panel.py b/frappe/patches/4_0/deprecate_control_panel.py new file mode 100644 index 0000000000..7bccf4bf2b --- /dev/null +++ b/frappe/patches/4_0/deprecate_control_panel.py @@ -0,0 +1,10 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.db.sql("update `tabDefaultValue` set parenttype='__default' where parenttype='Control Panel'") + frappe.db.sql("update `tabDefaultValue` set parent='__default' where parent='Control Panel'") + frappe.clear_cache() diff --git a/frappe/patches/4_0/file_manager_hooks.py b/frappe/patches/4_0/file_manager_hooks.py new file mode 100644 index 0000000000..5c72e62da3 --- /dev/null +++ b/frappe/patches/4_0/file_manager_hooks.py @@ -0,0 +1,27 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals + +import frappe +import os +from frappe.utils import get_files_path +from frappe.utils.file_manager import get_content_hash, get_file + + +def execute(): + frappe.reload_doc('core', 'doctype', 'file_data') + for name, file_name, file_url in frappe.db.sql( + """select name, file_name, file_url from `tabFile Data` + where file_name is not null"""): + b = frappe.get_doc('File Data', name) + old_file_name = b.file_name + b.file_name = os.path.basename(old_file_name) + if old_file_name.startswith('files/') or old_file_name.startswith('/files/'): + 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() + diff --git a/frappe/patches/4_0/rename_profile_to_user.py b/frappe/patches/4_0/rename_profile_to_user.py index 6c2b984429..66b8752da6 100644 --- a/frappe/patches/4_0/rename_profile_to_user.py +++ b/frappe/patches/4_0/rename_profile_to_user.py @@ -1,6 +1,7 @@ import frappe from frappe.model import rename_field +from frappe.model.meta import get_table_columns def execute(): tables = frappe.db.sql_list("show tables") @@ -9,6 +10,7 @@ def execute(): if frappe.db.exists("DocType", "Website Route Permission"): frappe.reload_doc("website", "doctype", "website_route_permission") - rename_field("Website Route Permission", "profile", "user") + if "profile" in get_table_columns("Website Route Permission"): + rename_field("Website Route Permission", "profile", "user") frappe.reload_doc("website", "doctype", "blogger") rename_field("Blogger", "profile", "user") diff --git a/frappe/patches/4_0/update_datetime.py b/frappe/patches/4_0/update_datetime.py index c87b2bee46..672dd1940b 100644 --- a/frappe/patches/4_0/update_datetime.py +++ b/frappe/patches/4_0/update_datetime.py @@ -6,4 +6,6 @@ def execute(): if field[1]=="datetime": frappe.db.sql("alter table `%s` change `%s` `%s` datetime(6)" % \ (table, field[0], field[0])) - \ No newline at end of file + elif field[1]=="time": + frappe.db.sql("alter table `%s` change `%s` `%s` time(6)" % \ + (table, field[0], field[0])) diff --git a/frappe/permissions.py b/frappe/permissions.py index 07006fb516..8a10f390f5 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -13,39 +13,39 @@ def check_admin_or_system_manager(): if ("System Manager" not in frappe.get_roles()) and \ (frappe.session.user!="Administrator"): msgprint("Only Allowed for Role System Manager or Administrator", raise_exception=True) - + def has_permission(doctype, ptype="read", doc=None, verbose=True): """check if user has permission""" if frappe.is_table(doctype): return True - + meta = frappe.get_meta(doctype) - + if ptype=="submit" and not cint(meta.is_submittable): return False - + if ptype=="import" and not cint(meta.allow_import): return False - + if frappe.session.user=="Administrator": return True - + # get user permissions if not get_user_perms(meta).get(ptype): return False - + if doc: if isinstance(doc, basestring): - doc = frappe.doc(meta.name, doc) - + doc = frappe.get_doc(meta.name, doc) + if not has_unrestricted_access(doc, verbose=verbose): return False - + if not has_controller_permissions(doc): return False return True - + def get_user_perms(meta, user=None): if not user: user = frappe.session.user @@ -53,7 +53,7 @@ def get_user_perms(meta, user=None): if not frappe.local.user_perms.get(cache_key): perms = frappe._dict() user_roles = frappe.get_roles(user) - + for p in meta.permissions: if cint(p.permlevel)==0 and (p.role in user_roles): for ptype in rights: @@ -61,16 +61,16 @@ def get_user_perms(meta, user=None): perms[ptype] = perms.get(ptype, 1) and cint(p.get(ptype)) else: perms[ptype] = perms.get(ptype, 0) or cint(p.get(ptype)) - + frappe.local.user_perms[cache_key] = perms return frappe.local.user_perms[cache_key] - + def has_unrestricted_access(doc, verbose=True): from frappe.defaults import get_restrictions restrictions = get_restrictions() - meta = frappe.get_meta(doc.get("doctype")) + meta = frappe.get_meta(doc.get("doctype")) if get_user_perms(meta).restricted: if doc.owner == frappe.session.user: # owner is always allowed for restricted permissions @@ -80,58 +80,67 @@ def has_unrestricted_access(doc, verbose=True): else: if not restrictions: return True - - # evaluate specific restrictions - fields_to_check = meta.get_restricted_fields(restrictions.keys()) - - has_restricted_data = False - for df in fields_to_check: - if doc.get(df.fieldname) and doc.get(df.fieldname) not in restrictions[df.options]: - if verbose: - msg = "{not_allowed}: {doctype} {having} {label} = {value}".format( - not_allowed=_("Sorry, you are not allowed to access"), doctype=_(df.options), - having=_("having"), label=_(df.label), value=doc.get(df.fieldname)) - - if doc.parentfield: - msg = "{doctype}, {row} #{idx}, ".format(doctype=_(doc.doctype), - row=_("Row"), idx=doc.idx) + msg - - msgprint(msg) - - has_restricted_data = True - + + def _has_unrestricted_access(d): + meta = frappe.get_meta(d.get("doctype")) + + # evaluate specific restrictions + fields_to_check = meta.get_restricted_fields(restrictions.keys()) + + _has_restricted_data = False + for df in fields_to_check: + if d.get(df.fieldname) and d.get(df.fieldname) not in restrictions[df.options]: + if verbose: + msg = "{not_allowed}: {doctype} {having} {label} = {value}".format( + not_allowed=_("Sorry, you are not allowed to access"), doctype=_(df.options), + having=_("having"), label=_(df.label), value=d.get(df.fieldname)) + + if d.parentfield: + msg = "{doctype}, {row} #{idx}, ".format(doctype=_(d.doctype), + row=_("Row"), idx=d.idx) + msg + + msgprint(msg) + + _has_restricted_data = True + + return _has_restricted_data + + has_restricted_data = _has_unrestricted_access(doc) + for d in doc.get_all_children(): + has_restricted_data = _has_unrestricted_access(d) or has_restricted_data + # check all restrictions before returning return False if has_restricted_data else True - + def has_controller_permissions(doc): if doc.get("__islocal"): - bean = frappe.bean([doc]) + doc = frappe.get_doc(doc) else: - bean = frappe.bean(doc.doctype, doc.name) - + doc = frappe.get_doc(doc.doctype, doc.name) + condition_methods = frappe.get_hooks("has_permission:" + doc.doctype) for method in frappe.get_hooks("has_permission:" + doc.doctype): - if not frappe.call(frappe.get_attr(method), doc=doc, bean=bean): + if not frappe.call(frappe.get_attr(method), doc=doc): return False - + return True - + def can_restrict_user(user, doctype, docname=None): if not can_restrict(doctype, docname): return False - + # check if target user does not have restrict permission if has_only_non_restrict_role(doctype, user): return True - + return False - + def can_restrict(doctype, docname=None): # System Manager can always restrict if "System Manager" in frappe.get_roles(): return True meta = frappe.get_meta(doctype) - + # check if current user has read permission for docname if docname and not has_permission(doctype, "read", docname): return False @@ -139,18 +148,18 @@ def can_restrict(doctype, docname=None): # check if current user has a role with restrict permission if not has_restrict_permission(meta): return False - + return True - + def has_restrict_permission(meta=None, user=None): return get_user_perms(meta, user).restrict==1 - + def has_only_non_restrict_role(doctype, user): meta = frappe.get_meta(doctype) # check if target user does not have restrict permission if has_restrict_permission(meta, user): return False - + # and has non-restrict role return get_user_perms(meta, user).restrict==0 @@ -161,7 +170,7 @@ def can_import(doctype, raise_exception=False): else: return False return True - + def can_export(doctype, raise_exception=False): if not ("System Manager" in frappe.get_roles() or has_permission(doctype, "export")): if raise_exception: @@ -169,4 +178,3 @@ def can_export(doctype, raise_exception=False): else: return False return True - \ No newline at end of file diff --git a/frappe/public/build.json b/frappe/public/build.json index 29134210f3..fd05d16b7f 100644 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -52,7 +52,7 @@ "public/js/lib/bootstrap.min.js", "public/js/lib/nprogress.js", "public/js/lib/beautify-html.js", - + "public/js/frappe/provide.js", "public/js/frappe/class.js", "public/js/frappe/translate.js", @@ -80,10 +80,9 @@ "public/js/frappe/ui/dialog.js", "public/js/frappe/ui/button.js", "public/js/frappe/ui/app_icon.js", - + "public/js/frappe/model/model.js", "public/js/frappe/model/meta.js", - "public/js/frappe/model/doclist.js", "public/js/frappe/model/sync.js", "public/js/frappe/model/create_new.js", "public/js/frappe/model/perm.js", @@ -118,7 +117,7 @@ "public/js/frappe/form/formatters.js", "public/js/legacy/layout.js", - + "public/js/frappe/ui/toolbar/selector_dialog.js", "public/js/frappe/ui/toolbar/new.js", "public/js/frappe/ui/toolbar/search.js", @@ -127,7 +126,7 @@ "public/js/frappe/ui/toolbar/bookmarks.js", "public/js/frappe/ui/toolbar/toolbar.js", "public/js/frappe/ui/editor.js", - + "public/js/legacy/form.js", "public/js/legacy/print_format.js", "public/js/legacy/clientscriptAPI.js", @@ -135,6 +134,7 @@ "public/js/frappe/form/toolbar.js", "public/js/frappe/form/infobar.js", "public/js/frappe/form/dashboard.js", + "public/js/frappe/form/save.js", "public/js/frappe/form/script_manager.js", "public/js/frappe/form/control.js", "public/js/frappe/form/link_selector.js", diff --git a/frappe/public/css/appframe.css b/frappe/public/css/appframe.css index 324cf23af5..8c965e9259 100644 --- a/frappe/public/css/appframe.css +++ b/frappe/public/css/appframe.css @@ -8,6 +8,16 @@ border-bottom: 1px solid #c7c7c7; } +.appframe-titlebar, .appframe-footer { + background: url(/assets/frappe/images/ui/sos.png) repeat; + -webkit-box-shadow: inset 0 0 7px rgba(0, 0, 0, .07); + box-shadow: inset 0 0 7px rgba(0, 0, 0, .07); +} + +.appframe-iconbar { + border-bottom: 1px solid #eee; +} + .titlebar-item { padding-top: 15px; padding-bottom: 15px; @@ -23,7 +33,7 @@ } .titlebar-item.text-left { - + } .titlebar-left-item { float: left; @@ -106,9 +116,6 @@ h2.titlebar-left-item { height: 28px; } -.appframe-iconbar { - background-color: #f7f7f7; -} .iconbar { display: inline-block; diff --git a/frappe/public/css/bootstrap.css b/frappe/public/css/bootstrap.css index b3d273b692..95f7a0fd3b 100644 --- a/frappe/public/css/bootstrap.css +++ b/frappe/public/css/bootstrap.css @@ -5845,4 +5845,3 @@ ul.with-margin li, ol.with-margin li { margin: 7px auto; } -/*# sourceMappingURL=bootstrap.css.map */ diff --git a/frappe/public/css/desk.css b/frappe/public/css/desk.css index 118da57ca2..30623fd2d5 100644 --- a/frappe/public/css/desk.css +++ b/frappe/public/css/desk.css @@ -7,7 +7,6 @@ body { height: 100%; margin: 0px; padding-top: 36px; - padding-bottom: 50px; } a { @@ -85,7 +84,7 @@ div#freeze { } .alert-badge a, .alert-badge a:hover { - + } .alert-badge .badge { @@ -93,14 +92,14 @@ div#freeze { } .icon-in-circle { - color: #b7b7b7; + color: #b7b7b7; background-color: white; - border-radius:50%; - text-align: center; - width: 20px; - height: 20px; - font-size: 20px; - display: inline-block; + border-radius:50%; + text-align: center; + width: 20px; + height: 20px; + font-size: 20px; + display: inline-block; } .form-layout { @@ -135,7 +134,7 @@ div#freeze { .doclist-row { position: relative; - padding-top: 5px; + padding-top: 5px; padding-bottom: 3px; border-bottom: "1px solid #eee"; } @@ -178,8 +177,8 @@ div#freeze { } .comm-header { - font-weight: bold; - height: 20px; + font-weight: bold; + height: 20px; overflow: hidden; } @@ -193,8 +192,8 @@ div#freeze { #alert-container { - position: fixed; - bottom: 8px; + position: fixed; + bottom: 8px; right: 8px; z-index: 10; } @@ -279,7 +278,7 @@ ul.linked-with-list li { background-color: #f8f8f8; padding: 6px; margin-bottom: 7px; - border-radius: 3px; + border-radius: 3px; min-height: 30px; } @@ -341,8 +340,8 @@ ul.linked-with-list li { /* hack */ .ui-datepicker { z-index: 9999999 !important; } -.ui-autocomplete { - z-index: 9999999 !important; +.ui-autocomplete { + z-index: 9999999 !important; } .bold, @@ -362,9 +361,9 @@ ul.linked-with-list li { float: left; margin-bottom: 5px; } - + .nav-stacked > li + li { margin-top: 0px; margin-left: 2px; } -} \ No newline at end of file +} diff --git a/frappe/public/images/ui/avatar.png b/frappe/public/images/ui/avatar.png deleted file mode 100644 index df9e7cf07f..0000000000 Binary files a/frappe/public/images/ui/avatar.png and /dev/null differ diff --git a/frappe/public/images/ui/cancelled.png b/frappe/public/images/ui/cancelled.png deleted file mode 100644 index 260c8af32e..0000000000 Binary files a/frappe/public/images/ui/cancelled.png and /dev/null differ diff --git a/frappe/public/images/ui/navbarsep.png b/frappe/public/images/ui/navbarsep.png deleted file mode 100644 index 86e54b2b17..0000000000 Binary files a/frappe/public/images/ui/navbarsep.png and /dev/null differ diff --git a/frappe/public/images/ui/sos.png b/frappe/public/images/ui/sos.png new file mode 100644 index 0000000000..ca28fcf8a6 Binary files /dev/null and b/frappe/public/images/ui/sos.png differ diff --git a/frappe/public/images/ui/sprinkles.png b/frappe/public/images/ui/sprinkles.png deleted file mode 100644 index 2d16b23475..0000000000 Binary files a/frappe/public/images/ui/sprinkles.png and /dev/null differ diff --git a/frappe/public/images/ui/submitted.png b/frappe/public/images/ui/submitted.png deleted file mode 100644 index 4e9999322a..0000000000 Binary files a/frappe/public/images/ui/submitted.png and /dev/null differ diff --git a/frappe/public/images/ui/vote_down.gif b/frappe/public/images/ui/vote_down.gif deleted file mode 100644 index 629839b07a..0000000000 Binary files a/frappe/public/images/ui/vote_down.gif and /dev/null differ diff --git a/frappe/public/images/ui/vote_up.gif b/frappe/public/images/ui/vote_up.gif deleted file mode 100644 index 3faaa6d6a5..0000000000 Binary files a/frappe/public/images/ui/vote_up.gif and /dev/null differ diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 564fa09d3d..7d2ed46f0e 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -78,7 +78,6 @@ frappe.Application = Class.extend({ load_bootinfo: function() { if(frappe.boot) { - frappe.control_panel = frappe.boot.control_panel; frappe.modules = frappe.boot.modules; this.check_metadata_cache_status(); this.set_globals(); diff --git a/frappe/public/js/frappe/form/attachments.js b/frappe/public/js/frappe/form/attachments.js index cbde3e16f5..1d9f7248ec 100644 --- a/frappe/public/js/frappe/form/attachments.js +++ b/frappe/public/js/frappe/form/attachments.js @@ -41,13 +41,14 @@ frappe.ui.form.Attachments = Class.extend({ this.$list.empty(); var attachments = this.get_attachments(); - var file_names = keys(attachments).sort(); + var that = this; + // add attachment objects - if(file_names.length) { - for(var i=0; i' + frappe._("None") + '

    ').appendTo(this.$list); } @@ -58,18 +59,23 @@ frappe.ui.form.Attachments = Class.extend({ get_attachments: function() { return this.frm.get_docinfo().attachments; }, - add_attachment: function(filename, attachments) { - var fileid = attachments[filename]; + add_attachment: function(attachment) { + var file_name = attachment.file_name; + var file_url = attachment.file_url; + var fileid = attachment.name; + if (!file_name) { + file_name = file_url; + } var me = this; var $attach = $(repl('
    \ \ - %(filename)s×\ + %(file_name)s×\
    ', { - filename: filename, - href: frappe.utils.get_file_link(filename) + file_name: file_name, + file_url: file_url })) .appendTo(this.$list) @@ -83,7 +89,7 @@ frappe.ui.form.Attachments = Class.extend({ me.remove_attachment($(remove_btn).data("fileid")) } ); - return false; + return false }); if(!frappe.model.can_write(this.frm.doctype, this.frm.name)) { @@ -131,9 +137,9 @@ frappe.ui.form.Attachments = Class.extend({ doctype: this.frm.doctype, docname: this.frm.docname, }, - callback: function(fileid, filename, r) { + callback: function(fileid, filename, file_url, r) { me.dialog.hide(); - me.update_attachment(fileid, filename, fieldname, r); + me.update_attachment(fileid, filename, file_url, fieldname, r.message); }, onerror: function() { me.dialog.hide(); @@ -142,26 +148,27 @@ frappe.ui.form.Attachments = Class.extend({ max_height: this.frm.cscript ? this.frm.cscript.attachment_max_height : null, }); }, - update_attachment: function(fileid, filename, fieldname, r) { + update_attachment: function(fileid, filename, fieldname, file_url, attachment) { if(fileid) { - this.add_to_attachments(fileid, filename); + this.add_to_attachments(attachment); this.refresh(); if(fieldname) { - this.frm.set_value(fieldname, frappe.utils.get_file_link(filename)); + this.frm.set_value(fieldname, file_url); this.frm.cscript[fieldname] && this.frm.cscript[fieldname](this.frm.doc); this.frm.toolbar.show_infobar(); } } }, - add_to_attachments: function(fileid, filename) { - this.get_attachments()[filename] = fileid; + add_to_attachments: function (attachment) { + this.get_attachments().push(attachment); }, remove_fileid: function(fileid) { var attachments = this.get_attachments(); - var new_attachments = {}; - $.each(attachments, function(key, value) { - if(value!=fileid) - new_attachments[key] = value; + var new_attachments = []; + $.each(attachments, function(i, attachment) { + if(attachment.name!=fileid) { + new_attachments.push(attachment); + } }); this.frm.get_docinfo().attachments = new_attachments; this.refresh(); @@ -181,4 +188,4 @@ frappe.ui.form.Attachments = Class.extend({ } } } -}); \ No newline at end of file +}); diff --git a/frappe/public/js/frappe/form/control.js b/frappe/public/js/frappe/form/control.js index 93ad0133ff..5a3d077113 100644 --- a/frappe/public/js/frappe/form/control.js +++ b/frappe/public/js/frappe/form/control.js @@ -37,27 +37,27 @@ frappe.ui.form.Control = Class.extend({ this.wrapper = this.$wrapper.get(0); this.wrapper.fieldobj = this; // reference for event handlers }, - + make_wrapper: function() { this.$wrapper = $("
    ").appendTo(this.parent); }, - - // returns "Read", "Write" or "None" + + // returns "Read", "Write" or "None" // as strings based on permissions get_status: function(explain) { if(!this.doctype) return "Write"; - return frappe.perm.get_field_display_status(this.df, + return frappe.perm.get_field_display_status(this.df, locals[this.doctype][this.docname], this.perm || this.frm.perm, explain); }, refresh: function() { this.disp_status = this.get_status(); - this.$wrapper + this.$wrapper && this.$wrapper.toggle(this.disp_status!="None") && this.$wrapper.trigger("refresh"); }, get_doc: function() { - return this.doctype && this.docname + return this.doctype && this.docname && locals[this.doctype] && locals[this.doctype][this.docname] || {}; }, parse_validate_and_set_in_model: function(value) { @@ -66,7 +66,7 @@ frappe.ui.form.Control = Class.extend({ if(this.parse) value = this.parse(value); var set = function(value) { - me.set_model_value(value); + me.set_model_value(value); me.inside_change_event = false; me.set_mandatory && me.set_mandatory(value); } @@ -75,12 +75,12 @@ frappe.ui.form.Control = Class.extend({ }, get_parsed_value: function() { var me = this; - return this.get_value ? - (this.parse ? this.parse(this.get_value()) : this.get_value()) : + return this.get_value ? + (this.parse ? this.parse(this.get_value()) : this.get_value()) : undefined; }, set_model_value: function(value) { - if(frappe.model.set_value(this.doctype, this.docname, this.df.fieldname, + if(frappe.model.set_value(this.doctype, this.docname, this.df.fieldname, value, this.df.fieldtype)) { this.last_value = value; } @@ -105,7 +105,7 @@ frappe.ui.form.ControlImage = frappe.ui.form.Control.extend({ var me = this; this.$wrapper = $("
    ").appendTo(this.parent); this.$body = $("
    ").appendTo(this.$wrapper) - .css({"margin-bottom": "10px", "margin-right": "15px", "float": "right", + .css({"margin-bottom": "10px", "margin-right": "15px", "float": "right", "text-align": "right", "max-width": "100%"}) this.$wrapper.on("refresh", function() { me.$body.empty(); @@ -128,7 +128,7 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ // parent element this._super(); this.set_input_areas(); - + // set description this.set_max_width(); this.setup_update_on_refresh(); @@ -169,7 +169,7 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ this.$wrapper.css({"max-width": "600px"}); } }, - + // update input value, label, description // display (show/hide/read-only), // mandatory style on refresh @@ -181,7 +181,7 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ if(me.doctype && me.docname) { me.value = frappe.model.get_value(me.doctype, me.docname, me.df.fieldname); } - + if(me.disp_status=="Write") { me.disp_area && $(me.disp_area).toggle(false); $(me.input_area).toggle(true); @@ -198,7 +198,7 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ frappe.format(me.value, me.df, {no_icon:true}, locals[me.doctype][me.name]) ); } - + me.set_description(); me.set_label(); me.set_mandatory(me.value); @@ -208,7 +208,7 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ }, bind_change_event: function() { var me = this; - this.$input && this.$input.on("change", this.change || function(e) { + this.$input && this.$input.on("change", this.change || function(e) { if(me.doctype && me.docname && me.get_value) { me.parse_validate_and_set_in_model(me.get_value()); } else { @@ -218,9 +218,9 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ value = me.parse(value); } if(me.validate) { - me.validate(value, function(value1) { + me.validate(value, function(value1) { if(value !== value1) - me.set_input(value1) + me.set_input(value1) }); } } @@ -228,10 +228,10 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ }, set_label: function(label) { if(label) this.df.label = label; - - if(this.only_input || this.df.label==this._label) + + if(this.only_input || this.df.label==this._label) return; - + // var icon = frappe.ui.form.fieldtype_icons[this.df.fieldtype]; // if(this.df.fieldtype==="Link") { // icon = frappe.boot.doctype_icons[this.df.options]; @@ -239,12 +239,12 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ // icon = frappe.boot.doctype_icons[this.df.link_doctype]; // } var icon = ""; - this.label_span.innerHTML = (icon ? ' ' : "") + + this.label_span.innerHTML = (icon ? ' ' : "") + frappe._(this.df.label) || " "; this._label = this.df.label; }, set_description: function() { - if(this.only_input || this.df.description===this._description) + if(this.only_input || this.df.description===this._description) return; if(this.df.description) { this.$wrapper.find(".help-box").html(frappe._(this.df.description)); @@ -254,10 +254,10 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ this._description = this.df.description; }, set_empty_description: function() { - this.$wrapper.find(".help-box").html(""); + this.$wrapper.find(".help-box").html(""); }, set_mandatory: function(value) { - this.$wrapper.toggleClass("has-error", (this.df.reqd + this.$wrapper.toggleClass("has-error", (this.df.reqd && (value==null || value==="")) ? true : false); }, }); @@ -270,7 +270,7 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({ .attr("type", this.input_type) .addClass("input-with-feedback form-control") .prependTo(this.input_area) - + this.set_input_attributes(); this.input = this.$input.get(0); this.has_input = true; @@ -310,7 +310,7 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({ } if(v && v.substr(0,2)=='00') { v1 += '00'; v = v.substr(2); - } + } if(v && v.substr(0,1)=='0') { v1 += '0'; v = v.substr(1); } @@ -332,7 +332,7 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({ frappe.ui.form.ControlReadOnly = frappe.ui.form.ControlData.extend({ get_status: function(explain) { var status = this._super(explain); - if(status==="Write") + if(status==="Write") status = "Read"; return; } @@ -350,12 +350,12 @@ frappe.ui.form.ControlInt = frappe.ui.form.ControlData.extend({ this.$input .css({"text-align": "right"}) .on("focus", function() { - setTimeout(function() { + setTimeout(function() { if(!document.activeElement) return; me.validate(document.activeElement.value, function(val) { document.activeElement.value = val; }); - document.activeElement.select() + document.activeElement.select() }, 100); return false; }) @@ -370,7 +370,7 @@ frappe.ui.form.ControlFloat = frappe.ui.form.ControlInt.extend({ return callback(isNaN(parseFloat(value)) ? null : flt(value)); }, format_for_input: function(value) { - var formatted_value = format_number(parseFloat(value), + var formatted_value = format_number(parseFloat(value), null, cint(frappe.boot.sysdefaults.float_precision, null)); return isNaN(parseFloat(value)) ? "" : formatted_value; } @@ -378,7 +378,7 @@ frappe.ui.form.ControlFloat = frappe.ui.form.ControlInt.extend({ frappe.ui.form.ControlCurrency = frappe.ui.form.ControlFloat.extend({ format_for_input: function(value) { - var formatted_value = format_number(parseFloat(value), + var formatted_value = format_number(parseFloat(value), get_number_format(this.get_currency())); return isNaN(parseFloat(value)) ? "" : formatted_value; }, @@ -391,7 +391,7 @@ frappe.ui.form.ControlPercent = frappe.ui.form.ControlFloat; frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({ datepicker_options: { - altFormat:'yy-mm-dd', + altFormat:'yy-mm-dd', changeYear: true, yearRange: "-70Y:+10Y", }, @@ -400,7 +400,7 @@ frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({ this.set_datepicker(); }, set_datepicker: function() { - this.datepicker_options.dateFormat = + this.datepicker_options.dateFormat = (frappe.boot.sysdefaults.date_format || 'yyyy-mm-dd').replace("yyyy", "yy") this.$input.datepicker(this.datepicker_options); }, @@ -424,7 +424,7 @@ import_timepicker = function() { frappe.require("assets/frappe/js/lib/jquery/jquery.ui.slider.min.js"); frappe.require("assets/frappe/js/lib/jquery/jquery.ui.sliderAccess.js"); frappe.require("assets/frappe/js/lib/jquery/jquery.ui.timepicker-addon.css"); - frappe.require("assets/frappe/js/lib/jquery/jquery.ui.timepicker-addon.js"); + frappe.require("assets/frappe/js/lib/jquery/jquery.ui.timepicker-addon.js"); } frappe.ui.form.ControlTime = frappe.ui.form.ControlData.extend({ @@ -440,16 +440,16 @@ frappe.ui.form.ControlTime = frappe.ui.form.ControlData.extend({ frappe.ui.form.ControlDatetime = frappe.ui.form.ControlDate.extend({ set_datepicker: function() { this.datepicker_options.timeFormat = "hh:mm:ss"; - this.datepicker_options.dateFormat = + this.datepicker_options.dateFormat = (frappe.boot.sysdefaults.date_format || 'yy-mm-dd').replace('yyyy','yy'); - + this.$input.datetimepicker(this.datepicker_options); }, make_input: function() { import_timepicker(); this._super(); }, - + }); frappe.ui.form.ControlText = frappe.ui.form.ControlData.extend({ @@ -479,7 +479,7 @@ frappe.ui.form.ControlCheck = frappe.ui.form.ControlData.extend({ set_input_areas: function() { this.label_area = this.label_span = this.$wrapper.find(".label-area").get(0); this.input_area = this.$wrapper.find(".input-area").get(0); - this.disp_area = this.$wrapper.find(".disp-area").get(0); + this.disp_area = this.$wrapper.find(".disp-area").get(0); }, make_input: function() { this._super(); @@ -551,7 +551,7 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ this.input = this.$input.get(0); this.set_input_attributes(); this.has_input = true; - + this.$value.find(".close").on("click", function() { if(me.frm) { me.frm.attachments.remove_attachment_by_filename(me.value, function() { @@ -574,12 +574,12 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ } $(this.dialog.body).empty(); - - this.set_upload_options(); + + this.set_upload_options(); frappe.upload.make(this.upload_options); this.dialog.show(); }, - + set_upload_options: function() { var me = this; this.upload_options = { @@ -587,7 +587,7 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ args: {}, max_width: this.df.max_width, max_height: this.df.max_height, - callback: function(fileid, filename, r) { + callback: function(fileid, filename, r) { me.dialog.hide(); me.on_upload_complete(fileid, filename, r); }, @@ -595,13 +595,13 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ me.dialog.hide(); }, } - + if(this.frm) { this.upload_options.args = { from_form: 1, doctype: this.frm.doctype, docname: this.frm.docname, - } + } } else { this.upload_options.on_attach = function(fileobj, dataurl) { me.dialog.hide(); @@ -617,7 +617,7 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ } } }, - + set_input: function(value, dataurl) { this.value = value; if(this.value) { @@ -630,7 +630,7 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ this.$value.toggle(false); } }, - + get_value: function() { if(this.frm) { return this.value; @@ -638,7 +638,7 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ return this.fileobj ? (this.fileobj.filename + "," + this.dataurl) : null; } }, - + on_upload_complete: function(fileid, filename, r) { if(this.frm) { this.parse_validate_and_set_in_model(filename); @@ -657,7 +657,7 @@ frappe.ui.form.ControlAttachImage = frappe.ui.form.ControlAttach.extend({ this.img = $("").appendTo($('
    \
    ') .prependTo(this.input_area)).toggle(false); - + var me = this; this.$wrapper.on("refresh", function() { if(me.value) { @@ -711,7 +711,7 @@ frappe.ui.form.ControlSelect = frappe.ui.form.ControlData.extend({ me.frm.attachments.new_attachment(me.df.fieldname); }) .prependTo(this.input_area); - + $(document).on("upload_complete", function(event, filename, file_url) { if(cur_frm === me.frm) { me.set_options(); @@ -820,7 +820,7 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ } else { this.$input_area.find(".btn-open").remove(); } - + // new if(frappe.model.can_create(me.df.options)) { this.$input_area.find(".btn-new").on("click", function() { @@ -829,12 +829,12 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ } else { this.$input_area.find(".btn-new").remove(); } - + if(this.only_input) this.$input_area.find(".btn-open, .btn-new").remove(); }, setup_autocomplete: function() { var me = this; - this.$input.on("blur", function() { + this.$input.on("blur", function() { if(me.selected) { me.selected = false; return; @@ -849,12 +849,12 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ this.$input.autocomplete({ source: function(request, response) { var args = { - 'txt': request.term, + 'txt': request.term, 'doctype': me.df.options, }; - + me.set_custom_query(args); - + return frappe.call({ type: "GET", method:'frappe.widgets.search.search_link', @@ -935,7 +935,7 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ callback(value); return; } - + this.frm.script_manager.validate_link_and_fetch(this.df, this.docname, value, callback); }, }); @@ -978,15 +978,17 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({ frappe.ui.form.ControlTable = frappe.ui.form.Control.extend({ make: function() { this._super(); - + // add title if prev field is not column / section heading or html - var prev_fieldtype = frappe.model.get("DocField", - {parent: this.frm.doctype, idx: this.df.idx-1})[0].fieldtype; - - if(["Column Break", "Section Break", "HTML"].indexOf(prev_fieldtype)===-1) { - $("
  • \ %(action)s
  • ', d)) @@ -160,7 +160,7 @@ frappe.ui.form.States = Class.extend({ // revert state on error var on_error = function() { // reset in locals - locals[me.frm.doctype][me.frm.docname] = doc_before_action; + frappe.model.add_to_locals(doc_before_action); me.frm.refresh(); } diff --git a/frappe/public/js/frappe/misc/datetime.js b/frappe/public/js/frappe/misc/datetime.js index 687aaf64b8..782b1b5a69 100644 --- a/frappe/public/js/frappe/misc/datetime.js +++ b/frappe/public/js/frappe/misc/datetime.js @@ -40,11 +40,11 @@ $.extend(frappe.datetime, { }, now_time: function() { var d = new Date(); - return [double_digit(d.getHours()), double_digit(d.getMinutes()), double_digit(d.getSeconds())].join(":") + return [double_digit(d.getHours()), double_digit(d.getMinutes()), double_digit(d.getSeconds())].join(":") + "." + d.getMilliseconds(); }, get_datetime_as_string: function(d) { if(!d) return null; return [d.getFullYear(), double_digit(d.getMonth()+1), double_digit(d.getDate())].join("-") + " " - + [double_digit(d.getHours()), double_digit(d.getMinutes()), double_digit(d.getSeconds())].join(":"); + + [double_digit(d.getHours()), double_digit(d.getMinutes()), double_digit(d.getSeconds())].join(":") + "." + d.getMilliseconds(); } }); \ No newline at end of file diff --git a/frappe/public/js/frappe/model/create_new.js b/frappe/public/js/frappe/model/create_new.js index 48bd5c3e5f..0da4f2d556 100644 --- a/frappe/public/js/frappe/model/create_new.js +++ b/frappe/public/js/frappe/model/create_new.js @@ -7,7 +7,7 @@ $.extend(frappe.model, { new_names: {}, new_name_count: {}, - get_new_doc: function(doctype, parent_doc) { + get_new_doc: function(doctype, parent_doc, parentfield) { frappe.provide("locals." + doctype); var doc = { docstatus: 0, @@ -18,28 +18,40 @@ $.extend(frappe.model, { owner: user }; frappe.model.set_default_values(doc, parent_doc); - locals[doctype][doc.name] = doc; - frappe.provide("frappe.model.docinfo." + doctype + "." + doc.name); + + if(parent_doc) { + $.extend(doc, { + parent: parent_doc.name, + parentfield: parentfield, + parenttype: parent_doc.doctype, + }); + if(!parent_doc[parentfield]) parent_doc[parentfield] = []; + parent_doc[parentfield].push(doc); + } else { + frappe.provide("frappe.model.docinfo." + doctype + "." + doc.name); + } + + frappe.model.add_to_locals(doc); return doc; }, - + make_new_doc_and_get_name: function(doctype) { return frappe.model.get_new_doc(doctype).name; }, - + get_new_name: function(doctype) { var cnt = frappe.model.new_name_count - if(!cnt[doctype]) + if(!cnt[doctype]) cnt[doctype] = 0; cnt[doctype]++; return frappe._('New') + ' '+ frappe._(doctype) + ' ' + cnt[doctype]; }, - + set_default_values: function(doc, parent_doc) { var doctype = doc.doctype; var docfields = frappe.meta.docfield_list[doctype] || []; var updated = []; - + for(var fid=0;fid 0) { + if(frappe.get_list(df["default"]).length > 0) { var ref_fieldname = df["default"].slice(1).toLowerCase().replace(" ", "_"); - var ref_value = parent_doc ? + var ref_value = parent_doc ? parent_doc[ref_fieldname] : frappe.defaults.get_user_default(ref_fieldname); - var ref_doc = ref_value ? frappe.model.get_doc(df["default"], ref_value) : null; - + var ref_doc = ref_value ? frappe.get_doc(df["default"], ref_value) : null; + if(ref_doc && ref_doc[df.fieldname]) { return ref_doc[df.fieldname]; } } }, - + add_child: function(parent_doc, doctype, parentfield, idx) { // create row doc - idx = idx ? - idx - 0.1 : - frappe.model.get_children(doctype, parent_doc.name, parentfield, - parent_doc.doctype).length + 1; + idx = idx ? idx - 0.1 : (parent_doc[parentfield] || []).length + 1; + + var d = frappe.model.get_new_doc(doctype, parent_doc, parentfield); + d.idx = idx; - var d = frappe.model.get_new_doc(doctype, parent_doc); - $.extend(d, { - parent: parent_doc.name, - parentfield: parentfield, - parenttype: parent_doc.doctype, - idx: idx - }); - // renum for fraction - idx !== cint(idx) && - frappe.model.get_children(doctype, parent_doc.name, parentfield, - parent_doc.doctype); + if(idx !== cint(idx)) { + var sorted = parent_doc[parentfield].sort(function(a, b) { return a.idx - b.idx; }); + $.each(sorted, function(i, d) { + d.idx = i + 1; + }); + } cur_frm && cur_frm.dirty(); - + return d; }, - - copy_doc: function(dt, dn, from_amend) { - var no_copy_list = ['name','amended_from','amendment_date','cancel_reason']; - var newdoc = frappe.model.get_new_doc(dt); - for(var key in locals[dt][dn]) { + copy_doc: function(doc, from_amend) { + var no_copy_list = ['name','amended_from','amendment_date','cancel_reason']; + var newdoc = frappe.model.get_new_doc(doc.doctype); + + for(var key in doc) { // dont copy name and blank fields - var df = frappe.meta.get_docfield(dt, key); - - if(key.substr(0,2)!='__' - && !in_list(no_copy_list, key) - && !(df && (!from_amend && cint(df.no_copy)==1))) { - newdoc[key] = locals[dt][dn][key]; + var df = frappe.meta.get_docfield(doc.doctype, key); + + if(df && key.substr(0,2)!='__' + && !in_list(no_copy_list, key) + && !(df && (!from_amend && cint(df.no_copy)==1))) { + value = doc[key]; + if(df.fieldtype==="Table") { + newdoc[key] = []; + $.each(value || [], function(i, d) { + newdoc[key].push(frappe.model.copy_doc(d, from_amend)) + newdoc[key][newdoc[key].length-1].idx = newdoc[key].length; + }) + } else { + newdoc[key] = doc[key]; + } } } + + newdoc.__islocal = 1; + newdoc.docstatus = 0; + newdoc.owner = user; + newdoc.creation = ''; + newdoc.modified_by = user; + newdoc.modified = ''; + return newdoc; }, - + open_mapped_doc: function(opts) { return frappe.call({ type: "GET", @@ -167,7 +191,7 @@ $.extend(frappe.model, { method: opts.method, args: { "source_name": opts.source_name, - "target_doclist": frappe.model.get_doclist(cur_frm.doc.doctype, cur_frm.doc.name) + "target_doclist": cur_frm.doc }, callback: function(r) { if(!r.exc) { @@ -182,10 +206,10 @@ $.extend(frappe.model, { title: frappe._("Get From ") + frappe._(opts.source_doctype), fields: [ { - "fieldtype": "Link", + "fieldtype": "Link", "label": frappe._(opts.source_doctype), - "fieldname": opts.source_doctype, - "options": opts.source_doctype, + "fieldname": opts.source_doctype, + "options": opts.source_doctype, "get_query": opts.get_query, reqd:1}, { @@ -193,7 +217,7 @@ $.extend(frappe.model, { "label": frappe._("Get"), click: function() { var values = d.get_values(); - if(!values) + if(!values) return; opts.source_name = values[opts.source_doctype]; d.hide(); @@ -207,4 +231,4 @@ $.extend(frappe.model, { _map(); } } -}) \ No newline at end of file +}) diff --git a/frappe/public/js/frappe/model/meta.js b/frappe/public/js/frappe/model/meta.js index f740ddfb34..cbd7fa88a2 100644 --- a/frappe/public/js/frappe/model/meta.js +++ b/frappe/public/js/frappe/model/meta.js @@ -8,6 +8,13 @@ frappe.provide('frappe.meta.doctypes'); frappe.provide("frappe.meta.precision_map"); $.extend(frappe.meta, { + sync: function(doc) { + $.each(doc.fields, function(i, df) { + frappe.meta.add_field(df); + }) + frappe.meta.sync_messages(doc); + }, + // build docfield_map and docfield_list add_field: function(df) { frappe.provide('frappe.meta.docfield_map.' + df.parent); @@ -84,8 +91,8 @@ $.extend(frappe.meta, { }, get_parentfield: function(parent_dt, child_dt) { - var df = frappe.model.get("DocField", {parent:parent_dt, fieldtype:"Table", - options:child_dt}) + var df = (frappe.get_doc("DocType", parent_dt).fields || []).filter(function(d) + { return d.fieldtype==="Table" && options===child_dt }) if(!df.length) throw "parentfield not found for " + parent_dt + ", " + child_dt; return df[0].fieldname; @@ -103,7 +110,7 @@ $.extend(frappe.meta, { var print_format_list = ["Standard"]; var default_print_format = locals.DocType[doctype].default_print_format; - var print_formats = frappe.model.get("Print Format", {doc_type: doctype}) + var print_formats = frappe.get_list("Print Format", {doc_type: doctype}) .sort(function(a, b) { return (a > b) ? 1 : -1; }); $.each(print_formats, function(i, d) { if(!in_list(print_format_list, d.name)) diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js index e02d2f5157..cbe401fc80 100644 --- a/frappe/public/js/frappe/model/model.js +++ b/frappe/public/js/frappe/model/model.js @@ -5,7 +5,7 @@ frappe.provide('frappe.model'); frappe.provide("frappe.model.map_info"); $.extend(frappe.model, { - no_value_type: ['Section Break', 'Column Break', 'HTML', 'Table', + no_value_type: ['Section Break', 'Column Break', 'HTML', 'Table', 'Button', 'Image'], std_fields_list: ['name', 'owner', 'creation', 'modified', 'modified_by', @@ -21,7 +21,7 @@ $.extend(frappe.model, { {fieldname:'_comments', fieldtype:'Text', label:'Comments'}, {fieldname:'docstatus', fieldtype:'Int', label:'Document Status'}, ], - + std_fields_table: [ {fieldname:'parent', fieldtype:'Data', label:'Parent'}, ], @@ -30,12 +30,12 @@ $.extend(frappe.model, { events: {}, get_std_field: function(fieldname) { - var docfield = $.map([].concat(frappe.model.std_fields).concat(frappe.model.std_fields_table), + var docfield = $.map([].concat(frappe.model.std_fields).concat(frappe.model.std_fields_table), function(d) { if(d.fieldname==fieldname) return d; }); if(!docfield.length) { - msgprint("Unknown Column: " + fieldname); + msgprint("Unknown Column: " + fieldname); } return docfield[0]; }, @@ -46,8 +46,8 @@ $.extend(frappe.model, { } else { var cached_timestamp = null; if(localStorage["_doctype:" + doctype]) { - var cached_doclist = JSON.parse(localStorage["_doctype:" + doctype]); - cached_timestamp = cached_doclist[0].modified; + var cached_doc = JSON.parse(localStorage["_doctype:" + doctype]); + cached_timestamp = cached_doc.modified; } return frappe.call({ method:'frappe.widgets.form.load.getdoctype', @@ -64,7 +64,7 @@ $.extend(frappe.model, { return; } if(r.message=="use_cache") { - frappe.model.sync(cached_doclist); + frappe.model.sync(cached_doc); } else { localStorage["_doctype:" + doctype] = JSON.stringify(r.docs); } @@ -75,7 +75,7 @@ $.extend(frappe.model, { }); } }, - + init_doctype: function(doctype) { var meta = locals.DocType[doctype]; if(meta.__list_js) { @@ -86,9 +86,9 @@ $.extend(frappe.model, { } if(meta.__map_js) { eval(meta.__map_js); - } + } }, - + with_doc: function(doctype, name, callback) { if(!name) name = doctype; // single type if(locals[doctype] && locals[doctype][name] && frappe.model.get_docinfo(doctype, name)) { @@ -105,18 +105,18 @@ $.extend(frappe.model, { }); } }, - + get_docinfo: function(doctype, name) { return frappe.model.docinfo[doctype] && frappe.model.docinfo[doctype][name] || null; }, - + get_server_module_name: function(doctype) { var dt = frappe.model.scrub(doctype); var module = frappe.model.scrub(locals.DocType[doctype].module); var app = frappe.boot.module_app[module]; return app + "." + module + '.doctype.' + dt + '.' + dt; }, - + scrub: function(txt) { return txt.replace(/ /g, "_").toLowerCase(); }, @@ -124,7 +124,7 @@ $.extend(frappe.model, { can_create: function(doctype) { return frappe.boot.user.can_create.indexOf(doctype)!==-1; }, - + can_read: function(doctype) { return frappe.boot.user.can_read.indexOf(doctype)!==-1; }, @@ -136,7 +136,7 @@ $.extend(frappe.model, { can_get_report: function(doctype) { return frappe.boot.user.can_get_report.indexOf(doctype)!==-1; }, - + can_delete: function(doctype) { if(!doctype) return false; return frappe.boot.user.can_delete.indexOf(doctype)!==-1; @@ -146,51 +146,51 @@ $.extend(frappe.model, { if(!doctype) return false; return frappe.boot.user.can_cancel.indexOf(doctype)!==-1; }, - + is_submittable: function(doctype) { if(!doctype) return false; return locals.DocType[doctype] && locals.DocType[doctype].is_submittable; }, - + can_import: function(doctype, frm) { // system manager can always import if(user_roles.indexOf("System Manager")!==-1) return true; - + if(frm) return frm.perm[0].import===1; return frappe.boot.user.can_import.indexOf(doctype)!==-1; }, - + can_export: function(doctype, frm) { // system manager can always export if(user_roles.indexOf("System Manager")!==-1) return true; - + if(frm) return frm.perm[0].export===1; return frappe.boot.user.can_export.indexOf(doctype)!==-1; }, - + can_print: function(doctype, frm) { if(frm) return frm.perm[0].print===1; return frappe.boot.user.can_print.indexOf(doctype)!==-1; }, - + can_email: function(doctype, frm) { if(frm) return frm.perm[0].email===1; return frappe.boot.user.can_email.indexOf(doctype)!==-1; }, - + can_restrict: function(doctype, frm) { // system manager can always restrict if(user_roles.indexOf("System Manager")!==-1) return true; - + if(frm) return frm.perm[0].restrict===1; return frappe.boot.user.can_restrict.indexOf(doctype)!==-1; }, - + has_value: function(dt, dn, fn) { // return true if property has value var val = locals[dt] && locals[dt][dn] && locals[dt][dn][fn]; var df = frappe.meta.get_docfield(dt, fn, dn); - + if(df.fieldtype=='Table') { var ret = false; $.each(locals[df.options] || {}, function(k,d) { @@ -205,31 +205,30 @@ $.extend(frappe.model, { return ret ? true : false; }, - get: function(doctype, filters) { - var src = locals[doctype] || locals[":" + doctype] || []; - if($.isEmptyObject(src)) + get_list: function(doctype, filters) { + var docsdict = locals[doctype] || locals[":" + doctype] || {}; + if($.isEmptyObject(docsdict)) return []; - return frappe.utils.filter_dict(src, filters); + return frappe.utils.filter_dict(docsdict, filters); }, - + get_value: function(doctype, filters, fieldname) { if(typeof filters==="string") { - return locals[doctype] && locals[doctype][filters] + return locals[doctype] && locals[doctype][filters] && locals[doctype][filters][fieldname]; } else { - var l = frappe.model.get(doctype, filters); + var l = frappe.get_list(doctype, filters); return (l.length && l[0]) ? l[0][fieldname] : null; } }, - - set_value: function(doctype, name, fieldname, value, fieldtype) { + + set_value: function(doctype, docname, fieldname, value, fieldtype) { /* help: Set a value locally (if changed) and execute triggers */ - if(!name) name = doctype; - var doc = locals[doctype] && locals[doctype][name] || null; - + var doc = locals[doctype] && locals[doctype][docname]; + if(doc && doc[fieldname] !== value) { doc[fieldname] = value; - frappe.model.trigger(fieldname, value, doc); + frappe.model.trigger(fieldname, value, doc); return true; } else { // execute link triggers (want to reselect to execute triggers) @@ -237,7 +236,7 @@ $.extend(frappe.model, { frappe.model.trigger(fieldname, value, doc); } }, - + on: function(doctype, fieldname, fn) { /* help: Attach a trigger on change of a particular field. To trigger on any change in a particular doctype, use fieldname as "*" @@ -254,7 +253,7 @@ $.extend(frappe.model, { } frappe.model.events[doctype][fieldname].push(fn); }, - + trigger: function(fieldname, value, doc) { var run = function(events, event_doc) { @@ -262,9 +261,9 @@ $.extend(frappe.model, { fn && fn(fieldname, value, event_doc || doc); }); }; - + if(frappe.model.events[doc.doctype]) { - + // field-level run(frappe.model.events[doc.doctype][fieldname]); @@ -272,62 +271,32 @@ $.extend(frappe.model, { run(frappe.model.events[doc.doctype]['*']); }; }, - + get_doc: function(doctype, name) { + if($.isPlainObject(name)) { + var doc = frappe.get_list(doctype, name); + return doc && doc.length ? doc[0] : null; + } return locals[doctype] ? locals[doctype][name] : null; }, - - get_doclist: function(doctype, name, filters) { - var doclist = []; - if(!locals[doctype]) - return doclist; - doclist[0] = locals[doctype][name]; - - $.each(frappe.model.get("DocField", {parent:doctype, fieldtype:"Table"}), - function(i, table_field) { - var child_doclist = frappe.model.get(table_field.options, { - parent:name, parenttype: doctype, - parentfield: table_field.fieldname}); - - if($.isArray(child_doclist)) { - child_doclist.sort(function(a, b) { return a.idx - b.idx; }); - doclist = doclist.concat(child_doclist); - } - } - ); - - if(filters) { - doclist = frappe.utils.filter_dict(doclist, filters); - } - - return doclist; - }, - - get_children: function(doctype, parent, parentfield, parenttype) { - if(parenttype) { - var l = frappe.model.get(doctype, {parent:parent, - parentfield:parentfield, parenttype:parenttype}); + get_children: function(doctype, parent, parentfield, filters) { + if($.isPlainObject(doctype)) { + var doc = doctype; + var filters = parentfield + var parentfield = parent; } else { - var l = frappe.model.get(doctype, {parent:parent, - parentfield:parentfield}); + var doc = frappe.get_doc(doctype, parent); } - if(l.length) { - l.sort(function(a,b) { return flt(a.idx) - flt(b.idx) }); - - // renumber - $.each(l, function(i, v) { v.idx = i+1; }); // for chrome bugs ??? + var children = doc[parentfield] || []; + if(filters) { + return frappe.utils.filter_dict(children, filters); + } else { + return children; } - return l; }, - clear_doclist: function(doctype, name) { - $.each(frappe.model.get_doclist(doctype, name), function(i, d) { - if(d) frappe.model.clear_doc(d.doctype, d.name); - }); - }, - clear_table: function(doctype, parenttype, parent, parentfield) { $.each(locals[doctype] || {}, function(i, d) { if(d.parent===parent && d.parenttype===parenttype && d.parentfield===parentfield) { @@ -337,7 +306,7 @@ $.extend(frappe.model, { }, remove_from_locals: function(doctype, name) { - this.clear_doclist(doctype, name); + this.clear_doc(doctype, name); if(frappe.views.formview[doctype]) { delete frappe.views.formview[doctype].frm.opendocs[name]; } @@ -345,30 +314,41 @@ $.extend(frappe.model, { clear_doc: function(doctype, name) { var doc = locals[doctype][name]; - + if(doc && doc.parenttype) { var parent = doc.parent, parenttype = doc.parenttype, parentfield = doc.parentfield; } delete locals[doctype][name]; - if(parent) - frappe.model.get_children(doctype, parent, parentfield, parenttype); + if(parent) { + var parent_doc = locals[parenttype][parent]; + var newlist = [], idx = 1; + $.each(parent_doc[parentfield], function(i, d) { + if(d.name!=name) { + newlist.push(d); + d.idx = idx; + idx++; + } + parent_doc[parentfield] = newlist; + }); + } }, - + get_no_copy_list: function(doctype) { var no_copy_list = ['name','amended_from','amendment_date','cancel_reason']; - $.each(frappe.model.get("DocField", {parent:doctype}), function(i, df) { + + $.each(frappe.get_doc("DocType", doctype).fields || [], function(i, df) { if(cint(df.no_copy)) no_copy_list.push(df.fieldname); }) return no_copy_list; }, - // args: source (doclist), target (doctype), table_map, field_map, callback + // args: source (doc), target (doc), table_map, field_map, callback map: function(args) { frappe.model.with_doctype(args.target, function() { var map_info = frappe.model.map_info[args.target] - if(map_info) + if(map_info) map_info = map_info[args.source[0].doctype]; if(!map_info) { map_info = { @@ -376,13 +356,13 @@ $.extend(frappe.model, { field_map: args.field_map || {} } } - + // main var target = frappe.model.map_doc(args.source[0], args.target, map_info.field_map[args.target]); - + // children $.each(map_info.table_map, function(child_target, child_source) { - $.each($.map(args.source, function(d) + $.each($.map(args.source, function(d) { if(d.doctype==child_source) return d; else return null; }), function(i, d) { var child = frappe.model.map_doc(d, child_target, map_info.field_map[child_target]); $.extend(child, { @@ -393,7 +373,7 @@ $.extend(frappe.model, { }); }); }); - + if(args.callback) { args.callback(target); } else { @@ -401,14 +381,14 @@ $.extend(frappe.model, { } }); }, - + // map a single doc to a new doc of given DocType and field_map map_doc: function(source, doctype, field_map) { var new_doc = frappe.model.get_new_doc(doctype); var no_copy_list = frappe.model.get_no_copy_list(doctype); if(!field_map) field_map = {}; delete no_copy_list[no_copy_list.indexOf("name")]; - + for(fieldname in frappe.meta.docfield_map[doctype]) { var df = frappe.meta.docfield_map[doctype][fieldname]; if(!df.no_copy) { @@ -425,19 +405,19 @@ $.extend(frappe.model, { } return new_doc; }, - + delete_doc: function(doctype, docname, callback) { frappe.confirm("Permanently delete "+ docname + "?", function() { return frappe.call({ method: 'frappe.client.delete', args: { - doctype: doctype, + doctype: doctype, name: docname }, callback: function(r, rt) { if(!r.exc) { - frappe.model.clear_doclist(doctype, docname); - if(frappe.ui.toolbar.recent) + frappe.model.clear_doc(doctype, docname); + if(frappe.ui.toolbar.recent) frappe.ui.toolbar.recent.remove(doctype, docname); if(callback) callback(r,rt); } @@ -445,7 +425,7 @@ $.extend(frappe.model, { }) }) }, - + rename_doc: function(doctype, docname, callback) { var d = new frappe.ui.Dialog({ title: "Rename " + docname, @@ -483,25 +463,38 @@ $.extend(frappe.model, { }); d.show(); }, - + round_floats_in: function(doc, fieldnames) { if(!fieldnames) { - fieldnames = frappe.meta.get_fieldnames(doc.doctype, doc.name, + fieldnames = frappe.meta.get_fieldnames(doc.doctype, doc.name, {"fieldtype": ["in", ["Currency", "Float"]]}); } $.each(fieldnames, function(i, fieldname) { doc[fieldname] = flt(doc[fieldname], precision(fieldname, doc)); }); }, - + validate_missing: function(doc, fieldname) { if(!doc[fieldname]) { - frappe.throw(frappe._("Please specify") + ": " + + frappe.throw(frappe._("Please specify") + ": " + frappe._(frappe.meta.get_label(doc.doctype, fieldname, doc.parent || doc.name))); } + }, + + get_all_docs: function(doc) { + var all = [doc]; + for(key in doc) { + if($.isArray(doc[key])) { + $.each(doc[key], function(i, d) { + all.push(d); + }); + } + } + return all; } }); // legacy -getchildren = frappe.model.get_children -make_doclist = frappe.model.get_doclist +frappe.get_doc = frappe.model.get_doc; +frappe.get_children = frappe.model.get_children; +frappe.get_list = frappe.model.get_list; diff --git a/frappe/public/js/frappe/model/perm.js b/frappe/public/js/frappe/model/perm.js index ea28d22459..86b1aa0405 100644 --- a/frappe/public/js/frappe/model/perm.js +++ b/frappe/public/js/frappe/model/perm.js @@ -37,18 +37,10 @@ $.extend(frappe.perm, { get_perm: function(doctype, docname) { var perm = [{read: 0}]; - var meta = frappe.model.get_doc("DocType", doctype); + var meta = frappe.get_doc("DocType", doctype); + if(!meta) { return perm; - } else if(meta.istable) { - // if a child table, use permissions of parent form - var parent_df = frappe.model.get("DocField", {fieldtype: "Table", options: doctype}); - if(parent_df.length) { - if(docname) { - docname = frappe.model.get_doc(doctype, docname).parent; - } - doctype = parent_df[0].parent; - } } if(user==="Administrator" || user_roles.indexOf("Administrator")!==-1) { @@ -60,7 +52,7 @@ $.extend(frappe.perm, { return perm; } - var docperms = frappe.model.get("DocPerm", {parent: doctype}); + var docperms = frappe.get_doc("DocType", doctype).permissions || []; $.each(docperms, function(i, p) { // if user has this role if(user_roles.indexOf(p.role)!==-1) { @@ -83,7 +75,7 @@ $.extend(frappe.perm, { has_unrestricted_access: function(doctype, docname, restricted) { var restrictions = frappe.defaults.get_restrictions(); - var doc = frappe.model.get_doc(doctype, docname); + var doc = frappe.get_doc(doctype, docname); if(restricted) { if(doc.owner==user) return true; @@ -101,7 +93,7 @@ $.extend(frappe.perm, { // loop and find if has restricted data var has_restricted_data = false; - var doc = frappe.model.get_doc(doctype, docname); + var doc = frappe.get_doc(doctype, docname); $.each(fields_to_check, function(i, df) { if(doc[df.fieldname] && restrictions[df.options].indexOf(doc[df.fieldname])===-1) { has_restricted_data = true; diff --git a/frappe/public/js/frappe/model/sync.js b/frappe/public/js/frappe/model/sync.js index cb1efaa22f..830e7e2c56 100644 --- a/frappe/public/js/frappe/model/sync.js +++ b/frappe/public/js/frappe/model/sync.js @@ -5,50 +5,34 @@ $.extend(frappe.model, { docinfo: {}, sync: function(r) { /* docs: - extract doclist, docinfo (attachments, comments, assignments) + extract docs, docinfo (attachments, comments, assignments) from incoming request and set in `locals` and `frappe.model.docinfo` */ if(!r.docs && !r.docinfo) r = {docs:r}; if(r.docs) { - var doclist = r.docs; - if(doclist._kl) - doclist = frappe.model.expand(doclist); - - if(doclist && doclist.length) - frappe.model.clear_doclist(doclist[0].doctype, doclist[0].name) - var last_parent_name = null; var dirty = []; - $.each(doclist, function(i, d) { + + $.each(r.docs, function(i, d) { if(!d.name && d.__islocal) { // get name (local if required) + frappe.model.clear_doc(d) d.name = frappe.model.get_new_name(d.doctype); frappe.provide("frappe.model.docinfo." + d.doctype + "." + d.name); - if(!d.parenttype) - last_parent_name = d.name; - - if(dirty.indexOf(d.parenttype || d.doctype)===-1) dirty.push(d.parenttype || d.doctype); } - // set parent for subsequent orphans - if(d.parenttype && !d.parent && d.__islocal) { - d.parent = last_parent_name; - } - - if(!locals[d.doctype]) - locals[d.doctype] = {}; - - locals[d.doctype][d.name] = d; + frappe.model.add_to_locals(d); d.__last_sync_on = new Date(); + + if(d.doctype==="DocType") { + frappe.meta.sync(d); + } if(cur_frm && cur_frm.doctype==d.doctype && cur_frm.docname==d.name) { cur_frm.doc = d; } - if(d.doctype=='DocField') frappe.meta.add_field(d); - if(d.doctype=='DocType') frappe.meta.sync_messages(d); - if(d.localname) { frappe.model.new_names[d.localname] = d.name; $(document).trigger('rename', [d.doctype, d.localname, d.name]); @@ -66,10 +50,10 @@ $.extend(frappe.model, { } - // set docinfo + // set docinfo (comments, assign, attachments) if(r.docinfo) { - if(doclist) { - var doc = doclist[0]; + if(r.docs) { + var doc = r.docs[0]; } else { var doc = cur_frm.doc; } @@ -78,68 +62,20 @@ $.extend(frappe.model, { frappe.model.docinfo[doc.doctype][doc.name] = r.docinfo; } - return doclist; + return r.docs; }, + add_to_locals: function(doc) { + if(!locals[doc.doctype]) locals[doc.doctype] = {}; + locals[doc.doctype][doc.name] = doc; + if(!doc.parent) { + $.each(doc, function(key, value) { + if($.isArray(value)) { + $.each(value, function(i, d) { + frappe.model.add_to_locals(d); + }); + } + }); + } + } - expand: function(data) { - function zip(k,v) { - var obj = {}; - for(var i=0;i|\s|

    <\/div>| )*$/, ''); @@ -135,8 +135,8 @@ bsEditor = Class.extend({ html = html.replace(/\s*style\s*=\s*["']\s*["']/g, ''); return html; } - }, - + }, + init_file_drops: function () { var me = this; this.editor.on('dragenter dragover', false) @@ -149,7 +149,7 @@ bsEditor = Class.extend({ } }); }, - + insert_files: function (files) { var me = this; this.editor.focus(); @@ -182,17 +182,17 @@ bsEditor = Class.extend({ } freader.readAsDataURL(fileobj); }, - + get_value: function() { return this.clean_html() }, - + set_input: function(value) { if(this.options.field && this.options.field.inside_change_event) return; this.editor.html(value==null ? "" : value); } - + }) bsEditorToolbar = Class.extend({ @@ -221,7 +221,7 @@ bsEditorToolbar = Class.extend({ padding: "5px", }, make: function(parent) { - if(!parent) + if(!parent) parent = $("body"); if(!parent.find(".frappe-list-toolbar").length) { this.toolbar = $('
    \ @@ -272,7 +272,7 @@ bsEditorToolbar = Class.extend({
    \ \
    ').prependTo(parent); - + if(this.inline) { this.toolbar.find("[data-action]").remove(); } else { @@ -280,7 +280,7 @@ bsEditorToolbar = Class.extend({ } } }, - + setup_image_button: function() { // magic-overlay var me = this; @@ -296,7 +296,7 @@ bsEditorToolbar = Class.extend({ me.file_input.trigger("click"); }) }, - + show: function() { var me = this; this.toolbar.toggle(true); @@ -317,14 +317,14 @@ bsEditorToolbar = Class.extend({ me.toolbar.toggle(false); }}); } - + this.editor && this.editor.attr('contenteditable', false).data("object").onhide(); this.editor = null; }, - + bind_events: function () { var me = this; - + // standard button events this.toolbar.find('a[data-' + me.options.command_role + ']').click(function () { me.restore_selection(); @@ -340,12 +340,19 @@ bsEditorToolbar = Class.extend({ // link this.toolbar.find(".btn-add-link").on("click", function() { - if(!window.bs_link_editor) { - window.bs_link_editor = new bsLinkEditor(me); + if(!me.toolbar.bs_link_editor) { + if(me.inline) { + me.toolbar.bs_link_editor = new bsLinkEditor(me); + } else { + if(!window.bs_link_editor) { + window.bs_link_editor = new bsLinkEditor(me); + } + me.toolbar.bs_link_editor = window.bs_link_editor; + } } - window.bs_link_editor.show(); - }) - + me.toolbar.bs_link_editor.show(); + }); + // file event this.toolbar.find('input[type=file][data-' + me.options.command_role + ']').change(function () { me.restore_selection(); @@ -356,7 +363,7 @@ bsEditorToolbar = Class.extend({ this.value = ''; return false; }); - + // save this.toolbar.find("[data-action='Save']").on("click", function() { me.hide(); }); @@ -364,7 +371,7 @@ bsEditorToolbar = Class.extend({ this.toolbar.find(".btn-html").on("click", function() { if(!window.bs_html_editor) window.bs_html_editor = new bsHTMLEditor(); - + window.bs_html_editor.show(me.editor); }) }, @@ -382,7 +389,7 @@ bsEditorToolbar = Class.extend({ }); } }, - + execCommand: function (commandWithArgs, valueArg) { var commandArr = commandWithArgs.split(' '), command = commandArr.shift(), @@ -390,18 +397,18 @@ bsEditorToolbar = Class.extend({ document.execCommand(command, 0, args); this.update(); }, - + get_current_range: function () { var sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { return sel.getRangeAt(0); } }, - + save_selection: function () { this.selected_range = this.get_current_range(); }, - + restore_selection: function () { var selection = window.getSelection(); if (this.selected_range) { @@ -409,14 +416,14 @@ bsEditorToolbar = Class.extend({ selection.addRange(this.selected_range); } }, - + mark_selection: function (input, color) { this.restore_selection(); document.execCommand('hiliteColor', 0, color || 'transparent'); this.save_selection(); input.data(this.options.selection_marker, color); }, - + // bind_touch: function() { // var me = this; // $(window).bind('touchend', function (e) { @@ -479,7 +486,7 @@ bsLinkEditor = Class.extend({ \ \ '); - + this.modal.addClass("frappe-ignore-click"); this.modal.find(".btn-primary").on("click", function() { me.toolbar.restore_selection(); diff --git a/frappe/public/js/frappe/ui/toolbar/toolbar.js b/frappe/public/js/frappe/ui/toolbar/toolbar.js index 8c09b7dfa5..0e020aae85 100644 --- a/frappe/public/js/frappe/ui/toolbar/toolbar.js +++ b/frappe/public/js/frappe/ui/toolbar/toolbar.js @@ -1,5 +1,5 @@ // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -// MIT License. See license.txt +// MIT License. See license.txt frappe.ui.toolbar.Toolbar = Class.extend({ @@ -12,11 +12,11 @@ frappe.ui.toolbar.Toolbar = Class.extend({ this.make_help(); this.make_user_menu(); this.make_notification(); - + $('.dropdown-toggle').dropdown(); - + $(document).trigger('toolbar_setup'); - + // clear all custom menus on page change $(document).on("page-change", function() { $("header .navbar .custom-menu").remove(); @@ -49,12 +49,12 @@ frappe.ui.toolbar.Toolbar = Class.extend({ \ \ \ - '); + '); }, make_home: function() { $('.navbar-brand').attr('href', "#"); }, - + make_notification: function() { $('.navbar .navbar-right').append(''); }, - + make_history: function() { frappe.ui.toolbar.recent = new frappe.ui.toolbar.RecentDocs(); }, - + make_bookmarks: function() { frappe.ui.toolbar.bookmarks = new frappe.ui.toolbar.Bookmarks(); }, @@ -124,7 +124,7 @@ frappe.ui.toolbar.Toolbar = Class.extend({ make_user_menu: function() { this.set_user_name(); - + $(repl('
  • \ %(my_settings)s
  • \
  • \ @@ -162,7 +162,7 @@ $.extend(frappe.ui.toolbar, { add_menu_divider: function(menu) { menu = typeof menu == "string" ? frappe.ui.toolbar.get_menu(menu) : menu; - + $('
  • ').prependTo(menu); }, }) @@ -171,7 +171,7 @@ frappe.ui.toolbar.update_notifications = function() { var total = 0; var doctypes = keys(frappe.boot.notification_info.open_count_doctype).sort(); var modules = keys(frappe.boot.notification_info.open_count_module).sort(); - + $("#navbar-notification").empty(); $.each(modules, function(i, module) { @@ -195,11 +195,11 @@ frappe.ui.toolbar.update_notifications = function() { total += count; } }); - + if(total) { $('
  • ').appendTo("#navbar-notification"); } - + $.each(doctypes, function(i, doctype) { var count = frappe.boot.notification_info.open_count_doctype[doctype]; if(count) { @@ -221,16 +221,16 @@ frappe.ui.toolbar.update_notifications = function() { total += count; } }); - + $(".navbar-new-comments") .html(total) .toggleClass("navbar-new-comments-true", total ? true : false); - + } frappe.ui.toolbar.clear_cache = function() { localStorage && localStorage.clear(); - $c('frappe.sessions.clear',{},function(r,rt){ + $c('frappe.sessions.clear',{},function(r,rt){ if(!r.exc) { show_alert(r.message); location.reload(); @@ -247,7 +247,7 @@ frappe.ui.toolbar.download_backup = function() { frappe.ui.toolbar.show_about = function() { try { - frappe.ui.misc.about(); + frappe.ui.misc.about(); } catch(e) { console.log(e); } @@ -256,4 +256,4 @@ frappe.ui.toolbar.show_about = function() { frappe.ui.toolbar.show_banner = function(msg) { return $('
    ').html(msg).appendTo($('header')); -} \ No newline at end of file +} diff --git a/frappe/public/js/frappe/upload.js b/frappe/public/js/frappe/upload.js index 90356fe317..19f49af828 100644 --- a/frappe/public/js/frappe/upload.js +++ b/frappe/public/js/frappe/upload.js @@ -91,9 +91,9 @@ frappe.upload = { opts.onerror ? opts.onerror(r) : opts.callback(null, null, r); return; } - opts.callback(r.message.fid, r.message.filename, r); + opts.callback(r.message.name , r.message.file_name, r.message.file_url, r); $(document).trigger("upload_complete", - [r.message.fid, r.message.filename]); + [r.message.name , r.message.file_name]); } }); } @@ -123,4 +123,4 @@ frappe.upload = { freader.readAsDataURL(fileobj); } } -} \ No newline at end of file +} diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index 3328208ca2..b6c9105429 100644 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -13,7 +13,7 @@ frappe.views.CommunicationList = Class.extend({ } if(!this.list) - this.list = frappe.model.get("Communication", {"parenttype": this.doc.doctype, "parent": this.doc.name}); + this.list = frappe.get_list("Communication", {"parenttype": this.doc.doctype, "parent": this.doc.name}); var sortfn = function (a, b) { return (b.creation > a.creation) ? 1 : -1; } this.list = this.list.sort(sortfn); diff --git a/frappe/public/js/frappe/views/listview.js b/frappe/public/js/frappe/views/listview.js index 46b7a11528..4677e59cdd 100644 --- a/frappe/public/js/frappe/views/listview.js +++ b/frappe/public/js/frappe/views/listview.js @@ -43,7 +43,7 @@ frappe.views.ListView = Class.extend({ 'modified_by'], function(i, fieldname) { add_field(fieldname); }) // add title field - var meta = frappe.model.get("DocType", this.doctype)[0]; + var meta = frappe.get_doc("DocType", this.doctype); if(meta.title_field) { this.title_field = meta.title_field; add_field(meta.title_field); @@ -56,8 +56,8 @@ frappe.views.ListView = Class.extend({ this.stats.push(this.workflow_state_fieldname); } - $.each(frappe.model.get("DocField", {"parent":this.doctype, "in_list_view":1}), function(i,d) { - if(frappe.perm.has_perm(me.doctype, d.permlevel, "read")) { + $.each(meta.fields, function(i,d) { + if(d.in_list_view && frappe.perm.has_perm(me.doctype, d.permlevel, "read")) { if(d.fieldtype=="Image" && d.options) { add_field(d.options); } else { @@ -103,8 +103,8 @@ frappe.views.ListView = Class.extend({ var overridden = $.map(this.settings.add_columns || [], function(d) { return d.content; }); - var docfields_in_list_view = frappe.model.get("DocField", {"parent":this.doctype, - "in_list_view":1}).sort(function(a, b) { return a.idx - b.idx }) + var docfields_in_list_view = frappe.get_children("DocType", this.doctype, "fields", + {"in_list_view":1}).sort(function(a, b) { return a.idx - b.idx }) $.each(docfields_in_list_view, function(i,d) { if(in_list(overridden, d.fieldname) || d.fieldname === me.title_field) { diff --git a/frappe/public/js/frappe/views/moduleview.js b/frappe/public/js/frappe/views/moduleview.js index cc8ee76121..bcdffa33ad 100644 --- a/frappe/public/js/frappe/views/moduleview.js +++ b/frappe/public/js/frappe/views/moduleview.js @@ -15,7 +15,7 @@ frappe.views.ModuleFactory = frappe.views.Factory.extend({ frappe.views.moduleview.make = function(wrapper, module, method) { if(!method) method = "frappe.widgets.moduleview.get"; - + wrapper.module_view = new frappe.views.moduleview.ModuleView(wrapper, module, method); } @@ -53,7 +53,7 @@ frappe.views.moduleview.ModuleView = Class.extend({ wrapper.appframe.set_title_right(frappe._("Refresh"), function() { me.make(wrapper, module); }); - + // full refresh wrapper.refresh = function() { // remake on refresh @@ -61,7 +61,7 @@ frappe.views.moduleview.ModuleView = Class.extend({ me.make(wrapper, module); } }; - + // counts $(document).on("notification-update", function() { if(wrapper.$layout) { @@ -74,7 +74,7 @@ frappe.views.moduleview.ModuleView = Class.extend({ return frappe.call({ method: "frappe.widgets.moduleview.get", args: { - module: module + module: module }, callback: function(r) { me.render(wrapper, r.message); @@ -92,16 +92,16 @@ frappe.views.moduleview.ModuleView = Class.extend({ me.add_section(d, $layout); me.add_items(d, $layout); }); - + me.item_count = message.item_count; me.add_reports(message.reports, $layout); me.show_counts($layout); me.setup_navigation($layout); - + // for refresh this.created_on = new Date(); }, - + make_layout: function(wrapper) { return $('
    \
    \ @@ -111,12 +111,12 @@ frappe.views.moduleview.ModuleView = Class.extend({
    \
    ').appendTo($(wrapper).find(".layout-main").empty()); }, - + add_section: function(d, $layout) { if(!d._label) d._label = d.label.toLowerCase().replace(/ /g, "_"); var $sections = $layout.find(".nav-pills"); var $nav = $sections.find('[data-label="'+d._label+'"]'); - + // if not found, add section if(!$nav.length) { // create nav tab @@ -126,33 +126,33 @@ frappe.views.moduleview.ModuleView = Class.extend({ .attr("data-label", d._label) .attr("data-section-label", d.label) .appendTo($sections); - + // create content pane for this nav var $content = $('
    ') .toggle(false) .attr("data-content-label", d._label) .appendTo($layout.find(".contents")); - - $('
    ').appendTo($content).html(' ' + + $('
    ').appendTo($content).html(' ' + d.label); - + var $list = $('
      ').appendTo($content); } }, - + add_items: function(d, $layout) { var me = this; var $content = $layout.find('[data-content-label="' + d._label + '"]'); var $list = $content.find(".list-group"); - + // add items in each pane $.each(d.items, function(i, item) { - if(item.country && frappe.boot.control_panel.country!==item.country) return; - - if((item.type==="doctype" && frappe.model.can_read(item.name)) + if(item.country && frappe.boot.sysdefaults.country!==item.country) return; + + if((item.type==="doctype" && frappe.model.can_read(item.name)) || (item.type==="page" && frappe.boot.page_info[item.name]) || (item.type==="report" && frappe.model.can_get_report(item.doctype))) { - + if(!item.label) { item.label = __(item.name); } @@ -164,9 +164,9 @@ frappe.views.moduleview.ModuleView = Class.extend({ } else if(item.type==="report" && item.doctype) { item.icon = item.icon || frappe.boot.doctype_icons[item.doctype]; } - + item.description = cstr(item.description); - + $list_item = $($r('
    • \
      \
      \ @@ -174,13 +174,13 @@ frappe.views.moduleview.ModuleView = Class.extend({
      %(description)s
      \
      \
    • ', item)).appendTo($list); - + // expand col if no description if(!item.description) { $list_item.find(".list-item-description").remove(); $list_item.find(".list-item-name").removeClass("col-sm-6").addClass("col-sm-12"); } - + $list_item.find("a") .on("click", function() { if(item.route) { @@ -189,10 +189,10 @@ frappe.views.moduleview.ModuleView = Class.extend({ var fn = eval(item.onclick); if(typeof(fn)==="function") { fn(); - } + } } else if(item.type==="doctype") { frappe.set_route("List", item.name) - } + } else if(item.type==="page") { frappe.set_route(item.route || item.link || item.name); } @@ -207,7 +207,7 @@ frappe.views.moduleview.ModuleView = Class.extend({ } return false; }); - + var show_count = (item.type==="doctype" || (item.type==="page" && item.doctype)) && !item.hide_count if(show_count) { $(repl('', @@ -216,10 +216,10 @@ frappe.views.moduleview.ModuleView = Class.extend({ } }); }, - + add_reports: function(reports, $layout) { if(!(reports && reports.length)) return; - + var reports_section = { label: __("Custom Reports"), icon: "icon-list-alt", @@ -228,7 +228,7 @@ frappe.views.moduleview.ModuleView = Class.extend({ this.add_section(reports_section, $layout); this.add_items(reports_section, $layout); }, - + show_counts: function($layout) { var me = this; // total count @@ -241,11 +241,11 @@ frappe.views.moduleview.ModuleView = Class.extend({ .css({cursor:"pointer"}); }); }); - + // open count this.update_open_count($layout); }, - + setup_navigation: function($layout) { var me = this; // section selection (can't use tab api - routing) @@ -259,7 +259,7 @@ frappe.views.moduleview.ModuleView = Class.extend({ $(this).parent().addClass("active"); $layout.find(".panel").toggle(false); $layout.find('[data-content-label="'+ $(this).parent().attr("data-label") +'"]').toggle(true); - + var section_label = $(this).parent().attr("data-section-label"); if(!me.item_count || me.item_count[section_label]==null) { frappe.call({ @@ -276,7 +276,7 @@ frappe.views.moduleview.ModuleView = Class.extend({ } }); $sections.find('a:first').trigger("click"); - + $layout.on("click", ".badge-important", function() { frappe.views.show_open_count_list(this); }); @@ -284,9 +284,9 @@ frappe.views.moduleview.ModuleView = Class.extend({ $layout.on("click", ".badge-count", function() { var doctype = $(this).attr("data-doctype-count"); frappe.set_route("List", doctype); - }); + }); }, - + update_open_count: function($layout) { var me = this; $layout.find(".badge-important").remove(); diff --git a/frappe/public/js/frappe/views/query_report.js b/frappe/public/js/frappe/views/query_report.js index 0d07129c3b..bb4cfdf8ce 100644 --- a/frappe/public/js/frappe/views/query_report.js +++ b/frappe/public/js/frappe/views/query_report.js @@ -93,7 +93,7 @@ frappe.views.QueryReport = Class.extend({ me.appframe.set_title(frappe._("Query Report")+": " + frappe._(me.report_name)); frappe.model.with_doc("Report", me.report_name, function() { - me.report_doc = frappe.model.get_doc("Report", me.report_name); + me.report_doc = frappe.get_doc("Report", me.report_name); frappe.model.with_doctype(me.report_doc.ref_doctype, function() { if(!frappe.query_reports[me.report_name]) { return frappe.call({ diff --git a/frappe/public/js/frappe/views/reportview.js b/frappe/public/js/frappe/views/reportview.js index 5108568ee1..94b64c47e3 100644 --- a/frappe/public/js/frappe/views/reportview.js +++ b/frappe/public/js/frappe/views/reportview.js @@ -27,7 +27,7 @@ frappe.views.ReportViewPage = Class.extend({ if(me.docname) { frappe.model.with_doc('Report', me.docname, function(r) { me.page.reportview.set_columns_and_filters( - JSON.parse(frappe.model.get("Report", me.docname)[0].json)); + JSON.parse(frappe.get_doc("Report", me.docname).json)); me.page.reportview.set_route_filters(); me.page.reportview.run(); }); @@ -337,13 +337,13 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ callback: function(r) { if(!r.exc) { d.hide(); - var doclist = r.message; + var doc = r.message; $.each(me.dataView.getItems(), function(i, item) { - if (item.name === doclist[0].name) { - var new_item = $.extend({}, item, doclist[0]); - $.each(doclist, function(i, doc) { - if(item[doc.doctype + ":name"]===doc.name) { - $.each(doc, function(k, v) { + if (item.name === doc.name) { + var new_item = $.extend({}, item); + $.each(frappe.model.get_all_docs(doc), function(i, d) { + if(item[d.doctype + ":name"]===d.name) { + $.each(d, function(k, v) { if(frappe.model.std_fields_list.indexOf(k)===-1) { new_item[k] = v; } diff --git a/frappe/public/js/legacy/clientscriptAPI.js b/frappe/public/js/legacy/clientscriptAPI.js index 968a5b22ac..c542fff82f 100644 --- a/frappe/public/js/legacy/clientscriptAPI.js +++ b/frappe/public/js/legacy/clientscriptAPI.js @@ -4,10 +4,7 @@ get_server_fields = function(method, arg, table_field, doc, dt, dn, allow_edit, call_back) { frappe.dom.freeze(); return $c('runserverobj', - args={'method':method, - 'docs':frappe.model.compress(make_doclist(doc.doctype, doc.name)), - 'arg':arg - }, + args={'method': method, 'docs': doc, 'arg': arg }, function(r, rt) { frappe.dom.unfreeze(); if (r.message) { @@ -125,10 +122,6 @@ _f.Frm.prototype.get_doc = function() { return locals[this.doctype][this.docname]; } -_f.Frm.prototype.get_doclist = function(filters) { - return frappe.model.get_doclist(this.doctype, this.docname, filters); -} - _f.Frm.prototype.field_map = function(fnames, fn) { if(typeof fnames==='string') { if(fnames == '*') { @@ -170,7 +163,7 @@ _f.Frm.prototype.toggle_display = function(fnames, show) { } _f.Frm.prototype.call_server = function(method, args, callback) { - return $c_obj(cur_frm.get_doclist(), method, args, callback); + return $c_obj(cur_frm.doc, method, args, callback); } _f.Frm.prototype.get_files = function() { diff --git a/frappe/public/js/legacy/datetime.js b/frappe/public/js/legacy/datetime.js index 29766d0dcb..ff56951989 100644 --- a/frappe/public/js/legacy/datetime.js +++ b/frappe/public/js/legacy/datetime.js @@ -1,5 +1,5 @@ // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -// MIT License. See license.txt +// MIT License. See license.txt // Date @@ -20,58 +20,58 @@ function int_to_str(i, len) { } frappe.datetime = { - - str_to_obj: function(d) { + + str_to_obj: function(d) { if(typeof d=="object") return d; - if(!d) return new Date(); + if(!d) return new Date(); var tm = [0, 0]; if(d.search(' ')!=-1) { var tm = d.split(' ')[1].split(':'); var d = d.split(' ')[0]; } if(d.search('-')!=-1) { - var t = d.split('-'); return new Date(t[0],t[1]-1,t[2],tm[0],tm[1]); + var t = d.split('-'); return new Date(t[0],t[1]-1,t[2],tm[0],tm[1]); } else if(d.search('/')!=-1) { - var t = d.split('/'); return new Date(t[0],t[1]-1,t[2],tm[0],tm[1]); + var t = d.split('/'); return new Date(t[0],t[1]-1,t[2],tm[0],tm[1]); } else { return new Date(); } }, - obj_to_str: function(d) { + obj_to_str: function(d) { if(typeof d=='string') return d; - return d.getFullYear() + '-' + int_to_str(d.getMonth()+1,2) + '-' + int_to_str(d.getDate(),2); + return d.getFullYear() + '-' + int_to_str(d.getMonth()+1,2) + '-' + int_to_str(d.getDate(),2); }, - - obj_to_user: function(d) { - return dateutil.str_to_user(dateutil.obj_to_str(d)); + + obj_to_user: function(d) { + return dateutil.str_to_user(dateutil.obj_to_str(d)); }, - + get_ms_diff: function(d1, d2) { if(typeof d1=='string') d1 = dateutil.str_to_obj(d1); if(typeof d2=='string') d2 = dateutil.str_to_obj(d2); return d1-d2 }, - - get_diff: function(d1, d2) { - return Math.round(dateutil.get_ms_diff(d1, d2) / 86400000); + + get_diff: function(d1, d2) { + return Math.round(dateutil.get_ms_diff(d1, d2) / 86400000); }, - + get_hour_diff: function(d1, d2) { - return Math.round(dateutil.get_ms_diff(d1, d2) / 3600000); + return Math.round(dateutil.get_ms_diff(d1, d2) / 3600000); }, - + get_day_diff: function(d1, d2) { - return dateutil.get_diff(new Date(d1.getYear(), d1.getMonth(), d1.getDate(), 0, 0), + return dateutil.get_diff(new Date(d1.getYear(), d1.getMonth(), d1.getDate(), 0, 0), new Date(d2.getYear(), d2.getMonth(), d2.getDate(), 0, 0)) }, - - add_days: function(d, days) { + + add_days: function(d, days) { var dt = dateutil.str_to_obj(d); var new_dt = new Date(dt.getTime()+(days*24*60*60*1000)); return dateutil.obj_to_str(new_dt); }, - + add_months: function(d, months) { dt = dateutil.str_to_obj(d) months = cint(months); @@ -82,33 +82,33 @@ frappe.datetime = { } return dateutil.obj_to_str(new_dt); }, - - month_start: function(d, month) { + + month_start: function(d, month) { if(!d)var d = new Date(); var m = month ? cint(month) : d.getMonth() + 1; return d.getFullYear() + '-' + int_to_str(m, 2) + '-01'; }, - - month_end: function(d, month) { + + month_end: function(d, month) { if(!d)var d = new Date(); var m = month ? cint(month) : d.getMonth() + 1; var y = d.getFullYear(); var last_date = new Date(y, m, 0).getDate(); return y + '-' + int_to_str(m, 2) + '-' + int_to_str(last_date, 2); }, - + get_user_fmt: function() { var t = sys_defaults.date_format; if(!t) t = 'yyyy-mm-dd'; return t; }, - + str_to_user: function(val, no_time_str) { var user_fmt = dateutil.get_user_fmt(); var time_str = ''; - + if(val==null||val=='')return null; - + // separate time string if there if(val.search(' ')!=-1) { var tmp = val.split(' '); @@ -138,22 +138,22 @@ frappe.datetime = { return val; }, - - full_str: function() { + + full_str: function() { var d = new Date(); return d.getFullYear() + '-' + (d.getMonth()+1) + '-' + d.getDate() + ' ' + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds(); }, - + user_to_str: function(d, no_time_str) { var user_fmt = this.get_user_fmt(); - + var time_str = ''; if (d.search(/ /)!=-1) { time_str = " " + d.split(" ")[1]; d = d.split(" ")[0]; } - + if(user_fmt=='dd-mm-yyyy') { var d = d.split('-'); var val = d[2]+'-'+d[1]+'-'+d[0]; @@ -173,16 +173,17 @@ frappe.datetime = { var d = d.split('-'); var val = d[2]+'-'+d[0]+'-'+d[1]; } - + + if(time_str && time_str.indexOf(".")===-1) time_str += ".0"; if(no_time_str)time_str = ''; - + return val + time_str; }, - + user_to_obj: function(d) { return dateutil.str_to_obj(dateutil.user_to_str(d)); }, - + global_date_format: function(d) { if(d.substr) d = this.str_to_obj(d); return nth(d.getDate()) + ' ' + month_list_full[d.getMonth()] + ' ' + d.getFullYear(); @@ -210,7 +211,7 @@ frappe.datetime.only_date = function(val) { } else { var d = val.split('-'); } - if(d.length==3) + if(d.length==3) val = d[2]+'-'+d[1]+'-'+d[0]; return val; } diff --git a/frappe/public/js/legacy/form.js b/frappe/public/js/legacy/form.js index 7a99b51438..d4b50838d9 100644 --- a/frappe/public/js/legacy/form.js +++ b/frappe/public/js/legacy/form.js @@ -212,7 +212,7 @@ _f.Frm.prototype.watch_model_updates = function() { }) // on table fields - $.each(frappe.model.get("DocField", {fieldtype:"Table", parent: me.doctype}), function(i, df) { + $.each(frappe.get_children("DocType", me.doctype, "fields", {fieldtype:"Table"}), function(i, df) { frappe.model.on(df.options, "*", function(fieldname, value, doc) { if(doc.parent===me.docname && doc.parentfield===df.fieldname) { me.dirty(); @@ -306,7 +306,7 @@ _f.Frm.prototype.rename_notify = function(dt, old, name) { // SETUP _f.Frm.prototype.setup_meta = function(doctype) { - this.meta = frappe.model.get_doc('DocType',this.doctype); + this.meta = frappe.get_doc('DocType',this.doctype); this.perm = frappe.perm.get_perm(this.doctype); // for create if(this.meta.istable) { this.meta.in_dialog = 1 } } @@ -366,7 +366,7 @@ _f.Frm.prototype.refresh = function(docname) { this.read_only = frappe.workflow.is_read_only(this.doctype, this.docname); // set the doc - this.doc = frappe.model.get_doc(this.doctype, this.docname); + this.doc = frappe.get_doc(this.doctype, this.docname); // check if doctype is already open if (!this.opendocs[this.docname]) { @@ -548,13 +548,11 @@ _f.Frm.prototype.setnewdoc = function() { _f.Frm.prototype.runscript = function(scriptname, callingfield, onrefresh) { var me = this; if(this.docname) { - // make doc list - var doclist = frappe.model.compress(make_doclist(this.doctype, this.docname)); // send to run if(callingfield) $(callingfield.input).set_working(); - return $c('runserverobj', {'docs':doclist, 'method':scriptname }, + return $c('runserverobj', {'docs':this.doc, 'method':scriptname }, function(r, rtxt) { // run refresh if(onrefresh) @@ -577,45 +575,10 @@ _f.Frm.prototype.copy_doc = function(onload, from_amend) { return; } - var dn = this.docname; - // copy parent - var newdoc = frappe.model.copy_doc(this.doctype, dn, from_amend); + var newdoc = frappe.model.copy_doc(this.doc, from_amend); + newdoc.idx = null; - - // copy chidren - var dl = make_doclist(this.doctype, dn); - - // table fields dict - for no_copy check - var tf_dict = {}; - - for(var d in dl) { - d1 = dl[d]; - - // get tabel field - if(d1.parentfield && !tf_dict[d1.parentfield]) { - tf_dict[d1.parentfield] = frappe.meta.get_docfield(d1.parenttype, d1.parentfield); - } - - if(d1.parent==dn && cint(tf_dict[d1.parentfield].no_copy)!=1) { - var ch = frappe.model.copy_doc(d1.doctype, d1.name, from_amend); - ch.parent = newdoc.name; - ch.docstatus = 0; - ch.owner = user; - ch.creation = ''; - ch.modified_by = user; - ch.modified = ''; - } - } - - newdoc.__islocal = 1; - newdoc.docstatus = 0; - newdoc.owner = user; - newdoc.creation = ''; - newdoc.modified_by = user; - newdoc.modified = ''; - if(onload)onload(newdoc); - loaddoc(newdoc.doctype, newdoc.name); } @@ -651,19 +614,15 @@ _f.Frm.prototype._save = function(save_action, callback, btn, on_error) { scroll(0, 0); // validate - if(save_action!="Cancel") { - validated = true; - this.script_manager.trigger("validate"); - if(!validated) { - if(on_error) - on_error(); - return; - } + validated = true; + this.script_manager.trigger("validate"); + if(!validated) { + if(on_error) + on_error(); + return; } - - var doclist = new frappe.model.DocList(this.doctype, this.docname); - - doclist.save(save_action || "Save", function(r) { + + var after_save = function(r) { if(!r.exc) { me.refresh(); } else { @@ -680,7 +639,9 @@ _f.Frm.prototype._save = function(save_action, callback, btn, on_error) { } frappe._from_link = null; } - }, btn); + } + + frappe.ui.form.save(this, save_action || "Save", after_save, btn); } @@ -713,14 +674,16 @@ _f.Frm.prototype.savecancel = function(btn, on_error) { on_error(); return; } - - var doclist = new frappe.model.DocList(me.doctype, me.docname); - doclist.cancel(function(r) { + + var after_cancel = function(r) { if(!r.exc) { me.refresh(); me.script_manager.trigger("after_cancel"); + } else { + on_error(); } - }, btn, on_error); + } + frappe.ui.form.save(this, "cancel", after_cancel, btn); }); } diff --git a/frappe/public/js/legacy/handler.js b/frappe/public/js/legacy/handler.js index 639890eb37..4e83448e3d 100644 --- a/frappe/public/js/legacy/handler.js +++ b/frappe/public/js/legacy/handler.js @@ -13,7 +13,7 @@ function $c(command, args, callback, error, no_spinner, freeze_msg, btn) { } // For calling an object -function $c_obj(doclist, method, arg, callback, no_spinner, freeze_msg, btn) { +function $c_obj(doc, method, arg, callback, no_spinner, freeze_msg, btn) { if(arg && typeof arg!='string') arg = JSON.stringify(arg); args = { @@ -22,10 +22,11 @@ function $c_obj(doclist, method, arg, callback, no_spinner, freeze_msg, btn) { method: method }; - if(typeof doclist=='string') - args.doctype = doclist; - else - args.docs = frappe.model.compress(doclist) + if(typeof doc=='string') { + args.doctype = doc; + } else { + args.docs = doc + } return frappe.request.call({ args: args, @@ -53,7 +54,7 @@ function $c_page(module, page, method, arg, callback, no_spinner, freeze_msg, bt } // For calling an for output as csv -function $c_obj_csv(doclist, method, arg) { +function $c_obj_csv(doc, method, arg) { // single var args = {} @@ -62,10 +63,10 @@ function $c_obj_csv(doclist, method, arg) { args.method = method; args.arg = arg; - if(doclist.substr) - args.doctype = doclist; + if(doc.substr) + args.doctype = doc; else - args.docs = frappe.model.compress(doclist); + args.docs = doc; // open open_url_post(frappe.request.url, args); diff --git a/frappe/public/js/legacy/print_format.js b/frappe/public/js/legacy/print_format.js index 6fc9a40174..a8ddde9bf1 100644 --- a/frappe/public/js/legacy/print_format.js +++ b/frappe/public/js/legacy/print_format.js @@ -1,5 +1,5 @@ // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -// MIT License. See license.txt +// MIT License. See license.txt // default print style _p.def_print_style_body = "html, body, div, span, td, p { \ @@ -46,11 +46,11 @@ _p.preview = function(html) { $.extend(_p, { show_dialog: function() { if(!_p.dialog) { - _p.make_dialog(); + _p.make_dialog(); } _p.dialog.show(); }, - + make_dialog: function() { // Prepare Dialog Box Layout var dialog = new frappe.ui.Dialog({ @@ -64,7 +64,7 @@ $.extend(_p, {

      '}, ] }); - + dialog.$wrapper.find(".btn-print").click(function() { var args = dialog.get_values(); _p.build( @@ -82,21 +82,21 @@ $.extend(_p, { args.no_letterhead // no_letterhead ); }); - + dialog.onshow = function() { var $print = dialog.fields_dict.print_format.$input; $print.empty().add_options(cur_frm.print_formats); - + if(cur_frm.$print_view_select && cur_frm.$print_view_select.val()) $print.val(cur_frm.$print_view_select.val()); } - + _p.dialog = dialog; }, - + // Define formats dict formats: {}, - + /* args dict can contain: + fmtname --> print format name + onload @@ -107,24 +107,24 @@ $.extend(_p, { if(!fmtname) { fmtname= "Standard"; } - + args = { fmtname: fmtname, onload: onload, no_letterhead: no_letterhead, only_body: only_body }; - + if(!cur_frm) { msgprint(frappe._("No document selected.")); return; } - + if(!frappe.model.can_print(cur_frm.doctype, cur_frm)) { msgprint(frappe._("You are not allowed to print this document.")); return; } - + // Get current doc (record) var doc = locals[cur_frm.doctype][cur_frm.docname]; if(args.fmtname == 'Standard') { @@ -151,35 +151,35 @@ $.extend(_p, { no_letterhead: args.no_letterhead, no_heading: no_heading, only_body: args.only_body - })); + })); } }, - + render: function(args) { var container = document.createElement('div'); var stat = ''; - + if(!args.no_heading) { // if draft/archived, show draft/archived banner - stat += _p.show_draft(args); + stat += _p.show_draft(args); stat += _p.show_archived(args); stat += _p.show_cancelled(args); } - + // Append args.body's content as a child of container container.innerHTML = args.body; - + // Show letterhead? _p.show_letterhead(container, args); - + _p.run_embedded_js(container, args.doc); var style = _p.consolidate_css(container, args); - + _p.render_header_on_break(container, args); - + return _p.render_final(style, stat, container, args); }, - + head_banner_format: function() { return "\ @@ -197,11 +197,11 @@ $.extend(_p, { }, /* - Check if doc's status is not submitted (docstatus == 0) + Check if doc's status is not submitted (docstatus == 0) and submission is pending - + Display draft in header if true - */ + */ show_draft: function(args) { var is_doctype_submittable = 0; var plist = locals['DocPerm']; @@ -222,10 +222,10 @@ $.extend(_p, { return ""; } }, - - + + /* - Check if doc is archived + Check if doc is archived Display archived in header if true */ show_archived: function(args) { @@ -236,7 +236,7 @@ $.extend(_p, { return archived; } else { return ""; - } + } }, @@ -252,7 +252,7 @@ $.extend(_p, { return cancelled; } else { return ""; - } + } }, @@ -274,11 +274,11 @@ $.extend(_p, { } style_list = container.getElementsByTagName('style'); } - + // Concatenate all styles style_concat = (args.only_body ? '' : _p.def_print_style_body) + _p.def_print_style_other + args.style + body_style; - + return style_concat; }, @@ -295,7 +295,7 @@ $.extend(_p, { } } }, - + add_span: function(html) { var tags = ["]>", "]>", "]>", "]>", "]>"]; var match = false; @@ -304,25 +304,25 @@ $.extend(_p, { match = true; } } - + if(!match) { html = "" + html + ""; } - + return html; }, - - + + // Attach letterhead at top of container - show_letterhead: function(container, args) { + show_letterhead: function(container, args) { if(!args.no_letterhead) { - container.innerHTML = '
      ' - + _p.get_letter_head() + '
      ' + container.innerHTML = '
      ' + + _p.get_letter_head() + '
      ' + container.innerHTML; } }, - - + + render_header_on_break: function(container, args) { var page_set = container.getElementsByClassName('page-settings'); if(page_set.length) { @@ -336,8 +336,8 @@ $.extend(_p, { } } }, - - + + // called by _p.render for final render of print render_final: function(style, stat, container, args) { if(!args.only_body) { @@ -362,13 +362,13 @@ $.extend(_p, { + container.innerHTML + '
    ' + footer; - - + + // replace relative links by absolute links var prefix = window.location.href.split("desk")[0] // find unique matches var matches = $.unique(finished.match(/src=['"]([^'"]*)['"]/g) || []); - + $.each(matches, function(i, v) { if(v.substr(0,4)=="src=") { var v = v.substr(5, v.length-6); @@ -376,23 +376,22 @@ $.extend(_p, { finished = finished.split(v).join(prefix + v); } }); - + return finished; }, - - + + // fetches letter head from current doc or control panel get_letter_head: function() { - var cp = frappe.control_panel; var lh = ''; if(cur_frm.doc.letter_head) { lh = cstr(frappe.boot.letter_heads[cur_frm.doc.letter_head]); - } else if (cp.letter_head) { - lh = cp.letter_head; + } else if (frappe.boot.sysdefaults.letter_head) { + lh = frappe.boot.sysdefaults.letter_head; } return lh; }, - + // common print style setting print_style: "\ .datalabelcell { \ @@ -415,12 +414,12 @@ $.extend(_p, { font-weight: bold; \ margin: 8px 0px; \ }", - + print_std: function(no_letterhead, no_heading) { // Get doctype, docname, layout for a doctype var docname = cur_frm.docname; var doctype = cur_frm.doctype; - var data = getchildren('DocField', doctype, 'fields', 'DocType'); + var data = frappe.get_children("DocType", doctype, "fields"); var layout = _p.add_layout(doctype); this.pf_list = [layout]; var me = this; @@ -431,10 +430,10 @@ $.extend(_p, { // Heading var h1_style = { fontSize: '22px', - marginBottom: '8px' + marginBottom: '8px' } var h1 = $a(me.layout.cur_row.header, 'h1', '', h1_style); - + // Get print heading if (cur_frm.pformat[docname]) { // first check in cur_frm.pformat @@ -451,7 +450,7 @@ $.extend(_p, { // if not, just have doctype has heading h1.innerHTML = val ? val : frappe._(doctype); } - + var h2_style = { fontSize: '16px', color: '#888', @@ -462,16 +461,16 @@ $.extend(_p, { } var h2 = $a(me.layout.cur_row.header, 'div', '', h2_style); h2.innerHTML = docname; - + if(cur_frm.state_fieldname) { $a(h2, 'br'); - var span = $a(h2, 'span', '', - {padding: "3px", color: "#fff", backgroundColor: "#777", + var span = $a(h2, 'span', '', + {padding: "3px", color: "#fff", backgroundColor: "#777", display:"inline-block"}); span.innerHTML = cur_frm.doc[cur_frm.state_fieldname]; } }, - + build_data: function(data, doctype, docname) { // Start with a row and a cell in that row if(data[0] && data[0].fieldtype != "Section Break") { @@ -480,47 +479,47 @@ $.extend(_p, { me.layout.addcell(); } } - + $.extend(this, { generate_custom_html: function(field, doctype, docname) { var container = $a(me.layout.cur_cell, 'div'); - container.innerHTML = cur_frm.pformat[field.fieldname](locals[doctype][docname]); + container.innerHTML = cur_frm.pformat[field.fieldname](locals[doctype][docname]); }, - + render_normal: function(field, data, i) { switch(field.fieldtype) { case 'Section Break': me.layout.addrow(); - + // Add column if no column break after this field if(data[i+1] && data[i+1].fieldtype != 'Column Break') { me.layout.addcell(); } break; - + case 'Column Break': - me.layout.addcell(field.width, field.label); + me.layout.addcell(field.width, field.label); break; - + case 'Table': var table = print_table( doctype, // dt docname, // dn field.fieldname, - field.options, // tabletype + field.options, // tabletype null, // cols null, // head_labels null, // widths null); // condition me.layout = _p.print_std_add_table(table, me.layout, me.pf_list, doctype, no_letterhead); break; - + case 'HTML': var div = $a(me.layout.cur_cell, 'div'); div.innerHTML = field.options; break; - + case 'Code': var div = $a(me.layout.cur_cell, 'div'); var val = _f.get_value(doctype, docname, @@ -529,22 +528,22 @@ $.extend(_p, { ':
    ' + (val ? val : '') +
     									'
    '; break; - + case 'Text Editor': var div = $a(me.layout.cur_cell, 'div'); var val = _f.get_value(doctype, docname, field.fieldname); div.innerHTML = val ? val : ''; break; - + default: // Add Cell Data _p.print_std_add_field(doctype, docname, field, me.layout); break; } - } + } }); - + // Then build each field for(var i = 0; i < data.length; i++) { var fieldname = data[i].fieldname ? data[i].fieldname : @@ -559,11 +558,11 @@ $.extend(_p, { // Do the normal rendering this.render_normal(field, data, i); } - } + } } me.layout.close_borders(); }, - + build_html: function() { var html = ''; for(var i = 0; i < me.pf_list.length; i++) { @@ -579,7 +578,7 @@ $.extend(_p, { return html; } }); - + if(!no_heading) { this.build_head(data, doctype, docname); } @@ -589,18 +588,18 @@ $.extend(_p, { var html = this.build_html(); return html; }, - + add_layout: function(doctype) { var layout = new Layout(); layout.addrow(); - + if(locals['DocType'][doctype].print_outline == 'Yes') { layout.with_border = 1 } - - return layout; + + return layout; }, - + print_std_add_table: function(t, layout, pf_list, dt, no_letterhead) { if(t.appendChild) { // If only one table is passed @@ -609,57 +608,57 @@ $.extend(_p, { page_break = '\n\
    '; - + // If a list of tables is passed for(var i = 0; i < t.length-1; i++) { // add to current page layout.cur_cell.appendChild(t[i]); layout.close_borders(); - + pf_list.push(page_break); - + // Create new page layout = _p.add_layout(dt, no_letterhead); pf_list.push(layout); - + layout.addrow(); layout.addcell(); - + var div = $a(layout.cur_cell, 'div'); div.innerHTML = 'Continued from previous page...'; div.style.padding = '4px'; } - + // Append last table layout.cur_cell.appendChild(t[t.length-1]); } return layout; }, - + print_std_add_field: function(dt, dn, f, layout) { var val = _f.get_value(dt, dn, f.fieldname); if(f.fieldtype!='Button') { if(val || in_list(['Float', 'Int', 'Currency'], f.fieldtype)) { // If value or a numeric type then proceed - + // Add field table row = _p.field_tab(layout.cur_cell); - + // Add label row.cells[0].innerHTML = frappe._(f.label ? f.label : f.fieldname); row.cells[1].innerHTML = frappe.format(val, f, {for_print: true}); - + // left align currency in normal display if(f.fieldtype == 'Currency') { $y(row.cells[1], { textAlign: 'left' }); } - + } } }, - + field_tab: function(layout_cell) { - var tab = $a(layout_cell, 'table', '', {width:'100%'}); + var tab = $a(layout_cell, 'table', '', {width:'100%'}); var row = tab.insertRow(0); _p.row = row; // Don't know this line's purpose row.insertCell(0); @@ -669,4 +668,4 @@ $.extend(_p, { row.cells[1].className = 'datainputcell'; return row; } -}); \ No newline at end of file +}); diff --git a/frappe/sessions.py b/frappe/sessions.py index 668926db14..bc639ede5d 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -1,18 +1,18 @@ # 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 """ Boot session from cache or build Session bootstraps info needed by common client side activities including -permission, homepage, control panel variables, system defaults etc +permission, homepage, default variables, system defaults etc """ import frappe, os, json import frappe import frappe.utils from frappe.utils import cint -import frappe.model.doctype +import frappe.model.meta import frappe.defaults import frappe.translate @@ -27,19 +27,19 @@ def clear(user=None): def clear_cache(user=None): cache = frappe.cache() - frappe.model.doctype.clear_cache() + frappe.model.meta.clear_cache() cache.delete_value(["app_hooks", "installed_apps", "app_modules", "module_apps", "home_page"]) - + if user: cache.delete_value("bootinfo:" + user) cache.delete_value("lang:" + user) cache.delete_value("roles:" + user) cache.delete_value("restrictions:" + user) - + # clear notifications if frappe.flags.in_install_app!="frappe": frappe.db.sql("""delete from `tabNotification Count` where owner=%s""", (user,)) - + if frappe.session: if user==frappe.session.user and frappe.session.sid: cache.delete_value("session:" + frappe.session.sid) @@ -71,7 +71,7 @@ def get(): from frappe.core.doctype.notification_count.notification_count import \ get_notification_info_for_boot, get_notifications from frappe.boot import get_bootinfo, get_startup_js - + bootinfo = None if not getattr(frappe.conf,'disable_session_cache', None): # check if cache exists @@ -80,20 +80,24 @@ def get(): bootinfo['from_cache'] = 1 bootinfo["user"]["recent"] = json.dumps(frappe.cache().get_value("recent:" + frappe.session.user)) bootinfo["notification_info"].update(get_notifications()) - + if not bootinfo: if not frappe.cache().get_stats(): frappe.msgprint("memcached is not working / stopped. Please start memcached for best results.") - + # if not create it bootinfo = get_bootinfo() bootinfo["notification_info"] = get_notification_info_for_boot() frappe.cache().set_value('bootinfo:' + frappe.session.user, bootinfo) - + + bootinfo["metadata_version"] = frappe.cache().get_value("metadata_version") + if not bootinfo["metadata_version"]: + bootinfo["metadata_version"] = frappe.reset_metadata_version() + bootinfo["startup_js"] = get_startup_js() for hook in frappe.get_hooks("extend_bootinfo"): frappe.get_attr(hook)(bootinfo=bootinfo) - + return bootinfo class Session: @@ -111,13 +115,13 @@ class Session: frappe.local.session = self.data def start(self): - """start a new session""" + """start a new session""" # generate sid if self.user=='Guest': sid = 'Guest' else: sid = frappe.generate_hash() - + self.data['user'] = self.user self.data['sid'] = sid self.data['data']['user'] = self.user @@ -126,37 +130,37 @@ class Session: self.data['data']['last_updated'] = frappe.utils.now() self.data['data']['session_expiry'] = self.get_expiry_period() self.data['data']['session_country'] = get_geo_ip_country(frappe.get_request_header('REMOTE_ADDR')) - + # insert session if self.user!="Guest": frappe.db.begin() self.insert_session_record() # update user - frappe.db.sql("""UPDATE tabUser SET last_login = %s, last_ip = %s + frappe.db.sql("""UPDATE tabUser SET last_login = %s, last_ip = %s where name=%s""", (frappe.utils.now(), frappe.get_request_header('REMOTE_ADDR'), self.data['user'])) - frappe.db.commit() + frappe.db.commit() def insert_session_record(self): - frappe.db.sql("""insert into tabSessions - (sessiondata, user, lastupdate, sid, status) - values (%s , %s, NOW(), %s, 'Active')""", + frappe.db.sql("""insert into tabSessions + (sessiondata, user, lastupdate, sid, status) + values (%s , %s, NOW(), %s, 'Active')""", (str(self.data['data']), self.data['user'], self.data['sid'])) - + # also add to memcache frappe.cache().set_value("session:" + self.data.sid, self.data) def resume(self): """non-login request: load a session""" - import frappe + import frappe data = self.get_session_record() if data: # set language self.data = frappe._dict({'data': data, 'user':data.user, 'sid': self.sid}) else: self.start_as_guest() - - frappe.local.lang = frappe.cache().get_value("lang:" + self.data.user, + + frappe.local.lang = frappe.cache().get_value("lang:" + self.data.user, lambda: frappe.translate.get_user_lang(self.data.user)) def get_session_record(self): @@ -168,13 +172,13 @@ class Session: clear_cookies() self.sid = "Guest" r = self.get_session_data() - + return r def get_session_data(self): if self.sid=="Guest": return frappe._dict({"user":"Guest"}) - + data = self.get_session_data_from_cache() if not data: data = self.get_session_data_from_db() @@ -184,20 +188,20 @@ class Session: data = frappe._dict(frappe.cache().get_value("session:" + self.sid) or {}) if data: session_data = data.get("data", {}) - self.time_diff = frappe.utils.time_diff_in_seconds(frappe.utils.now(), + self.time_diff = frappe.utils.time_diff_in_seconds(frappe.utils.now(), session_data.get("last_updated")) expiry = self.get_expiry_in_seconds(session_data.get("session_expiry")) if self.time_diff > expiry: self.delete_session() data = None - + return data and data.data def get_session_data_from_db(self): - rec = frappe.db.sql("""select user, sessiondata - from tabSessions where sid=%s and - TIMEDIFF(NOW(), lastupdate) < TIME(%s)""", (self.sid, + rec = frappe.db.sql("""select user, sessiondata + from tabSessions where sid=%s and + TIMEDIFF(NOW(), lastupdate) < TIME(%s)""", (self.sid, self.get_expiry_period())) if rec: data = frappe._dict(eval(rec and rec[0][1] or '{}')) @@ -233,39 +237,39 @@ class Session: last_updated = frappe.cache().get_value("last_db_session_update:" + self.sid) if last_updated: - time_diff = frappe.utils.time_diff_in_seconds(frappe.utils.now(), + time_diff = frappe.utils.time_diff_in_seconds(frappe.utils.now(), last_updated) - + if force or (frappe.session['user'] != 'Guest' and \ ((time_diff==None) or (time_diff > 1800))): # database persistence is secondary, don't update it too often - frappe.db.sql("""update tabSessions set sessiondata=%s, - lastupdate=NOW() where sid=%s""" , (str(self.data['data']), + frappe.db.sql("""update tabSessions set sessiondata=%s, + lastupdate=NOW() where sid=%s""" , (str(self.data['data']), self.data['sid'])) if frappe.form_dict.cmd not in ("frappe.sessions.clear", "logout"): - frappe.cache().set_value("last_db_session_update:" + self.sid, + frappe.cache().set_value("last_db_session_update:" + self.sid, 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" - + # incase seconds is missing if exp_sec: if len(exp_sec.split(':')) == 2: exp_sec = exp_sec + ':00' else: exp_sec = "2:00:00" - + return exp_sec - + def get_geo_ip_country(ip_addr): try: import pygeoip except ImportError: return - + import os try: diff --git a/frappe/templates/generators/blog_post.py b/frappe/templates/generators/blog_post.py index 5726481a9d..fd24ce9abd 100644 --- a/frappe/templates/generators/blog_post.py +++ b/frappe/templates/generators/blog_post.py @@ -13,7 +13,7 @@ sort_by = "published_on" sort_order = "desc" def get_context(context): - blog_post = context.bean.doc + blog_post = context.doc # this is for double precaution. usually it wont reach this code if not published if not cint(blog_post.published): @@ -24,7 +24,7 @@ def get_context(context): blog_post.updated = global_date_format(blog_post.published_on) if blog_post.blogger: - blog_post.blogger_info = frappe.doc("Blogger", blog_post.blogger).fields + blog_post.blogger_info = frappe.get_doc("Blogger", blog_post.blogger).as_dict() blog_post.description = blog_post.blog_intro or blog_post.content[:140] blog_post.meta_description = blog_post.description @@ -37,7 +37,7 @@ def get_context(context): and comment_docname=%s order by creation""", (blog_post.name,), as_dict=1) or [] - return blog_post.fields + return blog_post.as_dict() @frappe.whitelist(allow_guest=True) def get_blog_list(start=0, by=None, category=None): diff --git a/frappe/templates/generators/web_page.py b/frappe/templates/generators/web_page.py index f65be43684..f5ab6f1a3e 100644 --- a/frappe/templates/generators/web_page.py +++ b/frappe/templates/generators/web_page.py @@ -9,24 +9,26 @@ doctype = "Web Page" condition_field = "published" def get_context(context): - web_page = context.bean - - if web_page.doc.slideshow: - web_page.doc.fields.update(get_slideshow(web_page)) - - web_page.doc.meta_description = web_page.doc.description - - if web_page.doc.enable_comments: - web_page.doc.comment_list = frappe.db.sql("""select + web_page = frappe._dict(context.doc.as_dict()) + + if web_page.slideshow: + web_page.update(get_slideshow(web_page)) + + web_page.meta_description = web_page.description + + if web_page.enable_comments: + web_page.comment_list = frappe.db.sql("""select comment, comment_by_fullname, creation from `tabComment` where comment_doctype="Web Page" - and comment_docname=%s order by creation""", web_page.doc.name, as_dict=1) or [] - - web_page.doc.fields.update({ - "style": web_page.doc.css or "", - "script": web_page.doc.javascript or "" + and comment_docname=%s order by creation""", web_page.name, as_dict=1) or [] + + web_page.update({ + "style": web_page.css or "", + "script": web_page.javascript or "" }) - - web_page.doc.fields.update(context) - - return web_page.doc.fields + web_page.update(context) + + if "" in web_page.main_section: + web_page.no_sidebar = True + + return web_page diff --git a/frappe/templates/generators/website_group.py b/frappe/templates/generators/website_group.py index fae44481ac..77ad3aeb3a 100644 --- a/frappe/templates/generators/website_group.py +++ b/frappe/templates/generators/website_group.py @@ -13,13 +13,13 @@ no_cache = 1 def get_context(context): group, view = guess_group_view(context) - + try: if not has_access(context.access, view): raise frappe.PermissionError - + return get_group_context(group, view, context) - + except frappe.DoesNotExistError: return { "content": '
    ' @@ -30,71 +30,71 @@ def get_context(context): "content": '
    ' 'You are not permitted to view this page.
    ' } - + def get_group_context(group, view, context): cache_key = "website_group_context:{}:{}".format(group, view) - views = get_views(context.bean.doc.group_type) + views = get_views(context.doc.group_type) view = frappe._dict(views.get(view)) - + if can_cache(view.no_cache): group_context = frappe.cache().get_value(cache_key) if group_context: return group_context - + group_context = build_group_context(group, view, views, context) - + if can_cache(view.get("no_cache")): frappe.cache().set_value(cache_key, group_context) - + return group_context - + def build_group_context(group, view, views, context): - title = "{} - {}".format(context.bean.doc.group_title, view.get("label")) - + title = "{} - {}".format(context.doc.group_title, view.get("label")) + group_context = frappe._dict({ - "group": context.bean.doc.fields, + "group": context.doc, "view": view, "views": [v[1] for v in sorted(views.iteritems(), key=lambda (k, v): v.get("idx"))], "title": title, "pathname": context.pathname }) group_context.update(build_view_context(group_context)) - + return group_context - + def build_view_context(context): from frappe.templates.website_group.post import get_post_context - + if context.view.name in ("popular", "feed", "open", "closed", "upcoming", "past"): context.post_list_html = get_post_list_html(context.group.name, context.view.name) - + elif context.view.name == "edit": - context.post = frappe.doc("Post", frappe.form_dict.name).fields - + context.post = frappe.get_doc("Post", frappe.form_dict.name).as_dict() + if context.post.assigned_to: - context.user = frappe.doc("User", context.post.assigned_to) + context.user = frappe.get_doc("User", context.post.assigned_to) elif context.view.name == "settings": context.users = frappe.db.sql("""select p.*, wsp.`read`, wsp.`write`, wsp.`admin` from `tabUser` p, `tabWebsite Route Permission` wsp where wsp.website_route=%s and wsp.user=p.name""", context.pathname, as_dict=True) - + elif context.view.name == "post": context.update(get_post_context(context)) - + return context - + def guess_group_view(context): group = context.docname - view = frappe.form_dict.view or get_default_view(context.bean.doc.group_type) + view = frappe.form_dict.view or get_default_view(context.doc.group_type) return group, view - + def get_default_view(group_type): for view, opts in get_views(group_type).iteritems(): if opts.get("default"): return view - + def get_views(group_type=None): if group_type: group_views = frappe._dict(views[group_type]) @@ -102,22 +102,22 @@ def get_views(group_type=None): group_views = {} for group_type in views: group_views.update(views[group_type].copy()) - + group_views.update(common_views) - + if group_type == "Forum": group_views["post"]["upvote"] = True - + return group_views - -def has_access(access, view): + +def has_access(access, view): if view=="settings": return access.get("admin") elif view in ("add", "edit"): return access.get("write") else: return access.get("read") - + def clear_cache(path=None, website_group=None): from frappe.templates.website_group.post import clear_post_cache if path: @@ -127,7 +127,7 @@ def clear_cache(path=None, website_group=None): else: clear_post_cache() website_groups = frappe.db.sql_list("""select name from `tabWebsite Group`""") - + cache = frappe.cache() all_views = get_views() for group in website_groups: @@ -137,10 +137,10 @@ def clear_cache(path=None, website_group=None): def clear_event_cache(): for group in frappe.db.sql_list("""select name from `tabWebsite Group` where group_type='Event'"""): clear_unit_views(website_group=group) - -def clear_cache_on_bean_event(bean, method, *args, **kwargs): - clear_cache(path=bean.doc.website_route, website_group=bean.doc.website_group) - + +def clear_cache_on_doc_event(doc, method, *args, **kwargs): + clear_cache(path=doc.website_route, website_group=doc.website_group) + def get_pathname(group): return frappe.db.get_value("Website Route", {"ref_doctype": "Website Group", "docname": group}) @@ -237,4 +237,4 @@ common_views = { "hidden": True, "idx": 6 } -} \ No newline at end of file +} diff --git a/frappe/templates/includes/comments.py b/frappe/templates/includes/comments.py index abb05a2c90..e25429b9d5 100644 --- a/frappe/templates/includes/comments.py +++ b/frappe/templates/includes/comments.py @@ -30,7 +30,7 @@ def add_comment(args=None): if "cmd" in args: del args["cmd"] - comment = frappe.bean(args) + comment = frappe.get_doc(args) comment.ignore_permissions = True comment.insert() @@ -40,9 +40,9 @@ def add_comment(args=None): # notify commentors commentors = [d[0] for d in frappe.db.sql("""select comment_by from tabComment where comment_doctype=%s and comment_docname=%s and - ifnull(unsubscribed, 0)=0""", (comment.doc.comment_doctype, comment.doc.comment_docname))] + ifnull(unsubscribed, 0)=0""", (comment.comment_doctype, comment.comment_docname))] - owner = frappe.db.get_value(comment.doc.comment_doctype, comment.doc.comment_docname, "owner") + owner = frappe.db.get_value(comment.comment_doctype, comment.comment_docname, "owner") recipients = commentors if owner=="Administrator" else list(set(commentors + [owner])) @@ -50,12 +50,12 @@ def add_comment(args=None): send(recipients=recipients, doctype='Comment', email_field='comment_by', - subject='New Comment on %s: %s' % (comment.doc.comment_doctype, - comment.doc.title or comment.doc.comment_docname), + subject='New Comment on %s: %s' % (comment.comment_doctype, + comment.title or comment.comment_docname), message='%(comment)s

    By %(comment_by_fullname)s

    ' % args, - ref_doctype=comment.doc.comment_doctype, ref_docname=comment.doc.comment_docname) + ref_doctype=comment.comment_doctype, ref_docname=comment.comment_docname) template = frappe.get_template("templates/includes/comment.html") - return template.render({"comment": comment.doc.fields}) + return template.render({"comment": comment.as_dict()}) \ No newline at end of file diff --git a/frappe/templates/includes/sidebar.html b/frappe/templates/includes/sidebar.html index 375fd5e1d8..465d5aee9c 100644 --- a/frappe/templates/includes/sidebar.html +++ b/frappe/templates/includes/sidebar.html @@ -1,8 +1,9 @@ {% if children -%} {%- for child in children -%}
    {%- endfor -%} -{%- endif %} \ No newline at end of file +{%- endif %} diff --git a/frappe/templates/includes/static_index.html b/frappe/templates/includes/static_index.html new file mode 100644 index 0000000000..7b0bc34cd8 --- /dev/null +++ b/frappe/templates/includes/static_index.html @@ -0,0 +1,17 @@ + +
    +
    + +
    +
    diff --git a/frappe/templates/pages/about.html b/frappe/templates/pages/about.html index 2da2f9b97e..e1242c86e5 100644 --- a/frappe/templates/pages/about.html +++ b/frappe/templates/pages/about.html @@ -10,9 +10,9 @@ Be sure to update your company history and list of key team members in Website > About Us Settings

    """ }} - {% if obj.doclist.get({"doctype":"Company History"}) %} + {% if obj.get({"doctype":"Company History"}) %}

    {{ obj.doc.company_history_heading or "Company History" }}

    - {% for d in obj.doclist.get({"doctype":"Company History"}) %} + {% for d in obj.get({"doctype":"Company History"}) %}

    {{ d.year }}

    {{ d.highlight }}

    @@ -20,9 +20,9 @@ {% endfor %} {% endif %} - {% if obj.doclist.get({"doctype":"About Us Team Member"}) %} + {% if obj.get({"doctype":"About Us Team Member"}) %}

    {{ obj.doc.team_members_heading or "Team Members" }}

    - {% for d in obj.doclist.get({"doctype":"About Us Team Member"}) %} + {% for d in obj.get({"doctype":"About Us Team Member"}) %}
    diff --git a/frappe/templates/pages/about.py b/frappe/templates/pages/about.py index 75b6262716..13554fe53b 100644 --- a/frappe/templates/pages/about.py +++ b/frappe/templates/pages/about.py @@ -5,4 +5,4 @@ from __future__ import unicode_literals import frappe def get_context(context): - return { "obj": frappe.bean("About Us Settings", "About Us Settings").get_controller() } + return { "obj": frappe.get_doc("About Us Settings", "About Us Settings") } diff --git a/frappe/templates/pages/blog.py b/frappe/templates/pages/blog.py index f6f0854f15..7181ff63b4 100644 --- a/frappe/templates/pages/blog.py +++ b/frappe/templates/pages/blog.py @@ -5,4 +5,4 @@ from __future__ import unicode_literals import frappe def get_context(context): - return frappe.doc("Blog Settings", "Blog Settings").fields + return frappe.get_doc("Blog Settings", "Blog Settings").as_dict() diff --git a/frappe/templates/pages/contact.py b/frappe/templates/pages/contact.py index 24fc9237d4..cab5013ab4 100644 --- a/frappe/templates/pages/contact.py +++ b/frappe/templates/pages/contact.py @@ -7,17 +7,17 @@ import frappe from frappe.utils import now def get_context(context): - bean = frappe.bean("Contact Us Settings", "Contact Us Settings") + doc = frappe.get_doc("Contact Us Settings", "Contact Us Settings") - query_options = filter(None, bean.doc.query_options.replace(",", "\n").split()) if \ - bean.doc.query_options else ["Sales", "Support", "General"] + query_options = filter(None, doc.query_options.replace(",", "\n").split()) if \ + doc.query_options else ["Sales", "Support", "General"] - address = frappe.bean("Address", bean.doc.address).doc if bean.doc.address else None + address = frappe.get_doc("Address", doc.address) if doc.address else None out = { "query_options": query_options } - out.update(bean.doc.fields) + out.update(doc.as_dict()) return out diff --git a/frappe/templates/pages/desk.py b/frappe/templates/pages/desk.py index b596b7e8b1..e91c4e027b 100644 --- a/frappe/templates/pages/desk.py +++ b/frappe/templates/pages/desk.py @@ -1,9 +1,10 @@ # 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 no_sitemap = 1 +no_cache = 1 base_template_path = "templates/pages/desk.html" import frappe, os @@ -11,8 +12,8 @@ import frappe, os def get_context(context): hooks = frappe.get_hooks() return { - "build_version": str(os.path.getmtime(os.path.join(frappe.local.sites_path, "assets", "js", + "build_version": str(os.path.getmtime(os.path.join(frappe.local.sites_path, "assets", "js", "frappe.min.js"))), "include_js": hooks["app_include_js"], "include_css": hooks["app_include_css"] - } \ No newline at end of file + } diff --git a/frappe/templates/pages/login.py b/frappe/templates/pages/login.py index f98015af41..fcde1e341c 100644 --- a/frappe/templates/pages/login.py +++ b/frappe/templates/pages/login.py @@ -84,10 +84,10 @@ def get_oauth_keys(provider): if not keys: # try database - social = frappe.doc("Social Login Keys", "Social Login Keys") + social = frappe.get_doc("Social Login Keys", "Social Login Keys") keys = {} for fieldname in ("client_id", "client_secret"): - value = social.fields.get("{provider}_{fieldname}".format(provider=provider, fieldname=fieldname)) + value = social.get("{provider}_{fieldname}".format(provider=provider, fieldname=fieldname)) if not value: keys = {} break @@ -183,7 +183,7 @@ def create_oauth_user(data, provider): if isinstance(data.get("location"), dict): data["location"] = data.get("location").get("name") - user = frappe.bean({ + user = frappe.get_doc({ "doctype":"User", "first_name": data.get("first_name") or data.get("given_name") or data.get("name"), "last_name": data.get("last_name") or data.get("family_name"), @@ -198,18 +198,18 @@ def create_oauth_user(data, provider): }) if provider=="facebook": - user.doc.fields.update({ + user.update({ "fb_username": data["username"], "fb_userid": data["id"], "user_image": "https://graph.facebook.com/{username}/picture".format(username=data["username"]) }) elif provider=="google": - user.doc.google_userid = data["id"] + user.google_userid = data["id"] elif provider=="github": - user.doc.github_userid = data["id"] - user.doc.github_username = data["login"] + user.github_userid = data["id"] + user.github_username = data["login"] user.ignore_permissions = True - user.get_controller().no_welcome_mail = True + user.no_welcome_mail = True user.insert() diff --git a/frappe/templates/pages/rss.py b/frappe/templates/pages/rss.py index f41239473b..a02f1dda11 100644 --- a/frappe/templates/pages/rss.py +++ b/frappe/templates/pages/rss.py @@ -29,7 +29,7 @@ def get_context(context): else: modified = now() - ws = frappe.doc('Website Settings', 'Website Settings') + ws = frappe.get_doc('Website Settings', 'Website Settings') context = { 'title': ws.title_prefix, diff --git a/frappe/templates/pages/style_settings.py b/frappe/templates/pages/style_settings.py index fad8848f81..23f2fbbe20 100644 --- a/frappe/templates/pages/style_settings.py +++ b/frappe/templates/pages/style_settings.py @@ -9,10 +9,10 @@ base_template_path = "templates/pages/style_settings.css" def get_context(context): """returns web style""" - doc = frappe.doc("Style Settings", "Style Settings") + doc = frappe.get_doc("Style Settings", "Style Settings") prepare(doc) - return { "doc": doc.fields } + return { "doc": doc.as_dict() } def prepare(doc): from frappe.utils import cint, cstr @@ -28,8 +28,8 @@ def prepare(doc): } for d in default_colours: - if not doc.fields.get(d): - doc.fields[d] = default_colours[d] + if not doc.get(d): + doc.set(d, default_colours[d]) if not doc.font_size: doc.font_size = "14px" diff --git a/frappe/templates/pages/writers.py b/frappe/templates/pages/writers.py index 2b3093a551..4a793fd1be 100644 --- a/frappe/templates/pages/writers.py +++ b/frappe/templates/pages/writers.py @@ -17,6 +17,6 @@ def get_context(context): "categories": frappe.db.sql_list("select name from `tabBlog Category` order by name") } - writers_context.update(frappe.doc("Blog Settings", "Blog Settings").fields) + writers_context.update(frappe.get_doc("Blog Settings", "Blog Settings").as_dict()) return writers_context diff --git a/frappe/templates/website_group/post.py b/frappe/templates/website_group/post.py index 1563158975..ebdc15283d 100644 --- a/frappe/templates/website_group/post.py +++ b/frappe/templates/website_group/post.py @@ -9,10 +9,10 @@ from frappe.utils.file_manager import save_file from frappe.templates.generators.website_group import get_pathname def get_post_context(context): - post = frappe.doc("Post", frappe.form_dict.name) + post = frappe.get_doc("Post", frappe.form_dict.name) if post.parent_post: raise frappe.PermissionError - + def _get_post_context(): fullname = get_fullname(post.owner) return { @@ -21,42 +21,42 @@ def get_post_context(context): "post_list_html": get_child_posts_html(post, context), "parent_post": post.name } - + cache_key = "website_group_post:{}".format(post.name) return frappe.cache().get_value(cache_key, lambda: _get_post_context()) - + def get_parent_post_html(post, context): - user = frappe.bean("User", post.owner).doc + user = frappe.get_doc("User", post.owner) for fieldname in ("first_name", "last_name", "user_image", "location"): - post.fields[fieldname] = user.fields[fieldname] - + post.set(fieldname, user.get(fieldname)) + return frappe.get_template("templates/includes/inline_post.html")\ - .render({"post": post.fields, "view": context.view}) + .render({"post": post, "view": context.view}) def get_child_posts_html(post, context): posts = frappe.db.sql("""select p.*, pr.user_image, pr.first_name, pr.last_name from tabPost p, tabUser pr where p.parent_post=%s and pr.name = p.owner order by p.creation asc""", (post.name,), as_dict=True) - + return frappe.get_template("templates/includes/post_list.html")\ .render({ "posts": posts, "parent_post": post.name, "view": context.view }) - + def clear_post_cache(post=None): cache = frappe.cache() posts = [post] if post else frappe.db.sql_list("select name from `tabPost`") for post in posts: cache.delete_value("website_group_post:{}".format(post)) - + @frappe.whitelist(allow_guest=True) -def add_post(group, content, picture, picture_name, title=None, parent_post=None, +def add_post(group, content, picture, picture_name, title=None, parent_post=None, assigned_to=None, status=None, event_datetime=None): - + access = get_access(get_pathname(group)) if not access.get("write"): raise frappe.PermissionError @@ -64,56 +64,56 @@ def add_post(group, content, picture, picture_name, title=None, parent_post=None if parent_post: if frappe.db.get_value("Post", parent_post, "parent_post"): frappe.throw("Cannot reply to a reply") - - group = frappe.doc("Website Group", group) - post = frappe.bean({ + + group = frappe.get_doc("Website Group", group) + post = frappe.get_doc({ "doctype":"Post", "title": (title or "").title(), "content": content, "website_group": group.name, "parent_post": parent_post or None }) - + if not parent_post: if group.group_type == "Tasks": - post.doc.is_task = 1 - post.doc.assigned_to = assigned_to + post.is_task = 1 + post.assigned_to = assigned_to elif group.group_type == "Events": - post.doc.is_event = 1 - post.doc.event_datetime = event_datetime - + post.is_event = 1 + post.event_datetime = event_datetime + post.ignore_permissions = True post.insert() if picture_name and picture: process_picture(post, picture_name, picture) - + # send email if parent_post: post.run_method("send_email_on_reply") - - return post.doc.parent_post or post.doc.name - + + return post.parent_post or post.name + @frappe.whitelist(allow_guest=True) def save_post(post, content, picture=None, picture_name=None, title=None, assigned_to=None, status=None, event_datetime=None): - - post = frappe.bean("Post", post) - access = get_access(get_pathname(post.doc.website_group)) - + + post = frappe.get_doc("Post", post) + access = get_access(get_pathname(post.website_group)) + if not access.get("write"): raise frappe.PermissionError - + # TODO improve error message - if frappe.session.user != post.doc.owner: + if frappe.session.user != post.owner: for fieldname in ("title", "content"): - if post.doc.fields.get(fieldname) != locals().get(fieldname): + if post.get(fieldname) != locals().get(fieldname): frappe.throw("You cannot change: {}".format(fieldname.title())) - + if picture and picture_name: frappe.throw("You cannot change: Picture") - - post.doc.fields.update({ + + post.update({ "title": (title or "").title(), "content": content, "assigned_to": assigned_to, @@ -122,34 +122,34 @@ def save_post(post, content, picture=None, picture_name=None, title=None, }) post.ignore_permissions = True post.save() - + if picture_name and picture: process_picture(post, picture_name, picture) - - return post.doc.parent_post or post.doc.name - + + return post.parent_post or post.name + def process_picture(post, picture_name, picture): from frappe.templates.generators.website_group import clear_cache - - file_data = save_file(picture_name, picture, "Post", post.doc.name, decode=True) - post.doc.picture_url = file_data.file_name or file_data.file_url - frappe.db.set_value("Post", post.doc.name, "picture_url", post.doc.picture_url) - clear_cache(website_group=post.doc.website_group) - + + file_data = save_file(picture_name, picture, "Post", post.name, decode=True) + post.picture_url = file_data.file_name or file_data.file_url + frappe.db.set_value("Post", post.name, "picture_url", post.picture_url) + clear_cache(website_group=post.website_group) + @frappe.whitelist() def suggest_user(group, term): """suggest a user that has read permission in this group tree""" - users = frappe.db.sql("""select - pr.name, pr.first_name, pr.last_name, + users = frappe.db.sql("""select + pr.name, pr.first_name, pr.last_name, pr.user_image, pr.location from `tabUser` pr where (pr.first_name like %(term)s or pr.last_name like %(term)s) - and pr.user_type = 'Website User' and pr.enabled=1""", + and pr.user_type = 'Website User' and pr.enabled=1""", {"term": "%{}%".format(term), "group": group}, as_dict=True) - + template = frappe.get_template("templates/includes/user_display.html") return [{ - "value": "{} {}".format(pr.first_name or "", pr.last_name or "").strip(), + "value": "{} {}".format(pr.first_name or "", pr.last_name or "").strip(), "user_html": template.render({"user": pr}), "user": pr.name } for pr in users] diff --git a/frappe/templates/website_group/settings.py b/frappe/templates/website_group/settings.py index 702263ed40..d7397f7be9 100644 --- a/frappe/templates/website_group/settings.py +++ b/frappe/templates/website_group/settings.py @@ -36,7 +36,7 @@ def add_sitemap_permission(group, user): if not get_access(pathname).get("admin"): raise frappe.PermissionError - permission = frappe.bean({ + permission = frappe.get_doc({ "doctype": "Website Route Permission", "website_route": pathname, "user": user, @@ -44,7 +44,7 @@ def add_sitemap_permission(group, user): }) permission.insert(ignore_permissions=True) - user = permission.doc.fields + user = permission.as_dict() user.update(frappe.db.get_value("User", user.user, ["name", "first_name", "last_name", "user_image", "location"], as_dict=True)) @@ -58,8 +58,8 @@ def update_permission(group, user, perm, value): if not get_access(pathname).get("admin"): raise frappe.PermissionError - permission = frappe.bean("Website Route Permission", {"website_route": pathname, "user": user}) - permission.doc.fields[perm] = int(value) + permission = frappe.get_doc("Website Route Permission", {"website_route": pathname, "user": user}) + permission.set(perm, int(value)) permission.save(ignore_permissions=True) # send email @@ -79,8 +79,8 @@ def update_description(group, description): if not get_access(get_pathname(group)).get("admin"): raise frappe.PermissionError - group = frappe.bean("Website Group", group) - group.doc.group_description = description + group = frappe.get_doc("Website Group", group) + group.group_description = description group.save(ignore_permissions=True) @frappe.whitelist() @@ -91,7 +91,7 @@ def add_website_group(group, new_group, public_read, public_write, group_type="F parent_website_route = frappe.db.get_value("Website Route", {"ref_doctype": "Website Group", "docname": group}) - frappe.bean({ + frappe.get_doc({ "doctype": "Website Group", "group_name": group + "-" + new_group, "group_title": new_group, diff --git a/frappe/test_runner.py b/frappe/test_runner.py index 9a9fe36e09..6f50abb597 100644 --- a/frappe/test_runner.py +++ b/frappe/test_runner.py @@ -4,36 +4,32 @@ from __future__ import unicode_literals import frappe -import unittest, sys - -from frappe.model.meta import has_field -from frappe.model.code import load_doctype_module, get_module_name -from frappe.model.doctype import get_link_fields +import unittest, sys, json +import importlib +from frappe.modules import load_doctype_module, get_module_name from frappe.utils import cstr -def main(app=None, module=None, doctype=None, verbose=False): +def main(app=None, module=None, doctype=None, verbose=False, tests=()): frappe.flags.print_messages = verbose frappe.flags.in_test = True - + if not frappe.db: frappe.connect() - + + # workaround! since there is no separate test db + frappe.clear_cache() + if doctype: - ret = run_unittest(doctype, verbose=verbose) + ret = run_tests_for_doctype(doctype, verbose=verbose, tests=tests) elif module: - import importlib - - test_suite = unittest.TestSuite() - module = importlib.import_module(module) - if hasattr(module, "test_dependencies"): - for doctype in module.test_dependencies: - make_test_records(doctype, verbose=verbose) - - test_suite.addTest(unittest.TestLoader().loadTestsFromModule(module)) - ret = unittest.TextTestRunner(verbosity=1+(verbose and 1 or 0)).run(test_suite) + ret = run_tests_for_module(module, verbose=verbose, tests=tests) else: ret = run_all_tests(app, verbose) + + # workaround! since there is no separate test db + frappe.clear_cache() + return ret def run_all_tests(app=None, verbose=False): @@ -45,44 +41,68 @@ def run_all_tests(app=None, verbose=False): for app in apps: for path, folders, files in os.walk(frappe.get_pymodule_path(app)): for dontwalk in ('locals', '.git', 'public'): - if dontwalk in folders: + if dontwalk in folders: folders.remove(dontwalk) - + # print path for filename in files: filename = cstr(filename) if filename.startswith("test_") and filename.endswith(".py"): # print filename[:-3] - _run_test(path, filename, verbose, test_suite=test_suite, run=False) - + _add_test(path, filename, verbose, test_suite=test_suite) + return unittest.TextTestRunner(verbosity=1+(verbose and 1 or 0)).run(test_suite) -def _run_test(path, filename, verbose, test_suite=None, run=True): +def run_tests_for_doctype(doctype, verbose=False, tests=()): + module = frappe.db.get_value("DocType", doctype, "module") + test_module = get_module_name(doctype, module, "test_") + make_test_records(doctype, verbose=verbose) + module = frappe.get_module(test_module) + return _run_unittest(module, verbose=verbose, tests=tests) + +def run_tests_for_module(module, verbose=False, tests=()): + module = importlib.import_module(module) + if hasattr(module, "test_dependencies"): + for doctype in module.test_dependencies: + make_test_records(doctype, verbose=verbose) + + return _run_unittest(module=module, verbose=verbose, tests=tests) + +def _run_unittest(module, verbose=False, tests=()): + test_suite = unittest.TestSuite() + module_test_cases = unittest.TestLoader().loadTestsFromModule(module) + if tests: + for each in module_test_cases: + for test_case in each.__dict__["_tests"]: + if test_case.__dict__["_testMethodName"] in tests: + test_suite.addTest(test_case) + else: + test_suite.addTest(module_test_cases) + + return unittest.TextTestRunner(verbosity=1+(verbose and 1 or 0)).run(test_suite) + +def _add_test(path, filename, verbose, test_suite=None): import os, imp - from frappe.modules.utils import peval_doclist - + if not test_suite: test_suite = unittest.TestSuite() - + if os.path.basename(os.path.dirname(path))=="doctype": - txt_file = os.path.join(path, filename[5:].replace(".py", ".txt")) + txt_file = os.path.join(path, filename[5:].replace(".py", ".json")) with open(txt_file, 'r') as f: - doctype_doclist = peval_doclist(f.read()) - doctype = doctype_doclist[0]["name"] + doc = json.loads(f.read()) + doctype = doc["name"] make_test_records(doctype, verbose) - + module = imp.load_source(filename[:-3], os.path.join(path, filename)) test_suite.addTest(unittest.TestLoader().loadTestsFromModule(module)) - - if run: - unittest.TextTestRunner(verbosity=1+(verbose and 1 or 0)).run(test_suite) def make_test_records(doctype, verbose=0): frappe.flags.mute_emails = True - + if not frappe.db: frappe.connect() - + for options in get_dependencies(doctype): if options.startswith("link:"): options = options[5:] @@ -98,7 +118,7 @@ def get_modules(doctype): module = frappe.db.get_value("DocType", doctype, "module") try: test_module = load_doctype_module(doctype, module, "test_") - if test_module: + if test_module: reload(test_module) except ImportError, e: test_module = None @@ -107,22 +127,29 @@ def get_modules(doctype): def get_dependencies(doctype): module, test_module = get_modules(doctype) - - options_list = list(set([df.options for df in get_link_fields(doctype)] + [doctype])) - + meta = frappe.get_meta(doctype) + link_fields = meta.get_link_fields() + + for df in meta.get_table_fields(): + link_fields.extend(frappe.get_meta(df.options).get_link_fields()) + + options_list = [df.options for df in link_fields] + [doctype] + if hasattr(test_module, "test_dependencies"): options_list += test_module.test_dependencies - + + options_list = list(set(options_list)) + if hasattr(test_module, "test_ignore"): for doctype_name in test_module.test_ignore: if doctype_name in options_list: options_list.remove(doctype_name) - + return options_list def make_test_records_for_doctype(doctype, verbose=0): module, test_module = get_modules(doctype) - + if verbose: print "Making for " + doctype @@ -131,52 +158,50 @@ def make_test_records_for_doctype(doctype, verbose=0): elif hasattr(test_module, "test_records"): frappe.local.test_objects[doctype] += make_test_objects(doctype, test_module.test_records, verbose) - elif verbose: - print_mandatory_fields(doctype) + else: + test_records = frappe.get_test_records(doctype) + if test_records: + frappe.local.test_objects[doctype] += make_test_objects(doctype, test_records, verbose) + + elif verbose: + print_mandatory_fields(doctype) def make_test_objects(doctype, test_records, verbose=None): records = [] - - for doclist in test_records: - if "doctype" not in doclist[0]: - doclist[0]["doctype"] = doctype - d = frappe.bean(copy=doclist) - - if frappe.local.test_objects.get(d.doc.doctype): + + for doc in test_records: + if not doc.get("doctype"): + doc["doctype"] = doctype + + d = frappe.copy_doc(doc) + + if frappe.local.test_objects.get(d.doctype): # do not create test records, if already exists return [] - if has_field(d.doc.doctype, "naming_series"): - if not d.doc.naming_series: - d.doc.naming_series = "_T-" + d.doc.doctype + "-" + + if d.meta.get_field("naming_series"): + if not d.naming_series: + d.naming_series = "_T-" + d.doctype + "-" # submit if docstatus is set to 1 for test record - docstatus = d.doc.docstatus - - d.doc.docstatus = 0 + docstatus = d.docstatus + + d.docstatus = 0 d.insert() if docstatus == 1: d.submit() - - records.append(d.doc.name) + + records.append(d.name) return records def print_mandatory_fields(doctype): print "Please setup make_test_records for: " + doctype print "-" * 60 - doctype_obj = frappe.get_doctype(doctype) - print "Autoname: " + (doctype_obj[0].autoname or "") + meta = frappe.get_meta(doctype) + print "Autoname: " + (meta.autoname or "") print "Mandatory Fields: " - for d in doctype_obj.get({"reqd":1}): + for d in meta.get("fields", {"reqd":1}): print d.parent + ":" + d.fieldname + " | " + d.fieldtype + " | " + (d.options or "") - print - -def run_unittest(doctype, verbose=False): - module = frappe.db.get_value("DocType", doctype, "module") - test_module = get_module_name(doctype, module, "test_") - make_test_records(doctype, verbose=verbose) - test_suite = unittest.TestSuite() - module = frappe.get_module(test_module) - test_suite.addTest(unittest.TestLoader().loadTestsFromModule(module)) - return unittest.TextTestRunner(verbosity=1+(verbose and 1 or 0)).run(test_suite) + print diff --git a/frappe/tests/__init__.py b/frappe/tests/__init__.py index 5d22462144..00e6b358eb 100644 --- a/frappe/tests/__init__.py +++ b/frappe/tests/__init__.py @@ -24,14 +24,14 @@ def get_test_doclist(doctype, name=None): conf.test_data_path, doctype) if name: - with open(os.path.join(doctype_path, scrub(name) + '.txt'), 'r') as txtfile: + with open(os.path.join(doctype_path, scrub(name) + ".json"), 'r') as txtfile: doclist = peval_doclist(txtfile.read()) return doclist else: all_doclists = [] - for fname in filter(lambda n: n.endswith('.txt'), os.listdir(doctype_path)): + for fname in filter(lambda n: n.endswith(".json"), os.listdir(doctype_path)): with open(os.path.join(doctype_path, scrub(fname)), 'r') as txtfile: all_doclists.append(peval_doclist(txtfile.read())) diff --git a/frappe/tests/test_assign.py b/frappe/tests/test_assign.py new file mode 100644 index 0000000000..789e135668 --- /dev/null +++ b/frappe/tests/test_assign.py @@ -0,0 +1,22 @@ +# Copyright (c) 2014, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +import frappe, unittest +import frappe.widgets.form.assign_to + +class TestAssign(unittest.TestCase): + def test_assign(self): + todo = frappe.get_doc({"doctype":"ToDo", "description": "test"}).insert() + if not frappe.db.exists("User", "test@example.com"): + frappe.get_doc({"doctype":"User", "email":"test@example.com", "first_name":"Test"}) + + added = frappe.widgets.form.assign_to.add({ + "assign_to": "test@example.com", + "doctype": todo.doctype, + "name": todo.name, + "description": todo.description, + }) + self.assertTrue("test@example.com" in added) + + removed = frappe.widgets.form.assign_to.remove(todo.doctype, todo.name, "test@example.com") + self.assertTrue("test@example.com" not in removed) diff --git a/frappe/tests/test_data_import.py b/frappe/tests/test_data_import.py new file mode 100644 index 0000000000..140cd891ab --- /dev/null +++ b/frappe/tests/test_data_import.py @@ -0,0 +1,58 @@ +# Copyright (c) 2014, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +import frappe, unittest +from frappe.core.page.data_import_tool import exporter +from frappe.core.page.data_import_tool import importer +from frappe.utils.datautils import read_csv_content + +class TestDataImport(unittest.TestCase): + def test_export(self): + exporter.get_template("User", all_doctypes="No", with_data="No") + content = read_csv_content(frappe.response.result) + self.assertTrue(content[1][1], "User") + + def test_export_with_data(self): + exporter.get_template("User", all_doctypes="No", with_data="Yes") + content = read_csv_content(frappe.response.result) + self.assertTrue(content[1][1], "User") + self.assertTrue("Administrator" in [c[1] for c in content if len(c)>1]) + + def test_export_with_all_doctypes(self): + exporter.get_template("User", all_doctypes="Yes", with_data="Yes") + content = read_csv_content(frappe.response.result) + self.assertTrue(content[1][1], "User") + self.assertTrue('"Administrator"' in [c[1] for c in content if len(c)>1]) + self.assertEquals(content[13][0], "DocType:") + self.assertEquals(content[13][1], "User") + self.assertTrue("UserRole" in content[13]) + + def test_import(self): + exporter.get_template("Blog Category", all_doctypes="No", with_data="No") + content = read_csv_content(frappe.response.result) + content.append(["", "", "test-category", "Test Cateogry"]) + importer.upload(content) + self.assertTrue(frappe.db.get_value("Blog Category", "test-category", "title"), "Test Category") + + # export with data + exporter.get_template("Blog Category", all_doctypes="No", with_data="Yes") + content = read_csv_content(frappe.response.result) + + # overwrite + content[-1][3] = "New Title" + importer.upload(content, overwrite=True) + self.assertTrue(frappe.db.get_value("Blog Category", "test-category", "title"), "New Title") + + def test_import_with_children(self): + exporter.get_template("Event", all_doctypes="Yes", with_data="No") + content = read_csv_content(frappe.response.result) + content.append([""] * len(content[-2])) + content[-1][2] = "__Test Event" + content[-1][3] = "Private" + content[-1][3] = "2014-01-01 10:00:00.000000" + content[-1][content[15].index("person")] = "Administrator" + importer.upload(content) + + ev = frappe.get_doc("Event", {"subject":"__Test Event"}) + self.assertTrue("Administrator" in [d.person for d in ev.event_individuals]) + \ No newline at end of file diff --git a/frappe/tests/test_document.py b/frappe/tests/test_document.py index 965501bcfe..3cc8b1ae1d 100644 --- a/frappe/tests/test_document.py +++ b/frappe/tests/test_document.py @@ -4,6 +4,10 @@ import frappe, unittest, time class TestDocument(unittest.TestCase): + def test_get_return_empty_list_for_table_field_if_none(self): + d = frappe.get_doc({"doctype":"User"}) + self.assertEquals(d.get("user_roles"), []) + def test_load(self): d = frappe.get_doc("DocType", "User") self.assertEquals(d.doctype, "DocType") @@ -22,14 +26,14 @@ class TestDocument(unittest.TestCase): def test_insert(self): d = frappe.get_doc({ "doctype":"Event", - "subject":"_Test Event 1", + "subject":"test-doc-test-event 1", "starts_on": "2014-01-01", "event_type": "Public" }) d.insert() self.assertTrue(d.name.startswith("EV")) self.assertEquals(frappe.db.get_value("Event", d.name, "subject"), - "_Test Event 1") + "test-doc-test-event 1") # test if default values are added self.assertEquals(d.send_reminder, 1) @@ -38,7 +42,7 @@ class TestDocument(unittest.TestCase): def test_insert_with_child(self): d = frappe.get_doc({ "doctype":"Event", - "subject":"_Test Event 2", + "subject":"test-doc-test-event 2", "starts_on": "2014-01-01", "event_type": "Public", "event_individuals": [ @@ -50,7 +54,7 @@ class TestDocument(unittest.TestCase): d.insert() self.assertTrue(d.name.startswith("EV")) self.assertEquals(frappe.db.get_value("Event", d.name, "subject"), - "_Test Event 2") + "test-doc-test-event 2") d1 = frappe.get_doc("Event", d.name) self.assertTrue(d1.event_individuals[0].person, "Administrator") @@ -109,7 +113,7 @@ class TestDocument(unittest.TestCase): }) self.assertRaises(frappe.LinkValidationError, d.insert) d.user_roles = [] - d.set("user_roles", { + d.append("user_roles", { "role": "System Manager" }) d.insert() @@ -120,5 +124,6 @@ class TestDocument(unittest.TestCase): d.starts_on = "2014-01-01" d.ends_on = "2013-01-01" self.assertRaises(frappe.ValidationError, d.validate) + self.assertRaises(frappe.ValidationError, d.run_method, "validate") self.assertRaises(frappe.ValidationError, d.save) diff --git a/frappe/tests/test_filemanager.py b/frappe/tests/test_filemanager.py new file mode 100644 index 0000000000..45207d8857 --- /dev/null +++ b/frappe/tests/test_filemanager.py @@ -0,0 +1,80 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +import frappe +import os +import unittest + +from frappe.utils.file_manager import save_file, get_file, get_files_path + +test_content1 = 'Hello' +test_content2 = 'Hello World' + +def make_test_doc(): + d = frappe.new_doc('ToDo') + d.description = 'Test' + d.save() + return d.doctype, d.name + +class TestSimpleFile(unittest.TestCase): + + def setUp(self): + self.attached_to_doctype, self.attached_to_docname = make_test_doc() + self.test_content = test_content1 + self.saved_file = save_file('hello.txt', self.test_content, self.attached_to_doctype, self.attached_to_docname) + self.saved_filename = get_files_path(self.saved_file.file_name) + + def test_save(self): + filename, content = get_file(self.saved_file.name) + self.assertEqual(content, self.test_content) + + def tearDown(self): + # File gets deleted on rollback, so blank + pass + + +class TestSameFileName(unittest.TestCase): + + def setUp(self): + self.attached_to_doctype, self.attached_to_docname = make_test_doc() + self.test_content1 = test_content1 + self.test_content2 = test_content2 + self.saved_file1 = save_file('hello.txt', self.test_content1, self.attached_to_doctype, self.attached_to_docname) + self.saved_file2 = save_file('hello.txt', self.test_content2, self.attached_to_doctype, self.attached_to_docname) + self.saved_filename1 = get_files_path(self.saved_file1.file_name) + self.saved_filename2 = get_files_path(self.saved_file2.file_name) + + def test_saved_content(self): + filename1, content1 = get_file(self.saved_file1.name) + self.assertEqual(content1, self.test_content1) + filename2, content2 = get_file(self.saved_file2.name) + self.assertEqual(content2, self.test_content2) + + def tearDown(self): + # File gets deleted on rollback, so blank + pass + + +class TestSameContent(unittest.TestCase): + + def setUp(self): + self.attached_to_doctype1, self.attached_to_docname1 = make_test_doc() + self.attached_to_doctype2, self.attached_to_docname2 = make_test_doc() + self.test_content1 = test_content1 + self.test_content2 = test_content1 + self.orig_filename = 'hello.txt' + self.dup_filename = 'hello2.txt' + self.saved_file1 = save_file(self.orig_filename, self.test_content1, self.attached_to_doctype1, self.attached_to_docname1) + self.saved_file2 = save_file(self.dup_filename, self.test_content2, self.attached_to_doctype2, self.attached_to_docname2) + self.saved_filename1 = get_files_path(self.saved_file1.file_name) + self.saved_filename2 = get_files_path(self.saved_file2.file_name) + + def test_saved_content(self): + filename1, content1 = get_file(self.saved_file1.name) + filename2, content2 = get_file(self.saved_file2.name) + self.assertEqual(filename1, filename2) + self.assertFalse(os.path.exists(get_files_path(self.dup_filename))) + + def tearDown(self): + # File gets deleted on rollback, so blank + pass diff --git a/frappe/tests/test_form_load.py b/frappe/tests/test_form_load.py new file mode 100644 index 0000000000..79e7f3ecf4 --- /dev/null +++ b/frappe/tests/test_form_load.py @@ -0,0 +1,18 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +import frappe, unittest +from frappe.widgets.form.meta import get_meta +from frappe.widgets.form.load import getdoctype, getdoc + +class TestFormLoad(unittest.TestCase): + def test_load(self): + getdoctype("DocType") + meta = filter(lambda d: d.name=="DocType", frappe.response.docs)[0] + self.assertEquals(meta.name, "DocType") + self.assertTrue(meta.get("__js")) + + frappe.response.docs = [] + d = getdoctype("Event") + meta = filter(lambda d: d.name=="Event", frappe.response.docs)[0] + self.assertTrue(meta.get("__calendar_js")) diff --git a/frappe/translate.py b/frappe/translate.py index 223bb4c83e..a54b5a357c 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -141,11 +141,11 @@ def get_messages_for_app(app): def get_messages_from_doctype(name): messages = [] - meta = frappe.get_doctype(name) + meta = frappe.get_meta(name) - messages = [meta[0].name, meta[0].description, meta[0].module] + messages = [meta.name, meta.description, meta.module] - for d in meta.get({"doctype":"DocField"}): + for d in meta.get("fields"): messages.extend([d.label, d.description]) if d.fieldtype=='Select' and d.options \ @@ -156,7 +156,7 @@ def get_messages_from_doctype(name): messages.extend(options) # extract from js, py files - doctype_file_path = frappe.get_module_path(meta[0].module, "doctype", meta[0].name, meta[0].name) + doctype_file_path = frappe.get_module_path(meta.module, "doctype", meta.name, meta.name) messages.extend(get_messages_from_file(doctype_file_path + ".js")) return clean(messages) @@ -164,7 +164,7 @@ def get_messages_from_page(name): return get_messages_from_page_or_report("Page", name) def get_messages_from_report(name): - report = frappe.doc("Report", name) + report = frappe.get_doc("Report", name) messages = get_messages_from_page_or_report("Report", name, frappe.db.get_value("DocType", report.ref_doctype, "module")) if report.query: diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 38257a51b3..d722c13087 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -1,5 +1,5 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt # util __init__.py @@ -9,6 +9,8 @@ import os import re import urllib import frappe +import datetime +import math no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'FlexTable', 'Button', 'Image', 'Graph'] @@ -19,8 +21,8 @@ default_fields = ['doctype', 'name', 'owner', 'creation', 'modified', 'modified_ # TODO: deprecate it def getCSVelement(v): """ - Returns the CSV value of `v`, For example: - + Returns the CSV value of `v`, For example: + * apple becomes "apple" * hi"there becomes "hi""there" """ @@ -35,15 +37,15 @@ def get_fullname(user): """get the full name (first name + last name) of the user from User""" if not hasattr(frappe.local, "fullnames"): frappe.local.fullnames = {} - + if not frappe.local.fullnames.get(user): p = frappe.db.get_value("User", user, ["first_name", "last_name"], as_dict=True) if p: - frappe.local.fullnames[user] = " ".join(filter(None, + frappe.local.fullnames[user] = " ".join(filter(None, [p.get('first_name'), p.get('last_name')])) or user else: frappe.local.fullnames[user] = user - + return frappe.local.fullnames.get(user) def get_formatted_email(user): @@ -53,7 +55,7 @@ def get_formatted_email(user): from email.utils import formataddr fullname = get_fullname(user) return formataddr((fullname, user)) - + def extract_email_id(email): """fetch only the email part of the email id""" from email.utils import parseaddr @@ -61,7 +63,7 @@ def extract_email_id(email): if isinstance(email_id, basestring) and not isinstance(email_id, unicode): email_id = email_id.decode("utf-8", "ignore") return email_id - + def validate_email_add(email_str): """Validates the email string""" email = extract_email_id(email_str) @@ -72,23 +74,23 @@ def random_string(length): import string from random import choice return ''.join([choice(string.letters + string.digits) for i in range(length)]) - + def get_traceback(): """ Returns the traceback of the Exception """ import sys, traceback exc_type, value, tb = sys.exc_info() - + trace_list = traceback.format_tb(tb, None) + \ traceback.format_exception_only(exc_type, value) body = "Traceback (innermost last):\n" + "%-20s %s" % \ (unicode((b"").join(trace_list[:-1]), 'utf-8'), unicode(trace_list[-1], 'utf-8')) - + if frappe.logger: frappe.logger.error('Db:'+(frappe.db and frappe.db.cur_db_name or '') \ + ' - ' + body) - + return body def log(event, details): @@ -99,16 +101,14 @@ def getdate(string_date): """ Coverts string date (yyyy-mm-dd) to datetime.date object """ - import datetime - if isinstance(string_date, datetime.date): return string_date elif isinstance(string_date, datetime.datetime): return datetime.date() - + if " " in string_date: string_date = string_date.split(" ")[0] - + return datetime.datetime.strptime(string_date, "%Y-%m-%d").date() def add_to_date(date, years=0, months=0, days=0): @@ -121,7 +121,7 @@ def add_to_date(date, years=0, months=0, days=0): from dateutil.relativedelta import relativedelta date += relativedelta(years=years, months=months, days=days) - + if format: return date.strftime("%Y-%m-%d") else: @@ -141,7 +141,7 @@ def date_diff(string_ed_date, string_st_date): def time_diff(string_ed_date, string_st_date): return get_datetime(string_ed_date) - get_datetime(string_st_date) - + def time_diff_in_seconds(string_ed_date, string_st_date): return time_diff(string_ed_date, string_st_date).seconds @@ -149,16 +149,14 @@ def time_diff_in_hours(string_ed_date, string_st_date): return round(float(time_diff(string_ed_date, string_st_date).seconds) / 3600, 6) def now_datetime(): - from datetime import datetime - return convert_utc_to_user_timezone(datetime.utcnow()) + return convert_utc_to_user_timezone(datetime.datetime.utcnow()) def get_user_time_zone(): if getattr(frappe.local, "user_time_zone", None) is None: frappe.local.user_time_zone = frappe.cache().get_value("time_zone") - + if not frappe.local.user_time_zone: - frappe.local.user_time_zone = frappe.db.get_value('Control Panel', None, 'time_zone') \ - or 'Asia/Calcutta' + frappe.local.user_time_zone = frappe.db.get_default('time_zone') or 'Asia/Calcutta' frappe.cache().set_value("time_zone", frappe.local.user_time_zone) return frappe.local.user_time_zone @@ -178,27 +176,26 @@ def now(): now_datetime().strftime('%H:%M:%S.%f') else: return now_datetime().strftime('%Y-%m-%d %H:%M:%S.%f') - + def nowdate(): """return current date as yyyy-mm-dd""" return now_datetime().strftime('%Y-%m-%d') def today(): return nowdate() - + def nowtime(): """return current time in hh:mm""" - return now_datetime().strftime('%H:%M') + return now_datetime().strftime('%H:%M:%S.%f') def get_first_day(dt, d_years=0, d_months=0): """ Returns the first day of the month for the date specified by date object Also adds `d_years` and `d_months` if specified """ - import datetime dt = getdate(dt) - # d_years, d_months are "deltas" to apply to dt + # d_years, d_months are "deltas" to apply to dt overflow_years, month = divmod(dt.month + d_months - 1, 12) year = dt.year + d_years + overflow_years @@ -209,26 +206,34 @@ def get_last_day(dt): Returns last day of the month using: `get_first_day(dt, 0, 1) + datetime.timedelta(-1)` """ - import datetime return get_first_day(dt, 0, 1) + datetime.timedelta(-1) def get_datetime(datetime_str): - from datetime import datetime - if isinstance(datetime_str, datetime): - return datetime_str.replace(microsecond=0, tzinfo=None) - - return datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S.%f') - + try: + return datetime.datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S.%f') + + except TypeError: + if isinstance(datetime_str, datetime.datetime): + return datetime_str.replace(tzinfo=None) + else: + raise + + except ValueError: + if datetime_str=='0000-00-00 00:00:00.000000': + return None + + return datetime.datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S') + def get_datetime_str(datetime_obj): if isinstance(datetime_obj, basestring): datetime_obj = get_datetime(datetime_obj) - + return datetime_obj.strftime('%Y-%m-%d %H:%M:%S.%f') def formatdate(string_date=None): """ Convers the given string date to :data:`user_format` - User format specified in :term:`Control Panel` + User format specified in defaults Examples: @@ -240,21 +245,21 @@ def formatdate(string_date=None): string_date = getdate(string_date) else: string_date = now_datetime().date() - + if getattr(frappe.local, "user_format", None) is None: frappe.local.user_format = frappe.db.get_default("date_format") - + out = frappe.local.user_format - + return out.replace("dd", string_date.strftime("%d"))\ .replace("mm", string_date.strftime("%m"))\ .replace("yyyy", string_date.strftime("%Y")) - + def global_date_format(date): """returns date as 1 January 2012""" formatted_date = getdate(date).strftime("%d %B %Y") return formatted_date.startswith("0") and formatted_date[1:] or formatted_date - + def dict_to_str(args, sep='&'): """ Converts a dictionary to URL @@ -264,24 +269,10 @@ def dict_to_str(args, sep='&'): t.append(str(k)+'='+urllib.quote(str(args[k] or ''))) return sep.join(t) -def timestamps_equal(t1, t2): - """Returns true if same the two string timestamps are same""" - scrub = lambda x: x.replace(':', ' ').replace('-',' ').split() - - t1, t2 = scrub(t1), scrub(t2) - - if len(t1) != len(t2): - return - - for i in range(len(t1)): - if t1[i]!=t2[i]: - return - return 1 - def has_common(l1, l2): """Returns truthy value if there are common elements in lists l1 and l2""" return set(l1) & set(l2) - + def flt(s, precision=None): """Convert to float (ignore commas)""" if isinstance(s, basestring): @@ -303,13 +294,13 @@ def cint(s): def cstr(s): if isinstance(s, unicode): return s - elif s==None: + elif s==None: return '' elif isinstance(s, basestring): return unicode(s, 'utf-8') else: return unicode(s) - + def _round(num, precision=0): """round method for round halfs to nearest even algorithm""" precision = cint(precision) @@ -317,16 +308,15 @@ def _round(num, precision=0): # avoid rounding errors num = round(num * multiplier if precision else num, 8) - - import math + floor = math.floor(num) decimal_part = num - floor - + if decimal_part == 0.5: num = floor if (floor % 2 == 0) else floor + 1 else: num = round(num) - + return (num / multiplier) if precision else num def encode(obj, encoding="utf-8"): @@ -345,8 +335,6 @@ def encode(obj, encoding="utf-8"): def parse_val(v): """Converts to simple datatypes from SQL query results""" - import datetime - if isinstance(v, (datetime.date, datetime.datetime)): v = unicode(v) elif isinstance(v, datetime.timedelta): @@ -358,20 +346,20 @@ def parse_val(v): def fmt_money(amount, precision=None, currency=None): """ Convert to string with commas for thousands, millions etc - """ + """ number_format = frappe.db.get_default("number_format") or "#,###.##" decimal_str, comma_str, precision = get_number_format_info(number_format) - - + + amount = '%.*f' % (precision, flt(amount)) if amount.find('.') == -1: decimals = '' - else: + else: decimals = amount.split('.')[1] parts = [] minus = '' - if flt(amount) < 0: + if flt(amount) < 0: minus = '-' amount = cstr(abs(flt(amount))).split('.')[0] @@ -392,7 +380,7 @@ def fmt_money(amount, precision=None, currency=None): amount = comma_str.join(parts) + (precision and (decimal_str + decimals) or "") amount = minus + amount - + if currency: symbol = frappe.db.get_value("Currency", currency, "symbol") if symbol: @@ -411,16 +399,16 @@ number_format_info = { } def get_number_format_info(format): - return number_format_info.get(format) or (".", ",", 2) + return number_format_info.get(format) or (".", ",", 2) # # convet currency to words # def money_in_words(number, main_currency = None, fraction_currency=None): """ - Returns string in words with currency and fraction currency. + Returns string in words with currency and fraction currency. """ - + d = get_defaults() if not main_currency: main_currency = d.get('currency', 'INR') @@ -430,14 +418,14 @@ def money_in_words(number, main_currency = None, fraction_currency=None): n = "%.2f" % flt(number) main, fraction = n.split('.') if len(fraction)==1: fraction += '0' - - + + number_format = frappe.db.get_value("Currency", main_currency, "number_format") or \ frappe.db.get_default("number_format") or "#,###.##" - + in_million = True if number_format == "#,##,###.##": in_million = False - + out = main_currency + ' ' + in_words(main, in_million).title() if cint(fraction): out = out + ' and ' + in_words(fraction, in_million).title() + ' ' + fraction_currency @@ -455,15 +443,15 @@ def in_words(integer, in_million=True): known = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine', 10: 'ten', 11: 'eleven', 12: 'twelve', 13: 'thirteen', 14: 'fourteen', 15: 'fifteen', 16: 'sixteen', 17: 'seventeen', 18: 'eighteen', 19: 'nineteen', 20: 'twenty', 30: 'thirty', 40: 'forty', 50: 'fifty', 60: 'sixty', 70: 'seventy', 80: 'eighty', 90: 'ninety'} - + def psn(n, known, xpsn): - import sys; + import sys; if n in known: return known[n] bestguess, remainder = str(n), 0 if n<=20: frappe.errprint(sys.stderr) - frappe.errprint(n) + frappe.errprint(n) frappe.errprint("How did this happen?") assert 0 elif n < 100: @@ -482,7 +470,7 @@ def in_words(integer, in_million=True): remainder = n%1000000 else: bestguess= xpsn(n//1000000000, known, xpsn) + ' ' + 'billion' - remainder = n%1000000000 + remainder = n%1000000000 else: if n < 100000: bestguess= xpsn(n//1000, known, xpsn) + ' ' + 'thousand' @@ -503,19 +491,19 @@ def in_words(integer, in_million=True): return bestguess return psn(n, known, psn) - + # Get Defaults # ============================================================================== def get_defaults(key=None): """ - Get dictionary of default values from the :term:`Control Panel`, or a value if key is passed + Get dictionary of default values from the defaults, or a value if key is passed """ return frappe.db.get_defaults(key) def set_default(key, val): """ - Set / add a default value to :term:`Control Panel` + Set / add a default value to defaults` """ return frappe.db.set_default(key, val) @@ -530,46 +518,46 @@ def remove_blanks(d): empty_keys.append(key) for key in empty_keys: del d[key] - + return d - + def pprint_dict(d, level=1, no_blanks=True): """ Pretty print a dictionary with indents """ if no_blanks: remove_blanks(d) - + # make indent indent, ret = '', '' for i in range(0,level): indent += '\t' - + # add lines comment, lines = '', [] kl = d.keys() kl.sort() - + # make lines for key in kl: if key != '##comment': tmp = {key: d[key]} lines.append(indent + str(tmp)[1:-1] ) - + # add comment string if '##comment' in kl: ret = ('\n' + indent) + '# ' + d['##comment'] + '\n' # open ret += indent + '{\n' - + # lines ret += indent + ',\n\t'.join(lines) - + # close ret += '\n' + indent + '}' - + return ret - + def get_common(d1,d2): """ returns (list of keys) the common part of two dicts @@ -591,7 +579,7 @@ def get_diff_dict(d1, d2): return common dictionary of d1 and d2 """ diff_keys = set(d2.keys()).difference(set(d1.keys())) - + ret = {} for d in diff_keys: ret[d] = d2[d] return ret @@ -602,7 +590,7 @@ def get_file_timestamp(fn): Returns timestamp of the given file """ from frappe.utils import cint - + try: return str(cint(os.stat(fn).st_mtime)) except OSError, e: @@ -638,7 +626,7 @@ def unesc(s, esc_chars): esc_str = '\\' + c s = s.replace(esc_str, c) return s - + def is_html(text): out = False for key in ["
    ", " """ return re.compile(r'<.*?>').sub('', text) - + def escape_html(text): html_escape_table = { "&": "&", @@ -700,21 +688,20 @@ def pretty_date(iso_datetime): Ported from PrettyDate by John Resig """ if not iso_datetime: return '' - from datetime import datetime import math - + if isinstance(iso_datetime, basestring): - iso_datetime = datetime.strptime(iso_datetime, '%Y-%m-%d %H:%M:%S.%f') - now_dt = datetime.strptime(now(), '%Y-%m-%d %H:%M:%S.%f') + iso_datetime = datetime.datetime.strptime(iso_datetime, '%Y-%m-%d %H:%M:%S.%f') + now_dt = datetime.datetime.strptime(now(), '%Y-%m-%d %H:%M:%S.%f') dt_diff = now_dt - iso_datetime - + # available only in python 2.7+ # dt_diff_seconds = dt_diff.total_seconds() - + dt_diff_seconds = dt_diff.days * 86400.0 + dt_diff.seconds - + dt_diff_days = math.floor(dt_diff_seconds / 86400.0) - + # differnt cases if dt_diff_seconds < 60.0: return 'just now' @@ -736,20 +723,20 @@ def pretty_date(iso_datetime): return '%s months ago' % cint(math.ceil(dt_diff_days / 30.0)) else: return 'more than %s year(s) ago' % cint(math.floor(dt_diff_days / 365.0)) - + def execute_in_shell(cmd, verbose=0): # using Popen instead of os.system - as recommended by python docs from subprocess import Popen import tempfile - + with tempfile.TemporaryFile() as stdout: with tempfile.TemporaryFile() as stderr: p = Popen(cmd, shell=True, stdout=stdout, stderr=stderr) p.wait() - + stdout.seek(0) out = stdout.read() - + stderr.seek(0) err = stderr.read() @@ -761,10 +748,10 @@ def execute_in_shell(cmd, verbose=0): def comma_or(some_list): return comma_sep(some_list, " or ") - + def comma_and(some_list): return comma_sep(some_list, " and ") - + def comma_sep(some_list, sep): if isinstance(some_list, (list, tuple)): # list(some_list) is done to preserve the existing list @@ -778,36 +765,36 @@ def comma_sep(some_list, sep): return ", ".join(some_list[:-1]) + sep + some_list[-1] else: return some_list - + def filter_strip_join(some_list, sep): """given a list, filter None values, strip spaces and join""" return (cstr(sep)).join((cstr(a).strip() for a in filter(None, some_list))) - + def get_path(*path, **kwargs): base = kwargs.get('base') if not base: base = frappe.local.site_path return os.path.join(base, *path) - + def get_site_base_path(sites_dir=None, hostname=None): return frappe.local.site_path def get_site_path(*path): return get_path(base=get_site_base_path(), *path) - -def get_files_path(): - return get_site_path("public", "files") + +def get_files_path(*path): + return get_site_path("public", "files", *path) def get_backups_path(): return get_site_path("private", "backups") - + def get_request_site_address(full_address=False): return get_url(full_address=full_address) - + def get_url(uri=None, full_address=False): """get app url from request""" host_name = frappe.local.conf.host_name - + if not host_name: if hasattr(frappe.local, "request") and frappe.local.request and frappe.local.request.host: protocol = 'https' == frappe.get_request_header('X-Forwarded-Proto', "") and 'https://' or 'http://' @@ -819,30 +806,30 @@ def get_url(uri=None, full_address=False): "subdomain") if host_name and "http" not in host_name: host_name = "http://" + host_name - + if not host_name: host_name = "http://localhost" - + if not uri and full_address: uri = frappe.get_request_header("REQUEST_URI", "") - + url = urllib.basejoin(host_name, uri) if uri else host_name - + return url def get_url_to_form(doctype, name, base_url=None, label=None): if not base_url: base_url = get_url() - + if not label: label = name - + return """%(label)s""" % locals() - + def encode_dict(d, encoding="utf-8"): for key in d: if isinstance(d[key], basestring) and isinstance(d[key], unicode): d[key] = d[key].encode(encoding) - + return d def decode_dict(d, encoding="utf-8"): @@ -877,7 +864,7 @@ def compare(val1, condition, val2): ret = False if condition in operator_map: ret = operator_map[condition]((val1, val2)) - + return ret def get_site_name(hostname): @@ -890,26 +877,26 @@ def get_disk_usage(): return 0 err, out = execute_in_shell("du -hsm {files_path}".format(files_path=files_path)) return cint(out.split("\n")[-2].split("\t")[0]) - + def scrub_urls(html): html = expand_relative_urls(html) html = quote_urls(html) return html - + def expand_relative_urls(html): # expand relative urls url = get_url() if url.endswith("/"): url = url[:-1] - + def _expand_relative_urls(match): to_expand = list(match.groups()) if not to_expand[2].startswith("/"): to_expand[2] = "/" + to_expand[2] to_expand.insert(2, url) return "".join(to_expand) - + return re.sub('(href|src){1}([\s]*=[\s]*[\'"]?)((?!http)[^\'" >]+)([\'"]?)', _expand_relative_urls, html) - + def quote_urls(html): def _quote_url(match): groups = list(match.groups()) @@ -926,3 +913,11 @@ def touch_file(path): def get_test_client(): from frappe.app import application return Client(application) + +def get_hook_method(hook_name, fallback=None): + method = (frappe.get_hooks().get(hook_name)) + if method: + method = frappe.get_attr(method[0]) + return method + if fallback: + return fallback diff --git a/frappe/utils/datautils.py b/frappe/utils/datautils.py index 028391acdd..2bca461f6f 100644 --- a/frappe/utils/datautils.py +++ b/frappe/utils/datautils.py @@ -35,7 +35,7 @@ def read_csv_content_from_attached_file(doc): def read_csv_content(fcontent, ignore_encoding=False): rows = [] - if isinstance(fcontent, basestring): + if not isinstance(fcontent, unicode): decoded = False for encoding in ["utf-8", "windows-1250", "windows-1252"]: try: @@ -49,7 +49,7 @@ def read_csv_content(fcontent, ignore_encoding=False): frappe.msgprint(frappe._("Unknown file encoding. Tried utf-8, windows-1250, windows-1252."), raise_exception=True) - fcontent = fcontent.encode("utf-8").splitlines(True) + fcontent = fcontent.encode("utf-8").splitlines(True) try: reader = csv.reader(fcontent) @@ -92,17 +92,13 @@ class UnicodeWriter: def getvalue(self): return self.queue.getvalue() -def check_record(d, parenttype=None, doctype_dl=None): +def check_record(d): """check for mandatory, select options, dates. these should ideally be in doclist""" from frappe.utils.dateutils import parse_date - if parenttype and not d.get('parent'): - frappe.msgprint(_("Parent is required."), raise_exception=1) - - if not doctype_dl: - doctype_dl = frappe.model.doctype.get(d.doctype) + d = frappe.get_doc(d) for key in d: - docfield = doctype_dl.get_field(key) + docfield = d.meta.get_field(key) val = d[key] if docfield: if docfield.reqd and (val=='' or val==None): @@ -132,27 +128,27 @@ def import_doc(d, doctype, overwrite, row_idx, submit=False, ignore_links=False) """import main (non child) document""" if d.get("name") and frappe.db.exists(doctype, d['name']): if overwrite: - bean = frappe.bean(doctype, d['name']) - bean.ignore_links = ignore_links - bean.doc.fields.update(d) + doc = frappe.get_doc(doctype, d['name']) + doc.ignore_links = ignore_links + doc.update(d) if d.get("docstatus") == 1: - bean.update_after_submit() + doc.update_after_submit() else: - bean.save() + doc.save() return 'Updated row (#%d) %s' % (row_idx + 1, getlink(doctype, d['name'])) else: return 'Ignored row (#%d) %s (exists)' % (row_idx + 1, getlink(doctype, d['name'])) else: - bean = frappe.bean([d]) - bean.ignore_links = ignore_links - bean.insert() + doc = frappe.get_doc(d) + doc.ignore_links = ignore_links + doc.insert() if submit: - bean.submit() + doc.submit() return 'Inserted row (#%d) %s' % (row_idx + 1, getlink(doctype, - bean.doc.fields['name'])) + doc.get('name'))) def getlink(doctype, name): return '%(name)s' % locals() diff --git a/frappe/utils/email_lib/bulk.py b/frappe/utils/email_lib/bulk.py index ffdd6e2094..d47c4a6d42 100644 --- a/frappe/utils/email_lib/bulk.py +++ b/frappe/utils/email_lib/bulk.py @@ -78,7 +78,7 @@ def send(recipients=None, sender=None, doctype='User', email_field='email', def add(email, sender, subject, formatted, text_content=None, ref_doctype=None, ref_docname=None): """add to bulk mail queue""" - e = frappe.doc('Bulk Email') + e = frappe.new_doc('Bulk Email') e.sender = sender e.recipient = email try: diff --git a/frappe/utils/email_lib/email_body.py b/frappe/utils/email_lib/email_body.py index 4c732ad461..527f701a03 100644 --- a/frappe/utils/email_lib/email_body.py +++ b/frappe/utils/email_lib/email_body.py @@ -211,7 +211,7 @@ def get_footer(footer=None): footer = footer or "" # control panel - footer += frappe.db.get_value('Control Panel', None, 'mail_footer') or '' + footer += frappe.db.get_default('mail_footer') or '' # hooks for f in frappe.get_hooks("mail_footer"): diff --git a/frappe/utils/email_lib/smtp.py b/frappe/utils/email_lib/smtp.py index 9ba7d9e94a..8547e29898 100644 --- a/frappe/utils/email_lib/smtp.py +++ b/frappe/utils/email_lib/smtp.py @@ -36,9 +36,9 @@ def send(email, as_bulk=False): class SMTPServer: def __init__(self, login=None, password=None, server=None, port=None, use_ssl=None): - # get defaults from control panel + # get defaults from mail settings try: - es = frappe.doc('Outgoing Email Settings', 'Outgoing Email Settings') + es = frappe.get_doc('Outgoing Email Settings', 'Outgoing Email Settings') except frappe.DoesNotExistError: es = None diff --git a/frappe/utils/file_manager.py b/frappe/utils/file_manager.py index f61d901d26..06dea9ee33 100644 --- a/frappe/utils/file_manager.py +++ b/frappe/utils/file_manager.py @@ -4,9 +4,12 @@ from __future__ import unicode_literals import frappe import os, base64, re -from frappe.utils import cstr, cint, get_site_path +import hashlib +import mimetypes +from frappe.utils import cstr, cint, get_site_path, get_hook_method, get_files_path from frappe import _ from frappe import conf +from copy import copy class MaxFileSizeReachedError(frappe.ValidationError): pass @@ -31,7 +34,11 @@ def upload(): elif file_url: filedata = save_url(file_url, dt, dn) - return {"fid": filedata.name, "filename": filedata.file_name or filedata.file_url } + return { + "name": filedata.name, + "file_name": filedata.file_name, + "file_url": filedata.file_url + } def save_uploaded(dt, dn): fname, content = get_uploaded_content() @@ -45,7 +52,7 @@ def save_url(file_url, dt, dn): # frappe.msgprint("URL must start with 'http://' or 'https://'") # return None, None - f = frappe.bean({ + f = frappe.get_doc({ "doctype": "File Data", "file_url": file_url, "attached_to_doctype": dt, @@ -55,8 +62,8 @@ def save_url(file_url, dt, dn): try: f.insert(); except frappe.DuplicateEntryError: - return frappe.doc("File Data", f.doc.duplicate_entry) - return f.doc + return frappe.get_doc("File Data", f.duplicate_entry) + return f def get_uploaded_content(): # should not be unicode when reading a file, hence using frappe.form @@ -76,115 +83,66 @@ def extract_images_from_html(doc, fieldname): data = match.group(1) headers, content = data.split(",") filename = headers.split("filename=")[-1] - filename = save_file(filename, content, doc.doctype, doc.name, decode=True).get("file_name") + # TODO fix this + file_url = save_file(filename, content, doc.doctype, doc.name, decode=True).get("file_url") if not frappe.flags.has_dataurl: frappe.flags.has_dataurl = True - return ' 100: - frappe.msgprint("Too many versions", raise_exception=True) - - return new_fname - -def scrub_file_name(fname): - if '\\' in fname: - fname = fname.split('\\')[-1] - if '/' in fname: - fname = fname.split('/')[-1] - return fname +def save_file_on_filesystem(fname, content, content_type=None): + import filecmp + public_path = os.path.join(frappe.local.site_path, "public") + fpath = write_file(content, get_files_path(), fname) + path = os.path.relpath(fpath, public_path) + return { + 'file_name': os.path.basename(path), + 'file_url': '/' + path + } def check_max_file_size(content): max_file_size = conf.get('max_file_size') or 1000000 @@ -196,17 +154,14 @@ def check_max_file_size(content): return file_size -def write_file(content, files_path): +def write_file(content, file_path, fname): """write file to disk with a random name (to compare)""" - # create account folder (if not exists) - frappe.create_folder(files_path) - fname = os.path.join(files_path, frappe.generate_hash()) - + # create directory (if not exists) + frappe.create_folder(get_files_path()) # write the file - with open(fname, 'w+') as f: + with open(os.path.join(file_path, fname), 'w+') as f: f.write(content) - - return fname + return get_files_path(fname) def remove_all(dt, dn): """remove all files in a transaction""" @@ -220,6 +175,19 @@ def remove_all(dt, dn): def remove_file(fid): """Remove file and File Data entry""" frappe.delete_doc("File Data", fid) + +def delete_file_data_content(doc): + method = get_hook_method('delete_file_data_content', fallback=delete_file_from_filesystem) + method(doc) + +def delete_file_from_filesystem(doc): + path = doc.file_name + if path.startswith("files/"): + path = frappe.utils.get_site_path("public", doc.file_name) + else: + path = frappe.utils.get_site_path("public", "files", doc.file_name) + if os.path.exists(path): + os.remove(path) def get_file(fname): f = frappe.db.sql("""select file_name from `tabFile Data` @@ -237,3 +205,14 @@ def get_file(fname): content = f.read() return [file_name, content] + +def get_content_hash(content): + return hashlib.md5(content).hexdigest() + +def get_file_name(fname, optional_suffix): + n_records = frappe.db.sql("select name from `tabFile Data` where file_name='{}'".format(fname)) + if len(n_records) > 0: + partial, extn = fname.rsplit('.', 1) + return '{partial}{suffix}.{extn}'.format(partial=partial, extn=extn, suffix=optional_suffix) + return fname + diff --git a/frappe/utils/fixtures.py b/frappe/utils/fixtures.py index 816e3eccdb..30284c8e1f 100644 --- a/frappe/utils/fixtures.py +++ b/frappe/utils/fixtures.py @@ -1,17 +1,17 @@ # 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, os -from frappe.core.page.data_import_tool.data_import_tool import import_doclist, export_fixture, export_csv +from frappe.core.page.data_import_tool.data_import_tool import import_doc, export_fixture, export_csv def sync_fixtures(): for app in frappe.get_installed_apps(): if os.path.exists(frappe.get_app_path(app, "fixtures")): for fname in os.listdir(frappe.get_app_path(app, "fixtures")): if fname.endswith(".json") or fname.endswith(".csv"): - import_doclist(frappe.get_app_path(app, "fixtures", fname), ignore_links=True, overwrite=True) + import_doc(frappe.get_app_path(app, "fixtures", fname), ignore_links=True, overwrite=True) def export_fixtures(): diff --git a/frappe/utils/install.py b/frappe/utils/install.py index 96c70bbffc..8d7ed41f26 100644 --- a/frappe/utils/install.py +++ b/frappe/utils/install.py @@ -29,12 +29,12 @@ def after_install(): for d in install_docs: try: - frappe.bean(d).insert() + frappe.get_doc(d).insert() except NameError: pass # all roles to admin - frappe.bean("User", "Administrator").get_controller().add_roles(*frappe.db.sql_list(""" + frappe.get_doc("User", "Administrator").add_roles(*frappe.db.sql_list(""" select name from tabRole""")) # update admin password diff --git a/frappe/utils/nestedset.py b/frappe/utils/nestedset.py index 693f2da911..5d4c30521f 100644 --- a/frappe/utils/nestedset.py +++ b/frappe/utils/nestedset.py @@ -14,6 +14,7 @@ from __future__ import unicode_literals import frappe from frappe import msgprint, _ +from frappe.model.document import Document class NestedSetRecursionError(frappe.ValidationError): pass class NestedSetMultipleRootsError(frappe.ValidationError): pass @@ -21,38 +22,29 @@ class NestedSetChildExistsError(frappe.ValidationError): pass class NestedSetInvalidMergeError(frappe.ValidationError): pass # called in the on_update method -def update_nsm(doc_obj): +def update_nsm(doc): # get fields, data from the DocType opf = 'old_parent' + pf = "parent_" + frappe.scrub(doc.doctype) - if str(doc_obj.__class__)=='frappe.model.doc.Document': - # passed as a Document object - d = doc_obj - pf = "parent_" + frappe.scrub(d.doctype) - else: - # passed as a DocType object - d = doc_obj.doc - pf = "parent_" + frappe.scrub(d.doctype) - - if hasattr(doc_obj,'nsm_parent_field'): - pf = doc_obj.nsm_parent_field - if hasattr(doc_obj,'nsm_oldparent_field'): - opf = doc_obj.nsm_oldparent_field + if hasattr(doc,'nsm_parent_field'): + pf = doc.nsm_parent_field + if hasattr(doc,'nsm_oldparent_field'): + opf = doc.nsm_oldparent_field - p, op = d.fields.get(pf) or None, d.fields.get(opf) or None + p, op = doc.get(pf) or None, doc.get(opf) or None # has parent changed (?) or parent is None (root) - if not d.lft and not d.rgt: - update_add_node(d, p or '', pf) + if not doc.lft and not doc.rgt: + update_add_node(doc, p or '', pf) elif op != p: - update_move_node(d, pf) + update_move_node(doc, pf) # set old parent - d.fields[opf] = p - frappe.db.set_value(d.doctype, d.name, opf, p or '') - - # reload - d._loadfromdb() + doc.set(opf, p) + frappe.db.set_value(doc.doctype, doc.name, opf, p or '') + + doc.load_from_db() def update_add_node(doc, parent, parent_field): """ @@ -91,7 +83,7 @@ def update_add_node(doc, parent, parent_field): def update_move_node(doc, parent_field): - parent = doc.fields.get(parent_field) + parent = doc.get(parent_field) if parent: new_parent = frappe.db.sql("""select lft, rgt from `tab%s` @@ -188,54 +180,54 @@ def validate_loop(doctype, name, lft, rgt): "%s", "%s"), (lft, rgt)): frappe.throw("""Item cannot be added to its own descendents.""", NestedSetRecursionError) -class DocTypeNestedSet(object): +class NestedSet(Document): def on_update(self): update_nsm(self) self.validate_ledger() def on_trash(self): if not self.nsm_parent_field: - self.nsm_parent_field = frappe.scrub(self.doc.doctype) + "_parent" + self.nsm_parent_field = frappe.scrub(self.doctype) + "_parent" - parent = self.doc.fields[self.nsm_parent_field] + parent = self.get(self.nsm_parent_field) if not parent: - msgprint(_("Root ") + self.doc.doctype + _(" cannot be deleted."), raise_exception=1) + msgprint(_("Root ") + self.doctype + _(" cannot be deleted."), raise_exception=1) # cannot delete non-empty group has_children = frappe.db.sql("""select count(name) from `tab{doctype}` - where `{nsm_parent_field}`=%s""".format(doctype=self.doc.doctype, nsm_parent_field=self.nsm_parent_field), - (self.doc.name,))[0][0] + where `{nsm_parent_field}`=%s""".format(doctype=self.doctype, nsm_parent_field=self.nsm_parent_field), + (self.name,))[0][0] if has_children: frappe.throw("{cannot_delete}. {children_exist}: {name}.".format( - children_exist=_("Children exist for"), name=self.doc.name, + children_exist=_("Children exist for"), name=self.name, cannot_delete=_("Cannot delete")), NestedSetChildExistsError) - self.doc.fields[self.nsm_parent_field] = "" + self.set(self.nsm_parent_field, "") update_nsm(self) def before_rename(self, olddn, newdn, merge=False, group_fname="is_group"): if merge: - is_group = frappe.db.get_value(self.doc.doctype, newdn, group_fname) - if self.doc.fields[group_fname] != is_group: + is_group = frappe.db.get_value(self.doctype, newdn, group_fname) + if self.get(group_fname) != is_group: frappe.throw(_("""Merging is only possible between Group-to-Group or Ledger-to-Ledger"""), NestedSetInvalidMergeError) def after_rename(self, olddn, newdn, merge=False): if merge: - parent_field = "parent_" + self.doc.doctype.replace(" ", "_").lower() - rebuild_tree(self.doc.doctype, parent_field) + parent_field = "parent_" + self.doctype.replace(" ", "_").lower() + rebuild_tree(self.doctype, parent_field) def validate_one_root(self): - if not self.doc.fields[self.nsm_parent_field]: + if not self.get(self.nsm_parent_field): if frappe.db.sql("""select count(*) from `tab%s` where - ifnull(%s, '')=''""" % (self.doc.doctype, self.nsm_parent_field))[0][0] > 1: + ifnull(%s, '')=''""" % (self.doctype, self.nsm_parent_field))[0][0] > 1: frappe.throw(_("""Multiple root nodes not allowed."""), NestedSetMultipleRootsError) def validate_ledger(self, group_identifier="is_group"): - if self.doc.fields.get(group_identifier) == "No": + if self.get(group_identifier) == "No": if frappe.db.sql("""select name from `tab%s` where %s=%s and docstatus!=2""" % - (self.doc.doctype, self.nsm_parent_field, '%s'), (self.doc.name)): - frappe.throw(self.doc.doctype + ": " + self.doc.name + + (self.doctype, self.nsm_parent_field, '%s'), (self.name)): + frappe.throw(self.doctype + ": " + self.name + _(" can not be marked as a ledger as it has existing child")) def get_root_of(doctype): diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 89ac744aa5..ea13fe8bf2 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -9,7 +9,7 @@ import mimetypes import os import frappe from frappe import _ -from frappe.model.doc import Document +import frappe.model.document import frappe.utils import frappe.sessions import frappe.model.utils @@ -28,6 +28,9 @@ def report_error(status_code): return response def build_response(response_type=None): + if "docs" in frappe.local.response and not frappe.local.response.docs: + del frappe.local.response["docs"] + response_type_map = { 'csv': as_csv, 'download': as_raw, @@ -54,7 +57,6 @@ def as_raw(): def as_json(): make_logs() - cleanup_docs() response = Response() response.headers["Content-Type"] = "application/json; charset: utf-8" response = gzip(json.dumps(frappe.local.response, default=json_handler, separators=(',',':')), @@ -68,14 +70,11 @@ def make_logs(): frappe.response['exc'] = json.dumps([frappe.utils.cstr(d) for d in frappe.local.error_log]) if frappe.local.message_log: - frappe.response['_server_messages'] = json.dumps([frappe.utils.cstr(d) for d in frappe.local.message_log]) + frappe.response['_server_messages'] = json.dumps([frappe.utils.cstr(d) for + d in frappe.local.message_log]) if frappe.debug_log and frappe.conf.get("logging") or False: frappe.response['_debug_messages'] = json.dumps(frappe.local.debug_log) - -def cleanup_docs(): - if frappe.response.get('docs') and type(frappe.response['docs'])!=dict: - frappe.response['docs'] = frappe.model.utils.compress(frappe.response['docs']) def gzip(data, response): data = data.encode('utf-8') @@ -101,14 +100,13 @@ def compressBuf(buf): def json_handler(obj): """serialize non-serializable data for json""" - # serialize date if isinstance(obj, (datetime.date, datetime.timedelta, datetime.datetime)): return unicode(obj) elif isinstance(obj, LocalProxy): return unicode(obj) - elif isinstance(obj, Document): - return obj.fields + elif isinstance(obj, frappe.model.document.Document): + return obj.as_dict() else: raise TypeError, """Object of type %s with value of %s is not JSON serializable""" % \ (type(obj), repr(obj)) diff --git a/frappe/utils/scheduler.py b/frappe/utils/scheduler.py index f4d743aaea..c0153956e8 100644 --- a/frappe/utils/scheduler.py +++ b/frappe/utils/scheduler.py @@ -97,7 +97,7 @@ def log(method, message=None): frappe.db.rollback() frappe.db.begin() - d = frappe.doc("Scheduler Log") + d = frappe.get_doc("Scheduler Log") d.method = method d.error = message d.save() diff --git a/frappe/utils/user.py b/frappe/utils/user.py index 1c1d340146..59ce22ea0b 100644 --- a/frappe/utils/user.py +++ b/frappe/utils/user.py @@ -195,12 +195,12 @@ def get_system_managers(only_name=False): return [email.utils.formataddr((p.fullname, p.name)) for p in system_managers] def add_role(user, role): - user_wrapper = frappe.bean("User", user).get_controller().add_roles(role) + user_wrapper = frappe.get_doc("User", user).add_roles(role) def add_system_manager(email, first_name=None, last_name=None): # add user - user = frappe.new_bean("User") - user.doc.fields.update({ + user = frappe.new_doc("User") + user.update({ "name": email, "email": email, "enabled": 1, @@ -213,7 +213,7 @@ def add_system_manager(email, first_name=None, last_name=None): # add roles roles = frappe.db.sql_list("""select name from `tabRole` where name not in ("Administrator", "Guest", "All")""") - user.get_controller().add_roles(*roles) + user.add_roles(*roles) def get_roles(username=None, with_standard=True): """get roles of current user""" diff --git a/frappe/utils/verified_command.py b/frappe/utils/verified_command.py index 8d4c30a3f4..b3bf4316cc 100644 --- a/frappe/utils/verified_command.py +++ b/frappe/utils/verified_command.py @@ -23,12 +23,10 @@ def get_signature(params, nonce, secret=None): signature.update(params) return signature.hexdigest() -def verify_using_bean(bean, signature, cmd): - controller = bean.get_controller() - params = controller.get_signature_params() - return signature == get_signature(params, controller.get_nonce()) +def verify_using_doc(doc, signature, cmd): + params = doc.get_signature_params() + return signature == get_signature(params, doc.get_nonce()) -def get_url_using_bean(bean, cmd): - controller = bean.get_controller() - params = controller.get_signature_params() - return get_url(cmd, params, controller.get_nonce()) +def get_url_using_doc(doc, cmd): + params = doc.get_signature_params() + return get_url(cmd, params, doc.get_nonce()) diff --git a/frappe/website/context.py b/frappe/website/context.py index cf573202fd..15002468af 100644 --- a/frappe/website/context.py +++ b/frappe/website/context.py @@ -28,41 +28,37 @@ def get_context(path): context = build_context(context) - if can_cache(context.no_cache): + if can_cache(context.no_cache): frappe.cache().set_value(cache_key, context) else: context["access"] = get_access(context.pathname) - + if not context.data: context.data = {} context.data["path"] = path context.update(context.data or {}) - + # TODO private pages - + return context - + def build_context(sitemap_options): - """get_context method of bean or module is supposed to render content templates and push it into context""" + """get_context method of doc or module is supposed to render content templates and push it into context""" context = frappe._dict(sitemap_options) context.update(get_website_settings()) - - # provide bean + + # provide doc if context.doctype and context.docname: - context.bean = frappe.bean(context.doctype, context.docname) - + context.doc = frappe.get_doc(context.doctype, context.docname) + if context.controller: module = frappe.get_module(context.controller) if module and hasattr(module, "get_context"): context.update(module.get_context(context) or {}) - + if context.get("base_template_path") != context.get("template_path") and not context.get("rendered"): context.data = render_blocks(context) - - # remove bean, as it is not pickle friendly and its purpose is over - if context.bean: - del context["bean"] - + return context diff --git a/frappe/website/doctype/about_us_settings/about_us_settings.json b/frappe/website/doctype/about_us_settings/about_us_settings.json new file mode 100644 index 0000000000..674fe27d52 --- /dev/null +++ b/frappe/website/doctype/about_us_settings/about_us_settings.json @@ -0,0 +1,101 @@ +{ + "allow_attach": 1, + "creation": "2013-03-19 12:02:15.000000", + "description": "Settings for the About Us Page", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Other", + "fields": [ + { + "fieldname": "help", + "fieldtype": "HTML", + "label": "Help", + "options": "
    Link for About Us Page is \"/about\"
    ", + "permlevel": 0, + "read_only": 0 + }, + { + "description": "Introduce your company to the website visitor.", + "fieldname": "company_introduction", + "fieldtype": "Text Editor", + "label": "Company Introduction", + "permlevel": 0, + "read_only": 0 + }, + { + "fieldname": "sb0", + "fieldtype": "Section Break", + "label": "Org History", + "permlevel": 0, + "read_only": 0 + }, + { + "description": "\"Company History\"", + "fieldname": "company_history_heading", + "fieldtype": "Data", + "label": "Org History Heading", + "permlevel": 0, + "read_only": 0 + }, + { + "fieldname": "company_history", + "fieldtype": "Table", + "label": "Org History", + "options": "Company History", + "permlevel": 0, + "read_only": 0 + }, + { + "fieldname": "sb1", + "fieldtype": "Section Break", + "label": "Team Members", + "permlevel": 0, + "read_only": 0 + }, + { + "description": "\"Team Members\" or \"Management\"", + "fieldname": "team_members_heading", + "fieldtype": "Data", + "label": "Team Members Heading", + "permlevel": 0, + "read_only": 0 + }, + { + "fieldname": "team_members", + "fieldtype": "Table", + "label": "Team Members", + "options": "About Us Team Member", + "permlevel": 0, + "read_only": 0 + }, + { + "description": "More content for the bottom of the page.", + "fieldname": "footer", + "fieldtype": "Text Editor", + "label": "Footer", + "permlevel": 0, + "read_only": 0 + } + ], + "icon": "icon-group", + "idx": 1, + "issingle": 1, + "modified": "2013-12-20 19:22:52.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "About Us Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Website Manager", + "submit": 0, + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/about_us_settings/about_us_settings.py b/frappe/website/doctype/about_us_settings/about_us_settings.py index 5c272ba8da..8186ac9342 100644 --- a/frappe/website/doctype/about_us_settings/about_us_settings.py +++ b/frappe/website/doctype/about_us_settings/about_us_settings.py @@ -6,16 +6,16 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class AboutUsSettings(Document): def on_update(self): from frappe.website.render import clear_cache clear_cache("about") def get_args(): - obj = frappe.get_obj("About Us Settings") + obj = frappe.get_doc("About Us Settings") return { "obj": obj } \ No newline at end of file diff --git a/frappe/website/doctype/about_us_settings/about_us_settings.txt b/frappe/website/doctype/about_us_settings/about_us_settings.txt deleted file mode 100644 index 956117c78c..0000000000 --- a/frappe/website/doctype/about_us_settings/about_us_settings.txt +++ /dev/null @@ -1,112 +0,0 @@ -[ - { - "creation": "2013-03-19 12:02:15", - "docstatus": 0, - "modified": "2013-12-20 19:22:52", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 1, - "description": "Settings for the About Us Page", - "doctype": "DocType", - "document_type": "Other", - "icon": "icon-group", - "issingle": 1, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "About Us Settings", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "read_only": 0 - }, - { - "create": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "About Us Settings", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Website Manager", - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "About Us Settings" - }, - { - "doctype": "DocField", - "fieldname": "help", - "fieldtype": "HTML", - "label": "Help", - "options": "
    Link for About Us Page is \"/about\"
    " - }, - { - "description": "Introduce your company to the website visitor.", - "doctype": "DocField", - "fieldname": "company_introduction", - "fieldtype": "Text Editor", - "label": "Company Introduction" - }, - { - "doctype": "DocField", - "fieldname": "sb0", - "fieldtype": "Section Break", - "label": "Org History" - }, - { - "description": "\"Company History\"", - "doctype": "DocField", - "fieldname": "company_history_heading", - "fieldtype": "Data", - "label": "Org History Heading" - }, - { - "doctype": "DocField", - "fieldname": "company_history", - "fieldtype": "Table", - "label": "Org History", - "options": "Company History" - }, - { - "doctype": "DocField", - "fieldname": "sb1", - "fieldtype": "Section Break", - "label": "Team Members" - }, - { - "description": "\"Team Members\" or \"Management\"", - "doctype": "DocField", - "fieldname": "team_members_heading", - "fieldtype": "Data", - "label": "Team Members Heading" - }, - { - "doctype": "DocField", - "fieldname": "team_members", - "fieldtype": "Table", - "label": "Team Members", - "options": "About Us Team Member" - }, - { - "description": "More content for the bottom of the page.", - "doctype": "DocField", - "fieldname": "footer", - "fieldtype": "Text Editor", - "label": "Footer" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/about_us_team_member/about_us_team_member.json b/frappe/website/doctype/about_us_team_member/about_us_team_member.json new file mode 100644 index 0000000000..11f197ce42 --- /dev/null +++ b/frappe/website/doctype/about_us_team_member/about_us_team_member.json @@ -0,0 +1,41 @@ +{ + "creation": "2013-03-07 11:55:11.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "full_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Full Name", + "permlevel": 0, + "reqd": 1, + "width": "150px" + }, + { + "fieldname": "image_link", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Image Link", + "options": "attach_files:", + "permlevel": 0, + "width": "150px" + }, + { + "fieldname": "bio", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Bio", + "permlevel": 0, + "reqd": 1, + "width": "200px" + } + ], + "idx": 1, + "istable": 1, + "modified": "2013-12-20 19:22:52.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "About Us Team Member", + "owner": "Administrator" +} \ No newline at end of file diff --git a/frappe/website/doctype/about_us_team_member/about_us_team_member.py b/frappe/website/doctype/about_us_team_member/about_us_team_member.py index cb42e2eea2..1dea8b4be3 100644 --- a/frappe/website/doctype/about_us_team_member/about_us_team_member.py +++ b/frappe/website/doctype/about_us_team_member/about_us_team_member.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class AboutUsTeamMember(Document): + pass \ No newline at end of file diff --git a/frappe/website/doctype/about_us_team_member/about_us_team_member.txt b/frappe/website/doctype/about_us_team_member/about_us_team_member.txt deleted file mode 100644 index 8d34a53c4f..0000000000 --- a/frappe/website/doctype/about_us_team_member/about_us_team_member.txt +++ /dev/null @@ -1,52 +0,0 @@ -[ - { - "creation": "2013-03-07 11:55:11", - "docstatus": 0, - "modified": "2013-12-20 19:22:52", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "DocType", - "istable": 1, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "in_list_view": 1, - "name": "__common__", - "parent": "About Us Team Member", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocType", - "name": "About Us Team Member" - }, - { - "doctype": "DocField", - "fieldname": "full_name", - "fieldtype": "Data", - "label": "Full Name", - "reqd": 1, - "width": "150px" - }, - { - "doctype": "DocField", - "fieldname": "image_link", - "fieldtype": "Select", - "label": "Image Link", - "options": "attach_files:", - "width": "150px" - }, - { - "doctype": "DocField", - "fieldname": "bio", - "fieldtype": "Small Text", - "label": "Bio", - "reqd": 1, - "width": "200px" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/blog_category/blog_category.json b/frappe/website/doctype/blog_category/blog_category.json new file mode 100644 index 0000000000..5d57f4efed --- /dev/null +++ b/frappe/website/doctype/blog_category/blog_category.json @@ -0,0 +1,75 @@ +{ + "allow_import": 1, + "autoname": "field:category_name", + "creation": "2013-03-08 09:41:11.000000", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Master", + "fields": [ + { + "fieldname": "category_name", + "fieldtype": "Data", + "label": "Category Name", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "published", + "fieldtype": "Check", + "label": "Published", + "permlevel": 0 + }, + { + "default": "blog", + "fieldname": "parent_website_route", + "fieldtype": "Link", + "label": "Parent Website Route", + "options": "Website Route", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "page_name", + "fieldtype": "Data", + "label": "Page Name", + "permlevel": 0 + } + ], + "icon": "icon-tag", + "idx": 1, + "modified": "2014-02-18 15:25:05.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Blog Category", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Website Manager", + "write": 1 + }, + { + "cancel": 0, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "Blogger" + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/blog_category/blog_category.py b/frappe/website/doctype/blog_category/blog_category.py index 492ca04eb6..b768a32dd6 100644 --- a/frappe/website/doctype/blog_category/blog_category.py +++ b/frappe/website/doctype/blog_category/blog_category.py @@ -6,16 +6,14 @@ import frappe from frappe.website.website_generator import WebsiteGenerator from frappe.website.render import clear_cache -class DocType(WebsiteGenerator): - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +class BlogCategory(WebsiteGenerator): def autoname(self): # to override autoname of WebsiteGenerator - self.doc.name = self.doc.category_name + self.name = self.category_name def get_page_title(self): - return self.doc.title or self.doc.name + return self.title or self.name def on_update(self): WebsiteGenerator.on_update(self) diff --git a/frappe/website/doctype/blog_category/blog_category.txt b/frappe/website/doctype/blog_category/blog_category.txt deleted file mode 100644 index 3508e60faa..0000000000 --- a/frappe/website/doctype/blog_category/blog_category.txt +++ /dev/null @@ -1,90 +0,0 @@ -[ - { - "creation": "2013-03-08 09:41:11", - "docstatus": 0, - "modified": "2014-02-18 15:25:05", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_import": 1, - "autoname": "field:category_name", - "doctype": "DocType", - "document_type": "Master", - "icon": "icon-tag", - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Blog Category", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Blog Category", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1 - }, - { - "doctype": "DocType", - "name": "Blog Category" - }, - { - "doctype": "DocField", - "fieldname": "category_name", - "fieldtype": "Data", - "label": "Category Name", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "title", - "fieldtype": "Data", - "label": "Title", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "published", - "fieldtype": "Check", - "label": "Published" - }, - { - "default": "blog", - "doctype": "DocField", - "fieldname": "parent_website_route", - "fieldtype": "Link", - "label": "Parent Website Route", - "options": "Website Route", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "page_name", - "fieldtype": "Data", - "label": "Page Name" - }, - { - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "report": 1, - "role": "Website Manager", - "write": 1 - }, - { - "delete": 0, - "doctype": "DocPerm", - "role": "Blogger" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/blog_category/test_blog_category.py b/frappe/website/doctype/blog_category/test_blog_category.py index 6c6ee45a2a..5a227df02b 100644 --- a/frappe/website/doctype/blog_category/test_blog_category.py +++ b/frappe/website/doctype/blog_category/test_blog_category.py @@ -1,17 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -test_records = [ - [{ - "doctype": "Blog Category", - "category_name": "_Test Blog Category", - "title": "_Test Blog Category", - "parent_website_route": "blog" - }], - [{ - "doctype": "Blog Category", - "category_name": "_Test Blog Category 1", - "title": "_Test Blog Category 1", - "parent_website_route": "blog" - }] -] \ No newline at end of file +import frappe + +test_records = frappe.get_test_records('Blog Category') \ No newline at end of file diff --git a/frappe/website/doctype/blog_category/test_records.json b/frappe/website/doctype/blog_category/test_records.json new file mode 100644 index 0000000000..d4533ed27c --- /dev/null +++ b/frappe/website/doctype/blog_category/test_records.json @@ -0,0 +1,14 @@ +[ + { + "category_name": "_Test Blog Category", + "doctype": "Blog Category", + "parent_website_route": "blog", + "title": "_Test Blog Category" + }, + { + "category_name": "_Test Blog Category 1", + "doctype": "Blog Category", + "parent_website_route": "blog", + "title": "_Test Blog Category 1" + } +] \ No newline at end of file diff --git a/frappe/website/doctype/blog_post/blog_post.js b/frappe/website/doctype/blog_post/blog_post.js index f02b95ead3..ed555352d7 100644 --- a/frappe/website/doctype/blog_post/blog_post.js +++ b/frappe/website/doctype/blog_post/blog_post.js @@ -5,7 +5,7 @@ cur_frm.cscript.refresh = function(doc) { if(!doc.__islocal && doc.published) { if(!doc.email_sent) { cur_frm.add_custom_button('Email Subscribers', function() { - $c_obj(make_doclist(doc.doctype, doc.name), 'send_emails', '', function(r) { + $c_obj(doc, 'send_emails', '', function(r) { cur_frm.refresh(); }); }); diff --git a/frappe/website/doctype/blog_post/blog_post.json b/frappe/website/doctype/blog_post/blog_post.json new file mode 100644 index 0000000000..22b4cc68cd --- /dev/null +++ b/frappe/website/doctype/blog_post/blog_post.json @@ -0,0 +1,144 @@ +{ + "allow_attach": 1, + "allow_import": 1, + "creation": "2013-03-28 10:35:30.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "published_on", + "fieldtype": "Date", + "label": "Published On", + "permlevel": 0 + }, + { + "fieldname": "published", + "fieldtype": "Check", + "label": "Published", + "permlevel": 0 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "blogger", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Blogger", + "options": "Blogger", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "blog_category", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Blog Category", + "options": "Blog Category", + "permlevel": 0 + }, + { + "fieldname": "parent_website_route", + "fieldtype": "Link", + "hidden": 1, + "label": "Parent Website Route", + "options": "Website Route", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "permlevel": 0 + }, + { + "description": "Description for listing page, in plain text, only a couple of lines. (max 140 characters)", + "fieldname": "blog_intro", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Blog Intro", + "permlevel": 0, + "reqd": 0 + }, + { + "fieldname": "content", + "fieldtype": "Text Editor", + "label": "Content", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "page_name", + "fieldtype": "Data", + "hidden": 1, + "label": "Page Name", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "email_sent", + "fieldtype": "Check", + "hidden": 1, + "label": "Email Sent", + "permlevel": 0 + } + ], + "icon": "icon-quote-left", + "idx": 1, + "max_attachments": 5, + "modified": "2014-02-20 12:55:07.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Blog Post", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "restrict": 1, + "role": "Website Manager", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Blogger", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Guest", + "submit": 0, + "write": 0 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index 09b09aad52..aaaa551b83 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -10,32 +10,30 @@ from frappe.website.render import clear_cache from frappe import _ from frappe.utils import today -class DocType(WebsiteGenerator): - def __init__(self, d, dl): - self.doc, self.doclist = d, dl - self.save_versions = True +class BlogPost(WebsiteGenerator): + save_versions = True def get_page_title(self): - return self.doc.title + return self.title def validate(self): - if not self.doc.blog_intro: - self.doc.blog_intro = self.doc.content[:140] - re.sub("\<[^>]*\>", "", self.doc.blog_intro) + if not self.blog_intro: + self.blog_intro = self.content[:140] + re.sub("\<[^>]*\>", "", self.blog_intro) - if self.doc.blog_intro: - self.doc.blog_intro = self.doc.blog_intro[:140] + if self.blog_intro: + self.blog_intro = self.blog_intro[:140] - if self.doc.published and not self.doc.published_on: - self.doc.published_on = today() + if self.published and not self.published_on: + self.published_on = today() - self.doc.parent_website_route = frappe.db.get_value("Website Route", - {"ref_doctype": "Blog Category", "docname": self.doc.blog_category}) + self.parent_website_route = frappe.db.get_value("Website Route", + {"ref_doctype": "Blog Category", "docname": self.blog_category}) # update posts frappe.db.sql("""update tabBlogger set posts=(select count(*) from `tabBlog Post` where ifnull(blogger,'')=tabBlogger.name) - where name=%s""", (self.doc.blogger,)) + where name=%s""", (self.blogger,)) def on_update(self): diff --git a/frappe/website/doctype/blog_post/blog_post.txt b/frappe/website/doctype/blog_post/blog_post.txt deleted file mode 100644 index 84be241b19..0000000000 --- a/frappe/website/doctype/blog_post/blog_post.txt +++ /dev/null @@ -1,149 +0,0 @@ -[ - { - "creation": "2013-03-28 10:35:30", - "docstatus": 0, - "modified": "2014-02-20 12:55:07", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 1, - "allow_import": 1, - "doctype": "DocType", - "icon": "icon-quote-left", - "max_attachments": 5, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Blog Post", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "delete": 0, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Blog Post", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "submit": 0 - }, - { - "doctype": "DocType", - "name": "Blog Post" - }, - { - "doctype": "DocField", - "fieldname": "title", - "fieldtype": "Data", - "label": "Title", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "published_on", - "fieldtype": "Date", - "label": "Published On" - }, - { - "doctype": "DocField", - "fieldname": "published", - "fieldtype": "Check", - "label": "Published" - }, - { - "doctype": "DocField", - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "blogger", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Blogger", - "options": "Blogger", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "blog_category", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Blog Category", - "options": "Blog Category" - }, - { - "doctype": "DocField", - "fieldname": "parent_website_route", - "fieldtype": "Link", - "hidden": 1, - "label": "Parent Website Route", - "options": "Website Route", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "section_break_5", - "fieldtype": "Section Break" - }, - { - "description": "Description for listing page, in plain text, only a couple of lines. (max 140 characters)", - "doctype": "DocField", - "fieldname": "blog_intro", - "fieldtype": "Small Text", - "in_list_view": 1, - "label": "Blog Intro", - "reqd": 0 - }, - { - "doctype": "DocField", - "fieldname": "content", - "fieldtype": "Text Editor", - "label": "Content", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "page_name", - "fieldtype": "Data", - "hidden": 1, - "label": "Page Name", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "email_sent", - "fieldtype": "Check", - "hidden": 1, - "label": "Email Sent" - }, - { - "create": 1, - "doctype": "DocPerm", - "restrict": 1, - "role": "Website Manager", - "write": 1 - }, - { - "create": 1, - "doctype": "DocPerm", - "role": "Blogger", - "write": 1 - }, - { - "doctype": "DocPerm", - "role": "Guest", - "write": 0 - } -] \ No newline at end of file diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index ac42ccb289..15b615f5de 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -3,31 +3,12 @@ """Use blog post test to test permission restriction logic""" -test_records = [ - [{ - "doctype": "Blog Post", - "title":"_Test Blog Post", - "blog_category": "_Test Blog Category", - "blogger": "_Test Blogger", - "blog_intro": "Test Blog Intro", - "content": "Test Blog Content" - }], - [{ - "doctype": "Blog Post", - "title":"_Test Blog Post 1", - "blog_category": "_Test Blog Category 1", - "blogger": "_Test Blogger", - "blog_intro": "Test Blog Intro", - "content": "Test Blog Content" - }] - -] - import frappe import frappe.defaults import unittest from frappe.core.page.user_properties.user_properties import add, remove, get_properties, clear_restrictions +test_records = frappe.get_test_records('Blog Post') test_dependencies = ["User"] class TestBlogPost(unittest.TestCase): @@ -39,11 +20,11 @@ class TestBlogPost(unittest.TestCase): frappe.clear_cache(doctype="Blog Post") - user = frappe.bean("User", "test1@example.com") - user.get_controller().add_roles("Website Manager") + user = frappe.get_doc("User", "test1@example.com") + user.add_roles("Website Manager") - user = frappe.bean("User", "test2@example.com") - user.get_controller().add_roles("Blogger") + user = frappe.get_doc("User", "test2@example.com") + user.add_roles("Blogger") frappe.set_user("test1@example.com") @@ -53,18 +34,18 @@ class TestBlogPost(unittest.TestCase): clear_restrictions("Blog Post") def test_basic_permission(self): - post = frappe.bean("Blog Post", "_test-blog-post") - self.assertTrue(post.has_read_perm()) + post = frappe.get_doc("Blog Post", "_test-blog-post") + self.assertTrue(post.has_permission("read")) - def test_restriction_in_bean(self): + def test_restriction_in_doc(self): frappe.defaults.add_default("Blog Category", "_Test Blog Category 1", "test1@example.com", "Restriction") - post = frappe.bean("Blog Post", "_test-blog-post") - self.assertFalse(post.has_read_perm()) + post = frappe.get_doc("Blog Post", "_test-blog-post") + self.assertFalse(post.has_permission("read")) - post1 = frappe.bean("Blog Post", "_test-blog-post-1") - self.assertTrue(post1.has_read_perm()) + post1 = frappe.get_doc("Blog Post", "_test-blog-post-1") + self.assertTrue(post1.has_permission("read")) def test_restriction_in_report(self): frappe.defaults.add_default("Blog Category", "_Test Blog Category 1", "test1@example.com", @@ -87,16 +68,16 @@ class TestBlogPost(unittest.TestCase): and ifnull(permlevel,0)=0""") frappe.clear_cache(doctype="Blog Post") - def test_owner_match_bean(self): + def test_owner_match_doc(self): self.add_restricted_on_blogger() frappe.set_user("test2@example.com") - post = frappe.bean("Blog Post", "_test-blog-post") - self.assertTrue(post.has_read_perm()) + post = frappe.get_doc("Blog Post", "_test-blog-post") + self.assertTrue(post.has_permission("read")) - post1 = frappe.bean("Blog Post", "_test-blog-post-1") - self.assertFalse(post1.has_read_perm()) + post1 = frappe.get_doc("Blog Post", "_test-blog-post-1") + self.assertFalse(post1.has_permission("read")) def test_owner_match_report(self): frappe.db.sql("""update tabDocPerm set `restricted`=1 where parent='Blog Post' @@ -130,12 +111,12 @@ class TestBlogPost(unittest.TestCase): frappe.set_user("test2@example.com") # user can only access restricted blog post - bean = frappe.bean("Blog Post", "_test-blog-post") - self.assertTrue(bean.has_read_perm()) + doc = frappe.get_doc("Blog Post", "_test-blog-post") + self.assertTrue(doc.has_permission("read")) # and not this one - bean = frappe.bean("Blog Post", "_test-blog-post-1") - self.assertFalse(bean.has_read_perm()) + doc = frappe.get_doc("Blog Post", "_test-blog-post-1") + self.assertFalse(doc.has_permission("read")) def test_not_allowed_to_remove_self(self): self.add_restriction_to_user2() @@ -151,22 +132,21 @@ class TestBlogPost(unittest.TestCase): self.add_restricted_on_blogger() frappe.set_user("test2@example.com") - bean = frappe.bean("Blog Post", "_test-blog-post-1") - self.assertFalse(bean.has_read_perm()) + doc = frappe.get_doc("Blog Post", "_test-blog-post-1") + self.assertFalse(doc.has_permission("read")) frappe.set_user("test1@example.com") add("test2@example.com", "Blog Post", "_test-blog-post-1") frappe.set_user("test2@example.com") - bean = frappe.bean("Blog Post", "_test-blog-post-1") + doc = frappe.get_doc("Blog Post", "_test-blog-post-1") - self.assertTrue(bean.has_read_perm()) + self.assertTrue(doc.has_permission("read")) def test_set_only_once(self): - blog_post = frappe.get_doctype("Blog Post") + blog_post = frappe.get_meta("Blog Post") blog_post.get_field("title").set_only_once = 1 - bean = frappe.bean("Blog Post", "_test-blog-post-1") - bean.doc.title = "New" - self.assertRaises(frappe.CannotChangeConstantError, bean.save) - blog_post.get_field("title").set_only_once = 0 - + doc = frappe.get_doc("Blog Post", "_test-blog-post-1") + doc.title = "New" + self.assertRaises(frappe.CannotChangeConstantError, doc.save) + blog_post.get_field("title").set_only_once = 0 \ No newline at end of file diff --git a/frappe/website/doctype/blog_post/test_records.json b/frappe/website/doctype/blog_post/test_records.json new file mode 100644 index 0000000000..72282adcb2 --- /dev/null +++ b/frappe/website/doctype/blog_post/test_records.json @@ -0,0 +1,18 @@ +[ + { + "blog_category": "_Test Blog Category", + "blog_intro": "Test Blog Intro", + "blogger": "_Test Blogger", + "content": "Test Blog Content", + "doctype": "Blog Post", + "title": "_Test Blog Post" + }, + { + "blog_category": "_Test Blog Category 1", + "blog_intro": "Test Blog Intro", + "blogger": "_Test Blogger", + "content": "Test Blog Content", + "doctype": "Blog Post", + "title": "_Test Blog Post 1" + } +] \ No newline at end of file diff --git a/frappe/website/doctype/blog_settings/blog_settings.json b/frappe/website/doctype/blog_settings/blog_settings.json new file mode 100644 index 0000000000..963791679d --- /dev/null +++ b/frappe/website/doctype/blog_settings/blog_settings.json @@ -0,0 +1,45 @@ +{ + "creation": "2013-03-11 17:48:16.000000", + "description": "Blog Settings", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "blog_title", + "fieldtype": "Data", + "label": "Blog Title", + "permlevel": 0 + }, + { + "fieldname": "blog_introduction", + "fieldtype": "Small Text", + "label": "Blog Introduction", + "permlevel": 0 + }, + { + "fieldname": "writers_introduction", + "fieldtype": "Small Text", + "label": "Writers Introduction", + "permlevel": 0 + } + ], + "icon": "icon-cog", + "idx": 1, + "issingle": 1, + "modified": "2013-12-20 19:22:56.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Blog Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "Website Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/blog_settings/blog_settings.py b/frappe/website/doctype/blog_settings/blog_settings.py index 2c03816c3c..19a2525ad8 100644 --- a/frappe/website/doctype/blog_settings/blog_settings.py +++ b/frappe/website/doctype/blog_settings/blog_settings.py @@ -6,9 +6,9 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class BlogSettings(Document): def on_update(self): from frappe.website.render import clear_cache diff --git a/frappe/website/doctype/blog_settings/blog_settings.txt b/frappe/website/doctype/blog_settings/blog_settings.txt deleted file mode 100644 index 8581e7982d..0000000000 --- a/frappe/website/doctype/blog_settings/blog_settings.txt +++ /dev/null @@ -1,64 +0,0 @@ -[ - { - "creation": "2013-03-11 17:48:16", - "docstatus": 0, - "modified": "2013-12-20 19:22:56", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "description": "Blog Settings", - "doctype": "DocType", - "icon": "icon-cog", - "issingle": 1, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Blog Settings", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "create": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Blog Settings", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "role": "Website Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Blog Settings" - }, - { - "doctype": "DocField", - "fieldname": "blog_title", - "fieldtype": "Data", - "label": "Blog Title" - }, - { - "doctype": "DocField", - "fieldname": "blog_introduction", - "fieldtype": "Small Text", - "label": "Blog Introduction" - }, - { - "doctype": "DocField", - "fieldname": "writers_introduction", - "fieldtype": "Small Text", - "label": "Writers Introduction" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/blogger/blogger.json b/frappe/website/doctype/blogger/blogger.json new file mode 100644 index 0000000000..1cec8d7bd5 --- /dev/null +++ b/frappe/website/doctype/blogger/blogger.json @@ -0,0 +1,89 @@ +{ + "allow_attach": 1, + "allow_import": 1, + "autoname": "field:short_name", + "creation": "2013-03-25 16:00:51.000000", + "description": "User ID of a Blogger", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Master", + "fields": [ + { + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled", + "permlevel": 0 + }, + { + "description": "Will be used in url (usually first name).", + "fieldname": "short_name", + "fieldtype": "Data", + "label": "Short Name", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "full_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Full Name", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User", + "permlevel": 0 + }, + { + "fieldname": "bio", + "fieldtype": "Small Text", + "label": "Bio", + "permlevel": 0 + }, + { + "fieldname": "avatar", + "fieldtype": "Select", + "label": "Avatar", + "options": "attach_files:", + "permlevel": 0 + }, + { + "fieldname": "posts", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Posts", + "permlevel": 0, + "read_only": 1 + } + ], + "icon": "icon-user", + "idx": 1, + "max_attachments": 1, + "modified": "2013-12-20 19:23:58.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Blogger", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "Website Manager", + "write": 1 + }, + { + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "Blogger", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/blogger/blogger.py b/frappe/website/doctype/blogger/blogger.py index 3b71838336..4acc91408c 100644 --- a/frappe/website/doctype/blogger/blogger.py +++ b/frappe/website/doctype/blogger/blogger.py @@ -7,9 +7,9 @@ from __future__ import unicode_literals import frappe from frappe import _ -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class Blogger(Document): def on_update(self): "if user is set, then update all older blogs" @@ -17,9 +17,9 @@ class DocType: from frappe.website.doctype.blog_post.blog_post import clear_blog_cache clear_blog_cache() - if self.doc.user: + if self.user: for blog in frappe.db.sql_list("""select name from `tabBlog Post` where owner=%s - and ifnull(blogger,'')=''""", self.doc.user): - b = frappe.bean("Blog Post", blog) - b.doc.blogger = self.doc.name + and ifnull(blogger,'')=''""", self.user): + b = frappe.get_doc("Blog Post", blog) + b.blogger = self.name b.save() \ No newline at end of file diff --git a/frappe/website/doctype/blogger/blogger.txt b/frappe/website/doctype/blogger/blogger.txt deleted file mode 100644 index 3d7f343ea2..0000000000 --- a/frappe/website/doctype/blogger/blogger.txt +++ /dev/null @@ -1,104 +0,0 @@ -[ - { - "creation": "2013-03-25 16:00:51", - "docstatus": 0, - "modified": "2013-12-20 19:23:58", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 1, - "allow_import": 1, - "autoname": "field:short_name", - "description": "User ID of a Blogger", - "doctype": "DocType", - "document_type": "Master", - "icon": "icon-user", - "max_attachments": 1, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Blogger", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Blogger", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Blogger" - }, - { - "doctype": "DocField", - "fieldname": "disabled", - "fieldtype": "Check", - "label": "Disabled" - }, - { - "description": "Will be used in url (usually first name).", - "doctype": "DocField", - "fieldname": "short_name", - "fieldtype": "Data", - "label": "Short Name", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "full_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Full Name", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "user", - "fieldtype": "Link", - "label": "User", - "options": "User" - }, - { - "doctype": "DocField", - "fieldname": "bio", - "fieldtype": "Small Text", - "label": "Bio" - }, - { - "doctype": "DocField", - "fieldname": "avatar", - "fieldtype": "Select", - "label": "Avatar", - "options": "attach_files:" - }, - { - "doctype": "DocField", - "fieldname": "posts", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Posts", - "read_only": 1 - }, - { - "create": 1, - "doctype": "DocPerm", - "role": "Website Manager" - }, - { - "doctype": "DocPerm", - "role": "Blogger" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/blogger/test_blogger.py b/frappe/website/doctype/blogger/test_blogger.py index 15b31b34c7..db7feb4970 100644 --- a/frappe/website/doctype/blogger/test_blogger.py +++ b/frappe/website/doctype/blogger/test_blogger.py @@ -1,16 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -test_records = [ - [{ - "doctype": "Blogger", - "short_name": "_Test Blogger", - "full_name": "_Test Blogger" - }], - [{ - "doctype": "Blogger", - "short_name": "_Test Blogger 1", - "full_name": "_Test Blogger 1" - }] - -] \ No newline at end of file + +import frappe +test_records = frappe.get_test_records('Blogger') \ No newline at end of file diff --git a/frappe/website/doctype/blogger/test_records.json b/frappe/website/doctype/blogger/test_records.json new file mode 100644 index 0000000000..ecaaaa161e --- /dev/null +++ b/frappe/website/doctype/blogger/test_records.json @@ -0,0 +1,12 @@ +[ + { + "doctype": "Blogger", + "full_name": "_Test Blogger", + "short_name": "_Test Blogger" + }, + { + "doctype": "Blogger", + "full_name": "_Test Blogger 1", + "short_name": "_Test Blogger 1" + } +] \ No newline at end of file diff --git a/frappe/website/doctype/company_history/company_history.json b/frappe/website/doctype/company_history/company_history.json new file mode 100644 index 0000000000..08cfe8eb9e --- /dev/null +++ b/frappe/website/doctype/company_history/company_history.json @@ -0,0 +1,30 @@ +{ + "creation": "2013-02-22 01:28:08.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "year", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Year", + "permlevel": 0 + }, + { + "fieldname": "highlight", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Highlight", + "permlevel": 0, + "print_width": "300px", + "width": "300px" + } + ], + "idx": 1, + "istable": 1, + "modified": "2013-12-20 19:23:01.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Company History", + "owner": "Administrator" +} \ No newline at end of file diff --git a/frappe/website/doctype/company_history/company_history.py b/frappe/website/doctype/company_history/company_history.py index cb42e2eea2..83af48808c 100644 --- a/frappe/website/doctype/company_history/company_history.py +++ b/frappe/website/doctype/company_history/company_history.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class CompanyHistory(Document): + pass \ No newline at end of file diff --git a/frappe/website/doctype/company_history/company_history.txt b/frappe/website/doctype/company_history/company_history.txt deleted file mode 100644 index e605049a48..0000000000 --- a/frappe/website/doctype/company_history/company_history.txt +++ /dev/null @@ -1,42 +0,0 @@ -[ - { - "creation": "2013-02-22 01:28:08", - "docstatus": 0, - "modified": "2013-12-20 19:23:01", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "DocType", - "istable": 1, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "in_list_view": 1, - "name": "__common__", - "parent": "Company History", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocType", - "name": "Company History" - }, - { - "doctype": "DocField", - "fieldname": "year", - "fieldtype": "Data", - "label": "Year" - }, - { - "doctype": "DocField", - "fieldname": "highlight", - "fieldtype": "Text", - "label": "Highlight", - "print_width": "300px", - "width": "300px" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/contact_us_settings/contact_us_settings.json b/frappe/website/doctype/contact_us_settings/contact_us_settings.json new file mode 100644 index 0000000000..1479112631 --- /dev/null +++ b/frappe/website/doctype/contact_us_settings/contact_us_settings.json @@ -0,0 +1,118 @@ +{ + "creation": "2013-02-21 20:12:42.000000", + "description": "Settings for Contact Us Page", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "help", + "fieldtype": "HTML", + "label": "Help", + "options": "
    Link for Contact Page is \"/contact\"
    ", + "permlevel": 0 + }, + { + "fieldname": "introduction_section", + "fieldtype": "Section Break", + "label": "Introduction", + "permlevel": 0 + }, + { + "description": "Send enquiries to this email address", + "fieldname": "forward_to_email", + "fieldtype": "Data", + "label": "Forward To Email Address", + "permlevel": 0 + }, + { + "description": "Default: \"Contact Us\"", + "fieldname": "heading", + "fieldtype": "Data", + "label": "Heading", + "permlevel": 0 + }, + { + "description": "Introductory information for the Contact Us Page", + "fieldname": "introduction", + "fieldtype": "Text Editor", + "label": "Introduction", + "permlevel": 0 + }, + { + "description": "Contact options, like \"Sales Query, Support Query\" etc each on a new line or separated by commas.", + "fieldname": "query_options", + "fieldtype": "Small Text", + "label": "Query Options", + "permlevel": 0 + }, + { + "fieldname": "address", + "fieldtype": "Section Break", + "label": "Address", + "permlevel": 0 + }, + { + "fieldname": "address_title", + "fieldtype": "Data", + "label": "Address Title", + "permlevel": 0 + }, + { + "fieldname": "address_line1", + "fieldtype": "Data", + "label": "Address Line 1", + "permlevel": 0 + }, + { + "fieldname": "address_line2", + "fieldtype": "Data", + "label": "Address Line 2", + "permlevel": 0 + }, + { + "fieldname": "city", + "fieldtype": "Data", + "label": "City", + "permlevel": 0 + }, + { + "fieldname": "state", + "fieldtype": "Data", + "label": "State", + "permlevel": 0 + }, + { + "fieldname": "pincode", + "fieldtype": "Data", + "label": "Pincode", + "permlevel": 0 + }, + { + "fieldname": "country", + "fieldtype": "Data", + "label": "Country", + "permlevel": 0 + } + ], + "icon": "icon-cog", + "idx": 1, + "issingle": 1, + "modified": "2014-02-28 11:32:25.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Contact Us Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Website Manager", + "submit": 0, + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/contact_us_settings/contact_us_settings.py b/frappe/website/doctype/contact_us_settings/contact_us_settings.py index 12162c1799..c3185832ba 100644 --- a/frappe/website/doctype/contact_us_settings/contact_us_settings.py +++ b/frappe/website/doctype/contact_us_settings/contact_us_settings.py @@ -6,9 +6,9 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class ContactUsSettings(Document): def on_update(self): from frappe.website.render import clear_cache diff --git a/frappe/website/doctype/contact_us_settings/contact_us_settings.txt b/frappe/website/doctype/contact_us_settings/contact_us_settings.txt deleted file mode 100644 index cadce62a63..0000000000 --- a/frappe/website/doctype/contact_us_settings/contact_us_settings.txt +++ /dev/null @@ -1,137 +0,0 @@ -[ - { - "creation": "2013-02-21 20:12:42", - "docstatus": 0, - "modified": "2014-02-28 11:32:25", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "description": "Settings for Contact Us Page", - "doctype": "DocType", - "icon": "icon-cog", - "issingle": 1, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Contact Us Settings", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "create": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Contact Us Settings", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Website Manager", - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Contact Us Settings" - }, - { - "doctype": "DocField", - "fieldname": "help", - "fieldtype": "HTML", - "label": "Help", - "options": "
    Link for Contact Page is \"/contact\"
    " - }, - { - "doctype": "DocField", - "fieldname": "introduction_section", - "fieldtype": "Section Break", - "label": "Introduction" - }, - { - "description": "Send enquiries to this email address", - "doctype": "DocField", - "fieldname": "forward_to_email", - "fieldtype": "Data", - "label": "Forward To Email Address" - }, - { - "description": "Default: \"Contact Us\"", - "doctype": "DocField", - "fieldname": "heading", - "fieldtype": "Data", - "label": "Heading" - }, - { - "description": "Introductory information for the Contact Us Page", - "doctype": "DocField", - "fieldname": "introduction", - "fieldtype": "Text Editor", - "label": "Introduction" - }, - { - "description": "Contact options, like \"Sales Query, Support Query\" etc each on a new line or separated by commas.", - "doctype": "DocField", - "fieldname": "query_options", - "fieldtype": "Small Text", - "label": "Query Options" - }, - { - "doctype": "DocField", - "fieldname": "address", - "fieldtype": "Section Break", - "label": "Address" - }, - { - "doctype": "DocField", - "fieldname": "address_title", - "fieldtype": "Data", - "label": "Address Title" - }, - { - "doctype": "DocField", - "fieldname": "address_line1", - "fieldtype": "Data", - "label": "Address Line 1" - }, - { - "doctype": "DocField", - "fieldname": "address_line2", - "fieldtype": "Data", - "label": "Address Line 2" - }, - { - "doctype": "DocField", - "fieldname": "city", - "fieldtype": "Data", - "label": "City" - }, - { - "doctype": "DocField", - "fieldname": "state", - "fieldtype": "Data", - "label": "State" - }, - { - "doctype": "DocField", - "fieldname": "pincode", - "fieldtype": "Data", - "label": "Pincode" - }, - { - "doctype": "DocField", - "fieldname": "country", - "fieldtype": "Data", - "label": "Country" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/post/post.json b/frappe/website/doctype/post/post.json new file mode 100644 index 0000000000..ffbc54ecf3 --- /dev/null +++ b/frappe/website/doctype/post/post.json @@ -0,0 +1,128 @@ +{ + "autoname": "P.#######", + "creation": "2014-01-07 14:00:04.000000", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Transaction", + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "permlevel": 0, + "reqd": 0 + }, + { + "fieldname": "content", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Content", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "picture_url", + "fieldtype": "Attach", + "label": "Picture URL", + "permlevel": 0 + }, + { + "fieldname": "website_group", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Website Group", + "options": "Website Group", + "permlevel": 0, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "is_event", + "fieldtype": "Check", + "label": "Is Event", + "permlevel": 0 + }, + { + "fieldname": "event_datetime", + "fieldtype": "Datetime", + "label": "Event Datetime", + "permlevel": 0 + }, + { + "fieldname": "is_task", + "fieldtype": "Check", + "label": "Is Task", + "permlevel": 0 + }, + { + "fieldname": "assigned_to", + "fieldtype": "Link", + "label": "Assigned To", + "options": "User", + "permlevel": 0 + }, + { + "fieldname": "assigned_to_fullname", + "fieldtype": "Data", + "label": "Assigned To Fullname", + "permlevel": 0 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "\nOpen\nClosed", + "permlevel": 0, + "reqd": 0 + }, + { + "fieldname": "parent_post", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Parent Post", + "options": "Post", + "permlevel": 0, + "search_index": 1 + }, + { + "fieldname": "unsubscribe", + "fieldtype": "Check", + "label": "Unsubscribe", + "permlevel": 0 + }, + { + "default": "0", + "fieldname": "upvotes", + "fieldtype": "Int", + "label": "Upvotes", + "permlevel": 0 + }, + { + "default": "0", + "fieldname": "replies", + "fieldtype": "Int", + "label": "Replies", + "permlevel": 0 + } + ], + "icon": "icon-comment", + "idx": 1, + "modified": "2014-03-03 14:53:19.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Post", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "export": 1, + "permlevel": 0, + "read": 1, + "report": 1, + "role": "System Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/post/post.py b/frappe/website/doctype/post/post.py index cc83b76e2a..c3e51e8324 100644 --- a/frappe/website/doctype/post/post.py +++ b/frappe/website/doctype/post/post.py @@ -10,56 +10,56 @@ from frappe.utils import get_fullname from frappe.utils.email_lib.bulk import send from frappe.utils.email_lib import sendmail -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class Post(Document): def validate(self): - if not self.doc.parent_post and not self.doc.title: + if not self.parent_post and not self.title: frappe.throw("Please enter title!") - self.assigned_to = frappe.db.get_value(self.doc.doctype, self.doc.name, "assigned_to") - if self.doc.is_task: - if not self.doc.status: - self.doc.status = "Open" - if self.doc.assigned_to: - if not self.doc.assigned_to_fullname: - self.doc.assigned_to_fullname = get_fullname(self.doc.assigned_to) + self.assigned_to = frappe.db.get_value(self.doctype, self.name, "assigned_to") + if self.is_task: + if not self.status: + self.status = "Open" + if self.assigned_to: + if not self.assigned_to_fullname: + self.assigned_to_fullname = get_fullname(self.assigned_to) else: - self.doc.assigned_to_fullname = None + self.assigned_to_fullname = None else: - self.doc.assigned_to = self.doc.assigned_to_fullname = self.doc.status = None + self.assigned_to = self.assigned_to_fullname = self.status = None - if self.doc.is_event: - if not self.doc.event_datetime: + if self.is_event: + if not self.event_datetime: frappe.throw("Please specify Event's Date and Time") else: - self.doc.event_datetime = None + self.event_datetime = None def on_update(self): from frappe.templates.website_group.post import clear_post_cache from frappe.templates.generators.website_group import clear_cache - clear_cache(website_group=self.doc.website_group) - clear_post_cache(self.doc.parent_post or self.doc.name) + clear_cache(website_group=self.website_group) + clear_post_cache(self.parent_post or self.name) - if self.doc.assigned_to and self.doc.assigned_to != self.assigned_to \ - and frappe.session.user != self.doc.assigned_to: + if self.assigned_to and self.assigned_to != self.assigned_to \ + and frappe.session.user != self.assigned_to: # send assignment email - sendmail(recipients=[self.doc.assigned_to], - subject="You have been assigned this Task by {}".format(get_fullname(self.doc.modified_by)), - msg=self.get_reply_email_message(self.doc.name, get_fullname(self.doc.owner))) + sendmail(recipients=[self.assigned_to], + subject="You have been assigned this Task by {}".format(get_fullname(self.modified_by)), + msg=self.get_reply_email_message(self.name, get_fullname(self.owner))) def send_email_on_reply(self): - owner_fullname = get_fullname(self.doc.owner) + owner_fullname = get_fullname(self.owner) - parent_post = frappe.bean("Post", self.doc.parent_post).doc + parent_post = frappe.get_doc("Post", self.parent_post) - message = self.get_reply_email_message(self.doc.name, owner_fullname) + message = self.get_reply_email_message(self.name, owner_fullname) # send email to the owner of the post, if he/she is different - if parent_post.owner != self.doc.owner: + if parent_post.owner != self.owner: send(recipients=[parent_post.owner], subject="{someone} replied to your post".format(someone=owner_fullname), message=message, @@ -69,12 +69,12 @@ class DocType: email_field='owner', # for tracking sent status - ref_doctype=self.doc.doctype, ref_docname=self.doc.name) + ref_doctype=self.doctype, ref_docname=self.name) # send email to members who part of the conversation participants = frappe.db.sql("""select owner, name from `tabPost` where parent_post=%s and owner not in (%s, %s) order by creation asc""", - (self.doc.parent_post, parent_post.owner, self.doc.owner), as_dict=True) + (self.parent_post, parent_post.owner, self.owner), as_dict=True) send(recipients=[p.owner for p in participants], subject="{someone} replied to a post by {other}".format(someone=owner_fullname, @@ -86,13 +86,13 @@ class DocType: email_field='owner', # for tracking sent status - ref_doctype=self.doc.doctype, ref_docname=self.doc.name) + ref_doctype=self.doctype, ref_docname=self.name) def get_reply_email_message(self, post_name, owner_fullname=None): - message = self.doc.content - if self.doc.picture_url: + message = self.content + if self.picture_url: message += """
    """\ - .format(url=self.doc.picture_url) + .format(url=self.picture_url) message += "

    By {fullname}

    ".format(fullname=owner_fullname) message += "

    Click here to view the post

    ".format(fullname=owner_fullname, post_name=post_name) diff --git a/frappe/website/doctype/post/post.txt b/frappe/website/doctype/post/post.txt deleted file mode 100644 index a9970af463..0000000000 --- a/frappe/website/doctype/post/post.txt +++ /dev/null @@ -1,147 +0,0 @@ -[ - { - "creation": "2014-01-07 14:00:04", - "docstatus": 0, - "modified": "2014-03-03 14:53:19", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "P.#######", - "doctype": "DocType", - "document_type": "Transaction", - "icon": "icon-comment", - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Post", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "export": 1, - "name": "__common__", - "parent": "Post", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "read": 1, - "report": 1, - "role": "System Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Post" - }, - { - "doctype": "DocField", - "fieldname": "title", - "fieldtype": "Data", - "label": "Title", - "reqd": 0 - }, - { - "doctype": "DocField", - "fieldname": "content", - "fieldtype": "Text", - "in_list_view": 1, - "label": "Content", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "picture_url", - "fieldtype": "Attach", - "label": "Picture URL" - }, - { - "doctype": "DocField", - "fieldname": "website_group", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Website Group", - "options": "Website Group", - "reqd": 1, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "is_event", - "fieldtype": "Check", - "label": "Is Event" - }, - { - "doctype": "DocField", - "fieldname": "event_datetime", - "fieldtype": "Datetime", - "label": "Event Datetime" - }, - { - "doctype": "DocField", - "fieldname": "is_task", - "fieldtype": "Check", - "label": "Is Task" - }, - { - "doctype": "DocField", - "fieldname": "assigned_to", - "fieldtype": "Link", - "label": "Assigned To", - "options": "User" - }, - { - "doctype": "DocField", - "fieldname": "assigned_to_fullname", - "fieldtype": "Data", - "label": "Assigned To Fullname" - }, - { - "doctype": "DocField", - "fieldname": "status", - "fieldtype": "Select", - "label": "Status", - "options": "\nOpen\nClosed", - "reqd": 0 - }, - { - "doctype": "DocField", - "fieldname": "parent_post", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Parent Post", - "options": "Post", - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "unsubscribe", - "fieldtype": "Check", - "label": "Unsubscribe" - }, - { - "default": "0", - "doctype": "DocField", - "fieldname": "upvotes", - "fieldtype": "Int", - "label": "Upvotes" - }, - { - "default": "0", - "doctype": "DocField", - "fieldname": "replies", - "fieldtype": "Int", - "label": "Replies" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/style_settings/style_settings.json b/frappe/website/doctype/style_settings/style_settings.json new file mode 100644 index 0000000000..9b08275502 --- /dev/null +++ b/frappe/website/doctype/style_settings/style_settings.json @@ -0,0 +1,187 @@ +{ + "allow_attach": 1, + "creation": "2013-03-25 16:01:33.000000", + "description": "Set your background color, font and image (tiled)", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "color", + "fieldtype": "Section Break", + "label": "Color", + "permlevel": 0 + }, + { + "description": "If image is selected, color will be ignored (attach first)", + "fieldname": "background_image", + "fieldtype": "Select", + "label": "Background Image", + "options": "attach_files:", + "permlevel": 0 + }, + { + "description": "Solid background color (default light gray)", + "fieldname": "background_color", + "fieldtype": "Data", + "label": "Background Color", + "permlevel": 0 + }, + { + "fieldname": "page_background", + "fieldtype": "Data", + "label": "Page Background", + "permlevel": 0 + }, + { + "fieldname": "page_border", + "fieldtype": "Check", + "label": "Page Border", + "permlevel": 0 + }, + { + "fieldname": "page_headings", + "fieldtype": "Data", + "label": "Page Headings", + "permlevel": 0 + }, + { + "fieldname": "page_text", + "fieldtype": "Data", + "label": "Page Text", + "permlevel": 0 + }, + { + "fieldname": "page_links", + "fieldtype": "Data", + "label": "Page Links", + "permlevel": 0 + }, + { + "fieldname": "cb0", + "fieldtype": "Column Break", + "label": "Top Bar", + "permlevel": 0, + "print_width": "50%", + "width": "50%" + }, + { + "fieldname": "top_bar_background", + "fieldtype": "Data", + "label": "Top Bar Background", + "permlevel": 0 + }, + { + "description": "000 is black, fff is white", + "fieldname": "top_bar_foreground", + "fieldtype": "Data", + "label": "Top Bar Text", + "permlevel": 0 + }, + { + "fieldname": "fonts", + "fieldtype": "Section Break", + "label": "Fonts", + "permlevel": 0 + }, + { + "fieldname": "heading_font", + "fieldtype": "Select", + "label": "Font (Heading)", + "options": "\nHelvetica Neue\nLucida Grande\nVerdana\nArial\nGeorgia\nTahoma\nLato\nOpen Sans", + "permlevel": 0 + }, + { + "fieldname": "font", + "fieldtype": "Select", + "label": "Font (Text)", + "options": "\nHelvetica Neue\nLucida Grande\nVerdana\nArial\nGeorgia\nTahoma", + "permlevel": 0 + }, + { + "fieldname": "font_size", + "fieldtype": "Select", + "label": "Font Size (Text)", + "options": "\n12px\n13px\n14px\n15px\n16px\n17px\n18px", + "permlevel": 0 + }, + { + "fieldname": "heading_text_as", + "fieldtype": "Select", + "label": "Heading Text As", + "options": "\nUPPERCASE\nTitle Case\nlowercase", + "permlevel": 0 + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "description": "Add the name of Google Web Font e.g. \"Open Sans\"", + "fieldname": "google_web_font_for_heading", + "fieldtype": "Data", + "label": "Google Web Font (Heading)", + "permlevel": 0 + }, + { + "description": "Add the name of Google Web Font e.g. \"Open Sans\"", + "fieldname": "google_web_font_for_text", + "fieldtype": "Data", + "label": "Google Web Font (Text)", + "permlevel": 0 + }, + { + "fieldname": "css", + "fieldtype": "Section Break", + "label": "CSS", + "permlevel": 0 + }, + { + "description": "add your own CSS (careful!)", + "fieldname": "add_css", + "fieldtype": "Code", + "label": "Add CSS", + "permlevel": 0 + }, + { + "description": "Auto generated", + "fieldname": "custom_css", + "fieldtype": "Code", + "label": "Custom CSS", + "permlevel": 0, + "read_only": 1 + } + ], + "icon": "icon-cog", + "idx": 1, + "issingle": 1, + "max_attachments": 1, + "modified": "2013-12-20 19:22:48.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Style Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Website Manager", + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "permlevel": 1, + "read": 1, + "report": 0, + "role": "Website Manager", + "submit": 0 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/style_settings/style_settings.py b/frappe/website/doctype/style_settings/style_settings.py index c66e4779d0..8eeff8e484 100644 --- a/frappe/website/doctype/style_settings/style_settings.py +++ b/frappe/website/doctype/style_settings/style_settings.py @@ -7,22 +7,22 @@ import frappe from frappe.utils import cint, cstr from frappe import _ -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class StyleSettings(Document): def validate(self): """make custom css""" self.validate_colors() def validate_colors(self): - if (self.doc.page_background or self.doc.page_text) and \ - self.doc.page_background==self.doc.page_text: + if (self.page_background or self.page_text) and \ + self.page_background==self.page_text: frappe.msgprint(_("Page text and background is same color. Please change."), raise_exception=1) - if (self.doc.top_bar_background or self.doc.top_bar_foreground) and \ - self.doc.top_bar_background==self.doc.top_bar_foreground: + if (self.top_bar_background or self.top_bar_foreground) and \ + self.top_bar_background==self.top_bar_foreground: frappe.msgprint(_("Top Bar text and background is same color. Please change."), raise_exception=1) diff --git a/frappe/website/doctype/style_settings/style_settings.txt b/frappe/website/doctype/style_settings/style_settings.txt deleted file mode 100644 index 3582672e71..0000000000 --- a/frappe/website/doctype/style_settings/style_settings.txt +++ /dev/null @@ -1,203 +0,0 @@ -[ - { - "creation": "2013-03-25 16:01:33", - "docstatus": 0, - "modified": "2013-12-20 19:22:48", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 1, - "description": "Set your background color, font and image (tiled)", - "doctype": "DocType", - "icon": "icon-cog", - "issingle": 1, - "max_attachments": 1, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Style Settings", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocPerm", - "name": "__common__", - "parent": "Style Settings", - "parentfield": "permissions", - "parenttype": "DocType", - "read": 1, - "report": 0, - "role": "Website Manager", - "submit": 0 - }, - { - "doctype": "DocType", - "name": "Style Settings" - }, - { - "doctype": "DocField", - "fieldname": "color", - "fieldtype": "Section Break", - "label": "Color" - }, - { - "description": "If image is selected, color will be ignored (attach first)", - "doctype": "DocField", - "fieldname": "background_image", - "fieldtype": "Select", - "label": "Background Image", - "options": "attach_files:" - }, - { - "description": "Solid background color (default light gray)", - "doctype": "DocField", - "fieldname": "background_color", - "fieldtype": "Data", - "label": "Background Color" - }, - { - "doctype": "DocField", - "fieldname": "page_background", - "fieldtype": "Data", - "label": "Page Background" - }, - { - "doctype": "DocField", - "fieldname": "page_border", - "fieldtype": "Check", - "label": "Page Border" - }, - { - "doctype": "DocField", - "fieldname": "page_headings", - "fieldtype": "Data", - "label": "Page Headings" - }, - { - "doctype": "DocField", - "fieldname": "page_text", - "fieldtype": "Data", - "label": "Page Text" - }, - { - "doctype": "DocField", - "fieldname": "page_links", - "fieldtype": "Data", - "label": "Page Links" - }, - { - "doctype": "DocField", - "fieldname": "cb0", - "fieldtype": "Column Break", - "label": "Top Bar", - "print_width": "50%", - "width": "50%" - }, - { - "doctype": "DocField", - "fieldname": "top_bar_background", - "fieldtype": "Data", - "label": "Top Bar Background" - }, - { - "description": "000 is black, fff is white", - "doctype": "DocField", - "fieldname": "top_bar_foreground", - "fieldtype": "Data", - "label": "Top Bar Text" - }, - { - "doctype": "DocField", - "fieldname": "fonts", - "fieldtype": "Section Break", - "label": "Fonts" - }, - { - "doctype": "DocField", - "fieldname": "heading_font", - "fieldtype": "Select", - "label": "Font (Heading)", - "options": "\nHelvetica Neue\nLucida Grande\nVerdana\nArial\nGeorgia\nTahoma\nLato\nOpen Sans" - }, - { - "doctype": "DocField", - "fieldname": "font", - "fieldtype": "Select", - "label": "Font (Text)", - "options": "\nHelvetica Neue\nLucida Grande\nVerdana\nArial\nGeorgia\nTahoma" - }, - { - "doctype": "DocField", - "fieldname": "font_size", - "fieldtype": "Select", - "label": "Font Size (Text)", - "options": "\n12px\n13px\n14px\n15px\n16px\n17px\n18px" - }, - { - "doctype": "DocField", - "fieldname": "heading_text_as", - "fieldtype": "Select", - "label": "Heading Text As", - "options": "\nUPPERCASE\nTitle Case\nlowercase" - }, - { - "doctype": "DocField", - "fieldname": "column_break_13", - "fieldtype": "Column Break" - }, - { - "description": "Add the name of Google Web Font e.g. \"Open Sans\"", - "doctype": "DocField", - "fieldname": "google_web_font_for_heading", - "fieldtype": "Data", - "label": "Google Web Font (Heading)" - }, - { - "description": "Add the name of Google Web Font e.g. \"Open Sans\"", - "doctype": "DocField", - "fieldname": "google_web_font_for_text", - "fieldtype": "Data", - "label": "Google Web Font (Text)" - }, - { - "doctype": "DocField", - "fieldname": "css", - "fieldtype": "Section Break", - "label": "CSS" - }, - { - "description": "add your own CSS (careful!)", - "doctype": "DocField", - "fieldname": "add_css", - "fieldtype": "Code", - "label": "Add CSS" - }, - { - "description": "Auto generated", - "doctype": "DocField", - "fieldname": "custom_css", - "fieldtype": "Code", - "label": "Custom CSS", - "read_only": 1 - }, - { - "create": 1, - "doctype": "DocPerm", - "email": 1, - "permlevel": 0, - "print": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 1 - } -] \ No newline at end of file diff --git a/frappe/website/doctype/top_bar_item/top_bar_item.json b/frappe/website/doctype/top_bar_item/top_bar_item.json new file mode 100644 index 0000000000..db960600bd --- /dev/null +++ b/frappe/website/doctype/top_bar_item/top_bar_item.json @@ -0,0 +1,50 @@ +{ + "creation": "2013-02-22 01:28:08.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "permlevel": 0, + "print_width": "120px", + "width": "120px" + }, + { + "description": "If you set this, this Item will come in a drop-down under the selected parent.", + "fieldname": "parent_label", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Parent Label", + "permlevel": 0 + }, + { + "description": "Link to the page you want to open", + "fieldname": "url", + "fieldtype": "Data", + "in_list_view": 1, + "label": "URL", + "permlevel": 0, + "print_width": "200px", + "width": "200px" + }, + { + "description": "Select target = \"_blank\" to open in a new page.", + "fieldname": "target", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Target", + "options": "\ntarget = \"_blank\"", + "permlevel": 0 + } + ], + "idx": 1, + "istable": 1, + "modified": "2013-12-20 19:21:53.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Top Bar Item", + "owner": "Administrator" +} \ No newline at end of file diff --git a/frappe/website/doctype/top_bar_item/top_bar_item.py b/frappe/website/doctype/top_bar_item/top_bar_item.py index a48f49f2a7..5e8cd7c469 100644 --- a/frappe/website/doctype/top_bar_item/top_bar_item.py +++ b/frappe/website/doctype/top_bar_item/top_bar_item.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class TopBarItem(Document): + pass \ No newline at end of file diff --git a/frappe/website/doctype/top_bar_item/top_bar_item.txt b/frappe/website/doctype/top_bar_item/top_bar_item.txt deleted file mode 100644 index e5ee3aeaca..0000000000 --- a/frappe/website/doctype/top_bar_item/top_bar_item.txt +++ /dev/null @@ -1,60 +0,0 @@ -[ - { - "creation": "2013-02-22 01:28:08", - "docstatus": 0, - "modified": "2013-12-20 19:21:53", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "DocType", - "istable": 1, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "in_list_view": 1, - "name": "__common__", - "parent": "Top Bar Item", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocType", - "name": "Top Bar Item" - }, - { - "doctype": "DocField", - "fieldname": "label", - "fieldtype": "Data", - "label": "Label", - "print_width": "120px", - "width": "120px" - }, - { - "description": "If you set this, this Item will come in a drop-down under the selected parent.", - "doctype": "DocField", - "fieldname": "parent_label", - "fieldtype": "Select", - "label": "Parent Label" - }, - { - "description": "Link to the page you want to open", - "doctype": "DocField", - "fieldname": "url", - "fieldtype": "Data", - "label": "URL", - "print_width": "200px", - "width": "200px" - }, - { - "description": "Select target = \"_blank\" to open in a new page.", - "doctype": "DocField", - "fieldname": "target", - "fieldtype": "Select", - "label": "Target", - "options": "\ntarget = \"_blank\"" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/user_vote/user_vote.json b/frappe/website/doctype/user_vote/user_vote.json new file mode 100644 index 0000000000..31ab141701 --- /dev/null +++ b/frappe/website/doctype/user_vote/user_vote.json @@ -0,0 +1,43 @@ +{ + "autoname": "_VOTE.#######", + "creation": "2014-01-17 16:26:21.000000", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Transaction", + "fields": [ + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "label": "Ref DocType", + "options": "DocType", + "permlevel": 0, + "search_index": 0 + }, + { + "fieldname": "ref_name", + "fieldtype": "Data", + "label": "Ref Name", + "permlevel": 0, + "search_index": 0 + } + ], + "idx": 1, + "modified": "2014-01-21 15:45:30.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "User Vote", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "export": 1, + "permlevel": 0, + "read": 1, + "report": 1, + "role": "System Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/user_vote/user_vote.py b/frappe/website/doctype/user_vote/user_vote.py index 79b8334305..b0a333534e 100644 --- a/frappe/website/doctype/user_vote/user_vote.py +++ b/frappe/website/doctype/user_vote/user_vote.py @@ -5,15 +5,15 @@ from __future__ import unicode_literals import frappe from frappe.website.permissions import get_access -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class UserVote(Document): def validate(self): # if new - if self.doc.fields.get("__islocal"): - if frappe.db.get_value("User Vote", {"ref_doctype": self.doc.ref_doctype, - "ref_name": self.doc.ref_name, "owner": frappe.session.user}): + if self.get("__islocal"): + if frappe.db.get_value("User Vote", {"ref_doctype": self.ref_doctype, + "ref_name": self.ref_name, "owner": frappe.session.user}): raise frappe.DuplicateEntryError @@ -25,8 +25,8 @@ class DocType: def update_ref_count(self, cnt=0): count = frappe.db.sql("""select count(*) from `tabUser Vote` where ref_doctype=%s and ref_name=%s""", - (self.doc.ref_doctype, self.doc.ref_name))[0][0] - frappe.db.set_value(self.doc.ref_doctype, self.doc.ref_name, "upvotes", count + cnt) + (self.ref_doctype, self.ref_name))[0][0] + frappe.db.set_value(self.ref_doctype, self.ref_name, "upvotes", count + cnt) def on_doctype_update(): frappe.db.add_index("User Vote", ["ref_doctype", "ref_name"]) @@ -42,7 +42,7 @@ def set_vote(ref_doctype, ref_name): raise frappe.PermissionError try: - user_vote = frappe.bean({ + user_vote = frappe.get_doc({ "doctype": "User Vote", "ref_doctype": ref_doctype, "ref_name": ref_name diff --git a/frappe/website/doctype/user_vote/user_vote.txt b/frappe/website/doctype/user_vote/user_vote.txt deleted file mode 100644 index 86bbc7bc02..0000000000 --- a/frappe/website/doctype/user_vote/user_vote.txt +++ /dev/null @@ -1,61 +0,0 @@ -[ - { - "creation": "2014-01-17 16:26:21", - "docstatus": 0, - "modified": "2014-01-21 15:45:30", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "_VOTE.#######", - "doctype": "DocType", - "document_type": "Transaction", - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "User Vote", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0, - "search_index": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "export": 1, - "name": "__common__", - "parent": "User Vote", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "read": 1, - "report": 1, - "role": "System Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "User Vote" - }, - { - "doctype": "DocField", - "fieldname": "ref_doctype", - "fieldtype": "Link", - "label": "Ref DocType", - "options": "DocType" - }, - { - "doctype": "DocField", - "fieldname": "ref_name", - "fieldtype": "Data", - "label": "Ref Name" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/web_page/test_records.json b/frappe/website/doctype/web_page/test_records.json new file mode 100644 index 0000000000..dcbd51c6c8 --- /dev/null +++ b/frappe/website/doctype/web_page/test_records.json @@ -0,0 +1,35 @@ +[ + { + "doctype": "Web Page", + "main_section": "Test Content 1", + "published": 1, + "title": "Test Web Page 1" + }, + { + "doctype": "Web Page", + "main_section": "Test Content 2", + "parent_website_route": "test-web-page-1", + "published": 1, + "title": "Test Web Page 2" + }, + { + "doctype": "Web Page", + "main_section": "Test Content 3", + "parent_website_route": "test-web-page-1", + "published": 1, + "title": "Test Web Page 3" + }, + { + "doctype": "Web Page", + "main_section": "Test Content 4", + "published": 1, + "title": "Test Web Page 4" + }, + { + "doctype": "Web Page", + "main_section": "Test Content 5", + "parent_website_route": "test-web-page-1", + "published": 1, + "title": "Test Web Page 5" + } +] \ No newline at end of file diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index aeec26a454..234255e6fa 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -2,41 +2,7 @@ from __future__ import unicode_literals import unittest import frappe -test_records = [ - [{ - "doctype": "Web Page", - "title": "Test Web Page 1", - "main_section": "Test Content 1", - "published": 1 - }], - [{ - "doctype": "Web Page", - "title": "Test Web Page 2", - "main_section": "Test Content 2", - "published": 1, - "parent_website_route": "test-web-page-1" - }], - [{ - "doctype": "Web Page", - "title": "Test Web Page 3", - "main_section": "Test Content 3", - "published": 1, - "parent_website_route": "test-web-page-1" - }], - [{ - "doctype": "Web Page", - "title": "Test Web Page 4", - "main_section": "Test Content 4", - "published": 1, - }], - [{ - "doctype": "Web Page", - "title": "Test Web Page 5", - "main_section": "Test Content 5", - "published": 1, - "parent_website_route": "test-web-page-1" - }], -] +test_records = frappe.get_test_records('Web Page') class TestWebPage(unittest.TestCase): def test_check_sitemap(self): @@ -60,15 +26,15 @@ class TestWebPage(unittest.TestCase): {"ref_doctype":"Web Page", "docname": "test-web-page-5"}, 'idx'), 2) def test_check_rename(self): - web_page = frappe.bean("Web Page", "test-web-page-1") - web_page.doc.parent_website_route = "test-web-page-4" + web_page = frappe.get_doc("Web Page", "test-web-page-1") + web_page.parent_website_route = "test-web-page-4" web_page.save() self.assertEquals(frappe.db.get_value("Website Route", {"ref_doctype":"Web Page", "docname": "test-web-page-2"}), "test-web-page-4/test-web-page-1/test-web-page-2") - web_page.doc.parent_website_route = "" + web_page.parent_website_route = "" web_page.save() self.assertEquals(frappe.db.get_value("Website Route", @@ -76,8 +42,8 @@ class TestWebPage(unittest.TestCase): "test-web-page-1/test-web-page-2") def test_check_move(self): - web_page = frappe.bean("Web Page", "test-web-page-3") - web_page.doc.parent_website_route = "test-web-page-4" + web_page = frappe.get_doc("Web Page", "test-web-page-3") + web_page.parent_website_route = "test-web-page-4" web_page.save() self.assertEquals(frappe.db.get_value("Website Route", @@ -89,9 +55,6 @@ class TestWebPage(unittest.TestCase): self.assertEquals(frappe.db.get_value("Website Route", {"ref_doctype":"Web Page", "docname": "test-web-page-5"}, 'idx'), 1) - web_page = frappe.bean("Web Page", "test-web-page-3") - web_page.doc.parent_website_route = "test-web-page-1" - web_page.save() - - - \ No newline at end of file + web_page = frappe.get_doc("Web Page", "test-web-page-3") + web_page.parent_website_route = "test-web-page-1" + web_page.save() \ No newline at end of file diff --git a/frappe/website/doctype/web_page/web_page.json b/frappe/website/doctype/web_page/web_page.json new file mode 100644 index 0000000000..02f5dc7e84 --- /dev/null +++ b/frappe/website/doctype/web_page/web_page.json @@ -0,0 +1,166 @@ +{ + "allow_attach": 1, + "creation": "2013-03-28 10:35:30.000000", + "description": "Page to show on the website\n", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Transaction", + "fields": [ + { + "fieldname": "section_title", + "fieldtype": "Section Break", + "label": "Title", + "permlevel": 0 + }, + { + "description": "Title / headline of your page", + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "permlevel": 0, + "reqd": 1 + }, + { + "description": "Page url name (auto-generated)", + "fieldname": "page_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Page Name", + "permlevel": 0, + "read_only": 0 + }, + { + "fieldname": "parent_website_route", + "fieldtype": "Link", + "label": "Parent Website Page", + "options": "Website Route", + "permlevel": 0 + }, + { + "fieldname": "published", + "fieldtype": "Check", + "label": "Published", + "permlevel": 0 + }, + { + "fieldname": "cb1", + "fieldtype": "Column Break", + "permlevel": 0, + "width": "50%" + }, + { + "description": "Description for page header.", + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "permlevel": 0 + }, + { + "description": "Page content", + "fieldname": "sb1", + "fieldtype": "Section Break", + "label": "Content", + "permlevel": 0 + }, + { + "description": "Begin this page with a slideshow of images", + "fieldname": "slideshow", + "fieldtype": "Link", + "label": "Slideshow", + "options": "Website Slideshow", + "permlevel": 0 + }, + { + "description": "Content in markdown format that appears on the main side of your page", + "fieldname": "main_section", + "fieldtype": "Text Editor", + "label": "Main Section", + "permlevel": 0 + }, + { + "depends_on": "eval:!doc.__islocal", + "description": "Link to other pages in the side bar and next section", + "fieldname": "sb2", + "fieldtype": "Section Break", + "label": "More", + "permlevel": 0 + }, + { + "fieldname": "enable_comments", + "fieldtype": "Check", + "label": "Enable Comments", + "permlevel": 0 + }, + { + "fieldname": "text_align", + "fieldtype": "Select", + "label": "Text Align", + "options": "Left\nCenter\nRight", + "permlevel": 0 + }, + { + "fieldname": "custom_javascript", + "fieldtype": "Section Break", + "label": "Custom Javascript", + "permlevel": 0 + }, + { + "description": "Add code as <script>", + "fieldname": "insert_code", + "fieldtype": "Check", + "label": "Insert Code", + "permlevel": 0 + }, + { + "depends_on": "insert_code", + "fieldname": "javascript", + "fieldtype": "Code", + "label": "Javascript", + "options": "Javascript", + "permlevel": 0 + }, + { + "fieldname": "custom_css", + "fieldtype": "Section Break", + "label": "Custom CSS", + "permlevel": 0 + }, + { + "fieldname": "insert_style", + "fieldtype": "Check", + "label": "Insert Style", + "permlevel": 0 + }, + { + "depends_on": "insert_style", + "fieldname": "css", + "fieldtype": "Code", + "label": "CSS", + "options": "CSS", + "permlevel": 0 + } + ], + "icon": "icon-file-alt", + "idx": 1, + "max_attachments": 20, + "modified": "2014-02-28 14:24:14.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Web Page", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Website Manager", + "submit": 0, + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/web_page/web_page.py b/frappe/website/doctype/web_page/web_page.py index 550605e67a..7ec632b776 100644 --- a/frappe/website/doctype/web_page/web_page.py +++ b/frappe/website/doctype/web_page/web_page.py @@ -7,7 +7,5 @@ from frappe.website.website_generator import WebsiteGenerator from frappe.website.utils import cleanup_page_name from frappe.utils import cint -class DocType(WebsiteGenerator): - def __init__(self, doc, doclist): - self.doc, self.doclist = doc, doclist - self.save_versions = True \ No newline at end of file +class WebPage(WebsiteGenerator): + save_versions = True diff --git a/frappe/website/doctype/web_page/web_page.txt b/frappe/website/doctype/web_page/web_page.txt deleted file mode 100644 index 774e2ddccb..0000000000 --- a/frappe/website/doctype/web_page/web_page.txt +++ /dev/null @@ -1,185 +0,0 @@ -[ - { - "creation": "2013-03-28 10:35:30", - "docstatus": 0, - "modified": "2014-02-28 14:24:14", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 1, - "description": "Page to show on the website\n", - "doctype": "DocType", - "document_type": "Transaction", - "icon": "icon-file-alt", - "max_attachments": 20, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Web Page", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Web Page", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Website Manager", - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Web Page" - }, - { - "doctype": "DocField", - "fieldname": "section_title", - "fieldtype": "Section Break", - "label": "Title" - }, - { - "description": "Title / headline of your page", - "doctype": "DocField", - "fieldname": "title", - "fieldtype": "Data", - "label": "Title", - "reqd": 1 - }, - { - "description": "Page url name (auto-generated)", - "doctype": "DocField", - "fieldname": "page_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Page Name", - "read_only": 0 - }, - { - "doctype": "DocField", - "fieldname": "parent_website_route", - "fieldtype": "Link", - "label": "Parent Website Page", - "options": "Website Route" - }, - { - "doctype": "DocField", - "fieldname": "published", - "fieldtype": "Check", - "label": "Published" - }, - { - "doctype": "DocField", - "fieldname": "cb1", - "fieldtype": "Column Break", - "width": "50%" - }, - { - "description": "Description for page header.", - "doctype": "DocField", - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Description" - }, - { - "description": "Page content", - "doctype": "DocField", - "fieldname": "sb1", - "fieldtype": "Section Break", - "label": "Content" - }, - { - "description": "Begin this page with a slideshow of images", - "doctype": "DocField", - "fieldname": "slideshow", - "fieldtype": "Link", - "label": "Slideshow", - "options": "Website Slideshow" - }, - { - "description": "Content in markdown format that appears on the main side of your page", - "doctype": "DocField", - "fieldname": "main_section", - "fieldtype": "Text Editor", - "label": "Main Section" - }, - { - "depends_on": "eval:!doc.__islocal", - "description": "Link to other pages in the side bar and next section", - "doctype": "DocField", - "fieldname": "sb2", - "fieldtype": "Section Break", - "label": "More" - }, - { - "doctype": "DocField", - "fieldname": "enable_comments", - "fieldtype": "Check", - "label": "Enable Comments" - }, - { - "doctype": "DocField", - "fieldname": "text_align", - "fieldtype": "Select", - "label": "Text Align", - "options": "Left\nCenter\nRight" - }, - { - "doctype": "DocField", - "fieldname": "custom_javascript", - "fieldtype": "Section Break", - "label": "Custom Javascript" - }, - { - "description": "Add code as <script>", - "doctype": "DocField", - "fieldname": "insert_code", - "fieldtype": "Check", - "label": "Insert Code" - }, - { - "depends_on": "insert_code", - "doctype": "DocField", - "fieldname": "javascript", - "fieldtype": "Code", - "label": "Javascript", - "options": "Javascript" - }, - { - "doctype": "DocField", - "fieldname": "custom_css", - "fieldtype": "Section Break", - "label": "Custom CSS" - }, - { - "doctype": "DocField", - "fieldname": "insert_style", - "fieldtype": "Check", - "label": "Insert Style" - }, - { - "depends_on": "insert_style", - "doctype": "DocField", - "fieldname": "css", - "fieldtype": "Code", - "label": "CSS", - "options": "CSS" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/website_group/website_group.json b/frappe/website/doctype/website_group/website_group.json new file mode 100644 index 0000000000..3433dfab38 --- /dev/null +++ b/frappe/website/doctype/website_group/website_group.json @@ -0,0 +1,123 @@ +{ + "creation": "2014-01-29 15:57:42.000000", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Master", + "fields": [ + { + "fieldname": "name_and_description", + "fieldtype": "Section Break", + "label": "Name and Description", + "permlevel": 0 + }, + { + "fieldname": "group_name", + "fieldtype": "Data", + "label": "Group Name", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "group_title", + "fieldtype": "Data", + "label": "Group Title", + "no_copy": 0, + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "group_type", + "fieldtype": "Select", + "label": "Group Type", + "options": "Forum\nTasks\nEvents", + "permlevel": 0, + "reqd": 1 + }, + { + "description": "Display in the sidebar of this Website Route node", + "fieldname": "parent_website_route", + "fieldtype": "Link", + "in_list_view": 0, + "label": "Parent Website Page", + "options": "Website Route", + "permlevel": 0 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "group_description", + "fieldtype": "Text", + "label": "Group Description", + "permlevel": 0 + }, + { + "fieldname": "page_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Page Name", + "no_copy": 1, + "permlevel": 0, + "reqd": 0 + }, + { + "fieldname": "details", + "fieldtype": "Section Break", + "label": "Details", + "permlevel": 0 + }, + { + "fieldname": "public_read", + "fieldtype": "Check", + "label": "Anyone Can Read", + "permlevel": 0 + }, + { + "fieldname": "public_write", + "fieldtype": "Check", + "label": "Anyone Can Write", + "permlevel": 0 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "upvotes", + "fieldtype": "Int", + "label": "Upvotes", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "replies", + "fieldtype": "Int", + "label": "Replies", + "permlevel": 0, + "read_only": 1 + } + ], + "icon": "icon-group", + "idx": 1, + "modified": "2014-03-04 09:36:30.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Website Group", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "export": 1, + "permlevel": 0, + "read": 1, + "report": 1, + "role": "Website Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/website_group/website_group.py b/frappe/website/doctype/website_group/website_group.py index 3068d3984f..c1ef08dd63 100644 --- a/frappe/website/doctype/website_group/website_group.py +++ b/frappe/website/doctype/website_group/website_group.py @@ -5,18 +5,16 @@ from __future__ import unicode_literals import frappe from frappe.website.website_generator import WebsiteGenerator from frappe.templates.generators.website_group import clear_cache -from frappe.model.doc import make_autoname +from frappe.model.naming import make_autoname -class DocType(WebsiteGenerator): - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +class WebsiteGroup(WebsiteGenerator): def get_page_title(self): - return self.doc.group_title + return self.group_title def on_update(self): WebsiteGenerator.on_update(self) - clear_cache(website_group=self.doc.name) + clear_cache(website_group=self.name) def after_insert(self): - clear_cache(path=self.doc.parent_website_route) + clear_cache(path=self.parent_website_route) diff --git a/frappe/website/doctype/website_group/website_group.txt b/frappe/website/doctype/website_group/website_group.txt deleted file mode 100644 index 65e4942638..0000000000 --- a/frappe/website/doctype/website_group/website_group.txt +++ /dev/null @@ -1,142 +0,0 @@ -[ - { - "creation": "2014-01-29 15:57:42", - "docstatus": 0, - "modified": "2014-03-04 09:36:30", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "DocType", - "document_type": "Master", - "icon": "icon-group", - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Website Group", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "export": 1, - "name": "__common__", - "parent": "Website Group", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "read": 1, - "report": 1, - "role": "Website Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Website Group" - }, - { - "doctype": "DocField", - "fieldname": "name_and_description", - "fieldtype": "Section Break", - "label": "Name and Description" - }, - { - "doctype": "DocField", - "fieldname": "group_name", - "fieldtype": "Data", - "label": "Group Name", - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "group_title", - "fieldtype": "Data", - "label": "Group Title", - "no_copy": 0, - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "group_type", - "fieldtype": "Select", - "label": "Group Type", - "options": "Forum\nTasks\nEvents", - "reqd": 1 - }, - { - "description": "Display in the sidebar of this Website Route node", - "doctype": "DocField", - "fieldname": "parent_website_route", - "fieldtype": "Link", - "in_list_view": 0, - "label": "Parent Website Page", - "options": "Website Route" - }, - { - "doctype": "DocField", - "fieldname": "column_break_6", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "group_description", - "fieldtype": "Text", - "label": "Group Description" - }, - { - "doctype": "DocField", - "fieldname": "page_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Page Name", - "no_copy": 1, - "reqd": 0 - }, - { - "doctype": "DocField", - "fieldname": "details", - "fieldtype": "Section Break", - "label": "Details" - }, - { - "doctype": "DocField", - "fieldname": "public_read", - "fieldtype": "Check", - "label": "Anyone Can Read" - }, - { - "doctype": "DocField", - "fieldname": "public_write", - "fieldtype": "Check", - "label": "Anyone Can Write" - }, - { - "doctype": "DocField", - "fieldname": "column_break_12", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "upvotes", - "fieldtype": "Int", - "label": "Upvotes", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "replies", - "fieldtype": "Int", - "label": "Replies", - "read_only": 1 - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/website_route/website_route.json b/frappe/website/doctype/website_route/website_route.json new file mode 100644 index 0000000000..ee3c43c462 --- /dev/null +++ b/frappe/website/doctype/website_route/website_route.json @@ -0,0 +1,132 @@ +{ + "allow_rename": 1, + "autoname": "field:page_name", + "creation": "2013-11-18 15:38:40.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "page_or_generator", + "fieldtype": "Select", + "label": "Page or Generator", + "options": "Page\nGenerator", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "page_name", + "fieldtype": "Data", + "label": "Page Name", + "permlevel": 0, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "page_title", + "fieldtype": "Data", + "label": "Page Title", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "label": "Ref DocType", + "options": "DocType", + "permlevel": 0, + "reqd": 0 + }, + { + "fieldname": "docname", + "fieldtype": "Data", + "label": "Docname", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "lastmod", + "fieldtype": "Data", + "label": "Lastmod", + "permlevel": 0 + }, + { + "fieldname": "website_template", + "fieldtype": "Link", + "label": "Website Template", + "options": "Website Template", + "permlevel": 0 + }, + { + "fieldname": "parent_website_route", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Parent Website Sitemap", + "options": "Website Route", + "permlevel": 0, + "search_index": 1 + }, + { + "fieldname": "lft", + "fieldtype": "Int", + "hidden": 1, + "label": "lft", + "permlevel": 0, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "rgt", + "fieldtype": "Int", + "hidden": 1, + "in_list_view": 0, + "label": "rgt", + "permlevel": 0, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "old_parent", + "fieldtype": "Data", + "hidden": 1, + "label": "Old Parent", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "public_read", + "fieldtype": "Check", + "label": "Anyone Can Read", + "permlevel": 0 + }, + { + "fieldname": "public_write", + "fieldtype": "Check", + "label": "Anyone Can Write", + "permlevel": 0 + }, + { + "fieldname": "static_file_timestamp", + "fieldtype": "Data", + "label": "Static File Timestamp", + "permlevel": 0 + } + ], + "idx": 1, + "modified": "2014-02-24 12:46:59.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Website Route", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 0, + "delete": 1, + "permlevel": 0, + "read": 1, + "report": 1, + "role": "Website Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/website_route/website_route.py b/frappe/website/doctype/website_route/website_route.py index 1498898565..79818db01f 100644 --- a/frappe/website/doctype/website_route/website_route.py +++ b/frappe/website/doctype/website_route/website_route.py @@ -5,85 +5,83 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import cint -from frappe.utils.nestedset import DocTypeNestedSet +from frappe.utils.nestedset import NestedSet sitemap_fields = ("page_name", "ref_doctype", "docname", "page_or_generator", "idx", "lastmod", "parent_website_route", "public_read", "public_write", "page_title") -class DocType(DocTypeNestedSet): - def __init__(self, d, dl): - self.doc, self.doclist = d, dl - self.nsm_parent_field = "parent_website_route" +class WebsiteRoute(NestedSet): + nsm_parent_field = "parent_website_route" def autoname(self): - self.doc.name = self.get_url() + self.name = self.get_url() def get_url(self): - url = self.doc.page_name - if self.doc.parent_website_route: - url = self.doc.parent_website_route + "/" + url + url = self.page_name + if self.parent_website_route: + url = self.parent_website_route + "/" + url return url def validate(self): - if self.get_url() != self.doc.name: + if self.get_url() != self.name: self.rename() self.check_if_page_name_is_unique() self.make_private_if_parent_is_private() - if not self.doc.is_new(): + if not self.is_new(): self.renumber_if_moved() self.set_idx() def renumber_if_moved(self): - current_parent = frappe.db.get_value("Website Route", self.doc.name, "parent_website_route") - if current_parent and current_parent != self.doc.parent_website_route: + current_parent = frappe.db.get_value("Website Route", self.name, "parent_website_route") + if current_parent and current_parent != self.parent_website_route: # move-up # sitemap frappe.db.sql("""update `tabWebsite Route` set idx=idx-1 - where parent_website_route=%s and idx>%s""", (current_parent, self.doc.idx)) + where parent_website_route=%s and idx>%s""", (current_parent, self.idx)) # source table frappe.db.sql("""update `tab{0}` set idx=idx-1 - where parent_website_route=%s and idx>%s""".format(self.doc.ref_doctype), - (current_parent, self.doc.idx)) - self.doc.idx = None + where parent_website_route=%s and idx>%s""".format(self.ref_doctype), + (current_parent, self.idx)) + self.idx = None def on_update(self): if not frappe.flags.in_rebuild_config: - DocTypeNestedSet.on_update(self) + NestedSet.on_update(self) self.clear_cache() def set_idx(self): - if self.doc.parent_website_route: - if self.doc.idx == None: + if self.parent_website_route: + if self.idx == None: self.set_idx_as_last() else: self.validate_previous_idx_exists() def set_idx_as_last(self): # new, append - self.doc.idx = int(frappe.db.sql("""select ifnull(max(ifnull(idx, -1)), -1) + self.idx = int(frappe.db.sql("""select ifnull(max(ifnull(idx, -1)), -1) from `tabWebsite Route` where ifnull(parent_website_route, '')=%s and name!=%s""", - (self.doc.parent_website_route or '', - self.doc.name))[0][0]) + 1 + (self.parent_website_route or '', + self.name))[0][0]) + 1 def validate_previous_idx_exists(self): - self.doc.idx = cint(self.doc.idx) + self.idx = cint(self.idx) previous_idx = frappe.db.sql("""select max(idx) from `tab{}` where ifnull(parent_website_route, '')=%s - and ifnull(idx, -1) < %s""".format(self.doc.ref_doctype), - (self.doc.parent_website_route, self.doc.idx))[0][0] + and ifnull(idx, -1) < %s""".format(self.ref_doctype), + (self.parent_website_route, self.idx))[0][0] - if previous_idx and previous_idx != self.doc.idx - 1: + if previous_idx and previous_idx != self.idx - 1: frappe.throw("{}: {}, {}".format( - _("Sitemap Ordering Error. Index missing"), self.doc.name, self.doc.idx-1)) + _("Sitemap Ordering Error. Index missing"), self.name, self.idx-1)) def rename(self): - self.old_name = self.doc.name - self.doc.name = self.get_url() + self.old_name = self.name + self.name = self.get_url() frappe.db.sql("""update `tabWebsite Route` set name=%s where name=%s""", - (self.doc.name, self.old_name)) + (self.name, self.old_name)) self.rename_links() self.rename_descendants() self.clear_cache(self.old_name) @@ -93,80 +91,80 @@ class DocType(DocTypeNestedSet): fieldname='parent_website_route' and options='Website Route'"""): for name in frappe.db.sql_list("""select name from `tab{}` where parent_website_route=%s""".format(doctype), self.old_name): - frappe.db.set_value(doctype, name, "parent_website_route", self.doc.name) + frappe.db.set_value(doctype, name, "parent_website_route", self.name) def rename_descendants(self): # rename children for name in frappe.db.sql_list("""select name from `tabWebsite Route` - where parent_website_route=%s""", self.doc.name): - child = frappe.bean("Website Route", name) - child.doc.parent_website_route = self.doc.name + where parent_website_route=%s""", self.name): + child = frappe.get_doc("Website Route", name) + child.parent_website_route = self.name child.save() def check_if_page_name_is_unique(self): exists = False - if self.doc.page_or_generator == "Page": + if self.page_or_generator == "Page": # for a page, name and website sitemap config form a unique key exists = frappe.db.sql("""select name from `tabWebsite Route` - where name=%s and website_template!=%s""", (self.doc.name, self.doc.website_template)) + where name=%s and website_template!=%s""", (self.name, self.website_template)) else: # for a generator, name, ref_doctype and docname make a unique key exists = frappe.db.sql("""select name from `tabWebsite Route` where name=%s and (ifnull(ref_doctype, '')!=%s or ifnull(docname, '')!=%s)""", - (self.doc.name, self.doc.ref_doctype, self.doc.docname)) + (self.name, self.ref_doctype, self.docname)) if exists: frappe.throw("{}: {}. {}.".format(_("A Website Page already exists with the Page Name"), - self.doc.name, _("Please change it to continue"))) + self.name, _("Please change it to continue"))) def make_private_if_parent_is_private(self): - if self.doc.parent_website_route: - parent_pubic_read = frappe.db.get_value("Website Route", self.doc.parent_website_route, + if self.parent_website_route: + parent_pubic_read = frappe.db.get_value("Website Route", self.parent_website_route, "public_read") if not parent_pubic_read: - self.doc.public_read = self.doc.public_write = 0 + self.public_read = self.public_write = 0 def on_trash(self): # remove website sitemap permissions to_remove = frappe.db.sql_list("""select name from `tabWebsite Route Permission` - where website_route=%s""", (self.doc.name,)) + where website_route=%s""", (self.name,)) frappe.delete_doc("Website Route Permission", to_remove, ignore_permissions=True) self.clear_cache() def clear_cache(self, name=None): from frappe.website.render import clear_cache - clear_cache(name or self.doc.name) - if self.doc.parent_website_route: - clear_cache(self.doc.parent_website_route) + clear_cache(name or self.name) + if self.parent_website_route: + clear_cache(self.parent_website_route) def add_to_sitemap(options): - bean = frappe.new_bean("Website Route") + website_route = frappe.new_doc("Website Route") for key in sitemap_fields: - bean.doc.fields[key] = options.get(key) - if not bean.doc.page_name: - bean.doc.page_name = options.get("link_name") - bean.doc.website_template = options.get("link_name") + website_route.set(key, options.get(key)) + if not website_route.page_name: + website_route.page_name = options.get("link_name") + website_route.website_template = options.get("link_name") - bean.insert(ignore_permissions=True) + website_route.insert(ignore_permissions=True) - return bean.doc.idx + return website_route.idx def update_sitemap(website_route, options): - bean = frappe.bean("Website Route", website_route) + website_route = frappe.get_doc("Website Route", website_route) for key in sitemap_fields: - bean.doc.fields[key] = options.get(key) + website_route.set(key, options.get(key)) - if not bean.doc.page_name: + if not website_route.page_name: # for pages - bean.doc.page_name = options.get("link_name") + website_route.page_name = options.get("link_name") - bean.doc.website_template = options.get("link_name") - bean.save(ignore_permissions=True) + website_route.website_template = options.get("link_name") + website_route.save(ignore_permissions=True) - return bean.doc.idx + return website_route.idx def remove_sitemap(page_name=None, ref_doctype=None, docname=None): if page_name: diff --git a/frappe/website/doctype/website_route/website_route.txt b/frappe/website/doctype/website_route/website_route.txt deleted file mode 100644 index 65f3f4e9e1..0000000000 --- a/frappe/website/doctype/website_route/website_route.txt +++ /dev/null @@ -1,151 +0,0 @@ -[ - { - "creation": "2013-11-18 15:38:40", - "docstatus": 0, - "modified": "2014-02-24 12:46:59", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_rename": 1, - "autoname": "field:page_name", - "doctype": "DocType", - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Website Route", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 0, - "delete": 1, - "doctype": "DocPerm", - "name": "__common__", - "parent": "Website Route", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "read": 1, - "report": 1, - "role": "Website Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Website Route" - }, - { - "doctype": "DocField", - "fieldname": "page_or_generator", - "fieldtype": "Select", - "label": "Page or Generator", - "options": "Page\nGenerator", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "page_name", - "fieldtype": "Data", - "label": "Page Name", - "read_only": 1, - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "page_title", - "fieldtype": "Data", - "label": "Page Title", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "ref_doctype", - "fieldtype": "Link", - "label": "Ref DocType", - "options": "DocType", - "reqd": 0 - }, - { - "doctype": "DocField", - "fieldname": "docname", - "fieldtype": "Data", - "label": "Docname", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "lastmod", - "fieldtype": "Data", - "label": "Lastmod" - }, - { - "doctype": "DocField", - "fieldname": "website_template", - "fieldtype": "Link", - "label": "Website Template", - "options": "Website Template" - }, - { - "doctype": "DocField", - "fieldname": "parent_website_route", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Parent Website Sitemap", - "options": "Website Route", - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "lft", - "fieldtype": "Int", - "hidden": 1, - "label": "lft", - "read_only": 1, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "rgt", - "fieldtype": "Int", - "hidden": 1, - "in_list_view": 0, - "label": "rgt", - "read_only": 1, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "old_parent", - "fieldtype": "Data", - "hidden": 1, - "label": "Old Parent", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "public_read", - "fieldtype": "Check", - "label": "Anyone Can Read" - }, - { - "doctype": "DocField", - "fieldname": "public_write", - "fieldtype": "Check", - "label": "Anyone Can Write" - }, - { - "doctype": "DocField", - "fieldname": "static_file_timestamp", - "fieldtype": "Data", - "label": "Static File Timestamp" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/website_route_permission/website_route_permission.json b/frappe/website/doctype/website_route_permission/website_route_permission.json new file mode 100644 index 0000000000..fe72366385 --- /dev/null +++ b/frappe/website/doctype/website_route_permission/website_route_permission.json @@ -0,0 +1,70 @@ +{ + "autoname": "WSP.######", + "creation": "2014-01-29 17:56:29.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "website_route", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Website Route", + "options": "Website Route", + "permlevel": 0, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User", + "permlevel": 0, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "read", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Read", + "permlevel": 0 + }, + { + "fieldname": "write", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Write", + "permlevel": 0 + }, + { + "fieldname": "admin", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Admin", + "permlevel": 0 + } + ], + "icon": "icon-shield", + "idx": 1, + "modified": "2014-02-24 13:17:18.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Website Route Permission", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 0, + "export": 1, + "import": 0, + "permlevel": 0, + "read": 1, + "report": 1, + "role": "Website Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/website_route_permission/website_route_permission.py b/frappe/website/doctype/website_route_permission/website_route_permission.py index 3a840cd7df..6986189ecf 100644 --- a/frappe/website/doctype/website_route_permission/website_route_permission.py +++ b/frappe/website/doctype/website_route_permission/website_route_permission.py @@ -6,11 +6,11 @@ import frappe from frappe.website.permissions import remove_empty_permissions, clear_permissions -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class WebsiteRoutePermission(Document): def on_update(self): remove_empty_permissions() - clear_permissions(self.doc.user) + clear_permissions(self.user) \ No newline at end of file diff --git a/frappe/website/doctype/website_route_permission/website_route_permission.txt b/frappe/website/doctype/website_route_permission/website_route_permission.txt deleted file mode 100644 index ee5e3390b7..0000000000 --- a/frappe/website/doctype/website_route_permission/website_route_permission.txt +++ /dev/null @@ -1,85 +0,0 @@ -[ - { - "creation": "2014-01-29 17:56:29", - "docstatus": 0, - "modified": "2014-02-24 13:17:18", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "WSP.######", - "doctype": "DocType", - "icon": "icon-shield", - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "in_list_view": 1, - "name": "__common__", - "parent": "Website Route Permission", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 0, - "doctype": "DocPerm", - "export": 1, - "import": 0, - "name": "__common__", - "parent": "Website Route Permission", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "read": 1, - "report": 1, - "role": "Website Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Website Route Permission" - }, - { - "doctype": "DocField", - "fieldname": "website_route", - "fieldtype": "Link", - "label": "Website Route", - "options": "Website Route", - "reqd": 1, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "user", - "fieldtype": "Link", - "label": "User", - "options": "User", - "reqd": 1, - "search_index": 1 - }, - { - "doctype": "DocField", - "fieldname": "read", - "fieldtype": "Check", - "label": "Read" - }, - { - "doctype": "DocField", - "fieldname": "write", - "fieldtype": "Check", - "label": "Write" - }, - { - "doctype": "DocField", - "fieldname": "admin", - "fieldtype": "Check", - "label": "Admin" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/website_script/website_script.json b/frappe/website/doctype/website_script/website_script.json new file mode 100644 index 0000000000..68cffc71dd --- /dev/null +++ b/frappe/website/doctype/website_script/website_script.json @@ -0,0 +1,35 @@ +{ + "creation": "2012-12-27 11:51:24.000000", + "description": "Script to attach to all web pages.", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Other", + "fields": [ + { + "fieldname": "javascript", + "fieldtype": "Code", + "label": "Javascript", + "options": "Javascript", + "permlevel": 0 + } + ], + "icon": "icon-code", + "idx": 1, + "issingle": 1, + "modified": "2013-12-20 19:21:54.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Website Script", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "role": "Website Manager", + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/website_script/website_script.py b/frappe/website/doctype/website_script/website_script.py index 2fdabfe4c1..a50b91d9b9 100644 --- a/frappe/website/doctype/website_script/website_script.py +++ b/frappe/website/doctype/website_script/website_script.py @@ -6,9 +6,9 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class WebsiteScript(Document): def on_update(self): """clear cache""" diff --git a/frappe/website/doctype/website_script/website_script.txt b/frappe/website/doctype/website_script/website_script.txt deleted file mode 100644 index ce016d020c..0000000000 --- a/frappe/website/doctype/website_script/website_script.txt +++ /dev/null @@ -1,54 +0,0 @@ -[ - { - "creation": "2012-12-27 11:51:24", - "docstatus": 0, - "modified": "2013-12-20 19:21:54", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "description": "Script to attach to all web pages.", - "doctype": "DocType", - "document_type": "Other", - "icon": "icon-code", - "issingle": 1, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "fieldname": "javascript", - "fieldtype": "Code", - "label": "Javascript", - "name": "__common__", - "options": "Javascript", - "parent": "Website Script", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "create": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Website Script", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "role": "Website Manager", - "write": 1 - }, - { - "doctype": "DocType", - "name": "Website Script" - }, - { - "doctype": "DocField" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/website_settings/website_settings.js b/frappe/website/doctype/website_settings/website_settings.js index 4db6bd9e05..aecc9615a1 100644 --- a/frappe/website/doctype/website_settings/website_settings.js +++ b/frappe/website/doctype/website_settings/website_settings.js @@ -31,7 +31,7 @@ $.extend(cur_frm.cscript, { }, label: function(doc, cdt, cdn) { - var item = frappe.model.get_doc(cdt, cdn); + var item = frappe.get_doc(cdt, cdn); if(item.parentfield === "top_bar_items") { this.set_parent_label_options(); } @@ -56,7 +56,7 @@ $.extend(cur_frm.cscript, { // get labels of parent items get_parent_options: function(table_field) { - var items = getchildren('Top Bar Item', cur_frm.doc.name, table_field); + var items = cur_frm.doc[table_field] || []; var main_items = ['']; for(var i in items) { var d = items[i]; diff --git a/frappe/website/doctype/website_settings/website_settings.json b/frappe/website/doctype/website_settings/website_settings.json new file mode 100644 index 0000000000..effd71d009 --- /dev/null +++ b/frappe/website/doctype/website_settings/website_settings.json @@ -0,0 +1,251 @@ +{ + "allow_attach": 1, + "creation": "2013-04-30 12:58:46.000000", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Other", + "fields": [ + { + "fieldname": "sb0", + "fieldtype": "Section Break", + "label": "Landing Page", + "permlevel": 0 + }, + { + "description": "Link that is the website home page. Standard Links (index, login, products, blog, about, contact)", + "fieldname": "home_page", + "fieldtype": "Link", + "label": "Home Page", + "options": "Website Route", + "permlevel": 0, + "reqd": 0 + }, + { + "description": "The name of your company / website as you want to appear on browser title bar. All pages will have this as the prefix to the title.", + "fieldname": "title_prefix", + "fieldtype": "Data", + "label": "Title Prefix", + "permlevel": 0 + }, + { + "fieldname": "cb4", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "description": "If checked, the Home page will be the default Item Group for the website.", + "fieldname": "home_page_is_products", + "fieldtype": "Check", + "label": "Home Page is Products", + "permlevel": 0 + }, + { + "description": "Add a banner to the site. (small banners are usually good)", + "fieldname": "banner", + "fieldtype": "Section Break", + "label": "Banner", + "permlevel": 0 + }, + { + "description": "Select an image of approx width 150px with a transparent background for best results.", + "fieldname": "banner_image", + "fieldtype": "Select", + "label": "Banner Image", + "options": "attach_files:", + "permlevel": 0 + }, + { + "fieldname": "set_banner_from_image", + "fieldtype": "Button", + "label": "Set Banner from Image", + "permlevel": 0 + }, + { + "description": "Banner is above the Top Menu Bar.", + "fieldname": "banner_html", + "fieldtype": "Small Text", + "label": "Banner HTML", + "permlevel": 0 + }, + { + "description": "Menu items in the Top Bar. For setting the color of the Top Bar, go to Style Settings", + "fieldname": "top_bar", + "fieldtype": "Section Break", + "label": "Top Bar", + "permlevel": 0 + }, + { + "description": "Brand is what appears on the top-right of the toolbar. If it is an image, make sure it\nhas a transparent background and use the <img /> tag. Keep size as 200px x 30px", + "fieldname": "brand_html", + "fieldtype": "Small Text", + "label": "Brand HTML", + "permlevel": 0 + }, + { + "fieldname": "top_bar_items", + "fieldtype": "Table", + "label": "Top Bar Items", + "options": "Top Bar Item", + "permlevel": 0 + }, + { + "description": "Sidebar Links for Home Page only", + "fieldname": "sidebar", + "fieldtype": "Section Break", + "label": "Sidebar", + "permlevel": 0 + }, + { + "fieldname": "sidebar_items", + "fieldtype": "Table", + "label": "Sidebar Items", + "options": "Top Bar Item", + "permlevel": 0 + }, + { + "fieldname": "footer", + "fieldtype": "Section Break", + "label": "Footer", + "permlevel": 0 + }, + { + "description": "Address and other legal information you may want to put in the footer.", + "fieldname": "address", + "fieldtype": "Text Editor", + "label": "Address", + "permlevel": 0 + }, + { + "fieldname": "copyright", + "fieldtype": "Data", + "label": "Copyright", + "permlevel": 0 + }, + { + "fieldname": "footer_items", + "fieldtype": "Table", + "label": "Footer Items", + "options": "Top Bar Item", + "permlevel": 0 + }, + { + "fieldname": "integrations", + "fieldtype": "Section Break", + "label": "Integrations", + "permlevel": 0 + }, + { + "description": "Add Google Analytics ID: eg. UA-89XXX57-1. Please search help on Google Analytics for more information.", + "fieldname": "google_analytics_id", + "fieldtype": "Data", + "label": "Google Analytics ID", + "permlevel": 0 + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "google_plus_one", + "fieldtype": "Check", + "label": "Google Plus One", + "permlevel": 0 + }, + { + "fieldname": "facebook_share", + "fieldtype": "Check", + "label": "Facebook Share", + "permlevel": 0 + }, + { + "fieldname": "linked_in_share", + "fieldtype": "Check", + "label": "Linked In Share", + "permlevel": 0 + }, + { + "fieldname": "twitter_share", + "fieldtype": "Check", + "label": "Twitter Share", + "permlevel": 0 + }, + { + "description": "Tweet will be shared via your user account (if specified)", + "fieldname": "twitter_share_via", + "fieldtype": "Data", + "label": "Twitter Share via", + "permlevel": 0 + }, + { + "fieldname": "misc_section", + "fieldtype": "Section Break", + "label": "Misc", + "permlevel": 0 + }, + { + "description": "An icon file with .ico extension. Should be 16 x 16 px. Generated using a favicon generator. [favicon-generator.org]", + "fieldname": "favicon", + "fieldtype": "Select", + "label": "FavIcon", + "options": "attach_files:", + "permlevel": 0 + }, + { + "description": "Sub-domain provided by erpnext.com", + "fieldname": "subdomain", + "fieldtype": "Text", + "label": "Subdomain", + "permlevel": 0, + "read_only": 1, + "reqd": 0 + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "description": "Disable Customer Signup link in Login page", + "fieldname": "disable_signup", + "fieldtype": "Check", + "label": "Disable Signup", + "permlevel": 0 + } + ], + "icon": "icon-cog", + "idx": 1, + "issingle": 1, + "max_attachments": 10, + "modified": "2014-02-26 11:39:10.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Website Settings", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Website Manager", + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "permlevel": 1, + "read": 1, + "report": 0, + "role": "All", + "submit": 0 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index 81b59ea6c7..e1af4cc518 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -8,23 +8,22 @@ from frappe.utils import get_request_site_address, encode from frappe.model.controller import DocListController from urllib import quote -class DocType(DocListController): +class WebsiteSettings(DocListController): def validate(self): self.validate_top_bar_items() self.validate_footer_items() self.validate_home_page() def validate_home_page(self): - if self.doc.home_page and \ - not frappe.db.get_value("Website Route", {"name": self.doc.home_page}): + if self.home_page and \ + not frappe.db.get_value("Website Route", {"name": self.home_page}): frappe.throw(_("Invalid Home Page") + " (Standard pages - index, login, products, blog, about, contact)") def validate_top_bar_items(self): """validate url in top bar items""" - for top_bar_item in self.doclist.get({"parentfield": "top_bar_items"}): + for top_bar_item in self.get("top_bar_items"): if top_bar_item.parent_label: - parent_label_item = self.doclist.get({"parentfield": "top_bar_items", - "label": top_bar_item.parent_label}) + parent_label_item = self.get("top_bar_items", {"label": top_bar_item.parent_label}) if not parent_label_item: # invalid item @@ -38,7 +37,7 @@ class DocType(DocListController): def validate_footer_items(self): """clear parent label in footer""" - for footer_item in self.doclist.get({"parentfield": "footer_items"}): + for footer_item in self.get("footer_items"): footer_item.parent_label = None def on_update(self): @@ -80,12 +79,12 @@ def get_website_settings(): ] }) - settings = frappe.doc("Website Settings", "Website Settings") + settings = frappe.get_doc("Website Settings", "Website Settings") for k in ["banner_html", "brand_html", "copyright", "twitter_share_via", "favicon", "facebook_share", "google_plus_one", "twitter_share", "linked_in_share", "disable_signup"]: - if k in settings.fields: - context[k] = settings.fields.get(k) + if hasattr(settings, k): + context[k] = settings.get(k) if not context.get("favicon"): context["favicon"] = "/assets/frappe/images/favicon.ico" diff --git a/frappe/website/doctype/website_settings/website_settings.txt b/frappe/website/doctype/website_settings/website_settings.txt deleted file mode 100644 index 50975bc243..0000000000 --- a/frappe/website/doctype/website_settings/website_settings.txt +++ /dev/null @@ -1,266 +0,0 @@ -[ - { - "creation": "2013-04-30 12:58:46", - "docstatus": 0, - "modified": "2014-02-26 11:39:10", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 1, - "doctype": "DocType", - "document_type": "Other", - "icon": "icon-cog", - "issingle": 1, - "max_attachments": 10, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Website Settings", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "delete": 0, - "doctype": "DocPerm", - "name": "__common__", - "parent": "Website Settings", - "parentfield": "permissions", - "parenttype": "DocType", - "read": 1, - "report": 0, - "submit": 0 - }, - { - "doctype": "DocType", - "name": "Website Settings" - }, - { - "doctype": "DocField", - "fieldname": "sb0", - "fieldtype": "Section Break", - "label": "Landing Page" - }, - { - "description": "Link that is the website home page. Standard Links (index, login, products, blog, about, contact)", - "doctype": "DocField", - "fieldname": "home_page", - "fieldtype": "Link", - "label": "Home Page", - "options": "Website Route", - "reqd": 0 - }, - { - "description": "The name of your company / website as you want to appear on browser title bar. All pages will have this as the prefix to the title.", - "doctype": "DocField", - "fieldname": "title_prefix", - "fieldtype": "Data", - "label": "Title Prefix" - }, - { - "doctype": "DocField", - "fieldname": "cb4", - "fieldtype": "Column Break" - }, - { - "description": "If checked, the Home page will be the default Item Group for the website.", - "doctype": "DocField", - "fieldname": "home_page_is_products", - "fieldtype": "Check", - "label": "Home Page is Products" - }, - { - "description": "Add a banner to the site. (small banners are usually good)", - "doctype": "DocField", - "fieldname": "banner", - "fieldtype": "Section Break", - "label": "Banner" - }, - { - "description": "Select an image of approx width 150px with a transparent background for best results.", - "doctype": "DocField", - "fieldname": "banner_image", - "fieldtype": "Select", - "label": "Banner Image", - "options": "attach_files:" - }, - { - "doctype": "DocField", - "fieldname": "set_banner_from_image", - "fieldtype": "Button", - "label": "Set Banner from Image" - }, - { - "description": "Banner is above the Top Menu Bar.", - "doctype": "DocField", - "fieldname": "banner_html", - "fieldtype": "Small Text", - "label": "Banner HTML" - }, - { - "description": "Menu items in the Top Bar. For setting the color of the Top Bar, go to Style Settings", - "doctype": "DocField", - "fieldname": "top_bar", - "fieldtype": "Section Break", - "label": "Top Bar" - }, - { - "description": "Brand is what appears on the top-right of the toolbar. If it is an image, make sure it\nhas a transparent background and use the <img /> tag. Keep size as 200px x 30px", - "doctype": "DocField", - "fieldname": "brand_html", - "fieldtype": "Small Text", - "label": "Brand HTML" - }, - { - "doctype": "DocField", - "fieldname": "top_bar_items", - "fieldtype": "Table", - "label": "Top Bar Items", - "options": "Top Bar Item" - }, - { - "description": "Sidebar Links for Home Page only", - "doctype": "DocField", - "fieldname": "sidebar", - "fieldtype": "Section Break", - "label": "Sidebar" - }, - { - "doctype": "DocField", - "fieldname": "sidebar_items", - "fieldtype": "Table", - "label": "Sidebar Items", - "options": "Top Bar Item" - }, - { - "doctype": "DocField", - "fieldname": "footer", - "fieldtype": "Section Break", - "label": "Footer" - }, - { - "description": "Address and other legal information you may want to put in the footer.", - "doctype": "DocField", - "fieldname": "address", - "fieldtype": "Text Editor", - "label": "Address" - }, - { - "doctype": "DocField", - "fieldname": "copyright", - "fieldtype": "Data", - "label": "Copyright" - }, - { - "doctype": "DocField", - "fieldname": "footer_items", - "fieldtype": "Table", - "label": "Footer Items", - "options": "Top Bar Item" - }, - { - "doctype": "DocField", - "fieldname": "integrations", - "fieldtype": "Section Break", - "label": "Integrations" - }, - { - "description": "Add Google Analytics ID: eg. UA-89XXX57-1. Please search help on Google Analytics for more information.", - "doctype": "DocField", - "fieldname": "google_analytics_id", - "fieldtype": "Data", - "label": "Google Analytics ID" - }, - { - "doctype": "DocField", - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, - { - "doctype": "DocField", - "fieldname": "google_plus_one", - "fieldtype": "Check", - "label": "Google Plus One" - }, - { - "doctype": "DocField", - "fieldname": "facebook_share", - "fieldtype": "Check", - "label": "Facebook Share" - }, - { - "doctype": "DocField", - "fieldname": "linked_in_share", - "fieldtype": "Check", - "label": "Linked In Share" - }, - { - "doctype": "DocField", - "fieldname": "twitter_share", - "fieldtype": "Check", - "label": "Twitter Share" - }, - { - "description": "Tweet will be shared via your user account (if specified)", - "doctype": "DocField", - "fieldname": "twitter_share_via", - "fieldtype": "Data", - "label": "Twitter Share via" - }, - { - "doctype": "DocField", - "fieldname": "misc_section", - "fieldtype": "Section Break", - "label": "Misc" - }, - { - "description": "An icon file with .ico extension. Should be 16 x 16 px. Generated using a favicon generator. [favicon-generator.org]", - "doctype": "DocField", - "fieldname": "favicon", - "fieldtype": "Select", - "label": "FavIcon", - "options": "attach_files:" - }, - { - "description": "Sub-domain provided by erpnext.com", - "doctype": "DocField", - "fieldname": "subdomain", - "fieldtype": "Text", - "label": "Subdomain", - "read_only": 1, - "reqd": 0 - }, - { - "doctype": "DocField", - "fieldname": "column_break_28", - "fieldtype": "Column Break" - }, - { - "description": "Disable Customer Signup link in Login page", - "doctype": "DocField", - "fieldname": "disable_signup", - "fieldtype": "Check", - "label": "Disable Signup" - }, - { - "create": 1, - "doctype": "DocPerm", - "email": 1, - "permlevel": 0, - "print": 1, - "role": "Website Manager", - "write": 1 - }, - { - "amend": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 1, - "role": "All" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/website_slideshow/website_slideshow.json b/frappe/website/doctype/website_slideshow/website_slideshow.json new file mode 100644 index 0000000000..823b51454a --- /dev/null +++ b/frappe/website/doctype/website_slideshow/website_slideshow.json @@ -0,0 +1,66 @@ +{ + "allow_attach": 1, + "autoname": "field:slideshow_name", + "creation": "2013-03-07 15:53:15.000000", + "description": "Slideshow like display for the website", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Transaction", + "fields": [ + { + "fieldname": "slideshow_name", + "fieldtype": "Data", + "label": "Slideshow Name", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "description": "Note: For best results, images must be of the same size and width must be greater than height.", + "fieldname": "sb0", + "fieldtype": "Section Break", + "label": "Slideshow Items", + "permlevel": 0 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "slideshow_items", + "fieldtype": "Table", + "label": "Slideshow Items", + "options": "Website Slideshow Item", + "permlevel": 0 + }, + { + "depends_on": "eval:!doc.__islocal", + "description": "This goes above the slideshow.", + "fieldname": "header", + "fieldtype": "Text Editor", + "label": "Header", + "permlevel": 0 + } + ], + "icon": "icon-play", + "idx": 1, + "max_attachments": 10, + "modified": "2014-01-20 17:49:35.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Website Slideshow", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Website Manager", + "submit": 0, + "write": 1 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/website_slideshow/website_slideshow.py b/frappe/website/doctype/website_slideshow/website_slideshow.py index 8f1e859add..d0a3d3ab99 100644 --- a/frappe/website/doctype/website_slideshow/website_slideshow.py +++ b/frappe/website/doctype/website_slideshow/website_slideshow.py @@ -6,19 +6,22 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl - +from frappe.model.document import Document + +class WebsiteSlideshow(Document): + def on_update(self): # a slide show can be in use and any change in it should get reflected from frappe.website.render import clear_cache clear_cache() - -def get_slideshow(bean): - slideshow = frappe.bean("Website Slideshow", bean.doc.slideshow) - + +def get_slideshow(doc): + if not doc.slideshow: + return {} + + slideshow = frappe.get_doc("Website Slideshow", doc.slideshow) + return { - "slides": slideshow.doclist.get({"doctype":"Website Slideshow Item"}), - "slideshow_header": slideshow.doc.header or "" + "slides": slideshow.get({"doctype":"Website Slideshow Item"}), + "slideshow_header": slideshow.header or "" } diff --git a/frappe/website/doctype/website_slideshow/website_slideshow.txt b/frappe/website/doctype/website_slideshow/website_slideshow.txt deleted file mode 100644 index ee6a074148..0000000000 --- a/frappe/website/doctype/website_slideshow/website_slideshow.txt +++ /dev/null @@ -1,85 +0,0 @@ -[ - { - "creation": "2013-03-07 15:53:15", - "docstatus": 0, - "modified": "2014-01-20 17:49:35", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 1, - "autoname": "field:slideshow_name", - "description": "Slideshow like display for the website", - "doctype": "DocType", - "document_type": "Transaction", - "icon": "icon-play", - "max_attachments": 10, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Website Slideshow", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 1, - "delete": 1, - "doctype": "DocPerm", - "email": 1, - "name": "__common__", - "parent": "Website Slideshow", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Website Manager", - "submit": 0, - "write": 1 - }, - { - "doctype": "DocType", - "name": "Website Slideshow" - }, - { - "doctype": "DocField", - "fieldname": "slideshow_name", - "fieldtype": "Data", - "label": "Slideshow Name", - "read_only": 0, - "reqd": 1 - }, - { - "depends_on": "eval:!doc.__islocal", - "description": "Note: For best results, images must be of the same size and width must be greater than height.", - "doctype": "DocField", - "fieldname": "sb0", - "fieldtype": "Section Break", - "label": "Slideshow Items" - }, - { - "depends_on": "eval:!doc.__islocal", - "doctype": "DocField", - "fieldname": "slideshow_items", - "fieldtype": "Table", - "label": "Slideshow Items", - "options": "Website Slideshow Item" - }, - { - "depends_on": "eval:!doc.__islocal", - "description": "This goes above the slideshow.", - "doctype": "DocField", - "fieldname": "header", - "fieldtype": "Text Editor", - "label": "Header" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/website_slideshow_item/website_slideshow_item.json b/frappe/website/doctype/website_slideshow_item/website_slideshow_item.json new file mode 100644 index 0000000000..95a9236161 --- /dev/null +++ b/frappe/website/doctype/website_slideshow_item/website_slideshow_item.json @@ -0,0 +1,42 @@ +{ + "allow_attach": 0, + "creation": "2013-03-07 12:26:33.000000", + "docstatus": 0, + "doctype": "DocType", + "fields": [ + { + "fieldname": "image", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Image", + "options": "attach_files:", + "permlevel": 0 + }, + { + "fieldname": "heading", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Heading", + "permlevel": 0, + "print_width": "200px", + "width": "200px" + }, + { + "fieldname": "description", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Description", + "permlevel": 0, + "print_width": "200px", + "width": "200px" + } + ], + "idx": 1, + "istable": 1, + "max_attachments": 10, + "modified": "2013-12-20 19:21:55.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Website Slideshow Item", + "owner": "Administrator" +} \ No newline at end of file diff --git a/frappe/website/doctype/website_slideshow_item/website_slideshow_item.py b/frappe/website/doctype/website_slideshow_item/website_slideshow_item.py index cb42e2eea2..d782525f4b 100644 --- a/frappe/website/doctype/website_slideshow_item/website_slideshow_item.py +++ b/frappe/website/doctype/website_slideshow_item/website_slideshow_item.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file +from frappe.model.document import Document + +class WebsiteSlideshowItem(Document): + pass \ No newline at end of file diff --git a/frappe/website/doctype/website_slideshow_item/website_slideshow_item.txt b/frappe/website/doctype/website_slideshow_item/website_slideshow_item.txt deleted file mode 100644 index f549c36a1d..0000000000 --- a/frappe/website/doctype/website_slideshow_item/website_slideshow_item.txt +++ /dev/null @@ -1,53 +0,0 @@ -[ - { - "creation": "2013-03-07 12:26:33", - "docstatus": 0, - "modified": "2013-12-20 19:21:55", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "allow_attach": 0, - "doctype": "DocType", - "istable": 1, - "max_attachments": 10, - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "in_list_view": 1, - "name": "__common__", - "parent": "Website Slideshow Item", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "doctype": "DocType", - "name": "Website Slideshow Item" - }, - { - "doctype": "DocField", - "fieldname": "image", - "fieldtype": "Select", - "label": "Image", - "options": "attach_files:" - }, - { - "doctype": "DocField", - "fieldname": "heading", - "fieldtype": "Data", - "label": "Heading", - "print_width": "200px", - "width": "200px" - }, - { - "doctype": "DocField", - "fieldname": "description", - "fieldtype": "Text", - "label": "Description", - "print_width": "200px", - "width": "200px" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/website_template/website_template.json b/frappe/website/doctype/website_template/website_template.json new file mode 100644 index 0000000000..5461131bc1 --- /dev/null +++ b/frappe/website/doctype/website_template/website_template.json @@ -0,0 +1,130 @@ +{ + "autoname": "field:link_name", + "creation": "2013-11-18 15:35:00.000000", + "docstatus": 0, + "doctype": "DocType", + "document_type": "System", + "fields": [ + { + "fieldname": "page_or_generator", + "fieldtype": "Select", + "label": "Page or Generator", + "options": "Page\nGenerator", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "label": "Ref DocType", + "options": "DocType", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "link_name", + "fieldtype": "Data", + "label": "Link Name", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "page_title", + "fieldtype": "Data", + "label": "Page Title", + "permlevel": 0 + }, + { + "fieldname": "base_template_path", + "fieldtype": "Data", + "label": "Base Template Path", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "template_path", + "fieldtype": "Data", + "label": "Template Path", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "controller", + "fieldtype": "Data", + "label": "Controller", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "lastmod", + "fieldtype": "Data", + "label": "Lastmod", + "permlevel": 0 + }, + { + "fieldname": "no_cache", + "fieldtype": "Check", + "label": "No Cache", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "no_sitemap", + "fieldtype": "Check", + "label": "No Sitemap", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "no_sidebar", + "fieldtype": "Check", + "label": "No Sidebar", + "permlevel": 0 + }, + { + "fieldname": "page_name_field", + "fieldtype": "Data", + "label": "Page Name Field", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "condition_field", + "fieldtype": "Data", + "label": "Condition Field", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "sort_by", + "fieldtype": "Data", + "label": "Sort By", + "permlevel": 0 + }, + { + "fieldname": "sort_order", + "fieldtype": "Data", + "label": "Sort Order", + "permlevel": 0 + } + ], + "idx": 1, + "modified": "2014-02-24 12:47:44.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "Website Template", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 0, + "delete": 1, + "export": 0, + "permlevel": 0, + "read": 1, + "report": 1, + "role": "System Manager", + "write": 0 + } + ] +} \ No newline at end of file diff --git a/frappe/website/doctype/website_template/website_template.py b/frappe/website/doctype/website_template/website_template.py index c9a8f2af94..2f0357a23a 100644 --- a/frappe/website/doctype/website_template/website_template.py +++ b/frappe/website/doctype/website_template/website_template.py @@ -11,18 +11,18 @@ from frappe import _ from frappe.website.doctype.website_route.website_route import add_to_sitemap, update_sitemap, cleanup_sitemap from frappe.utils.nestedset import rebuild_tree -class DocType: - def __init__(self, d, dl): - self.doc, self.doclist = d, dl +from frappe.model.document import Document + +class WebsiteTemplate(Document): def after_insert(self): - if self.doc.page_or_generator == "Page": + if self.page_or_generator == "Page": website_route = frappe.db.get_value("Website Route", - {"website_template": self.doc.name, "page_or_generator": "Page"}) + {"website_template": self.name, "page_or_generator": "Page"}) - opts = self.doc.fields.copy() + opts = self.as_dict() opts.update({"public_read": 1}) - + if website_route: update_sitemap(website_route, opts) else: @@ -30,20 +30,20 @@ class DocType: else: condition = "" - if self.doc.condition_field: - condition = " where ifnull(%s, 0)=1" % self.doc.condition_field + if self.condition_field: + condition = " where ifnull(%s, 0)=1" % self.condition_field for name in frappe.db.sql_list("""select name from `tab{doctype}` {condition} order by idx asc, {sort_field} {sort_order}""".format( - doctype = self.doc.ref_doctype, + doctype = self.ref_doctype, condition = condition, - sort_field = self.doc.sort_field or "name", - sort_order = self.doc.sort_order or "asc" + sort_field = getattr(self, "sort_field", "name"), + sort_order = getattr(self, "sort_order", "asc") )): - bean = frappe.bean(self.doc.ref_doctype, name) + doc = frappe.get_doc(self.ref_doctype, name) # regenerate route - bean.run_method("on_update") + doc.run_method("on_update") def rebuild_website_template(): # TODO @@ -127,7 +127,7 @@ def add_website_template(page_or_generator, app, path, fname, app_path): # found by earlier app, override frappe.db.sql("""delete from `tabWebsite Template` where name=%s""", (wsc.link_name,)) - frappe.bean(wsc).insert() + frappe.get_doc(wsc).insert() return name diff --git a/frappe/website/doctype/website_template/website_template.txt b/frappe/website/doctype/website_template/website_template.txt deleted file mode 100644 index f7f9d545d5..0000000000 --- a/frappe/website/doctype/website_template/website_template.txt +++ /dev/null @@ -1,149 +0,0 @@ -[ - { - "creation": "2013-11-18 15:35:00", - "docstatus": 0, - "modified": "2014-02-24 12:47:44", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "autoname": "field:link_name", - "doctype": "DocType", - "document_type": "System", - "module": "Website", - "name": "__common__" - }, - { - "doctype": "DocField", - "name": "__common__", - "parent": "Website Template", - "parentfield": "fields", - "parenttype": "DocType", - "permlevel": 0 - }, - { - "cancel": 0, - "create": 0, - "delete": 1, - "doctype": "DocPerm", - "export": 0, - "name": "__common__", - "parent": "Website Template", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "read": 1, - "report": 1, - "role": "System Manager", - "write": 0 - }, - { - "doctype": "DocType", - "name": "Website Template" - }, - { - "doctype": "DocField", - "fieldname": "page_or_generator", - "fieldtype": "Select", - "label": "Page or Generator", - "options": "Page\nGenerator", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "ref_doctype", - "fieldtype": "Link", - "label": "Ref DocType", - "options": "DocType", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "link_name", - "fieldtype": "Data", - "label": "Link Name", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "page_title", - "fieldtype": "Data", - "label": "Page Title" - }, - { - "doctype": "DocField", - "fieldname": "base_template_path", - "fieldtype": "Data", - "label": "Base Template Path", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "template_path", - "fieldtype": "Data", - "label": "Template Path", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "controller", - "fieldtype": "Data", - "label": "Controller", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "lastmod", - "fieldtype": "Data", - "label": "Lastmod" - }, - { - "doctype": "DocField", - "fieldname": "no_cache", - "fieldtype": "Check", - "label": "No Cache", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "no_sitemap", - "fieldtype": "Check", - "label": "No Sitemap", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "no_sidebar", - "fieldtype": "Check", - "label": "No Sidebar" - }, - { - "doctype": "DocField", - "fieldname": "page_name_field", - "fieldtype": "Data", - "label": "Page Name Field", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "condition_field", - "fieldtype": "Data", - "label": "Condition Field", - "read_only": 1 - }, - { - "doctype": "DocField", - "fieldname": "sort_by", - "fieldtype": "Data", - "label": "Sort By" - }, - { - "doctype": "DocField", - "fieldname": "sort_order", - "fieldtype": "Data", - "label": "Sort Order" - }, - { - "doctype": "DocPerm" - } -] \ No newline at end of file diff --git a/frappe/website/page/sitemap_browser/sitemap_browser.json b/frappe/website/page/sitemap_browser/sitemap_browser.json new file mode 100644 index 0000000000..4cbbdf9bb6 --- /dev/null +++ b/frappe/website/page/sitemap_browser/sitemap_browser.json @@ -0,0 +1,23 @@ +{ + "creation": "2014-02-18 10:47:22.000000", + "docstatus": 0, + "doctype": "Page", + "icon": "icon-sitemap", + "idx": 1, + "modified": "2014-02-18 10:47:22.000000", + "modified_by": "Administrator", + "module": "Website", + "name": "sitemap-browser", + "owner": "Administrator", + "page_name": "sitemap-browser", + "roles": [ + { + "role": "Website Manager" + }, + { + "role": "System Manager" + } + ], + "standard": "Yes", + "title": "Sitemap Browser" +} \ No newline at end of file diff --git a/frappe/website/page/sitemap_browser/sitemap_browser.py b/frappe/website/page/sitemap_browser/sitemap_browser.py index 9538f9e1b2..66aabafbda 100644 --- a/frappe/website/page/sitemap_browser/sitemap_browser.py +++ b/frappe/website/page/sitemap_browser/sitemap_browser.py @@ -24,10 +24,10 @@ def move(name, up_or_down): if not frappe.has_permission("Website Route"): raise frappe.PermissionError - sitemap = frappe.doc("Website Route", name) + sitemap = frappe.get_doc("Website Route", name) if up_or_down=="up": if sitemap.idx > 0: - prev = frappe.doc("Website Route", { + prev = frappe.get_doc("Website Route", { "parent_website_route": sitemap.parent_website_route, "idx": sitemap.idx - 1 }) @@ -40,7 +40,7 @@ def move(name, up_or_down): ret = "ok" else: - nexts = frappe.doc("Website Route", { + nexts = frappe.get_doc("Website Route", { "parent_website_route": sitemap.parent_website_route, "idx": sitemap.idx + 1 }) @@ -60,13 +60,13 @@ def update_parent(name, new_parent): if not frappe.has_permission("Website Route"): raise frappe.PermissionError - sitemap = frappe.doc("Website Route", name) + sitemap = frappe.get_doc("Website Route", name) if sitemap.ref_doctype: - generator = frappe.bean(sitemap.ref_doctype, sitemap.docname) - if not generator.meta.has_field("parent_website_route"): + generator = frappe.get_doc(sitemap.ref_doctype, sitemap.docname) + if not generator.meta.get_field("parent_website_route"): frappe.throw("Does not allow moving.") - generator.doc.parent_website_route = new_parent + generator.parent_website_route = new_parent generator.save() else: frappe.msgprint("Template Pages cannot be moved.") diff --git a/frappe/website/page/sitemap_browser/sitemap_browser.txt b/frappe/website/page/sitemap_browser/sitemap_browser.txt deleted file mode 100644 index 00d46e2f2a..0000000000 --- a/frappe/website/page/sitemap_browser/sitemap_browser.txt +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "creation": "2014-02-18 10:47:22", - "docstatus": 0, - "modified": "2014-02-18 10:47:22", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "Page", - "icon": "icon-sitemap", - "module": "Website", - "name": "__common__", - "page_name": "sitemap-browser", - "standard": "Yes", - "title": "Sitemap Browser" - }, - { - "doctype": "Page Role", - "name": "__common__", - "parent": "sitemap-browser", - "parentfield": "roles", - "parenttype": "Page" - }, - { - "doctype": "Page", - "name": "sitemap-browser" - }, - { - "doctype": "Page Role", - "role": "Website Manager" - }, - { - "doctype": "Page Role", - "role": "System Manager" - } -] \ No newline at end of file diff --git a/frappe/website/render.py b/frappe/website/render.py index 8f2aa75648..9329edd131 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -111,8 +111,10 @@ def set_content_type(response, data, path): response.headers["Content-Type"] = "text/html; charset: utf-8" - if "." in path and not path.endswith(".html"): + if "." in path: content_type, encoding = mimetypes.guess_type(path) + if not content_type: + raise frappe.UnsupportedMediaType("Cannot determine content type of {}".format(path)) response.headers["Content-Type"] = content_type.encode("utf-8") return data diff --git a/frappe/website/sitemap.py b/frappe/website/sitemap.py index f401dfedc8..b4fbb58407 100644 --- a/frappe/website/sitemap.py +++ b/frappe/website/sitemap.py @@ -17,63 +17,67 @@ def get_sitemap_options(path): sitemap_options = build_sitemap_options(path) if can_cache(sitemap_options.no_cache): frappe.cache().set_value(cache_key, sitemap_options) - + return frappe._dict(sitemap_options) - + def build_sitemap_options(path): - sitemap_options = frappe._dict(frappe.doc("Website Route", path).fields) + sitemap_options = frappe._dict(frappe.get_doc("Website Route", path).as_dict()) home_page = get_home_page() - - sitemap_config = frappe.doc("Website Template", - sitemap_options.get("website_template")).fields - + + sitemap_config = frappe.get_doc("Website Template", + sitemap_options.get("website_template")).as_dict() + # get sitemap config fields too - for fieldname in ("base_template_path", "template_path", "controller", + for fieldname in ("base_template_path", "template_path", "controller", "no_cache", "no_sitemap", "page_name_field", "condition_field"): sitemap_options[fieldname] = sitemap_config.get(fieldname) - + sitemap_options.doctype = sitemap_options.ref_doctype sitemap_options.title = sitemap_options.page_title sitemap_options.pathname = sitemap_options.name - + # establish hierarchy sitemap_options.parents = frappe.db.sql("""select name, page_title from `tabWebsite Route` where lft < %s and rgt > %s order by lft asc""", (sitemap_options.lft, sitemap_options.rgt), as_dict=True) if not sitemap_options.no_sidebar: - set_sidebar_items(sitemap_options, sitemap_options.pathname, home_page) + sitemap_options.children = get_route_children(sitemap_options.pathname, home_page) if not sitemap_options.children: - set_sidebar_items(sitemap_options, sitemap_options.parent_website_route, home_page) + sitemap_options.children = get_route_children(sitemap_options.parent_website_route, home_page) # determine templates to be used if not sitemap_options.base_template_path: app_base = frappe.get_hooks("base_template") sitemap_options.base_template_path = app_base[0] if app_base else "templates/base.html" - + return sitemap_options -def set_sidebar_items(sitemap_options, pathname, home_page): +def get_route_children(pathname, home_page=None): + if not home_page: + home_page = get_home_page() + if pathname==home_page or not pathname: - sitemap_options.children = frappe.db.sql("""select url as name, label as page_title, + children = frappe.db.sql("""select url as name, label as page_title, 1 as public_read from `tabTop Bar Item` where parentfield='sidebar_items' order by idx""", as_dict=True) - else: - sitemap_options.children = frappe.db.sql("""select * from `tabWebsite Route` - where ifnull(parent_website_route,'')=%s - and public_read=1 + else: + children = frappe.db.sql("""select * from `tabWebsite Route` + where ifnull(parent_website_route,'')=%s + and public_read=1 order by idx, page_title asc""", pathname, as_dict=True) - - if sitemap_options.children: + + if children: # if children are from generator and sort order is specified, then get that condition - website_template = frappe.doc("Website Template", sitemap_options.children[0].website_template) + website_template = frappe.get_doc("Website Template", children[0].website_template) if website_template.sort_by!="name": - sitemap_options.children = frappe.db.sql("""select t1.* from + children = frappe.db.sql("""select t1.* from `tabWebsite Route` t1, `tab{ref_doctype}` t2 - where ifnull(t1.parent_website_route,'')=%s - and t1.public_read=1 + where ifnull(t1.parent_website_route,'')=%s + and t1.public_read=1 and t1.docname = t2.name - order by t2.{sort_by} {sort_order}""".format(**website_template.fields), + order by t2.{sort_by} {sort_order}""".format(**website_template.as_dict()), pathname, as_dict=True) - - sitemap_options.children = [frappe.doc("Website Route", pathname).fields] + sitemap_options.children - + + children = [frappe.get_doc("Website Route", pathname)] + children + + return children diff --git a/frappe/website/statics.py b/frappe/website/statics.py index f467fe18f6..b7c1748153 100644 --- a/frappe/website/statics.py +++ b/frappe/website/statics.py @@ -7,6 +7,7 @@ import frappe, os, time from frappe import _ from frappe.utils import cint from markdown2 import markdown +from frappe.website.sitemap import get_route_children def sync_statics(): s = sync() @@ -23,33 +24,33 @@ class sync(object): self.sync_for_app(app) self.cleanup() - + if self.updated: print str(self.updated) + " files updated" - + def sync_for_app(self, app): self.statics_path = frappe.get_app_path(app, "templates", "statics") if os.path.exists(self.statics_path): for basepath, folders, files in os.walk(self.statics_path): self.sync_folder(basepath, folders, files) - + def sync_folder(self, basepath, folders, files): folder_route = os.path.relpath(basepath, self.statics_path) self.get_index_txt(basepath, files) self.sync_index_page(basepath, files) - - if not frappe.db.exists("Website Route", folder_route) and basepath!=self.statics_path: + + if not frappe.db.exists("Website Route", folder_route) and basepath!=self.statics_path: # not synced either by generator or by index.html return - + if self.index: self.sync_using_given_index(basepath, folders, files) - + else: self.sync_alphabetically(basepath, folders, files) - - + + def get_index_txt(self, basepath, files): self.index = [] if "index.txt" in files: @@ -62,7 +63,7 @@ class sync(object): if fname in files: self.sync_file(fname, os.path.join(basepath, fname), None) return - + def sync_using_given_index(self, basepath, folders, files): for i, page_name in enumerate(self.index): if page_name in folders: @@ -88,22 +89,22 @@ class sync(object): page_name = fname.rsplit(".", 1)[0] if not (page_name=="index" and basepath!=self.statics_path): self.sync_file(fname, os.path.join(basepath, fname), None) - + def sync_file(self, fname, fpath, priority): route = os.path.relpath(fpath, self.statics_path).rsplit(".", 1)[0] if fname.rsplit(".", 1)[0]=="index" and os.path.dirname(fpath) != self.statics_path: route = os.path.dirname(route) - + if route in self.synced: return - + parent_website_route = os.path.dirname(route) page_name = os.path.basename(route) - - route_details = frappe.db.get_value("Website Route", route, + + route_details = frappe.db.get_value("Website Route", route, ["name", "idx", "static_file_timestamp", "docname"], as_dict=True) - + if route_details: self.update_web_page(route_details, fpath, priority, parent_website_route) else: @@ -111,72 +112,72 @@ class sync(object): self.insert_web_page(route, fpath, page_name, priority, parent_website_route) def insert_web_page(self, route, fpath, page_name, priority, parent_website_route): - page = frappe.bean({ + page = frappe.get_doc({ "doctype":"Web Page", "idx": priority, "page_name": page_name, "published": 1, "parent_website_route": parent_website_route }) - - page.doc.fields.update(get_static_content(fpath, page_name)) + + page.update(get_static_content(fpath, page_name, route)) try: page.insert() except NameError: # page exists, if deleted static, delete it and try again - old_route = frappe.doc("Website Route", {"ref_doctype":"Web Page", - "docname": page.doc.name}) - if old_route.static_file_timestamp and not os.path.exists(os.path.join(self.statics_path, + old_route = frappe.get_doc("Website Route", {"ref_doctype":"Web Page", + "docname": page.name}) + if old_route.static_file_timestamp and not os.path.exists(os.path.join(self.statics_path, old_route.name)): - - frappe.delete_doc("Web Page", page.doc.name) + + frappe.delete_doc("Web Page", page.name) page.insert() # retry - - + + # update timestamp - route_bean = frappe.bean("Website Route", {"ref_doctype": "Web Page", - "docname": page.doc.name}) - route_bean.doc.static_file_timestamp = cint(os.path.getmtime(fpath)) - route_bean.save() + route_doc = frappe.get_doc("Website Route", {"ref_doctype": "Web Page", + "docname": page.name}) + route_doc.static_file_timestamp = cint(os.path.getmtime(fpath)) + route_doc.save() self.updated += 1 - print route_bean.doc.name + " inserted" + print route_doc.name + " inserted" self.synced.append(route) - - def update_web_page(self, route_details, fpath, priority, parent_website_route): + + def update_web_page(self, route_details, fpath, priority, parent_website_route): if str(cint(os.path.getmtime(fpath)))!= route_details.static_file_timestamp \ or (cint(route_details.idx) != cint(priority) and (priority is not None)): - page = frappe.bean("Web Page", route_details.docname) - page.doc.fields.update(get_static_content(fpath, route_details.docname)) - page.doc.idx = priority + page = frappe.get_doc("Web Page", route_details.docname) + page.update(get_static_content(fpath, route_details.docname, route_details.name)) + page.idx = priority page.save() - route_bean = frappe.bean("Website Route", route_details.name) - route_bean.doc.static_file_timestamp = cint(os.path.getmtime(fpath)) - route_bean.save() + route_doc = frappe.get_doc("Website Route", route_details.name) + route_doc.static_file_timestamp = cint(os.path.getmtime(fpath)) + route_doc.save() - print route_bean.doc.name + " updated" + print route_doc.name + " updated" self.updated += 1 - + self.synced.append(route_details.name) def cleanup(self): if self.synced: - frappe.delete_doc("Web Page", frappe.db.sql_list("""select docname + frappe.delete_doc("Web Page", frappe.db.sql_list("""select docname from `tabWebsite Route` - where ifnull(static_file_timestamp,'')!='' and name not in ({}) - order by (rgt-lft) asc""".format(', '.join(["%s"]*len(self.synced))), + where ifnull(static_file_timestamp,'')!='' and name not in ({}) + order by (rgt-lft) asc""".format(', '.join(["%s"]*len(self.synced))), tuple(self.synced))) else: - frappe.delete_doc("Web Page", frappe.db.sql_list("""select docname + frappe.delete_doc("Web Page", frappe.db.sql_list("""select docname from `tabWebsite Route` - where ifnull(static_file_timestamp,'')!='' + where ifnull(static_file_timestamp,'')!='' order by (rgt-lft) asc""")) -def get_static_content(fpath, docname): +def get_static_content(fpath, docname, route): d = frappe._dict({}) with open(fpath, "r") as contentfile: content = unicode(contentfile.read(), 'utf-8') @@ -190,8 +191,14 @@ def get_static_content(fpath, docname): d.title = first_line[2:] content = "\n".join(lines[1:]) + if "{index}" in content: + children = get_route_children(route) + html = frappe.get_template("templates/includes/static_index.html").render({ + "items":children}) + content = content.replace("{index}", html) + content = markdown(content) - + d.main_section = unicode(content.encode("utf-8"), 'utf-8') if not d.title: d.title = docname.replace("-", " ").replace("_", " ").title() @@ -201,8 +208,8 @@ def get_static_content(fpath, docname): if os.path.exists(fpath): with open(fpath, "r") as f: d["css" if extn=="css" else "javascript"] = f.read() - + d.insert_style = 1 if d.css else 0 d.insert_code = 1 if d.javascript else 0 - + return d diff --git a/frappe/website/template.py b/frappe/website/template.py index 1e6ca86630..f9a3dd42c1 100644 --- a/frappe/website/template.py +++ b/frappe/website/template.py @@ -14,44 +14,44 @@ def render_blocks(context): """returns a dict of block name and its rendered content""" out = {} - + env = frappe.get_jenv() - + def _render_blocks(template_path): source = frappe.local.jloader.get_source(frappe.local.jenv, template_path)[0] for referenced_template_path in meta.find_referenced_templates(env.parse(source)): if referenced_template_path: _render_blocks(referenced_template_path) - + template = frappe.get_template(template_path) for block, render in template.blocks.items(): out[block] = scrub_relative_urls(concat(render(template.new_context(context)))) - + _render_blocks(context["template_path"]) # default blocks if not found if "title" not in out and out.get("header"): out["title"] = out["header"] - + if "title" not in out: out["title"] = context.get("title") - + if "header" not in out and out.get("title"): out["header"] = out["title"] if not out["header"].startswith("" - + if "breadcrumbs" not in out: out["breadcrumbs"] = scrub_relative_urls( frappe.get_template("templates/includes/breadcrumbs.html").render(context)) - - if "sidebar" not in out: + + if "sidebar" not in out and not context.get("no_sidebar"): out["sidebar"] = scrub_relative_urls( frappe.get_template("templates/includes/sidebar.html").render(context)) - + out["title"] = strip_html(out.get("title") or "") - + # remove style and script tags from blocks out["style"] = re.sub("]*>", "", out.get("style") or "") out["script"] = re.sub("]*>", "", out.get("script") or "") diff --git a/frappe/website/website_generator.py b/frappe/website/website_generator.py index 403d8cc973..b93c9616a8 100644 --- a/frappe/website/website_generator.py +++ b/frappe/website/website_generator.py @@ -4,82 +4,92 @@ from __future__ import unicode_literals import frappe from frappe.model.controller import DocListController +from frappe.model.naming import append_number_if_name_exists from frappe.website.utils import cleanup_page_name from frappe.utils import now from frappe.website.doctype.website_route.website_route import add_to_sitemap, update_sitemap, remove_sitemap -def call_website_generator(bean, method, *args, **kwargs): - getattr(WebsiteGenerator(bean.doc, bean.doclist), method)(*args, **kwargs) - class WebsiteGenerator(DocListController): def autoname(self): - self.doc.name = self.get_page_name() - self.doc.append_number_if_name_exists() + self.setup_generator() + if not self.website_template: return + + self.name = self.get_page_name() + append_number_if_name_exists(self) def set_page_name(self): """set page name based on parent page_name and title""" page_name = cleanup_page_name(self.get_page_title()) - if self.doc.is_new(): - self.doc.fields[self.website_template.page_name_field] = page_name + if self.is_new(): + self.set(self.website_template.page_name_field, page_name) else: - frappe.db.set(self.doc, self.website_template.page_name_field, page_name) - + frappe.db.set(self, self.website_template.page_name_field, page_name) + return page_name def get_parent_website_route(self): - return self.doc.parent_website_route + return self.parent_website_route def setup_generator(self): if not hasattr(self, "website_template"): - self.website_template = frappe.db.get_values("Website Template", - {"ref_doctype": self.doc.doctype}, "*")[0] + website_template = frappe.db.get_values("Website Template", + {"ref_doctype": self.doctype}, "*") + if website_template: + self.website_template = website_template[0] + else: + self.website_template = None def on_update(self): self.update_sitemap() if getattr(self, "save_versions", False): - frappe.add_version(self.doclist) - + frappe.add_version(self) + def after_rename(self, olddn, newdn, merge): + self.setup_generator() + if not self.website_template: return + frappe.db.sql("""update `tabWebsite Route` - set docname=%s where ref_doctype=%s and docname=%s""", (newdn, self.doc.doctype, olddn)) - + set docname=%s where ref_doctype=%s and docname=%s""", (newdn, self.doctype, olddn)) + if merge: - self.setup_generator() - remove_sitemap(ref_doctype=self.doc.doctype, docname=olddn) - + remove_sitemap(ref_doctype=self.doctype, docname=olddn) + def on_trash(self): self.setup_generator() - remove_sitemap(ref_doctype=self.doc.doctype, docname=self.doc.name) - + if not self.website_template: return + + remove_sitemap(ref_doctype=self.doctype, docname=self.name) + def update_sitemap(self): self.setup_generator() - + if not self.website_template: return + if self.website_template.condition_field and \ - not self.doc.fields.get(self.website_template.condition_field): + not self.get(self.website_template.condition_field): # condition field failed, remove and return! - remove_sitemap(ref_doctype=self.doc.doctype, docname=self.doc.name) + remove_sitemap(ref_doctype=self.doctype, docname=self.name) return - + self.add_or_update_sitemap() - + def add_or_update_sitemap(self): page_name = self.get_page_name() - - existing_site_map = frappe.db.get_value("Website Route", {"ref_doctype": self.doc.doctype, - "docname": self.doc.name}) - - if self.doc.modified: - lastmod = frappe.utils.get_datetime(self.doc.modified).strftime("%Y-%m-%d") + + existing_site_map = frappe.db.get_value("Website Route", {"ref_doctype": self.doctype, + "docname": self.name}) + + if self.modified: + lastmod = frappe.utils.get_datetime(self.modified).strftime("%Y-%m-%d") else: lastmod = now() - + opts = frappe._dict({ "page_or_generator": "Generator", - "ref_doctype":self.doc.doctype, - "idx": self.doc.idx, - "docname": self.doc.name, + "ref_doctype":self.doctype, + "idx": self.idx, + "docname": self.name, "page_name": page_name, "link_name": self.website_template.name, "lastmod": lastmod, @@ -89,35 +99,34 @@ class WebsiteGenerator(DocListController): }) self.update_permissions(opts) - + if existing_site_map: idx = update_sitemap(existing_site_map, opts) else: idx = add_to_sitemap(opts) - if idx!=None and self.doc.idx != idx: - frappe.db.set(self.doc, "idx", idx) - + if idx!=None and self.idx != idx: + frappe.db.set(self, "idx", idx) + def update_permissions(self, opts): if self.meta.get_field("public_read"): - opts.public_read = self.doc.public_read - opts.public_write = self.doc.public_write + opts.public_read = self.public_read + opts.public_write = self.public_write else: opts.public_read = 1 - + def get_page_name(self): page_name = self._get_page_name() if not page_name: page_name = self.set_page_name() - + return self._get_page_name() - + def _get_page_name(self): - self.setup_generator() - if self.meta.has_field(self.website_template.page_name_field): - return self.doc.fields.get(self.website_template.page_name_field) + if self.meta.get_field(self.website_template.page_name_field): + return self.get(self.website_template.page_name_field) else: return cleanup_page_name(self.get_page_title()) - + def get_page_title(self): - return self.doc.title or (self.doc.name.replace("-", " ").replace("_", " ").title()) + return self.get("title") or (self.name.replace("-", " ").replace("_", " ").title()) diff --git a/frappe/widgets/calendar.py b/frappe/widgets/calendar.py index 4bc7ef4848..8cc8c27a3c 100644 --- a/frappe/widgets/calendar.py +++ b/frappe/widgets/calendar.py @@ -11,8 +11,8 @@ import json def update_event(args, field_map): args = frappe._dict(json.loads(args)) field_map = frappe._dict(json.loads(field_map)) - w = frappe.bean(args.doctype, args.name) - w.doc.fields[field_map.start] = args[field_map.start] - w.doc.fields[field_map.end] = args[field_map.end] + w = frappe.get_doc(args.doctype, args.name) + w.set(field_map.start, args[field_map.start]) + w.set(field_map.end, args[field_map.end]) w.save() diff --git a/frappe/widgets/event.py b/frappe/widgets/event.py index 5239846ef4..68c2f2ebb6 100644 --- a/frappe/widgets/event.py +++ b/frappe/widgets/event.py @@ -7,9 +7,7 @@ from __future__ import unicode_literals import frappe @frappe.whitelist() -def get_cal_events(m_st, m_end): - import frappe.model.doc - +def get_cal_events(m_st, m_end): # load owned events res1 = frappe.db.sql("""select name from `tabEvent` WHERE ifnull(event_date,'2000-01-01') between %s and %s and owner = %s @@ -39,7 +37,7 @@ def get_cal_events(m_st, m_end): doclist, rl = [], [] for r in res1 + res2 + res3 + res4: if not r in rl: - doclist += frappe.model.doc.get('Event', r[0]) + doclist += frappe.get_doc('Event', r[0]) rl.append(r) return doclist diff --git a/frappe/widgets/form/assign_to.py b/frappe/widgets/form/assign_to.py index 51f60e0fae..3617649bc2 100644 --- a/frappe/widgets/form/assign_to.py +++ b/frappe/widgets/form/assign_to.py @@ -34,7 +34,7 @@ def add(args=None): else: from frappe.utils import nowdate - d = frappe.bean({ + d = frappe.get_doc({ "doctype":"ToDo", "owner": args['assign_to'], "reference_type": args['doctype'], @@ -44,11 +44,10 @@ def add(args=None): "status": "Open", "date": args.get('date', nowdate()), "assigned_by": args.get('assigned_by', frappe.user.name), - }).insert(ignore_permissions=True).doc + }).insert(ignore_permissions=True) # set assigned_to if field exists - from frappe.model.meta import has_field - if has_field(args['doctype'], "assigned_to"): + if frappe.get_meta(args['doctype']).get_field("assigned_to"): frappe.db.set_value(args['doctype'], args['name'], "assigned_to", args['assign_to']) try: @@ -64,31 +63,21 @@ def add(args=None): # notify if not args.get("no_notification"): notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN', description=args.get("description"), notify=args.get('notify')) - - # update feeed - try: - from erpnext.home import make_feed - from frappe.utils import get_fullname - make_feed('Assignment', d.reference_type, d.reference_name, frappe.session['user'], - '[%s] Assigned to %s' % (d.priority, get_fullname(d.owner)), '#C78F58') - except ImportError, e: - pass - + return get(args) @frappe.whitelist() def remove(doctype, name, assign_to): """remove from todo""" - todo = frappe.bean("ToDo", {"reference_type":doctype, "reference_name":name, "owner":assign_to, "status":"Open"}) - todo.doc.status = "Closed" + todo = frappe.get_doc("ToDo", {"reference_type":doctype, "reference_name":name, "owner":assign_to, "status":"Open"}) + todo.status = "Closed" todo.save(ignore_permissions=True) # clear assigned_to if field exists - from frappe.model.meta import has_field - if has_field(doctype, "assigned_to"): + if frappe.get_meta(doctype).get_field("assigned_to"): frappe.db.set_value(doctype, name, "assigned_to", None) - notify_assignment(todo.doc.assigned_by, todo.doc.owner, todo.doc.reference_type, todo.doc.reference_name) + notify_assignment(todo.assigned_by, todo.owner, todo.reference_type, todo.reference_name) return get({"doctype": doctype, "name": name}) diff --git a/frappe/widgets/form/load.py b/frappe/widgets/form/load.py index 672d9f6206..2e26d43ad2 100644 --- a/frappe/widgets/form/load.py +++ b/frappe/widgets/form/load.py @@ -3,8 +3,10 @@ from __future__ import unicode_literals import frappe, json -import frappe.model.doc import frappe.utils +import frappe.defaults +import frappe.widgets.form.meta +from frappe.utils.file_manager import get_file_url @frappe.whitelist() def getdoc(doctype, name, user=None): @@ -13,8 +15,6 @@ def getdoc(doctype, name, user=None): Requries "doctype", "name" as form variables. Will also call the "onload" method on the document. """ - - import frappe if not (doctype and name): raise Exception, 'doctype and name required!' @@ -26,14 +26,12 @@ def getdoc(doctype, name, user=None): return [] try: - bean = frappe.bean(doctype, name) - bean.run_method("onload") + doc = frappe.get_doc(doctype, name) + doc.run_method("onload") - if not bean.has_read_perm(): + if not doc.has_permission("read"): raise frappe.PermissionError - doclist = bean.doclist - # add file list get_docinfo(doctype, name) @@ -42,35 +40,39 @@ def getdoc(doctype, name, user=None): frappe.msgprint('Did not load.') raise - if bean and not name.startswith('_'): + if doc and not name.startswith('_'): frappe.user.update_recent(doctype, name) - frappe.response['docs'] = doclist + frappe.response.docs.append(doc) @frappe.whitelist() def getdoctype(doctype, with_parent=False, cached_timestamp=None): """load doctype""" - import frappe.model.doctype - import frappe.model.meta - - doclist = [] + docs = [] # with parent (called from report builder) if with_parent: parent_dt = frappe.model.meta.get_parent_dt(doctype) if parent_dt: - doclist = frappe.model.doctype.get(parent_dt, processed=True) + docs = get_meta_bundle(parent_dt) frappe.response['parent_dt'] = parent_dt - if not doclist: - doclist = frappe.model.doctype.get(doctype, processed=True) + if not docs: + docs = get_meta_bundle(doctype) - frappe.response['restrictions'] = get_restrictions(doclist) + frappe.response['restrictions'] = get_restrictions(docs[0]) - if cached_timestamp and doclist[0].modified==cached_timestamp: + if cached_timestamp and docs[0].modified==cached_timestamp: return "use_cache" - frappe.response['docs'] = doclist + frappe.response.docs.extend(docs) + +def get_meta_bundle(doctype): + bundle = [frappe.widgets.form.meta.get_meta(doctype)] + for df in bundle[0].fields: + if df.fieldtype=="Table": + bundle.append(frappe.widgets.form.meta.get_meta(df.options)) + return bundle def get_docinfo(doctype, name): frappe.response["docinfo"] = { @@ -87,11 +89,15 @@ def get_restrictions(meta): return out def add_attachments(dt, dn): - attachments = {} + attachments = [] for f in frappe.db.sql("""select name, file_name, file_url from `tabFile Data` where attached_to_name=%s and attached_to_doctype=%s""", (dn, dt), as_dict=True): - attachments[f.file_url or f.file_name] = f.name + attachments.append({ + 'name': f.name, + 'file_url': f.file_url, + 'file_name': f.file_name + }) return attachments @@ -121,4 +127,4 @@ def get_badge_info(doctypes, filters): for doctype in doctypes: out[doctype] = frappe.db.get_value(doctype, filters, "count(*)") - return out \ No newline at end of file + return out diff --git a/frappe/widgets/form/meta.py b/frappe/widgets/form/meta.py new file mode 100644 index 0000000000..63f0601864 --- /dev/null +++ b/frappe/widgets/form/meta.py @@ -0,0 +1,157 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +# metadata + +from __future__ import unicode_literals +import frappe, os +from frappe.utils import cstr, cint +from frappe.model.meta import Meta +from frappe.modules import scrub, get_module_path +from frappe.model.workflow import get_workflow_name + +###### + +def get_meta(doctype, cached=True): + if cached: + meta = frappe.cache().get_value("form_meta:" + doctype, lambda: FormMeta(doctype)) + else: + meta = FormMeta(doctype) + + if frappe.local.lang != 'en': + meta.set("__messages", frappe.get_lang_dict("doctype", doctype)) + + return meta + +class FormMeta(Meta): + def __init__(self, doctype): + super(FormMeta, self).__init__(doctype) + self.load_assets() + + def load_assets(self): + self.expand_selects() + self.add_search_fields() + + if not self.istable: + self.add_linked_with() + self.add_code() + self.load_print_formats() + self.load_workflows() + + def as_dict(self): + d = super(FormMeta, self).as_dict() + for k in ("__js", "__css", "__list_js", "__calendar_js", "__map_js", "__linked_with", "__messages"): + d[k] = self.get(k) + + for i, df in enumerate(d.get("fields")): + for k in ("link_doctype", "search_fields"): + df[k] = self.get("fields")[i].get(k) + + return d + + def add_code(self): + path = os.path.join(get_module_path(self.module), 'doctype', scrub(self.name)) + def _get_path(fname): + return os.path.join(path, scrub(fname)) + + self._add_code(_get_path(self.name + '.js'), '__js') + self._add_code(_get_path(self.name + '.css'), "__css") + self._add_code(_get_path(self.name + '_list.js'), '__list_js') + self._add_code(_get_path(self.name + '_calendar.js'), '__calendar_js') + self._add_code(_get_path(self.name + '_map.js'), '__map_js') + + self.add_custom_script() + self.add_code_via_hook("doctype_js", "__js") + + def _add_code(self, path, fieldname): + js = frappe.read_file(path) + if js: + self.set(fieldname, (self.get(fieldname) or "") + "\n\n" + render_jinja(js)) + + def add_code_via_hook(self, hook, fieldname): + hook = "{}:{}".format(hook, self.name) + for app_name in frappe.get_installed_apps(): + for file in frappe.get_hooks(hook, app_name=app_name): + path = frappe.get_app_path(app_name, *file.strip("/").split("/")) + self._add_code(path, fieldname) + + def add_custom_script(self): + """embed all require files""" + # custom script + custom = frappe.db.get_value("Custom Script", {"dt": self.name, + "script_type": "Client"}, "script") or "" + + self.set("__js", (self.get('__js') or '') + "\n\n" + custom) + + def render_jinja(content): + if "{% include" in content: + content = frappe.get_jenv().from_string(content).render() + return content + + def expand_selects(self): + for df in self.get("fields", {"fieldtype": "Select"}): + if df.options and df.options.startswith("link:"): + df.link_doctype = df.options.split("\n")[0][5:] + df.options = '\n'.join([''] + [o.name for o in frappe.db.sql("""select + name from `tab%s` where docstatus<2 order by name asc""" % df.link_doctype, as_dict=1)]) + + def add_search_fields(self): + """add search fields found in the doctypes indicated by link fields' options""" + for df in self.get("fields", {"fieldtype": "Link", "options":["!=", "[Select]"]}): + if df.options: + search_fields = frappe.get_meta(df.options).search_fields + if search_fields: + df.search_fields = map(lambda sf: sf.strip(), search_fields.split(",")) + + def add_linked_with(self): + """add list of doctypes this doctype is 'linked' with""" + links = frappe.db.sql("""select parent, fieldname from tabDocField + where (fieldtype="Link" and options=%s) + or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name)) + links += frappe.db.sql("""select dt as parent, fieldname from `tabCustom Field` + where (fieldtype="Link" and options=%s) + or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name)) + + links = dict(links) + + if not links: + return {} + + ret = {} + + for dt in links: + ret[dt] = { "fieldname": links[dt] } + + for grand_parent, options in frappe.db.sql("""select parent, options from tabDocField + where fieldtype="Table" + and options in (select name from tabDocType + where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)): + + ret[grand_parent] = {"child_doctype": options, "fieldname": links[options] } + if options in ret: + del ret[options] + + self.set("__linked_with", ret) + + def load_print_formats(self): + frappe.response.docs.extend(frappe.db.sql("""select * FROM `tabPrint Format` + WHERE doc_type=%s AND docstatus<2""", (self.name,), as_dict=1, update={"doctype":"Print Format"})) + + def load_workflows(self): + # get active workflow + workflow_name = get_workflow_name(self.name) + + if workflow_name and frappe.db.exists("Workflow", workflow_name): + workflow = frappe.get_doc("Workflow", workflow_name) + frappe.response.docs.append(workflow) + + for d in workflow.get("workflow_document_states"): + frappe.response.docs.append(frappe.get_doc("Workflow State", d.state)) + + +def render_jinja(content): + if "{% include" in content: + content = frappe.get_jenv().from_string(content).render() + return content + + \ No newline at end of file diff --git a/frappe/widgets/form/run_method.py b/frappe/widgets/form/run_method.py index 8f84c3aa53..eb8919ddef 100644 --- a/frappe/widgets/form/run_method.py +++ b/frappe/widgets/form/run_method.py @@ -2,6 +2,7 @@ # MIT License. See license.txt from __future__ import unicode_literals +import json import frappe from frappe import _ @@ -10,47 +11,43 @@ def runserverobj(): """ Run server objects """ - import frappe.model.code - from frappe.model.bean import Bean from frappe.utils import cint wrapper = None method = frappe.form_dict.get('method') - arg = frappe.form_dict.get('args', frappe.form_dict.get("arg")) dt = frappe.form_dict.get('doctype') dn = frappe.form_dict.get('docname') - frappe.response["docs"] = [] - if dt: # not called from a doctype (from a page) if not dn: dn = dt # single - so = frappe.model.code.get_obj(dt, dn) + doc = frappe.get_doc(dt, dn) else: - bean = Bean() - bean.from_compressed(frappe.form_dict.get('docs'), dn) - if not bean.has_read_perm(): - frappe.msgprint(_("No Permission"), raise_exception = True) - so = bean.make_controller() - bean.check_if_latest(method="runserverobj") + doc = frappe.get_doc(json.loads(frappe.form_dict.get('docs'))) + doc.check_if_latest() - check_guest_access(so.doc) + if not doc.has_permission("read"): + frappe.msgprint(_("No Permission"), raise_exception = True) - if so: - r = frappe.model.code.run_server_obj(so, method, arg) + if doc: + try: + args = frappe.form_dict.get('args', frappe.form_dict.get("arg")) + args = json.loads(args) + except ValueError: + r = doc.run_method(method, args) + except TypeError: + r = doc.run_method(method) + else: + r = doc.run_method(method, **args) + if r: #build output as csv if cint(frappe.form_dict.get('as_csv')): - make_csv_output(r, so.doc.doctype) + make_csv_output(r, doc.doctype) else: frappe.response['message'] = r - frappe.response['docs'] += so.doclist - -def check_guest_access(doc): - if frappe.session['user']=='Guest' and not frappe.db.sql("select name from tabDocPerm where role='Guest' and parent=%s and ifnull(`read`,0)=1", doc.doctype): - frappe.msgprint("Guest not allowed to call this object") - raise Exception + frappe.response.docs.append(doc) def make_csv_output(res, dt): """send method response as downloadable CSV file""" diff --git a/frappe/widgets/form/save.py b/frappe/widgets/form/save.py index d8430b5c1a..7d7eba846d 100644 --- a/frappe/widgets/form/save.py +++ b/frappe/widgets/form/save.py @@ -2,27 +2,25 @@ # MIT License. See license.txt from __future__ import unicode_literals -import frappe +import frappe, json @frappe.whitelist() def savedocs(): """save / submit / update doclist""" try: - wrapper = frappe.bean() - wrapper.from_compressed(frappe.form_dict.docs, frappe.form_dict.docname) + doc = frappe.get_doc(json.loads(frappe.form_dict.doc)) # action - action = frappe.form_dict.action - if action=='Update': action='update_after_submit' + doc.docstatus = {"Save":0, "Submit": 1, "Update": 1, "Cancel": 2}[frappe.form_dict.action] try: - getattr(wrapper, action.lower())() + doc.save() except NameError, e: frappe.msgprint(frappe._("Name Exists")) raise # update recent documents - frappe.user.update_recent(wrapper.doc.doctype, wrapper.doc.name) - send_updated_docs(wrapper) + frappe.user.update_recent(doc.doctype, doc.name) + send_updated_docs(doc) except Exception, e: frappe.msgprint(frappe._('Did not save')) @@ -33,20 +31,21 @@ def savedocs(): def cancel(doctype=None, name=None): """cancel a doclist""" try: - wrapper = frappe.bean(doctype, name) - wrapper.cancel() - send_updated_docs(wrapper) + doc = frappe.get_doc(doctype, name) + doc.cancel() + send_updated_docs(doc) except Exception, e: frappe.errprint(frappe.utils.get_traceback()) frappe.msgprint(frappe._("Did not cancel")) raise -def send_updated_docs(wrapper): +def send_updated_docs(doc): from load import get_docinfo - get_docinfo(wrapper.doc.doctype, wrapper.doc.name) + get_docinfo(doc.doctype, doc.name) - frappe.response['main_doc_name'] = wrapper.doc.name - frappe.response['doctype'] = wrapper.doc.doctype - frappe.response['docname'] = wrapper.doc.name - frappe.response['docs'] = wrapper.doclist \ No newline at end of file + d = doc.as_dict() + if hasattr(doc, 'localname'): + d["localname"] = doc.localname + + frappe.response.docs.append(d) diff --git a/frappe/widgets/form/utils.py b/frappe/widgets/form/utils.py index de71e4cf30..d8bc2f1847 100644 --- a/frappe/widgets/form/utils.py +++ b/frappe/widgets/form/utils.py @@ -1,8 +1,9 @@ # 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, json +import frappe.widgets.form.meta from frappe import _ @@ -34,46 +35,44 @@ def validate_link(): """validate link when updated by user""" import frappe import frappe.utils - + value, options, fetch = frappe.form_dict.get('value'), frappe.form_dict.get('options'), frappe.form_dict.get('fetch') # no options, don't validate if not options or options=='null' or options=='undefined': frappe.response['message'] = 'Ok' return - + if frappe.db.sql("select name from `tab%s` where name=%s" % (options, '%s'), (value,)): - + # get fetch values if fetch: frappe.response['fetch_values'] = [frappe.utils.parse_val(c) \ for c in frappe.db.sql("select %s from `tab%s` where name=%s" \ % (fetch, options, '%s'), (value,))[0]] - + frappe.response['message'] = 'Ok' @frappe.whitelist() def add_comment(doclist): """allow any logged user to post a comment""" doclist = json.loads(doclist) - + doclist[0]["__islocal"] = 1 - doclistobj = frappe.bean(doclist) + doclistobj = frappe.get_doc(doclist) doclistobj.ignore_permissions = True doclistobj.save() - - return [d.fields for d in doclist] - return save(doclist) + return doclistobj.as_dict() @frappe.whitelist() def get_next(doctype, name, prev): import frappe.widgets.reportview - + prev = int(prev) field = "`tab%s`.name" % doctype res = frappe.widgets.reportview.execute(doctype, - fields = [field], + fields = [field], filters = [[doctype, "name", "<" if prev else ">", name]], order_by = field + " " + ("desc" if prev else "asc"), limit_start=0, limit_page_length=1, as_list=True) @@ -87,33 +86,31 @@ def get_next(doctype, name, prev): @frappe.whitelist() def get_linked_docs(doctype, name, metadata_loaded=None): if not metadata_loaded: metadata_loaded = [] - meta = frappe.get_doctype(doctype, True) - linkinfo = meta[0].get("__linked_with") + meta = frappe.widgets.form.meta.get_meta(doctype) + linkinfo = meta.get("__linked_with") results = {} for dt, link in linkinfo.items(): link["doctype"] = dt - linkmeta = frappe.get_doctype(dt, True) - if not linkmeta[0].get("issingle"): - fields = [d.fieldname for d in linkmeta.get({"parent":dt, "in_list_view":1, + linkmeta = frappe.widgets.form.meta.get_meta(dt) + if not linkmeta.get("issingle"): + fields = [d.fieldname for d in linkmeta.get("fields", {"in_list_view":1, "fieldtype": ["not in", ["Image", "HTML", "Button", "Table"]]})] \ + ["name", "modified", "docstatus"] fields = ["`tab{dt}`.`{fn}`".format(dt=dt, fn=sf.strip()) for sf in fields if sf] if link.get("child_doctype"): - ret = frappe.get_list(doctype=dt, fields=fields, + ret = frappe.get_list(doctype=dt, fields=fields, filters=[[link.get('child_doctype'), link.get("fieldname"), '=', name]]) - + else: - ret = frappe.get_list(doctype=dt, fields=fields, + ret = frappe.get_list(doctype=dt, fields=fields, filters=[[dt, link.get("fieldname"), '=', name]]) - - if ret: + + if ret: results[dt] = ret - + if not dt in metadata_loaded: - if not "docs" in frappe.local.response: - frappe.local.response.docs = [] - frappe.local.response.docs += linkmeta - - return results \ No newline at end of file + frappe.local.response.docs.append(linkmeta) + + return results diff --git a/frappe/widgets/moduleview.py b/frappe/widgets/moduleview.py index 41da851126..572f08163a 100644 --- a/frappe/widgets/moduleview.py +++ b/frappe/widgets/moduleview.py @@ -173,4 +173,4 @@ def get_report_list(module, is_standard="No"): and docstatus in (0, NULL) and ifnull(is_standard, "No")=%s and ifnull(disabled,0) != 1 - order by name""", (module, is_standard), as_dict=True) \ No newline at end of file + order by name""", (module, is_standard), as_dict=True) diff --git a/frappe/widgets/page.py b/frappe/widgets/page.py index 953771544c..d586339146 100644 --- a/frappe/widgets/page.py +++ b/frappe/widgets/page.py @@ -3,22 +3,20 @@ from __future__ import unicode_literals import frappe -import frappe.model.doc -import frappe.model.code @frappe.whitelist() def get(name): """ Return the :term:`doclist` of the `Page` specified by `name` """ - page = frappe.bean("Page", name) - if has_permission(page.doclist): - page.run_method("get_from_files") - return page.doclist + page = frappe.get_doc('Page', name) + if has_permission(page): + page.load_assets() + return page else: frappe.response['403'] = 1 raise frappe.PermissionError, 'No read permission for Page %s' % \ - (page.doclist[0].title or name,) + (page.title or name) @frappe.whitelist(allow_guest=True) def getpage(): @@ -26,24 +24,19 @@ def getpage(): Load the page from `frappe.form` and send it via `frappe.response` """ page = frappe.form_dict.get('name') - doclist = get(page) + doc = get(page) - if has_permission(doclist): - # load translations - if frappe.lang != "en": - frappe.response["__messages"] = frappe.get_lang_dict("page", page) + # load translations + if frappe.lang != "en": + frappe.response["__messages"] = frappe.get_lang_dict("page", page) - frappe.response['docs'] = doclist - else: - frappe.response['403'] = 1 - raise frappe.PermissionError, 'No read permission for Page %s' % \ - (doclist[0].title or page, ) - -def has_permission(page_doclist): + frappe.response.docs.append(doc) + +def has_permission(page): if frappe.user.name == "Administrator" or "System Manager" in frappe.user.get_roles(): return True - page_roles = [d.role for d in page_doclist if d.fields.get("doctype")=="Page Role"] + page_roles = [d.role for d in page.get("roles")] if page_roles: if frappe.session.user == "Guest" and "Guest" not in page_roles: return False @@ -51,7 +44,7 @@ def has_permission(page_doclist): # check if roles match return False - if not frappe.has_permission("Page", ptype="read", refdoc=page_doclist[0].name): + if not frappe.has_permission("Page", ptype="read", doc=page): # check if there are any restrictions return False else: diff --git a/frappe/widgets/query_report.py b/frappe/widgets/query_report.py index 384c6979f1..d6e5d2f7f3 100644 --- a/frappe/widgets/query_report.py +++ b/frappe/widgets/query_report.py @@ -13,15 +13,15 @@ from frappe.utils import flt, cint import frappe.widgets.reportview def get_report_doc(report_name): - bean = frappe.bean("Report", report_name) - if not bean.has_read_perm(): + doc = frappe.get_doc("Report", report_name) + if not doc.has_permission("read"): raise frappe.PermissionError("You don't have access to: {report}".format(report=report_name)) - if not frappe.has_permission(bean.doc.ref_doctype, "report"): + if not frappe.has_permission(doc.ref_doctype, "report"): raise frappe.PermissionError("You don't have access to get a report on: {doctype}".format( - doctype=bean.doc.ref_doctype)) + doctype=doc.ref_doctype)) - return bean.doc + return doc @frappe.whitelist() def get_script(report_name): diff --git a/frappe/widgets/reportview.py b/frappe/widgets/reportview.py index a2a193cb20..e0b189b6c4 100644 --- a/frappe/widgets/reportview.py +++ b/frappe/widgets/reportview.py @@ -52,8 +52,7 @@ def compress(data): @frappe.whitelist() def save_report(): """save report""" - from frappe.model.doc import Document - + data = frappe.local.form_dict if frappe.db.exists('Report', data['name']): d = Document('Report', data['name']) @@ -64,7 +63,7 @@ def save_report(): d.report_type = "Report Builder" d.json = data['json'] - frappe.bean([d]).save() + frappe.get_doc(d).save() frappe.msgprint("%s saved." % d.name) return d.name @@ -119,14 +118,13 @@ def get_labels(columns): def delete_items(): """delete selected items""" import json - from frappe.model.code import get_obj - + il = json.loads(frappe.form_dict.get('items')) doctype = frappe.form_dict.get('doctype') for d in il: try: - dt_obj = get_obj(doctype, d) + dt_obj = frappe.get_doc(doctype, d) if hasattr(dt_obj, 'on_trash'): dt_obj.on_trash() frappe.delete_doc(doctype, d) diff --git a/frappe/widgets/search.py b/frappe/widgets/search.py index f740e2a6a3..53c794c13d 100644 --- a/frappe/widgets/search.py +++ b/frappe/widgets/search.py @@ -22,7 +22,7 @@ def search_widget(doctype, txt, query=None, searchfield="name", start=0, import json filters = json.loads(filters) - meta = frappe.get_doctype(doctype) + meta = frappe.get_meta(doctype) standard_queries = frappe.get_hooks().standard_queries or [] if standard_queries: @@ -57,9 +57,9 @@ def search_widget(doctype, txt, query=None, searchfield="name", start=0, # build from doctype if txt: filters.append([doctype, searchfield or "name", "like", txt + "%"]) - if meta.get({"parent":doctype, "fieldname":"enabled", "fieldtype":"Check"}): + if meta.get("fields", {"fieldname":"enabled", "fieldtype":"Check"}): filters.append([doctype, "enabled", "=", 1]) - if meta.get({"parent":doctype, "fieldname":"disabled", "fieldtype":"Check"}): + if meta.get("fields", {"fieldname":"disabled", "fieldtype":"Check"}): filters.append([doctype, "disabled", "!=", 1]) frappe.response["values"] = frappe.widgets.reportview.execute(doctype, @@ -68,12 +68,12 @@ def search_widget(doctype, txt, query=None, searchfield="name", start=0, def get_std_fields_list(meta, key): # get additional search fields - sflist = meta[0].search_fields and meta[0].search_fields.split(",") or [] + sflist = meta.search_fields and meta.search_fields.split(",") or [] sflist = ['name'] + sflist if not key in sflist: sflist = sflist + [key] - return ['`tab%s`.`%s`' % (meta[0].name, f.strip()) for f in sflist] + return ['`tab%s`.`%s`' % (meta.name, f.strip()) for f in sflist] def build_for_autosuggest(res): results = []