Ver a proveniência

[docs]

version-14
Rushabh Mehta há 10 anos
ascendente
cometimento
b0a43c28d3
8 ficheiros alterados com 267 adições e 44 eliminações
  1. +246
    -23
      frappe/__init__.py
  2. +2
    -1
      frappe/cli.py
  3. +2
    -1
      frappe/core/page/permission_manager/permission_manager.py
  4. +0
    -15
      frappe/model/__init__.py
  5. +1
    -1
      frappe/modules/patch_handler.py
  6. +2
    -2
      frappe/patches.txt
  7. +5
    -1
      frappe/translate.py
  8. +9
    -0
      frappe/website/permissions.py

+ 246
- 23
frappe/__init__.py Ver ficheiro

@@ -37,7 +37,7 @@ class _dict(dict):
return _dict(dict(self).copy())

def _(msg):
"""translate object in current lang, if exists"""
"""Returns translated string in current lang, if exists."""
if local.lang == "en":
return msg

@@ -45,12 +45,17 @@ def _(msg):
return get_full_dict(local.lang).get(msg, msg)

def get_lang_dict(fortype, name=None):
"""Returns the translated language dict for the given type and name.

:param fortype: must be one of `doctype`, `page`, `report`, `include`, `jsfile`, `boot`
:param name: name of the document for which assets are to be returned."""
if local.lang=="en":
return {}
from frappe.translate import get_dict
return get_dict(fortype, name)

def set_user_lang(user, user_language=None):
"""Guess and set user language for the session. `frappe.local.lang`"""
from frappe.translate import get_user_lang
local.lang = get_user_lang(user)

@@ -72,6 +77,7 @@ message_log = local("message_log")
lang = local("lang")

def init(site, sites_path=None):
"""Initialize frappe for the current site. Reset thread locals `frappe.local`"""
if getattr(local, "initialised", None):
return

@@ -111,6 +117,10 @@ def init(site, sites_path=None):
local.initialised = True

def connect(site=None, db_name=None):
"""Connect to site database instance.

:param site: If site is given, calls `frappe.init`.
:param db_name: Optional. Will use from `site_config.json`."""
from database import Database
if site:
init(site)
@@ -120,6 +130,8 @@ def connect(site=None, db_name=None):
set_user("Administrator")

def get_site_config(sites_path=None, site_path=None):
"""Returns `site_config.json` combined with `sites/common_site_config.json`.
`site_config` is a set of site wide settings like database name, password, email etc."""
config = {}

sites_path = sites_path or getattr(local, "sites_path", None)
@@ -138,7 +150,7 @@ def get_site_config(sites_path=None, site_path=None):
return _dict(config)

def destroy():
"""closes connection and releases werkzeug local"""
"""Closes connection and releases werkzeug local."""
if db:
db.close()

@@ -148,6 +160,7 @@ _memc = None

# memcache
def cache():
"""Returns memcache connection."""
global _memc
if not _memc:
from frappe.memc import MClient
@@ -155,10 +168,14 @@ def cache():
return _memc

def get_traceback():
"""Returns error traceback."""
import utils
return utils.get_traceback()

def errprint(msg):
"""Log error. This is sent back as `exc` in response.

:param msg: Message."""
from utils import cstr
if not request or (not "cmd" in local.form_dict):
print cstr(msg)
@@ -166,6 +183,9 @@ def errprint(msg):
error_log.append(cstr(msg))

def log(msg):
"""Add to `debug_log`.

:param msg: Message."""
if not request:
if conf.get("logging") or False:
print repr(msg)
@@ -174,6 +194,15 @@ def log(msg):
debug_log.append(cstr(msg))

def msgprint(msg, small=0, raise_exception=0, as_table=False):
"""Print a message to the user (via HTTP response).
Messages are sent in the `__server_messages` property in the
response JSON and shown in a pop-up / modal.

:param msg: Message.
:param small: [optional] Show as a floating message in the footer.
:param raise_exception: [optional] Raise given exception and show message.
:param as_table: [optional] If `msg` is a list of lists, render as HTML table.
"""
def _raise_exception():
if raise_exception:
if flags.rollback_on_exception:
@@ -199,9 +228,17 @@ def msgprint(msg, small=0, raise_exception=0, as_table=False):
_raise_exception()

def throw(msg, exc=ValidationError):
"""Throw execption and show message (`msgprint`).

:param msg: Message.
:param exc: Exception class. Default `frappe.ValidationError`"""
msgprint(msg, raise_exception=exc)

def create_folder(path, with_init=False):
"""Create a folder in the given path and add an `__init__.py` file (optional).

:param path: Folder path.
:param with_init: Create `__init__.py` in the new folder."""
from frappe.utils import touch_file
if not os.path.exists(path):
os.makedirs(path)
@@ -210,6 +247,9 @@ def create_folder(path, with_init=False):
touch_file(os.path.join(path, "__init__.py"))

def set_user(username):
"""Set current user.

:param username: **User** name to set as current user."""
from frappe.utils.user import User
local.session.user = username
local.session.sid = username
@@ -221,11 +261,29 @@ def set_user(username):
local.role_permissions = {}

def get_request_header(key, default=None):
"""Return HTTP request header.

:param key: HTTP header key.
:param default: Default value."""
return request.headers.get(key, default)

def sendmail(recipients=(), sender="", subject="No Subject", message="No Message",
as_markdown=False, bulk=False, ref_doctype=None, ref_docname=None,
add_unsubscribe_link=False, attachments=None, content=None, doctype=None, name=None):
"""Send email using user's default **Email Account** or global default **Email Account**.


:param recipients: List of recipients.
:param sender: Email sender. Default is current user.
:param subject: Email Subject.
: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 ref_doctype: (or `doctype`) Append as communication to this DocType.
:param ref_docname: (or `name`) Append as communication to this document name.
:param add_unsubscribe_link: Allow user to unsubscribe from these emails.
:param attachments: List of attachments.
"""

if bulk:
import frappe.email.bulk
@@ -247,12 +305,16 @@ whitelisted = []
guest_methods = []
def whitelist(allow_guest=False):
"""
decorator for whitelisting a function
Decorator for whitelisting a function and making it accessible via HTTP.
Standard request will be `/api/method/[path.to.method]`

Note: if the function is allowed to be accessed by a guest user,
it must explicitly be marked as allow_guest=True
:param allow_guest: Allow non logged-in user to access this method.

for specific roles, set allow_roles = ['Administrator'] etc.
Use as:

@frappe.whitelist()
def myfunc(param1, param2):
pass
"""
def innerfn(fn):
global whitelisted, guest_methods
@@ -266,6 +328,9 @@ def whitelist(allow_guest=False):
return innerfn

def only_for(roles):
"""Raise `frappe.PermissionError` if the user does not have any of the given **Roles**.

:param roles: List of roles to check."""
if not isinstance(roles, (tuple, list)):
roles = (roles,)
roles = set(roles)
@@ -274,7 +339,10 @@ def only_for(roles):
raise PermissionError

def clear_cache(user=None, doctype=None):
"""clear cache"""
"""Clear **User**, **DocType** or global cache.

:param user: If user is given, only user cache is cleared.
:param doctype: If doctype is given, only DocType cache is cleared."""
import frappe.sessions
if doctype:
import frappe.model.meta
@@ -294,12 +362,14 @@ def clear_cache(user=None, doctype=None):
frappe.local.role_permissions = {}

def get_roles(username=None):
"""Returns roles of current user."""
if not local.session:
return ["Guest"]

return get_user(username).get_roles()

def get_user(username):
"""Returns `frappe.utils.user.User` instance of given user."""
from frappe.utils.user import User
if not username or username == local.session.user:
return local.user
@@ -307,101 +377,153 @@ def get_user(username):
return User(username)

def has_permission(doctype, ptype="read", doc=None, user=None):
"""Raises `frappe.PermissionError` if not permitted.

:param doctype: DocType for which permission is to be check.
:param ptype: Permission type (`read`, `write`, `create`, `submit`, `cancel`, `amend`). Default: `read`.
: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, user=user)

def is_table(doctype):
"""Returns True if `istable` property (indicating child Table) is set for given DocType."""
tables = cache().get_value("is_table")
if tables==None:
tables = db.sql_list("select name from tabDocType where ifnull(istable,0)=1")
cache().set_value("is_table", tables)
return doctype in tables

def clear_perms(doctype):
db.sql("""delete from tabDocPerm where parent=%s""", doctype)

def reset_perms(doctype):
from frappe.core.doctype.notification_count.notification_count import delete_notification_count_for
delete_notification_count_for(doctype)

clear_perms(doctype)
reload_doc(db.get_value("DocType", doctype, "module"),
"DocType", doctype, force=True)

def generate_hash(txt=None):
"""Generates random hash for session id"""
"""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()

def reset_metadata_version():
"""Reset `metadata_version` (Client (Javascript) build ID) hash."""
v = generate_hash()
cache().set_value("metadata_version", v)
return v

def new_doc(doctype, parent_doc=None, parentfield=None):
"""Returns a new document of the given DocType with defaults set.

:param doctype: DocType of the new document.
:param parent_doc: [optional] add to parent document.
:param parentfield: [optional] add against this `parentfield`."""
from frappe.model.create_new import get_new_doc
return get_new_doc(doctype, parent_doc, parentfield)

def set_value(doctype, docname, fieldname, value):
"""Set document value. Calls `frappe.client.set_value`"""
import frappe.client
return frappe.client.set_value(doctype, docname, fieldname, value)

def get_doc(arg1, arg2=None):
"""Return a `frappe.model.document.Document` object of the given type and name.

:param arg1: DocType name as string **or** document JSON.
:param arg2: [optional] Document name as string.

Examples:

# insert a new document
todo = frappe.get_doc({"doctype":"ToDo", "description": "test"})
tood.insert()

# open an existing document
todo = frappe.get_doc("ToDo", "TD0001")

"""
import frappe.model.document
return frappe.model.document.get_doc(arg1, arg2)

def get_meta(doctype, cached=True):
"""Get `frappe.model.meta.Meta` instance of given doctype name."""
import frappe.model.meta
return frappe.model.meta.get_meta(doctype, cached=cached)

def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False, ignore_permissions=False):
"""Delete a document. Calls `frappe.model.delete_doc.delete_doc`.

:param doctype: DocType of document to be delete.
:param name: Name of document to be delete.
:param force: Allow even if document is linked. Warning: This may lead to data integrity errors.
:param ignore_doctypes: Ignore if child table is one of these.
:param for_reload: Call `before_reload` trigger before deleting.
:param ignore_permissions: Ignore user permissions."""
import frappe.model.delete_doc
frappe.model.delete_doc.delete_doc(doctype, name, force, ignore_doctypes, for_reload, ignore_permissions)

def delete_doc_if_exists(doctype, name):
"""Delete document if exists."""
if db.exists(doctype, name):
delete_doc(doctype, name)

def reload_doc(module, dt=None, dn=None, force=False):
"""Reload Document from model (`[module]/[doctype]/[name]/[name].json`) files.

:param module: Module name.
:param dt: DocType name.
:param dn: Document name.
:param force: Reload even if `modified` timestamp matches.
"""
import frappe.modules
return frappe.modules.reload_doc(module, dt, dn, force=force)

def rename_doc(doctype, old, new, debug=0, force=False, merge=False, ignore_permissions=False):
"""Rename a document. Calls `frappe.model.rename_doc.rename_doc`"""
from frappe.model.rename_doc import rename_doc
return rename_doc(doctype, old, new, force=force, merge=merge, ignore_permissions=ignore_permissions)

def insert(doclist):
import frappe.model
return frappe.model.insert(doclist)

def get_module(modulename):
"""Returns a module object for given Python module name using `importlib.import_module`."""
return importlib.import_module(modulename)

def scrub(txt):
"""Returns sluggified string. e.g. `Sales Order` becomes `sales_order`."""
return txt.replace(' ','_').replace('-', '_').lower()

def unscrub(txt):
"""Returns titlified string. e.g. `sales_order` becomes `Sales Order`."""
return txt.replace('_',' ').replace('-', ' ').title()

def get_module_path(module, *joins):
"""Get the path of the given module name.

:param module: Module name.
:param *joins: Join additional path elements using `os.path.join`."""
module = scrub(module)
return get_pymodule_path(local.module_app[module] + "." + module, *joins)

def get_app_path(app_name, *joins):
"""Return path of given app.

:param app: App name.
:param *joins: Join additional path elements using `os.path.join`."""
return get_pymodule_path(app_name, *joins)

def get_site_path(*joins):
"""Return path of current site.

:param *joins: Join additional path elements using `os.path.join`."""
return os.path.join(local.site_path, *joins)

def get_pymodule_path(modulename, *joins):
"""Return path of given Python module name.

:param modulename: Python module name.
:param *joins: Join additional path elements using `os.path.join`."""
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):
"""Get list of modules for given all via `app/modules.txt`."""
return get_file_items(os.path.join(os.path.dirname(get_module(app_name).__file__), "modules.txt"))

def get_all_apps(with_frappe=False, with_internal_apps=True, sites_path=None):
"""Get list of all apps via `sites/apps.txt`."""
if not sites_path:
sites_path = local.sites_path

@@ -413,6 +535,7 @@ def get_all_apps(with_frappe=False, with_internal_apps=True, sites_path=None):
return apps

def get_installed_apps():
"""Get list of installed apps in current site."""
if getattr(flags, "in_install_db", True):
return []
installed = json.loads(db.get_global("installed_apps") or "[]")
@@ -420,6 +543,16 @@ def get_installed_apps():

@whitelist()
def get_versions():
"""Get versions of all installed apps.

Example:

{
"frappe": {
"title": "Frappe Framework",
"version": "5.0.0"
}
}"""
versions = {}
for app in get_installed_apps():
versions[app] = {
@@ -434,6 +567,11 @@ def get_versions():
return versions

def get_hooks(hook=None, default=None, app_name=None):
"""Get hooks via `app/hooks.py`

:param hook: Name of the hook. Will gather all hooks for this name and return as a list.
:param default: Default if no hook found.
:param app_name: Filter by app."""
def load_app_hooks(app_name=None):
hooks = {}
for app in [app_name] if app_name else get_installed_apps():
@@ -476,6 +614,7 @@ def get_hooks(hook=None, default=None, app_name=None):
return hooks

def setup_module_map():
"""Rebuild map of all modules (internal)."""
_cache = cache()

if conf.db_name:
@@ -497,6 +636,7 @@ def setup_module_map():
_cache.set_value("module_app", local.module_app)

def get_file_items(path, raise_not_found=False, ignore_empty_lines=True):
"""Returns items from text file as a list. Ignores empty lines."""
content = read_file(path, raise_not_found=raise_not_found)
if content:
# \ufeff is no-width-break, \u200b is no-width-space
@@ -507,10 +647,12 @@ def get_file_items(path, raise_not_found=False, ignore_empty_lines=True):
return []

def get_file_json(path):
"""Read a file and return parsed JSON object."""
with open(path, 'r') as f:
return json.load(f)

def read_file(path, raise_not_found=False):
"""Open a file and return its content as Unicode."""
from frappe.utils import cstr
if os.path.exists(path):
with open(path, "r") as f:
@@ -521,11 +663,13 @@ def read_file(path, raise_not_found=False):
return None

def get_attr(method_string):
"""Get python method object from its name."""
modulename = '.'.join(method_string.split('.')[:-1])
methodname = method_string.split('.')[-1]
return getattr(get_module(modulename), methodname)

def call(fn, *args, **kwargs):
"""Call a function and match arguments."""
if hasattr(fn, 'fnargs'):
fnargs = fn.fnargs
else:
@@ -538,6 +682,7 @@ def call(fn, *args, **kwargs):
return fn(*args, **newargs)

def make_property_setter(args, ignore_validate=False, validate_fields_for_doctype=True):
"""Create a new **Property Setter** (for overriding DocType and DocField properties)."""
args = _dict(args)
ps = get_doc({
'doctype': "Property Setter",
@@ -554,6 +699,7 @@ def make_property_setter(args, ignore_validate=False, validate_fields_for_doctyp
ps.insert()

def import_doc(path, ignore_links=False, ignore_insert=False, insert=False):
"""Import a file using Data Import Tool."""
from frappe.core.page.data_import_tool import data_import_tool
data_import_tool.import_doc(path, ignore_links=ignore_links, ignore_insert=ignore_insert, insert=insert)

@@ -593,10 +739,31 @@ def copy_doc(doc, ignore_no_copy=True):
return newdoc

def compare(val1, condition, val2):
"""Compare two values using `frappe.utils.compare`

`condition` could be:
- "^"
- "in"
- "not in"
- "="
- "!="
- ">"
- "<"
- ">="
- "<="
- "not None"
- "None"
"""
import frappe.utils
return frappe.utils.compare(val1, condition, val2)

def respond_as_web_page(title, html, success=None, http_status_code=None):
"""Send response as a web page with a message rather than JSON. Used to show permission errors etc.

:param title: Page title and heading.
:param message: Message to be shown.
:param success: Alert message.
:param http_status_code: HTTP status code."""
local.message_title = title
local.message = html
local.message_success = success
@@ -606,18 +773,62 @@ def respond_as_web_page(title, html, success=None, http_status_code=None):
local.response['http_status_code'] = http_status_code

def build_match_conditions(doctype, as_condition=True):
"""Return match (User permissions) for given doctype as list or SQL."""
import frappe.desk.reportview
return frappe.desk.reportview.build_match_conditions(doctype, as_condition)

def get_list(doctype, *args, **kwargs):
"""List database query via `frappe.model.db_query`. Will also check for permissions.

:param doctype: DocType on which query is to be made.
:param fields: List of fields or `*`.
:param filters: List of filters (see example).
:param order_by: Order By e.g. `modified desc`.
:param limit_page_start: Start results at record #. Default 0.
:param limit_poge_length: No of records in the page. Default 20.

Example usage:

# simple dict filter
frappe.get_list("ToDo", fields=["name", "description"], filters = {"owner":"test@example.com"})

# filter as a list of lists
frappe.get_list("ToDo", fields="*", filters = [["modified", ">", "2014-01-01"]])

# filter as a list of dicts
frappe.get_list("ToDo", fields="*", filters = {"description": ("like", "test%")})
"""
import frappe.model.db_query
return frappe.model.db_query.DatabaseQuery(doctype).execute(None, *args, **kwargs)

def get_all(doctype, *args, **kwargs):
"""List database query via `frappe.model.db_query`. Will **not** check for conditions.
Parameters are same as `frappe.get_list`

:param doctype: DocType on which query is to be made.
:param fields: List of fields or `*`. Default is: `["name"]`.
:param filters: List of filters (see example).
:param order_by: Order By e.g. `modified desc`.
:param limit_page_start: Start results at record #. Default 0.
:param limit_poge_length: No of records in the page. Default 20.

Example usage:

# simple dict filter
frappe.get_all("ToDo", fields=["name", "description"], filters = {"owner":"test@example.com"})

# filter as a list of lists
frappe.get_all("ToDo", fields=["*"], filters = [["modified", ">", "2014-01-01"]])

# filter as a list of dicts
frappe.get_all("ToDo", fields=["*"], filters = {"description": ("like", "test%")})
"""
kwargs["ignore_permissions"] = True
return get_list(doctype, *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."""
get_doc({
"doctype": "Version",
"ref_doctype": doc.doctype,
@@ -626,6 +837,7 @@ def add_version(doc):
}).insert(ignore_permissions=True)

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
path = os.path.join(get_module_path(get_doctype_module(doctype)), "doctype", scrub(doctype), "test_records.json")
if os.path.exists(path):
@@ -635,10 +847,21 @@ def get_test_records(doctype):
return []

def format_value(value, df, doc=None, currency=None):
"""Format value with given field properties.

:param value: Value to be formatted.
:param df: DocField object with properties `fieldtype`, `options` etc."""
import frappe.utils.formatters
return frappe.utils.formatters.format_value(value, df, doc, currency=currency)

def get_print_format(doctype, name, print_format=None, style=None, as_pdf=False):
"""Get Print Format for given document.

:param doctype: DocType of document.
:param name: Name of document.
:param print_format: Print Format name. Default 'Standard',
:param style: Print Format style.
:param as_pdf: Return as PDF. Default False."""
from frappe.website.render import build_page
local.form_dict.doctype = doctype
local.form_dict.name = name


+ 2
- 1
frappe/cli.py Ver ficheiro

@@ -598,11 +598,12 @@ def sync_statics(force=False):

@cmd
def reset_perms():
from frappe.permissions import reset_perms
frappe.connect()
for d in frappe.db.sql_list("""select name from `tabDocType`
where ifnull(istable, 0)=0 and ifnull(custom, 0)=0"""):
frappe.clear_cache(doctype=d)
frappe.reset_perms(d)
reset_perms(d)
frappe.destroy()

@cmd


+ 2
- 1
frappe/core/page/permission_manager/permission_manager.py Ver ficheiro

@@ -7,6 +7,7 @@ import frappe.defaults
from frappe.modules.import_file import get_file_path, read_doc_from_file
from frappe.translate import send_translations
from frappe.core.doctype.notification_count.notification_count import delete_notification_count_for
from frappe.permissions import reset_perms

@frappe.whitelist()
def get_roles_and_doctypes():
@@ -82,7 +83,7 @@ def validate_and_reset(doctype, for_remove=False):
@frappe.whitelist()
def reset(doctype):
frappe.only_for("System Manager")
frappe.reset_perms(doctype)
reset_perms(doctype)
clear_doctype_cache(doctype)

def clear_doctype_cache(doctype):


+ 0
- 15
frappe/model/__init__.py Ver ficheiro

@@ -15,21 +15,6 @@ integer_docfield_properties = ["reqd", "search_index", "in_list_view", "permleve
"hidden", "read_only", "ignore_user_permissions", "allow_on_submit", "report_hide",
"in_filter", "no_copy", "print_hide", "unique"]

def insert(doclist):
if not isinstance(doclist, list):
doclist = [doclist]

for d in doclist:
if isinstance(d, dict):
d["__islocal"] = 1
else:
d.set("__islocal", 1)

wrapper = frappe.get_doc(doclist)
wrapper.save()

return wrapper

def rename(doctype, old, new, debug=False):
import frappe.model.rename_doc
frappe.model.rename_doc.rename_doc(doctype, old, new, debug)


+ 1
- 1
frappe/modules/patch_handler.py Ver ficheiro

@@ -12,7 +12,7 @@ from __future__ import unicode_literals

where patch1, patch2 is module name
"""
import frappe, os
import frappe, os, frappe.permissions

class PatchError(Exception): pass



+ 2
- 2
frappe/patches.txt Ver ficheiro

@@ -12,7 +12,7 @@ frappe.patches.v4_0.change_varchar_length
frappe.patches.v5_0.v4_to_v5

frappe.patches.v4_0.webnotes_to_frappe
execute:frappe.reset_perms("Module Def")
execute:frappe.permissions.reset_perms("Module Def")
execute:import frappe.installer;frappe.installer.make_site_dirs() #2014-02-19
frappe.patches.v4_0.rename_profile_to_user
frappe.patches.v4_0.deprecate_control_panel
@@ -38,7 +38,7 @@ execute:frappe.db.sql("update tabReport set apply_user_permissions=1") #2014-06-
frappe.patches.v4_0.replace_deprecated_timezones
execute:import frappe.website.render; frappe.website.render.clear_cache("login"); #2014-06-10
frappe.patches.v4_0.fix_attach_field_file_url
execute:frappe.reset_perms("User") #2014-06-13
execute:frappe.permissions.reset_perms("User") #2014-06-13
execute:frappe.db.sql("""delete from `tabUserRole` where ifnull(parentfield, '')='' or ifnull(`role`, '')=''""") #2014-08-18
frappe.patches.v4_0.remove_user_owner_custom_field
execute:frappe.delete_doc("DocType", "Website Template")


+ 5
- 1
frappe/translate.py Ver ficheiro

@@ -337,12 +337,16 @@ def read_csv_file(path):
from csv import reader
with codecs.open(path, 'r', 'utf-8') as msgfile:
data = msgfile.read()

# for japanese! #wtf
data = data.replace(chr(28), "").replace(chr(29), "")

data = reader([r.encode('utf-8') for r in data.splitlines()])
newdata = [[unicode(val, 'utf-8') for val in row] for row in data]
return newdata

def write_csv_file(path, app_messages, lang_dict):
"""Write translation CSV file
"""Write translation CSV file.

:param path: File path, usually `[app]/translations`.
:param app_messages: Translatable strings for this app.


+ 9
- 0
frappe/website/permissions.py Ver ficheiro

@@ -75,3 +75,12 @@ def clear_permissions(users=None):
cache = frappe.cache()
for user in users:
cache.delete_value("website_route_permissions:{}".format(user))

def reset_perms(doctype):
"""Reset permissions for given doctype."""
from frappe.core.doctype.notification_count.notification_count import delete_notification_count_for
delete_notification_count_for(doctype)

frappe.db.sql("""delete from tabDocPerm where parent=%s""", doctype)
frappe.reload_doc(frappe.db.get_value("DocType", doctype, "module"),
"DocType", doctype, force=True)

Carregando…
Cancelar
Guardar