@@ -1,7 +1,7 @@ | |||
# imports - standard imports | |||
import os | |||
import sys | |||
import shutil | |||
import sys | |||
# imports - third party imports | |||
import click | |||
@@ -65,11 +65,11 @@ def restore(context, sql_file_path, encryption_key=None, db_root_username=None, | |||
"Restore site database from an sql file" | |||
from frappe.installer import ( | |||
_new_site, | |||
extract_sql_from_archive, | |||
extract_files, | |||
extract_sql_from_archive, | |||
is_downgrade, | |||
is_partial, | |||
validate_database_sql | |||
validate_database_sql, | |||
) | |||
from frappe.utils.backups import Backup | |||
if not os.path.exists(sql_file_path): | |||
@@ -207,7 +207,7 @@ def restore(context, sql_file_path, encryption_key=None, db_root_username=None, | |||
@click.option('--encryption-key', help='Backup encryption key') | |||
@pass_context | |||
def partial_restore(context, sql_file_path, verbose, encryption_key=None): | |||
from frappe.installer import partial_restore, extract_sql_from_archive | |||
from frappe.installer import extract_sql_from_archive, partial_restore | |||
from frappe.utils.backups import Backup | |||
if not os.path.exists(sql_file_path): | |||
@@ -545,7 +545,7 @@ def _use(site, sites_path='.'): | |||
def use(site, sites_path='.'): | |||
if os.path.exists(os.path.join(sites_path, site)): | |||
with open(os.path.join(sites_path, "currentsite.txt"), "w") as sitefile: | |||
with open(os.path.join(sites_path, "currentsite.txt"), "w") as sitefile: | |||
sitefile.write(site) | |||
print("Current Site set to {}".format(site)) | |||
else: | |||
@@ -751,6 +751,7 @@ def set_admin_password(context, admin_password=None, logout_all_sessions=False): | |||
def set_user_password(site, user, password, logout_all_sessions=False): | |||
import getpass | |||
from frappe.utils.password import update_password | |||
try: | |||
@@ -881,15 +882,16 @@ def stop_recording(context): | |||
raise SiteNotSpecifiedError | |||
@click.command('ngrok') | |||
@click.option('--bind-tls', is_flag=True, default=False, help='Returns a reference to the https tunnel.') | |||
@pass_context | |||
def start_ngrok(context): | |||
def start_ngrok(context, bind_tls): | |||
from pyngrok import ngrok | |||
site = get_site(context) | |||
frappe.init(site=site) | |||
port = frappe.conf.http_port or frappe.conf.webserver_port | |||
tunnel = ngrok.connect(addr=str(port), host_header=site) | |||
tunnel = ngrok.connect(addr=str(port), host_header=site, bind_tls=bind_tls) | |||
print(f'Public URL: {tunnel.public_url}') | |||
print('Inspect logs at http://localhost:4040') | |||
@@ -18,6 +18,7 @@ from urllib.parse import unquote | |||
from frappe.utils.user import is_system_user | |||
from frappe.contacts.doctype.contact.contact import get_contact_name | |||
from frappe.automation.doctype.assignment_rule.assignment_rule import apply as apply_assignment_rule | |||
from parse import compile | |||
exclude_from_linked_with = True | |||
@@ -114,6 +115,44 @@ class Communication(Document, CommunicationEmailMixin): | |||
frappe.publish_realtime('new_message', self.as_dict(), | |||
user=self.reference_name, after_commit=True) | |||
def set_signature_in_email_content(self): | |||
"""Set sender's User.email_signature or default outgoing's EmailAccount.signature to the email | |||
""" | |||
if not self.content: | |||
return | |||
quill_parser = compile('<div class="ql-editor read-mode">{}</div>') | |||
email_body = quill_parser.parse(self.content) | |||
if not email_body: | |||
return | |||
email_body = email_body[0] | |||
user_email_signature = frappe.db.get_value( | |||
"User", | |||
self.sender, | |||
"email_signature", | |||
) if self.sender else None | |||
signature = user_email_signature or frappe.db.get_value( | |||
"Email Account", | |||
{"default_outgoing": 1, "add_signature": 1}, | |||
"signature", | |||
) | |||
if not signature: | |||
return | |||
_signature = quill_parser.parse(signature)[0] if "ql-editor" in signature else None | |||
if (_signature or signature) not in self.content: | |||
self.content = f'{self.content}</p><br><p class="signature">{signature}' | |||
def before_save(self): | |||
if not self.flags.skip_add_signature: | |||
self.set_signature_in_email_content() | |||
def on_update(self): | |||
# add to _comment property of the doctype, so it shows up in | |||
# comments count for the list view | |||
@@ -22,12 +22,30 @@ OUTGOING_EMAIL_ACCOUNT_MISSING = _(""" | |||
@frappe.whitelist() | |||
def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent", | |||
sender=None, sender_full_name=None, recipients=None, communication_medium="Email", send_email=False, | |||
print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, bcc=None, | |||
flags=None, read_receipt=None, print_letterhead=True, email_template=None, communication_type=None, | |||
ignore_permissions=False) -> Dict[str, str]: | |||
"""Make a new communication. | |||
def make( | |||
doctype=None, | |||
name=None, | |||
content=None, | |||
subject=None, | |||
sent_or_received="Sent", | |||
sender=None, | |||
sender_full_name=None, | |||
recipients=None, | |||
communication_medium="Email", | |||
send_email=False, | |||
print_html=None, | |||
print_format=None, | |||
attachments="[]", | |||
send_me_a_copy=False, | |||
cc=None, | |||
bcc=None, | |||
read_receipt=None, | |||
print_letterhead=True, | |||
email_template=None, | |||
communication_type=None, | |||
**kwargs, | |||
) -> Dict[str, str]: | |||
"""Make a new communication. Checks for email permissions for specified Document. | |||
:param doctype: Reference DocType. | |||
:param name: Reference Document name. | |||
@@ -44,17 +62,71 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = | |||
:param send_me_a_copy: Send a copy to the sender (default **False**). | |||
:param email_template: Template which is used to compose mail . | |||
""" | |||
is_error_report = (doctype=="User" and name==frappe.session.user and subject=="Error Report") | |||
send_me_a_copy = cint(send_me_a_copy) | |||
if kwargs: | |||
from frappe.utils.commands import warn | |||
warn( | |||
f"Options {kwargs} used in frappe.core.doctype.communication.email.make " | |||
"are deprecated or unsupported", | |||
category=DeprecationWarning | |||
) | |||
if doctype and name and not frappe.has_permission(doctype=doctype, ptype="email", doc=name): | |||
raise frappe.PermissionError( | |||
f"You are not allowed to send emails related to: {doctype} {name}" | |||
) | |||
return _make( | |||
doctype=doctype, | |||
name=name, | |||
content=content, | |||
subject=subject, | |||
sent_or_received=sent_or_received, | |||
sender=sender, | |||
sender_full_name=sender_full_name, | |||
recipients=recipients, | |||
communication_medium=communication_medium, | |||
send_email=send_email, | |||
print_html=print_html, | |||
print_format=print_format, | |||
attachments=attachments, | |||
send_me_a_copy=cint(send_me_a_copy), | |||
cc=cc, | |||
bcc=bcc, | |||
read_receipt=read_receipt, | |||
print_letterhead=print_letterhead, | |||
email_template=email_template, | |||
communication_type=communication_type, | |||
add_signature=False, | |||
) | |||
if not ignore_permissions: | |||
if doctype and name and not is_error_report and not frappe.has_permission(doctype, "email", name) and not (flags or {}).get('ignore_doctype_permissions'): | |||
raise frappe.PermissionError("You are not allowed to send emails related to: {doctype} {name}".format( | |||
doctype=doctype, name=name)) | |||
if not sender: | |||
sender = get_formatted_email(frappe.session.user) | |||
def _make( | |||
doctype=None, | |||
name=None, | |||
content=None, | |||
subject=None, | |||
sent_or_received="Sent", | |||
sender=None, | |||
sender_full_name=None, | |||
recipients=None, | |||
communication_medium="Email", | |||
send_email=False, | |||
print_html=None, | |||
print_format=None, | |||
attachments="[]", | |||
send_me_a_copy=False, | |||
cc=None, | |||
bcc=None, | |||
read_receipt=None, | |||
print_letterhead=True, | |||
email_template=None, | |||
communication_type=None, | |||
add_signature=True, | |||
) -> Dict[str, str]: | |||
"""Internal method to make a new communication that ignores Permission checks. | |||
""" | |||
sender = sender or get_formatted_email(frappe.session.user) | |||
recipients = list_to_str(recipients) if isinstance(recipients, list) else recipients | |||
cc = list_to_str(cc) if isinstance(cc, list) else cc | |||
bcc = list_to_str(bcc) if isinstance(bcc, list) else bcc | |||
@@ -77,7 +149,9 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = | |||
"read_receipt":read_receipt, | |||
"has_attachment": 1 if attachments else 0, | |||
"communication_type": communication_type, | |||
}).insert(ignore_permissions=True) | |||
}) | |||
comm.flags.skip_add_signature = not add_signature | |||
comm.insert(ignore_permissions=True) | |||
# if not committed, delayed task doesn't find the communication | |||
if attachments: | |||
@@ -87,17 +161,21 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = | |||
if cint(send_email): | |||
if not comm.get_outgoing_email_account(): | |||
frappe.throw(msg=OUTGOING_EMAIL_ACCOUNT_MISSING, exc=frappe.OutgoingEmailError) | |||
frappe.throw( | |||
msg=OUTGOING_EMAIL_ACCOUNT_MISSING, exc=frappe.OutgoingEmailError | |||
) | |||
comm.send_email(print_html=print_html, print_format=print_format, | |||
send_me_a_copy=send_me_a_copy, print_letterhead=print_letterhead) | |||
comm.send_email( | |||
print_html=print_html, | |||
print_format=print_format, | |||
send_me_a_copy=send_me_a_copy, | |||
print_letterhead=print_letterhead, | |||
) | |||
emails_not_sent_to = comm.exclude_emails_list(include_sender=send_me_a_copy) | |||
return { | |||
"name": comm.name, | |||
"emails_not_sent_to": ", ".join(emails_not_sent_to) | |||
} | |||
return {"name": comm.name, "emails_not_sent_to": ", ".join(emails_not_sent_to)} | |||
def validate_email(doc: "Communication") -> None: | |||
"""Validate Email Addresses of Recipients and CC""" | |||
@@ -668,8 +668,7 @@ | |||
"link_fieldname": "user" | |||
} | |||
], | |||
"max_attachments": 5, | |||
"modified": "2022-01-03 11:53:25.250822", | |||
"modified": "2022-03-09 01:47:56.745069", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "User", | |||
@@ -1,9 +1,10 @@ | |||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors | |||
# License: MIT. See LICENSE | |||
import json | |||
from collections import defaultdict | |||
import itertools | |||
from typing import List | |||
from typing import Dict, List, Optional | |||
import frappe | |||
import frappe.desk.form.load | |||
@@ -367,7 +368,7 @@ def get_exempted_doctypes(): | |||
@frappe.whitelist() | |||
def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None): | |||
def get_linked_docs(doctype: str, name: str, linkinfo: Optional[Dict] = None) -> Dict[str, List]: | |||
if isinstance(linkinfo, str): | |||
# additional fields are added in linkinfo | |||
linkinfo = json.loads(linkinfo) | |||
@@ -377,23 +378,21 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None): | |||
if not linkinfo: | |||
return results | |||
if for_doctype: | |||
links = frappe.get_doc(doctype, name).get_link_filters(for_doctype) | |||
if links: | |||
linkinfo = links | |||
if for_doctype in linkinfo: | |||
# only get linked with for this particular doctype | |||
linkinfo = { for_doctype: linkinfo.get(for_doctype) } | |||
else: | |||
return results | |||
for dt, link in linkinfo.items(): | |||
filters = [] | |||
link["doctype"] = dt | |||
link_meta_bundle = frappe.desk.form.load.get_meta_bundle(dt) | |||
try: | |||
link_meta_bundle = frappe.desk.form.load.get_meta_bundle(dt) | |||
except Exception as e: | |||
if isinstance(e, frappe.DoesNotExistError): | |||
if frappe.local.message_log: | |||
frappe.local.message_log.pop() | |||
continue | |||
linkmeta = link_meta_bundle[0] | |||
if not linkmeta.has_permission(): | |||
continue | |||
if not linkmeta.get("issingle"): | |||
fields = [d.fieldname for d in linkmeta.get("fields", { | |||
"in_list_view": 1, | |||
@@ -456,6 +455,13 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None): | |||
return results | |||
@frappe.whitelist() | |||
def get(doctype, docname): | |||
linked_doctypes = get_linked_doctypes(doctype=doctype) | |||
return get_linked_docs(doctype=doctype, name=docname, linkinfo=linked_doctypes) | |||
@frappe.whitelist() | |||
def get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False): | |||
"""add list of doctypes this doctype is 'linked' with. | |||
@@ -470,6 +476,7 @@ def get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False): | |||
else: | |||
return frappe.cache().hget("linked_doctypes", doctype, lambda: _get_linked_doctypes(doctype)) | |||
def _get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False): | |||
ret = {} | |||
# find fields where this doctype is linked | |||
@@ -499,6 +506,7 @@ def _get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False) | |||
return ret | |||
def get_linked_fields(doctype, without_ignore_user_permissions_enabled=False): | |||
filters = [['fieldtype','=', 'Link'], ['options', '=', doctype]] | |||
@@ -529,6 +537,7 @@ def get_linked_fields(doctype, without_ignore_user_permissions_enabled=False): | |||
return ret | |||
def get_dynamic_linked_fields(doctype, without_ignore_user_permissions_enabled=False): | |||
ret = {} | |||
@@ -236,8 +236,7 @@ | |||
"index_web_pages_for_search": 1, | |||
"is_published_field": "published", | |||
"links": [], | |||
"max_attachments": 3, | |||
"modified": "2021-12-06 20:09:37.963141", | |||
"modified": "2022-03-09 01:48:16.741603", | |||
"modified_by": "Administrator", | |||
"module": "Email", | |||
"name": "Newsletter", | |||
@@ -186,7 +186,7 @@ def get_context(context): | |||
def send_an_email(self, doc, context): | |||
from email.utils import formataddr | |||
from frappe.core.doctype.communication.email import make as make_communication | |||
from frappe.core.doctype.communication.email import _make as make_communication | |||
subject = self.subject | |||
if "{" in subject: | |||
subject = frappe.render_template(self.subject, context) | |||
@@ -216,7 +216,8 @@ def get_context(context): | |||
# Add mail notification to communication list | |||
# No need to add if it is already a communication. | |||
if doc.doctype != 'Communication': | |||
make_communication(doctype=doc.doctype, | |||
make_communication( | |||
doctype=doc.doctype, | |||
name=doc.name, | |||
content=message, | |||
subject=subject, | |||
@@ -228,7 +229,7 @@ def get_context(context): | |||
cc=cc, | |||
bcc=bcc, | |||
communication_type='Automated Message', | |||
ignore_permissions=True) | |||
) | |||
def send_a_slack_msg(self, doc, context): | |||
send_slack_message( | |||
@@ -259,17 +259,12 @@ def get_formatted_html(subject, message, footer=None, print_html=None, | |||
email_account = email_account or EmailAccount.find_outgoing(match_by_email=sender) | |||
signature = None | |||
if "<!-- signature-included -->" not in message: | |||
signature = get_signature(email_account) | |||
rendered_email = frappe.get_template("templates/emails/standard.html").render({ | |||
"brand_logo": get_brand_logo(email_account) if with_container or header else None, | |||
"with_container": with_container, | |||
"site_url": get_url(), | |||
"header": get_header(header), | |||
"content": message, | |||
"signature": signature, | |||
"footer": get_footer(email_account, footer), | |||
"title": subject, | |||
"print_html": print_html, | |||
@@ -281,8 +276,7 @@ def get_formatted_html(subject, message, footer=None, print_html=None, | |||
if unsubscribe_link: | |||
html = html.replace("<!--unsubscribe link here-->", unsubscribe_link.html) | |||
html = inline_style_in_html(html) | |||
return html | |||
return inline_style_in_html(html) | |||
@frappe.whitelist() | |||
def get_email_html(template, args, subject, header=None, with_container=False): | |||
@@ -43,8 +43,8 @@ def update_document_title( | |||
title_field = doc.meta.get_title_field() | |||
title_updated = (title_field != "name") and (updated_title != doc.get(title_field)) | |||
name_updated = updated_name != doc.name | |||
title_updated = updated_title and (title_field != "name") and (updated_title != doc.get(title_field)) | |||
name_updated = updated_name and (updated_name != doc.name) | |||
if name_updated: | |||
docname = rename_doc(doctype=doctype, old=docname, new=updated_name, merge=merge) | |||
@@ -1,9 +1,8 @@ | |||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||
// MIT License. See license.txt | |||
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors | |||
// MIT License. See LICENSE | |||
frappe.ui.form.LinkedWith = class LinkedWith { | |||
constructor(opts) { | |||
$.extend(this, opts); | |||
} | |||
@@ -21,29 +20,23 @@ frappe.ui.form.LinkedWith = class LinkedWith { | |||
} | |||
make_dialog() { | |||
this.dialog = new frappe.ui.Dialog({ | |||
title: __("Linked With") | |||
}); | |||
this.dialog.on_page_show = () => { | |||
// execute ajax calls sequentially | |||
// 1. get linked doctypes | |||
// 2. load all doctypes | |||
// 3. load linked docs | |||
this.get_linked_doctypes() | |||
.then(() => this.load_doctypes()) | |||
.then(() => this.links_not_permitted_or_missing()) | |||
.then(() => this.get_linked_docs()) | |||
.then(() => this.make_html()); | |||
frappe.xcall( | |||
"frappe.desk.form.linked_with.get", | |||
{"doctype": cur_frm.doctype, "docname": cur_frm.docname}, | |||
).then(r => { | |||
this.frm.__linked_docs = r; | |||
}).then(() => this.make_html()); | |||
}; | |||
} | |||
make_html() { | |||
const linked_docs = this.frm.__linked_docs; | |||
let html = ''; | |||
const linked_docs = this.frm.__linked_docs; | |||
const linked_doctypes = Object.keys(linked_docs); | |||
if (linked_doctypes.length === 0) { | |||
@@ -63,88 +56,6 @@ frappe.ui.form.LinkedWith = class LinkedWith { | |||
$(this.dialog.body).html(html); | |||
} | |||
load_doctypes() { | |||
const already_loaded = Object.keys(locals.DocType); | |||
let doctypes_to_load = []; | |||
if (this.frm.__linked_doctypes) { | |||
doctypes_to_load = | |||
Object.keys(this.frm.__linked_doctypes) | |||
.filter(doctype => !already_loaded.includes(doctype)); | |||
} | |||
// load all doctypes asynchronously using with_doctype | |||
const promises = doctypes_to_load.map(dt => { | |||
return frappe.model.with_doctype(dt, () => { | |||
if(frappe.listview_settings[dt]) { | |||
// add additional fields to __linked_doctypes | |||
this.frm.__linked_doctypes[dt].add_fields = | |||
frappe.listview_settings[dt].add_fields; | |||
} | |||
}); | |||
}); | |||
return Promise.all(promises); | |||
} | |||
links_not_permitted_or_missing() { | |||
let links = null; | |||
if (this.frm.__linked_doctypes) { | |||
links = | |||
Object.keys(this.frm.__linked_doctypes) | |||
.filter(frappe.model.can_get_report); | |||
} | |||
let flag; | |||
if(!links) { | |||
$(this.dialog.body).html(`${this.frm.__linked_doctypes | |||
? __("Not enough permission to see links") | |||
: __("Not Linked to any record")}`); | |||
flag = true; | |||
} | |||
flag = false; | |||
// reject Promise if not_permitted or missing | |||
return new Promise( | |||
(resolve, reject) => flag ? reject() : resolve() | |||
); | |||
} | |||
get_linked_doctypes() { | |||
return new Promise((resolve) => { | |||
if (this.frm.__linked_doctypes) { | |||
resolve(); | |||
} | |||
frappe.call({ | |||
method: "frappe.desk.form.linked_with.get_linked_doctypes", | |||
args: { | |||
doctype: this.frm.doctype | |||
}, | |||
callback: (r) => { | |||
this.frm.__linked_doctypes = r.message; | |||
resolve(); | |||
} | |||
}); | |||
}); | |||
} | |||
get_linked_docs() { | |||
return frappe.call({ | |||
method: "frappe.desk.form.linked_with.get_linked_docs", | |||
args: { | |||
doctype: this.frm.doctype, | |||
name: this.frm.docname, | |||
linkinfo: this.frm.__linked_doctypes, | |||
for_doctype: this.for_doctype | |||
}, | |||
callback: (r) => { | |||
this.frm.__linked_docs = r.message || {}; | |||
} | |||
}); | |||
} | |||
make_doc_head(heading) { | |||
return ` | |||
<header class="level list-row list-row-head text-muted small"> | |||
@@ -760,6 +760,10 @@ class FilterArea { | |||
const doctype_fields = this.list_view.meta.fields; | |||
const title_field = this.list_view.meta.title_field; | |||
const has_existing_filters = ( | |||
this.list_view.filters | |||
&& this.list_view.filters.length > 0 | |||
); | |||
fields = fields.concat( | |||
doctype_fields | |||
@@ -794,13 +798,17 @@ class FilterArea { | |||
options = options.join("\n"); | |||
} | |||
} | |||
let default_value = | |||
fieldtype === "Link" | |||
? frappe.defaults.get_user_default(options) | |||
: null; | |||
let default_value; | |||
if (fieldtype === "Link" && !has_existing_filters) { | |||
default_value = frappe.defaults.get_user_default(options); | |||
} | |||
if (["__default", "__global"].includes(default_value)) { | |||
default_value = null; | |||
} | |||
return { | |||
fieldtype: fieldtype, | |||
label: __(df.label), | |||
@@ -83,32 +83,15 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { | |||
this.sort_by = this.view_user_settings.sort_by || "modified"; | |||
this.sort_order = this.view_user_settings.sort_order || "desc"; | |||
// set filters from user_settings or list_settings | |||
if ( | |||
this.view_user_settings.filters && | |||
this.view_user_settings.filters.length | |||
) { | |||
// Priority 1: user_settings | |||
const saved_filters = this.view_user_settings.filters; | |||
this.filters = this.validate_filters(saved_filters); | |||
} else { | |||
// Priority 2: filters in listview_settings | |||
this.filters = (this.settings.filters || []).map((f) => { | |||
if (f.length === 3) { | |||
f = [this.doctype, f[0], f[1], f[2]]; | |||
} | |||
return f; | |||
}); | |||
} | |||
// build menu items | |||
this.menu_items = this.menu_items.concat(this.get_menu_items()); | |||
// set filters from view_user_settings or list_settings | |||
if ( | |||
this.view_user_settings.filters && | |||
this.view_user_settings.filters.length | |||
) { | |||
// Priority 1: saved filters | |||
// Priority 1: view_user_settings | |||
const saved_filters = this.view_user_settings.filters; | |||
this.filters = this.validate_filters(saved_filters); | |||
} else { | |||
@@ -125,11 +125,12 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { | |||
} | |||
after_render() { | |||
if (this.report_doc) { | |||
this.set_dirty_state_for_custom_report(); | |||
} else { | |||
if (!this.report_doc) { | |||
this.save_report_settings(); | |||
} else if (!$.isEmptyObject(this.report_doc.json)) { | |||
this.set_dirty_state_for_custom_report(); | |||
} | |||
if (!this.group_by) { | |||
this.init_chart(); | |||
} | |||
@@ -37,7 +37,6 @@ | |||
<tr> | |||
<td valign="top"> | |||
<p>{{ content }}</p> | |||
<p class="signature">{{ signature }}</p> | |||
</td> | |||
</tr> | |||
</table> | |||
@@ -213,8 +213,7 @@ | |||
"index_web_pages_for_search": 1, | |||
"is_published_field": "published", | |||
"links": [], | |||
"max_attachments": 5, | |||
"modified": "2021-11-23 10:42:01.759723", | |||
"modified": "2022-03-09 01:48:25.227295", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Blog Post", | |||
@@ -338,8 +338,7 @@ | |||
"index_web_pages_for_search": 1, | |||
"is_published_field": "published", | |||
"links": [], | |||
"max_attachments": 20, | |||
"modified": "2022-01-03 13:01:48.182645", | |||
"modified": "2022-03-09 01:45:28.548671", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Web Page", | |||
@@ -420,8 +420,7 @@ | |||
"index_web_pages_for_search": 1, | |||
"issingle": 1, | |||
"links": [], | |||
"max_attachments": 10, | |||
"modified": "2022-02-24 15:37:22.360138", | |||
"modified": "2022-03-09 01:47:31.094462", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Website Settings", | |||
@@ -29,6 +29,7 @@ maxminddb-geolite2==2018.703 | |||
num2words~=0.5.10 | |||
oauthlib~=3.1.0 | |||
openpyxl~=3.0.7 | |||
parse~=1.19.0 | |||
passlib~=1.7.4 | |||
paytmchecksum~=1.7.0 | |||
pdfkit~=0.6.1 | |||