Przeglądaj źródła

Merge branch 'v6-wip' into develop

version-14
Anand Doshi 10 lat temu
rodzic
commit
bd4c3aec06
100 zmienionych plików z 8385 dodań i 297 usunięć
  1. +4
    -2
      .travis.yml
  2. +2
    -0
      frappe/__init__.py
  3. +1
    -1
      frappe/__version__.py
  4. +2
    -2
      frappe/api.py
  5. +26
    -1
      frappe/app.py
  6. +219
    -0
      frappe/async.py
  7. +1
    -1
      frappe/boot.py
  8. +2
    -1
      frappe/build.py
  9. +11
    -3
      frappe/celery_app.py
  10. +1
    -0
      frappe/change_log/current/assign_to_myself.md
  11. +40
    -1
      frappe/commands.py
  12. +0
    -0
      frappe/core/doctype/async_task/__init__.py
  13. +142
    -0
      frappe/core/doctype/async_task/async_task.json
  14. +10
    -0
      frappe/core/doctype/async_task/async_task.py
  15. +12
    -0
      frappe/core/doctype/async_task/test_async_task.py
  16. +2
    -2
      frappe/core/doctype/communication/communication.json
  17. +4
    -3
      frappe/core/doctype/doctype/doctype.json
  18. +1
    -1
      frappe/core/doctype/doctype/doctype.py
  19. +2
    -2
      frappe/core/doctype/version/version.json
  20. +2
    -1
      frappe/core/page/data_import_tool/importer.py
  21. +50
    -23
      frappe/core/page/desktop/desktop.js
  22. +25
    -0
      frappe/core/page/desktop/desktop_list_view.html
  23. +2
    -2
      frappe/core/page/desktop/desktop_module_icon.html
  24. +39
    -0
      frappe/desk/doctype/note/note.js
  25. +2
    -2
      frappe/desk/doctype/note/note.json
  26. +20
    -0
      frappe/desk/doctype/todo/todo_list.js
  27. +12
    -3
      frappe/desk/form/meta.py
  28. +4
    -8
      frappe/desk/moduleview.py
  29. +4
    -28
      frappe/desk/page/messages/messages.js
  30. +3
    -2
      frappe/desk/page/messages/messages_row.html
  31. +2
    -2
      frappe/email/doctype/email_account/email_account.json
  32. +2
    -7
      frappe/email/doctype/standard_reply/standard_reply.json
  33. +1
    -0
      frappe/geo/country_info.json
  34. +2
    -2
      frappe/geo/doctype/country/country.json
  35. +20
    -1
      frappe/handler.py
  36. +5
    -1
      frappe/hooks.py
  37. +62
    -3
      frappe/installer.py
  38. +4
    -4
      frappe/model/base_document.py
  39. +1
    -1
      frappe/model/db_query.py
  40. +4
    -0
      frappe/patches.txt
  41. +0
    -0
      frappe/patches/v6_0/__init__.py
  42. +7
    -0
      frappe/patches/v6_0/document_type_rename.py
  43. +7
    -0
      frappe/patches/v6_0/fix_ghana_currency.py
  44. +6
    -0
      frappe/patches/v6_0/make_task_log_folder.py
  45. +4
    -1
      frappe/public/build.json
  46. +4
    -0
      frappe/public/css/common.css
  47. +4
    -0
      frappe/public/css/desk.css
  48. +12
    -0
      frappe/public/css/desktop.css
  49. +4
    -0
      frappe/public/css/form.css
  50. +36
    -10
      frappe/public/css/mobile.css
  51. +4
    -0
      frappe/public/css/website.css
  52. +0
    -4
      frappe/public/js/frappe/desk.js
  53. +7
    -1
      frappe/public/js/frappe/form/control.js
  54. +1
    -1
      frappe/public/js/frappe/form/dashboard.js
  55. +11
    -0
      frappe/public/js/frappe/form/footer/assign_to.js
  56. +6
    -1
      frappe/public/js/frappe/form/footer/timeline.js
  57. +9
    -13
      frappe/public/js/frappe/form/layout.js
  58. +38
    -22
      frappe/public/js/frappe/form/save.js
  59. +3
    -0
      frappe/public/js/frappe/form/toolbar.js
  60. +5
    -0
      frappe/public/js/frappe/list/doclistview.js
  61. +2
    -4
      frappe/public/js/frappe/list/list_item_row.html
  62. +1
    -1
      frappe/public/js/frappe/list/list_item_row_head.html
  63. +3
    -1
      frappe/public/js/frappe/list/list_sidebar.html
  64. +13
    -1
      frappe/public/js/frappe/list/list_sidebar.js
  65. +1
    -1
      frappe/public/js/frappe/misc/user.js
  66. +12
    -1
      frappe/public/js/frappe/request.js
  67. +114
    -0
      frappe/public/js/frappe/socket.js
  68. +12
    -10
      frappe/public/js/frappe/ui/dialog.js
  69. +8
    -0
      frappe/public/js/frappe/ui/messages.js
  70. +15
    -0
      frappe/public/js/frappe/ui/page.js
  71. +2
    -2
      frappe/public/js/frappe/ui/toolbar/about.js
  72. +0
    -1
      frappe/public/js/frappe/ui/toolbar/toolbar.js
  73. +5
    -0
      frappe/public/js/frappe/upload.js
  74. +18
    -4
      frappe/public/js/frappe/views/communication.js
  75. +1
    -1
      frappe/public/js/frappe/views/module/module_section.html
  76. +12
    -4
      frappe/public/js/frappe/views/reports/reportview.js
  77. +7
    -2
      frappe/public/js/legacy/form.js
  78. +7000
    -0
      frappe/public/js/lib/socket.io.min.js
  79. +5
    -0
      frappe/public/less/common.less
  80. +18
    -0
      frappe/public/less/desktop.less
  81. +5
    -0
      frappe/public/less/form.less
  82. +43
    -12
      frappe/public/less/mobile.less
  83. +2
    -0
      frappe/public/less/variables.less
  84. +4
    -1
      frappe/sessions.py
  85. +58
    -3
      frappe/tasks.py
  86. +17
    -0
      frappe/tests/test_async.py
  87. +5
    -5
      frappe/tests/test_data_import.py
  88. +1
    -0
      frappe/utils/__init__.py
  89. +1
    -2
      frappe/utils/backups.py
  90. +4
    -2
      frappe/utils/install.py
  91. +7
    -4
      frappe/utils/response.py
  92. +2
    -2
      frappe/website/doctype/blog_category/blog_category.json
  93. +2
    -2
      frappe/website/doctype/blogger/blogger.json
  94. +2
    -2
      frappe/website/doctype/web_form/web_form.json
  95. +2
    -2
      frappe/website/doctype/web_page/web_page.json
  96. +2
    -2
      frappe/website/doctype/website_slideshow/website_slideshow.json
  97. +59
    -59
      frappe/workflow/doctype/workflow_document_state/workflow_document_state.json
  98. +2
    -2
      frappe/workflow/doctype/workflow_state/workflow_state.json
  99. +1
    -0
      requirements.txt
  100. +1
    -1
      setup.py

+ 4
- 2
.travis.yml Wyświetl plik

@@ -15,7 +15,6 @@ install:
- wget https://raw.githubusercontent.com/frappe/bench/master/install_scripts/setup_frappe.sh - 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 bash setup_frappe.sh --skip-setup-bench --mysql-root-password travis
- sudo pip install --upgrade pip - sudo pip install --upgrade pip
- sudo service redis-server start
- rm $TRAVIS_BUILD_DIR/.git/shallow - rm $TRAVIS_BUILD_DIR/.git/shallow
- cd ~/ && bench init frappe-bench --frappe-path $TRAVIS_BUILD_DIR - cd ~/ && bench init frappe-bench --frappe-path $TRAVIS_BUILD_DIR
- cp -r $TRAVIS_BUILD_DIR/test_sites/test_site ~/frappe-bench/sites/ - cp -r $TRAVIS_BUILD_DIR/test_sites/test_site ~/frappe-bench/sites/
@@ -23,10 +22,13 @@ install:
script: script:
- cd ~/frappe-bench - cd ~/frappe-bench
- bench use test_site - bench use test_site
- bench setup redis-cache
- bench setup redis-async-broker
- bench setup procfile --with-celery-broker
- bench reinstall - bench reinstall
- bench build - bench build
- bench build-website - bench build-website
- bench serve &
- bench start &
- sleep 10 - sleep 10
- bench --verbose run-tests --driver Firefox - bench --verbose run-tests --driver Firefox




+ 2
- 0
frappe/__init__.py Wyświetl plik

@@ -7,6 +7,7 @@ globals attached to frappe module
from __future__ import unicode_literals from __future__ import unicode_literals


from werkzeug.local import Local, release_local from werkzeug.local import Local, release_local
from functools import wraps
import os, importlib, inspect, logging, json import os, importlib, inspect, logging, json


# public # public
@@ -14,6 +15,7 @@ from frappe.__version__ import __version__
from .exceptions import * from .exceptions import *
from .utils.jinja import get_jenv, get_template, render_template from .utils.jinja import get_jenv, get_template, render_template



local = Local() local = Local()


class _dict(dict): class _dict(dict):


+ 1
- 1
frappe/__version__.py Wyświetl plik

@@ -1,2 +1,2 @@
from __future__ import unicode_literals from __future__ import unicode_literals
__version__ = "5.4.2"
__version__ = "6.0.0"

+ 2
- 2
frappe/api.py Wyświetl plik

@@ -58,13 +58,13 @@ def handle():
if frappe.local.request.method=="GET": if frappe.local.request.method=="GET":
if not doc.has_permission("read"): if not doc.has_permission("read"):
frappe.throw(_("Not permitted"), frappe.PermissionError) 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 frappe.local.request.method=="POST":
if not doc.has_permission("write"): if not doc.has_permission("write"):
frappe.throw(_("Not permitted"), frappe.PermissionError) 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() frappe.db.commit()


else: else:


+ 26
- 1
frappe/app.py Wyświetl plik

@@ -12,6 +12,8 @@ from werkzeug.local import LocalManager
from werkzeug.exceptions import HTTPException, NotFound from werkzeug.exceptions import HTTPException, NotFound
from werkzeug.contrib.profiler import ProfilerMiddleware from werkzeug.contrib.profiler import ProfilerMiddleware
from werkzeug.wsgi import SharedDataMiddleware from werkzeug.wsgi import SharedDataMiddleware
from werkzeug.serving import run_with_reloader



import mimetypes import mimetypes
import frappe import frappe
@@ -20,9 +22,10 @@ import frappe.auth
import frappe.api import frappe.api
import frappe.utils.response import frappe.utils.response
import frappe.website.render 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.middlewares import StaticDataMiddleware



local_manager = LocalManager([frappe.local]) local_manager = LocalManager([frappe.local])


_site = None _site = None
@@ -30,6 +33,21 @@ _sites_path = os.environ.get("SITES_PATH", ".")


logger = frappe.get_logger() 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 @Request.application
def application(request): def application(request):
frappe.local.request = request frappe.local.request = request
@@ -135,6 +153,8 @@ def make_form_dict(request):
frappe.local.form_dict.pop("_") frappe.local.form_dict.pop("_")


application = local_manager.make_middleware(application) application = local_manager.make_middleware(application)
application.debug = True



def serve(port=8000, profile=False, site=None, sites_path='.'): def serve(port=8000, profile=False, site=None, sites_path='.'):
global application, _site, _sites_path global application, _site, _sites_path
@@ -155,5 +175,10 @@ def serve(port=8000, profile=False, site=None, sites_path='.'):
b'/files': os.path.abspath(sites_path).encode("utf-8") 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, run_simple('0.0.0.0', int(port), application, use_reloader=True,
use_debugger=True, use_evalex=True, threaded=True) use_debugger=True, use_evalex=True, threaded=True)

+ 219
- 0
frappe/async.py Wyświetl plik

@@ -0,0 +1,219 @@
# -*- 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
import json
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):
from frappe.tasks import run_async_task
from frappe.handler import execute_cmd
if frappe.conf.no_async:
return execute_cmd(cmd, async=True)
args = frappe._dict(args)
task = run_async_task.delay(frappe.local.site,
(frappe.session and frappe.session.user) or 'Administrator', cmd, args)
if set_in_response:
frappe.local.response['task_id'] = task.id
return task.id

@wraps(f)
def queue(*args, **kwargs):
from frappe.tasks import run_async_task
from frappe.handler import execute_cmd
if frappe.conf.no_async:
return execute_cmd(cmd, async=True)
task = run_async_task.delay(frappe.local.site,
(frappe.session and frappe.session.user) or 'Administrator', cmd,
frappe.local.form_dict)
frappe.local.response['task_id'] = task.id
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


def run_async_task(method, args, reference_doctype=None, reference_name=None, set_in_response=True):
if frappe.local.request and frappe.local.request.method == "GET":
frappe.throw("Cannot run task in a GET request")
task_id = method.run(args, set_in_response=set_in_response)
task = frappe.new_doc("Async Task")
task.celery_task_id = task_id
task.status = "Queued"
task.reference_doctype = reference_doctype
task.reference_name = reference_name
task.save()
return task_id


@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(6)
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):
frappe.db.set_value("Async Task", task_id, "status", status)
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 emit_via_redis(event, message, room=None):
r = get_redis_server()
try:
r.publish('events', json.dumps({'event': event, 'message': message, 'room': room}))
except redis.exceptions.ConnectionError:
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 = "task_progress:" + frappe.local.task_id
task_log_key = "task_log:" + task_id
emit_via_redis('task_progress', {
"message": {
"lines": {line_no: line}
},
"task_id": task_id
}, room=task_progress_room)
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).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).get_session_data()
return {
'user': session.user,
}

def new_comment(doc, event):
if not doc.comment_doctype:
return
if doc.comment_doctype == 'Message':
if doc.comment_docname == frappe.session.user:
message = doc.as_dict()
message['broadcast'] = True
emit_via_redis('new_message', message, room=get_site_room())
else:
emit_via_redis('new_message', doc.as_dict(), room=get_user_room(doc.comment_docname))
else:
emit_via_redis('new_comment', doc.as_dict(), room=get_doc_room(doc.comment_doctype, doc.comment_docname))

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'])


+ 1
- 1
frappe/boot.py Wyświetl plik

@@ -69,7 +69,7 @@ def get_bootinfo():
bootinfo['versions'] = {k: v['version'] for k, v in get_versions().items()} bootinfo['versions'] = {k: v['version'] for k, v in get_versions().items()}


bootinfo.error_report_email = frappe.get_hooks("error_report_email") 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.default_background_image = "/assets/frappe/images/ui/into-the-dawn.jpg"
bootinfo.calendars = sorted(frappe.get_hooks("calendars")) bootinfo.calendars = sorted(frappe.get_hooks("calendars"))


return bootinfo return bootinfo


+ 2
- 1
frappe/build.py Wyświetl plik

@@ -127,7 +127,8 @@ def pack(target, sources, no_compress, verbose):
tmpin, tmpout = StringIO(data.encode('utf-8')), StringIO() tmpin, tmpout = StringIO(data.encode('utf-8')), StringIO()
jsm.minify(tmpin, tmpout) jsm.minify(tmpin, tmpout)
minified = tmpout.getvalue() minified = tmpout.getvalue()
outtxt += unicode(minified or '', 'utf-8').strip('\n') + ';'
if minified:
outtxt += unicode(minified or '', 'utf-8').strip('\n') + ';'


if verbose: if verbose:
print "{0}: {1}k".format(f, int(len(minified) / 1024)) print "{0}: {1}k".format(f, int(len(minified) / 1024))


+ 11
- 3
frappe/celery_app.py Wyświetl plik

@@ -17,9 +17,10 @@ SITES_PATH = os.environ.get('SITES_PATH', '.')


# defaults # defaults
DEFAULT_CELERY_BROKER = "redis://localhost" DEFAULT_CELERY_BROKER = "redis://localhost"
DEFAULT_CELERY_BACKEND = None
DEFAULT_CELERY_BACKEND = "redis://localhost"
DEFAULT_SCHEDULER_INTERVAL = 300 DEFAULT_SCHEDULER_INTERVAL = 300
LONGJOBS_PREFIX = "longjobs@" LONGJOBS_PREFIX = "longjobs@"
ASYNC_TASKS_PREFIX = "async@"


_app = None _app = None
def get_celery(): def get_celery():
@@ -29,7 +30,7 @@ def get_celery():
_app = Celery('frappe', _app = Celery('frappe',
broker=conf.celery_broker or DEFAULT_CELERY_BROKER, broker=conf.celery_broker or DEFAULT_CELERY_BROKER,
backend=conf.celery_result_backend or DEFAULT_CELERY_BACKEND)
backend=conf.async_redis_server or DEFAULT_CELERY_BACKEND)
setup_celery(_app, conf) setup_celery(_app, conf)
@@ -41,9 +42,11 @@ def setup_celery(app, conf):
app.conf.CELERY_TASK_SERIALIZER = 'json' app.conf.CELERY_TASK_SERIALIZER = 'json'
app.conf.CELERY_ACCEPT_CONTENT = ['json'] app.conf.CELERY_ACCEPT_CONTENT = ['json']
app.conf.CELERY_TIMEZONE = 'UTC' app.conf.CELERY_TIMEZONE = 'UTC'
app.conf.CELERY_RESULT_SERIALIZER = 'json'
app.CELERY_TASK_RESULT_EXPIRES = timedelta(0, 3600)
if conf.celery_queue_per_site: if conf.celery_queue_per_site:
app.conf.CELERY_ROUTES = (SiteRouter(),)
app.conf.CELERY_ROUTES = (SiteRouter(), AsyncTaskRouter())
app.conf.CELERYBEAT_SCHEDULE = get_beat_schedule(conf) app.conf.CELERYBEAT_SCHEDULE = get_beat_schedule(conf)


@@ -61,6 +64,11 @@ class SiteRouter(object):
return get_queue(frappe.local.site) return get_queue(frappe.local.site)
return None 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): def get_queue(site, prefix=None):
return {'queue': "{}{}".format(prefix or "", site)} return {'queue': "{}{}".format(prefix or "", site)}


+ 1
- 0
frappe/change_log/current/assign_to_myself.md Wyświetl plik

@@ -0,0 +1 @@
- You can now quickly assign a document to yourself by clicking on "Assign to me"

+ 40
- 1
frappe/commands.py Wyświetl plik

@@ -163,6 +163,16 @@ def install_app(context, app):
finally: finally:
frappe.destroy() 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.command('add-system-manager')
@click.argument('email') @click.argument('email')
@click.option('--first-name') @click.option('--first-name')
@@ -616,7 +626,7 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), driver=None
site = get_single_site(context) site = get_single_site(context)
frappe.init(site=site) frappe.init(site=site)


if frappe.conf.run_selenium_tests:
if frappe.conf.run_selenium_tests and False:
sel.start(context.verbose, driver) sel.start(context.verbose, driver)


try: try:
@@ -738,6 +748,20 @@ def remove_from_installed_apps(context, app):
finally: finally:
frappe.destroy() 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): def move(dest_dir, site):
import os import os
if not os.path.isdir(dest_dir): if not os.path.isdir(dest_dir):
@@ -759,6 +783,18 @@ def move(dest_dir, site):
frappe.destroy() frappe.destroy()
return final_new_path 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.command('drop-site')
@click.argument('site') @click.argument('site')
@click.option('--root-login', default='root') @click.option('--root-login', default='root')
@@ -797,6 +833,7 @@ commands = [
restore, restore,
reinstall, reinstall,
install_app, install_app,
list_apps,
add_system_manager, add_system_manager,
migrate, migrate,
run_patch, run_patch,
@@ -836,5 +873,7 @@ commands = [
_use, _use,
backup, backup,
remove_from_installed_apps, remove_from_installed_apps,
uninstall,
drop_site, drop_site,
set_config,
] ]

+ 0
- 0
frappe/core/doctype/async_task/__init__.py Wyświetl plik


+ 142
- 0
frappe/core/doctype/async_task/async_task.json Wyświetl plik

@@ -0,0 +1,142 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:celery_task_id",
"creation": "2015-07-03 11:28:03.496346",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"fields": [
{
"allow_on_submit": 0,
"fieldname": "celery_task_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Celery Task ID",
"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,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Status",
"no_copy": 0,
"options": "\nQueued\nRunning\nFinished\nFailed\n",
"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,
"fieldname": "stdout",
"fieldtype": "Long Text",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "stdout",
"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,
"fieldname": "stderr",
"fieldtype": "Long Text",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "stderr",
"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
},
{
"fieldname": "reference_doctype",
"fieldtype": "Link",
"label": "Reference DocType",
"options": "DocType",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"label": "Reference Doc",
"options": "reference_doctype",
"permlevel": 0,
"precision": ""
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"modified": "2015-07-28 16:18:11.344349",
"modified_by": "Administrator",
"module": "Core",
"name": "Async Task",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
}

+ 10
- 0
frappe/core/doctype/async_task/async_task.py Wyświetl plik

@@ -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

+ 12
- 0
frappe/core/doctype/async_task/test_async_task.py Wyświetl plik

@@ -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

+ 2
- 2
frappe/core/doctype/communication/communication.json Wyświetl plik

@@ -5,7 +5,7 @@
"description": "Keep a track of all communications", "description": "Keep a track of all communications",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master",
"document_type": "Setup",
"fields": [ "fields": [
{ {
"default": "COMM-", "default": "COMM-",
@@ -198,7 +198,7 @@
"idx": 1, "idx": 1,
"in_dialog": 0, "in_dialog": 0,
"issingle": 0, "issingle": 0,
"modified": "2015-07-28 07:28:11.457131",
"modified": "2015-07-28 17:18:11.664740",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Core", "module": "Core",
"name": "Communication", "name": "Communication",


+ 4
- 3
frappe/core/doctype/doctype/doctype.json Wyświetl plik

@@ -7,6 +7,7 @@
"description": "DocType is a Table / Form in the application.", "description": "DocType is a Table / Form in the application.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Document",
"fields": [ "fields": [
{ {
"fieldname": "sb0", "fieldname": "sb0",
@@ -65,7 +66,7 @@
"label": "Document Type", "label": "Document Type",
"oldfieldname": "document_type", "oldfieldname": "document_type",
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "\nMaster\nTransaction\nSystem\nOther",
"options": "\nDocument\nSetup\nSystem\nOther",
"permlevel": 0 "permlevel": 0
}, },
{ {
@@ -108,7 +109,7 @@
"oldfieldtype": "Table", "oldfieldtype": "Table",
"options": "DocField", "options": "DocField",
"permlevel": 0, "permlevel": 0,
"reqd": 1,
"reqd": 0,
"search_index": 0 "search_index": 0
}, },
{ {
@@ -337,7 +338,7 @@
"idx": 1, "idx": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-03-03 10:40:45.768116",
"modified": "2015-07-28 16:18:11.925264",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Core", "module": "Core",
"name": "DocType", "name": "DocType",


+ 1
- 1
frappe/core/doctype/doctype/doctype.py Wyświetl plik

@@ -87,7 +87,7 @@ class DocType(Document):


if autoname and (not autoname.startswith('field:')) \ if autoname and (not autoname.startswith('field:')) \
and (not autoname.startswith('eval:')) \ and (not autoname.startswith('eval:')) \
and (not autoname in ('Prompt', 'hash')) \
and (not autoname.lower() in ('prompt', 'hash')) \
and (not autoname.startswith('naming_series:')): and (not autoname.startswith('naming_series:')):


prefix = autoname.split('.')[0] prefix = autoname.split('.')[0]


+ 2
- 2
frappe/core/doctype/version/version.json Wyświetl plik

@@ -3,7 +3,7 @@
"creation": "2014-02-20 17:22:37", "creation": "2014-02-20 17:22:37",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master",
"document_type": "Setup",
"fields": [ "fields": [
{ {
"fieldname": "ref_doctype", "fieldname": "ref_doctype",
@@ -33,7 +33,7 @@
], ],
"icon": "icon-copy", "icon": "icon-copy",
"idx": 1, "idx": 1,
"modified": "2014-08-05 01:23:37.541856",
"modified": "2015-07-28 16:18:12.706419",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Core", "module": "Core",
"name": "Version", "name": "Version",


+ 2
- 1
frappe/core/page/data_import_tool/importer.py Wyświetl plik

@@ -5,6 +5,7 @@ from __future__ import unicode_literals


import frappe, json import frappe, json
import frappe.permissions import frappe.permissions
import frappe.async


from frappe import _ from frappe import _


@@ -14,7 +15,7 @@ from frappe.utils.dateutils import parse_date
from frappe.utils import cint, cstr, flt from frappe.utils import cint, cstr, flt
from frappe.core.page.data_import_tool.data_import_tool import get_data_keys from frappe.core.page.data_import_tool.data_import_tool import get_data_keys


@frappe.whitelist()
@frappe.async.handler
def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, overwrite=None, def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, overwrite=None,
ignore_links=False, pre_process=None): ignore_links=False, pre_process=None):
"""upload data""" """upload data"""


+ 50
- 23
frappe/core/page/desktop/desktop.js Wyświetl plik

@@ -2,10 +2,18 @@ frappe.provide('frappe.desktop');


frappe.pages['desktop'].on_page_load = function(wrapper) { frappe.pages['desktop'].on_page_load = function(wrapper) {
// load desktop // load desktop
frappe.desktop.set_background();
if(!frappe.list_desktop) {
frappe.desktop.set_background();
}
frappe.desktop.refresh(wrapper); frappe.desktop.refresh(wrapper);
}; };


frappe.pages['desktop'].on_page_show = function(wrapper) {
if(frappe.list_desktop) {
$("body").attr("data-route", "list-desktop");
}
};

$.extend(frappe.desktop, { $.extend(frappe.desktop, {
refresh: function(wrapper) { refresh: function(wrapper) {
if (wrapper) { if (wrapper) {
@@ -20,7 +28,10 @@ $.extend(frappe.desktop, {
var me = this; var me = this;
frappe.utils.set_title("Desktop"); frappe.utils.set_title("Desktop");


this.wrapper.html(frappe.render_template("desktop_icon_grid", {
var template = frappe.list_desktop ? "desktop_list_view" : "desktop_icon_grid";


this.wrapper.html(frappe.render_template(template, {
// all visible icons // all visible icons
desktop_items: this.get_desktop_items(), desktop_items: this.get_desktop_items(),


@@ -28,7 +39,7 @@ $.extend(frappe.desktop, {
user_desktop_items: this.get_user_desktop_items(), user_desktop_items: this.get_user_desktop_items(),
})); }));


this.setup_icon_click();
this.setup_module_click();


// notifications // notifications
this.show_pending_notifications(); this.show_pending_notifications();
@@ -96,29 +107,40 @@ $.extend(frappe.desktop, {
return out; return out;
}, },


setup_icon_click: function() {
this.wrapper.on("click", ".app-icon", function() {
var parent = $(this).parent();
var link = parent.attr("data-link");
if(link) {
if(link.substr(0, 1)==="/" || link.substr(0, 4)==="http") {
window.open(link, "_blank");
} else {
frappe.set_route(link);
}
return false;
setup_module_click: function() {
var me = this;

if(frappe.list_desktop) {
this.wrapper.on("click", ".desktop-list-item", function() {
me.open_module($(this));
});
} else {
this.wrapper.on("click", ".app-icon", function() {
me.open_module($(this).parent());
});
}
},

open_module: function(parent) {
var link = parent.attr("data-link");
if(link) {
if(link.substr(0, 1)==="/" || link.substr(0, 4)==="http") {
window.open(link, "_blank");
} else { } else {
module = frappe.get_module(parent.attr("data-name"));
if (module && module.onclick) {
module.onclick();
return false;
}
frappe.set_route(link);
} }
});
return false;
} else {
module = frappe.get_module(parent.attr("data-name"));
if (module && module.onclick) {
module.onclick();
return false;
}
}
}, },


make_sortable: function() { make_sortable: function() {
if (frappe.dom.is_touchscreen()) {
if (frappe.dom.is_touchscreen() || frappe.list_desktop) {
return; return;
} }


@@ -215,10 +237,15 @@ $.extend(frappe.desktop, {
sum = frappe.boot.notification_info.open_count_module[module]; sum = frappe.boot.notification_info.open_count_module[module];
} }
if (frappe.modules[module]) { if (frappe.modules[module]) {
var notifier = $("#module-count-" + frappe.get_module(module)._id);
var notifier = $(".module-count-" + frappe.get_module(module)._id);
if(notifier.length) { if(notifier.length) {
notifier.toggle(sum ? true : false); notifier.toggle(sum ? true : false);
notifier.find(".circle-text").html(sum || "");
var circle = notifier.find(".circle-text");
if(circle.length) {
circle.html(sum || "");
} else {
notifier.html(sum);
}
} }
} }
} }


+ 25
- 0
frappe/core/page/desktop/desktop_list_view.html Wyświetl plik

@@ -0,0 +1,25 @@
<div class="container page-body">
<div class="row">
<div class="layout-main-section">
<div class="page-content desktop-list" style="margin-top: 40px;">
{% for (var i=0, l=desktop_items.length; i < l; i++) {
var module = frappe.get_module(desktop_items[i]);
if (!module || (user_desktop_items.indexOf(module.name)===-1 && !module.force_show)
|| frappe.user.is_module_blocked(module.name)) { continue; }
%}
<div class="desktop-list-item" id="module-icon-{%= module._id %}"
data-name="{%= module.name %}" data-link="{%= module.link %}"
title="{%= module._label %}">
<h4>
<i class="{{ module.icon }} text-muted"
style="font-size: 20px; margin-right: 15px;"></i>
{{ module._label }}
</h4>
<span class="open-notification module-count-{{ module._id }}"
style="display: none;"></span>
</div>
{% } %}
</div>
</div>
</div>
</div>

+ 2
- 2
frappe/core/page/desktop/desktop_module_icon.html Wyświetl plik

@@ -1,8 +1,8 @@
<div id="module-icon-{%= _id %}" class="case-wrapper"
<div class="case-wrapper"
data-name="{%= name %}" data-link="{%= link %}" title="{%= _label %}"> data-name="{%= name %}" data-link="{%= link %}" title="{%= _label %}">
{%= app_icon %} {%= app_icon %}
<div class="case-label text-ellipsis"> <div class="case-label text-ellipsis">
<div class="circle" id="module-count-{%= _id %}" style="display: none;">
<div class="circle module-count-{%= _id %}" style="display: none;">
<span class="circle-text"></span> <span class="circle-text"></span>
</div> </div>
<!-- <span id="module-count-{%= _id %}" class="octicon octicon-primitive-dot circle" style="display: None"></span> --> <!-- <span id="module-count-{%= _id %}" class="octicon octicon-primitive-dot circle" style="display: None"></span> -->


+ 39
- 0
frappe/desk/doctype/note/note.js Wyświetl plik

@@ -0,0 +1,39 @@
frappe.ui.form.on("Note", {
refresh: function(frm) {
if(frm.doc.__islocal) {
frm.events.set_editable(frm, true);
} else {
// toggle edit
frm.add_custom_button("Edit", function() {
frm.events.set_editable(frm, !frm.is_note_editable);
})
frm.events.set_editable(frm, false);
}
},
set_editable: function(frm, editable) {
// hide all fields other than content

// no permission
if(editable && !frm.perm[0].write) return;

// content read_only
frm.set_df_property("content", "read_only", editable ? 0: 1);

// hide all other fields
$.each(frm.fields_dict, function(fieldname, field) {

if(fieldname !== "content"
&& !in_list(["Section Break", "Column Break"], field.df.fieldtype)) {
frm.set_df_property(fieldname, "hidden", editable ? 0: 1);
}

})

// no label, description for content either
frm.get_field("content").toggle_label(editable);
frm.get_field("content").toggle_description(editable);

// set flag for toggle
frm.is_note_editable = editable;
}
});

+ 2
- 2
frappe/desk/doctype/note/note.json Wyświetl plik

@@ -4,7 +4,7 @@
"description": "Note is a free page where users can share documents / notes", "description": "Note is a free page where users can share documents / notes",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Transaction",
"document_type": "Document",
"fields": [ "fields": [
{ {
"fieldname": "title", "fieldname": "title",
@@ -35,7 +35,7 @@
], ],
"icon": "icon-file-text", "icon": "icon-file-text",
"idx": 1, "idx": 1,
"modified": "2015-02-06 00:44:06.475116",
"modified": "2015-07-28 16:18:12.301520",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Desk", "module": "Desk",
"name": "Note", "name": "Note",


+ 20
- 0
frappe/desk/doctype/todo/todo_list.js Wyświetl plik

@@ -5,6 +5,26 @@ frappe.listview_settings['ToDo'] = {
"status": "Open" "status": "Open"
}; };
me.page.set_title(__("To Do")); me.page.set_title(__("To Do"));

},
refresh: function(me) {
// override assigned to me by owner
me.page.sidebar.find(".assigned-to-me a").off("click").on("click", function() {
var assign_filter = me.filter_list.get_filter("assigned_by");
assign_filter && assign_filter.remove(true);

me.filter_list.add_filter(me.doctype, "owner", '=', user);
me.run();
});

// add assigned by me
me.page.add_sidebar_item(__("Assigned By Me"), function() {
var assign_filter = me.filter_list.get_filter("owner");
assign_filter && assign_filter.remove(true);

me.filter_list.add_filter(me.doctype, "assigned_by", '=', user);
me.run();
}, ".assigned-to-me");
}, },
add_fields: ["reference_type", "reference_name"], add_fields: ["reference_type", "reference_name"],
} }

+ 12
- 3
frappe/desk/form/meta.py Wyświetl plik

@@ -121,7 +121,14 @@ class FormMeta(Meta):
df.search_fields = map(lambda sf: sf.strip(), search_fields.split(",")) df.search_fields = map(lambda sf: sf.strip(), search_fields.split(","))


def add_linked_with(self): def add_linked_with(self):
"""add list of doctypes this doctype is 'linked' with"""
"""add list of doctypes this doctype is 'linked' with.

Example, for Customer:

{"Address": {"fieldname": "customer"}..}
"""

# find fields where this doctype is linked
links = frappe.db.sql("""select parent, fieldname from tabDocField links = frappe.db.sql("""select parent, fieldname from tabDocField
where (fieldtype="Link" and options=%s) where (fieldtype="Link" and options=%s)
or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name)) or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name))
@@ -137,15 +144,17 @@ class FormMeta(Meta):
ret[dt] = { "fieldname": links[dt] } ret[dt] = { "fieldname": links[dt] }


if links: if links:
for grand_parent, options in frappe.db.sql("""select parent, options from tabDocField
# find out if linked in a child table
for parent, options in frappe.db.sql("""select parent, options from tabDocField
where fieldtype="Table" where fieldtype="Table"
and options in (select name from tabDocType and options in (select name from tabDocType
where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)): where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)):


ret[grand_parent] = {"child_doctype": options, "fieldname": links[options] }
ret[parent] = {"child_doctype": options, "fieldname": links[options] }
if options in ret: if options in ret:
del ret[options] del ret[options]


# find links of parents
links = frappe.db.sql("""select dt from `tabCustom Field` links = frappe.db.sql("""select dt from `tabCustom Field`
where (fieldtype="Table" and options=%s)""", (self.name)) where (fieldtype="Table" and options=%s)""", (self.name))
links += frappe.db.sql("""select parent from tabDocField links += frappe.db.sql("""select parent from tabDocField


+ 4
- 8
frappe/desk/moduleview.py Wyświetl plik

@@ -59,10 +59,10 @@ def build_standard_config(module, doctype_info):
data = [] data = []


add_section(data, _("Documents"), "icon-star", add_section(data, _("Documents"), "icon-star",
[d for d in doctype_info if in_document_section(d)])
[d for d in doctype_info if d.document_type in ("Document", "Transaction")])


add_section(data, _("Setup"), "icon-cog", add_section(data, _("Setup"), "icon-cog",
[d for d in doctype_info if not in_document_section(d)])
[d for d in doctype_info if d.document_type in ("Master", "Setup", "")])


add_section(data, _("Standard Reports"), "icon-list", add_section(data, _("Standard Reports"), "icon-list",
get_report_list(module, is_standard="Yes")) get_report_list(module, is_standard="Yes"))
@@ -82,14 +82,10 @@ def add_section(data, label, icon, items):
def add_custom_doctypes(data, doctype_info): def add_custom_doctypes(data, doctype_info):
"""Adds Custom DocTypes to modules setup via `config/desktop.py`.""" """Adds Custom DocTypes to modules setup via `config/desktop.py`."""
add_section(data, _("Documents"), "icon-star", add_section(data, _("Documents"), "icon-star",
[d for d in doctype_info if (d.custom and in_document_section(d))])
[d for d in doctype_info if (d.custom and d.document_type in ("Document", "Transaction"))])


add_section(data, _("Setup"), "icon-cog", add_section(data, _("Setup"), "icon-cog",
[d for d in doctype_info if (d.custom and not in_document_section(d))])

def in_document_section(d):
"""Returns True if `document_type` property is one of `Master`, `Transaction` or not set."""
return d.document_type in ("Transaction", "Master", "")
[d for d in doctype_info if (d.custom and d.document_type in ("Setup", "Master", ""))])


def get_doctype_info(module): def get_doctype_info(module):
"""Returns list of non child DocTypes for given module.""" """Returns list of non child DocTypes for given module."""


+ 4
- 28
frappe/desk/page/messages/messages.js Wyświetl plik

@@ -37,7 +37,6 @@ frappe.desk.pages.Messages = Class.extend({


make: function() { make: function() {
this.make_sidebar(); this.make_sidebar();
this.set_next_refresh();
}, },


make_sidebar: function() { make_sidebar: function() {
@@ -135,6 +134,9 @@ frappe.desk.pages.Messages = Class.extend({
hide_refresh: true, hide_refresh: true,
freeze: false, freeze: false,
render_row: function(wrapper, data) { render_row: function(wrapper, data) {
if(data.parenttype==="Assignment" || data.comment_type==="Shared") {
data.is_system_message = 1;
}
var row = $(frappe.render_template("messages_row", { var row = $(frappe.render_template("messages_row", {
data: data data: data
})).appendTo(wrapper); })).appendTo(wrapper);
@@ -155,31 +157,7 @@ frappe.desk.pages.Messages = Class.extend({
}); });
}, },


refresh: function() {
// check for updates every 5 seconds if page is active
this.set_next_refresh();

if(!frappe.session_alive) {
// not in session
return;
}

if(frappe.get_route()[0]!="messages") {
// not on messages page
return;
}

if (this.list) {
this.list.run();
}
},

set_next_refresh: function() {
// 30 seconds
setTimeout("frappe.desk.pages.messages.refresh()", 30000);
},

////
refresh: function() {},


get_contact: function() { get_contact: function() {
var route = location.hash; var route = location.hash;
@@ -195,5 +173,3 @@ frappe.desk.pages.Messages = Class.extend({




}); });



+ 3
- 2
frappe/desk/page/messages/messages_row.html Wyświetl plik

@@ -6,10 +6,11 @@
<div class="media"> <div class="media">
<div class="pull-left hidden-xs"> <div class="pull-left hidden-xs">
<span class="avatar avatar-small" title="{%= frappe.user.full_name(data.owner) %} "> <span class="avatar avatar-small" title="{%= frappe.user.full_name(data.owner) %} ">
<img class="media-object" src="{%= frappe.user.image(data.owner) %}">
<img class="media-object {{ data.is_system_message ? "grayscale" : "" }}"
src="{%= frappe.user.image(data.owner) %}">
</span> </span>
</div> </div>
<div class="media-body">
<div class="media-body {{ data.is_system_message ? "text-muted" : "" }}">
{%= data.comment %} {%= data.comment %}
</div> </div>
</div> </div>


+ 2
- 2
frappe/email/doctype/email_account/email_account.json Wyświetl plik

@@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master",
"document_type": "Setup",
"fields": [ "fields": [
{ {
"fieldname": "email_settings", "fieldname": "email_settings",
@@ -429,7 +429,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-07-16 10:11:06.466258",
"modified": "2015-07-28 16:18:12.116327",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Email", "module": "Email",
"name": "Email Account", "name": "Email Account",


+ 2
- 7
frappe/email/doctype/standard_reply/standard_reply.json Wyświetl plik

@@ -4,7 +4,7 @@
"creation": "2014-06-19 05:20:26.331041", "creation": "2014-06-19 05:20:26.331041",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Transaction",
"document_type": "Document",
"fields": [ "fields": [
{ {
"fieldname": "subject", "fieldname": "subject",
@@ -33,18 +33,13 @@
} }
], ],
"icon": "icon-comment", "icon": "icon-comment",
"modified": "2015-02-05 05:11:46.922356",
"modified": "2015-07-28 16:18:12.432775",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Email", "module": "Email",
"name": "Standard Reply", "name": "Standard Reply",
"name_case": "", "name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{
"permlevel": 0,
"read": 1,
"role": "All"
},
{ {
"apply_user_permissions": 1, "apply_user_permissions": 1,
"create": 1, "create": 1,


+ 1
- 0
frappe/geo/country_info.json Wyświetl plik

@@ -911,6 +911,7 @@
}, },
"Ghana": { "Ghana": {
"code": "gh", "code": "gh",
"currency": "GHS",
"currency_fraction": "Pesewa", "currency_fraction": "Pesewa",
"currency_fraction_units": 100, "currency_fraction_units": 100,
"currency_symbol": "\u20b5", "currency_symbol": "\u20b5",


+ 2
- 2
frappe/geo/doctype/country/country.json Wyświetl plik

@@ -5,7 +5,7 @@
"creation": "2013-01-19 10:23:30", "creation": "2013-01-19 10:23:30",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master",
"document_type": "Setup",
"fields": [ "fields": [
{ {
"fieldname": "country_name", "fieldname": "country_name",
@@ -42,7 +42,7 @@
"icon": "icon-globe", "icon": "icon-globe",
"idx": 1, "idx": 1,
"in_create": 0, "in_create": 0,
"modified": "2015-02-05 05:11:36.234753",
"modified": "2015-07-28 16:18:11.855617",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Geo", "module": "Geo",
"name": "Country", "name": "Country",


+ 20
- 1
frappe/handler.py Wyświetl plik

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
import frappe.utils import frappe.utils
import frappe.async
import frappe.sessions import frappe.sessions
import frappe.utils.file_manager import frappe.utils.file_manager
import frappe.desk.form.run_method import frappe.desk.form.run_method
@@ -18,6 +19,10 @@ def version():
def ping(): def ping():
return "pong" return "pong"


@frappe.async.handler
def async_ping():
return "pong"

@frappe.whitelist() @frappe.whitelist()
def runserverobj(method, docs=None, dt=None, dn=None, arg=None, args=None): def runserverobj(method, docs=None, dt=None, dn=None, arg=None, args=None):
frappe.desk.form.run_method.runserverobj(method, docs=docs, dt=dt, dn=dn, arg=arg, args=args) frappe.desk.form.run_method.runserverobj(method, docs=docs, dt=dt, dn=dn, arg=arg, args=args)
@@ -70,7 +75,7 @@ def handle():


return build_response("json") return build_response("json")


def execute_cmd(cmd):
def execute_cmd(cmd, async=False):
"""execute a request as python module""" """execute a request as python module"""
for hook in frappe.get_hooks("override_whitelisted_methods", {}).get(cmd, []): for hook in frappe.get_hooks("override_whitelisted_methods", {}).get(cmd, []):
# override using the first hook # override using the first hook
@@ -78,6 +83,8 @@ def execute_cmd(cmd):
break break


method = get_attr(cmd) method = get_attr(cmd)
if async:
method = method.queue


# check if whitelisted # check if whitelisted
if frappe.session['user'] == 'Guest': if frappe.session['user'] == 'Guest':
@@ -103,3 +110,15 @@ def get_attr(cmd):
method = globals()[cmd] method = globals()[cmd]
frappe.log("method:" + cmd) frappe.log("method:" + cmd)
return method return method


@frappe.whitelist()
def get_async_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
}

+ 5
- 1
frappe/hooks.py Wyświetl plik

@@ -26,7 +26,7 @@ to ERPNext.
""" """


app_icon = "octicon octicon-circuit-board" app_icon = "octicon octicon-circuit-board"
app_version = "5.4.2"
app_version = "6.0.0"
app_color = "orange" app_color = "orange"
github_link = "https://github.com/frappe/frappe" github_link = "https://github.com/frappe/frappe"


@@ -131,6 +131,9 @@ doc_events = {
"frappe.email.doctype.email_alert.email_alert.trigger_email_alerts" "frappe.email.doctype.email_alert.email_alert.trigger_email_alerts"
], ],
"on_trash": "frappe.desk.notifications.clear_doctype_notifications" "on_trash": "frappe.desk.notifications.clear_doctype_notifications"
},
"Comment": {
"after_insert": "frappe.async.new_comment"
} }
} }


@@ -147,6 +150,7 @@ scheduler_events = {
"frappe.desk.doctype.event.event.send_event_digest", "frappe.desk.doctype.event.event.send_event_digest",
"frappe.sessions.clear_expired_sessions", "frappe.sessions.clear_expired_sessions",
"frappe.email.doctype.email_alert.email_alert.trigger_daily_alerts", "frappe.email.doctype.email_alert.email_alert.trigger_daily_alerts",
"frappe.async.remove_old_task_logs",
] ]
} }




+ 62
- 3
frappe/installer.py Wyświetl plik

@@ -151,6 +151,36 @@ def remove_from_installed_apps(app_name):
if frappe.flags.in_install: if frappe.flags.in_install:
post_install() post_install()


def remove_app(app_name, dry_run=False):
"""Delete app and all linked to the app's module with the app."""

if not dry_run:
confirm = raw_input("All doctypes (including custom), modules related to this app will be deleted. Are you sure you want to continue (y/n) ? ")
if confirm!="y":
return

from frappe.utils.backups import scheduled_backup
print "Backing up..."
scheduled_backup(ignore_files=True)

# remove modules, doctypes, roles
for module_name in frappe.get_module_list(app_name):
for doctype in frappe.get_list("DocType", filters={"module": module_name},
fields=["name", "issingle"]):
print "removing {0}...".format(doctype.name)
# drop table

if not dry_run:
if not doctype.issingle:
frappe.db.sql("drop table `tab{0}`".format(doctype.name))
frappe.delete_doc("DocType", doctype.name)

print "removing Module {0}...".format(module_name)
if not dry_run:
frappe.delete_doc("Module Def", module_name)

remove_from_installed_apps(app_name)

def post_install(rebuild_website=False): def post_install(rebuild_website=False):
if rebuild_website: if rebuild_website:
render.clear_cache() render.clear_cache()
@@ -188,7 +218,7 @@ def make_conf(db_name=None, db_password=None, site_config=None):


def make_site_config(db_name=None, db_password=None, site_config=None): def make_site_config(db_name=None, db_password=None, site_config=None):
frappe.create_folder(os.path.join(frappe.local.site_path)) frappe.create_folder(os.path.join(frappe.local.site_path))
site_file = os.path.join(frappe.local.site_path, "site_config.json")
site_file = get_site_config_path()


if not os.path.exists(site_file): if not os.path.exists(site_file):
if not (site_config and isinstance(site_config, dict)): if not (site_config and isinstance(site_config, dict)):
@@ -197,6 +227,34 @@ def make_site_config(db_name=None, db_password=None, site_config=None):
with open(site_file, "w") as f: with open(site_file, "w") as f:
f.write(json.dumps(site_config, indent=1, sort_keys=True)) f.write(json.dumps(site_config, indent=1, sort_keys=True))


def update_site_config(key, value):
"""Update a value in site_config"""
with open(get_site_config_path(), "r") as f:
site_config = json.loads(f.read())

# int
try:
value = int(value)
except ValueError:
pass

# boolean
if value in ("False", "True"):
value = eval(value)

# remove key if value is None
if value == "None":
if key in site_config:
del site_config[key]
else:
site_config[key] = value

with open(get_site_config_path(), "w") as f:
f.write(json.dumps(site_config, indent=1, sort_keys=True))

def get_site_config_path():
return os.path.join(frappe.local.site_path, "site_config.json")

def get_conf_params(db_name=None, db_password=None): def get_conf_params(db_name=None, db_password=None):
if not db_name: if not db_name:
db_name = raw_input("Database Name: ") db_name = raw_input("Database Name: ")
@@ -214,7 +272,8 @@ def make_site_dirs():
site_private_path = os.path.join(frappe.local.site_path, 'private') site_private_path = os.path.join(frappe.local.site_path, 'private')
for dir_path in ( for dir_path in (
os.path.join(site_private_path, 'backups'), os.path.join(site_private_path, 'backups'),
os.path.join(site_public_path, 'files')):
os.path.join(site_public_path, 'files'),
os.path.join(frappe.local.site_path, 'task-logs')):
if not os.path.exists(dir_path): if not os.path.exists(dir_path):
os.makedirs(dir_path) os.makedirs(dir_path)
locks_dir = frappe.get_site_path('locks') locks_dir = frappe.get_site_path('locks')
@@ -227,7 +286,7 @@ def add_module_defs(app):
d = frappe.new_doc("Module Def") d = frappe.new_doc("Module Def")
d.app_name = app d.app_name = app
d.module_name = module d.module_name = module
d.save()
d.save(ignore_permissions=True)


def remove_missing_apps(): def remove_missing_apps():
apps = ('frappe_subscription', 'shopping_cart') apps = ('frappe_subscription', 'shopping_cart')


+ 4
- 4
frappe/model/base_document.py Wyświetl plik

@@ -184,7 +184,7 @@ class BaseDocument(object):


elif df.fieldtype in ("Datetime", "Date") and d[fieldname]=="": elif df.fieldtype in ("Datetime", "Date") and d[fieldname]=="":
d[fieldname] = None d[fieldname] = None
elif df.get("unique") and cstr(d[fieldname]).strip()=="": elif df.get("unique") and cstr(d[fieldname]).strip()=="":
# unique empty field should be set to None # unique empty field should be set to None
d[fieldname] = None d[fieldname] = None
@@ -270,11 +270,11 @@ class BaseDocument(object):
self.name = None self.name = None
self.db_insert() self.db_insert()
return return
type, value, traceback = sys.exc_info() type, value, traceback = sys.exc_info()
frappe.msgprint(_("Duplicate name {0} {1}").format(self.doctype, self.name)) frappe.msgprint(_("Duplicate name {0} {1}").format(self.doctype, self.name))
raise frappe.DuplicateEntryError, (self.doctype, self.name, e), traceback raise frappe.DuplicateEntryError, (self.doctype, self.name, e), traceback
elif "Duplicate" in cstr(e.args[1]): elif "Duplicate" in cstr(e.args[1]):
# unique constraint # unique constraint
self.show_unique_validation_message(e) self.show_unique_validation_message(e)
@@ -303,7 +303,7 @@ class BaseDocument(object):
self.show_unique_validation_message(e) self.show_unique_validation_message(e)
else: else:
raise raise
def show_unique_validation_message(self, e): def show_unique_validation_message(self, e):
type, value, traceback = sys.exc_info() type, value, traceback = sys.exc_info()
fieldname = str(e).split("'")[-2] fieldname = str(e).split("'")[-2]


+ 1
- 1
frappe/model/db_query.py Wyświetl plik

@@ -238,7 +238,7 @@ class DatabaseQuery(object):


elif f[2] == "like" or (isinstance(f[3], basestring) and elif f[2] == "like" or (isinstance(f[3], basestring) and
(not df or df.fieldtype not in ["Float", "Int", "Currency", "Percent", "Check"])): (not df or df.fieldtype not in ["Float", "Int", "Currency", "Percent", "Check"])):
if f[2] == "like":
if f[2] == "like" and isinstance(f[3], basestring):
# because "like" uses backslash (\) for escaping # because "like" uses backslash (\) for escaping
f[3] = f[3].replace("\\", "\\\\") f[3] = f[3].replace("\\", "\\\\")




+ 4
- 0
frappe/patches.txt Wyświetl plik

@@ -85,3 +85,7 @@ execute:frappe.permissions.reset_perms("DocType")
execute:frappe.db.sql("delete from `tabProperty Setter` where `property` = 'idx'") execute:frappe.db.sql("delete from `tabProperty Setter` where `property` = 'idx'")
frappe.patches.v5_2.change_checks_to_not_null frappe.patches.v5_2.change_checks_to_not_null
frappe.patches.v5_3.rename_chinese_languages frappe.patches.v5_3.rename_chinese_languages

frappe.patches.v6_0.make_task_log_folder
frappe.patches.v6_0.document_type_rename
frappe.patches.v6_0.fix_ghana_currency

+ 0
- 0
frappe/patches/v6_0/__init__.py Wyświetl plik


+ 7
- 0
frappe/patches/v6_0/document_type_rename.py Wyświetl plik

@@ -0,0 +1,7 @@
import frappe

def execute():
frappe.db.sql("""update tabDocType set document_type='Document'
where document_type='Transaction'""")
frappe.db.sql("""update tabDocType set document_type='Setup'
where document_type='Master'""")

+ 7
- 0
frappe/patches/v6_0/fix_ghana_currency.py Wyświetl plik

@@ -0,0 +1,7 @@

def execute():
from frappe.geo.country_info import get_all
import frappe.utils.install

countries = get_all()
frappe.utils.install.add_country_and_currency("Ghana", frappe._dict(countries["Ghana"]))

+ 6
- 0
frappe/patches/v6_0/make_task_log_folder.py Wyświetl plik

@@ -0,0 +1,6 @@
import frappe.utils, os

def execute():
path = frappe.utils.get_site_path('task-logs')
if not os.path.exists(path):
os.makedirs(path)

+ 4
- 1
frappe/public/build.json Wyświetl plik

@@ -15,7 +15,8 @@
"public/js/lib/moment/moment.min.js", "public/js/lib/moment/moment.min.js",
"public/js/lib/highlight.pack.js", "public/js/lib/highlight.pack.js",
"public/js/frappe/class.js", "public/js/frappe/class.js",
"website/js/website.js"
"website/js/website.js",
"public/js/lib/socket.io.min.js"
], ],
"js/editor.min.js": [ "js/editor.min.js": [
"public/js/lib/jquery/jquery.hotkeys.js", "public/js/lib/jquery/jquery.hotkeys.js",
@@ -49,6 +50,7 @@
"public/js/lib/nprogress.js", "public/js/lib/nprogress.js",
"public/js/lib/moment/moment-with-locales.min.js", "public/js/lib/moment/moment-with-locales.min.js",
"public/js/lib/moment/moment-timezone-with-data.min.js", "public/js/lib/moment/moment-timezone-with-data.min.js",
"public/js/lib/socket.io.min.js",


"public/js/frappe/provide.js", "public/js/frappe/provide.js",
"public/js/frappe/class.js", "public/js/frappe/class.js",
@@ -61,6 +63,7 @@
"public/js/frappe/ui/messages.js", "public/js/frappe/ui/messages.js",


"public/js/frappe/request.js", "public/js/frappe/request.js",
"public/js/frappe/socket.js",
"public/js/frappe/router.js", "public/js/frappe/router.js",
"public/js/frappe/defaults.js", "public/js/frappe/defaults.js",
"public/js/lib/microtemplate.js", "public/js/lib/microtemplate.js",


+ 4
- 0
frappe/public/css/common.css Wyświetl plik

@@ -215,3 +215,7 @@ a.no-decoration:active {
text-align: center; text-align: center;
} }
} }
.grayscale {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}

+ 4
- 0
frappe/public/css/desk.css Wyświetl plik

@@ -215,6 +215,10 @@ a.no-decoration:active {
text-align: center; text-align: center;
} }
} }
.grayscale {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}
.nav-pills a, .nav-pills a,
.nav-pills a:hover { .nav-pills a:hover {
border-bottom: none; border-bottom: none;


+ 12
- 0
frappe/public/css/desktop.css Wyświetl plik

@@ -161,3 +161,15 @@ body[data-route="desktop"] .navbar-default {
margin-top: 3px; margin-top: 3px;
margin-bottom: 3px; margin-bottom: 3px;
} }
.desktop-list-item {
padding: 10px 15px;
border-bottom: 1px solid #d1d8dd;
cursor: pointer;
}
.desktop-list-item:hover,
.desktop-list-item:focus {
background-color: #f7fafc;
}
.desktop-list-item h4 {
display: inline-block;
}

+ 4
- 0
frappe/public/css/form.css Wyświetl plik

@@ -157,6 +157,10 @@ select.form-control {
-moz-appearance: none; -moz-appearance: none;
appearance: none; appearance: none;
} }
.form-control.bold {
font-weight: bold;
background-color: #fffce7;
}
.form-headline .alert { .form-headline .alert {
font-size: 12px; font-size: 12px;
border-color: #d1d8dd; border-color: #d1d8dd;


+ 36
- 10
frappe/public/css/mobile.css Wyświetl plik

@@ -166,6 +166,9 @@ body {
.timeline .timeline-item:last-child { .timeline .timeline-item:last-child {
border-bottom: none; border-bottom: none;
} }
.list-row {
padding: 13px 15px !important;
}
.doclist-row { .doclist-row {
position: relative; position: relative;
padding-right: 10px; padding-right: 10px;
@@ -192,6 +195,15 @@ body {
.doclist-row .list-row-right { .doclist-row .list-row-right {
float: right; float: right;
} }
.doclist-row .list-row-right .list-row-indicator {
top: 4px;
}
.doclist-row .list-row-right .list-row-indicator .indicator::before,
.doclist-row .list-row-right .list-row-indicator .indicator::after {
height: 12px;
width: 12px;
border-radius: 12px;
}
.doclist-row .list-row-right.no-right-column { .doclist-row .list-row-right.no-right-column {
position: absolute; position: absolute;
top: 0; top: 0;
@@ -199,15 +211,6 @@ body {
left: -10px; left: -10px;
width: 100%; width: 100%;
} }
.doclist-row .list-row-right.no-right-column .list-row-indicator {
top: 5px;
}
.doclist-row .list-row-right.no-right-column .list-row-indicator .indicator::before,
.doclist-row .list-row-right.no-right-column .list-row-indicator .indicator::after {
height: 14px;
width: 14px;
border-radius: 14px;
}
body[data-route^="messages"] .navbar-center { body[data-route^="messages"] .navbar-center {
display: block !important; display: block !important;
position: absolute; position: absolute;
@@ -258,6 +261,26 @@ body {
} }
} }
@media (max-width: 991px) { @media (max-width: 991px) {
input[type='checkbox'] {
-webkit-appearance: none;
width: 12px;
height: 12px;
background: white;
border-radius: 6px;
border: 1px solid #d1d8dd;
display: inline-block;
}
input[type='checkbox']:checked {
background: #3b99fc;
border-color: #3b99fc;
}
input.list-select-all {
margin-top: 0px;
}
.input-area input[type='checkbox'] {
margin-top: 2px;
margin-left: -23px;
}
.intro-area, .intro-area,
.footnote-area { .footnote-area {
padding: 15px 0px; padding: 15px 0px;
@@ -269,7 +292,7 @@ body {
position: relative; position: relative;
} }
.page-head .page-title h1 { .page-head .page-title h1 {
font-size: 18px;
font-size: 22px;
margin-top: 22px; margin-top: 22px;
} }
body[data-route^="Form"] .page-title h1 { body[data-route^="Form"] .page-title h1 {
@@ -304,6 +327,9 @@ body {
.module-item { .module-item {
padding: 7px 0px !important; padding: 7px 0px !important;
} }
.module-item h4 {
font-weight: normal;
}
#navbar-breadcrumbs { #navbar-breadcrumbs {
margin: 0px; margin: 0px;
display: inline-block; display: inline-block;


+ 4
- 0
frappe/public/css/website.css Wyświetl plik

@@ -215,6 +215,10 @@ a.no-decoration:active {
text-align: center; text-align: center;
} }
} }
.grayscale {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}
html { html {
min-height: 100%; min-height: 100%;
} }


+ 0
- 4
frappe/public/js/frappe/desk.js Wyświetl plik

@@ -126,10 +126,6 @@ frappe.Application = Class.extend({


if(frappe.get_route()[0] != "messages") { if(frappe.get_route()[0] != "messages") {
if(r.message.new_messages.length) { if(r.message.new_messages.length) {
$.each(r.message.new_messages, function(i, m) {
frappe.utils.notify(__("Message from {0}", [m.comment_by_fullname]),
m.comment);
});
frappe.utils.set_title_prefix("(" + r.message.new_messages.length + ")"); frappe.utils.set_title_prefix("(" + r.message.new_messages.length + ")");
} }
} }


+ 7
- 1
frappe/public/js/frappe/form/control.js Wyświetl plik

@@ -190,6 +190,12 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
</div>').appendTo(this.parent); </div>').appendTo(this.parent);
} }
}, },
toggle_label: function(show) {
this.$wrapper.find(".control-label").toggleClass("hide", !show);
},
toggle_description: function(show) {
this.$wrapper.find(".help-box").toggleClass("hide", !show);
},
set_input_areas: function() { set_input_areas: function() {
if(this.only_input) { if(this.only_input) {
this.input_area = this.wrapper; this.input_area = this.wrapper;
@@ -647,7 +653,7 @@ frappe.ui.form.ControlButton = frappe.ui.form.ControlData.extend({
this.input = this.$input.get(0); this.input = this.$input.get(0);
this.set_input_attributes(); this.set_input_attributes();
this.has_input = true; this.has_input = true;
this.$wrapper.find(".control-label").addClass("hide");
this.toggle_label(false);
}, },
onclick: function() { onclick: function() {
if(this.frm && this.frm.doc) { if(this.frm && this.frm.doc) {


+ 1
- 1
frappe/public/js/frappe/form/dashboard.js Wyświetl plik

@@ -42,7 +42,7 @@ frappe.ui.form.Dashboard = Class.extend({
var badge = $(repl('<div class="col-md-4">\ var badge = $(repl('<div class="col-md-4">\
<div class="alert-badge">\ <div class="alert-badge">\
<a class="badge-link grey">%(label)s</a>\ <a class="badge-link grey">%(label)s</a>\
<span class="badge">-</span>\
<span class="badge" style="margin-left: 10px;">-</span>\
</div></div>', {label:label, icon: frappe.boot.doctype_icons[doctype]})) </div></div>', {label:label, icon: frappe.boot.doctype_icons[doctype]}))
.appendTo(this.body) .appendTo(this.body)




+ 11
- 0
frappe/public/js/frappe/form/footer/assign_to.js Wyświetl plik

@@ -92,6 +92,7 @@ frappe.ui.form.AssignTo = Class.extend({
me.dialog = new frappe.ui.Dialog({ me.dialog = new frappe.ui.Dialog({
title: __('Add to To Do'), title: __('Add to To Do'),
fields: [ fields: [
{fieldtype:'Check', fieldname:'myself', label:__("Assign to me"), "default":0},
{fieldtype:'Link', fieldname:'assign_to', options:'User', {fieldtype:'Link', fieldname:'assign_to', options:'User',
label:__("Assign To"), label:__("Assign To"),
description:__("Add to To Do List Of"), reqd:true}, description:__("Add to To Do List Of"), reqd:true},
@@ -115,6 +116,16 @@ frappe.ui.form.AssignTo = Class.extend({
} }


me.dialog.show(); me.dialog.show();

me.dialog.get_input("myself").on("click", function() {
if($(this).prop("checked")) {
me.dialog.set_value("assign_to", user);
me.dialog.set_value("notify", 0);
} else {
me.dialog.set_value("assign_to", "");
me.dialog.set_value("notify", 1);
}
});
}, },
add_assignment: function() { add_assignment: function() {
var me = this; var me = this;


+ 6
- 1
frappe/public/js/frappe/form/footer/timeline.js Wyświetl plik

@@ -227,9 +227,14 @@ frappe.ui.form.Comments = Class.extend({
btn: btn, btn: btn,
callback: function(r) { callback: function(r) {
if(!r.exc) { if(!r.exc) {
var comment_exists = !!$.map(me.get_comments(), function(x) {
return x.name == r.message.name? true : undefined}).length;
me.input.val("");
if (comment_exists) {
return;
}
me.frm.get_docinfo().comments = me.frm.get_docinfo().comments =
me.get_comments().concat([r.message]); me.get_comments().concat([r.message]);
me.input.val("");
me.refresh(true); me.refresh(true);
} }
} }


+ 9
- 13
frappe/public/js/frappe/form/layout.js Wyświetl plik

@@ -44,24 +44,20 @@ frappe.ui.form.Layout = Class.extend({
$(this.frm.wrapper).trigger("refresh-fields"); $(this.frm.wrapper).trigger("refresh-fields");
} }


if (this.frm) {
// show empty form notification
setTimeout(function() {
me.page.find(".empty-form-alert").remove();
if(!(me.page.find(".frappe-control:visible").length)) {
$('<div class="empty-form-alert text-muted" style="padding: 15px;">'
+__("This form does not have any input")+'</div>')
.appendTo(me.page);
}
}, 100);
}

// dependent fields // dependent fields
this.refresh_dependency(); this.refresh_dependency();


// refresh sections // refresh sections
this.refresh_sections(); this.refresh_sections();
}, },
show_empty_form_message: function() {
this.wrapper.find(".empty-form-alert").remove();
if(!(this.wrapper.find(".frappe-control:visible").length)) {
$('<div class="empty-form-alert text-muted" style="padding: 15px;">'
+__("This form does not have any input")+'</div>')
.appendTo(this.page);
}
},
attach_doc_and_docfields: function(refresh) { attach_doc_and_docfields: function(refresh) {
var me = this; var me = this;
for(var i=0, l=this.fields_list.length; i<l; i++) { for(var i=0, l=this.fields_list.length; i<l; i++) {
@@ -85,7 +81,7 @@ frappe.ui.form.Layout = Class.extend({


this.section = null; this.section = null;
this.column = null; this.column = null;
if(this.fields[0] && this.fields[0].fieldtype!="Section Break") {
if((this.fields[0] && this.fields[0].fieldtype!="Section Break") || !this.fields.length) {
this.make_section(); this.make_section();
} }
$.each(this.fields, function(i, df) { $.each(this.fields, function(i, df) {


+ 38
- 22
frappe/public/js/frappe/form/save.js Wyświetl plik

@@ -18,21 +18,23 @@ frappe.ui.form.save = function(frm, action, callback, btn) {
var freeze_message = working_label ? __(working_label) : ""; var freeze_message = working_label ? __(working_label) : "";


var save = function() { var save = function() {
check_name();
if(check_mandatory()) {
_call({
method: "frappe.desk.form.save.savedocs",
args: { doc: frm.doc, action:action},
callback: function(r) {
$(document).trigger("save", [frm.doc]);
callback(r);
},
btn: btn,
freeze_message: freeze_message
});
} else {
$(btn).prop("disabled", false);
}
check_name(function() {
if(check_mandatory()) {
_call({
method: "frappe.desk.form.save.savedocs",
args: { doc: frm.doc, action:action},
callback: function(r) {
$(document).trigger("save", [frm.doc]);
callback(r);
},
btn: btn,
freeze_message: freeze_message
});
} else {
$(btn).prop("disabled", false);
}
});

}; };


var cancel = function() { var cancel = function() {
@@ -63,19 +65,33 @@ frappe.ui.form.save = function(frm, action, callback, btn) {
}); });
}; };


var check_name = function() {
var check_name = function(callback) {
var doc = frm.doc; var doc = frm.doc;
var meta = locals.DocType[doc.doctype]; var meta = locals.DocType[doc.doctype];
if(doc.__islocal && (meta && meta.autoname if(doc.__islocal && (meta && meta.autoname
&& meta.autoname.toLowerCase()=='prompt')) { && meta.autoname.toLowerCase()=='prompt')) {
var newname = prompt('Enter the name of the new '+ doc.doctype, '');
if(newname) {
doc.__newname = strip(newname);
} else {
msgprint(__("Name is required"));
var d = frappe.prompt(__("Name"), function(values) {
var newname = values.value;
if(newname) {
doc.__newname = strip(newname);
} else {
msgprint(__("Name is required"));
throw "name required";
}

callback();

}, __('Enter the name of the new {0}', [doc.doctype]), __("Create"));

if(doc.__newname) {
d.set_value("value", doc.__newname);
}

d.onhide = function() {
$(btn).prop("disabled", false); $(btn).prop("disabled", false);
throw "name required";
} }
} else {
callback();
} }
}; };




+ 3
- 0
frappe/public/js/frappe/form/toolbar.js Wyświetl plik

@@ -220,6 +220,9 @@ frappe.ui.form.Toolbar = Class.extend({
} else { } else {
var click = { var click = {
"Save": function() { "Save": function() {
if(!frappe.dom.is_touchscreen() && Math.random() < 0.25) {
show_alert(__("ProTip: You can also use Ctrl+S to Save"));
}
me.frm.save('Save', null, this); me.frm.save('Save', null, this);
}, },
"Submit": function() { "Submit": function() {


+ 5
- 0
frappe/public/js/frappe/list/doclistview.js Wyświetl plik

@@ -216,6 +216,11 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
refresh: function() { refresh: function() {
var me = this; var me = this;
this.init_stats(); this.init_stats();

if(this.listview.settings.refresh) {
this.listview.settings.refresh(this);
}

if(frappe.route_options) { if(frappe.route_options) {
me.set_route_options(); me.set_route_options();
} else if(me.dirty) { } else if(me.dirty) {


+ 2
- 4
frappe/public/js/frappe/list/list_item_row.html Wyświetl plik

@@ -1,5 +1,5 @@
<div class="row doclist-row {% if (data._checkbox) { %} has-checkbox {% } %}"> <div class="row doclist-row {% if (data._checkbox) { %} has-checkbox {% } %}">
<div class="{% if(right_column) { %} col-xs-12 {% } else { %} col-xs-10 {% } %}
<div class="col-xs-10
{% if (list.meta.title_field) { %} {% if (list.meta.title_field) { %}
col-sm-8 col-sm-8
{% } else { %} {% } else { %}
@@ -13,8 +13,7 @@
{% if (list.meta.title_field) { {% if (list.meta.title_field) {
var is_different = data.name !== data[list.meta.title_field]; var is_different = data.name !== data[list.meta.title_field];
%} %}
<div class="list-col col-sm-2 col-xs-10 text-right text-ellipsis rtl list-row-id
{% if (!is_different) { %} hidden-xs {% } %}">
<div class="list-col col-sm-2 hidden-xs text-right text-ellipsis rtl list-row-id">
{% if (is_different) { %} {% if (is_different) { %}
<a class="text-muted list-value" href="#Form/{%= data._doctype_encoded %}/{%= data._name_encoded %}"> <a class="text-muted list-value" href="#Form/{%= data._doctype_encoded %}/{%= data._name_encoded %}">
{%= data.name %}</a> {%= data.name %}</a>
@@ -24,7 +23,6 @@


<!-- comment --> <!-- comment -->
<div class="list-col col-sm-2 col-xs-2 <div class="list-col col-sm-2 col-xs-2
{% if(!right_column) { %} no-right-column {% } %}
text-right list-row-right"> text-right list-row-right">
<div class="visible-xs pull-right list-row-indicator">{%= list.get_indicator_dot(data) %}</div> <div class="visible-xs pull-right list-row-indicator">{%= list.get_indicator_dot(data) %}</div>
<div class="hidden-xs pull-right"> <div class="hidden-xs pull-right">


+ 1
- 1
frappe/public/js/frappe/list/list_item_row_head.html Wyświetl plik

@@ -1,6 +1,6 @@
<div class="list-row list-row-head"> <div class="list-row list-row-head">
<div class="row doclist-row"> <div class="row doclist-row">
<div class="col-xs-12
<div class="col-xs-10
{% if (list.meta.title_field) { %} {% if (list.meta.title_field) { %}
col-sm-8 col-sm-8
{% } else { %} {% } else { %}


+ 3
- 1
frappe/public/js/frappe/list/list_sidebar.html Wyświetl plik

@@ -6,7 +6,9 @@
<li><a href="#Report/{%= doctype %}">{%= __("Report") %}</a></li> <li><a href="#Report/{%= doctype %}">{%= __("Report") %}</a></li>
<li class="hide calendar-link"><a href="#Calendar/{%= doctype %}">{%= __("Calendar") %}</a></li> <li class="hide calendar-link"><a href="#Calendar/{%= doctype %}">{%= __("Calendar") %}</a></li>
<li class="hide gantt-link"><a href="#Gantt/{%= doctype %}">{%= __("Gantt") %}</a></li> <li class="hide gantt-link"><a href="#Gantt/{%= doctype %}">{%= __("Gantt") %}</a></li>
<li><a onclick="cur_list.assigned_to_me()">{%= __("Assigned To Me") %}</a></li>
<li class="assigned-to-me">
<a>{%= __("Assigned To Me") %}</a>
</li>
{% if(frappe.help.has_help(doctype)) { %} {% if(frappe.help.has_help(doctype)) { %}
<li><a class="help-link" data-doctype="{{ doctype }}">{{ __("Help") }}</a></li> <li><a class="help-link" data-doctype="{{ doctype }}">{{ __("Help") }}</a></li>
{% } %} {% } %}


+ 13
- 1
frappe/public/js/frappe/list/list_sidebar.js Wyświetl plik

@@ -12,8 +12,8 @@ frappe.provide('frappe.views');
frappe.views.ListSidebar = Class.extend({ frappe.views.ListSidebar = Class.extend({
init: function(opts) { init: function(opts) {
$.extend(this, opts); $.extend(this, opts);
this.get_stats();
this.make(); this.make();
this.get_stats();
}, },
make: function() { make: function() {
var sidebar_content = frappe.render_template("list_sidebar", {doctype: this.doclistview.doctype}); var sidebar_content = frappe.render_template("list_sidebar", {doctype: this.doclistview.doctype});
@@ -25,10 +25,22 @@ frappe.views.ListSidebar = Class.extend({


this.sidebar = this.page_sidebar.add(this.offcanvas_list_sidebar); this.sidebar = this.page_sidebar.add(this.offcanvas_list_sidebar);


this.setup_assigned_to_me();

if(frappe.views.calendar[this.doctype]) { if(frappe.views.calendar[this.doctype]) {
this.sidebar.find(".calendar-link, .gantt-link").removeClass("hide"); this.sidebar.find(".calendar-link, .gantt-link").removeClass("hide");
} }
}, },
setup_assigned_to_me: function() {
var me = this;
this.page.sidebar.find(".assigned-to-me a").on("click", function() {
me.doclistview.assigned_to_me();
});

this.offcanvas_list_sidebar.find(".assigned-to-me a").on("click", function() {
me.doclistview.assigned_to_me();
});
},
get_stats: function() { get_stats: function() {
var me = this var me = this
return frappe.call({ return frappe.call({


+ 1
- 1
frappe/public/js/frappe/misc/user.js Wyświetl plik

@@ -62,7 +62,7 @@ frappe.get_gravatar = function(email_id) {
frappe.ui.set_user_background = function(src, selector, style) { frappe.ui.set_user_background = function(src, selector, style) {
if(!selector) selector = "#page-desktop"; if(!selector) selector = "#page-desktop";
if(!style) style = "Fill Screen"; if(!style) style = "Fill Screen";
if(!src) src = frappe.boot.default_background_image;
if(!src) src = frappe.urllib.get_full_url(frappe.boot.default_background_image);


frappe.dom.set_style(repl('%(selector)s { \ frappe.dom.set_style(repl('%(selector)s { \
background: url("%(src)s") center center;\ background: url("%(src)s") center center;\


+ 12
- 1
frappe/public/js/frappe/request.js Wyświetl plik

@@ -28,10 +28,21 @@ frappe.call = function(opts) {
args.cmd = opts.method; args.cmd = opts.method;
} }


var callback = function(data, xhr) {
if(data.task_id) {
// async call, subscribe
frappe.socket.subscribe(data.task_id, opts);
}
else {
// ajax
return opts.callback(data, xhr);
}
}

return frappe.request.call({ return frappe.request.call({
type: opts.type || "POST", type: opts.type || "POST",
args: args, args: args,
success: opts.callback,
success: callback,
error: opts.error, error: opts.error,
always: opts.always, always: opts.always,
btn: opts.btn, btn: opts.btn,


+ 114
- 0
frappe/public/js/frappe/socket.js Wyświetl plik

@@ -0,0 +1,114 @@
frappe.socket = {
open_tasks: {},
init: function() {
if (frappe.boot.no_async) {
return;
}
var socketio_server = frappe.boot.dev_server? '//' + document.domain + ':3000' : '//' + document.domain + ':' + window.location.port;
frappe.socket.socket = io.connect(socketio_server);
frappe.socket.socket.on('msgprint', function(message) {
frappe.msgprint(message)
});

frappe.socket.setup_listeners();
frappe.socket.setup_reconnect();
$(document).on('form-load', function(e, frm) {
frappe.socket.doc_subscribe(frm.doctype, frm.docname);
});

$(document).on('form-unload', function(e, frm) {
frappe.socket.doc_unsubscribe(frm.doctype, frm.docname);
});
},
subscribe: function(task_id, opts) {
frappe.socket.socket.emit('task_subscribe', task_id);
frappe.socket.socket.emit('progress_subscribe', task_id);

frappe.socket.open_tasks[task_id] = opts;
},
doc_subscribe: function(doctype, docname) {
frappe.socket.socket.emit('doc_subscribe', doctype, docname);
frappe.socket.open_doc = {doctype: doctype, docname: docname};
},
doc_unsubscribe: function(doctype, docname) {
frappe.socket.socket.emit('doc_unsubscribe', doctype, docname);
frappe.socket.open_doc = null;
},
setup_listeners: function() {
frappe.socket.socket.on('task_status_change', function(data) {
if(data.status==="Running") {
frappe.socket.process_response(data, "running");
} else {
// failed or finished
frappe.socket.process_response(data, "callback");
// delete frappe.socket.open_tasks[data.task_id];
}
});
frappe.socket.socket.on('task_progress', function(data) {
frappe.socket.process_response(data, "progress");
});
frappe.socket.socket.on('new_comment', function(comment) {
if (frappe.model.docinfo[comment.comment_doctype] && frappe.model.docinfo[comment.comment_doctype][comment.comment_docname]) {
var comments = frappe.model.docinfo[comment.comment_doctype][comment.comment_docname].comments
var comment_exists = !!$.map(comments, function(x) { return x.name == comment.name? true : undefined}).length
if (!comment_exists) {
frappe.model.docinfo[comment.comment_doctype][comment.comment_docname].comments = comments.concat([comment]);
}
}
if (cur_frm.doctype === comment.comment_doctype && cur_frm.docname === comment.comment_docname) {
cur_frm.comments.refresh();
}
});
frappe.socket.socket.on('new_message', function(comment) {
frappe.utils.notify(__("Message from {0}", [comment.comment_by_fullname]), comment.comment);
if ($(cur_page.page).data('page-route') === 'messages') {
var current_contact = $(cur_page.page).find('[data-contact]').data('contact');
var on_broadcast_page = current_contact === user;
if (current_contact == comment.owner || (on_broadcast_page && comment.broadcast)) {
var $row = $('<div class="list-row"/>');
frappe.desk.pages.messages.list.data.unshift(comment);
frappe.desk.pages.messages.list.render_row($row, comment);
frappe.desk.pages.messages.list.parent.prepend($row);
}
}
else {
}
});
},
setup_reconnect: function() {
// subscribe again to open_tasks
frappe.socket.socket.on("connect", function() {
$.each(frappe.socket.open_tasks, function(task_id, opts) {
frappe.socket.subscribe(task_id, opts);
});
});

if(frappe.socket.open_doc) {
frappe.socket.doc_subscribe(frappe.socket.open_doc.doctype, frappe.socket.open_doc.docname);
}
},
process_response: function(data, method) {
if(!data) {
return;
}

// success
if(data) {
var opts = frappe.socket.open_tasks[data.task_id];
if(opts[method]) opts[method](data);
}

// always
frappe.request.cleanup(opts, data);
if(opts.always) {
opts.always(data);
}

// error
if(data.status_code && data.status_code > 400 && opts.error) {
opts.error(data);
}
}
}

$(frappe.socket.init);

+ 12
- 10
frappe/public/js/frappe/ui/dialog.js Wyświetl plik

@@ -48,19 +48,21 @@ frappe.ui.Dialog = frappe.ui.FieldGroup.extend({
me.display = true; me.display = true;
cur_dialog = me; cur_dialog = me;
frappe.ui.open_dialogs.push(me); frappe.ui.open_dialogs.push(me);
var first = $(me.body).find('.modal-content :input:first');
if(first.length && first.attr("data-fieldtype")!="Date") {
try {
first.get(0).focus();
} catch(e) {
console.log("Dialog: unable to focus on first input: " + e);
}
}
me.focus_on_first_input();
me.on_page_show && me.on_page_show(); me.on_page_show && me.on_page_show();
})

});


}, },
focus_on_first_input: function() {
var first = $(this.body).find(':input:first');
if(first.length && first.attr("data-fieldtype")!="Date") {
try {
first.get(0).focus();
} catch(e) {
console.log("Dialog: unable to focus on first input: " + e);
}
}
},
get_primary_btn: function() { get_primary_btn: function() {
return this.$wrapper.find(".modal-header .btn-primary"); return this.$wrapper.find(".modal-header .btn-primary");
}, },


+ 8
- 0
frappe/public/js/frappe/ui/messages.js Wyświetl plik

@@ -44,6 +44,14 @@ frappe.confirm = function(message, ifyes, ifno) {
} }


frappe.prompt = function(fields, callback, title, primary_label) { frappe.prompt = function(fields, callback, title, primary_label) {
if (typeof fields === "string") {
fields = [{
label: fields,
fieldname: "value",
fieldtype: "Data",
reqd: 1
}];
}
if(!$.isArray(fields)) fields = [fields]; if(!$.isArray(fields)) fields = [fields];
var d = new frappe.ui.Dialog({ var d = new frappe.ui.Dialog({
fields: fields, fields: fields,


+ 15
- 0
frappe/public/js/frappe/ui/page.js Wyświetl plik

@@ -232,6 +232,21 @@ frappe.ui.Page = Class.extend({
.on("click", action).appendTo(this.inner_toolbar.removeClass("hide")) .on("click", action).appendTo(this.inner_toolbar.removeClass("hide"))
}, },


//-- Sidebar --//

add_sidebar_item: function(label, action, insert_after) {
var parent = this.sidebar.find(".sidebar-menu.standard-actions");
var li = $('<li>');
var link = $('<a>').html(label).on("click", action).appendTo(li);

if(insert_after) {
li.insertAfter(parent.find(insert_after));
} else {
li.appendTo(parent);
}
return link;
},

//---// //---//


clear_user_actions: function() { clear_user_actions: function() {


+ 2
- 2
frappe/public/js/frappe/ui/toolbar/about.js Wyświetl plik

@@ -33,8 +33,8 @@ frappe.ui.misc.about = function() {
var $wrap = $("#about-app-versions").empty(); var $wrap = $("#about-app-versions").empty();
$.each(keys(versions).sort(), function(i, key) { $.each(keys(versions).sort(), function(i, key) {
var v = versions[key]; var v = versions[key];
$($.format('<p><b>{0}:</b> v{1}<br><span class="text-muted">{2}</span></p>',
[v.title, v.version, v.description])).appendTo($wrap);
$($.format('<p><b>{0}:</b> v{1}<br></p>',
[v.title, v.version])).appendTo($wrap);
}); });


frappe.versions = versions; frappe.versions = versions;


+ 0
- 1
frappe/public/js/frappe/ui/toolbar/toolbar.js Wyświetl plik

@@ -126,7 +126,6 @@ frappe.ui.toolbar.update_notifications = function() {
frappe.views.show_open_count_list(this); frappe.views.show_open_count_list(this);
} }
} }
return false;
}); });


$(".navbar-new-comments") $(".navbar-new-comments")


+ 5
- 0
frappe/public/js/frappe/upload.js Wyświetl plik

@@ -93,6 +93,11 @@ frappe.upload = {
opts.callback(attachment, r); opts.callback(attachment, r);
$(document).trigger("upload_complete", attachment); $(document).trigger("upload_complete", attachment);
}, },
error: function(r) {
// if no onerror, assume callback will handle errors
opts.onerror ? opts.onerror(r) : opts.callback(null, null, r);
return;
},
btn: opts.btn btn: opts.btn
}); });
} }


+ 18
- 4
frappe/public/js/frappe/views/communication.js Wyświetl plik

@@ -130,11 +130,25 @@ frappe.views.CommunicationComposer = Class.extend({
this.dialog.get_input("standard_reply").on("change", function() { this.dialog.get_input("standard_reply").on("change", function() {
var standard_reply = $(this).val(); var standard_reply = $(this).val();
var prepend_reply = function() { var prepend_reply = function() {
if(me.reply_added===standard_reply) {
return;
}
var content_field = me.dialog.fields_dict.content; var content_field = me.dialog.fields_dict.content;
var content = content_field.get_value() || ""; var content = content_field.get_value() || "";
content_field.set_input(
frappe.standard_replies[standard_reply]
+ "<br><br>" + content);

parts = content.split('<!-- salutation-ends -->');

if(parts.length===2) {
content = [parts[0], frappe.standard_replies[standard_reply],
"<br>", parts[1]];
} else {
content = [frappe.standard_replies[standard_reply],
"<br>", content];
}

content_field.set_input(content.join(''));

me.reply_added = standard_reply;
} }
if(frappe.standard_replies[standard_reply]) { if(frappe.standard_replies[standard_reply]) {
prepend_reply(); prepend_reply();
@@ -369,7 +383,7 @@ frappe.views.CommunicationComposer = Class.extend({


if(this.real_name) { if(this.real_name) {
this.message = '<p>'+__('Dear') +' ' this.message = '<p>'+__('Dear') +' '
+ this.real_name + ",</p>" + (this.message || "");
+ this.real_name + ",</p><!-- salutation-ends --><br>" + (this.message || "");
} }


var reply = (this.message || "") var reply = (this.message || "")


+ 1
- 1
frappe/public/js/frappe/views/module/module_section.html Wyświetl plik

@@ -7,7 +7,7 @@
{% if (item.type==="doctype") { %} {% if (item.type==="doctype") { %}
<span class="open-notification hide" data-doctype="{%= item.name %}"></span> <span class="open-notification hide" data-doctype="{%= item.name %}"></span>
{% } %} {% } %}
<p class="text-muted small module-item-description">{%= item.description %}</p>
<p class="text-muted small module-item-description hidden-xs">{%= item.description %}</p>
</div> </div>
<div class="col-xs-4 text-muted text-right small" style="padding-top: 5px;"> <div class="col-xs-4 text-muted text-right small" style="padding-top: 5px;">
{% if (item.last_modified) { %} {% if (item.last_modified) { %}


+ 12
- 4
frappe/public/js/frappe/views/reports/reportview.js Wyświetl plik

@@ -97,9 +97,9 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
parent: this.page.main, parent: this.page.main,
start: 0, start: 0,
show_filters: true, show_filters: true,
new_doctype: this.doctype,
allow_delete: true, allow_delete: true,
}); });
this.make_new_and_refresh();
this.make_delete(); this.make_delete();
this.make_column_picker(); this.make_column_picker();
this.make_sorter(); this.make_sorter();
@@ -109,8 +109,16 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
this.make_save(); this.make_save();
this.make_user_permissions(); this.make_user_permissions();
this.set_tag_and_status_filter(); this.set_tag_and_status_filter();
this.page.add_menu_item(__("Refresh"), function() {
me.refresh();
},

make_new_and_refresh: function() {
var me = this;
this.page.set_primary_action(__("Refresh"), function() {
me.run();
});

this.page.add_menu_item(__("New {0}", [this.doctype]), function() {
new_doc(me.doctype);
}, true); }, true);
}, },


@@ -244,7 +252,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({


if(docfield.fieldtype==="Link" && docfield.fieldname!=="name") { if(docfield.fieldtype==="Link" && docfield.fieldname!=="name") {
docfield.link_onclick = docfield.link_onclick =
repl('frappe.container.page.reportview.set_filter("%(fieldname)s", "%(value)s").page.reportview.run()',
repl('frappe.container.page.reportview.set_filter("%(fieldname)s", "%(value)s").run()',
{fieldname:docfield.fieldname, value:value}); {fieldname:docfield.fieldname, value:value});
} }
return frappe.format(value, docfield, {for_print: for_print}, dataContext); return frappe.format(value, docfield, {for_print: for_print}, dataContext);


+ 7
- 2
frappe/public/js/legacy/form.js Wyświetl plik

@@ -273,7 +273,7 @@ _f.Frm.prototype.rename_notify = function(dt, old, name) {
return; return;


// cleanup // cleanup
if(this && this.opendocs[old]) {
if(this && this.opendocs[old] && frappe.meta.docfield_copy[dt]) {
// delete docfield copy // delete docfield copy
frappe.meta.docfield_copy[dt][name] = frappe.meta.docfield_copy[dt][old]; frappe.meta.docfield_copy[dt][name] = frappe.meta.docfield_copy[dt][old];
delete frappe.meta.docfield_copy[dt][old]; delete frappe.meta.docfield_copy[dt][old];
@@ -391,8 +391,13 @@ _f.Frm.prototype.refresh = function(docname) {
// load the record for the first time, if not loaded (call 'onload') // load the record for the first time, if not loaded (call 'onload')
cur_frm.cscript.is_onload = false; cur_frm.cscript.is_onload = false;
if(!this.opendocs[this.docname]) { if(!this.opendocs[this.docname]) {
var me = this;
cur_frm.cscript.is_onload = true; cur_frm.cscript.is_onload = true;
this.setnewdoc(); this.setnewdoc();
$(document).trigger("form-load", [this]);
$(this.page.wrapper).on('hide', function(e) {
$(document).trigger("form-unload", [me]);
})
} else { } else {
this.render_form(is_a_different_doc); this.render_form(is_a_different_doc);
} }
@@ -442,13 +447,13 @@ _f.Frm.prototype.render_form = function(is_a_different_doc) {
first.focus(); first.focus();
} }
} }

} else { } else {
this.refresh_header(is_a_different_doc); this.refresh_header(is_a_different_doc);
} }


$(cur_frm.wrapper).trigger('render_complete'); $(cur_frm.wrapper).trigger('render_complete');


this.layout.show_empty_form_message();
} }


_f.Frm.prototype.refresh_field = function(fname) { _f.Frm.prototype.refresh_field = function(fname) {


+ 7000
- 0
frappe/public/js/lib/socket.io.min.js
Plik diff jest za duży
Wyświetl plik


+ 5
- 0
frappe/public/less/common.less Wyświetl plik

@@ -232,3 +232,8 @@ a.no-decoration& {
text-align: center; text-align: center;
} }
} }

.grayscale {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}

+ 18
- 0
frappe/public/less/desktop.less Wyświetl plik

@@ -200,3 +200,21 @@ body[data-route=""] .navbar-default, body[data-route="desktop"] .navbar-default
margin-bottom: 3px; margin-bottom: 3px;
} }
} }

.desktop-list {

}

.desktop-list-item& {
padding: 10px 15px;
border-bottom: 1px solid @border-color;
cursor: pointer;

&:hover, &:focus {
background-color: @panel-bg;
}

h4 {
display: inline-block;
}
}

+ 5
- 0
frappe/public/less/form.less Wyświetl plik

@@ -205,6 +205,11 @@ select.form-control {
appearance: none; appearance: none;
} }


.form-control.bold {
font-weight: bold;
background-color: @light-yellow;
}

.form-headline .alert { .form-headline .alert {
font-size: @text-medium; font-size: @text-medium;
border-color: @border-color; border-color: @border-color;


+ 43
- 12
frappe/public/less/mobile.less Wyświetl plik

@@ -157,6 +157,10 @@
} }


// listviews // listviews
.list-row {
padding: 13px 15px !important;
}

.doclist-row& { .doclist-row& {
position: relative; position: relative;
padding-right: 10px; padding-right: 10px;
@@ -187,6 +191,19 @@


.list-row-right { .list-row-right {
float: right; float: right;

.list-row-indicator {
top: 4px;

// bigger indicators for list
.indicator::before, .indicator::after {
height: 12px;
width: 12px;
border-radius: 12px;
}

}

} }


.list-row-right.no-right-column { .list-row-right.no-right-column {
@@ -196,17 +213,6 @@
left: -10px; left: -10px;
width: 100%; width: 100%;


.list-row-indicator {
top: 5px;

// bigger indicators for list
.indicator::before, .indicator::after {
height: 14px;
width: 14px;
border-radius: 14px;
}

}
} }
} }


@@ -273,6 +279,27 @@
} }


@media(max-width: 991px) { @media(max-width: 991px) {
input[type='checkbox'] {
-webkit-appearance:none;
width: 12px;
height: 12px;
background: white;
border-radius: 6px;
border: 1px solid @border-color;
display: inline-block;
}
input[type='checkbox']:checked {
background: @checkbox-color;
border-color: @checkbox-color;
}
input.list-select-all {
margin-top: 0px;
}
.input-area input[type='checkbox'] {
margin-top: 2px;
margin-left: -23px;
}

.intro-area, .intro-area,
.footnote-area { .footnote-area {
padding: 15px 0px; padding: 15px 0px;
@@ -288,7 +315,7 @@


.page-head { .page-head {
.page-title h1 { .page-title h1 {
font-size: 18px;
font-size: 22px;
margin-top: 22px; margin-top: 22px;
} }
} }
@@ -334,6 +361,10 @@


.module-item { .module-item {
padding: 7px 0px !important; padding: 7px 0px !important;

h4 {
font-weight: normal;
}
} }


#navbar-breadcrumbs { #navbar-breadcrumbs {


+ 2
- 0
frappe/public/less/variables.less Wyświetl plik

@@ -32,3 +32,5 @@
@label-info-bg: #E8DDFF; @label-info-bg: #E8DDFF;
@label-warning-bg: #FFE6BF; @label-warning-bg: #FFE6BF;
@label-danger-bg: #FFDCDC; @label-danger-bg: #FFDCDC;

@checkbox-color: #3b99fc;

+ 4
- 1
frappe/sessions.py Wyświetl plik

@@ -17,6 +17,7 @@ import frappe.defaults
import frappe.translate import frappe.translate
from frappe.utils.change_log import get_change_log from frappe.utils.change_log import get_change_log
import redis import redis
import os
from urllib import unquote from urllib import unquote


@frappe.whitelist() @frappe.whitelist()
@@ -124,6 +125,8 @@ def get():
frappe.get_attr(hook)(bootinfo=bootinfo) frappe.get_attr(hook)(bootinfo=bootinfo)


bootinfo["lang"] = frappe.translate.get_user_lang() bootinfo["lang"] = frappe.translate.get_user_lang()
bootinfo["dev_server"] = os.environ.get('DEV_SERVER', False)
bootinfo["no_async"] = frappe.conf.no_async
return bootinfo return bootinfo


class Session: class Session:
@@ -163,7 +166,7 @@ class Session:
"full_name": self.full_name, "full_name": self.full_name,
"user_type": self.user_type, "user_type": self.user_type,
"device": self.device, "device": self.device,
"session_country": get_geo_ip_country(frappe.local.request_ip)
"session_country": get_geo_ip_country(frappe.local.request_ip) if frappe.local.request_ip else None
}) })


# insert session # insert session


+ 58
- 3
frappe/tasks.py Wyświetl plik

@@ -4,9 +4,13 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils.scheduler import enqueue_events from frappe.utils.scheduler import enqueue_events
from frappe.celery_app import get_celery, celery_task, task_logger, LONGJOBS_PREFIX
from frappe.celery_app import get_celery, celery_task, task_logger, LONGJOBS_PREFIX, ASYNC_TASKS_PREFIX
from frappe.utils import get_sites from frappe.utils import get_sites
from frappe.utils.file_lock import create_lock, delete_lock from frappe.utils.file_lock import create_lock, delete_lock
from frappe.handler import execute_cmd
from frappe.async import set_task_status, END_LINE, get_std_streams
import frappe.utils.response
import sys
import time import time
import MySQLdb import MySQLdb


@@ -14,7 +18,7 @@ import MySQLdb
def sync_queues(): def sync_queues():
"""notifies workers to monitor newly added sites""" """notifies workers to monitor newly added sites"""
app = get_celery() app = get_celery()
shortjob_workers, longjob_workers = get_workers(app)
shortjob_workers, longjob_workers, async_tasks_workers = get_workers(app)


if shortjob_workers: if shortjob_workers:
for worker in shortjob_workers: for worker in shortjob_workers:
@@ -24,18 +28,25 @@ def sync_queues():
for worker in longjob_workers: for worker in longjob_workers:
sync_worker(app, worker, prefix=LONGJOBS_PREFIX) sync_worker(app, worker, prefix=LONGJOBS_PREFIX)


if async_tasks_workers:
for worker in async_tasks_workers:
sync_worker(app, worker, prefix=ASYNC_TASKS_PREFIX)

def get_workers(app): def get_workers(app):
longjob_workers = [] longjob_workers = []
shortjob_workers = [] shortjob_workers = []
async_tasks_workers = []


active_queues = app.control.inspect().active_queues() active_queues = app.control.inspect().active_queues()
for worker in active_queues: for worker in active_queues:
if worker.startswith(LONGJOBS_PREFIX): if worker.startswith(LONGJOBS_PREFIX):
longjob_workers.append(worker) longjob_workers.append(worker)
elif worker.startswith(ASYNC_TASKS_PREFIX):
async_tasks_workers.append(worker)
else: else:
shortjob_workers.append(worker) shortjob_workers.append(worker)


return shortjob_workers, longjob_workers
return shortjob_workers, longjob_workers, async_tasks_workers


def sync_worker(app, worker, prefix=''): def sync_worker(app, worker, prefix=''):
active_queues = set(get_active_queues(app, worker)) active_queues = set(get_active_queues(app, worker))
@@ -125,6 +136,50 @@ def pull_from_email_account(site, email_account):
finally: finally:
frappe.destroy() frappe.destroy()


@celery_task(bind=True)
def run_async_task(self, site, user, cmd, form_dict):
ret = {}
frappe.init(site)
frappe.connect()
sys.stdout, sys.stderr = get_std_streams(self.request.id)
frappe.local.stdout, frappe.local.stderr = sys.stdout, sys.stderr
frappe.local.task_id = self.request.id
frappe.cache()
try:
set_task_status(self.request.id, "Running")
frappe.db.commit()
frappe.set_user(user)
# sleep(60)
frappe.local.form_dict = frappe._dict(form_dict)
execute_cmd(cmd, async=True)
ret = frappe.local.response
except Exception, e:
frappe.db.rollback()
if not frappe.flags.in_test:
frappe.db.commit()

ret = frappe.local.response
http_status_code = getattr(e, "http_status_code", 500)
ret['status_code'] = http_status_code
frappe.errprint(frappe.get_traceback())
frappe.utils.response.make_logs()
set_task_status(self.request.id, "Failed", response=ret)
task_logger.error('Exception in running {}: {}'.format(cmd, ret['exc']))
else:
set_task_status(self.request.id, "Finished", response=ret)
if not frappe.flags.in_test:
frappe.db.commit()
finally:
sys.stdout.write('\n' + END_LINE)
sys.stderr.write('\n' + END_LINE)
if not frappe.flags.in_test:
frappe.destroy()
sys.stdout.close()
sys.stderr.close()
sys.stdout, sys.stderr = 1, 0
return ret


@celery_task() @celery_task()
def sendmail(site, communication_name, print_html=None, print_format=None, attachments=None, recipients=None, except_recipient=False): def sendmail(site, communication_name, print_html=None, print_format=None, attachments=None, recipients=None, except_recipient=False):
try: try:


+ 17
- 0
frappe/tests/test_async.py Wyświetl plik

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-

# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt

from __future__ import unicode_literals
import unittest
import frappe

from frappe.tasks import run_async_task

class TestAsync(unittest.TestCase):
def test_response(self):
result = run_async_task.delay(frappe.local.site, 'Administrator', 'async_ping',
frappe._dict())
result = result.get()
self.assertEquals(result.get("message"), "pong")

+ 5
- 5
frappe/tests/test_data_import.py Wyświetl plik

@@ -35,7 +35,7 @@ class TestDataImport(unittest.TestCase):
exporter.get_template("Blog Category", all_doctypes="No", with_data="No") exporter.get_template("Blog Category", all_doctypes="No", with_data="No")
content = read_csv_content(frappe.response.result) content = read_csv_content(frappe.response.result)
content.append(["", "", "test-category", "Test Cateogry"]) content.append(["", "", "test-category", "Test Cateogry"])
importer.upload(content)
importer.upload.queue(content)
self.assertTrue(frappe.db.get_value("Blog Category", "test-category", "title"), "Test Category") self.assertTrue(frappe.db.get_value("Blog Category", "test-category", "title"), "Test Category")


# export with data # export with data
@@ -44,7 +44,7 @@ class TestDataImport(unittest.TestCase):


# overwrite # overwrite
content[-1][3] = "New Title" content[-1][3] = "New Title"
importer.upload(content, overwrite=True)
importer.upload.queue(content, overwrite=True)
self.assertTrue(frappe.db.get_value("Blog Category", "test-category", "title"), "New Title") self.assertTrue(frappe.db.get_value("Blog Category", "test-category", "title"), "New Title")


def test_import_only_children(self): def test_import_only_children(self):
@@ -57,7 +57,7 @@ class TestDataImport(unittest.TestCase):
exporter.get_template("UserRole", "User", all_doctypes="No", with_data="No") exporter.get_template("UserRole", "User", all_doctypes="No", with_data="No")
content = read_csv_content(frappe.response.result) content = read_csv_content(frappe.response.result)
content.append(["", "test_import_userrole@example.com", "Blogger"]) content.append(["", "test_import_userrole@example.com", "Blogger"])
importer.upload(content)
importer.upload.queue(content)


user = frappe.get_doc("User", user_email) user = frappe.get_doc("User", user_email)
self.assertEquals(len(user.get("user_roles")), 1) self.assertEquals(len(user.get("user_roles")), 1)
@@ -67,7 +67,7 @@ class TestDataImport(unittest.TestCase):
exporter.get_template("UserRole", "User", all_doctypes="No", with_data="No") exporter.get_template("UserRole", "User", all_doctypes="No", with_data="No")
content = read_csv_content(frappe.response.result) content = read_csv_content(frappe.response.result)
content.append(["", "test_import_userrole@example.com", "Website Manager"]) content.append(["", "test_import_userrole@example.com", "Website Manager"])
importer.upload(content, overwrite=True)
importer.upload.queue(content, overwrite=True)


user = frappe.get_doc("User", user_email) user = frappe.get_doc("User", user_email)
self.assertEquals(len(user.get("user_roles")), 1) self.assertEquals(len(user.get("user_roles")), 1)
@@ -81,7 +81,7 @@ class TestDataImport(unittest.TestCase):
content[-1][3] = "Private" content[-1][3] = "Private"
content[-1][4] = "2014-01-01 10:00:00.000000" content[-1][4] = "2014-01-01 10:00:00.000000"
content[-1][content[15].index("role")] = "System Manager" content[-1][content[15].index("role")] = "System Manager"
importer.upload(content)
importer.upload.queue(content)


ev = frappe.get_doc("Event", {"subject":"__Test Event"}) ev = frappe.get_doc("Event", {"subject":"__Test Event"})
self.assertTrue("System Manager" in [d.role for d in ev.roles]) self.assertTrue("System Manager" in [d.role for d in ev.roles])

+ 1
- 0
frappe/utils/__init__.py Wyświetl plik

@@ -400,3 +400,4 @@ def get_request_session(max_retries=3):
session.mount("http://", requests.adapters.HTTPAdapter(max_retries=Retry(total=5, status_forcelist=[500]))) session.mount("http://", requests.adapters.HTTPAdapter(max_retries=Retry(total=5, status_forcelist=[500])))
session.mount("https://", requests.adapters.HTTPAdapter(max_retries=Retry(total=5, status_forcelist=[500]))) session.mount("https://", requests.adapters.HTTPAdapter(max_retries=Retry(total=5, status_forcelist=[500])))
return session return session


+ 1
- 2
frappe/utils/backups.py Wyświetl plik

@@ -41,7 +41,7 @@ class BackupGenerator:
last_db, last_file = self.get_recent_backup(older_than) last_db, last_file = self.get_recent_backup(older_than)
else: else:
last_db, last_file = False, False last_db, last_file = False, False
if not (self.backup_path_files and self.backup_path_db): if not (self.backup_path_files and self.backup_path_db):
self.set_backup_file_name() self.set_backup_file_name()
if not (last_db and last_file): if not (last_db and last_file):
@@ -219,4 +219,3 @@ if __name__ == "__main__":


if cmd == "delete_temp_backups": if cmd == "delete_temp_backups":
delete_temp_backups() delete_temp_backups()


+ 4
- 2
frappe/utils/install.py Wyświetl plik

@@ -106,7 +106,8 @@ def add_country_and_currency(name, country):
"country_name": name, "country_name": name,
"code": country.code, "code": country.code,
"date_format": country.date_format or "dd-mm-yyyy", "date_format": country.date_format or "dd-mm-yyyy",
"time_zones": "\n".join(country.timezones or [])
"time_zones": "\n".join(country.timezones or []),
"docstatus": 0
}).db_insert() }).db_insert()


if country.currency and not frappe.db.exists("Currency", country.currency): if country.currency and not frappe.db.exists("Currency", country.currency):
@@ -116,6 +117,7 @@ def add_country_and_currency(name, country):
"fraction": country.currency_fraction, "fraction": country.currency_fraction,
"symbol": country.currency_symbol, "symbol": country.currency_symbol,
"fraction_units": country.currency_fraction_units, "fraction_units": country.currency_fraction_units,
"number_format": country.number_format
"number_format": country.number_format,
"docstatus": 0
}).db_insert() }).db_insert()



+ 7
- 4
frappe/utils/response.py Wyświetl plik

@@ -64,18 +64,21 @@ def as_json():
response.data = json.dumps(frappe.local.response, default=json_handler, separators=(',',':')) response.data = json.dumps(frappe.local.response, default=json_handler, separators=(',',':'))
return response return response


def make_logs():
def make_logs(response = None):
"""make strings for msgprint and errprint""" """make strings for msgprint and errprint"""
if not response:
response = frappe.local.response

if frappe.error_log: if frappe.error_log:
# frappe.response['exc'] = json.dumps("\n".join([cstr(d) for d in frappe.error_log])) # frappe.response['exc'] = json.dumps("\n".join([cstr(d) for d in frappe.error_log]))
frappe.response['exc'] = json.dumps([frappe.utils.cstr(d) for d in frappe.local.error_log])
response['exc'] = json.dumps([frappe.utils.cstr(d) for d in frappe.local.error_log])


if frappe.local.message_log: if frappe.local.message_log:
frappe.response['_server_messages'] = json.dumps([frappe.utils.cstr(d) for
response['_server_messages'] = json.dumps([frappe.utils.cstr(d) for
d in frappe.local.message_log]) d in frappe.local.message_log])


if frappe.debug_log and frappe.conf.get("logging") or False: if frappe.debug_log and frappe.conf.get("logging") or False:
frappe.response['_debug_messages'] = json.dumps(frappe.local.debug_log)
response['_debug_messages'] = json.dumps(frappe.local.debug_log)


def json_handler(obj): def json_handler(obj):
"""serialize non-serializable data for json""" """serialize non-serializable data for json"""


+ 2
- 2
frappe/website/doctype/blog_category/blog_category.json Wyświetl plik

@@ -4,7 +4,7 @@
"creation": "2013-03-08 09:41:11", "creation": "2013-03-08 09:41:11",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master",
"document_type": "Setup",
"fields": [ "fields": [
{ {
"fieldname": "category_name", "fieldname": "category_name",
@@ -49,7 +49,7 @@
], ],
"icon": "icon-tag", "icon": "icon-tag",
"idx": 1, "idx": 1,
"modified": "2015-02-05 05:11:34.877605",
"modified": "2015-07-28 16:18:11.486847",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Website", "module": "Website",
"name": "Blog Category", "name": "Blog Category",


+ 2
- 2
frappe/website/doctype/blogger/blogger.json Wyświetl plik

@@ -5,7 +5,7 @@
"description": "User ID of a Blogger", "description": "User ID of a Blogger",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master",
"document_type": "Setup",
"fields": [ "fields": [
{ {
"fieldname": "disabled", "fieldname": "disabled",
@@ -61,7 +61,7 @@
"icon": "icon-user", "icon": "icon-user",
"idx": 1, "idx": 1,
"max_attachments": 1, "max_attachments": 1,
"modified": "2015-02-19 09:29:25.836280",
"modified": "2015-07-28 16:18:11.567110",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Website", "module": "Website",
"name": "Blogger", "name": "Blogger",


+ 2
- 2
frappe/website/doctype/web_form/web_form.json Wyświetl plik

@@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Transaction",
"document_type": "Document",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@@ -230,7 +230,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-02-05 05:11:48.897157",
"modified": "2015-07-28 16:18:12.772231",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Website", "module": "Website",
"name": "Web Form", "name": "Web Form",


+ 2
- 2
frappe/website/doctype/web_page/web_page.json Wyświetl plik

@@ -3,7 +3,7 @@
"description": "Page to show on the website\n", "description": "Page to show on the website\n",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Transaction",
"document_type": "Document",
"fields": [ "fields": [
{ {
"fieldname": "section_title", "fieldname": "section_title",
@@ -200,7 +200,7 @@
"icon": "icon-file-alt", "icon": "icon-file-alt",
"idx": 1, "idx": 1,
"max_attachments": 20, "max_attachments": 20,
"modified": "2015-07-22 12:38:08.696692",
"modified": "2015-07-28 16:18:12.887565",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Website", "module": "Website",
"name": "Web Page", "name": "Web Page",


+ 2
- 2
frappe/website/doctype/website_slideshow/website_slideshow.json Wyświetl plik

@@ -4,7 +4,7 @@
"description": "Slideshow like display for the website", "description": "Slideshow like display for the website",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Transaction",
"document_type": "Document",
"fields": [ "fields": [
{ {
"fieldname": "slideshow_name", "fieldname": "slideshow_name",
@@ -42,7 +42,7 @@
"icon": "icon-play", "icon": "icon-play",
"idx": 1, "idx": 1,
"max_attachments": 10, "max_attachments": 10,
"modified": "2015-02-20 05:04:19.614170",
"modified": "2015-07-28 16:18:13.013029",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Website", "module": "Website",
"name": "Website Slideshow", "name": "Website Slideshow",


+ 59
- 59
frappe/workflow/doctype/workflow_document_state/workflow_document_state.json Wyświetl plik

@@ -1,75 +1,75 @@
{ {
"allow_import": 1,
"creation": "2013-02-22 01:27:36",
"description": "Represents the states allowed in one document and role assigned to change the state.",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"allow_import": 1,
"creation": "2013-02-22 01:27:36",
"description": "Represents the states allowed in one document and role assigned to change the state.",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"fields": [ "fields": [
{ {
"fieldname": "state",
"fieldtype": "Link",
"in_list_view": 1,
"label": "State",
"options": "Workflow State",
"permlevel": 0,
"print_width": "160px",
"reqd": 1,
"fieldname": "state",
"fieldtype": "Link",
"in_list_view": 1,
"label": "State",
"options": "Workflow State",
"permlevel": 0,
"print_width": "160px",
"reqd": 1,
"width": "160px" "width": "160px"
},
},
{ {
"description": "0 - Draft; 1 - Submitted; 2 - Cancelled",
"fieldname": "doc_status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Doc Status",
"options": "0\n1\n2",
"permlevel": 0,
"print_width": "80px",
"description": "0 - Draft; 1 - Submitted; 2 - Cancelled",
"fieldname": "doc_status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Doc Status",
"options": "0\n1\n2",
"permlevel": 0,
"print_width": "80px",
"width": "80px" "width": "80px"
},
},
{ {
"fieldname": "update_field",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Update Field",
"fieldname": "update_field",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Update Field",
"permlevel": 0 "permlevel": 0
},
},
{ {
"fieldname": "update_value",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Update Value",
"fieldname": "update_value",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Update Value",
"permlevel": 0 "permlevel": 0
},
},
{ {
"fieldname": "allow_edit",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Only Allow Edit For",
"options": "Role",
"permlevel": 0,
"print_width": "160px",
"reqd": 1,
"fieldname": "allow_edit",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Only Allow Edit For",
"options": "Role",
"permlevel": 0,
"print_width": "160px",
"reqd": 1,
"width": "160px" "width": "160px"
},
},
{ {
"fieldname": "message",
"fieldtype": "Text",
"in_list_view": 1,
"label": "Message",
"permlevel": 0,
"print_width": "160px",
"reqd": 0,
"fieldname": "message",
"fieldtype": "Text",
"in_list_view": 1,
"label": "Message",
"permlevel": 0,
"print_width": "160px",
"reqd": 0,
"width": "160px" "width": "160px"
} }
],
"idx": 1,
"istable": 1,
"modified": "2014-05-27 06:35:01.070158",
"modified_by": "Administrator",
"module": "Workflow",
"name": "Workflow Document State",
"owner": "Administrator",
],
"idx": 1,
"istable": 1,
"modified": "2015-07-28 16:18:13.257862",
"modified_by": "Administrator",
"module": "Workflow",
"name": "Workflow Document State",
"owner": "Administrator",
"permissions": [] "permissions": []
}
}

+ 2
- 2
frappe/workflow/doctype/workflow_state/workflow_state.json Wyświetl plik

@@ -5,7 +5,7 @@
"description": "Workflow state represents the current state of a document.", "description": "Workflow state represents the current state of a document.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master",
"document_type": "Setup",
"fields": [ "fields": [
{ {
"fieldname": "workflow_state_name", "fieldname": "workflow_state_name",
@@ -36,7 +36,7 @@
], ],
"icon": "icon-flag", "icon": "icon-flag",
"idx": 1, "idx": 1,
"modified": "2015-05-27 02:51:01.978973",
"modified": "2015-07-28 16:18:13.320514",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Workflow", "module": "Workflow",
"name": "Workflow State", "name": "Workflow State",


+ 1
- 0
requirements.txt Wyświetl plik

@@ -28,4 +28,5 @@ html2text
email_reply_parser email_reply_parser
click click
num2words num2words
gevent-socketio
watchdog==0.8.0 watchdog==0.8.0

+ 1
- 1
setup.py Wyświetl plik

@@ -1,6 +1,6 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages


version = "5.4.2"
version = "6.0.0"


with open("requirements.txt", "r") as f: with open("requirements.txt", "r") as f:
install_requires = f.readlines() install_requires = f.readlines()


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików

Ładowanie…
Anuluj
Zapisz