Merge branch 'develop' of http://github.com/frappe/frappe into develop
This commit is contained in:
commit
629221c8bd
981 changed files with 157508 additions and 26827 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -8,4 +8,3 @@ locale
|
|||
*.egg-info
|
||||
dist/
|
||||
build/
|
||||
docs/
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ install:
|
|||
- sudo apt-get purge -y mysql-common
|
||||
- wget https://raw.githubusercontent.com/frappe/bench/master/install_scripts/setup_frappe.sh
|
||||
- sudo bash setup_frappe.sh --skip-setup-bench --mysql-root-password travis
|
||||
- sudo pip install --upgrade pip
|
||||
- sudo service redis-server start
|
||||
- rm $TRAVIS_BUILD_DIR/.git/shallow
|
||||
- cd ~/ && bench init frappe-bench --frappe-path $TRAVIS_BUILD_DIR
|
||||
- cp -r $TRAVIS_BUILD_DIR/test_sites/test_site ~/frappe-bench/sites/
|
||||
|
|
@ -23,10 +21,13 @@ install:
|
|||
script:
|
||||
- cd ~/frappe-bench
|
||||
- bench use test_site
|
||||
- bench setup redis-cache
|
||||
- bench setup redis-async-broker
|
||||
- bench setup procfile --with-celery-broker
|
||||
- bench reinstall
|
||||
- bench build
|
||||
- bench build-website
|
||||
- bench serve &
|
||||
- bench start &
|
||||
- sleep 10
|
||||
- bench --verbose run-tests --driver Firefox
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
## frappe [](https://travis-ci.org/frappe/frappe)
|
||||
## Frappe Framework
|
||||
|
||||
[](https://travis-ci.org/frappe/frappe)
|
||||
|
||||
Full-stack web application framework that uses Python and MariaDB on the server side and a tightly integrated client side library. [Built for ERPNext](https://erpnext.com)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ globals attached to frappe module
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from werkzeug.local import Local, release_local
|
||||
from functools import wraps
|
||||
import os, importlib, inspect, logging, json
|
||||
|
||||
# public
|
||||
|
|
@ -14,6 +15,7 @@ from frappe.__version__ import __version__
|
|||
from .exceptions import *
|
||||
from .utils.jinja import get_jenv, get_template, render_template
|
||||
|
||||
|
||||
local = Local()
|
||||
|
||||
class _dict(dict):
|
||||
|
|
@ -36,13 +38,16 @@ class _dict(dict):
|
|||
def copy(self):
|
||||
return _dict(dict(self).copy())
|
||||
|
||||
def _(msg):
|
||||
def _(msg, lang=None):
|
||||
"""Returns translated string in current lang, if exists."""
|
||||
if local.lang == "en":
|
||||
if not lang:
|
||||
lang = local.lang
|
||||
|
||||
if lang == "en":
|
||||
return msg
|
||||
|
||||
from frappe.translate import get_full_dict
|
||||
return get_full_dict(local.lang).get(msg, msg)
|
||||
return get_full_dict(local.lang).get(msg) or msg
|
||||
|
||||
def get_lang_dict(fortype, name=None):
|
||||
"""Returns the translated language dict for the given type and name.
|
||||
|
|
@ -64,7 +69,6 @@ db = local("db")
|
|||
conf = local("conf")
|
||||
form = form_dict = local("form_dict")
|
||||
request = local("request")
|
||||
request_method = local("request_method")
|
||||
response = local("response")
|
||||
session = local("session")
|
||||
user = local("user")
|
||||
|
|
@ -87,6 +91,7 @@ def init(site, sites_path=None):
|
|||
local.error_log = []
|
||||
local.message_log = []
|
||||
local.debug_log = []
|
||||
local.realtime_log = []
|
||||
local.flags = _dict({
|
||||
"ran_schedulers": [],
|
||||
"redirect_location": "",
|
||||
|
|
@ -106,9 +111,9 @@ def init(site, sites_path=None):
|
|||
local.sites_path = sites_path
|
||||
local.site_path = os.path.join(sites_path, site)
|
||||
|
||||
local.request_method = request.method if request else None
|
||||
local.request_ip = None
|
||||
local.response = _dict({"docs":[]})
|
||||
local.task_id = None
|
||||
|
||||
local.conf = _dict(get_site_config())
|
||||
local.lang = local.conf.lang or "en"
|
||||
|
|
@ -239,7 +244,7 @@ def msgprint(msg, small=0, raise_exception=0, as_table=False):
|
|||
msg = '<table border="1px" style="border-collapse: collapse" cellpadding="2px">' + ''.join(['<tr>'+''.join(['<td>%s</td>' % c for c in r])+'</tr>' for r in msg]) + '</table>'
|
||||
|
||||
if flags.print_messages:
|
||||
print "Message: " + repr(msg)
|
||||
print "Message: " + repr(msg).encode("utf-8")
|
||||
|
||||
message_log.append((small and '__small:' or '')+cstr(msg or ''))
|
||||
_raise_exception()
|
||||
|
|
@ -305,7 +310,8 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message
|
|||
as_markdown=False, bulk=False, reference_doctype=None, reference_name=None,
|
||||
unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
|
||||
attachments=None, content=None, doctype=None, name=None, reply_to=None,
|
||||
cc=(), message_id=None, as_bulk=False, send_after=None):
|
||||
cc=(), show_as_cc=(), message_id=None, as_bulk=False, send_after=None, expose_recipients=False,
|
||||
bulk_priority=1):
|
||||
"""Send email using user's default **Email Account** or global default **Email Account**.
|
||||
|
||||
|
||||
|
|
@ -315,6 +321,7 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message
|
|||
:param message: (or `content`) Email Content.
|
||||
:param as_markdown: Convert content markdown to HTML.
|
||||
:param bulk: Send via scheduled email sender **Bulk Email**. Don't send immediately.
|
||||
:param bulk_priority: Priority for bulk email, default 1.
|
||||
:param reference_doctype: (or `doctype`) Append as communication to this DocType.
|
||||
:param reference_name: (or `name`) Append as communication to this document name.
|
||||
:param unsubscribe_method: Unsubscribe url with options email, doctype, name. e.g. `/api/method/unsubscribe`
|
||||
|
|
@ -323,6 +330,7 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message
|
|||
:param reply_to: Reply-To email id.
|
||||
:param message_id: Used for threading. If a reply is received to this email, Message-Id is sent back as In-Reply-To in received email.
|
||||
:param send_after: Send after the given datetime.
|
||||
:param expose_recipients: Display all recipients in the footer message - "This email was sent to"
|
||||
"""
|
||||
|
||||
if bulk or as_bulk:
|
||||
|
|
@ -331,7 +339,8 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message
|
|||
subject=subject, message=content or message,
|
||||
reference_doctype = doctype or reference_doctype, reference_name = name or reference_name,
|
||||
unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, unsubscribe_message=unsubscribe_message,
|
||||
attachments=attachments, reply_to=reply_to, cc=cc, message_id=message_id, send_after=send_after)
|
||||
attachments=attachments, reply_to=reply_to, cc=cc, show_as_cc=show_as_cc, message_id=message_id, send_after=send_after,
|
||||
expose_recipients=expose_recipients, bulk_priority=bulk_priority)
|
||||
else:
|
||||
import frappe.email
|
||||
if as_markdown:
|
||||
|
|
@ -346,7 +355,8 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message
|
|||
logger = None
|
||||
whitelisted = []
|
||||
guest_methods = []
|
||||
def whitelist(allow_guest=False):
|
||||
xss_safe_methods = []
|
||||
def whitelist(allow_guest=False, xss_safe=False):
|
||||
"""
|
||||
Decorator for whitelisting a function and making it accessible via HTTP.
|
||||
Standard request will be `/api/method/[path.to.method]`
|
||||
|
|
@ -360,12 +370,15 @@ def whitelist(allow_guest=False):
|
|||
pass
|
||||
"""
|
||||
def innerfn(fn):
|
||||
global whitelisted, guest_methods
|
||||
global whitelisted, guest_methods, xss_safe_methods
|
||||
whitelisted.append(fn)
|
||||
|
||||
if allow_guest:
|
||||
guest_methods.append(fn)
|
||||
|
||||
if xss_safe:
|
||||
xss_safe_methods.append(fn)
|
||||
|
||||
return fn
|
||||
|
||||
return innerfn
|
||||
|
|
@ -405,7 +418,7 @@ def clear_cache(user=None, doctype=None):
|
|||
|
||||
frappe.local.role_permissions = {}
|
||||
|
||||
def has_permission(doctype, ptype="read", doc=None, user=None, verbose=False):
|
||||
def has_permission(doctype, ptype="read", doc=None, user=None, verbose=False, throw=False):
|
||||
"""Raises `frappe.PermissionError` if not permitted.
|
||||
|
||||
:param doctype: DocType for which permission is to be check.
|
||||
|
|
@ -413,7 +426,14 @@ def has_permission(doctype, ptype="read", doc=None, user=None, verbose=False):
|
|||
:param doc: [optional] Checks User permissions for given doc.
|
||||
:param user: [optional] Check for given user. Default: current user."""
|
||||
import frappe.permissions
|
||||
return frappe.permissions.has_permission(doctype, ptype, doc=doc, verbose=verbose, user=user)
|
||||
out = frappe.permissions.has_permission(doctype, ptype, doc=doc, verbose=verbose, user=user)
|
||||
if throw and not out:
|
||||
if doc:
|
||||
frappe.throw(_("No permission for {0}").format(doc.doctype + " " + doc.name))
|
||||
else:
|
||||
frappe.throw(_("No permission for {0}").format(doctype))
|
||||
|
||||
return out
|
||||
|
||||
def has_website_permission(doctype, ptype="read", doc=None, user=None, verbose=False):
|
||||
"""Raises `frappe.PermissionError` if not permitted.
|
||||
|
|
@ -428,6 +448,9 @@ def has_website_permission(doctype, ptype="read", doc=None, user=None, verbose=F
|
|||
|
||||
hooks = (get_hooks("has_website_permission") or {}).get(doctype, [])
|
||||
if hooks:
|
||||
if isinstance(doc, basestring):
|
||||
doc = get_doc(doctype, doc)
|
||||
|
||||
for method in hooks:
|
||||
result = call(get_attr(method), doc=doc, ptype=ptype, user=user, verbose=verbose)
|
||||
# if even a single permission check is Falsy
|
||||
|
|
@ -443,7 +466,7 @@ def has_website_permission(doctype, ptype="read", doc=None, user=None, verbose=F
|
|||
def is_table(doctype):
|
||||
"""Returns True if `istable` property (indicating child Table) is set for given DocType."""
|
||||
def get_tables():
|
||||
return db.sql_list("select name from tabDocType where ifnull(istable,0)=1")
|
||||
return db.sql_list("select name from tabDocType where istable=1")
|
||||
|
||||
tables = cache().get_value("is_table", get_tables)
|
||||
return doctype in tables
|
||||
|
|
@ -453,11 +476,14 @@ def get_precision(doctype, fieldname, currency=None, doc=None):
|
|||
from frappe.model.meta import get_field_precision
|
||||
return get_field_precision(get_meta(doctype).get_field(fieldname), doc, currency)
|
||||
|
||||
def generate_hash(txt=None):
|
||||
def generate_hash(txt=None, length=None):
|
||||
"""Generates random hash for given text + current timestamp + random string."""
|
||||
import hashlib, time
|
||||
from .utils import random_string
|
||||
return hashlib.sha224((txt or "") + repr(time.time()) + repr(random_string(8))).hexdigest()
|
||||
digest = hashlib.sha224((txt or "") + repr(time.time()) + repr(random_string(8))).hexdigest()
|
||||
if length:
|
||||
digest = digest[:length]
|
||||
return digest
|
||||
|
||||
def reset_metadata_version():
|
||||
"""Reset `metadata_version` (Client (Javascript) build ID) hash."""
|
||||
|
|
@ -538,9 +564,9 @@ def delete_doc_if_exists(doctype, name):
|
|||
if db.exists(doctype, name):
|
||||
delete_doc(doctype, name)
|
||||
|
||||
def reload_doctype(doctype):
|
||||
def reload_doctype(doctype, force=False):
|
||||
"""Reload DocType from model (`[module]/[doctype]/[name]/[name].json`) files."""
|
||||
reload_doc(scrub(db.get_value("DocType", doctype, "module")), "doctype", scrub(doctype))
|
||||
reload_doc(scrub(db.get_value("DocType", doctype, "module")), "doctype", scrub(doctype), force=force)
|
||||
|
||||
def reload_doc(module, dt=None, dn=None, force=False):
|
||||
"""Reload Document from model (`[module]/[doctype]/[name]/[name].json`) files.
|
||||
|
|
@ -597,7 +623,8 @@ def get_pymodule_path(modulename, *joins):
|
|||
|
||||
:param modulename: Python module name.
|
||||
:param *joins: Join additional path elements using `os.path.join`."""
|
||||
joins = [scrub(part) for part in joins]
|
||||
if not "public" in 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):
|
||||
|
|
@ -623,6 +650,9 @@ def get_installed_apps(sort=False):
|
|||
if getattr(flags, "in_install_db", True):
|
||||
return []
|
||||
|
||||
if not db:
|
||||
connect()
|
||||
|
||||
installed = json.loads(db.get_global("installed_apps") or "[]")
|
||||
|
||||
if sort:
|
||||
|
|
@ -719,6 +749,9 @@ def get_file_json(path):
|
|||
def read_file(path, raise_not_found=False):
|
||||
"""Open a file and return its content as Unicode."""
|
||||
from frappe.utils import cstr
|
||||
if isinstance(path, unicode):
|
||||
path = path.encode("utf-8")
|
||||
|
||||
if os.path.exists(path):
|
||||
with open(path, "r") as f:
|
||||
return cstr(f.read())
|
||||
|
|
@ -729,6 +762,10 @@ def read_file(path, raise_not_found=False):
|
|||
|
||||
def get_attr(method_string):
|
||||
"""Get python method object from its name."""
|
||||
app_name = method_string.split(".")[0]
|
||||
if not local.flags.in_install and app_name not in get_installed_apps():
|
||||
throw(_("App {0} is not installed").format(app_name), AppNotInstalledError)
|
||||
|
||||
modulename = '.'.join(method_string.split('.')[:-1])
|
||||
methodname = method_string.split('.')[-1]
|
||||
return getattr(get_module(modulename), methodname)
|
||||
|
|
@ -898,6 +935,20 @@ def get_all(doctype, *args, **kwargs):
|
|||
kwargs["limit_page_length"] = 0
|
||||
return get_list(doctype, *args, **kwargs)
|
||||
|
||||
def get_value(*args, **kwargs):
|
||||
"""Returns a document property or list of properties.
|
||||
|
||||
Alias for `frappe.db.get_value`
|
||||
|
||||
:param doctype: DocType name.
|
||||
:param filters: Filters like `{"x":"y"}` or name of the document. `None` if Single DocType.
|
||||
:param fieldname: Column name.
|
||||
:param ignore: Don't raise exception if table, column is missing.
|
||||
:param as_dict: Return values as dict.
|
||||
:param debug: Print query in error log.
|
||||
"""
|
||||
return db.get_value(*args, **kwargs)
|
||||
|
||||
def add_version(doc):
|
||||
"""Insert a new **Version** of the given document.
|
||||
A **Version** is a JSON dump of the current document state."""
|
||||
|
|
@ -912,6 +963,9 @@ def as_json(obj, indent=1):
|
|||
from frappe.utils.response import json_handler
|
||||
return json.dumps(obj, indent=indent, sort_keys=True, default=json_handler)
|
||||
|
||||
def are_emails_muted():
|
||||
return flags.mute_emails or conf.get("mute_emails") or False
|
||||
|
||||
def get_test_records(doctype):
|
||||
"""Returns list of objects from `test_records.json` in the given doctype's folder."""
|
||||
from frappe.modules import get_doctype_module, get_module_path
|
||||
|
|
@ -989,3 +1043,43 @@ def get_logger(module=None):
|
|||
logging_setup_complete = True
|
||||
|
||||
return logging.getLogger(module or "frappe")
|
||||
|
||||
def publish_realtime(*args, **kwargs):
|
||||
"""Publish real-time updates
|
||||
|
||||
:param event: Event name, like `task_progress` etc.
|
||||
:param message: JSON message object. For async must contain `task_id`
|
||||
:param room: Room in which to publish update (default entire site)
|
||||
:param user: Transmit to user
|
||||
:param doctype: Transmit to doctype, docname
|
||||
:param docname: Transmit to doctype, docname"""
|
||||
import frappe.async
|
||||
|
||||
return frappe.async.publish_realtime(*args, **kwargs)
|
||||
|
||||
def local_cache(namespace, key, generator, regenerate_if_none=False):
|
||||
"""A key value store for caching within a request
|
||||
|
||||
:param namespace: frappe.local.cache[namespace]
|
||||
:param key: frappe.local.cache[namespace][key] used to retrieve value
|
||||
:param generator: method to generate a value if not found in store
|
||||
|
||||
"""
|
||||
if namespace not in local.cache:
|
||||
local.cache[namespace] = {}
|
||||
|
||||
if key not in local.cache[namespace]:
|
||||
local.cache[namespace][key] = generator()
|
||||
|
||||
elif local.cache[namespace][key]==None and regenerate_if_none:
|
||||
# if key exists but the previous result was None
|
||||
local.cache[namespace][key] = generator()
|
||||
|
||||
return local.cache[namespace][key]
|
||||
|
||||
def get_doctype_app(doctype):
|
||||
def _get_doctype_app():
|
||||
doctype_module = local.db.get_value("DocType", doctype, "module")
|
||||
return local.module_app[scrub(doctype_module)]
|
||||
|
||||
return local_cache("doctype_app", doctype, generator=_get_doctype_app)
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
from __future__ import unicode_literals
|
||||
__version__ = "5.0.29"
|
||||
__version__ = "6.16.4"
|
||||
|
|
|
|||
|
|
@ -58,13 +58,13 @@ def handle():
|
|||
if frappe.local.request.method=="GET":
|
||||
if not doc.has_permission("read"):
|
||||
frappe.throw(_("Not permitted"), frappe.PermissionError)
|
||||
doc.run_method(method, **frappe.local.form_dict)
|
||||
frappe.local.response.update({"data": doc.run_method(method, **frappe.local.form_dict)})
|
||||
|
||||
if frappe.local.request.method=="POST":
|
||||
if not doc.has_permission("write"):
|
||||
frappe.throw(_("Not permitted"), frappe.PermissionError)
|
||||
|
||||
doc.run_method(method, **frappe.local.form_dict)
|
||||
frappe.local.response.update({"data": doc.run_method(method, **frappe.local.form_dict)})
|
||||
frappe.db.commit()
|
||||
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,28 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import sys, os
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import MySQLdb
|
||||
|
||||
from werkzeug.wrappers import Request, Response
|
||||
from werkzeug.wrappers import Request
|
||||
from werkzeug.local import LocalManager
|
||||
from werkzeug.exceptions import HTTPException, NotFound
|
||||
from werkzeug.contrib.profiler import ProfilerMiddleware
|
||||
from werkzeug.wsgi import SharedDataMiddleware
|
||||
from werkzeug.serving import run_with_reloader
|
||||
|
||||
|
||||
import mimetypes
|
||||
import frappe
|
||||
import frappe.handler
|
||||
import frappe.auth
|
||||
import frappe.api
|
||||
import frappe.async
|
||||
import frappe.utils.response
|
||||
import frappe.website.render
|
||||
from frappe.utils import get_site_name
|
||||
from frappe.utils import get_site_name, get_site_path
|
||||
from frappe.middlewares import StaticDataMiddleware
|
||||
|
||||
from frappe.utils.error import make_error_snapshot
|
||||
|
||||
local_manager = LocalManager([frappe.local])
|
||||
|
||||
_site = None
|
||||
|
|
@ -30,6 +33,21 @@ _sites_path = os.environ.get("SITES_PATH", ".")
|
|||
|
||||
logger = frappe.get_logger()
|
||||
|
||||
class RequestContext(object):
|
||||
|
||||
def __init__(self, environ):
|
||||
self.request = Request(environ)
|
||||
|
||||
def __enter__(self):
|
||||
frappe.local.request = self.request
|
||||
init_site(self.request)
|
||||
make_form_dict(self.request)
|
||||
frappe.local.http_request = frappe.auth.HTTPRequest()
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
frappe.destroy()
|
||||
|
||||
|
||||
@Request.application
|
||||
def application(request):
|
||||
frappe.local.request = request
|
||||
|
|
@ -56,6 +74,9 @@ def application(request):
|
|||
elif frappe.request.path.startswith('/backups'):
|
||||
response = frappe.utils.response.download_backup(request.path)
|
||||
|
||||
elif frappe.request.path.startswith('/private/files/'):
|
||||
response = frappe.utils.response.download_private_file(request.path)
|
||||
|
||||
elif frappe.local.request.method in ('GET', 'HEAD'):
|
||||
response = frappe.website.render.render(request.path)
|
||||
|
||||
|
|
@ -82,8 +103,12 @@ def application(request):
|
|||
if frappe.local.is_ajax or 'application/json' in request.headers.get('Accept', ''):
|
||||
response = frappe.utils.response.report_error(http_status_code)
|
||||
else:
|
||||
traceback = "<pre>"+frappe.get_traceback()+"</pre>"
|
||||
if frappe.local.flags.disable_traceback:
|
||||
traceback = ""
|
||||
|
||||
frappe.respond_as_web_page("Server Error",
|
||||
"<pre>"+frappe.get_traceback()+"</pre>",
|
||||
traceback,
|
||||
http_status_code=http_status_code)
|
||||
response = frappe.website.render.render("message", http_status_code=http_status_code)
|
||||
|
||||
|
|
@ -94,10 +119,13 @@ def application(request):
|
|||
if http_status_code==500:
|
||||
logger.error('Request Error')
|
||||
|
||||
make_error_snapshot(e)
|
||||
|
||||
else:
|
||||
if frappe.local.request.method in ("POST", "PUT") and frappe.db:
|
||||
frappe.db.commit()
|
||||
rollback = False
|
||||
if (frappe.local.request.method in ("POST", "PUT") or frappe.local.flags.commit) and frappe.db:
|
||||
if frappe.db.transaction_writes:
|
||||
frappe.db.commit()
|
||||
rollback = False
|
||||
|
||||
# update session
|
||||
if getattr(frappe.local, "session_obj", None):
|
||||
|
|
@ -105,6 +133,10 @@ def application(request):
|
|||
if updated_in_db:
|
||||
frappe.db.commit()
|
||||
|
||||
# publish realtime
|
||||
for args in frappe.local.realtime_log:
|
||||
frappe.async.emit_via_redis(*args)
|
||||
|
||||
finally:
|
||||
if frappe.local.request.method in ("POST", "PUT") and frappe.db and rollback:
|
||||
frappe.db.rollback()
|
||||
|
|
@ -134,6 +166,8 @@ def make_form_dict(request):
|
|||
frappe.local.form_dict.pop("_")
|
||||
|
||||
application = local_manager.make_middleware(application)
|
||||
application.debug = True
|
||||
|
||||
|
||||
def serve(port=8000, profile=False, site=None, sites_path='.'):
|
||||
global application, _site, _sites_path
|
||||
|
|
@ -154,5 +188,10 @@ def serve(port=8000, profile=False, site=None, sites_path='.'):
|
|||
b'/files': os.path.abspath(sites_path).encode("utf-8")
|
||||
})
|
||||
|
||||
application.debug = True
|
||||
application.config = {
|
||||
'SERVER_NAME': 'localhost:8000'
|
||||
}
|
||||
|
||||
run_simple('0.0.0.0', int(port), application, use_reloader=True,
|
||||
use_debugger=True, use_evalex=True, threaded=True)
|
||||
|
|
|
|||
228
frappe/async.py
Normal file
228
frappe/async.py
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
import frappe
|
||||
import os
|
||||
import time
|
||||
import redis
|
||||
from functools import wraps
|
||||
from frappe.utils import get_site_path
|
||||
from frappe import conf
|
||||
|
||||
END_LINE = '<!-- frappe: end-file -->'
|
||||
TASK_LOG_MAX_AGE = 86400 # 1 day in seconds
|
||||
redis_server = None
|
||||
|
||||
def handler(f):
|
||||
cmd = f.__module__ + '.' + f.__name__
|
||||
|
||||
def run(args, set_in_response=True, hijack_std=False):
|
||||
from frappe.tasks import run_async_task
|
||||
from frappe.handler import execute_cmd
|
||||
if frappe.conf.disable_async:
|
||||
return execute_cmd(cmd, from_async=True)
|
||||
args = frappe._dict(args)
|
||||
task = run_async_task.delay(site=frappe.local.site,
|
||||
user=(frappe.session and frappe.session.user) or 'Administrator', cmd=cmd,
|
||||
form_dict=args, hijack_std=hijack_std)
|
||||
if set_in_response:
|
||||
frappe.local.response['task_id'] = task.id
|
||||
return task.id
|
||||
|
||||
@wraps(f)
|
||||
def queue(*args, **kwargs):
|
||||
task_id = run(frappe.local.form_dict, set_in_response=True)
|
||||
return {
|
||||
"status": "queued",
|
||||
"task_id": task_id
|
||||
}
|
||||
|
||||
queue.async = True
|
||||
queue.queue = f
|
||||
queue.run = run
|
||||
frappe.whitelisted.append(f)
|
||||
frappe.whitelisted.append(queue)
|
||||
return queue
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_pending_tasks_for_doc(doctype, docname):
|
||||
return frappe.db.sql_list("select name from `tabAsync Task` where status in ('Queued', 'Running') and reference_doctype=%s and reference_name=%s", (doctype, docname))
|
||||
|
||||
|
||||
@handler
|
||||
def ping():
|
||||
from time import sleep
|
||||
sleep(1)
|
||||
return "pong"
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_task_status(task_id):
|
||||
from frappe.celery_app import get_celery
|
||||
c = get_celery()
|
||||
a = c.AsyncResult(task_id)
|
||||
frappe.local.response['response'] = a.result
|
||||
return {
|
||||
"state": a.state,
|
||||
"progress": 0
|
||||
}
|
||||
|
||||
def set_task_status(task_id, status, response=None):
|
||||
if not response:
|
||||
response = {}
|
||||
response.update({
|
||||
"status": status,
|
||||
"task_id": task_id
|
||||
})
|
||||
emit_via_redis("task_status_change", response, room="task:" + task_id)
|
||||
|
||||
|
||||
def remove_old_task_logs():
|
||||
logs_path = get_site_path('task-logs')
|
||||
|
||||
def full_path(_file):
|
||||
return os.path.join(logs_path, _file)
|
||||
|
||||
files_to_remove = [full_path(_file) for _file in os.listdir(logs_path)]
|
||||
files_to_remove = [_file for _file in files_to_remove if is_file_old(_file) and os.path.isfile(_file)]
|
||||
for _file in files_to_remove:
|
||||
os.remove(_file)
|
||||
|
||||
|
||||
def is_file_old(file_path):
|
||||
return ((time.time() - os.stat(file_path).st_mtime) > TASK_LOG_MAX_AGE)
|
||||
|
||||
|
||||
def publish_realtime(event=None, message=None, room=None, user=None, doctype=None, docname=None, now=False):
|
||||
"""Publish real-time updates
|
||||
|
||||
:param event: Event name, like `task_progress` etc. that will be handled by the client (default is `task_progress` if within task or `global`)
|
||||
:param message: JSON message object. For async must contain `task_id`
|
||||
:param room: Room in which to publish update (default entire site)
|
||||
:param user: Transmit to user
|
||||
:param doctype: Transmit to doctype, docname
|
||||
:param docname: Transmit to doctype, docname"""
|
||||
if message is None:
|
||||
message = {}
|
||||
|
||||
if event is None:
|
||||
if frappe.local.task_id:
|
||||
event = "task_progress"
|
||||
else:
|
||||
event = "global"
|
||||
|
||||
if not room:
|
||||
if frappe.local.task_id:
|
||||
room = get_task_progress_room()
|
||||
if not "task_id" in message:
|
||||
message["task_id"] = frappe.local.task_id
|
||||
|
||||
now = True
|
||||
elif user:
|
||||
room = get_user_room(user)
|
||||
elif doctype and docname:
|
||||
room = get_doc_room(doctype, docname)
|
||||
else:
|
||||
room = get_site_room()
|
||||
|
||||
if now:
|
||||
emit_via_redis(event, message, room)
|
||||
else:
|
||||
frappe.local.realtime_log.append([event, message, room])
|
||||
|
||||
def emit_via_redis(event, message, room):
|
||||
"""Publish real-time updates via redis
|
||||
|
||||
:param event: Event name, like `task_progress` etc.
|
||||
:param message: JSON message object. For async must contain `task_id`
|
||||
:param room: name of the room"""
|
||||
r = get_redis_server()
|
||||
|
||||
try:
|
||||
r.publish('events', frappe.as_json({'event': event, 'message': message, 'room': room}))
|
||||
except redis.exceptions.ConnectionError:
|
||||
# print frappe.get_traceback()
|
||||
pass
|
||||
|
||||
def put_log(line_no, line, task_id=None):
|
||||
r = get_redis_server()
|
||||
if not task_id:
|
||||
task_id = frappe.local.task_id
|
||||
task_progress_room = get_task_progress_room()
|
||||
task_log_key = "task_log:" + task_id
|
||||
publish_realtime('task_progress', {
|
||||
"message": {
|
||||
"lines": {line_no: line}
|
||||
},
|
||||
"task_id": task_id
|
||||
}, room=task_progress_room, now=True)
|
||||
r.hset(task_log_key, line_no, line)
|
||||
r.expire(task_log_key, 3600)
|
||||
|
||||
|
||||
def get_redis_server():
|
||||
"""Returns memcache connection."""
|
||||
global redis_server
|
||||
if not redis_server:
|
||||
from redis import Redis
|
||||
redis_server = Redis.from_url(conf.get("async_redis_server") or "redis://localhost:12311")
|
||||
return redis_server
|
||||
|
||||
|
||||
class FileAndRedisStream(file):
|
||||
def __init__(self, *args, **kwargs):
|
||||
ret = super(FileAndRedisStream, self).__init__(*args, **kwargs)
|
||||
self.count = 0
|
||||
return ret
|
||||
|
||||
def write(self, data):
|
||||
ret = super(FileAndRedisStream, self).write(data)
|
||||
if frappe.local.task_id:
|
||||
put_log(self.count, data, task_id=frappe.local.task_id)
|
||||
self.count += 1
|
||||
return ret
|
||||
|
||||
|
||||
def get_std_streams(task_id):
|
||||
stdout = FileAndRedisStream(get_task_log_file_path(task_id, 'stdout'), 'w')
|
||||
# stderr = FileAndRedisStream(get_task_log_file_path(task_id, 'stderr'), 'w')
|
||||
return stdout, stdout
|
||||
|
||||
|
||||
def get_task_log_file_path(task_id, stream_type):
|
||||
logs_dir = frappe.utils.get_site_path('task-logs')
|
||||
return os.path.join(logs_dir, task_id + '.' + stream_type)
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def can_subscribe_doc(doctype, docname, sid):
|
||||
from frappe.sessions import Session
|
||||
from frappe.exceptions import PermissionError
|
||||
session = Session(None, resume=True).get_session_data()
|
||||
if not frappe.has_permission(user=session.user, doctype=doctype, doc=docname, ptype='read'):
|
||||
raise PermissionError()
|
||||
return True
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def get_user_info(sid):
|
||||
from frappe.sessions import Session
|
||||
session = Session(None, resume=True).get_session_data()
|
||||
return {
|
||||
'user': session.user,
|
||||
}
|
||||
|
||||
def get_doc_room(doctype, docname):
|
||||
return ''.join([frappe.local.site, ':doc:', doctype, '/', docname])
|
||||
|
||||
def get_user_room(user):
|
||||
return ''.join([frappe.local.site, ':user:', user])
|
||||
|
||||
def get_site_room():
|
||||
return ''.join([frappe.local.site, ':all'])
|
||||
|
||||
def get_task_progress_room():
|
||||
return "task_progress:" + frappe.local.task_id
|
||||
|
|
@ -23,7 +23,7 @@ class HTTPRequest:
|
|||
self.domain = self.domain[4:]
|
||||
|
||||
if frappe.get_request_header('X-Forwarded-For'):
|
||||
frappe.local.request_ip = frappe.get_request_header('X-Forwarded-For')
|
||||
frappe.local.request_ip = (frappe.get_request_header('X-Forwarded-For').split(",")[0]).strip()
|
||||
|
||||
elif frappe.get_request_header('REMOTE_ADDR'):
|
||||
frappe.local.request_ip = frappe.get_request_header('REMOTE_ADDR')
|
||||
|
|
@ -32,22 +32,19 @@ class HTTPRequest:
|
|||
frappe.local.request_ip = '127.0.0.1'
|
||||
|
||||
# language
|
||||
self.set_lang(frappe.request.accept_languages.values())
|
||||
self.set_lang()
|
||||
|
||||
# load cookies
|
||||
frappe.local.cookie_manager = CookieManager()
|
||||
|
||||
# override request method. All request to be of type POST, but if _type == "POST" then commit
|
||||
if frappe.form_dict.get("_type"):
|
||||
frappe.local.request_method = frappe.form_dict.get("_type")
|
||||
del frappe.form_dict["_type"]
|
||||
|
||||
# set db
|
||||
self.connect()
|
||||
|
||||
# login
|
||||
frappe.local.login_manager = LoginManager()
|
||||
|
||||
self.validate_csrf_token()
|
||||
|
||||
# write out latest cookies
|
||||
frappe.local.cookie_manager.init_cookies()
|
||||
|
||||
|
|
@ -57,19 +54,25 @@ class HTTPRequest:
|
|||
# run login triggers
|
||||
if frappe.form_dict.get('cmd')=='login':
|
||||
frappe.local.login_manager.run_trigger('on_session_creation')
|
||||
self.clear_active_sessions()
|
||||
|
||||
def clear_active_sessions(self):
|
||||
if not frappe.conf.get("deny_multiple_sessions"):
|
||||
return
|
||||
def validate_csrf_token(self):
|
||||
if frappe.local.request and frappe.local.request.method=="POST":
|
||||
if not frappe.local.session.data.csrf_token or frappe.local.session.data.device=="mobile":
|
||||
# not via boot
|
||||
return
|
||||
|
||||
if frappe.session.user != "Guest":
|
||||
clear_sessions(frappe.session.user, keep_current=True)
|
||||
csrf_token = frappe.get_request_header("X-Frappe-CSRF-Token")
|
||||
if not csrf_token and "csrf_token" in frappe.local.form_dict:
|
||||
csrf_token = frappe.local.form_dict.csrf_token
|
||||
del frappe.local.form_dict["csrf_token"]
|
||||
|
||||
if frappe.local.session.data.csrf_token != csrf_token:
|
||||
frappe.local.flags.disable_traceback = True
|
||||
frappe.throw(_("Invalid Request"), frappe.CSRFTokenError)
|
||||
|
||||
def set_lang(self, lang_codes):
|
||||
def set_lang(self):
|
||||
from frappe.translate import guess_language
|
||||
frappe.local.lang = guess_language(lang_codes)
|
||||
frappe.local.lang = guess_language()
|
||||
|
||||
def get_db_name(self):
|
||||
"""get database name from conf"""
|
||||
|
|
@ -89,8 +92,16 @@ class LoginManager:
|
|||
|
||||
if frappe.local.form_dict.get('cmd')=='login' or frappe.local.request.path=="/api/method/login":
|
||||
self.login()
|
||||
self.resume = False
|
||||
else:
|
||||
self.make_session(resume=True)
|
||||
try:
|
||||
self.resume = True
|
||||
self.make_session(resume=True)
|
||||
self.set_user_info(resume=True)
|
||||
except AttributeError:
|
||||
self.user = "Guest"
|
||||
self.make_session()
|
||||
self.set_user_info()
|
||||
|
||||
def login(self):
|
||||
# clear cache
|
||||
|
|
@ -99,29 +110,34 @@ class LoginManager:
|
|||
self.post_login()
|
||||
|
||||
def post_login(self):
|
||||
self.info = frappe.db.get_value("User", self.user,
|
||||
["user_type", "first_name", "last_name", "user_image"], as_dict=1)
|
||||
self.full_name = " ".join(filter(None, [self.info.first_name, self.info.last_name]))
|
||||
self.user_type = self.info.user_type
|
||||
|
||||
self.run_trigger('on_login')
|
||||
self.validate_ip_address()
|
||||
self.validate_hour()
|
||||
self.make_session()
|
||||
self.set_user_info()
|
||||
|
||||
def set_user_info(self):
|
||||
def set_user_info(self, resume=False):
|
||||
# set sid again
|
||||
frappe.local.cookie_manager.init_cookies()
|
||||
|
||||
self.info = frappe.db.get_value("User", self.user,
|
||||
["user_type", "first_name", "last_name", "user_image"], as_dict=1)
|
||||
self.full_name = " ".join(filter(None, [self.info.first_name,
|
||||
self.info.last_name]))
|
||||
self.user_type = self.info.user_type
|
||||
|
||||
if self.info.user_type=="Website User":
|
||||
frappe.local.cookie_manager.set_cookie("system_user", "no")
|
||||
frappe.local.response["message"] = "No App"
|
||||
if not resume:
|
||||
frappe.local.response["message"] = "No App"
|
||||
else:
|
||||
frappe.local.cookie_manager.set_cookie("system_user", "yes")
|
||||
frappe.local.response['message'] = 'Logged In'
|
||||
if not resume:
|
||||
frappe.local.response['message'] = 'Logged In'
|
||||
|
||||
if not resume:
|
||||
frappe.response["full_name"] = self.full_name
|
||||
|
||||
frappe.response["full_name"] = self.full_name
|
||||
frappe.local.cookie_manager.set_cookie("full_name", self.full_name)
|
||||
frappe.local.cookie_manager.set_cookie("user_id", self.user)
|
||||
frappe.local.cookie_manager.set_cookie("user_image", self.info.user_image or "")
|
||||
|
|
@ -134,6 +150,14 @@ class LoginManager:
|
|||
# reset user if changed to Guest
|
||||
self.user = frappe.local.session_obj.user
|
||||
frappe.local.session = frappe.local.session_obj.data
|
||||
self.clear_active_sessions()
|
||||
|
||||
def clear_active_sessions(self):
|
||||
if not frappe.conf.get("deny_multiple_sessions"):
|
||||
return
|
||||
|
||||
if frappe.session.user != "Guest":
|
||||
clear_sessions(frappe.session.user, keep_current=True)
|
||||
|
||||
def authenticate(self, user=None, pwd=None):
|
||||
if not (user and pwd):
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ def get_bootinfo():
|
|||
bootinfo['user_info'] = get_fullnames()
|
||||
bootinfo['sid'] = frappe.session['sid'];
|
||||
|
||||
# home page
|
||||
bootinfo.modules = {}
|
||||
for app in frappe.get_installed_apps():
|
||||
try:
|
||||
|
|
@ -45,7 +44,7 @@ def get_bootinfo():
|
|||
bootinfo.hidden_modules = frappe.db.get_global("hidden_modules")
|
||||
bootinfo.doctype_icons = dict(frappe.db.sql("""select name, icon from
|
||||
tabDocType where ifnull(icon,'')!=''"""))
|
||||
bootinfo.single_types = frappe.db.sql_list("""select name from tabDocType where ifnull(issingle,0)=1""")
|
||||
bootinfo.single_types = frappe.db.sql_list("""select name from tabDocType where issingle=1""")
|
||||
add_home_page(bootinfo, doclist)
|
||||
bootinfo.page_info = get_allowed_pages()
|
||||
load_translations(bootinfo)
|
||||
|
|
@ -53,6 +52,7 @@ def get_bootinfo():
|
|||
load_conf_settings(bootinfo)
|
||||
load_print(bootinfo, doclist)
|
||||
doclist.extend(get_meta_bundle("Page"))
|
||||
bootinfo.home_folder = frappe.db.get_value("File", {"is_home_folder": 1})
|
||||
|
||||
# ipinfo
|
||||
if frappe.session['data'].get('ipinfo'):
|
||||
|
|
@ -69,13 +69,13 @@ def get_bootinfo():
|
|||
bootinfo['versions'] = {k: v['version'] for k, v in get_versions().items()}
|
||||
|
||||
bootinfo.error_report_email = frappe.get_hooks("error_report_email")
|
||||
bootinfo.default_background_image = get_url("/assets/frappe/images/ui/into-the-dawn.jpg")
|
||||
bootinfo.calendars = sorted(frappe.get_hooks("calendars"))
|
||||
|
||||
return bootinfo
|
||||
|
||||
def load_conf_settings(bootinfo):
|
||||
from frappe import conf
|
||||
bootinfo.max_file_size = conf.get('max_file_size') or 5242880
|
||||
bootinfo.max_file_size = conf.get('max_file_size') or 10485760
|
||||
for key in ['developer_mode']:
|
||||
if key in conf: bootinfo[key] = conf.get(key)
|
||||
|
||||
|
|
@ -104,16 +104,23 @@ def get_allowed_pages():
|
|||
|
||||
def load_translations(bootinfo):
|
||||
if frappe.local.lang != 'en':
|
||||
bootinfo["__messages"] = frappe.get_lang_dict("boot")
|
||||
messages = frappe.get_lang_dict("boot")
|
||||
|
||||
bootinfo["lang"] = frappe.lang
|
||||
|
||||
# load translated report names
|
||||
for name in bootinfo.user.all_reports:
|
||||
messages[name] = frappe._(name)
|
||||
|
||||
bootinfo["__messages"] = messages
|
||||
|
||||
def get_fullnames():
|
||||
"""map of user fullnames"""
|
||||
ret = frappe.db.sql("""select name,
|
||||
concat(ifnull(first_name, ''),
|
||||
if(ifnull(last_name, '')!='', ' ', ''), ifnull(last_name, '')) as fullname,
|
||||
user_image as image, gender, email
|
||||
from tabUser where ifnull(enabled, 0)=1 and user_type!="Website User" """, as_dict=1)
|
||||
user_image as image, gender, email, username
|
||||
from tabUser where enabled=1 and user_type!="Website User" """, as_dict=1)
|
||||
|
||||
d = {}
|
||||
for r in ret:
|
||||
|
|
@ -132,10 +139,15 @@ def add_home_page(bootinfo, docs):
|
|||
if frappe.session.user=="Guest":
|
||||
return
|
||||
home_page = frappe.db.get_default("desktop:home_page")
|
||||
|
||||
if home_page == "setup-wizard":
|
||||
bootinfo.setup_wizard_requires = frappe.get_hooks("setup_wizard_requires")
|
||||
|
||||
try:
|
||||
page = frappe.desk.desk_page.get(home_page)
|
||||
except (frappe.DoesNotExistError, frappe.PermissionError):
|
||||
frappe.message_log.pop()
|
||||
if frappe.message_log:
|
||||
frappe.message_log.pop()
|
||||
page = frappe.desk.desk_page.get('desktop')
|
||||
|
||||
bootinfo['home_page'] = page.name
|
||||
|
|
@ -154,4 +166,4 @@ def load_print(bootinfo, doclist):
|
|||
load_print_css(bootinfo, print_settings)
|
||||
|
||||
def load_print_css(bootinfo, print_settings):
|
||||
bootinfo.print_css = frappe.get_attr("frappe.templates.pages.print.get_print_style")(print_settings.print_style or "Modern")
|
||||
bootinfo.print_css = frappe.get_attr("frappe.templates.pages.print.get_print_style")(print_settings.print_style or "Modern", for_legacy=True)
|
||||
|
|
|
|||
|
|
@ -127,7 +127,8 @@ def pack(target, sources, no_compress, verbose):
|
|||
tmpin, tmpout = StringIO(data.encode('utf-8')), StringIO()
|
||||
jsm.minify(tmpin, tmpout)
|
||||
minified = tmpout.getvalue()
|
||||
outtxt += unicode(minified or '', 'utf-8').strip('\n') + ';'
|
||||
if minified:
|
||||
outtxt += unicode(minified or '', 'utf-8').strip('\n') + ';'
|
||||
|
||||
if verbose:
|
||||
print "{0}: {1}k".format(f, int(len(minified) / 1024))
|
||||
|
|
@ -173,6 +174,10 @@ def files_dirty():
|
|||
return False
|
||||
|
||||
def compile_less():
|
||||
from distutils.spawn import find_executable
|
||||
if not find_executable("lessc"):
|
||||
return
|
||||
|
||||
for path in app_paths:
|
||||
less_path = os.path.join(path, "public", "less")
|
||||
if os.path.exists(less_path):
|
||||
|
|
@ -188,4 +193,4 @@ def compile_less():
|
|||
print "compiling {0}".format(fpath)
|
||||
|
||||
css_path = os.path.join(path, "public", "css", fname.rsplit(".", 1)[0] + ".css")
|
||||
os.system("which lessc && lessc {0} > {1}".format(fpath, css_path))
|
||||
os.system("lessc {0} > {1}".format(fpath, css_path))
|
||||
|
|
|
|||
|
|
@ -10,48 +10,59 @@ task_logger = get_task_logger(__name__)
|
|||
|
||||
from datetime import timedelta
|
||||
import frappe
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
|
||||
SITES_PATH = os.environ.get('SITES_PATH', '.')
|
||||
|
||||
# defaults
|
||||
DEFAULT_CELERY_BROKER = "redis://localhost"
|
||||
DEFAULT_CELERY_BACKEND = None
|
||||
DEFAULT_CELERY_BACKEND = "redis://localhost"
|
||||
DEFAULT_SCHEDULER_INTERVAL = 300
|
||||
LONGJOBS_PREFIX = "longjobs@"
|
||||
ASYNC_TASKS_PREFIX = "async@"
|
||||
|
||||
_app = None
|
||||
def get_celery():
|
||||
global _app
|
||||
if not _app:
|
||||
conf = frappe.get_site_config(sites_path=SITES_PATH)
|
||||
|
||||
_app = Celery('frappe',
|
||||
broker=conf.celery_broker or DEFAULT_CELERY_BROKER,
|
||||
backend=conf.celery_result_backend or DEFAULT_CELERY_BACKEND)
|
||||
|
||||
setup_celery(_app, conf)
|
||||
|
||||
_app = get_celery_app()
|
||||
return _app
|
||||
|
||||
def setup_celery(app, conf):
|
||||
|
||||
def get_celery_app():
|
||||
conf = get_site_config()
|
||||
app = Celery('frappe',
|
||||
broker=conf.celery_broker or DEFAULT_CELERY_BROKER,
|
||||
backend=conf.async_redis_server or DEFAULT_CELERY_BACKEND)
|
||||
|
||||
app.autodiscover_tasks(frappe.get_all_apps(with_frappe=True, with_internal_apps=False,
|
||||
sites_path=SITES_PATH))
|
||||
|
||||
app.conf.CELERY_TASK_SERIALIZER = 'json'
|
||||
app.conf.CELERY_ACCEPT_CONTENT = ['json']
|
||||
app.conf.CELERY_TIMEZONE = 'UTC'
|
||||
|
||||
if conf.celery_queue_per_site:
|
||||
app.conf.CELERY_ROUTES = (SiteRouter(),)
|
||||
|
||||
app.conf.CELERY_RESULT_SERIALIZER = 'json'
|
||||
app.conf.CELERY_TASK_RESULT_EXPIRES = timedelta(0, 3600)
|
||||
|
||||
if conf.monitory_celery:
|
||||
app.conf.CELERY_SEND_EVENTS = True
|
||||
app.conf.CELERY_SEND_TASK_SENT_EVENT = True
|
||||
|
||||
app.conf.CELERY_ROUTES = (SiteRouter(), AsyncTaskRouter())
|
||||
|
||||
app.conf.CELERYBEAT_SCHEDULE = get_beat_schedule(conf)
|
||||
|
||||
if conf.celery_error_emails:
|
||||
app.conf.CELERY_SEND_TASK_ERROR_EMAILS = True
|
||||
for k, v in conf.celery_error_emails.iteritems():
|
||||
setattr(app.conf, k, v)
|
||||
|
||||
|
||||
return app
|
||||
|
||||
def get_site_config():
|
||||
return frappe.get_site_config(sites_path=SITES_PATH)
|
||||
|
||||
class SiteRouter(object):
|
||||
def route_for_task(self, task, args=None, kwargs=None):
|
||||
if hasattr(frappe.local, 'site'):
|
||||
|
|
@ -59,12 +70,17 @@ class SiteRouter(object):
|
|||
return get_queue(frappe.local.site, LONGJOBS_PREFIX)
|
||||
else:
|
||||
return get_queue(frappe.local.site)
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class AsyncTaskRouter(object):
|
||||
def route_for_task(self, task, args=None, kwargs=None):
|
||||
if task == "frappe.tasks.run_async_task" and hasattr(frappe.local, 'site'):
|
||||
return get_queue(frappe.local.site, ASYNC_TASKS_PREFIX)
|
||||
|
||||
def get_queue(site, prefix=None):
|
||||
return {'queue': "{}{}".format(prefix or "", site)}
|
||||
|
||||
|
||||
def get_beat_schedule(conf):
|
||||
schedule = {
|
||||
'scheduler': {
|
||||
|
|
@ -72,17 +88,135 @@ def get_beat_schedule(conf):
|
|||
'schedule': timedelta(seconds=conf.scheduler_interval or DEFAULT_SCHEDULER_INTERVAL)
|
||||
},
|
||||
}
|
||||
|
||||
if conf.celery_queue_per_site:
|
||||
schedule['sync_queues'] = {
|
||||
'task': 'frappe.tasks.sync_queues',
|
||||
'schedule': timedelta(seconds=conf.scheduler_interval or DEFAULT_SCHEDULER_INTERVAL)
|
||||
}
|
||||
|
||||
|
||||
schedule['sync_queues'] = {
|
||||
'task': 'frappe.tasks.sync_queues',
|
||||
'schedule': timedelta(seconds=conf.scheduler_interval or DEFAULT_SCHEDULER_INTERVAL)
|
||||
}
|
||||
|
||||
return schedule
|
||||
|
||||
|
||||
def celery_task(*args, **kwargs):
|
||||
return get_celery().task(*args, **kwargs)
|
||||
|
||||
|
||||
def make_async_task(args):
|
||||
task = frappe.new_doc("Async Task")
|
||||
task.update(args)
|
||||
task.status = "Queued"
|
||||
task.set_docstatus_user_and_timestamp()
|
||||
task.db_insert()
|
||||
task.notify_update()
|
||||
|
||||
def run_test():
|
||||
for i in xrange(30):
|
||||
test.delay(site=frappe.local.site)
|
||||
|
||||
@celery_task()
|
||||
def test(site=None):
|
||||
time.sleep(1)
|
||||
print "task"
|
||||
|
||||
class MonitorThread(object):
|
||||
"""Thread manager for monitoring celery events"""
|
||||
def __init__(self, celery_app, interval=1):
|
||||
self.celery_app = celery_app
|
||||
self.interval = interval
|
||||
|
||||
self.state = self.celery_app.events.State()
|
||||
|
||||
self.thread = threading.Thread(target=self.run, args=())
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
|
||||
def catchall(self, event):
|
||||
if event['type'] != 'worker-heartbeat':
|
||||
self.state.event(event)
|
||||
|
||||
if not 'uuid' in event:
|
||||
return
|
||||
|
||||
task = self.state.tasks.get(event['uuid'])
|
||||
info = task.info()
|
||||
|
||||
if 'name' in event and 'enqueue_events_for_site' in event['name']:
|
||||
return
|
||||
|
||||
try:
|
||||
kwargs = eval(info.get('kwargs'))
|
||||
|
||||
if 'site' in kwargs:
|
||||
frappe.connect(kwargs['site'])
|
||||
|
||||
if event['type']=='task-sent':
|
||||
make_async_task({
|
||||
'name': event['uuid'],
|
||||
'task_name': kwargs.get("cmd") or event['name']
|
||||
})
|
||||
|
||||
elif event['type']=='task-received':
|
||||
try:
|
||||
task = frappe.get_doc("Async Task", event['uuid'])
|
||||
task.status = 'Started'
|
||||
task.set_docstatus_user_and_timestamp()
|
||||
task.db_update()
|
||||
task.notify_update()
|
||||
except frappe.DoesNotExistError:
|
||||
pass
|
||||
|
||||
elif event['type']=='task-succeeded':
|
||||
try:
|
||||
task = frappe.get_doc("Async Task", event['uuid'])
|
||||
task.status = 'Succeeded'
|
||||
task.result = info.get('result')
|
||||
task.runtime = info.get('runtime')
|
||||
task.set_docstatus_user_and_timestamp()
|
||||
task.db_update()
|
||||
task.notify_update()
|
||||
except frappe.DoesNotExistError:
|
||||
pass
|
||||
|
||||
elif event['type']=='task-failed':
|
||||
try:
|
||||
task = frappe.get_doc("Async Task", event['uuid'])
|
||||
task.status = 'Failed'
|
||||
task.traceback = event.get('traceback') or event.get('exception')
|
||||
task.traceback = frappe.as_json(info) + "\n\n" + task.traceback
|
||||
task.runtime = info.get('runtime')
|
||||
task.set_docstatus_user_and_timestamp()
|
||||
task.db_update()
|
||||
task.notify_update()
|
||||
except frappe.DoesNotExistError:
|
||||
pass
|
||||
|
||||
frappe.db.commit()
|
||||
except Exception:
|
||||
print frappe.get_traceback()
|
||||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
try:
|
||||
with self.celery_app.connection() as connection:
|
||||
recv = self.celery_app.events.Receiver(connection, handlers={
|
||||
'*': self.catchall
|
||||
})
|
||||
recv.capture(limit=None, timeout=None, wakeup=True)
|
||||
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
|
||||
except Exception:
|
||||
# unable to capture
|
||||
print "unable to capture:"
|
||||
print frappe.get_traceback()
|
||||
|
||||
time.sleep(self.interval)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
get_celery().start()
|
||||
app = get_celery()
|
||||
if get_site_config().get("monitor_celery"):
|
||||
MonitorThread(app)
|
||||
app.start()
|
||||
|
|
|
|||
3
frappe/change_log/current/readme.md
Normal file
3
frappe/change_log/current/readme.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Leave change log files in this folder for user release notes.
|
||||
|
||||
(this file is just a place holder, don't delete it)
|
||||
5
frappe/change_log/v5/v5_0_32.md
Normal file
5
frappe/change_log/v5/v5_0_32.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
- Reports are now searchable from awesome bar
|
||||
- Show currect label for title in list views
|
||||
- Datepicker now sets default value as Today
|
||||
- Map child table as per meta, if not mentioned in table_map via mapper
|
||||
- Re-enable save button on error
|
||||
3
frappe/change_log/v5/v5_1_0.md
Normal file
3
frappe/change_log/v5/v5_1_0.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
- Change print font from Setup > Print Settings or set it for each Print Format. Font options are "Default", "Arial", "Helvetica", "Verdana", "Monospace".
|
||||
- Print and full-page print preview in user's language
|
||||
- Fixed inconsistent visibility of a logged-in user's image in website
|
||||
1
frappe/change_log/v5/v5_1_1.md
Normal file
1
frappe/change_log/v5/v5_1_1.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Ability to **Share with Everyone** (except Guest) using **Share With**
|
||||
40
frappe/change_log/v5/v5_3_0.md
Normal file
40
frappe/change_log/v5/v5_3_0.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
- Added Language Support for Following languages
|
||||
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<td style="width: 30%">bo*</td>
|
||||
<td>ལྷ་སའི་སྐད་</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>fi</td>
|
||||
<td>suomalainen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>km</td>
|
||||
<td>ភាសាខ្មែរ</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mk</td>
|
||||
<td>македонски</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>my</td>
|
||||
<td>Melayu</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>no</td>
|
||||
<td>norsk</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>sv</td>
|
||||
<td>Svenska</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>sq</td>
|
||||
<td>shqiptar</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
* Unable to find translations for Tibetian via Google
|
||||
|
||||
- To contribute to translations, please login to [https://translate.erpnext.com](https://translate.erpnext.com)
|
||||
1
frappe/change_log/v5/v5_4_0.md
Normal file
1
frappe/change_log/v5/v5_4_0.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Moved Backup Manager and Social Login Keys to the new **Integrations** module
|
||||
3
frappe/change_log/v6/v6_0_0.md
Normal file
3
frappe/change_log/v6/v6_0_0.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
- **Realtime updates** for new comments and list view
|
||||
- Get warned if someone else modified the document that you are working on
|
||||
- You can now quickly assign a document to yourself by clicking on "Assign to me"
|
||||
1
frappe/change_log/v6/v6_0_8.md
Normal file
1
frappe/change_log/v6/v6_0_8.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Set <head> HTML in Website Settings. This is usually used for website verification and SEO.
|
||||
1
frappe/change_log/v6/v6_12_0.md
Normal file
1
frappe/change_log/v6/v6_12_0.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Extract emails using IMAP. Contributed by Gangadhar Kadam ([New Indictrans](http://indictranstech.com/))
|
||||
4
frappe/change_log/v6/v6_13_0.md
Normal file
4
frappe/change_log/v6/v6_13_0.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
- Attachments can now be marked as **Private**
|
||||
- Private files cannot be accessed unless you are logged in
|
||||
- To access a private file, you need to have read permission on the file or read permission on the document to which the file is attached
|
||||
- All attachments in a new incoming email are private
|
||||
1
frappe/change_log/v6/v6_14_1.md
Normal file
1
frappe/change_log/v6/v6_14_1.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Added language support for Malayalam: **ml - മലയാളം**
|
||||
4
frappe/change_log/v6/v6_15_0.md
Normal file
4
frappe/change_log/v6/v6_15_0.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
- **For Developers:** Automatic logging of request errors and its context in **Error Snapshot**
|
||||
- Thank you [Maxwell Morais](https://discuss.erpnext.com/users/max_morais_dmm/activity) for this useful feature
|
||||
- You can access it from *Developer > Logs > Error Snapshot*
|
||||
- Added language support for [Gujarati](https://translate.erpnext.com/view?lang=gu): **gu - ગુજરાતી**
|
||||
1
frappe/change_log/v6/v6_16_1.md
Normal file
1
frappe/change_log/v6/v6_16_1.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Mention users in comments using `@username`. Mentioned users will receive an email with the comment.
|
||||
2
frappe/change_log/v6/v6_16_4.md
Normal file
2
frappe/change_log/v6/v6_16_4.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
- Developer Tutorial [Videos](http://frappe.github.io/frappe/user/videos/)
|
||||
- Increased uploaded file size limit upto 10MB
|
||||
7
frappe/change_log/v6/v6_1_0.md
Normal file
7
frappe/change_log/v6/v6_1_0.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
- Sections can now be set as **Collapsible**.
|
||||
- Collapsible sections can be shown as collapsed based on certain rules defined in the **Collapsible Depends On** property of the document field (DocField).
|
||||
- Title is now editable from the form if the `fieldname` of the title field is **title**.
|
||||
- Document can now be renamed by clicking the page heading.
|
||||
- Fields can be set as **Bold** so that they can be easily identified in long forms.
|
||||
- Fixed mobile views
|
||||
- See Data Import progress in realtime
|
||||
3
frappe/change_log/v6/v6_2_0.md
Normal file
3
frappe/change_log/v6/v6_2_0.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
- **Permissions:**
|
||||
- If User Permissions are missing for a DocType, don't show non-matching records.
|
||||
- If **Ignore User Permissions If Missing** is checked in System Settings, show records even if User Permissions are not defined.
|
||||
2
frappe/change_log/v6/v6_3_0.md
Normal file
2
frappe/change_log/v6/v6_3_0.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
- You can now add **CC** in Email
|
||||
- Show checkboxes in Print
|
||||
1
frappe/change_log/v6/v6_4_0.md
Normal file
1
frappe/change_log/v6/v6_4_0.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
- **File Manager:** A Document Management System for your organisation. Add files, organize them in folders and share it with a few users or everyone in the company.
|
||||
20
frappe/change_log/v6/v6_4_8.md
Normal file
20
frappe/change_log/v6/v6_4_8.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
- Added Language Support for Following languages
|
||||
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<td style="width: 30%">bn</td>
|
||||
<td>বাংলা </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>da-DK</td>
|
||||
<td>dansk (Danmark)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>es-PE</td>
|
||||
<td>Español (Perú)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>si</td>
|
||||
<td>slovenščina</td>
|
||||
</tr>
|
||||
</table>
|
||||
2
frappe/change_log/v6/v6_5_0.md
Normal file
2
frappe/change_log/v6/v6_5_0.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
- **Linked With** will now show links from Dynamic Links
|
||||
- **Data** field-type size truncated to 140 characters from 255 (by default). Can be changed by setting the **length** property from **Customize Form View**
|
||||
1
frappe/change_log/v6/v6_6_0.md
Normal file
1
frappe/change_log/v6/v6_6_0.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Added language support for Ukranian: **uk - українська**
|
||||
3
frappe/change_log/v6/v6_7_0.md
Normal file
3
frappe/change_log/v6/v6_7_0.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
- See who is currently viewing the document
|
||||
- Sounds for various actions
|
||||
- Added language support for Slovene: **sl - slovenščina (Slovene)**
|
||||
2
frappe/change_log/v6/v6_8_0.md
Normal file
2
frappe/change_log/v6/v6_8_0.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
- Pre-configured Email Account for Yandex.Mail
|
||||
- Fixed inline images in received emails
|
||||
|
|
@ -156,9 +156,12 @@ def has_permission(doctype, docname, perm_type="read"):
|
|||
|
||||
@frappe.whitelist()
|
||||
def get_js(src):
|
||||
if src[0]=="/":
|
||||
src = src[1:]
|
||||
contentpath = os.path.join(frappe.local.sites_path, src)
|
||||
src = src.strip("/").split("/")
|
||||
|
||||
if ".." in src:
|
||||
frappe.throw(_("Invalid file path: {0}").format("/".join(src)))
|
||||
|
||||
contentpath = os.path.join(frappe.local.sites_path, *src)
|
||||
with open(contentpath, "r") as srcfile:
|
||||
code = frappe.utils.cstr(srcfile.read())
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
from __future__ import unicode_literals, absolute_import
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import json
|
||||
import click
|
||||
import hashlib
|
||||
|
|
@ -17,6 +16,8 @@ from frappe.utils import cint
|
|||
from distutils.spawn import find_executable
|
||||
from functools import wraps
|
||||
|
||||
click.disable_unicode_literals_warning = True
|
||||
|
||||
def pass_context(f):
|
||||
@wraps(f)
|
||||
def _func(ctx, *args, **kwargs):
|
||||
|
|
@ -104,7 +105,7 @@ def _is_scheduler_enabled():
|
|||
enable_scheduler = False
|
||||
try:
|
||||
frappe.connect()
|
||||
enable_scheduler = cint(frappe.db.get_default("enable_scheduler"))
|
||||
enable_scheduler = cint(frappe.db.get_single_value("System Settings", "enable_scheduler")) and True or False
|
||||
except:
|
||||
pass
|
||||
finally:
|
||||
|
|
@ -125,9 +126,8 @@ def restore(context, sql_file_path, mariadb_root_username=None, mariadb_root_pas
|
|||
|
||||
site = get_single_site(context)
|
||||
frappe.init(site=site)
|
||||
if not db_name:
|
||||
db_name = frappe.conf.db_name
|
||||
_new_site(db_name, site, mariadb_root_username=mariadb_root_username, mariadb_root_password=mariadb_root_password, admin_password=admin_password, verbose=context.verbose, install_apps=install_app, source_sql=sql_file_path, force=context.force)
|
||||
db_name = db_name or frappe.conf.db_name or hashlib.sha1(site).hexdigest()[:10]
|
||||
_new_site(db_name, site, mariadb_root_username=mariadb_root_username, mariadb_root_password=mariadb_root_password, admin_password=admin_password, verbose=context.verbose, install_apps=install_app, source_sql=sql_file_path, force=context.force)
|
||||
|
||||
@click.command('reinstall')
|
||||
@pass_context
|
||||
|
|
@ -140,7 +140,7 @@ def reinstall(context):
|
|||
frappe.clear_cache()
|
||||
installed = frappe.get_installed_apps()
|
||||
frappe.clear_cache()
|
||||
except Exception, e:
|
||||
except Exception:
|
||||
installed = []
|
||||
finally:
|
||||
if frappe.db:
|
||||
|
|
@ -164,6 +164,16 @@ def install_app(context, app):
|
|||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
@click.command('list-apps')
|
||||
@pass_context
|
||||
def list_apps(context):
|
||||
"Reinstall site ie. wipe all data and start over"
|
||||
site = get_single_site(context)
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
print "\n".join(frappe.get_installed_apps())
|
||||
frappe.destroy()
|
||||
|
||||
@click.command('add-system-manager')
|
||||
@click.argument('email')
|
||||
@click.option('--first-name')
|
||||
|
|
@ -191,8 +201,6 @@ def migrate(context, rebuild_website=False):
|
|||
import frappe.translate
|
||||
from frappe.desk.notifications import clear_notifications
|
||||
|
||||
verbose = context.verbose
|
||||
|
||||
for site in context.sites:
|
||||
print 'Migrating', site
|
||||
frappe.init(site=site)
|
||||
|
|
@ -312,15 +320,16 @@ def destroy_all_sessions(context):
|
|||
frappe.destroy()
|
||||
|
||||
@click.command('sync-www')
|
||||
@click.option('--force', help='Rebuild all pages', is_flag=True, default=False)
|
||||
@pass_context
|
||||
def sync_www(context):
|
||||
def sync_www(context, force=False):
|
||||
"Sync files from static pages from www directory to Web Pages"
|
||||
from frappe.website import statics
|
||||
for site in context.sites:
|
||||
try:
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
statics.sync_statics(rebuild=context.force)
|
||||
statics.sync_statics(rebuild=force)
|
||||
frappe.db.commit()
|
||||
finally:
|
||||
frappe.destroy()
|
||||
|
|
@ -340,35 +349,102 @@ def build_website(context):
|
|||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
@click.command('setup-docs')
|
||||
@click.argument('app')
|
||||
@click.argument('docs-app')
|
||||
@click.argument('path')
|
||||
@click.command('make-docs')
|
||||
@pass_context
|
||||
def setup_docs(context,app, docs_app, path):
|
||||
@click.argument('app')
|
||||
@click.argument('docs_version')
|
||||
def make_docs(context, app, docs_version):
|
||||
"Setup docs in target folder of target app"
|
||||
from frappe.utils.setup_docs import setup_docs
|
||||
for site in context.sites:
|
||||
try:
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
setup_docs(app, docs_app, path)
|
||||
make = setup_docs(app)
|
||||
make.build(docs_version)
|
||||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
@click.command('sync-docs')
|
||||
@pass_context
|
||||
@click.argument('app')
|
||||
def sync_docs(context, app):
|
||||
"Sync docs from /docs folder into the database (Web Page)"
|
||||
from frappe.utils.setup_docs import setup_docs
|
||||
for site in context.sites:
|
||||
try:
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
make = setup_docs(app)
|
||||
make.sync_docs()
|
||||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
|
||||
@click.command('write-docs')
|
||||
@pass_context
|
||||
@click.argument('app')
|
||||
@click.argument('target')
|
||||
@click.option('--local', default=False, is_flag=True, help='Run app locally')
|
||||
def write_docs(context, app, target, local=False):
|
||||
"Setup docs in target folder of target app"
|
||||
from frappe.utils.setup_docs import setup_docs
|
||||
for site in context.sites:
|
||||
try:
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
make = setup_docs(app)
|
||||
make.make_docs(target, local)
|
||||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
@click.command('build-docs')
|
||||
@click.argument('app')
|
||||
@pass_context
|
||||
def build_docs(context, app):
|
||||
"Build docs from /src to /www folder in app"
|
||||
from frappe.utils.autodoc import build
|
||||
frappe.destroy()
|
||||
@click.argument('app')
|
||||
@click.option('--docs-version', default='current')
|
||||
@click.option('--target', default=None)
|
||||
@click.option('--local', default=False, is_flag=True, help='Run app locally')
|
||||
@click.option('--watch', default=False, is_flag=True, help='Watch for changes and rewrite')
|
||||
def build_docs(context, app, docs_version="current", target=None, local=False, watch=False):
|
||||
"Setup docs in target folder of target app"
|
||||
from frappe.utils import watch as start_watch
|
||||
if not target:
|
||||
target = os.path.abspath(os.path.join("..", "docs", app))
|
||||
|
||||
for site in context.sites:
|
||||
try:
|
||||
frappe.init(site=site)
|
||||
build(app)
|
||||
finally:
|
||||
frappe.destroy()
|
||||
_build_docs_once(site, app, docs_version, target, local)
|
||||
|
||||
if watch:
|
||||
def trigger_make(source_path, event_type):
|
||||
if "/templates/autodoc/" in source_path:
|
||||
_build_docs_once(site, app, docs_version, target, local)
|
||||
|
||||
elif ("/docs.css" in source_path
|
||||
or "/docs/" in source_path
|
||||
or "docs.py" in source_path):
|
||||
_build_docs_once(site, app, docs_version, target, local, only_content_updated=True)
|
||||
|
||||
apps_path = frappe.get_app_path("frappe", "..", "..")
|
||||
start_watch(apps_path, handler=trigger_make)
|
||||
|
||||
def _build_docs_once(site, app, docs_version, target, local, only_content_updated=False):
|
||||
from frappe.utils.setup_docs import setup_docs
|
||||
|
||||
try:
|
||||
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
make = setup_docs(app)
|
||||
|
||||
if not only_content_updated:
|
||||
make.build(docs_version)
|
||||
make.sync_docs()
|
||||
|
||||
make.make_docs(target, local)
|
||||
|
||||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
|
||||
@click.command('reset-perms')
|
||||
@pass_context
|
||||
|
|
@ -380,7 +456,7 @@ def reset_perms(context):
|
|||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
for d in frappe.db.sql_list("""select name from `tabDocType`
|
||||
where ifnull(istable, 0)=0 and ifnull(custom, 0)=0"""):
|
||||
where istable=0 and custom=0"""):
|
||||
frappe.clear_cache(doctype=d)
|
||||
reset_perms(d)
|
||||
finally:
|
||||
|
|
@ -388,22 +464,34 @@ def reset_perms(context):
|
|||
|
||||
@click.command('execute')
|
||||
@click.argument('method')
|
||||
@click.option('--args')
|
||||
@click.option('--kwargs')
|
||||
@pass_context
|
||||
def execute(context, method):
|
||||
def execute(context, method, args=None, kwargs=None):
|
||||
"execute a function"
|
||||
for site in context.sites:
|
||||
try:
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
print frappe.local.site
|
||||
ret = frappe.get_attr(method)()
|
||||
|
||||
if args:
|
||||
args = eval(args)
|
||||
else:
|
||||
args = ()
|
||||
|
||||
if kwargs:
|
||||
kwargs = eval(args)
|
||||
else:
|
||||
kwargs = {}
|
||||
|
||||
ret = frappe.get_attr(method)(*args, **kwargs)
|
||||
|
||||
if frappe.db:
|
||||
frappe.db.commit()
|
||||
finally:
|
||||
frappe.destroy()
|
||||
if ret:
|
||||
print ret
|
||||
print json.dumps(ret)
|
||||
|
||||
@click.command('celery')
|
||||
@click.argument('args')
|
||||
|
|
@ -529,6 +617,55 @@ def import_doc(context, path, force=False):
|
|||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
@click.command('import-csv')
|
||||
@click.argument('path')
|
||||
@click.option('--only-insert', default=False, is_flag=True, help='Do not overwrite existing records')
|
||||
@click.option('--submit-after-import', default=False, is_flag=True, help='Submit document after importing it')
|
||||
@click.option('--ignore-encoding-errors', default=False, is_flag=True, help='Ignore encoding errors while coverting to unicode')
|
||||
@pass_context
|
||||
def import_csv(context, path, only_insert=False, submit_after_import=False, ignore_encoding_errors=False):
|
||||
"Import CSV using data import tool"
|
||||
from frappe.core.page.data_import_tool import importer
|
||||
from frappe.utils.csvutils import read_csv_content
|
||||
site = get_single_site(context)
|
||||
|
||||
with open(path, 'r') as csvfile:
|
||||
content = read_csv_content(csvfile.read())
|
||||
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
|
||||
try:
|
||||
importer.upload(content, submit_after_import=submit_after_import,
|
||||
ignore_encoding_errors=ignore_encoding_errors, overwrite=not only_insert,
|
||||
via_console=True)
|
||||
frappe.db.commit()
|
||||
except Exception:
|
||||
print frappe.get_traceback()
|
||||
|
||||
frappe.destroy()
|
||||
|
||||
@click.command('bulk-rename')
|
||||
@click.argument('doctype')
|
||||
@click.argument('path')
|
||||
@pass_context
|
||||
def _bulk_rename(context, doctype, path):
|
||||
"Rename multiple records via CSV file"
|
||||
from frappe.model.rename_doc import bulk_rename
|
||||
from frappe.utils.csvutils import read_csv_content
|
||||
|
||||
site = get_single_site(context)
|
||||
|
||||
with open(path, 'r') as csvfile:
|
||||
rows = read_csv_content(csvfile.read())
|
||||
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
|
||||
bulk_rename(doctype, rows, via_console = True)
|
||||
|
||||
frappe.destroy()
|
||||
|
||||
# translation
|
||||
@click.command('build-message-files')
|
||||
@pass_context
|
||||
|
|
@ -613,6 +750,7 @@ def console(context):
|
|||
site = get_single_site(context)
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
frappe.local.lang = frappe.db.get_default("lang")
|
||||
import IPython
|
||||
IPython.embed()
|
||||
|
||||
|
|
@ -632,7 +770,7 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), driver=None
|
|||
site = get_single_site(context)
|
||||
frappe.init(site=site)
|
||||
|
||||
if frappe.conf.run_selenium_tests:
|
||||
if frappe.conf.run_selenium_tests and False:
|
||||
sel.start(context.verbose, driver)
|
||||
|
||||
try:
|
||||
|
|
@ -686,11 +824,19 @@ def request(context, args):
|
|||
|
||||
@click.command('doctor')
|
||||
def doctor():
|
||||
"Get untranslated strings for lang."
|
||||
"Get diagnostic info about background workers"
|
||||
from frappe.utils.doctor import doctor as _doctor
|
||||
frappe.init('')
|
||||
return _doctor()
|
||||
|
||||
@click.command('celery-doctor')
|
||||
@click.option('--site', help='site name')
|
||||
def celery_doctor(site=None):
|
||||
"Get diagnostic info about background workers"
|
||||
from frappe.utils.doctor import celery_doctor as _celery_doctor
|
||||
frappe.init('')
|
||||
return _celery_doctor(site=site)
|
||||
|
||||
@click.command('purge-all-tasks')
|
||||
def purge_all_tasks():
|
||||
"Purge any pending periodic tasks of 'all' event. Doesn't purge hourly, daily and weekly"
|
||||
|
|
@ -725,21 +871,23 @@ def use(site, sites_path='.'):
|
|||
@click.command('backup')
|
||||
@click.option('--with-files', default=False, is_flag=True, help="Take backup with files")
|
||||
@pass_context
|
||||
def backup(context, with_files=False, backup_path_db=None, backup_path_files=None, quiet=False):
|
||||
def backup(context, with_files=False, backup_path_db=None, backup_path_files=None,
|
||||
backup_path_private_files=None, quiet=False):
|
||||
"Backup"
|
||||
from frappe.utils.backups import scheduled_backup
|
||||
verbose = context.verbose
|
||||
for site in context.sites:
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
odb = scheduled_backup(ignore_files=not with_files, backup_path_db=backup_path_db, backup_path_files=backup_path_files, force=True)
|
||||
odb = scheduled_backup(ignore_files=not with_files, backup_path_db=backup_path_db, backup_path_files=backup_path_files, backup_path_private_files=backup_path_private_files, force=True)
|
||||
if verbose:
|
||||
from frappe.utils import now
|
||||
print "database backup taken -", odb.backup_path_db, "- on", now()
|
||||
if with_files:
|
||||
print "files backup taken -", odb.backup_path_files, "- on", now()
|
||||
frappe.destroy()
|
||||
print "private files backup taken -", odb.backup_path_private_files, "- on", now()
|
||||
|
||||
frappe.destroy()
|
||||
|
||||
@click.command('remove-from-installed-apps')
|
||||
@click.argument('app')
|
||||
|
|
@ -754,6 +902,20 @@ def remove_from_installed_apps(context, app):
|
|||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
@click.command('uninstall-app')
|
||||
@click.argument('app')
|
||||
@click.option('--dry-run', help='List all doctypes that will be deleted', is_flag=True, default=False)
|
||||
@pass_context
|
||||
def uninstall(context, app, dry_run=False):
|
||||
from frappe.installer import remove_app
|
||||
for site in context.sites:
|
||||
try:
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
remove_app(app, dry_run)
|
||||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
def move(dest_dir, site):
|
||||
import os
|
||||
if not os.path.isdir(dest_dir):
|
||||
|
|
@ -775,6 +937,18 @@ def move(dest_dir, site):
|
|||
frappe.destroy()
|
||||
return final_new_path
|
||||
|
||||
|
||||
@click.command('set-config')
|
||||
@click.argument('key')
|
||||
@click.argument('value')
|
||||
@pass_context
|
||||
def set_config(context, key, value):
|
||||
from frappe.installer import update_site_config
|
||||
for site in context.sites:
|
||||
frappe.init(site=site)
|
||||
update_site_config(key, value)
|
||||
frappe.destroy()
|
||||
|
||||
@click.command('drop-site')
|
||||
@click.argument('site')
|
||||
@click.option('--root-login', default='root')
|
||||
|
|
@ -799,6 +973,15 @@ def drop_site(site, root_login='root', root_password=None):
|
|||
os.mkdir(archived_sites_dir)
|
||||
move(archived_sites_dir, site)
|
||||
|
||||
@click.command('version')
|
||||
@pass_context
|
||||
def get_version(context):
|
||||
frappe.init(site=context.sites[0])
|
||||
for m in sorted(frappe.local.app_modules.keys()):
|
||||
module = frappe.get_module(m)
|
||||
if hasattr(module, "__version__"):
|
||||
print "{0} {1}".format(m, module.__version__)
|
||||
|
||||
# commands = [
|
||||
# new_site,
|
||||
# restore,
|
||||
|
|
@ -813,6 +996,7 @@ commands = [
|
|||
restore,
|
||||
reinstall,
|
||||
install_app,
|
||||
list_apps,
|
||||
add_system_manager,
|
||||
migrate,
|
||||
run_patch,
|
||||
|
|
@ -824,7 +1008,9 @@ commands = [
|
|||
destroy_all_sessions,
|
||||
sync_www,
|
||||
build_website,
|
||||
setup_docs,
|
||||
make_docs,
|
||||
sync_docs,
|
||||
write_docs,
|
||||
build_docs,
|
||||
reset_perms,
|
||||
execute,
|
||||
|
|
@ -837,6 +1023,8 @@ commands = [
|
|||
export_csv,
|
||||
export_fixtures,
|
||||
import_doc,
|
||||
import_csv,
|
||||
_bulk_rename,
|
||||
build_message_files,
|
||||
get_untranslated,
|
||||
update_translations,
|
||||
|
|
@ -846,6 +1034,7 @@ commands = [
|
|||
serve,
|
||||
request,
|
||||
doctor,
|
||||
celery_doctor,
|
||||
purge_all_tasks,
|
||||
dump_queue_status,
|
||||
console,
|
||||
|
|
@ -853,5 +1042,8 @@ commands = [
|
|||
_use,
|
||||
backup,
|
||||
remove_from_installed_apps,
|
||||
uninstall,
|
||||
drop_site,
|
||||
set_config,
|
||||
get_version,
|
||||
]
|
||||
|
|
|
|||
61
frappe/config/core.py
Normal file
61
frappe/config/core.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return [
|
||||
{
|
||||
"label": _("Documents"),
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "DocType",
|
||||
"description": _("Models (building blocks) of the Application"),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Module Def",
|
||||
"description": _("Groups of DocTypes"),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Page",
|
||||
"description": _("Pages in Desk (place holders)"),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Report",
|
||||
"description": _("Script or Query reports"),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Print Format",
|
||||
"description": _("Customized Formats for Printing, Email"),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Custom Script",
|
||||
"description": _("Client side script extensions in Javascript"),
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Logs"),
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Scheduler Log",
|
||||
"description": _("Errors in Background Events"),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Bulk Email",
|
||||
"description": _("Background Email Queue"),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Error Snapshot",
|
||||
"description": _("A log of request errors"),
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
43
frappe/config/desk.py
Normal file
43
frappe/config/desk.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return [
|
||||
{
|
||||
"label": _("Tools"),
|
||||
"icon": "octicon octicon-briefcase",
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "ToDo",
|
||||
"label": _("To Do"),
|
||||
"description": _("Documents assigned to you and by you."),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Event",
|
||||
"label": _("Calendar"),
|
||||
"view": "Calendar",
|
||||
"description": _("Event and other calendars."),
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"label": _("Messages"),
|
||||
"name": "messages",
|
||||
"description": _("Chat messages and other notifications."),
|
||||
"data_doctype": "Comment"
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Note",
|
||||
"description": _("Private and public Notes."),
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"label": _("Activity"),
|
||||
"name": "activity",
|
||||
"description": _("Activity log of all users."),
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -3,73 +3,38 @@ from frappe import _
|
|||
|
||||
def get_data():
|
||||
return {
|
||||
"Activity": {
|
||||
"color": "#e67e22",
|
||||
"icon": "icon-play",
|
||||
"icon": "octicon octicon-pulse",
|
||||
"label": _("Activity"),
|
||||
"link": "activity",
|
||||
"type": "page"
|
||||
},
|
||||
"Calendar": {
|
||||
"color": "#2980b9",
|
||||
"icon": "icon-calendar",
|
||||
"icon": "octicon octicon-calendar",
|
||||
"label": _("Calendar"),
|
||||
"link": "Calendar/Event",
|
||||
"type": "view"
|
||||
},
|
||||
"Messages": {
|
||||
"color": "#9b59b6",
|
||||
"icon": "icon-comments",
|
||||
"icon": "octicon octicon-comment-discussion",
|
||||
"label": _("Messages"),
|
||||
"link": "messages",
|
||||
"type": "page"
|
||||
},
|
||||
"To Do": {
|
||||
"color": "#f1c40f",
|
||||
"icon": "icon-check",
|
||||
"icon": "octicon octicon-check",
|
||||
"label": _("To Do"),
|
||||
"link": "List/ToDo",
|
||||
"doctype": "ToDo",
|
||||
"type": "list"
|
||||
},
|
||||
"Notes": {
|
||||
"color": "#95a5a6",
|
||||
"doctype": "Note",
|
||||
"icon": "icon-file-alt",
|
||||
"icon": "octicon octicon-file-text",
|
||||
"label": _("Notes"),
|
||||
"link": "List/Note",
|
||||
"File Manager": {
|
||||
"color": "#AA784D",
|
||||
"doctype": "File",
|
||||
"icon": "octicon octicon-file-directory",
|
||||
"label": _("File Manager"),
|
||||
"link": "List/File",
|
||||
"type": "list"
|
||||
},
|
||||
"Website": {
|
||||
"color": "#16a085",
|
||||
"icon": "icon-globe",
|
||||
"icon": "octicon octicon-globe",
|
||||
"type": "module"
|
||||
},
|
||||
"Installer": {
|
||||
"color": "#5ac8fb",
|
||||
"icon": "icon-download",
|
||||
"icon": "octicon octicon-cloud-download",
|
||||
"link": "applications",
|
||||
"type": "page",
|
||||
"label": _("Installer")
|
||||
},
|
||||
"Setup": {
|
||||
"color": "#bdc3c7",
|
||||
"icon": "icon-wrench",
|
||||
"reverse": 1,
|
||||
"icon": "octicon octicon-settings",
|
||||
"type": "module"
|
||||
},
|
||||
"Core": {
|
||||
"label": _("Developer"),
|
||||
"color": "#589494",
|
||||
"icon": "icon-cog",
|
||||
"icon": "octicon octicon-file-binary",
|
||||
"icon": "octicon octicon-circuit-board",
|
||||
"type": "module",
|
||||
"system_manager": 1
|
||||
},
|
||||
"Desk": {
|
||||
"label": _("Tools"),
|
||||
"color": "#FFF5A7",
|
||||
"reverse": 1,
|
||||
"icon": "octicon octicon-calendar",
|
||||
"type": "module"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
frappe/config/docs.py
Normal file
31
frappe/config/docs.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
source_link = "https://github.com/frappe/frappe"
|
||||
docs_base_url = "https://frappe.github.io/frappe"
|
||||
headline = "Superhero Web Framework"
|
||||
sub_heading = "Build extensions to ERPNext or make your own app"
|
||||
hide_install = True
|
||||
long_description = """Frappe is a full stack web application framework written in Python,
|
||||
Javascript, HTML/CSS with MySQL as the backend. It was built for ERPNext
|
||||
but is pretty generic and can be used to build database driven apps.
|
||||
|
||||
The key differece in Frappe compared to other frameworks is that Frappe
|
||||
is that meta-data is also treated as data and is used to build front-ends
|
||||
very easily. Frappe comes with a full blown admin UI called the **Desk**
|
||||
that handles forms, navigation, lists, menus, permissions, file attachment
|
||||
and much more out of the box.
|
||||
|
||||
Frappe also has a plug-in architecture that can be used to build plugins
|
||||
to ERPNext.
|
||||
|
||||
Frappe Framework was designed to build [ERPNext](https://erpnext.com), open source
|
||||
ERP for managing small and medium sized businesses.
|
||||
|
||||
[Get started with the Tutorial](https://frappe.github.io/frappe/user/tutorial)
|
||||
"""
|
||||
docs_version = "6.x.x"
|
||||
|
||||
def get_context(context):
|
||||
context.top_bar_items = [
|
||||
{"label": "Developer Tutorials", "url": context.docs_base_url + "/user", "right": 1},
|
||||
{"label": "Documentation", "url": context.docs_base_url + "/current", "right": 1}
|
||||
]
|
||||
|
|
@ -38,6 +38,13 @@ def get_data():
|
|||
"icon": "icon-shield",
|
||||
"description": _("Set Permissions per User")
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"name": "modules_setup",
|
||||
"label": _("Show / Hide Modules"),
|
||||
"icon": "icon-upload",
|
||||
"description": _("Show or hide modules globally.")
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
|
|
@ -67,17 +74,14 @@ def get_data():
|
|||
"hide_count": True
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"name": "modules_setup",
|
||||
"label": _("Show / Hide Modules"),
|
||||
"icon": "icon-upload",
|
||||
"description": _("Show or hide modules globally.")
|
||||
"type": "doctype",
|
||||
"name": "Scheduler Log",
|
||||
"description": _("Log of error on automated events (scheduler).")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Naming Series",
|
||||
"description": _("Set numbering series for transactions."),
|
||||
"hide_count": True
|
||||
"name": "Error Snapshot",
|
||||
"description": _("Log of error during requests.")
|
||||
},
|
||||
]
|
||||
},
|
||||
|
|
@ -94,15 +98,24 @@ def get_data():
|
|||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Rename Tool",
|
||||
"description": _("Rename many items by uploading a .csv file."),
|
||||
"name": "Naming Series",
|
||||
"description": _("Set numbering series for transactions."),
|
||||
"hide_count": True
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "File Data",
|
||||
"description": _("Manage uploaded files.")
|
||||
}
|
||||
"name": "Rename Tool",
|
||||
"label": _("Bulk Rename"),
|
||||
"description": _("Rename many items by uploading a .csv file."),
|
||||
"hide_count": True
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"name": "backups",
|
||||
"label": _("Download Backups"),
|
||||
"description": _("List of backups available for download"),
|
||||
"icon": "icon-download"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -169,6 +182,30 @@ def get_data():
|
|||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Integrations"),
|
||||
"icon": "icon-star",
|
||||
"items": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "applications",
|
||||
"label": _("Application Installer"),
|
||||
"description": _("Install Applications."),
|
||||
"icon": "icon-download"
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Social Login Keys",
|
||||
"description": _("Enter keys to enable login via Facebook, Google, GitHub."),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Dropbox Backup",
|
||||
"description": _("Manage cloud backups on Dropbox"),
|
||||
"hide_count": True
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Customize"),
|
||||
"icon": "icon-glass",
|
||||
|
|
@ -197,30 +234,6 @@ def get_data():
|
|||
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("System"),
|
||||
"icon": "icon-cog",
|
||||
"items": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "applications",
|
||||
"label": _("Application Installer"),
|
||||
"description": _("Install Applications."),
|
||||
"icon": "icon-download"
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Backup Manager",
|
||||
"description": _("Manage cloud backups on Dropbox"),
|
||||
"hide_count": True
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Scheduler Log",
|
||||
"description": _("Log of error on automated events (scheduler).")
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
add_setup_section(data, "frappe", "website", _("Website"), "icon-globe")
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -77,11 +77,6 @@ def get_data():
|
|||
"type": "doctype",
|
||||
"name": "Website Theme",
|
||||
"description": _("List of themes for Website."),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Social Login Keys",
|
||||
"description": _("Enter keys to enable login via Facebook, Google, GitHub."),
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
240
frappe/core/doctype/async_task/async_task.json
Normal file
240
frappe/core/doctype/async_task/async_task.json
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "",
|
||||
"creation": "2015-07-03 11:28:03.496346",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nQueued\nRunning\nSucceeded\nFailed\n",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "task_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Task Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "runtime",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Runtime",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "result",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Result",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "traceback",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Traceback",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "reference_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference DocType",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference Doc",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "reference_doctype",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:42.038458",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Async Task",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "task_name"
|
||||
}
|
||||
10
frappe/core/doctype/async_task/async_task.py
Normal file
10
frappe/core/doctype/async_task/async_task.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class AsyncTask(Document):
|
||||
pass
|
||||
10
frappe/core/doctype/async_task/async_task_list.js
Normal file
10
frappe/core/doctype/async_task/async_task_list.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
frappe.listview_settings['Async Task'] = {
|
||||
add_fields: ["status"],
|
||||
get_indicator: function(doc) {
|
||||
if(doc.status==="Succeeded") {
|
||||
return [__("Succeeded"), "green", "status,=,Succeeded"];
|
||||
} else if(doc.status==="Failed") {
|
||||
return [__("Failed"), "red", "status,=,Failed"];
|
||||
}
|
||||
}
|
||||
};
|
||||
12
frappe/core/doctype/async_task/test_async_task.py
Normal file
12
frappe/core/doctype/async_task/test_async_task.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Async Task')
|
||||
|
||||
class TestAsyncTask(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -10,6 +10,8 @@
|
|||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "module",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
|
|
@ -17,6 +19,7 @@
|
|||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Module",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
|
|
@ -26,7 +29,8 @@
|
|||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
|
|
@ -36,7 +40,8 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-03-24 16:03:52.359042",
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:42.426522",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Block Module",
|
||||
|
|
|
|||
|
|
@ -1,154 +1,338 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"creation": "2012-08-08 10:40:11",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "comment",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Comment",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "comment",
|
||||
"oldfieldtype": "Text",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "comment_type",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Comment Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Email\nChat\nPhone\nSMS\nCreated\nSubmitted\nCancelled\nAssigned\nAssignment Completed\nComment\nWorkflow\nLabel\nAttachment\nAttachment Removed",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "comment_by",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Comment By",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "comment_by",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"search_index": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "comment_by_fullname",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Comment By Fullname",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "comment_by_fullname",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"search_index": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "comment_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Comment Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "comment_date",
|
||||
"oldfieldtype": "Date",
|
||||
"permlevel": 0,
|
||||
"search_index": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "comment_time",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Comment Time",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "comment_time",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"search_index": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "comment_doctype",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Comment Doctype",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "comment_doctype",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"search_index": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "comment_docname",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Comment Docname",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "comment_docname",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"search_index": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "post_topic",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Post Topic",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "post_topic",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"search_index": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "unsubscribed",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Unsubscribed",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "reference_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference DocType",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"read_only": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Reference DocType and Reference Name are used to render a comment as a link (href) to a Doc.",
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "reference_doctype",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"read_only": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-comments",
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"modified": "2015-06-08 12:31:15.122312",
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:43.314568",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Comment",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"title_field": "comment"
|
||||
}
|
||||
|
|
@ -7,11 +7,13 @@ from frappe import _
|
|||
from frappe.website.render import clear_cache
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.db_schema import add_column
|
||||
from frappe.utils import get_fullname
|
||||
from frappe.utils import get_fullname, get_link_to_form
|
||||
from frappe.core.doctype.user.user import extract_mentions
|
||||
|
||||
exclude_from_linked_with = True
|
||||
|
||||
class Comment(Document):
|
||||
"""Comments are added to Documents via forms or views like blogs etc."""
|
||||
__doclink__ = "https://frappe.io/docs/models/core/comment"
|
||||
no_feed_on_delete = True
|
||||
|
||||
def get_feed(self):
|
||||
|
|
@ -33,6 +35,25 @@ class Comment(Document):
|
|||
"feed_type": comment_type
|
||||
}
|
||||
|
||||
def after_insert(self):
|
||||
"""Send realtime updates"""
|
||||
if not self.comment_doctype:
|
||||
return
|
||||
|
||||
if self.comment_doctype == 'Message':
|
||||
if self.comment_docname == frappe.session.user:
|
||||
message = self.as_dict()
|
||||
message['broadcast'] = True
|
||||
frappe.publish_realtime('new_message', message)
|
||||
else:
|
||||
# comment_docname contains the user who is addressed in the messages' page comment
|
||||
frappe.publish_realtime('new_message', self.as_dict(), user=self.comment_docname)
|
||||
else:
|
||||
frappe.publish_realtime('new_comment', self.as_dict(), doctype= self.comment_doctype,
|
||||
docname = self.comment_docname)
|
||||
|
||||
self.notify_mentions()
|
||||
|
||||
def validate(self):
|
||||
"""Raise exception for more than 50 comments."""
|
||||
if frappe.db.sql("""select count(*) from tabComment where comment_doctype=%s
|
||||
|
|
@ -127,6 +148,33 @@ class Comment(Document):
|
|||
|
||||
self.update_comments_in_parent(_comments)
|
||||
|
||||
def notify_mentions(self):
|
||||
if self.comment_doctype and self.comment_docname and self.comment and self.comment_type=="Comment":
|
||||
mentions = extract_mentions(self.comment)
|
||||
|
||||
if not mentions:
|
||||
return
|
||||
|
||||
sender_fullname = get_fullname(frappe.session.user)
|
||||
parent_doc_label = "{0} {1}".format(_(self.comment_doctype), self.comment_docname)
|
||||
subject = _("{0} mentioned you in a comment in {1}").format(sender_fullname, parent_doc_label)
|
||||
message = frappe.get_template("templates/emails/mentioned_in_comment.html").render({
|
||||
"sender_fullname": sender_fullname,
|
||||
"comment": self,
|
||||
"link": get_link_to_form(self.comment_doctype, self.comment_docname, label=parent_doc_label)
|
||||
})
|
||||
|
||||
recipients = [frappe.db.get_value("User", {"enabled": 1, "username": username, "user_type": "System User"})
|
||||
for username in mentions]
|
||||
|
||||
frappe.sendmail(
|
||||
recipients=recipients,
|
||||
sender=frappe.session.user,
|
||||
subject=subject,
|
||||
message=message,
|
||||
bulk=True
|
||||
)
|
||||
|
||||
def on_doctype_update():
|
||||
"""Add index to `tabComment` `(comment_doctype, comment_name)`"""
|
||||
if not frappe.db.sql("""show index from `tabComment`
|
||||
|
|
@ -134,4 +182,3 @@ def on_doctype_update():
|
|||
frappe.db.commit()
|
||||
frappe.db.sql("""alter table `tabComment`
|
||||
add index comment_doctype_docname_index(comment_doctype, comment_docname)""")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,31 @@
|
|||
frappe.ui.form.on("Communication", "setup", function(frm) {
|
||||
frappe.call({
|
||||
method:"frappe.core.doctype.doctype.communication.get_convert_to",
|
||||
callback: function(r) {
|
||||
frappe.communication_convert_to = r.message;
|
||||
frm.convert_to_click = [];
|
||||
$.each(r.message, function(i, v) {
|
||||
frm.convert_to_click.append({label:__(v), value:v, action:function() {
|
||||
frm.convert_to($(this).attr("data-value"));
|
||||
}});
|
||||
});
|
||||
frm.set_convert_button();
|
||||
}
|
||||
});
|
||||
|
||||
frm.set_convert_button = function() {
|
||||
frm.add_custom_button(__("Add To"), frm.convert_to_click);
|
||||
};
|
||||
|
||||
frm.convert_to = function(doctype) {
|
||||
|
||||
};
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Communication", "refresh", function(frm) {
|
||||
frm.convert_to_click && frm.set_convert_button();
|
||||
frm.subject_field = "subject";
|
||||
|
||||
if(frm.doc.reference_doctype && frm.doc.reference_name) {
|
||||
frm.add_custom_button(__(frm.doc.reference_name), function() {
|
||||
frappe.set_route("Form", frm.doc.reference_doctype, frm.doc.reference_name);
|
||||
}, frappe.boot.doctype_icons[frm.doc.reference_doctype]);
|
||||
} else {
|
||||
// if an unlinked communication, set email field
|
||||
if (frm.doc.sent_or_received==="Received") {
|
||||
frm.email_field = "sender";
|
||||
} else {
|
||||
frm.email_field = "recipients";
|
||||
}
|
||||
}
|
||||
|
||||
if(frm.doc.status==="Open") {
|
||||
frm.add_custom_button("Close", function() {
|
||||
frm.set_value("status", "Closed");
|
||||
frm.save();
|
||||
});
|
||||
} else if (frm.doc.status !== "Linked") {
|
||||
frm.add_custom_button("Reopen", function() {
|
||||
frm.set_value("status", "Open");
|
||||
frm.save();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Communication", "onload", function(frm) {
|
||||
|
|
|
|||
|
|
@ -1,188 +1,605 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-01-29 10:47:14",
|
||||
"custom": 0,
|
||||
"description": "Keep a track of all communications",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Master",
|
||||
"document_type": "Setup",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "COMM-",
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"label": "Series",
|
||||
"options": "COMM-",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "sent_or_received",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Sent or Received",
|
||||
"options": "Sent\nReceived",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"label": "Status",
|
||||
"options": "Open\nReplied\nArchived",
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
},
|
||||
{
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Subject",
|
||||
"label": "Series",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "COMM-",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "communication_medium",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Communication Medium",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nChat\nPhone\nEmail\nSMS\nVisit\nOther",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "recipients",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Recipients",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:doc.communication_medium===\"Email\"",
|
||||
"fieldname": "cc",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "CC",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:doc.communication_medium!==\"Email\"",
|
||||
"fieldname": "phone_no",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Phone No.",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_doctype",
|
||||
"fieldtype": "Link",
|
||||
"label": "Reference DocType",
|
||||
"options": "DocType",
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Open\nReplied\nClosed\nLinked",
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Reference Name",
|
||||
"options": "reference_doctype",
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "sent_or_received",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Sent or Received",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Sent\nReceived",
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Integrations can use this field to set email delivery status",
|
||||
"fieldname": "delivery_status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Delivery Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nSent\nBounced\nOpened\nMarked As Spam\nRejected\nDelayed\nSoft-Bounced\nClicked\nRecipient Unsubscribed",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_10",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Subject",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "content",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Content",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "400"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"fieldname": "additional_info",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Additional Info",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "recipients",
|
||||
"fieldtype": "Data",
|
||||
"label": "Recipients",
|
||||
"permlevel": 0
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "More Information",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "sender",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Sender",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "sender_full_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Sender Full Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"fieldname": "email_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Email Account",
|
||||
"options": "Email Account",
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
},
|
||||
{
|
||||
"default": "__user",
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "User",
|
||||
"options": "User",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break5",
|
||||
"fieldtype": "Column Break",
|
||||
"label": "On",
|
||||
"permlevel": 0
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "Today",
|
||||
"fieldname": "communication_date",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Date",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "_user_tags",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "User Tags",
|
||||
"no_copy": 1,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_14",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "reference_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference DocType",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "reference_doctype",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "in_reply_to",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "In Reply To",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Communication",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "email_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Email Account",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Email Account",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "__user",
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 1,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "User",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "User",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "0",
|
||||
"fieldname": "unread_notification_sent",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Unread Notification Sent",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"read_only": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "_user_tags",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "User Tags",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-comment",
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"modified": "2015-03-23 02:33:55.289739",
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:43.407180",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Communication",
|
||||
|
|
@ -190,62 +607,27 @@
|
|||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Support Team",
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales Manager",
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales User",
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"search_fields": "subject",
|
||||
"title_field": "subject"
|
||||
}
|
||||
|
|
@ -5,8 +5,9 @@ from __future__ import unicode_literals, absolute_import
|
|||
import frappe
|
||||
import json
|
||||
from email.utils import formataddr, parseaddr
|
||||
from frappe.utils import get_url, get_formatted_email, cstr, cint
|
||||
from frappe.utils import get_url, get_formatted_email, cint, validate_email_add, split_emails
|
||||
from frappe.utils.file_manager import get_file
|
||||
from frappe.email.bulk import check_bulk_limit
|
||||
import frappe.email.smtp
|
||||
from frappe import _
|
||||
|
||||
|
|
@ -16,6 +17,7 @@ class Communication(Document):
|
|||
no_feed_on_delete = True
|
||||
|
||||
"""Communication represents an external communication like Email."""
|
||||
|
||||
def get_parent_doc(self):
|
||||
"""Returns document of `reference_doctype`, `reference_doctype`"""
|
||||
if not hasattr(self, "parent_doc"):
|
||||
|
|
@ -25,6 +27,31 @@ class Communication(Document):
|
|||
self.parent_doc = None
|
||||
return self.parent_doc
|
||||
|
||||
def validate(self):
|
||||
if self.get("__islocal"):
|
||||
if self.reference_doctype and self.reference_name:
|
||||
self.status = "Linked"
|
||||
else:
|
||||
self.status = "Open"
|
||||
|
||||
# validate recipients
|
||||
for email in split_emails(self.recipients):
|
||||
validate_email_add(email, throw=True)
|
||||
|
||||
# validate CC
|
||||
for email in split_emails(self.cc):
|
||||
validate_email_add(email, throw=True)
|
||||
|
||||
def after_insert(self):
|
||||
# send new comment to listening clients
|
||||
comment = self.as_dict()
|
||||
comment["comment"] = comment["content"]
|
||||
comment["comment_by"] = comment["sender"]
|
||||
comment["comment_type"] = comment["communication_medium"]
|
||||
|
||||
frappe.publish_realtime('new_comment', comment, doctype = self.reference_doctype,
|
||||
docname = self.reference_name)
|
||||
|
||||
def on_update(self):
|
||||
"""Update parent status as `Open` or `Replied`."""
|
||||
self.update_parent()
|
||||
|
|
@ -41,7 +68,9 @@ class Communication(Document):
|
|||
to_status = "Open" if self.sent_or_received=="Received" else "Replied"
|
||||
|
||||
if to_status in status_field.options.splitlines():
|
||||
frappe.db.set_value(parent.doctype, parent.name, "status", to_status)
|
||||
parent.db_set("status", to_status)
|
||||
|
||||
parent.notify_update()
|
||||
|
||||
def send(self, print_html=None, print_format=None, attachments=None,
|
||||
send_me_a_copy=False, recipients=None):
|
||||
|
|
@ -53,31 +82,44 @@ class Communication(Document):
|
|||
self.send_me_a_copy = send_me_a_copy
|
||||
self.notify(print_html, print_format, attachments, recipients)
|
||||
|
||||
def set_incoming_outgoing_accounts(self):
|
||||
self.incoming_email_account = self.outgoing_email_account = None
|
||||
def notify(self, print_html=None, print_format=None, attachments=None,
|
||||
recipients=None, cc=None, fetched_from_email_account=False):
|
||||
"""Calls a delayed celery task 'sendmail' that enqueus email in Bulk Email queue
|
||||
|
||||
if self.reference_doctype:
|
||||
self.incoming_email_account = frappe.db.get_value("Email Account",
|
||||
{"append_to": self.reference_doctype, "enable_incoming": 1}, "email_id")
|
||||
:param print_html: Send given value as HTML attachment
|
||||
:param print_format: Attach print format of parent document
|
||||
:param attachments: A list of filenames that should be attached when sending this email
|
||||
:param recipients: Email recipients
|
||||
:param cc: Send email as CC to
|
||||
:param fetched_from_email_account: True when pulling email, the notification shouldn't go to the main recipient
|
||||
|
||||
self.outgoing_email_account = frappe.db.get_value("Email Account",
|
||||
{"append_to": self.reference_doctype, "enable_outgoing": 1},
|
||||
["email_id", "always_use_account_email_id_as_sender"], as_dict=True)
|
||||
"""
|
||||
recipients, cc = self.get_recipients_and_cc(recipients, cc,
|
||||
fetched_from_email_account=fetched_from_email_account)
|
||||
|
||||
if not self.incoming_email_account:
|
||||
self.incoming_email_account = frappe.db.get_value("Email Account", {"default_incoming": 1}, "email_id")
|
||||
self.emails_not_sent_to = set(self.all_email_addresses) - set(self.sent_email_addresses)
|
||||
|
||||
if not self.outgoing_email_account:
|
||||
self.outgoing_email_account = frappe.db.get_value("Email Account", {"default_outgoing": 1},
|
||||
["email_id", "always_use_account_email_id_as_sender"], as_dict=True) or frappe._dict()
|
||||
if frappe.flags.in_test:
|
||||
# for test cases, run synchronously
|
||||
self._notify(print_html=print_html, print_format=print_format, attachments=attachments,
|
||||
recipients=recipients, cc=cc)
|
||||
else:
|
||||
check_bulk_limit(list(set(self.sent_email_addresses)))
|
||||
|
||||
from frappe.tasks import sendmail
|
||||
sendmail.delay(frappe.local.site, self.name,
|
||||
print_html=print_html, print_format=print_format, attachments=attachments,
|
||||
recipients=recipients, cc=cc, lang=frappe.local.lang, session=frappe.local.session)
|
||||
|
||||
def _notify(self, print_html=None, print_format=None, attachments=None,
|
||||
recipients=None, cc=None):
|
||||
|
||||
def notify(self, print_html=None, print_format=None, attachments=None, recipients=None, except_recipient=False):
|
||||
self.prepare_to_notify(print_html, print_format, attachments)
|
||||
if not recipients:
|
||||
recipients = self.get_recipients(except_recipient=except_recipient)
|
||||
|
||||
frappe.sendmail(
|
||||
recipients=recipients,
|
||||
recipients=(recipients or []) + (cc or []),
|
||||
show_as_cc=(cc or []),
|
||||
expose_recipients=True,
|
||||
sender=self.sender,
|
||||
reply_to=self.incoming_email_account,
|
||||
subject=self.subject,
|
||||
|
|
@ -90,6 +132,33 @@ class Communication(Document):
|
|||
bulk=True
|
||||
)
|
||||
|
||||
def get_recipients_and_cc(self, recipients, cc, fetched_from_email_account=False):
|
||||
self.all_email_addresses = []
|
||||
self.sent_email_addresses = []
|
||||
self.previous_email_sender = None
|
||||
|
||||
if not recipients:
|
||||
recipients = self.get_recipients(fetched_from_email_account=fetched_from_email_account)
|
||||
|
||||
if not cc:
|
||||
cc = self.get_cc(recipients, fetched_from_email_account=fetched_from_email_account)
|
||||
|
||||
if fetched_from_email_account:
|
||||
# email was already sent to the original recipient by the sender's email service
|
||||
original_recipients, recipients = recipients, []
|
||||
|
||||
# send email to the sender of the previous email in the thread which this email is a reply to
|
||||
if self.previous_email_sender:
|
||||
recipients.append(self.previous_email_sender)
|
||||
|
||||
# cc that was received in the email
|
||||
original_cc = split_emails(self.cc)
|
||||
|
||||
# don't cc to people who already received the mail from sender's email service
|
||||
cc = list(set(cc) - set(original_cc) - set(original_recipients))
|
||||
|
||||
return recipients, cc
|
||||
|
||||
def prepare_to_notify(self, print_html=None, print_format=None, attachments=None):
|
||||
"""Prepare to make multipart MIME Email
|
||||
|
||||
|
|
@ -125,70 +194,134 @@ class Communication(Document):
|
|||
else:
|
||||
self.attachments.append(a)
|
||||
|
||||
def get_recipients(self, except_recipient=False):
|
||||
"""Build a list of users to which this email should go to"""
|
||||
def set_incoming_outgoing_accounts(self):
|
||||
self.incoming_email_account = self.outgoing_email_account = None
|
||||
|
||||
if self.reference_doctype:
|
||||
self.incoming_email_account = frappe.db.get_value("Email Account",
|
||||
{"append_to": self.reference_doctype, "enable_incoming": 1}, "email_id")
|
||||
|
||||
self.outgoing_email_account = frappe.db.get_value("Email Account",
|
||||
{"append_to": self.reference_doctype, "enable_outgoing": 1},
|
||||
["email_id", "always_use_account_email_id_as_sender"], as_dict=True)
|
||||
|
||||
if not self.incoming_email_account:
|
||||
self.incoming_email_account = frappe.db.get_value("Email Account",
|
||||
{"default_incoming": 1, "enable_incoming": 1}, "email_id")
|
||||
|
||||
if not self.outgoing_email_account:
|
||||
self.outgoing_email_account = frappe.db.get_value("Email Account",
|
||||
{"default_outgoing": 1, "enable_outgoing": 1},
|
||||
["email_id", "always_use_account_email_id_as_sender"], as_dict=True) or frappe._dict()
|
||||
|
||||
def get_recipients(self, fetched_from_email_account=False):
|
||||
"""Build a list of email addresses for To"""
|
||||
# [EDGE CASE] self.recipients can be None when an email is sent as BCC
|
||||
original_recipients = [s.strip() for s in cstr(self.recipients).split(",")]
|
||||
recipients = original_recipients[:]
|
||||
recipients = split_emails(self.recipients)
|
||||
|
||||
if fetched_from_email_account and self.in_reply_to:
|
||||
# add sender of previous reply
|
||||
self.previous_email_sender = frappe.db.get_value("Communication", self.in_reply_to, "sender")
|
||||
recipients.append(self.previous_email_sender)
|
||||
|
||||
if recipients:
|
||||
# exclude email accounts
|
||||
exclude = [d[0] for d in
|
||||
frappe.db.get_all("Email Account", ["email_id"], {"enable_incoming": 1}, as_list=True)]
|
||||
exclude += [d[0] for d in
|
||||
frappe.db.get_all("Email Account", ["login_id"], {"enable_incoming": 1}, as_list=True)
|
||||
if d[0]]
|
||||
|
||||
recipients = self.filter_email_list(recipients, exclude)
|
||||
|
||||
return recipients
|
||||
|
||||
def get_cc(self, recipients=None, fetched_from_email_account=False):
|
||||
"""Build a list of email addresses for CC"""
|
||||
# get a copy of CC list
|
||||
cc = split_emails(self.cc)
|
||||
|
||||
if self.reference_doctype and self.reference_name:
|
||||
recipients += self.get_earlier_participants()
|
||||
recipients += self.get_commentors()
|
||||
recipients += self.get_assignees()
|
||||
recipients += self.get_starrers()
|
||||
if fetched_from_email_account:
|
||||
# if it is a fetched email, add follows to CC
|
||||
cc.append(self.get_owner_email())
|
||||
cc += self.get_assignees()
|
||||
cc += self.get_starrers()
|
||||
|
||||
# remove unsubscribed recipients
|
||||
unsubscribed = [d[0] for d in frappe.db.get_all("User", ["name"], {"thread_notify": 0}, as_list=True)]
|
||||
email_accounts = [d[0] for d in frappe.db.get_all("Email Account", ["email_id"], {"enable_incoming": 1}, as_list=True)]
|
||||
sender = parseaddr(self.sender)[1]
|
||||
if cc:
|
||||
# exclude email accounts, unfollows, recipients and unsubscribes
|
||||
exclude = [d[0] for d in
|
||||
frappe.db.get_all("Email Account", ["email_id"], {"enable_incoming": 1}, as_list=True)]
|
||||
exclude += [d[0] for d in
|
||||
frappe.db.get_all("Email Account", ["login_id"], {"enable_incoming": 1}, as_list=True)
|
||||
if d[0]]
|
||||
exclude += [d[0] for d in frappe.db.get_all("User", ["name"], {"thread_notify": 0}, as_list=True)]
|
||||
exclude += [(parseaddr(email)[1] or "").lower() for email in recipients]
|
||||
|
||||
if fetched_from_email_account:
|
||||
# exclude sender when pulling email
|
||||
exclude += [parseaddr(self.sender)[1]]
|
||||
|
||||
if self.reference_doctype and self.reference_name:
|
||||
exclude += [d[0] for d in frappe.db.get_all("Email Unsubscribe", ["email"],
|
||||
{"reference_doctype": self.reference_doctype, "reference_name": self.reference_name}, as_list=True)]
|
||||
|
||||
cc = self.filter_email_list(cc, exclude, is_cc=True)
|
||||
|
||||
if getattr(self, "send_me_a_copy", False) and self.sender not in cc:
|
||||
self.all_email_addresses.append((parseaddr(self.sender)[1] or "").lower())
|
||||
cc.append(self.sender)
|
||||
|
||||
return cc
|
||||
|
||||
def filter_email_list(self, email_list, exclude, is_cc=False):
|
||||
# temp variables
|
||||
filtered = []
|
||||
for e in list(set(recipients)):
|
||||
if (e=="Administrator") or ((e==self.sender) and (e not in original_recipients)) or \
|
||||
(e in unsubscribed) or (e in email_accounts):
|
||||
email_address_list = []
|
||||
|
||||
for email in list(set(email_list)):
|
||||
email_address = (parseaddr(email)[1] or "").lower()
|
||||
if not email_address:
|
||||
continue
|
||||
|
||||
email_id = parseaddr(e)[1]
|
||||
if email_id==sender or email_id in unsubscribed or email_id in email_accounts:
|
||||
# this will be used to eventually find email addresses that aren't sent to
|
||||
self.all_email_addresses.append(email_address)
|
||||
|
||||
if (email in exclude) or (email_address in exclude):
|
||||
continue
|
||||
|
||||
if except_recipient and (e==self.recipients or email_id==self.recipients):
|
||||
# while pulling email, don't send email to current recipient
|
||||
continue
|
||||
if is_cc:
|
||||
is_user_enabled = frappe.db.get_value("User", email_address, "enabled")
|
||||
if is_user_enabled==0:
|
||||
# don't send to disabled users
|
||||
continue
|
||||
|
||||
if e not in filtered and email_id not in filtered:
|
||||
filtered.append(e)
|
||||
# make sure of case-insensitive uniqueness of email address
|
||||
if email_address not in email_address_list:
|
||||
# append the full email i.e. "Human <human@example.com>"
|
||||
filtered.append(email)
|
||||
email_address_list.append(email_address)
|
||||
|
||||
if getattr(self, "send_me_a_copy", False):
|
||||
filtered.append(self.sender)
|
||||
self.sent_email_addresses.extend(email_address_list)
|
||||
|
||||
return filtered
|
||||
|
||||
def get_starrers(self):
|
||||
"""Return list of users who have starred this document."""
|
||||
if self.reference_doctype and self.reference_name:
|
||||
return self.get_parent_doc().get_starred_by()
|
||||
else:
|
||||
return []
|
||||
return [( get_formatted_email(user) or user ) for user in self.get_parent_doc().get_starred_by()]
|
||||
|
||||
def get_earlier_participants(self):
|
||||
return frappe.db.sql_list("""
|
||||
select distinct sender
|
||||
from tabCommunication where
|
||||
reference_doctype=%s and reference_name=%s""",
|
||||
(self.reference_doctype, self.reference_name))
|
||||
|
||||
def get_commentors(self):
|
||||
return frappe.db.sql_list("""
|
||||
select distinct comment_by
|
||||
from tabComment where
|
||||
comment_doctype=%s and comment_docname=%s and
|
||||
ifnull(unsubscribed, 0)=0 and comment_by!='Administrator'""",
|
||||
(self.reference_doctype, self.reference_name))
|
||||
def get_owner_email(self):
|
||||
owner = self.get_parent_doc().owner
|
||||
return get_formatted_email(owner) or owner
|
||||
|
||||
def get_assignees(self):
|
||||
return [d.owner for d in frappe.db.get_all("ToDo", filters={"reference_type": self.reference_doctype,
|
||||
"reference_name": self.reference_name, "status": "Open"}, fields=["owner"])]
|
||||
return [( get_formatted_email(d.owner) or d.owner ) for d in
|
||||
frappe.db.get_all("ToDo", filters={
|
||||
"reference_type": self.reference_doctype,
|
||||
"reference_name": self.reference_name,
|
||||
"status": "Open"
|
||||
}, fields=["owner"])
|
||||
]
|
||||
|
||||
def get_attach_link(self, print_format):
|
||||
"""Returns public link for the attachment via `templates/emails/print_link.html`."""
|
||||
|
|
@ -208,7 +341,7 @@ def on_doctype_update():
|
|||
def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent",
|
||||
sender=None, recipients=None, communication_medium="Email", send_email=False,
|
||||
print_html=None, print_format=None, attachments='[]', ignore_doctype_permissions=False,
|
||||
send_me_a_copy=False):
|
||||
send_me_a_copy=False, cc=None):
|
||||
"""Make a new communication.
|
||||
|
||||
:param doctype: Reference DocType.
|
||||
|
|
@ -232,7 +365,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
|
|||
raise frappe.PermissionError("You are not allowed to send emails related to: {doctype} {name}".format(
|
||||
doctype=doctype, name=name))
|
||||
|
||||
if not sender and frappe.session.user != "Administrator":
|
||||
if not sender:
|
||||
sender = get_formatted_email(frappe.session.user)
|
||||
|
||||
comm = frappe.get_doc({
|
||||
|
|
@ -241,24 +374,23 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
|
|||
"content": content,
|
||||
"sender": sender,
|
||||
"recipients": recipients,
|
||||
"communication_medium": "Email",
|
||||
"cc": cc or None,
|
||||
"communication_medium": communication_medium,
|
||||
"sent_or_received": sent_or_received,
|
||||
"reference_doctype": doctype,
|
||||
"reference_name": name
|
||||
})
|
||||
comm.insert(ignore_permissions=True)
|
||||
|
||||
recipients = None
|
||||
|
||||
# needed for communication.notify which uses celery delay
|
||||
# if not committed, delayed task doesn't find the communication
|
||||
frappe.db.commit()
|
||||
|
||||
if send_email:
|
||||
comm.send_me_a_copy = send_me_a_copy
|
||||
recipients = comm.get_recipients()
|
||||
comm.send(print_html, print_format, attachments, send_me_a_copy=send_me_a_copy, recipients=recipients)
|
||||
comm.send(print_html, print_format, attachments, send_me_a_copy=send_me_a_copy)
|
||||
|
||||
return {
|
||||
"name": comm.name,
|
||||
"recipients": ", ".join(recipients) if recipients else None
|
||||
"emails_not_sent_to": ", ".join(comm.emails_not_sent_to) if hasattr(comm, "emails_not_sent_to") else None
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_convert_to():
|
||||
return frappe.get_hooks("communication_convert_to")
|
||||
|
|
|
|||
|
|
@ -1,49 +1,81 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"creation": "2013-02-22 01:27:32",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "defkey",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Key",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "defkey",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "200px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "200px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "defvalue",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Value",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "defvalue",
|
||||
"oldfieldtype": "Text",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "200px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "200px"
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-02-19 01:06:59.622792",
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:44.604889",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DefaultValue",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"read_only": 0
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -6,5 +6,4 @@ from __future__ import unicode_literals
|
|||
from frappe.model.document import Document
|
||||
|
||||
class DocField(Document):
|
||||
__doclink__ = "https://frappe.io/docs/models/core/docfield"
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,242 +1,628 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"creation": "2013-02-22 01:27:33",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "role_and_level",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Role and Level",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "role",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Role",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "role",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Role",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "150px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "150px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Filter records based on User Permissions defined for a user",
|
||||
"fieldname": "apply_user_permissions",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Apply User Permissions",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Apply this rule if the User is the Owner",
|
||||
"fieldname": "if_owner",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "If user is the owner",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "0",
|
||||
"fieldname": "permlevel",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Level",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "permlevel",
|
||||
"oldfieldtype": "Int",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "40px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "40px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "",
|
||||
"description": "JSON list of DocTypes used to apply User Permissions. If empty, all linked DocTypes will be used to apply User Permissions.",
|
||||
"fieldname": "user_permission_doctypes",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "User Permission DocTypes",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_4",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Permissions",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"fieldname": "read",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Read",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "read",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "32px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "32px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"fieldname": "write",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Write",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "write",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "32px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "32px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"fieldname": "create",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Create",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "create",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "32px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "32px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"fieldname": "delete",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Delete",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_8",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "submit",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Submit",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "submit",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "32px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "32px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "cancel",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Cancel",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "cancel",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "32px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "32px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "amend",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Amend",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "amend",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "32px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "32px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "additional_permissions",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Additional Permissions",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"fieldname": "report",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Report",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "32px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "32px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"fieldname": "export",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Export",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "import",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Import",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "This role update User Permissions for a user",
|
||||
"fieldname": "set_user_permissions",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Set User Permissions",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_19",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"fieldname": "share",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Share",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"fieldname": "print",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Print",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"fieldname": "email",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Email",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-03-18 06:09:58.928014",
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:45.429411",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocPerm",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"read_only": 0
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
|
|
@ -7,5 +7,4 @@ import frappe
|
|||
from frappe.model.document import Document
|
||||
|
||||
class DocPerm(Document):
|
||||
__doclink__ = "https://frappe.io/docs/models/v5.x/core/docperm"
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -19,6 +21,7 @@
|
|||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "User",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "User",
|
||||
"permlevel": 0,
|
||||
|
|
@ -26,12 +29,15 @@
|
|||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"reqd": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "share_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -39,6 +45,7 @@
|
|||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Document Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
|
|
@ -48,10 +55,13 @@
|
|||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "share_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -59,6 +69,7 @@
|
|||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Document Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "share_doctype",
|
||||
"permlevel": 0,
|
||||
|
|
@ -68,10 +79,13 @@
|
|||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "0",
|
||||
"fieldname": "read",
|
||||
"fieldtype": "Check",
|
||||
|
|
@ -80,6 +94,7 @@
|
|||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Read",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
|
|
@ -88,10 +103,13 @@
|
|||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "0",
|
||||
"fieldname": "write",
|
||||
"fieldtype": "Check",
|
||||
|
|
@ -100,6 +118,7 @@
|
|||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Write",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
|
|
@ -108,10 +127,13 @@
|
|||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "0",
|
||||
"fieldname": "share",
|
||||
"fieldtype": "Check",
|
||||
|
|
@ -120,6 +142,7 @@
|
|||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Share",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
|
|
@ -128,7 +151,31 @@
|
|||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "everyone",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Everyone",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
|
|
@ -138,7 +185,8 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-02-12 11:30:52.968078",
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:45.569747",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocShare",
|
||||
|
|
@ -153,6 +201,7 @@
|
|||
"delete": 1,
|
||||
"email": 0,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 1,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
|
|
|
|||
|
|
@ -7,10 +7,13 @@ from frappe.model.document import Document
|
|||
from frappe import _
|
||||
from frappe.utils import get_fullname
|
||||
|
||||
exclude_from_linked_with = True
|
||||
|
||||
class DocShare(Document):
|
||||
no_feed_on_delete = True
|
||||
|
||||
def validate(self):
|
||||
self.validate_user()
|
||||
self.check_share_permission()
|
||||
self.cascade_permissions_downwards()
|
||||
self.get_doc().run_method("validate_share", self)
|
||||
|
|
@ -26,6 +29,12 @@ class DocShare(Document):
|
|||
self._doc = frappe.get_doc(self.share_doctype, self.share_name)
|
||||
return self._doc
|
||||
|
||||
def validate_user(self):
|
||||
if self.everyone:
|
||||
self.user = None
|
||||
elif not self.user:
|
||||
frappe.throw(_("User is mandatory for Share"), frappe.MandatoryError)
|
||||
|
||||
def check_share_permission(self):
|
||||
if (not self.flags.ignore_share_permission and
|
||||
not frappe.has_permission(self.share_doctype, "share", self.get_doc())):
|
||||
|
|
|
|||
|
|
@ -78,3 +78,15 @@ class TestDocShare(unittest.TestCase):
|
|||
frappe.set_user(self.user)
|
||||
self.assertFalse(self.event.has_permission("share"))
|
||||
|
||||
def test_share_with_everyone(self):
|
||||
self.assertTrue(self.event.name not in frappe.share.get_shared("Event", self.user))
|
||||
|
||||
frappe.share.set_permission("Event", self.event.name, None, "read", everyone=1)
|
||||
self.assertTrue(self.event.name in frappe.share.get_shared("Event", self.user))
|
||||
self.assertTrue(self.event.name in frappe.share.get_shared("Event", "test1@example.com"))
|
||||
self.assertTrue(self.event.name not in frappe.share.get_shared("Event", "Guest"))
|
||||
|
||||
frappe.share.set_permission("Event", self.event.name, None, "read", value=0, everyone=1)
|
||||
self.assertTrue(self.event.name not in frappe.share.get_shared("Event", self.user))
|
||||
self.assertTrue(self.event.name not in frappe.share.get_shared("Event", "test1@example.com"))
|
||||
self.assertTrue(self.event.name not in frappe.share.get_shared("Event", "Guest"))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 1,
|
||||
"autoname": "Prompt",
|
||||
"creation": "2013-02-18 13:36:19",
|
||||
|
|
@ -7,371 +8,936 @@
|
|||
"description": "DocType is a Table / Form in the application.",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "sb0",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldtype": "Section Break",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "module",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Module",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "module",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Module Def",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Child Tables are shown as a Grid in other DocTypes.",
|
||||
"fieldname": "istable",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Is Child Table",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "istable",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Single Types have only one record no tables associated. Values are stored in tabSingles",
|
||||
"fieldname": "issingle",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Is Single",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "issingle",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "cb01",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "document_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Document Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "document_type",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nMaster\nTransaction\nSystem\nOther",
|
||||
"permlevel": 0
|
||||
"options": "\nDocument\nSetup\nSystem\nOther",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "icon",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Icon",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "custom",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Custom?",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "plugin",
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "app",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Plugin",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"read_only": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "fields_section_break",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Fields",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldtype": "Section Break",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "fields",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Fields",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "fields",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "DocField",
|
||||
"permlevel": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "sb1",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Naming",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "<a onclick=\"msgprint('<ol>\\\n<li><b>field:[fieldname]</b> - By Field\\\n<li><b>naming_series:</b> - By Naming Series (field called naming_series must be present\\\n<li><b>Prompt</b> - Prompt user for a name\\\n<li><b>[series]</b> - Series by prefix (separated by a dot); for example PRE.#####\\\n</ol>')\">Naming Options</a>",
|
||||
"fieldname": "autoname",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Auto Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "autoname",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "name_case",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Name Case",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "name_case",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nTitle Case\nUPPER CASE",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Text",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_15",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Show this field as title",
|
||||
"fieldname": "title_field",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Title Field",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "search_fields",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Search Fields",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "search_fields",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "modified",
|
||||
"description": "",
|
||||
"fieldname": "sort_field",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Sort Field",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "DESC",
|
||||
"fieldname": "sort_order",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Sort Order",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "ASC\nDESC",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:!doc.istable",
|
||||
"fieldname": "sb2",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Permission Rules",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "permissions",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Permissions",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "permissions",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "DocPerm",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:!doc.istable",
|
||||
"fieldname": "sb3",
|
||||
"fieldtype": "Section Break",
|
||||
"permlevel": 0
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "cb30",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Permissions Settings",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "in_create",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "User Cannot Create",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "in_create",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "read_only",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "User Cannot Search",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "read_only",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "is_submittable",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Is Submittable",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Allow Import via Data Import Tool",
|
||||
"fieldname": "allow_import",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Allow Import",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "allow_rename",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Allow Rename",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "allow_rename",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "in_dialog",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "In Dialog",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "in_dialog",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "read_only_onload",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Show Print First",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "read_only_onload",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "max_attachments",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Max Attachments",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "max_attachments",
|
||||
"oldfieldtype": "Int",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "cb31",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Hide Actions",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "hide_heading",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Hide Heading",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "hide_heading",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "hide_toolbar",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Hide Toolbar",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "hide_toolbar",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "allow_copy",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Hide Copy",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "allow_copy",
|
||||
"oldfieldtype": "Check",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "default_print_format",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Default Print Format",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-bolt",
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-03-03 10:40:45.768116",
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:45.648699",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocType",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Administrator",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"search_fields": "module",
|
||||
"sort_field": "name",
|
||||
"sort_order": "ASC"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
|
@ -12,13 +13,15 @@ from frappe.model.document import Document
|
|||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||
from frappe.desk.notifications import delete_notification_count_for
|
||||
from frappe.modules import make_boilerplate
|
||||
from frappe.model.db_schema import validate_column_name
|
||||
|
||||
class InvalidFieldNameError(frappe.ValidationError): pass
|
||||
|
||||
form_grid_templates = {
|
||||
"fields": "templates/form_grid/fields.html"
|
||||
}
|
||||
|
||||
class DocType(Document):
|
||||
__doclink__ = "https://frappe.io/docs/models/core/doctype"
|
||||
def get_feed(self):
|
||||
return self.name
|
||||
|
||||
|
|
@ -30,14 +33,13 @@ class DocType(Document):
|
|||
- Check fieldnames (duplication etc)
|
||||
- Clear permission table for child tables
|
||||
- Add `amended_from` and `ameneded_by` if Amendable"""
|
||||
if not frappe.conf.get("developer_mode") and not self.custom:
|
||||
frappe.throw(_("Not in Developer Mode! Set in site_config.json or make 'Custom' DocType."))
|
||||
self.check_developer_mode()
|
||||
for c in [".", "/", "#", "&", "=", ":", "'", '"']:
|
||||
if c in self.name:
|
||||
frappe.throw(_("{0} not allowed in name").format(c))
|
||||
self.validate_series()
|
||||
self.scrub_field_names()
|
||||
self.validate_title_field()
|
||||
self.validate_document_type()
|
||||
validate_fields(self)
|
||||
|
||||
if self.istable:
|
||||
|
|
@ -48,6 +50,20 @@ class DocType(Document):
|
|||
|
||||
self.make_amendable()
|
||||
|
||||
def check_developer_mode(self):
|
||||
"""Throw exception if not developer mode or via patch"""
|
||||
if frappe.flags.in_patch:
|
||||
return
|
||||
|
||||
if not frappe.conf.get("developer_mode") and not self.custom:
|
||||
frappe.throw(_("Not in Developer Mode! Set in site_config.json or make 'Custom' DocType."))
|
||||
|
||||
def validate_document_type(self):
|
||||
if self.document_type=="Transaction":
|
||||
self.document_type = "Document"
|
||||
if self.document_type=="Master":
|
||||
self.document_type = "Setup"
|
||||
|
||||
def change_modified_of_parent(self):
|
||||
"""Change the timestamp of parent DocType if the current one is a child to clear caches."""
|
||||
if frappe.flags.in_import:
|
||||
|
|
@ -71,12 +87,8 @@ class DocType(Document):
|
|||
else:
|
||||
d.fieldname = d.fieldtype.lower().replace(" ","_") + "_" + str(d.idx)
|
||||
|
||||
|
||||
def validate_title_field(self):
|
||||
"""Throw exception if `title_field` is not a valid field."""
|
||||
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"))
|
||||
# fieldnames should be lowercase
|
||||
d.fieldname = d.fieldname.lower()
|
||||
|
||||
def validate_series(self, autoname=None, name=None):
|
||||
"""Validate if `autoname` property is correctly set."""
|
||||
|
|
@ -88,7 +100,7 @@ class DocType(Document):
|
|||
|
||||
if autoname and (not autoname.startswith('field:')) \
|
||||
and (not autoname.startswith('eval:')) \
|
||||
and (not autoname in ('Prompt', 'hash')) \
|
||||
and (not autoname.lower() in ('prompt', 'hash')) \
|
||||
and (not autoname.startswith('naming_series:')):
|
||||
|
||||
prefix = autoname.split('.')[0]
|
||||
|
|
@ -127,6 +139,11 @@ class DocType(Document):
|
|||
|
||||
def before_rename(self, old, new, merge=False):
|
||||
"""Throw exception if merge. DocTypes cannot be merged."""
|
||||
if not self.custom and frappe.session.user != "Administrator":
|
||||
frappe.throw(_("DocType can only be renamed by Administrator"))
|
||||
|
||||
self.check_developer_mode()
|
||||
|
||||
if merge:
|
||||
frappe.throw(_("DocType can not be merged"))
|
||||
|
||||
|
|
@ -201,7 +218,7 @@ class DocType(Document):
|
|||
return max_idx and max_idx[0][0] or 0
|
||||
|
||||
def validate_fields_for_doctype(doctype):
|
||||
validate_fields(frappe.get_meta(doctype))
|
||||
validate_fields(frappe.get_meta(doctype, cached=False))
|
||||
|
||||
# this is separate because it is also called via custom field
|
||||
def validate_fields(meta):
|
||||
|
|
@ -217,13 +234,13 @@ def validate_fields(meta):
|
|||
9. Precision is set in numeric fields and is between 1 & 6.
|
||||
10. Fold is not at the end (if set).
|
||||
11. `search_fields` are valid.
|
||||
12. `title_field` and title field pattern are valid.
|
||||
13. `unique` check is only valid for Data, Link and Read Only fieldtypes.
|
||||
14. `unique` cannot be checked if there exist non-unique values.
|
||||
|
||||
:param meta: `frappe.model.meta.Meta` object to check."""
|
||||
def check_illegal_characters(fieldname):
|
||||
for c in ['.', ',', ' ', '-', '&', '%', '=', '"', "'", '*', '$',
|
||||
'(', ')', '[', ']', '/']:
|
||||
if c in fieldname:
|
||||
frappe.throw(_("{0} not allowed in fieldname {1}").format(c, fieldname))
|
||||
validate_column_name(fieldname)
|
||||
|
||||
def check_unique_fieldname(fieldname):
|
||||
duplicates = filter(None, map(lambda df: df.fieldname==fieldname and str(df.idx) or None, fields))
|
||||
|
|
@ -263,8 +280,8 @@ def validate_fields(meta):
|
|||
def check_dynamic_link_options(d):
|
||||
if d.fieldtype=="Dynamic Link":
|
||||
doctype_pointer = filter(lambda df: df.fieldname==d.options, fields)
|
||||
if not doctype_pointer or (doctype_pointer[0].fieldtype!="Link") \
|
||||
or (doctype_pointer[0].options!="DocType"):
|
||||
if not doctype_pointer or (doctype_pointer[0].fieldtype not in ("Link", "Select")) \
|
||||
or (doctype_pointer[0].fieldtype=="Link" and doctype_pointer[0].options!="DocType"):
|
||||
frappe.throw(_("Options 'Dynamic Link' type of field must point to another Link Field with options as 'DocType'"))
|
||||
|
||||
def check_illegal_default(d):
|
||||
|
|
@ -278,8 +295,17 @@ def validate_fields(meta):
|
|||
frappe.throw(_("Precision should be between 1 and 6"))
|
||||
|
||||
def check_unique_and_text(d):
|
||||
if getattr(d, "unique", False) and d.fieldtype in ("Text", "Long Text", "Small Text", "Code", "Text Editor"):
|
||||
frappe.throw(_("Fieldtype {0} for {1} cannot be unique").format(d.fieldtype, d.label))
|
||||
if getattr(d, "unique", False):
|
||||
if d.fieldtype not in ("Data", "Link", "Read Only"):
|
||||
frappe.throw(_("Fieldtype {0} for {1} cannot be unique").format(d.fieldtype, d.label))
|
||||
|
||||
if not d.get("__islocal"):
|
||||
has_non_unique_values = frappe.db.sql("""select `{fieldname}`, count(*)
|
||||
from `tab{doctype}` group by `{fieldname}` having count(*) > 1 limit 1""".format(
|
||||
doctype=d.parent, fieldname=d.fieldname))
|
||||
|
||||
if has_non_unique_values and has_non_unique_values[0][0]:
|
||||
frappe.throw(_("Field '{0}' cannot be set as Unique as it has non-unique values").format(d.label))
|
||||
|
||||
if d.search_index and d.fieldtype in ("Text", "Long Text", "Small Text", "Code", "Text Editor"):
|
||||
frappe.throw(_("Fieldtype {0} for {1} cannot be indexed").format(d.fieldtype, d.label))
|
||||
|
|
@ -299,6 +325,7 @@ def validate_fields(meta):
|
|||
frappe.throw(_("Fold can not be at the end of the form"))
|
||||
|
||||
def check_search_fields(meta):
|
||||
"""Throw exception if `search_fields` don't contain valid fields."""
|
||||
if not meta.search_fields:
|
||||
return
|
||||
|
||||
|
|
@ -308,13 +335,40 @@ def validate_fields(meta):
|
|||
if fieldname not in fieldname_list:
|
||||
frappe.throw(_("Search Fields should contain valid fieldnames"))
|
||||
|
||||
def check_title_field(meta):
|
||||
"""Throw exception if `title_field` isn't a valid fieldname."""
|
||||
if not meta.title_field:
|
||||
return
|
||||
|
||||
fieldname_list = [d.fieldname for d in fields]
|
||||
|
||||
if meta.title_field not in fieldname_list:
|
||||
frappe.throw(_("Title field must be a valid fieldname"), InvalidFieldNameError)
|
||||
|
||||
def _validate_title_field_pattern(pattern):
|
||||
if not pattern:
|
||||
return
|
||||
|
||||
for fieldname in re.findall("{(.*?)}", pattern, re.UNICODE):
|
||||
if fieldname.startswith("{"):
|
||||
# edge case when double curlies are used for escape
|
||||
continue
|
||||
|
||||
if fieldname not in fieldname_list:
|
||||
frappe.throw(_("{{{0}}} is not a valid fieldname pattern. It should be {{field_name}}.").format(fieldname),
|
||||
InvalidFieldNameError)
|
||||
|
||||
df = meta.get("fields", filters={"fieldname": meta.title_field})[0]
|
||||
if df:
|
||||
_validate_title_field_pattern(df.options)
|
||||
_validate_title_field_pattern(df.default)
|
||||
|
||||
fields = meta.get("fields")
|
||||
for d in fields:
|
||||
if not d.permlevel: d.permlevel = 0
|
||||
if not d.fieldname:
|
||||
frappe.throw(_("Fieldname is required in row {0}").format(d.idx))
|
||||
d.fieldname = d.fieldname.lower()
|
||||
check_illegal_characters(d.fieldname)
|
||||
check_unique_fieldname(d.fieldname)
|
||||
check_illegal_mandatory(d)
|
||||
|
|
@ -327,6 +381,7 @@ def validate_fields(meta):
|
|||
|
||||
check_fold(fields)
|
||||
check_search_fields(meta)
|
||||
check_title_field(meta)
|
||||
|
||||
def validate_permissions_for_doctype(doctype, for_remove=False):
|
||||
"""Validates if permissions are set correctly."""
|
||||
|
|
@ -362,14 +417,21 @@ def validate_permissions(doctype, for_remove=False):
|
|||
|
||||
def check_double(d):
|
||||
has_similar = False
|
||||
similar_because_of = ""
|
||||
for p in permissions:
|
||||
if (p.role==d.role and p.permlevel==d.permlevel
|
||||
and p.apply_user_permissions==d.apply_user_permissions and p!=d):
|
||||
has_similar = True
|
||||
break
|
||||
if p.role==d.role and p.permlevel==d.permlevel and p!=d:
|
||||
if p.apply_user_permissions==d.apply_user_permissions:
|
||||
has_similar = True
|
||||
similar_because_of = _("Apply User Permissions")
|
||||
break
|
||||
elif p.if_owner==d.if_owner:
|
||||
similar_because_of = _("If Owner")
|
||||
has_similar = True
|
||||
break
|
||||
|
||||
if has_similar:
|
||||
frappe.throw(_("{0}: Only one rule allowed with the same Role, Level and Apply User Permissions").format(get_txt(d)))
|
||||
frappe.throw(_("{0}: Only one rule allowed with the same Role, Level and {1}")\
|
||||
.format(get_txt(d), similar_because_of))
|
||||
|
||||
def check_level_zero_is_set(d):
|
||||
if cint(d.permlevel) > 0 and d.role != 'All':
|
||||
|
|
@ -466,4 +528,3 @@ def init_list(doctype):
|
|||
doc = frappe.get_meta(doctype)
|
||||
make_boilerplate("controller_list.js", doc)
|
||||
make_boilerplate("controller_list.html", doc)
|
||||
|
||||
|
|
|
|||
12
frappe/core/doctype/doctype/test_doctype.py
Normal file
12
frappe/core/doctype/doctype/test_doctype.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('DocType')
|
||||
|
||||
class TestDocType(unittest.TestCase):
|
||||
pass
|
||||
16
frappe/core/doctype/error_snapshot/error_snapshot.js
Normal file
16
frappe/core/doctype/error_snapshot/error_snapshot.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
frappe.ui.form.on("Error Snapshot", "load", function(frm){
|
||||
frm.set_read_only(true);
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Error Snapshot", "refresh", function(frm){
|
||||
frm.set_df_property("view", "options", frappe.render_template("error_snapshot", {"doc": frm.doc}));
|
||||
|
||||
if (frm.doc.relapses) {
|
||||
frm.add_custom_button(__('Show Relapses'), function() {
|
||||
frappe.route_options = {
|
||||
parent_error_snapshot: frm.doc.name
|
||||
};
|
||||
frappe.set_route("List", "Error Snapshot");
|
||||
});
|
||||
}
|
||||
});
|
||||
344
frappe/core/doctype/error_snapshot/error_snapshot.json
Normal file
344
frappe/core/doctype/error_snapshot/error_snapshot.json
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"creation": "2015-11-28 00:57:39.766888",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "System",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "view",
|
||||
"fieldtype": "HTML",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Snapshot View",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "seen",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 1,
|
||||
"in_list_view": 0,
|
||||
"label": "Seen",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "evalue",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Friendly Title",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "timestamp",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Timestamp",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"fieldname": "relapses",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Relapses",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "etype",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Exception Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "traceback",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Traceback",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "parent_error_snapshot",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Parent Error Snapshot",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "pyver",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Pyver",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "exception",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Exception",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "locals",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Locals",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "frames",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Frames",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"in_create": 1,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-12-28 00:44:30.751680",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Error Snapshot",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Administrator",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "timestamp",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "evalue"
|
||||
}
|
||||
29
frappe/core/doctype/error_snapshot/error_snapshot.py
Normal file
29
frappe/core/doctype/error_snapshot/error_snapshot.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class ErrorSnapshot(Document):
|
||||
def onload(self):
|
||||
if not self.parent_error_snapshot:
|
||||
self.db_set('seen', True, update_modified=False)
|
||||
|
||||
for relapsed in frappe.get_all("Error Snapshot", filters={"parent_error_snapshot": self.name}):
|
||||
frappe.db.set_value("Error Snapshot", relapsed.name, "seen", True, update_modified=False)
|
||||
|
||||
frappe.local.flags.commit = True
|
||||
|
||||
def validate(self):
|
||||
parent = frappe.get_all("Error Snapshot",
|
||||
filters={"evalue": self.evalue, "parent_error_snapshot": ""},
|
||||
fields=["name", "relapses", "seen"], limit_page_length=1)
|
||||
|
||||
if parent:
|
||||
parent = parent[0]
|
||||
self.update({"parent_error_snapshot": parent['name']})
|
||||
frappe.db.set_value('Error Snapshot', parent['name'], 'relapses', parent["relapses"] + 1)
|
||||
if parent["seen"]:
|
||||
frappe.db.set_value("Error Snapshot", parent["name"], "seen", False)
|
||||
14
frappe/core/doctype/error_snapshot/error_snapshot_list.js
Normal file
14
frappe/core/doctype/error_snapshot/error_snapshot_list.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
frappe.listview_settings["Error Snapshot"] = {
|
||||
add_fields: ["parent_error_snapshot", "relapses", "seen"],
|
||||
filters:[
|
||||
["parent_error_snapshot","=",null],
|
||||
["seen", "=", false]
|
||||
],
|
||||
get_indicator: function(doc){
|
||||
if (doc.parent_error_snapshot && doc.parent_error_snapshot.length){
|
||||
return [__("Relapsed"), !doc.seen ? "orange" : "blue", "parent_error_snapshot,!=,"];
|
||||
} else {
|
||||
return [__("First Level"), !doc.seen ? "red" : "green", "parent_error_snapshot,=,"];
|
||||
}
|
||||
}
|
||||
}
|
||||
12
frappe/core/doctype/error_snapshot/test_error_snapshot.py
Normal file
12
frappe/core/doctype/error_snapshot/test_error_snapshot.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Error Snapshot')
|
||||
|
||||
class TestErrorSnapshot(unittest.TestCase):
|
||||
pass
|
||||
0
frappe/core/doctype/file/__init__.py
Normal file
0
frappe/core/doctype/file/__init__.py
Normal file
25
frappe/core/doctype/file/file.js
Normal file
25
frappe/core/doctype/file/file.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
frappe.ui.form.on("File", "refresh", function(frm) {
|
||||
if(!frm.doc.is_folder) {
|
||||
frm.add_custom_button(__('Download'), function() {
|
||||
var file_url = frm.doc.file_url;
|
||||
if (frm.doc.file_name) {
|
||||
file_url = file_url.replace(/#/g, '%23');
|
||||
}
|
||||
window.open(file_url);
|
||||
}, "icon-download");
|
||||
}
|
||||
|
||||
var wrapper = frm.get_field("preview_html").$wrapper;
|
||||
var is_viewable = frappe.utils.is_image_file(frm.doc.file_url);
|
||||
|
||||
frm.toggle_display("preview", is_viewable);
|
||||
frm.toggle_display("preview_html", is_viewable);
|
||||
|
||||
if(is_viewable){
|
||||
wrapper.html('<div class="img_preview">\
|
||||
<img class="img-responsive" src="'+frm.doc.file_url+'"></img>\
|
||||
</div>');
|
||||
} else {
|
||||
wrapper.empty();
|
||||
}
|
||||
});
|
||||
575
frappe/core/doctype/file/file.json
Normal file
575
frappe/core/doctype/file/file.json
Normal file
|
|
@ -0,0 +1,575 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
"autoname": "",
|
||||
"creation": "2012-12-12 11:19:22",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "file_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "File Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "file_name",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:!doc.is_folder",
|
||||
"fieldname": "is_private",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Is Private",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "preview",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Preview",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "preview_html",
|
||||
"fieldtype": "HTML",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Preview HTML",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "is_home_folder",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Is Home Folder",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "is_attachments_folder",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Is Attachments Folder",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "file_size",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "File Size",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:!doc.is_folder",
|
||||
"fieldname": "file_url",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "File URL",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "thumbnail_url",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Thumbnail URL",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "folder",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Folder",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "File",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "is_folder",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Is Folder",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:!doc.is_folder",
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "attached_to_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Attached To DocType",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "attached_to_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Attached To Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "content_hash",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Content Hash",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "lft",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "lft",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "rgt",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "rgt",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "old_parent",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "old_parent",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-file",
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2015-12-08 05:03:48.767257",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "File",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 1,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"title_field": "file_name"
|
||||
}
|
||||
351
frappe/core/doctype/file/file.py
Normal file
351
frappe/core/doctype/file/file.py
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
"""
|
||||
record of files
|
||||
|
||||
naming for same name files: file.gif, file-1.gif, file-2.gif etc
|
||||
"""
|
||||
|
||||
import frappe, frappe.utils
|
||||
from frappe.utils.file_manager import delete_file_data_content, get_content_hash, get_random_filename
|
||||
from frappe import _
|
||||
|
||||
from frappe.utils.nestedset import NestedSet
|
||||
from frappe.utils import strip
|
||||
import json
|
||||
import urllib
|
||||
from PIL import Image, ImageOps
|
||||
import os
|
||||
import requests
|
||||
import requests.exceptions
|
||||
import StringIO
|
||||
import mimetypes, imghdr
|
||||
from frappe.utils import get_files_path
|
||||
|
||||
class FolderNotEmpty(frappe.ValidationError): pass
|
||||
|
||||
exclude_from_linked_with = True
|
||||
|
||||
class File(NestedSet):
|
||||
nsm_parent_field = 'folder'
|
||||
no_feed_on_delete = True
|
||||
|
||||
def before_insert(self):
|
||||
frappe.local.rollback_observers.append(self)
|
||||
self.set_folder_name()
|
||||
self.set_name()
|
||||
|
||||
def get_name_based_on_parent_folder(self):
|
||||
path = get_breadcrumbs(self.folder)
|
||||
folder_name = frappe.get_value("File", self.folder, "file_name")
|
||||
return "/".join([d.file_name for d in path] + [folder_name, self.file_name])
|
||||
|
||||
def set_name(self):
|
||||
"""Set name for folder"""
|
||||
if self.is_folder:
|
||||
if self.folder:
|
||||
self.name = self.get_name_based_on_parent_folder()
|
||||
else:
|
||||
# home
|
||||
self.name = self.file_name
|
||||
else:
|
||||
self.name = frappe.generate_hash("", 10)
|
||||
|
||||
def after_insert(self):
|
||||
self.update_parent_folder_size()
|
||||
|
||||
def after_rename(self, olddn, newdn, merge=False):
|
||||
for successor in self.get_successor():
|
||||
setup_folder_path(successor, self.name)
|
||||
|
||||
def get_successor(self):
|
||||
return frappe.db.sql_list("select name from tabFile where folder='%s'"%self.name) or []
|
||||
|
||||
def validate(self):
|
||||
if self.is_new():
|
||||
self.validate_duplicate_entry()
|
||||
self.validate_folder()
|
||||
|
||||
if not self.flags.ignore_file_validate:
|
||||
self.validate_file()
|
||||
self.generate_content_hash()
|
||||
|
||||
self.set_folder_size()
|
||||
|
||||
def set_folder_size(self):
|
||||
"""Set folder size if folder"""
|
||||
if self.is_folder and not self.is_new():
|
||||
self.file_size = self.get_folder_size()
|
||||
frappe.db.set_value("File", self.name, "file_size", self.file_size)
|
||||
|
||||
for folder in self.get_ancestors():
|
||||
frappe.db.set_value("File", folder, "file_size", self.get_folder_size(folder))
|
||||
|
||||
def get_folder_size(self, folder=None):
|
||||
"""Returns folder size for current folder"""
|
||||
if not folder:
|
||||
folder = self.name
|
||||
file_size = frappe.db.sql("""select sum(ifnull(file_size,0))
|
||||
from tabFile where folder=%s """, (folder))[0][0]
|
||||
|
||||
return file_size
|
||||
|
||||
def update_parent_folder_size(self):
|
||||
"""Update size of parent folder"""
|
||||
if self.folder and not self.is_folder: # it not home
|
||||
frappe.get_doc("File", self.folder).save(ignore_permissions=True)
|
||||
|
||||
def set_folder_name(self):
|
||||
"""Make parent folders if not exists based on reference doctype and name"""
|
||||
if self.attached_to_doctype and not self.folder:
|
||||
self.folder = frappe.db.get_value("File", {"is_attachments_folder": 1})
|
||||
|
||||
def validate_folder(self):
|
||||
if not self.is_home_folder and not self.folder and \
|
||||
not self.flags.ignore_folder_validate:
|
||||
frappe.throw(_("Folder is mandatory"))
|
||||
|
||||
def validate_file(self):
|
||||
"""Validates existence of public file
|
||||
TODO: validate for private file
|
||||
"""
|
||||
if (self.file_url or "").startswith("/files/"):
|
||||
if not self.file_name:
|
||||
self.file_name = self.file_url.split("/files/")[-1]
|
||||
|
||||
if not os.path.exists(get_files_path(self.file_name.lstrip("/"))):
|
||||
frappe.throw(_("File {0} does not exist").format(self.file_url), IOError)
|
||||
|
||||
def validate_duplicate_entry(self):
|
||||
if not self.flags.ignore_duplicate_entry_error and not self.is_folder:
|
||||
# check duplicate name
|
||||
|
||||
# check duplicate assignement
|
||||
n_records = frappe.db.sql("""select name from `tabFile`
|
||||
where content_hash=%s
|
||||
and name!=%s
|
||||
and attached_to_doctype=%s
|
||||
and attached_to_name=%s""", (self.content_hash, self.name, self.attached_to_doctype,
|
||||
self.attached_to_name))
|
||||
if len(n_records) > 0:
|
||||
self.duplicate_entry = n_records[0][0]
|
||||
frappe.throw(frappe._("Same file has already been attached to the record"), frappe.DuplicateEntryError)
|
||||
|
||||
def generate_content_hash(self):
|
||||
if self.content_hash or not self.file_url:
|
||||
return
|
||||
|
||||
if self.file_url.startswith("/files/"):
|
||||
try:
|
||||
with open(get_files_path(self.file_name.lstrip("/")), "r") as f:
|
||||
self.content_hash = get_content_hash(f.read())
|
||||
except IOError:
|
||||
frappe.msgprint(_("File {0} does not exist").format(self.file_url))
|
||||
raise
|
||||
|
||||
def on_trash(self):
|
||||
if self.is_home_folder or self.is_attachments_folder:
|
||||
frappe.throw(_("Cannot delete Home and Attachments folders"))
|
||||
self.check_folder_is_empty()
|
||||
self.check_reference_doc_permission()
|
||||
super(File, self).on_trash()
|
||||
self.delete_file()
|
||||
|
||||
def make_thumbnail(self):
|
||||
if self.file_url:
|
||||
if self.file_url.startswith("/files"):
|
||||
try:
|
||||
image, filename, extn = get_local_image(self.file_url)
|
||||
except IOError:
|
||||
return
|
||||
|
||||
else:
|
||||
try:
|
||||
image, filename, extn = get_web_image(self.file_url)
|
||||
except (requests.exceptions.HTTPError, requests.exceptions.SSLError, IOError):
|
||||
return
|
||||
|
||||
thumbnail = ImageOps.fit(
|
||||
image,
|
||||
(300, 300),
|
||||
Image.ANTIALIAS
|
||||
)
|
||||
|
||||
thumbnail_url = filename + "_small." + extn
|
||||
|
||||
path = os.path.abspath(frappe.get_site_path("public", thumbnail_url.lstrip("/")))
|
||||
|
||||
try:
|
||||
thumbnail.save(path)
|
||||
self.db_set("thumbnail_url", thumbnail_url)
|
||||
except IOError:
|
||||
frappe.msgprint("Unable to write file format for {0}".format(path))
|
||||
return
|
||||
|
||||
return thumbnail_url
|
||||
|
||||
def after_delete(self):
|
||||
self.update_parent_folder_size()
|
||||
|
||||
def check_folder_is_empty(self):
|
||||
"""Throw exception if folder is not empty"""
|
||||
files = frappe.get_all("File", filters={"folder": self.name}, fields=("name", "file_name"))
|
||||
|
||||
if self.is_folder and files:
|
||||
frappe.throw(_("Folder {0} is not empty").format(self.name), FolderNotEmpty)
|
||||
|
||||
def check_reference_doc_permission(self):
|
||||
"""Check if permission exists for reference document"""
|
||||
if self.attached_to_name:
|
||||
# check persmission
|
||||
try:
|
||||
if not self.flags.ignore_permissions and \
|
||||
not frappe.has_permission(self.attached_to_doctype,
|
||||
"write", self.attached_to_name):
|
||||
frappe.throw(frappe._("No permission to write / remove."),
|
||||
frappe.PermissionError)
|
||||
except frappe.DoesNotExistError:
|
||||
pass
|
||||
|
||||
def delete_file(self):
|
||||
"""If file not attached to any other record, delete it"""
|
||||
if self.file_name and self.content_hash and (not frappe.db.count("File",
|
||||
{"content_hash": self.content_hash, "name": ["!=", self.name]})):
|
||||
delete_file_data_content(self)
|
||||
|
||||
elif self.file_url:
|
||||
delete_file_data_content(self, only_thumbnail=True)
|
||||
|
||||
def on_rollback(self):
|
||||
self.flags.on_rollback = True
|
||||
self.on_trash()
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("File", ["attached_to_doctype", "attached_to_name"])
|
||||
|
||||
def make_home_folder():
|
||||
home = frappe.get_doc({
|
||||
"doctype": "File",
|
||||
"is_folder": 1,
|
||||
"is_home_folder": 1,
|
||||
"file_name": _("Home")
|
||||
}).insert()
|
||||
|
||||
frappe.get_doc({
|
||||
"doctype": "File",
|
||||
"folder": home.name,
|
||||
"is_folder": 1,
|
||||
"is_attachments_folder": 1,
|
||||
"file_name": _("Attachments")
|
||||
}).insert()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_breadcrumbs(folder):
|
||||
"""returns name, file_name of parent folder"""
|
||||
lft, rgt = frappe.db.get_value("File", folder, ["lft", "rgt"])
|
||||
return frappe.db.sql("""select name, file_name from tabFile
|
||||
where lft < %s and rgt > %s order by lft asc""", (lft, rgt), as_dict=1)
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_new_folder(file_name, folder):
|
||||
""" create new folder under current parent folder """
|
||||
file = frappe.new_doc("File")
|
||||
file.file_name = file_name
|
||||
file.is_folder = 1
|
||||
file.folder = folder
|
||||
file.insert()
|
||||
|
||||
@frappe.whitelist()
|
||||
def move_file(file_list, new_parent, old_parent):
|
||||
if isinstance(file_list, basestring):
|
||||
file_list = json.loads(file_list)
|
||||
|
||||
for file_obj in file_list:
|
||||
setup_folder_path(file_obj.get("name"), new_parent)
|
||||
|
||||
# recalculate sizes
|
||||
frappe.get_doc("File", old_parent).save()
|
||||
frappe.get_doc("File", new_parent).save()
|
||||
|
||||
def setup_folder_path(filename, new_parent):
|
||||
file = frappe.get_doc("File", filename)
|
||||
file.folder = new_parent
|
||||
file.save()
|
||||
|
||||
if file.is_folder:
|
||||
frappe.rename_doc("File", file.name, file.get_name_based_on_parent_folder(), ignore_permissions=True)
|
||||
|
||||
def get_extension(filename, extn, content):
|
||||
mimetype = None
|
||||
if extn:
|
||||
mimetype = mimetypes.guess_type(filename + "." + extn)[0]
|
||||
|
||||
if mimetype is None or not mimetype.startswith("image/") and content:
|
||||
# detect file extension by reading image header properties
|
||||
extn = imghdr.what(filename + "." + (extn or ""), h=content)
|
||||
|
||||
return extn
|
||||
|
||||
def get_local_image(file_url):
|
||||
file_path = frappe.get_site_path("public", file_url.lstrip("/"))
|
||||
|
||||
try:
|
||||
image = Image.open(file_path)
|
||||
except IOError:
|
||||
frappe.msgprint("Unable to read file format for {0}".format(file_url))
|
||||
raise
|
||||
|
||||
content = None
|
||||
|
||||
try:
|
||||
filename, extn = file_url.rsplit(".", 1)
|
||||
except ValueError:
|
||||
# no extn
|
||||
with open(file_path, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
filename = file_url
|
||||
extn = None
|
||||
|
||||
extn = get_extension(filename, extn, content)
|
||||
|
||||
return image, filename, extn
|
||||
|
||||
def get_web_image(file_url):
|
||||
# downlaod
|
||||
file_url = frappe.utils.get_url(file_url)
|
||||
r = requests.get(file_url, stream=True)
|
||||
try:
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError, e:
|
||||
if "404" in e.args[0]:
|
||||
frappe.msgprint(_("File '{0}' not found").format(file_url))
|
||||
else:
|
||||
frappe.msgprint("Unable to read file format for {0}".format(file_url))
|
||||
raise
|
||||
|
||||
image = Image.open(StringIO.StringIO(r.content))
|
||||
|
||||
try:
|
||||
filename, extn = file_url.rsplit("/", 1)[1].rsplit(".", 1)
|
||||
except ValueError:
|
||||
# the case when the file url doesn't have filename or extension
|
||||
# but is fetched due to a query string. example: https://encrypted-tbn3.gstatic.com/images?q=something
|
||||
filename = get_random_filename()
|
||||
extn = None
|
||||
|
||||
extn = get_extension(filename, extn, r.content)
|
||||
filename = "/files/" + strip(urllib.unquote(filename))
|
||||
|
||||
return image, filename, extn
|
||||
|
||||
def check_file_permission(file_url):
|
||||
for file in frappe.get_all("File", filters={"file_url": file_url, "is_private": 1}, fields=["name", "attached_to_doctype", "attached_to_name"]):
|
||||
|
||||
if (frappe.has_permission("File", ptype="read", doc=file.name)
|
||||
or frappe.has_permission(file.attached_to_doctype, ptype="read", doc=file.attached_to_name)):
|
||||
return True
|
||||
|
||||
raise frappe.PermissionError
|
||||
216
frappe/core/doctype/file/file_list.js
Normal file
216
frappe/core/doctype/file/file_list.js
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
frappe.provide("frappe.ui");
|
||||
|
||||
frappe.listview_settings['File'] = {
|
||||
hide_name_column: true,
|
||||
use_route: true,
|
||||
add_fields: ["is_folder", "file_name", "file_url", "folder", "is_private"],
|
||||
formatters: {
|
||||
file_size: function(value) {
|
||||
// formatter for file size
|
||||
if(value > 1048576) {
|
||||
value = flt(flt(value) / 1048576, 1) + "M";
|
||||
} else if (value > 1024) {
|
||||
value = flt(flt(value) / 1024, 1) + "K";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
prepare_data: function(data) {
|
||||
// set image icons
|
||||
var icon = ""
|
||||
|
||||
if(data.is_folder) {
|
||||
icon += '<i class="icon-folder-close-alt icon-fixed-width"></i> ';
|
||||
} else if(frappe.utils.is_image_file(data.file_name)) {
|
||||
icon += '<i class="icon-picture icon-fixed-width"></i> ';
|
||||
} else {
|
||||
icon += '<i class="icon-file-alt icon-fixed-width"></i> '
|
||||
}
|
||||
|
||||
data._title = icon + (data.file_name ? data.file_name : data.file_url)
|
||||
|
||||
if (data.is_private) {
|
||||
data._title += ' <i class="icon-lock icon-fixed-width text-warning"></i>'
|
||||
}
|
||||
},
|
||||
onload: function(doclist) {
|
||||
doclist.filter_area = doclist.wrapper.find(".show_filters");
|
||||
|
||||
doclist.breadcrumb = $('<ol class="breadcrumb for-file-list"></ol>')
|
||||
.insertBefore(doclist.filter_area);
|
||||
|
||||
doclist.listview.settings.setup_menu(doclist);
|
||||
doclist.listview.settings.setup_dragdrop(doclist);
|
||||
|
||||
doclist.$page.on("click", ".list-delete", function(event) {
|
||||
doclist.listview.settings.add_menu_item_copy(doclist);
|
||||
})
|
||||
},
|
||||
list_view_doc:function(doclist){
|
||||
$(doclist.wrapper).on("click", 'button[list_view_doc="'+doclist.doctype+'"]', function(){
|
||||
dialog = frappe.ui.get_upload_dialog({
|
||||
"args": {
|
||||
"folder": doclist.current_folder,
|
||||
"from_form": 1
|
||||
},
|
||||
callback: function() {
|
||||
doclist.refresh();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
setup_menu: function(doclist) {
|
||||
doclist.page.add_menu_item(__("New Folder"), function() {
|
||||
var d = frappe.prompt(__("Name"), function(values) {
|
||||
if((values.value.indexOf("/") > -1)){
|
||||
frappe.throw("Folder name should not include / !!!");
|
||||
return;
|
||||
}
|
||||
var data = {
|
||||
"file_name": values.value,
|
||||
"folder": doclist.current_folder
|
||||
};
|
||||
frappe.call({
|
||||
method: "frappe.core.doctype.file.file.create_new_folder",
|
||||
args: data,
|
||||
callback: function(r) { }
|
||||
})
|
||||
}, __('Enter folder name'), __("Create"));
|
||||
});
|
||||
|
||||
doclist.page.add_menu_item(__("Edit Folder"), function() {
|
||||
frappe.set_route("Form", "File", doclist.current_folder);
|
||||
});
|
||||
},
|
||||
setup_dragdrop: function(doclist) {
|
||||
$(doclist.$page).on('dragenter dragover', false)
|
||||
.on('drop', function (e) {
|
||||
var dataTransfer = e.originalEvent.dataTransfer;
|
||||
if (!(dataTransfer && dataTransfer.files && dataTransfer.files.length > 0)) {
|
||||
return;
|
||||
}
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
frappe.upload.upload_file(dataTransfer.files[0], {
|
||||
"folder": doclist.current_folder,
|
||||
"from_form": 1
|
||||
}, {
|
||||
confirm_is_private: 1
|
||||
});
|
||||
});
|
||||
},
|
||||
add_menu_item_copy: function(doclist){
|
||||
if (!doclist.copy) {
|
||||
var copy_menu = doclist.page.add_menu_item(__("Copy"), function() {
|
||||
if(doclist.$page.find(".list-delete:checked").length){
|
||||
doclist.selected_files = doclist.get_checked_items();
|
||||
doclist.old_parent = doclist.current_folder;
|
||||
doclist.listview.settings.add_menu_item_paste(doclist);
|
||||
}
|
||||
else{
|
||||
frappe.throw("Please select file to copy");
|
||||
}
|
||||
})
|
||||
doclist.copy = true;
|
||||
}
|
||||
},
|
||||
add_menu_item_paste:function(doclist){
|
||||
var paste_menu = doclist.page.add_menu_item(__("Paste"), function(){
|
||||
frappe.call({
|
||||
method:"frappe.core.doctype.file.file.move_file",
|
||||
args: {
|
||||
"file_list": doclist.selected_files,
|
||||
"new_parent": doclist.current_folder,
|
||||
"old_parent": doclist.old_parent
|
||||
},
|
||||
callback:function(r){
|
||||
doclist.paste = false;
|
||||
frappe.msgprint(__(r.message));
|
||||
doclist.selected_files = [];
|
||||
$(paste_menu).remove();
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
before_run: function(doclist) {
|
||||
var name_filter = doclist.filter_list.get_filter("file_name");
|
||||
if(name_filter) {
|
||||
doclist.filter_area.removeClass("hide");
|
||||
doclist.breadcrumb.addClass("hide");
|
||||
} else {
|
||||
doclist.filter_area.addClass("hide");
|
||||
doclist.breadcrumb.removeClass("hide");
|
||||
}
|
||||
},
|
||||
refresh: function(doclist) {
|
||||
// set folder before querying
|
||||
var name_filter = doclist.filter_list.get_filter("file_name");
|
||||
|
||||
var folder_filter = doclist.filter_list.get_filter("folder");
|
||||
if(folder_filter) {
|
||||
folder_filter.remove(true);
|
||||
}
|
||||
|
||||
if(name_filter) return;
|
||||
|
||||
var route = frappe.get_route();
|
||||
if(route[2]) {
|
||||
doclist.current_folder = route.slice(2).join("/");
|
||||
doclist.current_folder_name = route.slice(-1)[0];
|
||||
}
|
||||
|
||||
if(!doclist.current_folder) {
|
||||
doclist.current_folder = frappe.boot.home_folder;
|
||||
doclist.current_folder_name = __("Home");
|
||||
}
|
||||
|
||||
doclist.filter_list.add_filter("File", "folder", "=", doclist.current_folder, true);
|
||||
doclist.dirty = true;
|
||||
doclist.fresh = false;
|
||||
|
||||
doclist.page.set_title(doclist.current_folder_name);
|
||||
frappe.utils.set_title(doclist.current_folder_name);
|
||||
},
|
||||
set_primary_action:function(doclist){
|
||||
doclist.page.clear_primary_action();
|
||||
doclist.page.set_primary_action(__("New"), function() {
|
||||
dialog = frappe.ui.get_upload_dialog({
|
||||
"args": {
|
||||
"folder": doclist.current_folder,
|
||||
"from_form": 1
|
||||
},
|
||||
callback: function() {
|
||||
doclist.refresh();
|
||||
}
|
||||
});
|
||||
}, "octicon octicon-plus");
|
||||
},
|
||||
post_render_item: function(list, row, data) {
|
||||
if(data.is_folder) {
|
||||
$(row).find(".list-id").attr("href", "#List/File/" + data.name);
|
||||
}
|
||||
},
|
||||
set_file_route: function(name) {
|
||||
frappe.set_route(["List", "File"].concat(decodeURIComponent(name).split("/")));
|
||||
},
|
||||
post_render: function(doclist) {
|
||||
frappe.call({
|
||||
method: "frappe.core.doctype.file.file.get_breadcrumbs",
|
||||
args: {
|
||||
folder: doclist.current_folder
|
||||
},
|
||||
callback: function(r) {
|
||||
doclist.breadcrumb.empty();
|
||||
if(r.message && r.message.length) {
|
||||
$.each(r.message, function(i, folder) {
|
||||
$('<li><a href="#List/File/'+folder.name+'">'
|
||||
+ folder.file_name+'</a></li>')
|
||||
.appendTo(doclist.breadcrumb);
|
||||
});
|
||||
}
|
||||
$('<li class="active">'+ doclist.current_folder_name+'</li>')
|
||||
.appendTo(doclist.breadcrumb);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
97
frappe/core/doctype/file/test_file.py
Normal file
97
frappe/core/doctype/file/test_file.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
from frappe.utils.file_manager import save_file, get_files_path
|
||||
from frappe import _
|
||||
from frappe.core.doctype.file.file import move_file
|
||||
# test_records = frappe.get_test_records('File')
|
||||
|
||||
class TestFile(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.delete_test_data()
|
||||
self.upload_file()
|
||||
|
||||
def tearDown(self):
|
||||
try:
|
||||
frappe.get_doc("File", {"file_name": "file_copy.txt"}).delete()
|
||||
except frappe.DoesNotExistError:
|
||||
pass
|
||||
|
||||
def delete_test_data(self):
|
||||
for f in frappe.db.sql('''select name, file_name from tabFile where
|
||||
is_home_folder = 0 and is_attachments_folder = 0 order by rgt-lft asc'''):
|
||||
frappe.delete_doc("File", f[0])
|
||||
|
||||
def upload_file(self):
|
||||
self.saved_file = save_file('file_copy.txt', "Testing file copy example.",\
|
||||
"", "", self.get_folder("Test Folder 1", "Home").name)
|
||||
self.saved_filename = get_files_path(self.saved_file.file_name)
|
||||
|
||||
def get_folder(self, folder_name, parent_folder="Home"):
|
||||
return frappe.get_doc({
|
||||
"doctype": "File",
|
||||
"file_name": _(folder_name),
|
||||
"is_folder": 1,
|
||||
"folder": _(parent_folder)
|
||||
}).insert()
|
||||
|
||||
def tests_after_upload(self):
|
||||
self.assertEqual(self.saved_file.folder, _("Home/Test Folder 1"))
|
||||
|
||||
folder_size = frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size")
|
||||
saved_file_size = frappe.db.get_value("File", self.saved_file.name, "file_size")
|
||||
|
||||
self.assertEqual(folder_size, saved_file_size)
|
||||
|
||||
def test_file_copy(self):
|
||||
folder = self.get_folder("Test Folder 2", "Home")
|
||||
|
||||
file = frappe.get_doc("File", {"file_name":"file_copy.txt"})
|
||||
move_file([{"name": file.name}], folder.name, file.folder)
|
||||
file = frappe.get_doc("File", {"file_name":"file_copy.txt"})
|
||||
|
||||
self.assertEqual(_("Home/Test Folder 2"), file.folder)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 2"), "file_size"), file.file_size)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), 0)
|
||||
|
||||
def test_folder_copy(self):
|
||||
folder = self.get_folder("Test Folder 2", "Home")
|
||||
folder = self.get_folder("Test Folder 3", "Home/Test Folder 2")
|
||||
|
||||
self.saved_file = save_file('folder_copy.txt', "Testing folder copy example.", "", "", folder.name)
|
||||
|
||||
move_file([{"name": folder.name}], 'Home/Test Folder 1', folder.folder)
|
||||
|
||||
file = frappe.get_doc("File", {"file_name":"folder_copy.txt"})
|
||||
file_copy_txt = frappe.get_value("File", {"file_name":"file_copy.txt"})
|
||||
if file_copy_txt:
|
||||
frappe.get_doc("File", file_copy_txt).delete()
|
||||
|
||||
self.assertEqual(_("Home/Test Folder 1/Test Folder 3"), file.folder)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), file.file_size)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 2"), "file_size"), 0)
|
||||
|
||||
def test_non_parent_folder(self):
|
||||
d = frappe.get_doc({
|
||||
"doctype": "File",
|
||||
"file_name": _("Test_Folder"),
|
||||
"is_folder": 1
|
||||
})
|
||||
|
||||
self.assertRaises(frappe.ValidationError, d.save)
|
||||
|
||||
def test_on_delete(self):
|
||||
file = frappe.get_doc("File", {"file_name":"file_copy.txt"})
|
||||
file.delete()
|
||||
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), 0)
|
||||
|
||||
folder = self.get_folder("Test Folder 3", "Home/Test Folder 1")
|
||||
self.saved_file = save_file('folder_copy.txt', "Testing folder copy example.", "", "", folder.name)
|
||||
|
||||
folder = frappe.get_doc("File", "Home/Test Folder 1/Test Folder 3")
|
||||
self.assertRaises(frappe.ValidationError, folder.delete)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
File record and its relation to document to which the file is attached.
|
||||
|
||||
This is appened to the form as `docinfo` when a document is loaded.
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
{
|
||||
"allow_import": 1,
|
||||
"autoname": "File.######",
|
||||
"creation": "2012-12-12 11:19:22",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "file_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "File Name",
|
||||
"oldfieldname": "file_name",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "file_url",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "File URL",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "attached_to_doctype",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Attached To DocType",
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "attached_to_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Attached To Name",
|
||||
"permlevel": 0,
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "file_size",
|
||||
"fieldtype": "Int",
|
||||
"in_list_view": 1,
|
||||
"label": "File Size",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "content_hash",
|
||||
"fieldtype": "Data",
|
||||
"label": "Content Hash",
|
||||
"permlevel": 0,
|
||||
"search_index": 1
|
||||
}
|
||||
],
|
||||
"icon": "icon-file",
|
||||
"idx": 1,
|
||||
"in_create": 1,
|
||||
"modified": "2015-02-05 05:11:38.944926",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "File Data",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"read_only": 0
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
"""
|
||||
record of files
|
||||
|
||||
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 FileData(Document):
|
||||
no_feed_on_delete = True
|
||||
|
||||
def before_insert(self):
|
||||
frappe.local.rollback_observers.append(self)
|
||||
|
||||
def validate(self):
|
||||
if not self.flags.ignore_duplicate_entry_error:
|
||||
# check duplicate assignement
|
||||
n_records = frappe.db.sql("""select name from `tabFile Data`
|
||||
where content_hash=%s
|
||||
and name!=%s
|
||||
and attached_to_doctype=%s
|
||||
and attached_to_name=%s""", (self.content_hash, self.name, self.attached_to_doctype,
|
||||
self.attached_to_name))
|
||||
if len(n_records) > 0:
|
||||
self.duplicate_entry = n_records[0][0]
|
||||
frappe.throw(frappe._("Same file has already been attached to the record"), frappe.DuplicateEntryError)
|
||||
|
||||
def on_trash(self):
|
||||
if self.attached_to_name:
|
||||
# check persmission
|
||||
try:
|
||||
if not self.flags.ignore_permissions 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)
|
||||
|
||||
except frappe.DoesNotExistError:
|
||||
pass
|
||||
|
||||
# if file not attached to any other record, delete it
|
||||
if self.file_name and self.content_hash and (not frappe.db.count("File Data",
|
||||
{"content_hash": self.content_hash, "name": ["!=", self.name]})):
|
||||
delete_file_data_content(self)
|
||||
|
||||
def on_rollback(self):
|
||||
self.on_trash()
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("File Data", ["attached_to_doctype", "attached_to_name"])
|
||||
|
||||
|
|
@ -1,31 +1,71 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:module_name",
|
||||
"creation": "2013-01-10 16:34:03",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "module_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Module Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "module_name",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "app_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "App Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-sitemap",
|
||||
"idx": 1,
|
||||
"modified": "2015-02-05 05:11:41.388856",
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:50.398063",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Module Def",
|
||||
|
|
@ -33,23 +73,45 @@
|
|||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Administrator",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"role": "System Manager"
|
||||
"report": 0,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
]
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ import frappe, os
|
|||
from frappe.model.document import Document
|
||||
|
||||
class ModuleDef(Document):
|
||||
__doclink__ = "https://frappe.io/docs/models/core/module_def"
|
||||
def on_update(self):
|
||||
"""If in `developer_mode`, create folder for module and
|
||||
add in `modules.txt` of app if missing."""
|
||||
|
|
@ -39,7 +38,3 @@ class ModuleDef(Document):
|
|||
|
||||
frappe.clear_cache()
|
||||
frappe.setup_module_map()
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
12
frappe/core/doctype/module_def/test_module_def.py
Normal file
12
frappe/core/doctype/module_def/test_module_def.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Module Def')
|
||||
|
||||
class TestModuleDef(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -1,105 +1,254 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:page_name",
|
||||
"creation": "2012-12-20 17:16:49",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "page_html",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Page HTML",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldtype": "Section Break",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "page_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Page Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "page_name",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Title",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "icon",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "icon",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break0",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "module",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Module",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "module",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Module Def",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "standard",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Standard",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "standard",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nYes\nNo",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break0",
|
||||
"fieldtype": "Section Break",
|
||||
"permlevel": 0
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "roles",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Roles",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "roles",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Page Role",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-file",
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-02-05 05:11:41.982758",
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:51.370746",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Page",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Administrator",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
|
|
@ -112,6 +261,7 @@
|
|||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
|
|
@ -122,15 +272,8 @@
|
|||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"apply_user_permissions": 1,
|
||||
"email": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "All"
|
||||
}
|
||||
],
|
||||
"read_only": 0
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.build import html_to_js_template
|
||||
from frappe import conf, _
|
||||
from frappe.desk.form.meta import get_code_files_via_hooks, get_js
|
||||
|
||||
class Page(Document):
|
||||
def autoname(self):
|
||||
|
|
@ -26,13 +28,18 @@ class Page(Document):
|
|||
cnt = 1
|
||||
self.name += '-' + str(cnt)
|
||||
|
||||
def validate(self):
|
||||
if self.is_new() and not getattr(conf,'developer_mode', 0):
|
||||
frappe.throw(_("Not in Developer Mode"))
|
||||
if frappe.session.user!="Administrator":
|
||||
frappe.throw(_("Only Administrator can edit"))
|
||||
|
||||
# export
|
||||
def on_update(self):
|
||||
"""
|
||||
Writes the .txt for this page and if write_content is checked,
|
||||
it will write out a .html file
|
||||
"""
|
||||
from frappe import conf
|
||||
from frappe.core.doctype.doctype.doctype import make_module_and_roles
|
||||
make_module_and_roles(self, "roles")
|
||||
|
||||
|
|
@ -62,20 +69,37 @@ class Page(Document):
|
|||
d[key] = self.get(key)
|
||||
return d
|
||||
|
||||
def is_permitted(self):
|
||||
"""Returns true if Page Role is not set or the user is allowed."""
|
||||
from frappe.utils import has_common
|
||||
|
||||
allowed = [d.role for d in frappe.get_all("Page Role", fields=["role"],
|
||||
filters={"parent": self.name})]
|
||||
|
||||
if not allowed:
|
||||
return True
|
||||
|
||||
roles = frappe.get_roles()
|
||||
|
||||
if has_common(roles, allowed):
|
||||
return True
|
||||
|
||||
def load_assets(self):
|
||||
from frappe.modules import get_module_path, scrub
|
||||
import os
|
||||
|
||||
path = os.path.join(get_module_path(self.module), 'page', scrub(self.name))
|
||||
page_name = scrub(self.name)
|
||||
|
||||
path = os.path.join(get_module_path(self.module), 'page', page_name)
|
||||
|
||||
# script
|
||||
fpath = os.path.join(path, scrub(self.name) + '.js')
|
||||
fpath = os.path.join(path, page_name + '.js')
|
||||
if os.path.exists(fpath):
|
||||
with open(fpath, 'r') as f:
|
||||
self.script = unicode(f.read(), "utf-8")
|
||||
|
||||
# css
|
||||
fpath = os.path.join(path, scrub(self.name) + '.css')
|
||||
fpath = os.path.join(path, page_name + '.css')
|
||||
if os.path.exists(fpath):
|
||||
with open(fpath, 'r') as f:
|
||||
self.style = unicode(f.read(), "utf-8")
|
||||
|
|
@ -85,8 +109,27 @@ class Page(Document):
|
|||
if fname.endswith(".html"):
|
||||
with open(os.path.join(path, fname), 'r') as f:
|
||||
template = unicode(f.read(), "utf-8")
|
||||
if "<!-- jinja -->" in template:
|
||||
context = {}
|
||||
try:
|
||||
context = frappe.get_attr("{app}.{module}.page.{page}.{page}.get_context".format(
|
||||
app = frappe.local.module_app[scrub(self.module)],
|
||||
module = scrub(self.module),
|
||||
page = page_name
|
||||
))(context)
|
||||
except (AttributeError, ImportError):
|
||||
pass
|
||||
|
||||
template = frappe.render_template(template, context)
|
||||
self.script = html_to_js_template(fname, template) + self.script
|
||||
|
||||
if frappe.lang != 'en':
|
||||
from frappe.translate import get_lang_js
|
||||
self.script += get_lang_js("page", self.name)
|
||||
|
||||
for path in get_code_files_via_hooks("page_js", self.name):
|
||||
js = get_js(path)
|
||||
if js:
|
||||
self.script += "\n\n" + js
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,34 +1,54 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"creation": "2013-02-22 01:27:34",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "role",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Role",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "role",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Role",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-02-19 01:07:00.897854",
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:51.420589",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Page Role",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"read_only": 0
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
|
|
@ -1,33 +1,75 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "PATCHLOG.#####",
|
||||
"creation": "2013-01-17 11:36:45.000000",
|
||||
"creation": "2013-01-17 11:36:45",
|
||||
"custom": 0,
|
||||
"description": "List of patches executed",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "System",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "patch",
|
||||
"fieldtype": "Data",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Patch",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-cog",
|
||||
"idx": 1,
|
||||
"modified": "2013-12-20 19:24:15.000000",
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:51.487656",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Patch Log",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Administrator"
|
||||
"role": "Administrator",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
]
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
12
frappe/core/doctype/patch_log/test_patch_log.py
Normal file
12
frappe/core/doctype/patch_log/test_patch_log.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Patch Log')
|
||||
|
||||
class TestPatchLog(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -1,173 +1,408 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:report_name",
|
||||
"creation": "2013-03-09 15:45:57",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "System",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "report_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Report Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "ref_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Ref DocType",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "is_standard",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Is Standard",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "No\nYes",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "module",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Module",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Module Def",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "add_total_row",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Add Total Row",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"read_only": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "report_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Report Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Report Builder\nQuery Report\nScript Report",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Disabled",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"read_only": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"depends_on": "eval:[\"Query Report\", \"Script Report\"].indexOf(doc.report_type)!==-1",
|
||||
"fieldname": "apply_user_permissions",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Apply User Permissions",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"read_only": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:doc.report_type==\"Query Report\"",
|
||||
"fieldname": "query",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Query",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"read_only": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "",
|
||||
"description": "JavaScript Format: frappe.query_reports['REPORTNAME'] = {}",
|
||||
"fieldname": "javascript",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Javascript",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:doc.report_type==\"Report Builder\"",
|
||||
"fieldname": "json",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "JSON",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-table",
|
||||
"idx": 1,
|
||||
"modified": "2015-02-05 05:11:44.753200",
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:55.357855",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Report",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Administrator",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Report Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"apply_user_permissions": 1,
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"submit": 0
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
|
|
@ -1,29 +1,49 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:role_name",
|
||||
"creation": "2013-01-08 15:50:01",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "role_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Role Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "role_name",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-bookmark",
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-02-05 05:11:44.831475",
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:55.424835",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Role",
|
||||
|
|
@ -31,36 +51,65 @@
|
|||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Administrator",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"apply_user_permissions": 1,
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"role": "All"
|
||||
"report": 0,
|
||||
"role": "All",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
],
|
||||
"read_only": 0
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
|
|
@ -7,4 +7,9 @@ import frappe
|
|||
from frappe.model.document import Document
|
||||
|
||||
class Role(Document):
|
||||
pass
|
||||
def after_insert(self):
|
||||
# Add role to Administrator
|
||||
if frappe.flags.in_install != "frappe":
|
||||
user = frappe.get_doc("User", "Administrator")
|
||||
user.flags.ignore_permissions = True
|
||||
user.add_roles(self.name)
|
||||
|
|
|
|||
|
|
@ -1,55 +1,121 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "SCHLOG.#####",
|
||||
"creation": "2013-01-16 13:09:40",
|
||||
"custom": 0,
|
||||
"description": "Log of Scheduler Errors",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "System",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "0",
|
||||
"fieldname": "seen",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Seen",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "method",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Method",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "error",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Error",
|
||||
"permlevel": 0
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-warning-sign",
|
||||
"idx": 1,
|
||||
"modified": "2015-05-28 02:49:12.819934",
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:29:57.440897",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Scheduler Log",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
]
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
|
|
@ -16,4 +16,4 @@ class SchedulerLog(Document):
|
|||
|
||||
def set_old_logs_as_seen():
|
||||
frappe.db.sql("""update `tabScheduler Log` set seen=1
|
||||
where ifnull(seen, 0)=0 and datediff(curdate(), creation) > 7""")
|
||||
where seen=0 and datediff(curdate(), creation) > 7""")
|
||||
|
|
|
|||
|
|
@ -1,152 +1,461 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"creation": "2014-04-17 16:53:52.640856",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "System",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "localization",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "",
|
||||
"permlevel": 0
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "language",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Language",
|
||||
"no_copy": 0,
|
||||
"options": "Loading...",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "time_zone",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Time Zone",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "date_and_number_format",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "",
|
||||
"permlevel": 0
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "date_format",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Date Format",
|
||||
"no_copy": 0,
|
||||
"options": "yyyy-mm-dd\ndd-mm-yyyy\ndd/mm/yyyy\ndd.mm.yyyy\nmm/dd/yyyy\nmm-dd-yyyy",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "number_format",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Number Format",
|
||||
"no_copy": 0,
|
||||
"options": "#,###.##\n#.###,##\n# ###.##\n# ###,##\n#'###.##\n#, ###.##\n#,##,###.##\n#,###.###\n#.###\n#,###",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "float_precision",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Float Precision",
|
||||
"no_copy": 0,
|
||||
"options": "\n2\n3\n4\n5\n6",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "security",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Security",
|
||||
"permlevel": 0
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "06:00",
|
||||
"description": "Session Expiry in Hours e.g. 06:00",
|
||||
"fieldname": "session_expiry",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Session Expiry",
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "720:00",
|
||||
"description": "In Hours",
|
||||
"fieldname": "session_expiry_mobile",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Session Expiry Mobile",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_13",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Run scheduled jobs only if checked",
|
||||
"fieldname": "enable_scheduler",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Enable Scheduled Jobs",
|
||||
"permlevel": 0
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "scheduler_last_event",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Scheduler Last Event",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"report_hide": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 1,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "eg. If Apply User Permissions is checked for Report DocType but no User Permissions are defined for Report for a User, then all Reports are shown to that User",
|
||||
"fieldname": "ignore_user_permissions_if_missing",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Ignore User Permissions If Missing",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "email",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "EMail",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Your organization name and address for the email footer.",
|
||||
"fieldname": "email_footer_address",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Email Footer Address",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_18",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "disable_standard_email_footer",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Disable Standard Email Footer",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-cog",
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"modified": "2015-05-21 07:15:55.682132",
|
||||
"istable": 0,
|
||||
"modified": "2015-09-07 11:36:15.465900",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "System Settings",
|
||||
|
|
@ -154,12 +463,26 @@
|
|||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
]
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
|
|
@ -35,10 +35,6 @@ cur_frm.cscript.before_load = function(doc, dt, dn, callback) {
|
|||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.user_image = function(doc) {
|
||||
refresh_field("user_image_show");
|
||||
}
|
||||
|
||||
cur_frm.cscript.refresh = function(doc) {
|
||||
if(doc.name===user && !doc.__unsaved && frappe.languages && (doc.language || frappe.boot.user.language)
|
||||
&& doc.language !== frappe.boot.user.language) {
|
||||
|
|
@ -76,7 +72,7 @@ cur_frm.cscript.refresh = function(doc) {
|
|||
cur_frm.cscript.enabled = function(doc) {
|
||||
if(!doc.__islocal && has_common(user_roles, ["Administrator", "System Manager"])) {
|
||||
cur_frm.toggle_display(['sb1', 'sb3', 'modules_access'], doc.enabled);
|
||||
cur_frm.toggle_enable('*', doc.enabled);
|
||||
// cur_frm.toggle_enable('*', doc.enabled);
|
||||
cur_frm.set_df_property('enabled', 'read_only', 0);
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -10,6 +10,7 @@ from frappe.desk.notifications import clear_notifications
|
|||
from frappe.utils.user import get_system_managers
|
||||
import frappe.permissions
|
||||
import frappe.share
|
||||
import re
|
||||
|
||||
STANDARD_USERS = ("Guest", "Administrator")
|
||||
|
||||
|
|
@ -38,6 +39,8 @@ class User(Document):
|
|||
self.update_gravatar()
|
||||
self.ensure_unique_roles()
|
||||
self.remove_all_roles_for_guest()
|
||||
self.validate_username()
|
||||
|
||||
if self.language == "Loading...":
|
||||
self.language = None
|
||||
|
||||
|
|
@ -85,7 +88,7 @@ class User(Document):
|
|||
self.share_with_self()
|
||||
clear_notifications(user=self.name)
|
||||
frappe.clear_cache(user=self.name)
|
||||
self.send_password_notifcation(self.__new_password)
|
||||
self.send_password_notification(self.__new_password)
|
||||
|
||||
def share_with_self(self):
|
||||
if self.user_type=="System User":
|
||||
|
|
@ -103,7 +106,7 @@ class User(Document):
|
|||
else:
|
||||
frappe.throw(_("Sorry! Sharing with Website User is prohibited."))
|
||||
|
||||
def send_password_notifcation(self, new_password):
|
||||
def send_password_notification(self, new_password):
|
||||
try:
|
||||
if self.in_insert:
|
||||
if self.name not in STANDARD_USERS:
|
||||
|
|
@ -141,7 +144,7 @@ class User(Document):
|
|||
return frappe.db.sql("""select distinct user.name from tabUserRole user_role, tabUser user
|
||||
where user_role.role='System Manager'
|
||||
and user.docstatus<2
|
||||
and ifnull(user.enabled,0)=1
|
||||
and user.enabled=1
|
||||
and user_role.parent = user.name
|
||||
and user_role.parent not in ('Administrator', %s) limit 1""", (self.name,))
|
||||
|
||||
|
|
@ -164,7 +167,7 @@ class User(Document):
|
|||
link = get_url("/update-password?key=" + key)
|
||||
|
||||
self.send_login_mail(_("Verify Your Account"), "templates/emails/new_user.html",
|
||||
{"link": link})
|
||||
{"link": link, "site_url": get_url()})
|
||||
|
||||
def send_login_mail(self, subject, template, add_args):
|
||||
"""send mail with login details"""
|
||||
|
|
@ -296,6 +299,48 @@ class User(Document):
|
|||
else:
|
||||
exists.append(d.role)
|
||||
|
||||
def validate_username(self):
|
||||
if not self.username and self.is_new() and self.first_name:
|
||||
self.username = frappe.scrub(self.first_name)
|
||||
|
||||
if not self.username:
|
||||
return
|
||||
|
||||
# strip space and @
|
||||
self.username = self.username.strip(" @")
|
||||
|
||||
if self.username_exists():
|
||||
frappe.msgprint(_("Username {0} already exists").format(self.username))
|
||||
self.suggest_username()
|
||||
self.username = ""
|
||||
|
||||
# should be made up of characters, numbers and underscore only
|
||||
if self.username and not re.match(r"^[\w]+$", self.username):
|
||||
frappe.msgprint(_("Username should not contain any special characters other than letters, numbers and underscore"))
|
||||
self.username = ""
|
||||
|
||||
def suggest_username(self):
|
||||
def _check_suggestion(suggestion):
|
||||
if self.username != suggestion and not self.username_exists(suggestion):
|
||||
return suggestion
|
||||
|
||||
return None
|
||||
|
||||
# @firstname
|
||||
username = _check_suggestion(frappe.scrub(self.first_name))
|
||||
|
||||
if not username:
|
||||
# @firstname_last_name
|
||||
username = _check_suggestion(frappe.scrub("{0} {1}".format(self.first_name, self.last_name or "")))
|
||||
|
||||
if username:
|
||||
frappe.msgprint(_("Suggested Username: {0}").format(username))
|
||||
|
||||
return username
|
||||
|
||||
def username_exists(self, username=None):
|
||||
return frappe.db.get_value("User", {"username": username or self.username, "name": ("!=", self.name)})
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_languages():
|
||||
from frappe.translate import get_lang_dict
|
||||
|
|
@ -397,7 +442,7 @@ def user_query(doctype, txt, searchfield, start, page_len, filters):
|
|||
txt = "%{}%".format(txt)
|
||||
return frappe.db.sql("""select name, concat_ws(' ', first_name, middle_name, last_name)
|
||||
from `tabUser`
|
||||
where ifnull(enabled, 0)=1
|
||||
where enabled=1
|
||||
and docstatus < 2
|
||||
and name not in ({standard_users})
|
||||
and user_type != 'Website User'
|
||||
|
|
@ -437,7 +482,7 @@ def get_active_users():
|
|||
return frappe.db.sql("""select count(*) from `tabUser`
|
||||
where enabled = 1 and user_type != 'Website User'
|
||||
and name not in ({})
|
||||
and hour(timediff(now(), last_login)) < 72""".format(", ".join(["%s"]*len(STANDARD_USERS))), STANDARD_USERS)[0][0]
|
||||
and hour(timediff(now(), last_active)) < 72""".format(", ".join(["%s"]*len(STANDARD_USERS))), STANDARD_USERS)[0][0]
|
||||
|
||||
def get_website_users():
|
||||
"""Returns total no. of website users"""
|
||||
|
|
@ -448,7 +493,7 @@ def get_active_website_users():
|
|||
"""Returns No. of website users who logged in, in the last 3 days"""
|
||||
return frappe.db.sql("""select count(*) from `tabUser`
|
||||
where enabled = 1 and user_type = 'Website User'
|
||||
and hour(timediff(now(), last_login)) < 72""")[0][0]
|
||||
and hour(timediff(now(), last_active)) < 72""")[0][0]
|
||||
|
||||
def get_permission_query_conditions(user):
|
||||
if user=="Administrator":
|
||||
|
|
@ -490,3 +535,7 @@ def notifify_admin_access_to_system_manager(login_manager=None):
|
|||
frappe.sendmail(recipients=get_system_managers(), subject=_("Administrator Logged In"),
|
||||
message=message, bulk=True)
|
||||
|
||||
def extract_mentions(txt):
|
||||
"""Find all instances of @username in the string.
|
||||
The mentions will be separated by non-word characters or may appear at the start of the string"""
|
||||
return re.findall(r'(?:[^\w]|^)@([\w]*)', txt)
|
||||
|
|
|
|||
|
|
@ -1,36 +1,56 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"creation": "2013-02-06 11:30:13",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "role",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Role",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "role",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Role",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "200px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "200px"
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-02-19 01:07:02.561834",
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:30:00.004591",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "UserRole",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"read_only": 0
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
|
|
@ -1,50 +1,119 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "_VER.######",
|
||||
"creation": "2014-02-20 17:22:37",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Master",
|
||||
"document_type": "Setup",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "ref_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Ref DocType",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "docname",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Docname",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "doclist_json",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Doclist JSON",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-copy",
|
||||
"idx": 1,
|
||||
"modified": "2014-08-05 01:23:37.541856",
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-11-16 06:30:00.036254",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Version",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager"
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
]
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue