Merge branch 'develop' of frappe/frappe into web_hooks
This commit is contained in:
commit
620b7f482e
36 changed files with 799 additions and 260 deletions
|
|
@ -30,6 +30,7 @@ def get_bootinfo():
|
|||
get_user(bootinfo)
|
||||
|
||||
# system info
|
||||
bootinfo.sitename = frappe.local.site
|
||||
bootinfo.sysdefaults = frappe.defaults.get_defaults()
|
||||
bootinfo.user_permissions = get_user_permissions()
|
||||
bootinfo.server_date = frappe.utils.nowdate()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from frappe import _
|
|||
from frappe.model.document import Document
|
||||
from frappe.utils import validate_email_add, get_fullname, strip_html, cstr
|
||||
from frappe.core.doctype.communication.comment import (notify_mentions,
|
||||
update_comment_in_doc)
|
||||
update_comment_in_doc, on_trash)
|
||||
from frappe.core.doctype.communication.email import (validate_email,
|
||||
notify, _notify, update_parent_status)
|
||||
from frappe.utils.bot import BotReply
|
||||
|
|
@ -111,6 +111,8 @@ class Communication(Document):
|
|||
frappe.publish_realtime('delete_communication', self.as_dict(),
|
||||
doctype= self.reference_doctype, docname = self.reference_name,
|
||||
after_commit=True)
|
||||
# delete the comments from _comment
|
||||
on_trash(self)
|
||||
|
||||
def set_status(self):
|
||||
if not self.is_new():
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ class File(NestedSet):
|
|||
|
||||
if frappe.db.exists('File', {'name': self.name, 'is_folder': 0}):
|
||||
if not self.is_folder and (self.is_private != self.db_get('is_private')):
|
||||
old_file_url = self.file_url
|
||||
private_files = frappe.get_site_path('private', 'files')
|
||||
public_files = frappe.get_site_path('public', 'files')
|
||||
|
||||
|
|
@ -91,6 +92,11 @@ class File(NestedSet):
|
|||
|
||||
self.file_url = "/private/files/{0}".format(self.file_name)
|
||||
|
||||
# update documents image url with new file url
|
||||
if self.attached_to_doctype and self.attached_to_name and \
|
||||
frappe.db.get_value(self.attached_to_doctype, self.attached_to_name, "image") == old_file_url:
|
||||
frappe.db.set_value(self.attached_to_doctype, self.attached_to_name, "image", self.file_url)
|
||||
|
||||
def set_folder_size(self):
|
||||
"""Set folder size if folder"""
|
||||
if self.is_folder and not self.is_new():
|
||||
|
|
|
|||
|
|
@ -217,8 +217,8 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False,
|
|||
|
||||
# header
|
||||
if not rows:
|
||||
from frappe.utils.file_manager import save_uploaded
|
||||
file_doc = save_uploaded(dt=None, dn="Data Import", folder='Home', is_private=1)
|
||||
from frappe.utils.file_manager import get_file_doc
|
||||
file_doc = get_file_doc(dt='', dn="Data Import", folder='Home', is_private=1)
|
||||
filename, file_extension = os.path.splitext(file_doc.file_name)
|
||||
|
||||
if file_extension == '.xlsx' and from_data_import == 'Yes':
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -44,6 +45,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -74,6 +76,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -103,6 +106,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -131,6 +135,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -159,6 +164,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -170,7 +176,7 @@
|
|||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Subject",
|
||||
"length": 0,
|
||||
|
|
@ -187,6 +193,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -198,7 +205,7 @@
|
|||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Message",
|
||||
"length": 0,
|
||||
|
|
@ -215,6 +222,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -245,6 +253,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -275,6 +284,69 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"description": "",
|
||||
"fieldname": "published",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Published",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "route",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Route",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -304,6 +376,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -333,6 +406,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -362,6 +436,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -391,19 +466,20 @@
|
|||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"has_web_view": 1,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-envelope",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_published_field": "published",
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 3,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-03-07 12:59:18.173824",
|
||||
"modified": "2017-09-14 15:38:01.891251",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Newsletter",
|
||||
|
|
@ -433,6 +509,7 @@
|
|||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"route": "newsletters",
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_order": "ASC",
|
||||
"title_field": "subject",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
import frappe.utils
|
||||
from frappe import throw, _
|
||||
from frappe.model.document import Document
|
||||
from frappe.website.website_generator import WebsiteGenerator
|
||||
from frappe.email.queue import check_email_limit
|
||||
from frappe.utils.verified_command import get_signed_params, verify_request
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
|
|
@ -17,7 +17,7 @@ from frappe.utils import parse_addr
|
|||
from frappe.utils import validate_email_add
|
||||
|
||||
|
||||
class Newsletter(Document):
|
||||
class Newsletter(WebsiteGenerator):
|
||||
def onload(self):
|
||||
if self.email_sent:
|
||||
self.get("__onload").status_count = dict(frappe.db.sql("""select status, count(name)
|
||||
|
|
@ -25,6 +25,7 @@ class Newsletter(Document):
|
|||
group by status""", (self.doctype, self.name))) or None
|
||||
|
||||
def validate(self):
|
||||
self.route = "newsletters/" + self.name
|
||||
if self.send_from:
|
||||
validate_email_add(self.send_from, True)
|
||||
|
||||
|
|
@ -105,6 +106,26 @@ class Newsletter(Document):
|
|||
throw(_("Please save the Newsletter before sending"))
|
||||
check_email_limit(self.recipients)
|
||||
|
||||
def get_context(self, context):
|
||||
newsletters = get_newsletter_list("Newsletter", None, None, 0)
|
||||
if newsletters:
|
||||
newsletter_list = [d.name for d in newsletters]
|
||||
if self.name not in newsletter_list:
|
||||
frappe.redirect_to_message(_('Permission Error'),
|
||||
_("You are not permitted to view the newsletter."))
|
||||
frappe.local.flags.redirect_location = frappe.local.response.location
|
||||
raise frappe.Redirect
|
||||
else:
|
||||
context.attachments = get_attachments(self.name)
|
||||
context.no_cache = 1
|
||||
context.show_sidebar = True
|
||||
|
||||
|
||||
def get_attachments(name):
|
||||
return frappe.get_all("File",
|
||||
fields=["name", "file_name", "file_url", "is_private"],
|
||||
filters = {"attached_to_name": name, "attached_to_doctype": "Newsletter", "is_private":0})
|
||||
|
||||
|
||||
def get_email_groups(name):
|
||||
return frappe.db.get_all("Newsletter Email Group", ["email_group"],{"parent":name, "parenttype":"Newsletter"})
|
||||
|
|
@ -219,4 +240,24 @@ def send_newsletter(newsletter):
|
|||
frappe.db.commit()
|
||||
|
||||
|
||||
def get_list_context(context=None):
|
||||
context.update({
|
||||
"show_sidebar": True,
|
||||
"show_search": True,
|
||||
'no_breadcrumbs': True,
|
||||
"title": _("Newsletter"),
|
||||
"get_list": get_newsletter_list,
|
||||
"row_template": "email/doctype/newsletter/templates/newsletter_row.html",
|
||||
})
|
||||
|
||||
|
||||
def get_newsletter_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"):
|
||||
email_group_list = frappe.db.sql('''select eg.name from `tabEmail Group` eg, `tabEmail Group Member` egm
|
||||
where egm.unsubscribed=0 and eg.name=egm.email_group and egm.email = %s''', frappe.session.user)
|
||||
if email_group_list:
|
||||
return frappe.db.sql('''select n.name, n.subject, n.message, n.modified
|
||||
from `tabNewsletter` n, `tabNewsletter Email Group` neg
|
||||
where n.name = neg.parent and n.email_sent=1 and n.published=1 and neg.email_group in %s
|
||||
order by n.modified desc limit {0}, {1}
|
||||
'''.format(limit_start, limit_page_length), [email_group_list], as_dict=1)
|
||||
|
||||
|
|
|
|||
65
frappe/email/doctype/newsletter/templates/newsletter.html
Normal file
65
frappe/email/doctype/newsletter/templates/newsletter.html
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
{% extends "templates/web.html" %}
|
||||
|
||||
{% block title %} {{ _("Newsletter") }} {% endblock %}
|
||||
|
||||
{% block page_content %}
|
||||
<style>
|
||||
.blog-container {
|
||||
max-width: 720px;
|
||||
margin: auto;
|
||||
}
|
||||
.blog-header {
|
||||
font-weight: 700;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.blog-info {
|
||||
text-align:center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.blog-text {
|
||||
padding-top: 50px;
|
||||
padding-bottom: 50px;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.blog-text p {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="blog-container">
|
||||
<article class="blog-content" itemscope>
|
||||
<div class="blog-info">
|
||||
<h1 itemprop="headline" class="blog-header">{{ doc.subject }}</h1>
|
||||
<p class="post-by text-muted">
|
||||
{{ frappe.format_date(doc.modified) }}
|
||||
</p>
|
||||
</div>
|
||||
<div itemprop="articleBody" class="longform blog-text">
|
||||
{{ doc.message }}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{% if attachments %}
|
||||
<div>
|
||||
<div class="row text-muted">
|
||||
<div class="col-sm-12 h6 text-uppercase">
|
||||
{{ _("Attachments") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{% for attachment in attachments %}
|
||||
<p class="small">
|
||||
<a href="{{ attachment.file_url }}" target="blank">
|
||||
{{ attachment.file_name }}
|
||||
</a>
|
||||
</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<div class="web-list-item transaction-list-item">
|
||||
<a href = "{{ route }}/">
|
||||
<div class="row">
|
||||
<div class="col-sm-8 text-left bold">
|
||||
{{ doc.subject }}
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="text-muted text-right"
|
||||
title="{{ frappe.utils.format_datetime(doc.modified, "medium") }}">
|
||||
{{ frappe.utils.pretty_date(doc.modified) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
23
frappe/email/doctype/newsletter/test_newsletter.js
Normal file
23
frappe/email/doctype/newsletter/test_newsletter.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Newsletter", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Newsletter
|
||||
() => frappe.tests.make('Newsletter', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
||||
|
|
@ -7,24 +7,26 @@ import frappe, unittest
|
|||
from frappe.email.doctype.newsletter.newsletter import confirmed_unsubscribe
|
||||
from six.moves.urllib.parse import unquote
|
||||
|
||||
|
||||
emails = ["test_subscriber1@example.com", "test_subscriber2@example.com",
|
||||
"test_subscriber3@example.com"]
|
||||
"test_subscriber3@example.com", "test1@example.com"]
|
||||
|
||||
class TestNewsletter(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.set_user("Administrator")
|
||||
frappe.db.sql('delete from `tabEmail Group Member`')
|
||||
for email in emails:
|
||||
frappe.get_doc({
|
||||
"doctype": "Email Group Member",
|
||||
"email": email,
|
||||
"email_group": "_Test Email Group"
|
||||
}).insert()
|
||||
}).insert()
|
||||
|
||||
def test_send(self):
|
||||
name = self.send_newsletter()
|
||||
|
||||
email_queue_list = [frappe.get_doc('Email Queue', e.name) for e in frappe.get_all("Email Queue")]
|
||||
self.assertEquals(len(email_queue_list), 3)
|
||||
self.assertEquals(len(email_queue_list), 4)
|
||||
recipients = [e.recipients[0].recipient for e in email_queue_list]
|
||||
for email in emails:
|
||||
self.assertTrue(email in recipients)
|
||||
|
|
@ -41,13 +43,14 @@ class TestNewsletter(unittest.TestCase):
|
|||
name = self.send_newsletter()
|
||||
|
||||
email_queue_list = [frappe.get_doc('Email Queue', e.name) for e in frappe.get_all("Email Queue")]
|
||||
self.assertEquals(len(email_queue_list), 2)
|
||||
self.assertEquals(len(email_queue_list), 3)
|
||||
recipients = [e.recipients[0].recipient for e in email_queue_list]
|
||||
for email in emails:
|
||||
if email != to_unsubscribe:
|
||||
self.assertTrue(email in recipients)
|
||||
|
||||
def send_newsletter(self):
|
||||
@staticmethod
|
||||
def send_newsletter(published=0):
|
||||
frappe.db.sql("delete from `tabEmail Queue`")
|
||||
frappe.db.sql("delete from `tabEmail Queue Recipient`")
|
||||
frappe.db.sql("delete from `tabNewsletter`")
|
||||
|
|
@ -55,7 +58,8 @@ class TestNewsletter(unittest.TestCase):
|
|||
"doctype": "Newsletter",
|
||||
"subject": "_Test Newsletter",
|
||||
"send_from": "Test Sender <test_sender@example.com>",
|
||||
"message": "Testing my news."
|
||||
"message": "Testing my news.",
|
||||
"published": published
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
newsletter.append("email_group", {"email_group": "_Test Email Group"})
|
||||
|
|
@ -63,4 +67,21 @@ class TestNewsletter(unittest.TestCase):
|
|||
newsletter.send_emails()
|
||||
return newsletter.name
|
||||
|
||||
def test_portal(self):
|
||||
self.send_newsletter(1)
|
||||
frappe.set_user("test1@example.com")
|
||||
from frappe.email.doctype.newsletter.newsletter import get_newsletter_list
|
||||
newsletters = get_newsletter_list("Newsletter", None, None, 0)
|
||||
self.assertEquals(len(newsletters), 1)
|
||||
|
||||
def test_newsletter_context(self):
|
||||
context = frappe._dict()
|
||||
newsletter_name = self.send_newsletter(1)
|
||||
frappe.set_user("test2@example.com")
|
||||
doc = frappe.get_doc("Newsletter", newsletter_name)
|
||||
doc.get_context(context)
|
||||
self.assertEquals(context.no_cache, 1)
|
||||
self.assertTrue("attachments" not in context.keys())
|
||||
|
||||
|
||||
test_dependencies = ["Email Group"]
|
||||
|
|
|
|||
|
|
@ -269,9 +269,11 @@
|
|||
},
|
||||
"Benin": {
|
||||
"code": "bj",
|
||||
"currency": "XOF",
|
||||
"currency_name": "West African CFA Franc",
|
||||
"currency_symbol": "CFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Porto-Novo"
|
||||
|
|
@ -386,6 +388,8 @@
|
|||
},
|
||||
"Bulgaria": {
|
||||
"code": "bg",
|
||||
"currency": "BGN",
|
||||
"currency_name": "Bulgarian Lev",
|
||||
"currency_fraction": "Stotinka",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "\u043b\u0432",
|
||||
|
|
@ -396,9 +400,11 @@
|
|||
},
|
||||
"Burkina Faso": {
|
||||
"code": "bf",
|
||||
"currency": "XOF",
|
||||
"currency_name": "West African CFA Franc",
|
||||
"currency_symbol": "CFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Ouagadougou"
|
||||
|
|
@ -430,9 +436,11 @@
|
|||
},
|
||||
"Cameroon": {
|
||||
"code": "cm",
|
||||
"currency": "XAF",
|
||||
"currency_name": "Central African CFA Franc",
|
||||
"currency_symbol": "FCFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Douala"
|
||||
|
|
@ -504,9 +512,11 @@
|
|||
},
|
||||
"Central African Republic": {
|
||||
"code": "cf",
|
||||
"currency": "XAF",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"currency_name": "Central African CFA Franc",
|
||||
"currency_symbol": "FCFA",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Bangui"
|
||||
|
|
@ -514,9 +524,11 @@
|
|||
},
|
||||
"Chad": {
|
||||
"code": "td",
|
||||
"currency": "XAF",
|
||||
"currency_name": "Central African CFA Franc",
|
||||
"currency_symbol": "FCFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Ndjamena"
|
||||
|
|
@ -592,7 +604,12 @@
|
|||
},
|
||||
"Congo": {
|
||||
"code": "cg",
|
||||
"number_format": "#,###.##"
|
||||
"number_format": "#,###.##",
|
||||
"currency": "XAF",
|
||||
"currency_name": "Central African CFA Franc",
|
||||
"currency_symbol": "FCFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100
|
||||
},
|
||||
"Congo, The Democratic Republic of the": {
|
||||
"code": "cd",
|
||||
|
|
@ -758,9 +775,11 @@
|
|||
},
|
||||
"Equatorial Guinea": {
|
||||
"code": "gq",
|
||||
"currency": "XAF",
|
||||
"currency_name": "Central African CFA Franc",
|
||||
"currency_symbol": "FCFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Malabo"
|
||||
|
|
@ -877,9 +896,11 @@
|
|||
},
|
||||
"Gabon": {
|
||||
"code": "ga",
|
||||
"currency": "XAF",
|
||||
"currency_name": "Central African CFA Franc",
|
||||
"currency_symbol": "FCFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Libreville"
|
||||
|
|
@ -1019,9 +1040,11 @@
|
|||
},
|
||||
"Guinea-Bissau": {
|
||||
"code": "gw",
|
||||
"currency": "XOF",
|
||||
"currency_name": "West African CFA Franc",
|
||||
"currency_symbol": "CFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Bissau"
|
||||
|
|
@ -1205,10 +1228,15 @@
|
|||
},
|
||||
"Ivory Coast": {
|
||||
"code": "ci",
|
||||
"currency": "XOF",
|
||||
"currency_name": "West African CFA Franc",
|
||||
"currency_symbol": "CFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##"
|
||||
"number_format": "#,###.##",
|
||||
"timeszones": [
|
||||
"Africa/Abidjan"
|
||||
]
|
||||
},
|
||||
"Jamaica": {
|
||||
"code": "jm",
|
||||
|
|
@ -1496,9 +1524,11 @@
|
|||
},
|
||||
"Mali": {
|
||||
"code": "ml",
|
||||
"currency": "XOF",
|
||||
"currency_name": "West African CFA Franc",
|
||||
"currency_symbol": "CFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Bamako"
|
||||
|
|
@ -1758,9 +1788,11 @@
|
|||
},
|
||||
"Niger": {
|
||||
"code": "ne",
|
||||
"currency": "XOF",
|
||||
"currency_name": "West African CFA Franc",
|
||||
"currency_symbol": "CFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Niamey"
|
||||
|
|
@ -2087,9 +2119,11 @@
|
|||
},
|
||||
"Senegal": {
|
||||
"code": "sn",
|
||||
"currency": "XOF",
|
||||
"currency_name": "West African CFA Franc",
|
||||
"currency_symbol": "CFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Dakar"
|
||||
|
|
@ -2355,9 +2389,11 @@
|
|||
},
|
||||
"Togo": {
|
||||
"code": "tg",
|
||||
"currency": "XOF",
|
||||
"currency_name": "West African CFA Franc",
|
||||
"currency_symbol": "CFA",
|
||||
"currency_fraction": "Centime",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "Fr",
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Lome"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import unicode_literals
|
||||
from . import __version__ as app_version
|
||||
|
||||
|
||||
app_name = "frappe"
|
||||
app_title = "Frappe Framework"
|
||||
app_publisher = "Frappe Technologies"
|
||||
|
|
@ -48,9 +49,11 @@ bootstrap = "assets/frappe/css/bootstrap.css"
|
|||
web_include_css = [
|
||||
"assets/css/frappe-web.css"
|
||||
]
|
||||
|
||||
website_route_rules = [
|
||||
{"from_route": "/blog/<category>", "to_route": "Blog Post"},
|
||||
{"from_route": "/kb/<category>", "to_route": "Help Article"}
|
||||
{"from_route": "/kb/<category>", "to_route": "Help Article"},
|
||||
{"from_route": "/newsletters", "to_route": "Newsletter"}
|
||||
]
|
||||
|
||||
write_file_keys = ["file_url", "file_name"]
|
||||
|
|
|
|||
|
|
@ -193,3 +193,4 @@ frappe.patches.v8_x.update_user_permission
|
|||
frappe.patches.v8_5.patch_event_colors
|
||||
frappe.patches.v8_7.update_email_queue_status
|
||||
frappe.patches.v8_10.delete_static_web_page_from_global_search
|
||||
frappe.patches.v8_x.add_bgn_xaf_xof_currencies
|
||||
|
|
|
|||
12
frappe/patches/v8_x/add_bgn_xaf_xof_currencies.py
Normal file
12
frappe/patches/v8_x/add_bgn_xaf_xof_currencies.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
"""
|
||||
This will add the following currencies:
|
||||
1. BGN (Bulgarian Lev) to Bulgaria.
|
||||
2. XAF (Central African CFA Franc) to Cameroon, Republic of Congo, Chad, Gabon, Equitorial Guinea and
|
||||
Central African Republic.
|
||||
3. XOF (West African CFA Franc) to Benin, Niger, Burkina Faso, Mali, Senegal, Togo, Ivory Coast and Guinea Bissau.
|
||||
"""
|
||||
from frappe.utils.install import import_country_and_currency
|
||||
|
||||
|
||||
def execute():
|
||||
import_country_and_currency()
|
||||
|
|
@ -601,11 +601,9 @@ select.form-control {
|
|||
.control-code.bold {
|
||||
height: 400px;
|
||||
font-family: Monaco, "Courier New", monospace;
|
||||
background-color: black;
|
||||
color: #fffce7;
|
||||
color: #36414C;
|
||||
font-size: 12px;
|
||||
line-height: 1.7em;
|
||||
border: none;
|
||||
}
|
||||
.delivery-status-indicator {
|
||||
display: inline-block;
|
||||
|
|
|
|||
|
|
@ -94,9 +94,6 @@ body[data-route^="Module"] .main-menu .form-sidebar {
|
|||
position: absolute;
|
||||
right: 5px;
|
||||
}
|
||||
.form-sidebar .attachment-row a.close {
|
||||
margin-top: -5px;
|
||||
}
|
||||
.form-sidebar .form-shared .share-doc-btn,
|
||||
.form-sidebar .form-viewers .share-doc-btn {
|
||||
cursor: pointer;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ frappe.Application = Class.extend({
|
|||
this.startup();
|
||||
},
|
||||
startup: function() {
|
||||
frappe.socket.init();
|
||||
frappe.socketio.init();
|
||||
frappe.model.init();
|
||||
|
||||
if(frappe.boot.status==='failed') {
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({
|
|||
});
|
||||
this.$value = $('<div style="margin-top: 5px;">\
|
||||
<div class="ellipsis" style="display: inline-block; width: 90%;">\
|
||||
<i class="fa fa-paper-clip"></i> \
|
||||
<i class="fa fa-paperclip"></i> \
|
||||
<a class="attached-file" target="_blank"></a>\
|
||||
</div>\
|
||||
<a class="close">×</a></div>')
|
||||
<a class="close" style="position: absolute; right: 15px;">×</a></div>')
|
||||
.prependTo(me.input_area)
|
||||
.toggle(false);
|
||||
this.input = this.$input.get(0);
|
||||
|
|
|
|||
|
|
@ -2,13 +2,21 @@ frappe.ui.form.ControlHTML = frappe.ui.form.Control.extend({
|
|||
make: function() {
|
||||
this._super();
|
||||
this.disp_area = this.wrapper;
|
||||
$(document).on('change', () => {
|
||||
setTimeout(() => this.refresh_input(), 500);
|
||||
});
|
||||
},
|
||||
refresh_input: function() {
|
||||
var content = this.get_content();
|
||||
if(content) this.$wrapper.html(content);
|
||||
},
|
||||
get_content: function() {
|
||||
return this.df.options || "";
|
||||
var content = this.df.options || "";
|
||||
try {
|
||||
return frappe.render(content, this);
|
||||
} catch (e) {
|
||||
return content;
|
||||
}
|
||||
},
|
||||
html: function(html) {
|
||||
this.$wrapper.html(html || this.get_content());
|
||||
|
|
|
|||
|
|
@ -137,17 +137,17 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({
|
|||
});
|
||||
},
|
||||
get_image: function (fileobj, callback) {
|
||||
var freader = new FileReader();
|
||||
var reader = new FileReader();
|
||||
|
||||
freader.onload = function() {
|
||||
var dataurl = freader.result;
|
||||
reader.onload = function() {
|
||||
var dataurl = reader.result;
|
||||
// add filename to dataurl
|
||||
var parts = dataurl.split(",");
|
||||
parts[0] += ";filename=" + fileobj.name;
|
||||
dataurl = parts[0] + ',' + parts[1];
|
||||
callback(dataurl);
|
||||
};
|
||||
freader.readAsDataURL(fileobj);
|
||||
reader.readAsDataURL(fileobj);
|
||||
},
|
||||
hide_elements_on_mobile: function() {
|
||||
this.note_editor.find('.note-btn-underline,\
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ frappe.ui.form.QuickEntryForm = Class.extend({
|
|||
return false;
|
||||
}
|
||||
|
||||
if (this.too_many_mandatory_fields() || this.has_child_table()) {
|
||||
if (this.too_many_mandatory_fields() || this.has_child_table()
|
||||
|| !this.mandatory.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ frappe.call = function(opts) {
|
|||
var callback = function(data, response_text) {
|
||||
if(data.task_id) {
|
||||
// async call, subscribe
|
||||
frappe.socket.subscribe(data.task_id, opts);
|
||||
frappe.socketio.subscribe(data.task_id, opts);
|
||||
|
||||
if(opts.queued) {
|
||||
opts.queued(data);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
frappe.socket = {
|
||||
frappe.socketio = {
|
||||
open_tasks: {},
|
||||
open_docs: [],
|
||||
emit_queue: [],
|
||||
|
|
@ -7,40 +7,40 @@ frappe.socket = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (frappe.socket.socket) {
|
||||
if (frappe.socketio.socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (frappe.boot.developer_mode) {
|
||||
// File watchers for development
|
||||
frappe.socket.setup_file_watchers();
|
||||
frappe.socketio.setup_file_watchers();
|
||||
}
|
||||
|
||||
//Enable secure option when using HTTPS
|
||||
if (window.location.protocol == "https:") {
|
||||
frappe.socket.socket = io.connect(frappe.socket.get_host(), {secure: true});
|
||||
frappe.socketio.socket = io.connect(frappe.socketio.get_host(), {secure: true});
|
||||
}
|
||||
else if (window.location.protocol == "http:") {
|
||||
frappe.socket.socket = io.connect(frappe.socket.get_host());
|
||||
frappe.socketio.socket = io.connect(frappe.socketio.get_host());
|
||||
}
|
||||
else if (window.location.protocol == "file:") {
|
||||
frappe.socket.socket = io.connect(window.localStorage.server);
|
||||
frappe.socketio.socket = io.connect(window.localStorage.server);
|
||||
}
|
||||
|
||||
if (!frappe.socket.socket) {
|
||||
console.log("Unable to connect to " + frappe.socket.get_host());
|
||||
if (!frappe.socketio.socket) {
|
||||
console.log("Unable to connect to " + frappe.socketio.get_host());
|
||||
return;
|
||||
}
|
||||
|
||||
frappe.socket.socket.on('msgprint', function(message) {
|
||||
frappe.socketio.socket.on('msgprint', function(message) {
|
||||
frappe.msgprint(message);
|
||||
});
|
||||
|
||||
frappe.socket.socket.on('eval_js', function(message) {
|
||||
frappe.socketio.socket.on('eval_js', function(message) {
|
||||
eval(message);
|
||||
});
|
||||
|
||||
frappe.socket.socket.on('progress', function(data) {
|
||||
frappe.socketio.socket.on('progress', function(data) {
|
||||
if(data.progress) {
|
||||
data.percent = flt(data.progress[0]) / data.progress[1] * 100;
|
||||
}
|
||||
|
|
@ -53,23 +53,24 @@ frappe.socket = {
|
|||
}
|
||||
});
|
||||
|
||||
frappe.socket.setup_listeners();
|
||||
frappe.socket.setup_reconnect();
|
||||
frappe.socketio.setup_listeners();
|
||||
frappe.socketio.setup_reconnect();
|
||||
frappe.socketio.uploader = new frappe.socketio.SocketIOUploader();
|
||||
|
||||
$(document).on('form-load form-rename', function(e, frm) {
|
||||
if (frm.is_new()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i=0, l=frappe.socket.open_docs.length; i<l; i++) {
|
||||
var d = frappe.socket.open_docs[i];
|
||||
for (var i=0, l=frappe.socketio.open_docs.length; i<l; i++) {
|
||||
var d = frappe.socketio.open_docs[i];
|
||||
if (frm.doctype==d.doctype && frm.docname==d.name) {
|
||||
// already subscribed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
frappe.socket.doc_subscribe(frm.doctype, frm.docname);
|
||||
frappe.socketio.doc_subscribe(frm.doctype, frm.docname);
|
||||
});
|
||||
|
||||
$(document).on("form_refresh", function(e, frm) {
|
||||
|
|
@ -77,7 +78,7 @@ frappe.socket = {
|
|||
return;
|
||||
}
|
||||
|
||||
frappe.socket.doc_open(frm.doctype, frm.docname);
|
||||
frappe.socketio.doc_open(frm.doctype, frm.docname);
|
||||
});
|
||||
|
||||
$(document).on('form-unload', function(e, frm) {
|
||||
|
|
@ -85,8 +86,8 @@ frappe.socket = {
|
|||
return;
|
||||
}
|
||||
|
||||
// frappe.socket.doc_unsubscribe(frm.doctype, frm.docname);
|
||||
frappe.socket.doc_close(frm.doctype, frm.docname);
|
||||
// frappe.socketio.doc_unsubscribe(frm.doctype, frm.docname);
|
||||
frappe.socketio.doc_close(frm.doctype, frm.docname);
|
||||
});
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
|
|
@ -96,7 +97,7 @@ frappe.socket = {
|
|||
|
||||
// if tab/window is closed, notify other users
|
||||
if (cur_frm.doc) {
|
||||
frappe.socket.doc_close(cur_frm.doctype, cur_frm.docname);
|
||||
frappe.socketio.doc_close(cur_frm.doctype, cur_frm.docname);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -115,16 +116,16 @@ frappe.socket = {
|
|||
subscribe: function(task_id, opts) {
|
||||
// TODO DEPRECATE
|
||||
|
||||
frappe.socket.socket.emit('task_subscribe', task_id);
|
||||
frappe.socket.socket.emit('progress_subscribe', task_id);
|
||||
frappe.socketio.socket.emit('task_subscribe', task_id);
|
||||
frappe.socketio.socket.emit('progress_subscribe', task_id);
|
||||
|
||||
frappe.socket.open_tasks[task_id] = opts;
|
||||
frappe.socketio.open_tasks[task_id] = opts;
|
||||
},
|
||||
task_subscribe: function(task_id) {
|
||||
frappe.socket.socket.emit('task_subscribe', task_id);
|
||||
frappe.socketio.socket.emit('task_subscribe', task_id);
|
||||
},
|
||||
task_unsubscribe: function(task_id) {
|
||||
frappe.socket.socket.emit('task_unsubscribe', task_id);
|
||||
frappe.socketio.socket.emit('task_unsubscribe', task_id);
|
||||
},
|
||||
doc_subscribe: function(doctype, docname) {
|
||||
if (frappe.flags.doc_subscribe) {
|
||||
|
|
@ -137,12 +138,12 @@ frappe.socket = {
|
|||
// throttle to 1 per sec
|
||||
setTimeout(function() { frappe.flags.doc_subscribe = false }, 1000);
|
||||
|
||||
frappe.socket.socket.emit('doc_subscribe', doctype, docname);
|
||||
frappe.socket.open_docs.push({doctype: doctype, docname: docname});
|
||||
frappe.socketio.socket.emit('doc_subscribe', doctype, docname);
|
||||
frappe.socketio.open_docs.push({doctype: doctype, docname: docname});
|
||||
},
|
||||
doc_unsubscribe: function(doctype, docname) {
|
||||
frappe.socket.socket.emit('doc_unsubscribe', doctype, docname);
|
||||
frappe.socket.open_docs = $.filter(frappe.socket.open_docs, function(d) {
|
||||
frappe.socketio.socket.emit('doc_unsubscribe', doctype, docname);
|
||||
frappe.socketio.open_docs = $.filter(frappe.socketio.open_docs, function(d) {
|
||||
if(d.doctype===doctype && d.name===docname) {
|
||||
return null;
|
||||
} else {
|
||||
|
|
@ -152,44 +153,44 @@ frappe.socket = {
|
|||
},
|
||||
doc_open: function(doctype, docname) {
|
||||
// notify that the user has opened this doc, if not already notified
|
||||
if(!frappe.socket.last_doc
|
||||
|| (frappe.socket.last_doc[0]!=doctype && frappe.socket.last_doc[0]!=docname)) {
|
||||
frappe.socket.socket.emit('doc_open', doctype, docname);
|
||||
if(!frappe.socketio.last_doc
|
||||
|| (frappe.socketio.last_doc[0]!=doctype && frappe.socketio.last_doc[0]!=docname)) {
|
||||
frappe.socketio.socket.emit('doc_open', doctype, docname);
|
||||
}
|
||||
frappe.socket.last_doc = [doctype, docname];
|
||||
frappe.socketio.last_doc = [doctype, docname];
|
||||
},
|
||||
doc_close: function(doctype, docname) {
|
||||
// notify that the user has closed this doc
|
||||
frappe.socket.socket.emit('doc_close', doctype, docname);
|
||||
frappe.socketio.socket.emit('doc_close', doctype, docname);
|
||||
},
|
||||
setup_listeners: function() {
|
||||
frappe.socket.socket.on('task_status_change', function(data) {
|
||||
frappe.socket.process_response(data, data.status.toLowerCase());
|
||||
frappe.socketio.socket.on('task_status_change', function(data) {
|
||||
frappe.socketio.process_response(data, data.status.toLowerCase());
|
||||
});
|
||||
frappe.socket.socket.on('task_progress', function(data) {
|
||||
frappe.socket.process_response(data, "progress");
|
||||
frappe.socketio.socket.on('task_progress', function(data) {
|
||||
frappe.socketio.process_response(data, "progress");
|
||||
});
|
||||
},
|
||||
setup_reconnect: function() {
|
||||
// subscribe again to open_tasks
|
||||
frappe.socket.socket.on("connect", function() {
|
||||
frappe.socketio.socket.on("connect", function() {
|
||||
// wait for 5 seconds before subscribing again
|
||||
// because it takes more time to start python server than nodejs server
|
||||
// and we use validation requests to python server for subscribing
|
||||
setTimeout(function() {
|
||||
$.each(frappe.socket.open_tasks, function(task_id, opts) {
|
||||
frappe.socket.subscribe(task_id, opts);
|
||||
$.each(frappe.socketio.open_tasks, function(task_id, opts) {
|
||||
frappe.socketio.subscribe(task_id, opts);
|
||||
});
|
||||
|
||||
// re-connect open docs
|
||||
$.each(frappe.socket.open_docs, function(d) {
|
||||
$.each(frappe.socketio.open_docs, function(d) {
|
||||
if(locals[d.doctype] && locals[d.doctype][d.name]) {
|
||||
frappe.socket.doc_subscribe(d.doctype, d.name);
|
||||
frappe.socketio.doc_subscribe(d.doctype, d.name);
|
||||
}
|
||||
});
|
||||
|
||||
if (cur_frm && cur_frm.doc) {
|
||||
frappe.socket.doc_open(cur_frm.doc.doctype, cur_frm.doc.name);
|
||||
frappe.socketio.doc_open(cur_frm.doc.doctype, cur_frm.doc.name);
|
||||
}
|
||||
}, 5000);
|
||||
});
|
||||
|
|
@ -208,9 +209,9 @@ frappe.socket = {
|
|||
}
|
||||
host = host + ':' + port;
|
||||
|
||||
frappe.socket.file_watcher = io.connect(host);
|
||||
frappe.socketio.file_watcher = io.connect(host);
|
||||
// css files auto reload
|
||||
frappe.socket.file_watcher.on('reload_css', function(filename) {
|
||||
frappe.socketio.file_watcher.on('reload_css', function(filename) {
|
||||
let abs_file_path = "assets/" + filename;
|
||||
const link = $(`link[href*="${abs_file_path}"]`);
|
||||
abs_file_path = abs_file_path.split('?')[0] + '?v='+ moment();
|
||||
|
|
@ -221,7 +222,7 @@ frappe.socket = {
|
|||
}, 5);
|
||||
});
|
||||
// js files show alert
|
||||
frappe.socket.file_watcher.on('reload_js', function(filename) {
|
||||
frappe.socketio.file_watcher.on('reload_js', function(filename) {
|
||||
filename = "assets/" + filename;
|
||||
var msg = $(`
|
||||
<span>${filename} changed <a data-action="reload">Click to Reload</a></span>
|
||||
|
|
@ -239,7 +240,7 @@ frappe.socket = {
|
|||
}
|
||||
|
||||
// success
|
||||
var opts = frappe.socket.open_tasks[data.task_id];
|
||||
var opts = frappe.socketio.open_tasks[data.task_id];
|
||||
if(opts[method]) {
|
||||
opts[method](data);
|
||||
}
|
||||
|
|
@ -264,15 +265,108 @@ frappe.socket = {
|
|||
|
||||
frappe.provide("frappe.realtime");
|
||||
frappe.realtime.on = function(event, callback) {
|
||||
frappe.socket.socket && frappe.socket.socket.on(event, callback);
|
||||
frappe.socketio.socket && frappe.socketio.socket.on(event, callback);
|
||||
};
|
||||
|
||||
frappe.realtime.off = function(event, callback) {
|
||||
frappe.socket.socket && frappe.socket.socket.off(event, callback);
|
||||
frappe.socketio.socket && frappe.socketio.socket.off(event, callback);
|
||||
}
|
||||
|
||||
frappe.realtime.publish = function(event, message) {
|
||||
if(frappe.socket.socket) {
|
||||
frappe.socket.socket.emit(event, message);
|
||||
if(frappe.socketio.socket) {
|
||||
frappe.socketio.socket.emit(event, message);
|
||||
}
|
||||
}
|
||||
|
||||
frappe.socketio.SocketIOUploader = class SocketIOUploader {
|
||||
constructor() {
|
||||
frappe.socketio.socket.on('upload-request-slice', (data) => {
|
||||
var place = data.currentSlice * this.chunk_size,
|
||||
slice = this.file.slice(place,
|
||||
place + Math.min(this.chunk_size, this.file.size - place));
|
||||
|
||||
if (this.on_progress) {
|
||||
// update progress
|
||||
this.on_progress(place / this.file.size * 100);
|
||||
}
|
||||
|
||||
this.reader.readAsArrayBuffer(slice);
|
||||
this.keep_alive();
|
||||
});
|
||||
|
||||
frappe.socketio.socket.on('upload-end', (data) => {
|
||||
if (data.file_url.substr(0, 7)==='/public') {
|
||||
data.file_url = data.file_url.substr(7);
|
||||
}
|
||||
this.callback(data);
|
||||
this.reader = null;
|
||||
this.file = null;
|
||||
});
|
||||
|
||||
frappe.socketio.socket.on('upload-error', (data) => {
|
||||
this.disconnect(false);
|
||||
frappe.msgprint({
|
||||
title: __('Upload Failed'),
|
||||
message: data.error,
|
||||
indicator: 'red'
|
||||
});
|
||||
});
|
||||
|
||||
frappe.socketio.socket.on('disconnect', () => {
|
||||
this.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
start({file=null, is_private=0, filename='', callback=null, on_progress=null,
|
||||
chunk_size=100000} = {}) {
|
||||
|
||||
if (this.reader) {
|
||||
frappe.throw(__('File Upload in Progress. Please try again in a few moments.'));
|
||||
}
|
||||
|
||||
this.reader = new FileReader();
|
||||
this.file = file;
|
||||
this.chunk_size = chunk_size;
|
||||
this.callback = callback;
|
||||
this.on_progress = on_progress;
|
||||
|
||||
this.reader.onload = () => {
|
||||
frappe.socketio.socket.emit('upload-accept-slice', {
|
||||
is_private: is_private,
|
||||
name: filename,
|
||||
type: this.file.type,
|
||||
size: this.file.size,
|
||||
data: this.reader.result
|
||||
});
|
||||
this.keep_alive();
|
||||
};
|
||||
|
||||
var slice = file.slice(0, this.chunk_size);
|
||||
this.reader.readAsArrayBuffer(slice);
|
||||
}
|
||||
|
||||
keep_alive() {
|
||||
if (this.next_check) {
|
||||
clearTimeout (this.next_check);
|
||||
}
|
||||
this.next_check = setTimeout (() => {
|
||||
this.disconnect();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
disconnect(with_message = true) {
|
||||
if (this.reader) {
|
||||
this.reader = null;
|
||||
this.file = null;
|
||||
frappe.hide_progress();
|
||||
if (with_message) {
|
||||
frappe.msgprint({
|
||||
title: __('File Upload'),
|
||||
message: __('File Upload Disconnected. Please try again.'),
|
||||
indicator: 'red'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -234,7 +234,7 @@ frappe.verify_password = function(callback) {
|
|||
}, __("Verify Password"), __("Verify"))
|
||||
}
|
||||
|
||||
frappe.show_progress = function(title, count, total) {
|
||||
frappe.show_progress = function(title, count, total=100, description) {
|
||||
if(frappe.cur_progress && frappe.cur_progress.title === title
|
||||
&& frappe.cur_progress.$wrapper.is(":visible")) {
|
||||
var dialog = frappe.cur_progress;
|
||||
|
|
@ -242,7 +242,10 @@ frappe.show_progress = function(title, count, total) {
|
|||
var dialog = new frappe.ui.Dialog({
|
||||
title: title,
|
||||
});
|
||||
dialog.progress = $('<div class="progress"><div class="progress-bar"></div></div>')
|
||||
dialog.progress = $(`<div class="progress">
|
||||
<div class="progress-bar"></div>
|
||||
<p class="description text-muted small"></p>
|
||||
</div>`)
|
||||
.appendTo(dialog.body);
|
||||
dialog.progress_bar = dialog.progress.css({"margin-top": "10px"})
|
||||
.find(".progress-bar");
|
||||
|
|
@ -250,7 +253,12 @@ frappe.show_progress = function(title, count, total) {
|
|||
dialog.show();
|
||||
frappe.cur_progress = dialog;
|
||||
}
|
||||
dialog.progress_bar.css({"width": cint(flt(count) * 100 / total) + "%" });
|
||||
if (description) {
|
||||
dialog.progress.find('.description').text(description);
|
||||
}
|
||||
dialog.percent = cint(flt(count) * 100 / total);
|
||||
dialog.progress_bar.css({"width": dialog.percent + "%" });
|
||||
return dialog;
|
||||
}
|
||||
|
||||
frappe.hide_progress = function() {
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ frappe.upload = {
|
|||
},
|
||||
upload_multiple_files: function(files /*FileData array*/, args, opts) {
|
||||
var i = -1;
|
||||
|
||||
frappe.upload.total_files = files ? files.length : 0;
|
||||
// upload the first file
|
||||
upload_next();
|
||||
// subsequent files will be uploaded after
|
||||
|
|
@ -200,7 +200,7 @@ frappe.upload = {
|
|||
var file = files[i];
|
||||
args.is_private = file.is_private;
|
||||
if(!opts.progress) {
|
||||
frappe.show_progress(__('Uploading'), i+1, files.length);
|
||||
frappe.show_progress(__('Uploading'), i, files.length);
|
||||
}
|
||||
}
|
||||
frappe.upload.upload_file(file, args, opts);
|
||||
|
|
@ -225,20 +225,21 @@ frappe.upload = {
|
|||
return;
|
||||
}
|
||||
|
||||
if(args.file_url) {
|
||||
frappe.upload._upload_file(fileobj, args, opts);
|
||||
} else {
|
||||
if(fileobj) {
|
||||
frappe.upload.read_file(fileobj, args, opts);
|
||||
} else {
|
||||
// with file_url
|
||||
frappe.upload._upload_file(fileobj, args, opts);
|
||||
}
|
||||
},
|
||||
|
||||
_upload_file: function(fileobj, args, opts, dataurl) {
|
||||
_upload_file: function(fileobj, args, opts) {
|
||||
if (args.file_size) {
|
||||
frappe.upload.validate_max_file_size(args.file_size);
|
||||
}
|
||||
|
||||
if(opts.on_attach) {
|
||||
opts.on_attach(args, dataurl)
|
||||
opts.on_attach(args)
|
||||
} else {
|
||||
if (opts.confirm_is_private) {
|
||||
frappe.prompt({
|
||||
|
|
@ -248,55 +249,53 @@ frappe.upload = {
|
|||
"default": 1
|
||||
}, function(values) {
|
||||
args["is_private"] = values.is_private;
|
||||
frappe.upload.upload_to_server(fileobj, args, opts, dataurl);
|
||||
frappe.upload.upload_to_server(fileobj, args, opts);
|
||||
}, __("Private or Public?"));
|
||||
} else {
|
||||
if ("is_private" in opts) {
|
||||
args["is_private"] = opts.is_private;
|
||||
}
|
||||
|
||||
frappe.upload.upload_to_server(fileobj, args, opts, dataurl);
|
||||
frappe.upload.upload_to_server(fileobj, args, opts);
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
read_file: function(fileobj, args, opts) {
|
||||
var freader = new FileReader();
|
||||
args.filename = fileobj.name.split(' ').join('_');
|
||||
args.file_url = null;
|
||||
|
||||
freader.onload = function() {
|
||||
args.filename = fileobj.name.split(' ').join('_');
|
||||
if(opts.options && opts.options.toLowerCase()=="image") {
|
||||
if(!frappe.utils.is_image_file(args.filename)) {
|
||||
frappe.msgprint(__("Only image extensions (.gif, .jpg, .jpeg, .tiff, .png, .svg) allowed"));
|
||||
return;
|
||||
}
|
||||
if(opts.options && opts.options.toLowerCase()=="image") {
|
||||
if(!frappe.utils.is_image_file(args.filename)) {
|
||||
frappe.msgprint(__("Only image extensions (.gif, .jpg, .jpeg, .tiff, .png, .svg) allowed"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if((opts.max_width || opts.max_height) && frappe.utils.is_image_file(args.filename)) {
|
||||
frappe.utils.resize_image(freader, function(_dataurl) {
|
||||
var dataurl = _dataurl;
|
||||
args.filedata = _dataurl.split(",")[1];
|
||||
args.file_size = Math.round(args.filedata.length * 3 / 4);
|
||||
console.log("resized!")
|
||||
frappe.upload._upload_file(fileobj, args, opts, dataurl);
|
||||
})
|
||||
} else {
|
||||
var dataurl = freader.result;
|
||||
args.filedata = freader.result.split(",")[1];
|
||||
args.file_size = fileobj.size;
|
||||
frappe.upload._upload_file(fileobj, args, opts, dataurl);
|
||||
let start_complete = frappe.cur_progress ? frappe.cur_progress.percent : 0;
|
||||
|
||||
frappe.socketio.uploader.start({
|
||||
file: fileobj,
|
||||
filename: args.filename,
|
||||
is_private: args.is_private,
|
||||
callback: (data) => {
|
||||
args.file_url = data.file_url;
|
||||
frappe.upload._upload_file(fileobj, args, opts);
|
||||
},
|
||||
on_progress: (percent_complete) => {
|
||||
let increment = (flt(percent_complete) / frappe.upload.total_files);
|
||||
frappe.show_progress(__('Uploading'),
|
||||
start_complete + increment);
|
||||
}
|
||||
};
|
||||
|
||||
freader.readAsDataURL(fileobj);
|
||||
});
|
||||
},
|
||||
|
||||
upload_to_server: function(fileobj, args, opts, dataurl) {
|
||||
// var msgbox = frappe.msgprint(__("Uploading..."));
|
||||
upload_to_server: function(file, args, opts) {
|
||||
if(opts.start) {
|
||||
opts.start();
|
||||
}
|
||||
|
||||
var ajax_args = {
|
||||
"method": "uploadfile",
|
||||
args: args,
|
||||
|
|
@ -368,7 +367,7 @@ frappe.upload = {
|
|||
d.hide();
|
||||
opts.loopcallback = function (){
|
||||
if (i < j) {
|
||||
args.is_private = d.fields_dict[fileobjs[i].name + "_is_private"].get_value()
|
||||
args.is_private = d.fields_dict[fileobjs[i].name + "_is_private"].get_value();
|
||||
frappe.upload.upload_file(fileobjs[i], args, opts);
|
||||
i++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -749,11 +749,9 @@ select.form-control {
|
|||
.control-code, .control-code.bold {
|
||||
height: 400px;
|
||||
font-family: Monaco, "Courier New", monospace;
|
||||
background-color: black;
|
||||
color: @light-yellow;
|
||||
color: @text-color;
|
||||
font-size: 12px;
|
||||
line-height: 1.7em;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.delivery-status-indicator {
|
||||
|
|
|
|||
|
|
@ -130,15 +130,6 @@ body[data-route^="Module"] .main-menu {
|
|||
right: 5px;
|
||||
}
|
||||
|
||||
// .attachment-row .icon-lock {
|
||||
// color: @text-warning;
|
||||
// display: inline-block;
|
||||
// margin-top: 1px;
|
||||
// }
|
||||
|
||||
.attachment-row a.close {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.form-shared, .form-viewers {
|
||||
.share-doc-btn {
|
||||
|
|
|
|||
51
frappe/tests/ui/test_control_html.js
Normal file
51
frappe/tests/ui/test_control_html.js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
QUnit.module('controls');
|
||||
|
||||
QUnit.test("Test ControlHTML", function(assert) {
|
||||
assert.expect(3);
|
||||
const random_name = frappe.utils.get_random(3).toLowerCase();
|
||||
|
||||
let done = assert.async();
|
||||
|
||||
frappe.run_serially([
|
||||
() => {
|
||||
return frappe.tests.make('Custom Field', [
|
||||
{dt: 'ToDo'},
|
||||
{fieldtype: 'HTML'},
|
||||
{label: random_name},
|
||||
{options: '<h3> Test </h3>'}
|
||||
]);
|
||||
},
|
||||
() => {
|
||||
return frappe.tests.make('Custom Field', [
|
||||
{dt: 'ToDo'},
|
||||
{fieldtype: 'HTML'},
|
||||
{label: random_name + "_template"},
|
||||
{options: '<h3> Test {%= doc.status %} </h3>'}
|
||||
]);
|
||||
},
|
||||
() => frappe.set_route('List', 'ToDo'),
|
||||
() => frappe.new_doc('ToDo'),
|
||||
() => {
|
||||
if (frappe.quick_entry)
|
||||
{
|
||||
frappe.quick_entry.dialog.$wrapper.find('.edit-full').click();
|
||||
return frappe.timeout(1);
|
||||
}
|
||||
},
|
||||
() => {
|
||||
const control = $(`.frappe-control[data-fieldname="${random_name}"]`)[0];
|
||||
return assert.ok(control.innerHTML === '<h3> Test </h3>');
|
||||
},
|
||||
() => {
|
||||
const control = $(`.frappe-control[data-fieldname="${random_name}_template"]`)[0];
|
||||
return assert.ok(control.innerHTML === '<h3> Test Open </h3>');
|
||||
},
|
||||
() => frappe.tests.set_control("status", "Closed"),
|
||||
() => frappe.timeout(1),
|
||||
() => {
|
||||
const control = $(`.frappe-control[data-fieldname="${random_name}_template"]`)[0];
|
||||
return assert.ok(control.innerHTML === '<h3> Test Closed </h3>');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
|
@ -13,3 +13,4 @@ frappe/custom/doctype/customize_form/test_customize_form.js
|
|||
frappe/desk/doctype/event/test_event.js
|
||||
frappe/workflow/doctype/workflow/tests/test_workflow_create.js
|
||||
frappe/workflow/doctype/workflow/tests/test_workflow_test.js
|
||||
frappe/tests/ui/test_control_html.js
|
||||
|
|
|
|||
|
|
@ -179,11 +179,13 @@ def delete_temp_backups(older_than=24):
|
|||
"""
|
||||
Cleans up the backup_link_path directory by deleting files older than 24 hours
|
||||
"""
|
||||
file_list = os.listdir(get_backup_path())
|
||||
for this_file in file_list:
|
||||
this_file_path = os.path.join(get_backup_path(), this_file)
|
||||
if is_file_old(this_file_path, older_than):
|
||||
os.remove(this_file_path)
|
||||
backup_path = get_backup_path()
|
||||
if os.path.exists(backup_path):
|
||||
file_list = os.listdir(get_backup_path())
|
||||
for this_file in file_list:
|
||||
this_file_path = os.path.join(get_backup_path(), this_file)
|
||||
if is_file_old(this_file_path, older_than):
|
||||
os.remove(this_file_path)
|
||||
|
||||
def is_file_old(db_file_name, older_than=24):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from six import text_type
|
|||
|
||||
class MaxFileSizeReachedError(frappe.ValidationError): pass
|
||||
|
||||
|
||||
def get_file_url(file_data_name):
|
||||
data = frappe.db.get_value("File", file_data_name, ["file_name", "file_url"], as_dict=True)
|
||||
return data.file_url or data.file_name
|
||||
|
|
@ -23,38 +24,51 @@ def upload():
|
|||
# get record details
|
||||
dt = frappe.form_dict.doctype
|
||||
dn = frappe.form_dict.docname
|
||||
folder = frappe.form_dict.folder
|
||||
file_url = frappe.form_dict.file_url
|
||||
filename = frappe.form_dict.filename
|
||||
is_private = cint(frappe.form_dict.is_private)
|
||||
frappe.form_dict.is_private = cint(frappe.form_dict.is_private)
|
||||
|
||||
if not filename and not file_url:
|
||||
frappe.msgprint(_("Please select a file or url"),
|
||||
raise_exception=True)
|
||||
|
||||
# save
|
||||
if frappe.form_dict.filedata:
|
||||
filedata = save_uploaded(dt, dn, folder, is_private)
|
||||
elif file_url:
|
||||
filedata = save_url(file_url, filename, dt, dn, folder, is_private)
|
||||
file_doc = get_file_doc()
|
||||
|
||||
comment = {}
|
||||
if dt and dn:
|
||||
comment = frappe.get_doc(dt, dn).add_comment("Attachment",
|
||||
_("added {0}").format("<a href='{file_url}' target='_blank'>{file_name}</a>{icon}".format(**{
|
||||
"icon": ' <i class="fa fa-lock text-warning"></i>' if filedata.is_private else "",
|
||||
"file_url": filedata.file_url.replace("#", "%23") if filedata.file_name else filedata.file_url,
|
||||
"file_name": filedata.file_name or filedata.file_url
|
||||
"icon": ' <i class="fa fa-lock text-warning"></i>' \
|
||||
if file_doc.is_private else "",
|
||||
"file_url": file_doc.file_url.replace("#", "%23") \
|
||||
if file_doc.file_name else file_doc.file_url,
|
||||
"file_name": file_doc.file_name or file_doc.file_url
|
||||
})))
|
||||
|
||||
return {
|
||||
"name": filedata.name,
|
||||
"file_name": filedata.file_name,
|
||||
"file_url": filedata.file_url,
|
||||
"is_private": filedata.is_private,
|
||||
"name": file_doc.name,
|
||||
"file_name": file_doc.file_name,
|
||||
"file_url": file_doc.file_url,
|
||||
"is_private": file_doc.is_private,
|
||||
"comment": comment.as_dict() if comment else {}
|
||||
}
|
||||
|
||||
def get_file_doc(dt=None, dn=None, folder=None, is_private=None):
|
||||
'''returns File object (Document) from given parameters or form_dict'''
|
||||
r = frappe.form_dict
|
||||
|
||||
if dt is None: dt = r.doctype
|
||||
if dn is None: dn = r.docname
|
||||
if folder is None: folder = r.folder
|
||||
if is_private is None: is_private = r.is_private
|
||||
|
||||
if r.filedata:
|
||||
file_doc = save_uploaded(dt, dn, folder, is_private)
|
||||
elif r.file_url:
|
||||
file_doc = save_url(r.file_url, r.filename, dt, dn, folder, is_private)
|
||||
|
||||
return file_doc
|
||||
|
||||
def save_uploaded(dt, dn, folder, is_private):
|
||||
fname, content = get_uploaded_content()
|
||||
if content:
|
||||
|
|
@ -97,55 +111,6 @@ def get_uploaded_content():
|
|||
frappe.msgprint(_('No file attached'))
|
||||
return None, None
|
||||
|
||||
def extract_images_from_doc(doc, fieldname):
|
||||
content = doc.get(fieldname)
|
||||
content = extract_images_from_html(doc, content)
|
||||
if frappe.flags.has_dataurl:
|
||||
doc.set(fieldname, content)
|
||||
|
||||
def extract_images_from_html(doc, content):
|
||||
frappe.flags.has_dataurl = False
|
||||
|
||||
def _save_file(match):
|
||||
data = match.group(1)
|
||||
data = data.split("data:")[1]
|
||||
headers, content = data.split(",")
|
||||
|
||||
if "filename=" in headers:
|
||||
filename = headers.split("filename=")[-1]
|
||||
|
||||
# decode filename
|
||||
if not isinstance(filename, text_type):
|
||||
filename = text_type(filename, 'utf-8')
|
||||
else:
|
||||
mtype = headers.split(";")[0]
|
||||
filename = get_random_filename(content_type=mtype)
|
||||
|
||||
doctype = doc.parenttype if doc.parent else doc.doctype
|
||||
name = doc.parent or doc.name
|
||||
|
||||
# TODO fix this
|
||||
file_url = save_file(filename, content, doctype, name, decode=True).get("file_url")
|
||||
if not frappe.flags.has_dataurl:
|
||||
frappe.flags.has_dataurl = True
|
||||
|
||||
return '<img src="{file_url}"'.format(file_url=file_url)
|
||||
|
||||
if content:
|
||||
content = re.sub('<img[^>]*src\s*=\s*["\'](?=data:)(.*?)["\']', _save_file, content)
|
||||
|
||||
return content
|
||||
|
||||
def get_random_filename(extn=None, content_type=None):
|
||||
if extn:
|
||||
if not extn.startswith("."):
|
||||
extn = "." + extn
|
||||
|
||||
elif content_type:
|
||||
extn = mimetypes.guess_extension(content_type)
|
||||
|
||||
return random_string(7) + (extn or "")
|
||||
|
||||
def save_file(fname, content, dt, dn, folder=None, decode=False, is_private=0):
|
||||
if decode:
|
||||
if isinstance(content, text_type):
|
||||
|
|
@ -370,3 +335,52 @@ def download_file(file_url):
|
|||
frappe.local.response.filename = file_url[file_url.rfind("/")+1:]
|
||||
frappe.local.response.filecontent = filedata
|
||||
frappe.local.response.type = "download"
|
||||
|
||||
def extract_images_from_doc(doc, fieldname):
|
||||
content = doc.get(fieldname)
|
||||
content = extract_images_from_html(doc, content)
|
||||
if frappe.flags.has_dataurl:
|
||||
doc.set(fieldname, content)
|
||||
|
||||
def extract_images_from_html(doc, content):
|
||||
frappe.flags.has_dataurl = False
|
||||
|
||||
def _save_file(match):
|
||||
data = match.group(1)
|
||||
data = data.split("data:")[1]
|
||||
headers, content = data.split(",")
|
||||
|
||||
if "filename=" in headers:
|
||||
filename = headers.split("filename=")[-1]
|
||||
|
||||
# decode filename
|
||||
if not isinstance(filename, text_type):
|
||||
filename = text_type(filename, 'utf-8')
|
||||
else:
|
||||
mtype = headers.split(";")[0]
|
||||
filename = get_random_filename(content_type=mtype)
|
||||
|
||||
doctype = doc.parenttype if doc.parent else doc.doctype
|
||||
name = doc.parent or doc.name
|
||||
|
||||
# TODO fix this
|
||||
file_url = save_file(filename, content, doctype, name, decode=True).get("file_url")
|
||||
if not frappe.flags.has_dataurl:
|
||||
frappe.flags.has_dataurl = True
|
||||
|
||||
return '<img src="{file_url}"'.format(file_url=file_url)
|
||||
|
||||
if content:
|
||||
content = re.sub('<img[^>]*src\s*=\s*["\'](?=data:)(.*?)["\']', _save_file, content)
|
||||
|
||||
return content
|
||||
|
||||
def get_random_filename(extn=None, content_type=None):
|
||||
if extn:
|
||||
if not extn.startswith("."):
|
||||
extn = "." + extn
|
||||
|
||||
elif content_type:
|
||||
extn = mimetypes.guess_extension(content_type)
|
||||
|
||||
return random_string(7) + (extn or "")
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// MIT License. See license.txt
|
||||
|
||||
cur_frm.cscript.refresh = function(doc) {
|
||||
cur_frm.set_intro("");
|
||||
if(doc.__islocal) {
|
||||
cur_frm.set_intro("First set the name and save the record.");
|
||||
frappe.ui.form.on("Website Slideshow", {
|
||||
refresh: (frm) => {
|
||||
let intro = frm.doc.__islocal?
|
||||
"First set the name and save the record.": "Attach files / urls and add in table.";
|
||||
frm.set_intro(intro);
|
||||
}
|
||||
else {
|
||||
cur_frm.set_intro("Attach files / urls and add in table.");
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:slideshow_name",
|
||||
|
|
@ -13,33 +14,7 @@
|
|||
"editable_grid": 0,
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "slideshow_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Slideshow Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -52,6 +27,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "",
|
||||
|
|
@ -69,6 +45,36 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "slideshow_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Slideshow Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -80,6 +86,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Slideshow Items",
|
||||
|
|
@ -98,6 +105,7 @@
|
|||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -110,6 +118,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Header",
|
||||
|
|
@ -127,18 +136,18 @@
|
|||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-play",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 10,
|
||||
"modified": "2016-12-29 14:40:39.690960",
|
||||
"modified": "2017-09-18 11:19:31.627585",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Website Slideshow",
|
||||
|
|
@ -154,7 +163,6 @@
|
|||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"is_custom": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
|
|
@ -169,6 +177,7 @@
|
|||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
|
|
@ -5,15 +5,26 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
class WebsiteSlideshow(Document):
|
||||
def validate(self):
|
||||
self.validate_images()
|
||||
|
||||
def on_update(self):
|
||||
# a slide show can be in use and any change in it should get reflected
|
||||
from frappe.website.render import clear_cache
|
||||
clear_cache()
|
||||
|
||||
def validate_images(self):
|
||||
''' atleast one image file should be public for slideshow '''
|
||||
files = map(lambda row: row.image, self.slideshow_items)
|
||||
result = frappe.get_all("File", filters={ "file_url":("in", files) }, fields="is_private")
|
||||
if any([file.is_private for file in result]):
|
||||
frappe.throw(_("All Images attached to Website Slideshow should be public"))
|
||||
|
||||
def get_slideshow(doc):
|
||||
if not doc.slideshow:
|
||||
return {}
|
||||
|
|
|
|||
|
|
@ -168,6 +168,9 @@ def build_page(path):
|
|||
frappe.local.path = path
|
||||
|
||||
context = get_context(path)
|
||||
if "{{" in context.title:
|
||||
title_template = context.pop('title')
|
||||
context.title = frappe.render_template(title_template, context)
|
||||
|
||||
if context.source:
|
||||
html = frappe.render_template(context.source, context)
|
||||
|
|
|
|||
61
socketio.js
61
socketio.js
|
|
@ -3,11 +3,22 @@ var http = require('http').Server(app);
|
|||
var io = require('socket.io')(http);
|
||||
var cookie = require('cookie')
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var redis = require("redis");
|
||||
var request = require('superagent');
|
||||
|
||||
var conf = get_conf();
|
||||
var flags = {};
|
||||
var files_struct = {
|
||||
name: null,
|
||||
type: null,
|
||||
size: 0,
|
||||
data: [],
|
||||
slice: 0,
|
||||
site_name: null,
|
||||
is_private: 0
|
||||
};
|
||||
|
||||
var subscriber = redis.createClient(conf.redis_socketio || conf.redis_async_broker_port);
|
||||
|
||||
// serve socketio
|
||||
|
|
@ -21,7 +32,7 @@ app.get('/', function(req, res) {
|
|||
});
|
||||
|
||||
// on socket connection
|
||||
io.on('connection', function(socket){
|
||||
io.on('connection', function(socket) {
|
||||
if (get_hostname(socket.request.headers.host) != get_hostname(socket.request.headers.origin)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -41,6 +52,7 @@ io.on('connection', function(socket){
|
|||
setTimeout(function() { flags[sid] = null; }, 10000);
|
||||
|
||||
socket.user = cookie.parse(socket.request.headers.cookie).user_id;
|
||||
socket.files = {};
|
||||
|
||||
// console.log("firing get_user_info");
|
||||
request.get(get_url(socket, '/api/method/frappe.async.get_user_info'))
|
||||
|
|
@ -61,6 +73,10 @@ io.on('connection', function(socket){
|
|||
}
|
||||
});
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
delete socket.files;
|
||||
})
|
||||
|
||||
socket.on('task_subscribe', function(task_id) {
|
||||
var room = get_task_room(socket, task_id);
|
||||
socket.join(room);
|
||||
|
|
@ -134,9 +150,46 @@ io.on('connection', function(socket){
|
|||
});
|
||||
});
|
||||
|
||||
// socket.on('disconnect', function (arguments) {
|
||||
// console.log("user disconnected", arguments);
|
||||
// });
|
||||
socket.on('upload-accept-slice', (data) => {
|
||||
try {
|
||||
if (!socket.files[data.name]) {
|
||||
socket.files[data.name] = Object.assign({}, files_struct, data);
|
||||
socket.files[data.name].data = [];
|
||||
}
|
||||
|
||||
//convert the ArrayBuffer to Buffer
|
||||
data.data = new Buffer(new Uint8Array(data.data));
|
||||
//save the data
|
||||
socket.files[data.name].data.push(data.data);
|
||||
socket.files[data.name].slice++;
|
||||
|
||||
if (socket.files[data.name].slice * 100000 >= socket.files[data.name].size) {
|
||||
// do something with the data
|
||||
var fileBuffer = Buffer.concat(socket.files[data.name].data);
|
||||
|
||||
const file_url = path.join((socket.files[data.name].is_private ? 'private' : 'public'),
|
||||
'files', data.name);
|
||||
const file_path = path.join('sites', get_site_name(socket), file_url);
|
||||
|
||||
fs.writeFile(file_path, fileBuffer, (err) => {
|
||||
delete socket.files[data.name];
|
||||
if (err) return socket.emit('upload error');
|
||||
socket.emit('upload-end', {
|
||||
file_url: '/' + file_url
|
||||
});
|
||||
});
|
||||
} else {
|
||||
socket.emit('upload-request-slice', {
|
||||
currentSlice: socket.files[data.name].slice
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
socket.emit('upload-error', {
|
||||
error: e.message
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
subscriber.on("message", function(channel, message) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue