diff --git a/.eslintrc b/.eslintrc index a2538feab5..d123023a68 100644 --- a/.eslintrc +++ b/.eslintrc @@ -147,6 +147,7 @@ "context": true, "before": true, "beforeEach": true, - "qz": true + "qz": true, + "localforage": true } } diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 416d782ffe..1964b96d70 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -312,7 +312,6 @@ Cypress.Commands.add('add_filter', () => { cy.get('.filter-section .filter-button').click(); cy.wait(300); cy.get('.filter-popover').should('exist'); - cy.get('.filter-popover').find('.add-filter').click(); }); Cypress.Commands.add('clear_filters', () => { diff --git a/frappe/app.py b/frappe/app.py index adf2bfa8c9..29ef69ef2d 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -181,6 +181,9 @@ def make_form_dict(request): else: args = request.form or request.args + if not isinstance(args, dict): + frappe.throw("Invalid request arguments") + try: frappe.local.form_dict = frappe._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \ for k, v in iteritems(args) }) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 569414e98b..1533829b3c 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -555,7 +555,7 @@ }, { "group": "Customization", - "link_doctype": "Custom Script", + "link_doctype": "Client Script", "link_fieldname": "dt" }, { @@ -609,7 +609,7 @@ "link_fieldname": "reference_doctype" } ], - "modified": "2020-12-10 15:10:09.227205", + "modified": "2021-02-04 15:10:09.227205", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index 0d6aa3d7d1..e02d9e5db0 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -3,6 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals +from typing import Dict, List import frappe, json from frappe.model.document import Document @@ -11,12 +12,13 @@ from datetime import datetime from croniter import croniter from frappe.utils.background_jobs import enqueue, get_jobs + class ScheduledJobType(Document): def autoname(self): - self.name = '.'.join(self.method.split('.')[-2:]) + self.name = ".".join(self.method.split(".")[-2:]) def validate(self): - if self.frequency != 'All': + if self.frequency != "All": # force logging for all events other than continuous ones (ALL) self.create_log = 1 @@ -84,7 +86,7 @@ class ScheduledJobType(Document): def log_status(self, status): # log file - frappe.logger("scheduler").info('Scheduled Job {0}: {1} for {2}'.format(status, self.method, frappe.local.site)) + frappe.logger("scheduler").info(f"Scheduled Job {status}: {self.method} for {frappe.local.site}") self.update_scheduler_log(status) def update_scheduler_log(self, status): @@ -111,28 +113,28 @@ class ScheduledJobType(Document): @frappe.whitelist() -def execute_event(doc): - frappe.only_for('System Manager') +def execute_event(doc: str): + frappe.only_for("System Manager") doc = json.loads(doc) - frappe.get_doc('Scheduled Job Type', doc.get('name')).enqueue(force=True) + frappe.get_doc("Scheduled Job Type", doc.get("name")).enqueue(force=True) -def run_scheduled_job(job_type): - '''This is a wrapper function that runs a hooks.scheduler_events method''' +def run_scheduled_job(job_type: str): + """This is a wrapper function that runs a hooks.scheduler_events method""" try: - frappe.get_doc('Scheduled Job Type', dict(method=job_type)).execute() + frappe.get_doc("Scheduled Job Type", dict(method=job_type)).execute() except Exception: print(frappe.get_traceback()) -def sync_jobs(hooks=None): +def sync_jobs(hooks: Dict = None): frappe.reload_doc("core", "doctype", "scheduled_job_type") scheduler_events = hooks or frappe.get_hooks("scheduler_events") all_events = insert_events(scheduler_events) clear_events(all_events) -def insert_events(scheduler_events): +def insert_events(scheduler_events: Dict) -> List: cron_jobs, event_jobs = [], [] for event_type in scheduler_events: events = scheduler_events.get(event_type) @@ -144,7 +146,7 @@ def insert_events(scheduler_events): return cron_jobs + event_jobs -def insert_cron_jobs(events): +def insert_cron_jobs(events: Dict) -> List: cron_jobs = [] for cron_format in events: for event in events.get(cron_format): @@ -153,25 +155,29 @@ def insert_cron_jobs(events): return cron_jobs -def insert_event_jobs(events, event_type): +def insert_event_jobs(events: List, event_type: str) -> List: event_jobs = [] for event in events: event_jobs.append(event) - frequency = event_type.replace('_', ' ').title() + frequency = event_type.replace("_", " ").title() insert_single_event(frequency, event) return event_jobs -def insert_single_event(frequency, event, cron_format=None): +def insert_single_event(frequency: str, event: str, cron_format: str = None): cron_expr = {"cron_format": cron_format} if cron_format else {} - doc = frappe.get_doc({ - "doctype": "Scheduled Job Type", - "method": event, - "cron_format": cron_format, - "frequency": frequency - }) + doc = frappe.get_doc( + { + "doctype": "Scheduled Job Type", + "method": event, + "cron_format": cron_format, + "frequency": frequency, + } + ) - if not frappe.db.exists("Scheduled Job Type", {"method": event, "frequency": frequency, **cron_expr }): + if not frappe.db.exists( + "Scheduled Job Type", {"method": event, "frequency": frequency, **cron_expr} + ): try: doc.insert() except frappe.DuplicateEntryError: @@ -179,7 +185,12 @@ def insert_single_event(frequency, event, cron_format=None): doc.insert() -def clear_events(all_events): - for event in frappe.get_all("Scheduled Job Type", ("name", "method")): - if event.method not in all_events: +def clear_events(all_events: List): + for event in frappe.get_all( + "Scheduled Job Type", fields=["name", "method", "server_script"] + ): + is_server_script = event.server_script + is_defined_in_hooks = event.method in all_events + + if not (is_defined_in_hooks or is_server_script): frappe.delete_doc("Scheduled Job Type", event.name) diff --git a/frappe/core/doctype/server_script/server_script.js b/frappe/core/doctype/server_script/server_script.js index a317d69166..95a63780f8 100644 --- a/frappe/core/doctype/server_script/server_script.js +++ b/frappe/core/doctype/server_script/server_script.js @@ -6,46 +6,11 @@ frappe.ui.form.on('Server Script', { frm.trigger('setup_help'); }, refresh: function(frm) { - if (frm.doc.script_type === 'Scheduler Event' && !frm.doc.disabled) { - frm.add_custom_button('Schedule Script', function() { - var d = new frappe.ui.Dialog({ - title: "Schedule Script Execution", - fields: [ - { - fieldname: "event_type", - label: __('Select Event Type'), - fieldtype: "Select", - options: "All\nHourly\nDaily\nWeekly\nMonthly\nYearly\nHourly Long\nDaily Long\nWeekly Long\nMonthly Long" - }, - ], - primary_action_label: __('Schedule Script'), - primary_action: () => { - d.get_primary_btn().attr('disabled', true); - var data = d.get_values(); - d.hide(); - if(data) { - frm.events.schedule_script(frm, data); - } - - } - }); - - d.show(); - - }); + if (frm.doc.script_type != 'Scheduler Event') { + frm.dashboard.hide(); } }, - schedule_script(frm, data) { - frm.call({ - method: "frappe.core.doctype.server_script.server_script.setup_scheduler_events", - args: { - 'script_name': frm.doc.name, - 'frequency': data.event_type - } - }); - }, - setup_help(frm) { frm.get_field('help_html').html(`

DocType Event

diff --git a/frappe/core/doctype/server_script/server_script.json b/frappe/core/doctype/server_script/server_script.json index 9aa7b5afe5..b7e49673f8 100644 --- a/frappe/core/doctype/server_script/server_script.json +++ b/frappe/core/doctype/server_script/server_script.json @@ -8,6 +8,7 @@ "field_order": [ "script_type", "reference_doctype", + "event_frequency", "doctype_event", "api_method", "allow_guest", @@ -84,11 +85,24 @@ { "fieldname": "help_html", "fieldtype": "HTML" + }, + { + "depends_on": "eval:doc.script_type == \"Scheduler Event\"", + "fieldname": "event_frequency", + "fieldtype": "Select", + "label": "Event Frequency", + "mandatory_depends_on": "eval:doc.script_type == \"Scheduler Event\"", + "options": "All\nHourly\nDaily\nWeekly\nMonthly\nYearly\nHourly Long\nDaily Long\nWeekly Long\nMonthly Long" } ], "index_web_pages_for_search": 1, - "links": [], - "modified": "2021-01-03 18:50:14.767595", + "links": [ + { + "link_doctype": "Scheduled Job Type", + "link_fieldname": "server_script" + } + ], + "modified": "2021-02-18 12:36:19.803425", "modified_by": "Administrator", "module": "Core", "name": "Server Script", diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index 88d68dba14..8838d9e954 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import ast +from typing import Dict, List import frappe from frappe.model.document import Document @@ -14,67 +15,146 @@ from frappe import _ class ServerScript(Document): def validate(self): - frappe.only_for('Script Manager', True) + frappe.only_for("Script Manager", True) + self.validate_script() + self.sync_scheduled_jobs() + self.clear_scheduled_events() + + def on_update(self): + frappe.cache().delete_value("server_script_map") + self.sync_scheduler_events() + + def on_trash(self): + if self.script_type == "Scheduler Event": + for job in self.scheduled_jobs: + frappe.delete_doc("Scheduled Job Type", job.name) + + @property + def scheduled_jobs(self) -> List[Dict[str, str]]: + return frappe.get_all( + "Scheduled Job Type", + filters={"server_script": self.name}, + fields=["name", "stopped"], + ) + + def validate_script(self): + """Utilizes the ast module to check for syntax errors + """ ast.parse(self.script) - @staticmethod - def on_update(): - frappe.cache().delete_value('server_script_map') + def sync_scheduled_jobs(self): + """Sync Scheduled Job Type statuses if Server Script's disabled status is changed + """ + if self.script_type != "Scheduler Event" or not self.has_value_changed("disabled"): + return - def execute_method(self): - if self.script_type == 'API': - # validate if guest is allowed - if frappe.session.user == 'Guest' and not self.allow_guest: - raise frappe.PermissionError - _globals, _locals = safe_exec(self.script) - return _globals.frappe.flags # output can be stored in flags - else: - # wrong report type! + for scheduled_job in self.scheduled_jobs: + if bool(scheduled_job.stopped) != bool(self.disabled): + job = frappe.get_doc("Scheduled Job Type", scheduled_job.name) + job.stopped = self.disabled + job.save() + + def sync_scheduler_events(self): + """Create or update Scheduled Job Type documents for Scheduler Event Server Scripts + """ + if not self.disabled and self.event_frequency and self.script_type == "Scheduler Event": + setup_scheduler_events(script_name=self.name, frequency=self.event_frequency) + + def clear_scheduled_events(self): + """Deletes existing scheduled jobs by Server Script if self.event_frequency has changed + """ + if self.script_type == "Scheduler Event" and self.has_value_changed("event_frequency"): + for scheduled_job in self.scheduled_jobs: + frappe.delete_doc("Scheduled Job Type", scheduled_job.name) + + def execute_method(self) -> Dict: + """Specific to API endpoint Server Scripts + + Raises: + frappe.DoesNotExistError: If self.script_type is not API + frappe.PermissionError: If self.allow_guest is unset for API accessed by Guest user + + Returns: + dict: Evaluates self.script with frappe.utils.safe_exec.safe_exec and returns the flags set in it's safe globals + """ + # wrong report type! + if self.script_type != "API": raise frappe.DoesNotExistError - def execute_doc(self, doc): - # execute event - safe_exec(self.script, None, dict(doc = doc)) + # validate if guest is allowed + if frappe.session.user == "Guest" and not self.allow_guest: + raise frappe.PermissionError + + # output can be stored in flags + _globals, _locals = safe_exec(self.script) + return _globals.frappe.flags + + def execute_doc(self, doc: Document): + """Specific to Document Event triggered Server Scripts + + Args: + doc (Document): Executes script with for a certain document's events + """ + safe_exec(self.script, _locals={"doc": doc}) def execute_scheduled_method(self): - if self.script_type == 'Scheduler Event': - safe_exec(self.script) - else: - # wrong report type! + """Specific to Scheduled Jobs via Server Scripts + + Raises: + frappe.DoesNotExistError: If script type is not a scheduler event + """ + if self.script_type != "Scheduler Event": raise frappe.DoesNotExistError - def get_permission_query_conditions(self, user): + safe_exec(self.script) + + def get_permission_query_conditions(self, user: str) -> List[str]: + """Specific to Permission Query Server Scripts + + Args: + user (str): Takes user email to execute script and return list of conditions + + Returns: + list: Returns list of conditions defined by rules in self.script + """ locals = {"user": user, "conditions": ""} safe_exec(self.script, None, locals) if locals["conditions"]: return locals["conditions"] + @frappe.whitelist() def setup_scheduler_events(script_name, frequency): - method = frappe.scrub('{0}-{1}'.format(script_name, frequency)) - scheduled_script = frappe.db.get_value('Scheduled Job Type', - dict(method=method)) + """Creates or Updates Scheduled Job Type documents based on the specified script name and frequency + + Args: + script_name (str): Name of the Server Script document + frequency (str): Event label compatible with the Frappe scheduler + """ + method = frappe.scrub(f"{script_name}-{frequency}") + scheduled_script = frappe.db.get_value("Scheduled Job Type", {"method": method}) if not scheduled_script: - doc = frappe.get_doc(dict( - doctype = 'Scheduled Job Type', - method = method, - frequency = frequency, - server_script = script_name - )) + frappe.get_doc( + { + "doctype": "Scheduled Job Type", + "method": method, + "frequency": frequency, + "server_script": script_name, + } + ).insert() - doc.insert() - - frappe.msgprint(_('Enabled scheduled execution for script {0}').format(script_name)) + frappe.msgprint(_("Enabled scheduled execution for script {0}").format(script_name)) else: - doc = frappe.get_doc('Scheduled Job Type', scheduled_script) - doc.update(dict( - doctype = 'Scheduled Job Type', - method = method, - frequency = frequency, - server_script = script_name - )) + doc = frappe.get_doc("Scheduled Job Type", scheduled_script) + + if doc.frequency == frequency: + return + + doc.frequency = frequency doc.save() - frappe.msgprint(_('Scheduled execution for script {0} has updated').format(script_name)) + frappe.msgprint( + _("Scheduled execution for script {0} has updated").format(script_name) + ) diff --git a/frappe/core/workspace/build/build.json b/frappe/core/workspace/build/build.json index c4bde55d7f..aefda698b1 100644 --- a/frappe/core/workspace/build/build.json +++ b/frappe/core/workspace/build/build.json @@ -11,6 +11,7 @@ "hide_custom": 0, "icon": "tool", "idx": 0, + "is_default": 0, "is_standard": 1, "label": "Build", "links": [ @@ -163,8 +164,8 @@ { "hidden": 0, "is_query_report": 0, - "label": "Custom Script", - "link_to": "Custom Script", + "label": "Client Script", + "link_to": "Client Script", "link_type": "DocType", "onboard": 0, "only_for": "", @@ -181,7 +182,7 @@ "type": "Link" } ], - "modified": "2021-01-02 14:03:15.029699", + "modified": "2021-02-04 13:48:48.493146", "modified_by": "Administrator", "module": "Core", "name": "Build", diff --git a/frappe/custom/doctype/custom_script/README.md b/frappe/custom/doctype/client_script/README.md similarity index 100% rename from frappe/custom/doctype/custom_script/README.md rename to frappe/custom/doctype/client_script/README.md diff --git a/frappe/custom/doctype/custom_script/__init__.py b/frappe/custom/doctype/client_script/__init__.py similarity index 100% rename from frappe/custom/doctype/custom_script/__init__.py rename to frappe/custom/doctype/client_script/__init__.py diff --git a/frappe/custom/doctype/custom_script/custom_script.js b/frappe/custom/doctype/client_script/client_script.js similarity index 97% rename from frappe/custom/doctype/custom_script/custom_script.js rename to frappe/custom/doctype/client_script/client_script.js index 711e7d1796..21e7334b82 100644 --- a/frappe/custom/doctype/custom_script/custom_script.js +++ b/frappe/custom/doctype/client_script/client_script.js @@ -1,7 +1,7 @@ // Copyright (c) 2016, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on('Custom Script', { +frappe.ui.form.on('Client Script', { refresh(frm) { if (frm.doc.dt && frm.doc.script) { frm.add_custom_button(__('Go to {0}', [frm.doc.dt]), diff --git a/frappe/custom/doctype/custom_script/custom_script.json b/frappe/custom/doctype/client_script/client_script.json similarity index 86% rename from frappe/custom/doctype/custom_script/custom_script.json rename to frappe/custom/doctype/client_script/client_script.json index 328b247c49..57e6c68094 100644 --- a/frappe/custom/doctype/custom_script/custom_script.json +++ b/frappe/custom/doctype/client_script/client_script.json @@ -2,7 +2,7 @@ "actions": [], "allow_import": 1, "creation": "2013-01-10 16:34:01", - "description": "Adds a client custom script to a DocType", + "description": "Adds a custom client script to a DocType", "doctype": "DocType", "document_type": "Document", "engine": "InnoDB", @@ -22,9 +22,7 @@ "oldfieldname": "dt", "oldfieldtype": "Link", "options": "DocType", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "script", @@ -32,35 +30,29 @@ "label": "Script", "oldfieldname": "script", "oldfieldtype": "Code", - "options": "JS", - "show_days": 1, - "show_seconds": 1 + "options": "JS" }, { "fieldname": "sample", "fieldtype": "HTML", "label": "Sample", - "options": "

Custom Script Help

\n

Custom Scripts are executed only on the client-side (i.e. in Forms). Here are some examples to get you started

\n
\n\n// fetch local_tax_no on selection of customer \n// cur_frm.add_fetch(link_field,  source_fieldname,  target_fieldname); \ncur_frm.add_fetch('customer',  'local_tax_no',  'local_tax_no');\n\n// additional validation on dates \nfrappe.ui.form.on('Task',  'validate',  function(frm) {\n    if (frm.doc.from_date < get_today()) {\n        msgprint('You can not select past date in From Date');\n        validated = false;\n    } \n});\n\n// make a field read-only after saving \nfrappe.ui.form.on('Task',  {\n    refresh: function(frm) {\n        // use the __islocal value of doc,  to check if the doc is saved or not\n        frm.set_df_property('myfield',  'read_only',  frm.doc.__islocal ? 0 : 1);\n    } \n});\n\n// additional permission check\nfrappe.ui.form.on('Task',  {\n    validate: function(frm) {\n        if(user=='user1@example.com' && frm.doc.purpose!='Material Receipt') {\n            msgprint('You are only allowed Material Receipt');\n            validated = false;\n        }\n    } \n});\n\n// calculate sales incentive\nfrappe.ui.form.on('Sales Invoice',  {\n    validate: function(frm) {\n        // calculate incentives for each person on the deal\n        total_incentive = 0\n        $.each(frm.doc.sales_team,  function(i,  d) {\n            // calculate incentive\n            var incentive_percent = 2;\n            if(frm.doc.base_grand_total > 400) incentive_percent = 4;\n            // actual incentive\n            d.incentives = flt(frm.doc.base_grand_total) * incentive_percent / 100;\n            total_incentive += flt(d.incentives)\n        });\n        frm.doc.total_incentive = total_incentive;\n    } \n})\n\n
", - "show_days": 1, - "show_seconds": 1 + "options": "

Client Script Help

\n

Client Scripts are executed only on the client-side (i.e. in Forms). Here are some examples to get you started

\n
\n\n// fetch local_tax_no on selection of customer \n// cur_frm.add_fetch(link_field,  source_fieldname,  target_fieldname); \ncur_frm.add_fetch('customer',  'local_tax_no',  'local_tax_no');\n\n// additional validation on dates \nfrappe.ui.form.on('Task',  'validate',  function(frm) {\n    if (frm.doc.from_date < get_today()) {\n        msgprint('You can not select past date in From Date');\n        validated = false;\n    } \n});\n\n// make a field read-only after saving \nfrappe.ui.form.on('Task',  {\n    refresh: function(frm) {\n        // use the __islocal value of doc,  to check if the doc is saved or not\n        frm.set_df_property('myfield',  'read_only',  frm.doc.__islocal ? 0 : 1);\n    } \n});\n\n// additional permission check\nfrappe.ui.form.on('Task',  {\n    validate: function(frm) {\n        if(user=='user1@example.com' && frm.doc.purpose!='Material Receipt') {\n            msgprint('You are only allowed Material Receipt');\n            validated = false;\n        }\n    } \n});\n\n// calculate sales incentive\nfrappe.ui.form.on('Sales Invoice',  {\n    validate: function(frm) {\n        // calculate incentives for each person on the deal\n        total_incentive = 0\n        $.each(frm.doc.sales_team,  function(i,  d) {\n            // calculate incentive\n            var incentive_percent = 2;\n            if(frm.doc.base_grand_total > 400) incentive_percent = 4;\n            // actual incentive\n            d.incentives = flt(frm.doc.base_grand_total) * incentive_percent / 100;\n            total_incentive += flt(d.incentives)\n        });\n        frm.doc.total_incentive = total_incentive;\n    } \n})\n\n
" }, { "default": "0", "fieldname": "enabled", "fieldtype": "Check", - "label": "Enabled", - "show_days": 1, - "show_seconds": 1 + "label": "Enabled" } ], "icon": "fa fa-glass", "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-08-24 21:56:07.719579", + "modified": "2021-02-04 13:57:56.509437", "modified_by": "Administrator", "module": "Custom", - "name": "Custom Script", + "name": "Client Script", "owner": "Administrator", "permissions": [ { @@ -86,6 +78,7 @@ "write": 1 } ], + "sort_field": "modified", "sort_order": "ASC", "track_changes": 1 } \ No newline at end of file diff --git a/frappe/custom/doctype/custom_script/custom_script.py b/frappe/custom/doctype/client_script/client_script.py similarity index 84% rename from frappe/custom/doctype/custom_script/custom_script.py rename to frappe/custom/doctype/client_script/client_script.py index e15819de65..e252e2a750 100644 --- a/frappe/custom/doctype/custom_script/custom_script.py +++ b/frappe/custom/doctype/client_script/client_script.py @@ -5,9 +5,9 @@ import frappe from frappe.model.document import Document -class CustomScript(Document): +class ClientScript(Document): def autoname(self): - self.name = self.dt + "-Client" + self.name = self.dt def on_update(self): frappe.clear_cache(doctype=self.dt) diff --git a/frappe/custom/doctype/custom_script/test_custom_script.py b/frappe/custom/doctype/client_script/test_client_script.py similarity index 65% rename from frappe/custom/doctype/custom_script/test_custom_script.py rename to frappe/custom/doctype/client_script/test_client_script.py index 6947e6060d..de113c1ce7 100644 --- a/frappe/custom/doctype/custom_script/test_custom_script.py +++ b/frappe/custom/doctype/client_script/test_client_script.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe import unittest -# test_records = frappe.get_test_records('Custom Script') +# test_records = frappe.get_test_records('Client Script') -class TestCustomScript(unittest.TestCase): +class TestClientScript(unittest.TestCase): pass diff --git a/frappe/custom/workspace/customization/customization.json b/frappe/custom/workspace/customization/customization.json index 3631914249..cdc3b73366 100644 --- a/frappe/custom/workspace/customization/customization.json +++ b/frappe/custom/workspace/customization/customization.json @@ -10,6 +10,7 @@ "hide_custom": 0, "icon": "customization", "idx": 0, + "is_default": 0, "is_standard": 1, "label": "Customization", "links": [ @@ -81,8 +82,8 @@ "dependencies": "", "hidden": 0, "is_query_report": 0, - "label": "Custom Script", - "link_to": "Custom Script", + "label": "Client Script", + "link_to": "Client Script", "link_type": "DocType", "onboard": 0, "type": "Link" @@ -115,7 +116,7 @@ "type": "Link" } ], - "modified": "2020-12-01 13:38:39.843773", + "modified": "2021-02-04 13:50:35.750463", "modified_by": "Administrator", "module": "Custom", "name": "Customization", @@ -134,8 +135,14 @@ "type": "DocType" }, { - "label": "Custom Script", - "link_to": "Custom Script", + "label": "Client Script", + "link_to": "Client Script", + "type": "DocType" + }, + { + "doc_view": "", + "label": "Server Script", + "link_to": "Server Script", "type": "DocType" } ] diff --git a/frappe/desk/doctype/kanban_board/kanban_board.py b/frappe/desk/doctype/kanban_board/kanban_board.py index f1ad41db6c..a655e9e1da 100644 --- a/frappe/desk/doctype/kanban_board/kanban_board.py +++ b/frappe/desk/doctype/kanban_board/kanban_board.py @@ -17,6 +17,10 @@ class KanbanBoard(Document): def on_update(self): frappe.clear_cache(doctype=self.reference_doctype) + def before_insert(self): + for column in self.columns: + column.order = get_order_for_column(self, column.column_name) + def validate_column_name(self): for column in self.columns: if not column.column_name: @@ -125,6 +129,53 @@ def update_order(board_name, order): board.save() return board, updated_cards +@frappe.whitelist() +def update_order_for_single_card(board_name, docname, from_colname, to_colname, old_index, new_index): + '''Save the order of cards in columns''' + board = frappe.get_doc('Kanban Board', board_name) + doctype = board.reference_doctype + fieldname = board.field_name + old_index = frappe.parse_json(old_index) + new_index = frappe.parse_json(new_index) + + # save current order and index of columns to be updated + from_col_order, from_col_idx = get_kanban_column_order_and_index(board, from_colname) + to_col_order, to_col_idx = get_kanban_column_order_and_index(board, to_colname) + + if from_colname == to_colname: + from_col_order = to_col_order + + to_col_order.insert(new_index, from_col_order.pop((old_index))) + + # save updated order + board.columns[from_col_idx].order = frappe.as_json(from_col_order) + board.columns[to_col_idx].order = frappe.as_json(to_col_order) + board.save() + + # update changed value in doc + frappe.set_value(doctype, docname, fieldname, to_colname) + + return board + +def get_kanban_column_order_and_index(board, colname): + for i, col in enumerate(board.columns): + if col.column_name == colname: + col_order = frappe.parse_json(col.order) + col_idx = i + + return col_order, col_idx + +@frappe.whitelist() +def add_card(board_name, docname, colname): + board = frappe.get_doc('Kanban Board', board_name) + + col_order, col_idx = get_kanban_column_order_and_index(board, colname) + col_order.insert(0, docname) + + board.columns[col_idx].order = frappe.as_json(col_order) + + board.save() + return board @frappe.whitelist() def quick_kanban_board(doctype, board_name, field_name, project=None): @@ -133,6 +184,13 @@ def quick_kanban_board(doctype, board_name, field_name, project=None): doc = frappe.new_doc('Kanban Board') meta = frappe.get_meta(doctype) + doc.kanban_board_name = board_name + doc.reference_doctype = doctype + doc.field_name = field_name + + if project: + doc.filters = '[["Task","project","=","{0}"]]'.format(project) + options = '' for field in meta.fields: if field.fieldname == field_name: @@ -149,12 +207,6 @@ def quick_kanban_board(doctype, board_name, field_name, project=None): column_name=column )) - doc.kanban_board_name = board_name - doc.reference_doctype = doctype - doc.field_name = field_name - - if project: - doc.filters = '[["Task","project","=","{0}"]]'.format(project) if doctype in ['Note', 'ToDo']: doc.private = 1 @@ -162,6 +214,12 @@ def quick_kanban_board(doctype, board_name, field_name, project=None): doc.save() return doc +def get_order_for_column(board, colname): + filters = [[board.reference_doctype, board.field_name, '=', colname]] + if board.filters: + filters.append(frappe.parse_json(board.filters)[0]) + + return frappe.as_json(frappe.get_list(board.reference_doctype, filters=filters, pluck='name')) @frappe.whitelist() def update_column_order(board_name, order): diff --git a/frappe/desk/form/meta.py b/frappe/desk/form/meta.py index d5428b1da2..c63da93a33 100644 --- a/frappe/desk/form/meta.py +++ b/frappe/desk/form/meta.py @@ -130,7 +130,7 @@ class FormMeta(Meta): def add_custom_script(self): """embed all require files""" # custom script - custom = frappe.db.get_value("Custom Script", {"dt": self.name, "enabled": 1}, "script") or "" + custom = frappe.db.get_value("Client Script", {"dt": self.name, "enabled": 1}, "script") or "" self.set("__custom_js", custom) diff --git a/frappe/desk/form/utils.py b/frappe/desk/form/utils.py index 4c3bab2e23..395d2b9571 100644 --- a/frappe/desk/form/utils.py +++ b/frappe/desk/form/utils.py @@ -47,7 +47,7 @@ def validate_link(): except Exception as e: error_message = str(e).split("Unknown column '") fieldname = None if len(error_message)<=1 else error_message[1].split("'")[0] - frappe.msgprint(_("Wrong fieldname {0} in add_fetch configuration of custom script").format(fieldname)) + frappe.msgprint(_("Wrong fieldname {0} in add_fetch configuration of custom client script").format(fieldname)) frappe.errprint(frappe.get_traceback()) if fetch_value: diff --git a/frappe/desk/page/leaderboard/leaderboard.js b/frappe/desk/page/leaderboard/leaderboard.js index 825e1d959b..b3fccf84f9 100644 --- a/frappe/desk/page/leaderboard/leaderboard.js +++ b/frappe/desk/page/leaderboard/leaderboard.js @@ -141,7 +141,7 @@ class Leaderboard { } create_date_range_field() { - let timespan_field = $(this.parent).find(`.frappe-control[data-original-title='Timespan']`); + let timespan_field = $(this.parent).find(`.frappe-control[data-original-title=${__('Timespan')}]`); this.date_range_field = $(`
`).insertAfter(timespan_field).hide(); let date_field = frappe.ui.form.make_control({ diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index c39a73ccd7..af06696621 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -118,7 +118,7 @@ core_doctypes_list = ( 'Customize Form Field', 'Property Setter', 'Custom Field', - 'Custom Script' + 'Client Script' ) log_types = ( diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 7b29692ad1..d0e0a6fb1a 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -68,7 +68,7 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa check_permission_and_not_submitted(doc) frappe.db.sql("delete from `tabCustom Field` where dt = %s", name) - frappe.db.sql("delete from `tabCustom Script` where dt = %s", name) + frappe.db.sql("delete from `tabClient Script` where dt = %s", name) frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", name) frappe.db.sql("delete from `tabReport` where ref_doctype=%s", name) frappe.db.sql("delete from `tabCustom DocPerm` where parent=%s", name) diff --git a/frappe/model/document.py b/frappe/model/document.py index 1cd981ead8..3ecc335cdd 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -1015,6 +1015,8 @@ class Document(BaseDocument): def notify_update(self): """Publish realtime that the current document is modified""" + if frappe.flags.in_patch: return + frappe.publish_realtime("doc_update", {"modified": self.modified, "doctype": self.doctype, "name": self.name}, doctype=self.doctype, docname=self.name, after_commit=True) diff --git a/frappe/patches.txt b/frappe/patches.txt index 0f37946398..5400c96354 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -329,3 +329,5 @@ frappe.core.doctype.page.patches.drop_unused_pages execute:frappe.get_doc('Role', 'Guest').save() # remove desk access frappe.patches.v13_0.rename_desk_page_to_workspace # 02.02.2021 frappe.patches.v13_0.delete_package_publish_tool +frappe.patches.v13_0.rename_list_view_setting_to_list_view_settings +frappe.patches.v13_0.rename_custom_client_script diff --git a/frappe/patches/v13_0/rename_custom_client_script.py b/frappe/patches/v13_0/rename_custom_client_script.py new file mode 100644 index 0000000000..718f1f6a46 --- /dev/null +++ b/frappe/patches/v13_0/rename_custom_client_script.py @@ -0,0 +1,9 @@ +import frappe + + +def execute(): + if frappe.db.exists("DocType", "Client Script"): + return + + frappe.rename_doc("DocType", "Custom Script", "Client Script") + frappe.reload_doctype("Client Script", force=True) diff --git a/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py b/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py new file mode 100644 index 0000000000..fcf8afc826 --- /dev/null +++ b/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py @@ -0,0 +1,20 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe + + +def execute(): + if frappe.db.table_exists('List View Setting'): + existing_list_view_settings = frappe.get_all('List View Settings', as_list=True) + for list_view_setting in frappe.get_all('List View Setting', fields = ['disable_count', 'disable_sidebar_stats', 'disable_auto_refresh', 'name']): + name = list_view_setting.pop('name') + if name not in [x[0] for x in existing_list_view_settings]: + list_view_setting['doctype'] = 'List View Settings' + list_view_settings = frappe.get_doc(list_view_setting) + # setting name here is necessary because autoname is set as prompt + list_view_settings.name = name + list_view_settings.insert() + frappe.delete_doc("DocType", "List View Setting", force=True) + frappe.db.commit() diff --git a/frappe/public/build.json b/frappe/public/build.json index c05eff01ac..51a2f55a37 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -75,8 +75,6 @@ "public/css/octicons/octicons.css", "public/less/desk.less", "public/less/module.less", - "public/less/link_preview.less", - "public/less/form.less", "public/less/mobile.less", "public/less/controls.less", "public/less/chat.less", @@ -95,14 +93,14 @@ "public/scss/print.scss" ], "concat:js/libs.min.js": [ - "public/js/lib/awesomplete/awesomplete.min.js", "public/js/lib/Sortable.min.js", "public/js/lib/jquery/jquery.hotkeys.js", "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js", "node_modules/vue/dist/vue.min.js", "node_modules/moment/min/moment-with-locales.min.js", "node_modules/moment-timezone/builds/moment-timezone-with-data.min.js", - "public/js/lib/socket.io.min.js", + "node_modules/socket.io-client/dist/socket.io.slim.js", + "node_modules/localforage/dist/localforage.min.js", "public/js/lib/jSignature.min.js", "public/js/lib/leaflet/leaflet.js", "public/js/lib/leaflet/leaflet.draw.js", @@ -225,7 +223,6 @@ "public/js/frappe/form/form.js", "public/js/frappe/meta_tag.js" ], - "css/list.min.css": "public/less/gantt.less", "js/list.min.js": [ "public/js/frappe/ui/listing.html", @@ -292,7 +289,7 @@ "css/web_form.css": [ "website/css/web_form.css", "public/css/octicons/octicons.css", - "public/less/controls.less", + "public/scss/controls.scss", "node_modules/frappe-datatable/dist/frappe-datatable.css" ], "css/email.css": "public/scss/email.scss", diff --git a/frappe/public/css/common.css b/frappe/public/css/common.css deleted file mode 100644 index b08be904a6..0000000000 --- a/frappe/public/css/common.css +++ /dev/null @@ -1,253 +0,0 @@ -/* the element that this class is applied to, should have a max width for this to work*/ -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -} -a { - cursor: pointer; -} -a, -a:hover, -a:active, -a:focus, -.btn, -.btn:hover, -.btn:active, -.btn:focus { - outline: 0; -} -img { - max-width: 100%; -} -p { - margin: 10px 0px; -} -.text-color { - color: #36414C !important; -} -.text-muted { - color: #8D99A6 !important; -} -.text-extra-muted { - color: #d1d8dd !important; -} -a, -.badge { - -webkit-transition: 0.2s; - -o-transition: 0.2s; - transition: 0.2s; -} -.btn { - -webkit-transition: background-color 0.2s; - -o-transition: background-color 0.2s; - transition: background-color 0.2s; -} -a.disabled, -a.disabled:hover { - color: #888; - cursor: default; - text-decoration: none; -} -a.grey, -.sidebar-section a, -.control-value a, -.data-row a { - text-decoration: none; -} -a.grey:hover, -.sidebar-section a:hover, -.control-value a:hover, -.data-row a:hover, -a.grey:focus, -.sidebar-section a:focus, -.control-value a:focus, -.data-row a:focus { - text-decoration: underline; -} -a.text-muted, -a.text-extra-muted { - text-decoration: none; -} -.underline { - text-decoration: underline; -} -.inline-block { - display: inline-block; -} -.bold, -.strong { - font-weight: bold; -} -kbd { - color: inherit; - background-color: #F0F4F7; -} -.btn [class^="fa fa-"], -.nav [class^="fa fa-"], -.btn [class*="fa fa-"], -.nav [class*="fa fa-"] { - display: inline-block; -} -.dropdown-menu > li > a { - padding: 14px; - white-space: normal; -} -.dropdown-menu { - min-width: 200px; - padding: 0px; - font-size: 12px; - max-height: 400px; - overflow: auto; - border-radius: 0px 0px 4px 4px; -} -.dropdown-menu .dropdown-header { - padding: 3px 14px; - font-size: 11px; - font-weight: 200; - padding-top: 12px; -} -.dropdown-menu .divider { - margin: 0px; -} -a.badge-hover:hover .badge, -a.badge-hover:focus .badge, -a.badge-hover:active .badge { - background-color: #D8DFE5; -} -.msgprint { - word-wrap: break-word; -} -.msgprint pre { - text-align: left; -} -.centered { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); -} -.border-top { - border-top: 1px solid #d1d8dd; -} -.border-bottom { - border-bottom: 1px solid #d1d8dd; -} -.border-left { - border-left: 1px solid #d1d8dd; -} -.border-right { - border-right: 1px solid #d1d8dd; -} -.border { - border: 1px solid #d1d8dd; -} -.close-inline { - font-size: 120%; - font-weight: bold; - line-height: 1; - cursor: pointer; - color: inherit; - display: inline-block; -} -.close-inline:hover, -.close-inline:focus { - text-decoration: none; -} -.middle { - vertical-align: middle; -} -.full-center-container { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} -.full-center { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); -} -#freeze { - z-index: 1020; - bottom: 0px; - opacity: 0; - background-color: #fafbfc; -} -#freeze .freeze-message-container { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} -#freeze .freeze-message { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); - text-align: center; - color: #36414C !important; -} -#freeze.dark { - background-color: #334143; -} -#freeze.in { - opacity: 0.5; -} -a.no-decoration { - text-decoration: none; - color: inherit; -} -a.no-decoration:hover, -a.no-decoration:focus, -a.no-decoration:active { - text-decoration: none; - color: inherit; -} -.padding { - padding: 15px; -} -.margin { - margin: 15px; -} -.margin-top { - margin-top: 15px; -} -.margin-bottom { - margin-bottom: 15px; -} -.margin-left { - margin-left: 15px; -} -.margin-right { - margin-right: 15px; -} -@media (max-width: 767px) { - .text-center-xs { - text-align: center; - } -} -.grayscale { - -webkit-filter: grayscale(100%); - filter: grayscale(100%); -} -.uppercase { - padding-bottom: 4px; - text-transform: uppercase; - font-size: 12px; - letter-spacing: 0.4px; - color: #8D99A6; -} -.ellipsis { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - vertical-align: middle; -} diff --git a/frappe/public/css/docs.css b/frappe/public/css/docs.css deleted file mode 100644 index 3c57d0bf45..0000000000 --- a/frappe/public/css/docs.css +++ /dev/null @@ -1,604 +0,0 @@ -/* the element that this class is applied to, should have a max width for this to work*/ -.navbar .dropdown-toggle { - padding-top: 8px; - padding-bottom: 8px; -} -.navbar-fixed-top { - left: 0px; - right: 0px; -} -.navbar a { - font-size: 12px; - font-weight: bold; -} -.navbar-icon-home { - vertical-align: middle; -} -.navbar-icon-home:hover, -.navbar-icon-home:focus, -.navbar-icon-home:active, -.navbar-icon-home-hover { - opacity: 1; - Filter: alpha(opacity=100); - /* For IE8 and earlier */ -} -.navbar-user-image { - width: 24px; - height: 24px; - margin-right: 3px; - border-radius: 4px; -} -@media (max-width: 991px) { - .navbar-desk { - width: 35% !important; - } - .navbar-desk ~ ul > li { - float: left; - } - .navbar-desk ~ ul > li a { - padding-left: 10px !important; - padding-right: 10px !important; - } - .navbar-desk ~ ul > li a .avatar { - margin-right: 0; - } - .dropdown-navbar-new-comments > a { - padding: 8px 0 !important; - margin-left: 0 !important; - } -} -@media (max-width: 767px) { - .navbar-desk { - width: 50% !important; - } -} -#search-modal .modal-dialog, -#search-modal .modal-content { - background: transparent; -} -#search-modal .modal-header { - background: #fff; - width: 100%; -} -#search-modal .modal-header form { - vertical-align: middle; -} -#search-modal .modal-header button { - line-height: 0; - position: absolute; - right: 0; - top: 0; - z-index: 9; - padding: 9px; -} -.dropdown-navbar-new-comments > a { - border: 0; -} -.dropdown-navbar-new-comments .dropdown-menu { - margin-top: 0; -} -.dropdown-help .dropdown-menu { - width: 350px !important; - max-height: 440px; - overflow: auto; -} -.dropdown-help .dropdown-menu .input-group { - width: 100%; - background-color: #f5f7fa; - padding: 8px 12px; -} -.dropdown-help .dropdown-menu input { - width: 100%; - padding: 5px 10px; - outline: none; - border-radius: 3px 0 0 3px; - border: 1px solid #d1d8dd; - opacity: 0.9; - line-height: 1.5; -} -.dropdown-help .dropdown-menu button { - border: 1px solid #d1d8dd; -} -@media (max-width: 767px) { - .dropdown-help .dropdown-menu { - position: fixed !important; - top: 40px; - width: 100% !important; - } -} -@media (max-width: 767px) { - .dropdown-mobile.open .dropdown-menu { - position: absolute; - border-top: 1px solid rgba(0, 0, 0, 0.14902); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - background-color: #fff; - right: 0; - left: auto; - } - .dropdown-mobile.open .dropdown-menu > li > a { - padding: 12px; - } - .dropdown-help { - display: none !important; - } -} -.navbar-new-comments { - display: inline-block; - min-width: 24px; - height: 24px; - border-radius: 4px; - color: #fff; - text-align: center; - padding: 2px 5px; - background-color: #b8c2cc; -} -.navbar-new-comments-true { - background-color: #ff5858; -} -.navbar-form .awesomplete { - margin-left: -15px; - width: 300px; -} -@media (max-width: 1199px) { - .navbar-form .awesomplete { - width: 280px; - } -} -@media (max-width: 991px) { - .navbar-form .awesomplete { - width: 250px; - } -} -#navbar-search { - width: 100%; - background-color: rgba(255, 255, 255, 0.9); -} -.navbar .navbar-search-icon { - color: #6C7680; - font-size: inherit; - position: relative; - right: 24px; - top: 1px; -} -.navbar .badge { - font-weight: normal; -} -#navbar-search-results { - left: auto; - right: inherit; - margin-top: -1px; - max-height: 300px; - overflow-y: auto; - overflow-x: hidden; -} -.navbar-center { - float: left; - color: #6C7680; -} -#navbar-breadcrumbs > li > a:before { - font-family: FontAwesome; - font-weight: normal; - font-style: normal; - text-decoration: inherit; - -webkit-font-smoothing: antialiased; - *margin-right: 0.3em; - display: inline-block; - speak: none; - font-size: 24px; - transition: 0.2s; - position: relative; - top: 3px; - content: "\f105"; - margin-right: 10px; - color: #C0C9D2; -} -#navbar-breadcrumbs > li > a:hover:before, -#navbar-breadcrumbs > li > a:focus:before, -#navbar-breadcrumbs > li > a:active:before { - color: #36414C; -} -#navbar-breadcrumbs > li > a { - padding: 6px 15px 10px 0px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 170px; -} -@media (min-width: 991px) and (max-width: 1199px) { - #navbar-breadcrumbs > li > a { - max-width: 120px; - } -} -.toolbar-user-fullname { - max-width: 150px; - display: inline-block; -} -.navbar-brand > img { - display: inline-block; -} -.toggle-sidebar { - margin-right: 10px; -} -.navbar-default .navbar-nav > li > a, -.navbar-default .navbar-brand { - color: #8D99A6; -} -body { - font-size: 16px; - line-height: 1.65em; - color: #454e57; - -webkit-font-smoothing: antialiased; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -} -.container { - max-width: 870px; -} -img { - max-width: 100%; -} -.splash { - border-bottom: 1px solid #d1d8dd; -} -.splash .jumbotron { - background-color: transparent; - padding: 40px 0 60px 0; - text-align: center; -} -.splash .jumbotron h1 { - font-size: 48px; - font-weight: 400; - opacity: 0.9; - color: #2E3338; -} -.splash .jumbotron p { - font-size: 24px; - font-color: #8D99A6 !important; - letter-spacing: 0px; - opacity: 0.7; - margin-bottom: 90px; - font-weight: 300; - line-height: 1.4em; -} -.splash .section { - padding: 30px 0 0 0; -} -.page-container { - padding-top: 38px; - margin: 0 auto; - max-width: 870px; -} -.page-container .webpage-content ol > li, -.page-container .webpage-content ul > li { - margin: 13px auto; -} -.page-container .webpage-content ol > li li, -.page-container .webpage-content ul > li li { - margin: 4px auto; -} -.page-container .webpage-content ol li ol { - list-style-type: disc; -} -.page-container .webpage-content ul, -.page-container .webpage-content ol { - margin-bottom: 32px; -} -@media (min-width: 768px) { - .page-container .page-content { - width: 83%; - margin: 0 auto; - } -} -#page-index { - padding-top: 0; - width: 100%; - margin: 0; -} -#page-index .page-content { - width: 100%; - margin: 0; -} -body[data-path="index"] .navbar .toggle-sidebar i { - color: #fff; -} -code { - color: #e66a12; - background: #fff6df; -} -pre { - background: #fafbfc; - border: 1px solid #e1e9f0; - border-radius: 2px; -} -.hljs { - background: transparent; - border: none; - padding: 1.2em 1.5em 1.5em; - color: #454e57; -} -.hljs-keyword, -.hljs-tag, -.css .hljs-class, -.css .hljs-id, -.lisp .hljs-title, -.nginx .hljs-title, -.hljs-request, -.hljs-status, -.clojure .hljs-attribute { - color: #e66a12; -} -.diff .hljs-deletion, -.hljs-string, -.hljs-tag .hljs-value, -.hljs-preprocessor, -.hljs-pragma, -.hljs-built_in, -.hljs-javadoc, -.smalltalk .hljs-class, -.smalltalk .hljs-localvars, -.smalltalk .hljs-array, -.css .hljs-rules .hljs-value, -.hljs-attr_selector, -.hljs-pseudo, -.apache .hljs-cbracket, -.tex .hljs-formula, -.coffeescript .hljs-attribute { - color: #dd4a68; -} -.hljs-number, -.hljs-date, -.hljs-regexp, -.hljs-literal, -.hljs-hexcolor, -.smalltalk .hljs-symbol, -.smalltalk .hljs-char, -.go .hljs-constant, -.hljs-change, -.lasso .hljs-variable, -.makefile .hljs-variable, -.asciidoc .hljs-bullet, -.markdown .hljs-bullet, -.asciidoc .hljs-link_url, -.markdown .hljs-link_url { - color: #7575ff; -} -.hljs-shebang, -.diff .hljs-addition, -.hljs-comment, -.hljs-annotation, -.hljs-template_comment, -.hljs-pi, -.hljs-doctype { - color: #6a906a; -} -.dos .hljs-keyword, -.hljs-decorator, -.hljs-title, -.hljs-type, -.diff .hljs-header, -.ruby .hljs-class .hljs-parent, -.apache .hljs-tag, -.nginx .hljs-built_in, -.tex .hljs-command, -.hljs-prompt { - color: #4f4fa4; -} -.navbar { - background-color: #36414C !important; - margin-left: auto; - margin-right: auto; -} -.navbar .container { - max-width: 870px; -} -.navbar .brand-logo { - width: 30px; - margin-top: -4px; - margin-right: 7px; -} -.navbar a { - font-size: 16px; - font-weight: normal; - color: #fff !important; -} -.navbar a.navbar-brand { - font-weight: bold; -} -.navbar a.toggle-sidebar { - margin-top: 8px; -} -.sidebar a { - font-size: 14px; - padding-top: 14px !important; - padding-bottom: 14px !important; -} -.breadcrumb { - line-height: 1em; - color: #8D99A6; - background-color: transparent; - margin-bottom: 32px; - padding: 0px; - padding-left: 20px; - background: url('/assets/img/up.png') 0% 30% no-repeat; -} -.breadcrumb .icon { - display: none; -} -.breadcrumb a, -.breadcrumb a:hover, -.breadcrumb a:focus, -.breadcrumb a:visited { - color: #7575ff; - font-size: 16px; -} -.hero-and-content a, -.hero-and-content a:hover, -.hero-and-content a:focus, -.hero-and-content a:visited { - color: #5E64FF; -} -.hero-and-content a.btn { - color: inherit; -} -a.btn-primary { - color: #7575ff; -} -a.btn-primary:hover, -a.btn-primary:focus, -a.btn-primary:visited { - color: #7575ff; -} -.btn-next-wrapper { - margin-top: 32px; - text-align: right; -} -h2 { - margin-top: 48px; - font-size: 24px; -} -h3, -h4 { - margin-top: 48px; -} -p { - margin-bottom: 16px; -} -.hero-and-content > p { - max-width: 723px; - margin: 0 auto; -} -.navbar { - background-color: transparent; - border: none; - padding: 15px 0px; - border-radius: 0px; - border-bottom: 1px solid #d1d8dd; -} -.section { - padding: 64px 0 0 0; -} -.dev-header { - margin-bottom: 30px; -} -.docs-footer { - padding: 30px 0px 60px 0px; - border-top: 1px solid #d1d8dd; - max-width: 870px; - margin: 0 auto; - margin-top: 80px; - font-size: 14px; -} -.docs-footer h3 { - margin-top: 24px; - font-size: 16px; -} -.docs-footer img.frappe-bird { - width: 40px; - height: 40px; - background: #fff; - margin-bottom: 10px; - padding: 5px; -} -.docs-footer a { - color: #8D99A6; -} -.docs-footer li { - display: inline-block; - margin: 0 10px; -} -.docs-footer .built-with-frappe { - margin-top: -50px; -} -.browser-image { - min-height: 200px; - border: 1px solid #d1d8dd; - border-bottom: 0px; -} -.fake-browser-frame { - position: relative; - margin: 24px auto 0px; - box-shadow: 0px -6px 100px 1px rgba(0, 0, 0, 0.1), 0px -6px 50px 1px rgba(0, 0, 0, 0.4); -} -.fake-browser-frame::before { - content: ""; - height: 24px; - position: absolute; - top: -24px; - left: 0px; - right: 0px; - border: 1px solid #d1d8dd; - background: #f5f7fa; - border-bottom: none; - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.fake-browser-frame::after { - content: '\f111 \00a0\00a0 \f111 \00a0\00a0 \f111'; - position: absolute; - color: #d1d8dd; - top: -15px; - left: 8px; - /* octicon */ - font: normal normal; - font-size: 8px; - font-family: 'FontAwesome'; - line-height: 1; - display: inline-block; - text-decoration: none; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.fake-iphone-frame { - position: relative; - padding: 40px 8px; - border: 1px solid #d1d8dd; - border-radius: 15px; -} -.fake-ipad-frame { - position: relative; - padding: 8px 40px; - border: 1px solid #d1d8dd; - border-radius: 15px; -} -hr { - margin: 48px 0px 30px; -} -.edit { - color: #8d99a6; -} -a.edit, -a.edit:hover, -a.edit:focus, -a.edit:visited, -.edit-container .icon { - color: #8d99a6; -} -.btn-next { - margin-top: 30px; - margin-bottom: 0px; -} -.btn-next:after { - content: " \2192"; -} -#current td { - font-weight: bold; -} -#current td code { - font-weight: normal; - background: transparent; - font-family: "Helvetica Neue", Helvetica, Arial, "Open Sans", sans-serif; - color: #454e57; - font-size: 16px; -} -.hero-and-content [data-html-block="hero"] { - overflow-y: hidden; -} -.page-content-wrapper > .row .col-sm-8 { - width: 100%; -} -.page-content-wrapper > .row .col-sm-4 { - display: none; -} diff --git a/frappe/public/css/form.css b/frappe/public/css/form.css deleted file mode 100644 index baad12b495..0000000000 --- a/frappe/public/css/form.css +++ /dev/null @@ -1,729 +0,0 @@ -.form-print-wrapper { - border: 1px solid #d1d8dd; - border-top: none; -} -.print-preview-wrapper { - padding: 30px 0px; - background-color: #f5f7fa; -} -.print-toolbar { - margin: 0px; - padding: 10px 0px; - border-bottom: 1px solid #d1d8dd; -} -.print-toolbar > div { - padding-right: 0px; -} -.print-toolbar > div:last-child { - padding-right: 15px; -} -.form-inner-toolbar { - padding: 10px 15px 0px; - background-color: #fafbfc; - text-align: right; -} -.form-inner-toolbar .btn { - margin-bottom: 10px; -} -.form-clickable-section { - border-top: 1px solid #d1d8dd; - padding: 10px 15px; - background-color: #F7FAFC; -} -.form-page.second-page { - border-top: 1px solid #d1d8dd; -} -.form-message { - padding: 15px 30px; - border-bottom: 1px solid #d1d8dd; -} -.document-flow-wrapper { - padding: 40px 15px 30px; - font-size: 12px; - border-bottom: 1px solid #EBEFF2; -} -.document-flow-wrapper .document-flow { - display: inline-block; - position: relative; - left: 50%; - transform: translateX(-50%); -} -.document-flow-wrapper .document-flow .document-flow-link-wrapper { - width: 140px; - display: inline-block; -} -.document-flow-wrapper .document-flow .document-flow-link-wrapper:not(:last-child) { - border-top: 1px solid #b8c2cc; - margin-right: -4px; -} -.document-flow-wrapper .document-flow .document-flow-link-wrapper:last-child { - margin-right: -140px; -} -.document-flow-wrapper .document-flow .document-flow-link { - margin-top: -10px; - display: inline-block; -} -.document-flow-wrapper .document-flow .document-flow-link:not(.disabled):hover .document-flow-link-label, -.document-flow-wrapper .document-flow .document-flow-link:not(.disabled):focus .document-flow-link-label, -.document-flow-wrapper .document-flow .document-flow-link:not(.disabled):active .document-flow-link-label { - text-decoration: underline; -} -.document-flow-wrapper .document-flow .document-flow-link-label { - display: inline-block; - margin-left: -50%; - margin-top: 5px; -} -@media (max-width: 767px) { - .document-flow-wrapper { - display: none; - } -} -.form-dashboard { - background-color: #fafbfc; -} -.form-dashboard-wrapper { - margin: -15px 0px; -} -.form-documents h6 { - margin-top: 15px; -} -.form-dashboard-section { - margin: 0px -15px; - padding: 15px 30px; - border-bottom: 1px solid #EBEFF2; -} -.form-dashboard-section:first-child { - padding-top: 0px; -} -.form-dashboard-section:last-child { - border-bottom: none; -} -.form-heatmap .heatmap { - display: flex; - justify-content: center; -} -.form-heatmap .heatmap-message { - margin-top: 10px; -} -@media (max-width: 991px) { - .form-heatmap { - overflow: hidden; - overflow-x: scroll; - } -} -.inline-graph .inline-graph-half { - width: 48%; - display: inline-block; - position: relative; - height: 30px; -} -.inline-graph .inline-graph-half .inline-graph-count { - font-size: 10px; - position: absolute; - left: 0; - right: 0; - top: 3px; - padding: 0px 5px; - text-align: left; -} -.inline-graph .inline-graph-half .inline-graph-bar { - position: absolute; - left: 0; - right: 0; - top: 20px; -} -.inline-graph .inline-graph-half .inline-graph-bar-inner { - display: block; - float: left; - background-color: #d1d8dd; - height: 6px; - border-radius: 0px 3px 3px 0px; -} -.inline-graph .inline-graph-half .inline-graph-bar-inner.dark { - background-color: #36414C; -} -.inline-graph .inline-graph-half:first-child { - border-right: 1px solid #d1d8dd; - margin-right: -3px; -} -.inline-graph .inline-graph-half:first-child .inline-graph-count { - text-align: right; -} -.inline-graph .inline-graph-half:first-child .inline-graph-bar-inner { - float: right; - border-radius: 3px 0px 0px 3px; -} -.progress-area { - padding-top: 15px; - padding-bottom: 15px; -} -.form-links .document-link { - margin-bottom: 10px; - height: 22px; -} -.form-links .document-link:hover .badge-link { - text-decoration: underline; -} -.form-links .document-link:hover .badge-link[disabled='disabled'] { - text-decoration: none; -} -.form-links .count { - display: inline-block; - margin-left: 5px; - margin-right: 5px; -} -h6.uppercase, -.h6.uppercase { - font-size: 11px; - font-weight: normal; - letter-spacing: 0.4px; - text-transform: uppercase; - color: #8D99A6; -} -.form-section { - margin: 0px; - padding: 15px; -} -.form-section .form-section-description { - margin-bottom: 10px; -} -.form-section .form-section-heading { - margin: 10px 0px; -} -.form-section .section-head { - margin: 0px 0px 15px 15px; - cursor: pointer; -} -.form-section .section-head .collapse-indicator { - color: #d1d8dd; - margin-left: 10px; - position: relative; - bottom: -1px; -} -.form-section .section-head .collapse-indicator.octicon-chevron-up { - bottom: -2px; -} -.form-section .section-head.collapsed { - margin-bottom: 0px; -} -.form-section:not(:last-child), -.form-inner-toolbar { - border-bottom: 1px solid #d1d8dd; -} -.empty-section { - display: none !important; -} -.modal .form-layout { - margin: -15px; -} -.modal .form-grid .form-layout { - margin: 0px; -} -.modal .form-section { - padding: 15px 7px; -} -.help ol { - padding-left: 19px; -} -.field_description_top { - margin-bottom: 3px; -} -.user-actions { - margin-bottom: 15px; -} -.user-actions a { - font-weight: bold; -} -.badge-important { - background-color: #e74c3c; -} -.address-box { - background-color: #fafbfc; - padding: 0px 15px; - margin: 15px 0px; - border: 1px solid #d1d8dd; - border-radius: 3px; - font-size: 12px; -} -.timeline { - margin: 30px 0px; -} -.timeline .timeline-head .comment-input { - height: auto; -} -.timeline-item { - margin-top: 0px; -} -.timeline-item b { - color: #36414C !important; -} -.timeline-item blockquote { - font-size: inherit; -} -.timeline-item .btn-more { - margin-left: 65px; -} -.timeline-item .gmail_extra { - display: none; -} -.timeline-items { - position: relative; -} -.timeline { - position: relative; -} -.timeline::before { - content: " "; - border-left: 1px solid #d1d8dd; - position: absolute; - top: 0px; - bottom: -124px; - left: 43px; - z-index: 0; -} -.timeline.in-dialog::before { - bottom: 0px; -} -@media (max-width: 991px) { - .timeline::before { - bottom: -64px; - } -} -.timeline-item.user-content { - margin: 30px 0px 30px 27px; -} -.timeline-item.user-content .media-body { - border: 1px solid #d1d8dd; - border-radius: 3px; - margin-left: -7px; - position: relative; - max-width: calc(100% - 50px); - padding-right: 0px; - overflow: visible; -} -.timeline-item.user-content .avatar-medium { - margin-right: 10px; - height: 45px; - width: 45px; -} -.timeline-item.user-content .action-btns { - position: absolute; - right: 0; - padding: 8px 15px 0 5px; -} -.timeline-item.user-content .action-btns .edit-btn-container { - margin-right: 13px; -} -.timeline-item.user-content .comment-header { - background-color: #fafbfc; - padding: 10px 15px 8px 13px; - margin: 0px; - color: #8D99A6; - border-bottom: 1px solid #EBEFF2; -} -.timeline-item.user-content .comment-header.links-active { - padding-right: 77px; -} -.timeline-item.user-content .comment-header .asset-details { - display: inline-block; - width: 100%; -} -.timeline-item.user-content .comment-header .asset-details .btn-link { - border: 0; - border-radius: 0; - padding: 0; -} -.timeline-item.user-content .comment-header .asset-details .btn-link:hover { - text-decoration: none; -} -.timeline-item.user-content .comment-header .commented-on-small { - display: none; -} -.timeline-item.user-content .comment-header .octicon-heart { - color: #ff5858; - cursor: pointer; -} -.timeline-item.user-content .reply { - padding: 15px; - overflow: auto; -} -.timeline-item.user-content .reply > div > p:first-child { - margin-top: 0px; -} -.timeline-item.user-content .reply > div > p:last-child { - margin-bottom: 0px; -} -.timeline-item.user-content .reply hr { - margin: 10px 0px; -} -.timeline-item.user-content .close-btn-container .close { - color: inherit; - opacity: 1; - padding: 0; - font-size: 18px; -} -.timeline-item.user-content .edit-btn-container { - padding: 0; -} -.timeline-item.user-content .edit-btn-container .edit { - color: inherit; - font-size: 21px; - line-height: 1; -} -.timeline-item.user-content .edit-btn-container .edit .octicon-check { - font-size: 1em; -} -.timeline-item.user-content .edit-btn-container .edit:hover, -.timeline-item.user-content .edit-btn-container .edit:focus { - color: #000; -} -.timeline-item.user-content .comment-likes { - margin-left: 5px; -} -.timeline-item.user-content .media-body:after, -.timeline-item.user-content .media-body:before { - right: 100%; - top: 15px; - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; -} -.timeline-item.user-content .media-body:after { - border-color: rgba(136, 183, 213, 0); - border-right-color: #fafbfc; - border-width: 6px; - margin-top: -6px; -} -.timeline-item.user-content .media-body:before { - border-color: rgba(194, 225, 245, 0); - border-right-color: #d1d8dd; - border-width: 7px; - margin-top: -7px; -} -.timeline-item.notification-content { - padding-left: 30px; - margin: 30px 0px; - position: relative; - color: #8D99A6; -} -.timeline-item.notification-content * { - color: #8D99A6; -} -.timeline-item.notification-content .fa-fw { - margin-left: 36px; -} -.timeline-item.notification-content div.small { - padding-left: 40px; -} -.timeline-item.notification-content div.small .fa-fw { - margin-left: 0px; -} -.timeline-item.notification-content .octicon-heart { - color: #ff5858 !important; -} -.timeline-item.notification-content::before { - content: " "; - width: 7px; - height: 7px; - background-color: #d1d8dd; - position: absolute; - left: 40px; - border-radius: 50%; - top: 5px; -} -.timeline-item .reply-link { - margin-left: 15px; - font-size: 12px; -} -.timeline-head { - background-color: white; - border: 1px solid #d1d8dd; - border-radius: 3px; - position: relative; - z-index: 1; -} -.timeline-head .comment-input-header { - background-color: #fafbfc; - padding: 7px 15px; - border-bottom: 1px solid #EBEFF2; -} -.timeline-head .comment-input-container { - padding: 15px; -} -.timeline-head .comment-input-container .awesomplete > ul { - min-width: 200px; -} -.timeline-head .comment-input { - border-color: #EBEFF2; - max-width: 100%; -} -.timeline-head .comment-input:focus { - box-shadow: none; -} -@media (max-width: 767px) { - .timeline-head { - border-left: none; - border-right: none; - border-radius: 0px; - } -} -.signature-field { - min-height: 300px; - background: #fff; - border: 1px solid #d1d8dd; - border-radius: 3px; - position: relative; - margin-top: -10px; -} -.signature-display { - margin: 7px 0px; - background: #fff; -} -.signature-btn-row { - position: absolute; - bottom: 12px; - right: 12px; -} -.signature-reset { - z-index: 10; - height: 30px; - width: 30px; - padding: 4px 0px; -} -.signature-img { - background: #fff; - border-radius: 3px; - margin-top: 5px; - max-height: 150px; -} -.timeline-new-email { - margin: 30px 0px; - padding-left: 70px; - position: relative; -} -.timeline-new-email::before { - content: " "; - width: 7px; - height: 7px; - background-color: #d1d8dd; - position: absolute; - left: 40px; - border-radius: 50%; - top: 5px; -} -.form-footer h5 { - margin: 15px 0px; - font-weight: bold; -} -.control-label, -.grid-heading-row { - color: #8D99A6; - font-size: 12px; -} -.control-label { - margin-bottom: 5px; - font-weight: normal; -} -.like-disabled-input { - margin-bottom: 7px; - min-height: 30px; - font-weight: bold; - background-color: #f5f7fa; - padding: 5px 10px; - border-radius: 3px; -} -.disabled-check { - color: #f5f7fa; - margin-right: 5px; - margin-bottom: -2px; -} -.like-disabled-input.for-description { - font-weight: normal; - font-size: 12px; -} -.frappe-control { - margin-bottom: 10px; -} -.frappe-control .help-box { - margin-top: 3px; -} -.frappe-control pre { - white-space: pre-wrap; - background-color: inherit; - border: none; - padding: 0px; - margin: 0px; -} -.flex-justify-center { - display: flex; - justify-content: center; -} -.flex-justify-end { - display: flex; - justify-content: flex-end; -} -.hide-control { - display: none !important; -} -.shared-user { - margin-bottom: 10px; -} -.attach-missing-image, -.attach-image-display { - cursor: pointer; -} -select.form-control { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} -.form-control.bold { - color: #000; - font-weight: bold; - background-color: #fffdf4; -} -.form-control[data-fieldtype="Password"] { - position: inherit; -} -.password-strength-indicator { - float: right; - padding: 15px; - margin-top: -41px; - margin-right: -7px; -} -.password-strength-message { - margin-top: -10px; -} -.control-code, -.control-code.bold { - height: 400px; - font-family: Monaco, "Courier New", monospace; - color: #36414C; - font-size: 12px; - line-height: 1.7em; -} -.delivery-status-indicator { - display: inline-block; - margin-top: -3px; - margin-left: 1px; - font-weight: 500; - color: #8D99A6; -} -.attach-btn { - margin-top: 10px; -} -@media (min-width: 768px) { - .layout-main .form-column.col-sm-12 > form > .input-max-width { - max-width: 50%; - padding-right: 15px; - } - .col-sm-6 .form-grid .form-column.col-sm-12 > form > .input-max-width { - max-width: none; - padding-right: 0px; - } - .form-column.col-sm-6 textarea[data-fieldtype="Code"] { - height: 120px !important; - } -} -@media (max-width: 991px) { - .form-section .form-section-heading { - margin-top: 10px; - } -} -@media (max-width: 767px) { - .form-section .section-head { - padding: 15px 15px 15px 0px; - } - .form-section .section-body .form-column:first-child .radio, - .form-section .section-body .form-column:first-child .checkbox { - margin-top: 0; - } - .form-column { - border-bottom: 1px solid #EBEFF2; - padding-top: 15px; - padding-bottom: 15px; - } - .form-column:last-child { - border-bottom: 0px; - } - .form-section { - padding-left: 0px !important; - padding-right: 0px !important; - } - .form-grid { - margin-left: -17px; - margin-right: -17px; - border-left: none !important; - border-right: none !important; - border-radius: none; - } - .form-page .form-section { - padding: 0px 15px; - } - .frappe-control.form-page { - padding: 7px 15px; - border-bottom: 1px solid #EBEFF2; - margin: 0px -15px; - } - .frappe-control.form-page .link-btn { - top: -2px; - } - .frappe-control.form-page .like-disabled-input { - min-height: 0px !important; - } - .frappe-control.form-page:last-child { - margin-bottom: 0px; - } - .form-page .frappe-control:last-child { - border-bottom: 0px; - } - .form-page .frappe-control[data-fieldtype="Table"] { - padding: 0px 15px; - margin-top: -1px; - border-bottom: none; - } - .form-page .frappe-control[data-fieldtype="Table"] label { - margin-top: 7px; - } - .form-page .form-control { - border: none; - border-bottom: 1px solid #d1d8dd; - box-shadow: none; - background-color: inherit; - height: auto; - padding: 0px; - margin-bottom: 7px; - border-radius: 0px; - text-align: left !important; - } - .form-page .form-control:focus { - box-shadow: none; - } -} -/* goals */ -.goals-page-container { - background-color: #fafbfc; - padding-top: 1px; -} -.goals-page-container .goal-container { - background-color: #fff; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - border-radius: 2px; - padding: 10px; - margin: 10px; -} -body[data-route^="Form/Communication"] textarea[data-fieldname="subject"] { - height: 80px !important; -} -.frappe-control[data-fieldtype="Attach"] .attached-file { - position: relative; - margin-top: 5px; -} -.frappe-control[data-fieldtype="Attach"] .attached-file .close { - position: absolute; - top: 0; - right: 0; -} diff --git a/frappe/public/css/mobile.css b/frappe/public/css/mobile.css deleted file mode 100644 index 1cf8bb011a..0000000000 --- a/frappe/public/css/mobile.css +++ /dev/null @@ -1,411 +0,0 @@ -/* the element that this class is applied to, should have a max width for this to work*/ -html { - min-height: 100%; -} -body { - height: 100%; - margin: 0px; - padding: 0px !important; -} -html, -body { - overflow-x: hidden; - overflow-y: overlay; -} -@media (max-width: 991px) { - .intro-area, - .footnote-area { - padding: 15px; - } - .grid-row-open { - top: 0; - } - .layout-main { - position: relative; - } - body[data-route^="Form"] .page-title h1 { - margin-top: 12px; - } - body[data-route^="Form"] .page-title h1.editable-title { - padding-right: 80px; - } - body[data-route^="Form"] .page-title .indicator { - display: inline-block; - margin-top: 12px; - } - body[data-route^="Form"] .page-actions { - padding-top: 20px !important; - padding-bottom: 0px !important; - padding-left: 0px !important; - } - body[data-route^="Form"] .page-head .sub-heading { - font-weight: normal; - font-size: 10px; - display: block; - position: absolute; - right: 140px; - min-width: 200px; - } - body[data-route^="Form"] .title-text { - margin-top: 10px; - } - .toggle-navbar-new-comments { - padding: 8px 0px !important; - } - .navbar > .container > .navbar-header { - float: left; - width: 80%; - } - .navbar > .container > .navbar-right { - float: right; - } - .module-item { - padding: 7px 0px !important; - } - .module-item h4 { - font-weight: normal; - } - #navbar-breadcrumbs { - margin: 0px; - display: inline-block; - max-width: 150px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - #navbar-breadcrumbs > li, - #navbar-breadcrumbs > li > a { - display: inline-block; - vertical-align: middle; - } - #navbar-breadcrumbs > li > a:before { - content: "\f104"; - margin-right: 10px; - color: #6C7680; - } - #navbar-breadcrumbs li:not(:nth-last-child(-n+1)) { - display: none; - } - .navbar-nav { - margin: 0px; - margin-right: -15px; - } - .sidebar .form-group { - margin-bottom: 0px; - } - #sidebar-search { - height: 27px; - } - .sidebar .navbar-search-icon { - float: right; - color: #6C7680; - font-size: inherit; - position: relative; - right: 7px; - top: -20px; - height: 0; - } - .sidebar form { - padding: 7px; - } - .sidebar .main-menu { - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 41px; - overflow-y: auto; - } - .sidebar .user-menu { - padding: 9px 14px; - background-color: #f5f7fa; - position: absolute; - left: 0; - bottom: 0; - right: 0; - } - .sidebar .user-menu, - .sidebar .user-menu .octicon { - color: #6C7680; - } - .sidebar .user-menu img { - margin-top: -1px; - } - body[data-route^="Module"] .navbar-center { - display: block !important; - position: absolute; - top: 10px; - left: 25%; - right: 25%; - text-align: center; - } - body.no-breadcrumbs .navbar .navbar-home { - display: inline-block !important; - padding-left: 0px; - margin-left: 0px; - padding-top: 6px; - } - body.no-breadcrumbs .navbar .navbar-home:before { - font-family: FontAwesome; - font-weight: normal; - font-style: normal; - text-decoration: inherit; - -webkit-font-smoothing: antialiased; - *margin-right: 0.3em; - display: inline-block; - speak: none; - font-size: 24px; - transition: 0.2s; - position: relative; - top: 3px; - content: "\f104"; - margin-right: 10px; - color: #6C7680; - } - body.no-breadcrumbs .navbar .navbar-home:hover:before, - body.no-breadcrumbs .navbar .navbar-home:focus:before, - body.no-breadcrumbs .navbar .navbar-home:active:before { - color: #36414C !important; - } - body[data-route=""] .navbar .navbar-home, - body[data-route="desktop"] .navbar .navbar-home { - padding: 8px 10px; - } - body[data-route=""] .navbar .navbar-home:before, - body[data-route="desktop"] .navbar .navbar-home:before { - display: none; - } - body[data-route=""] .navbar .navbar-home img, - body[data-route="desktop"] .navbar .navbar-home img { - margin-top: 0; - } - body[data-route=""] .toggle-sidebar, - body[data-route="desktop"] .toggle-sidebar { - display: none !important; - } - body[data-sidebar="0"] .toggle-sidebar { - display: none !important; - } - body[data-sidebar="0"] #navbar-breadcrumbs, - body[data-sidebar="0"] .navbar-home { - margin-left: 15px !important; - } -} -@media (max-width: 991px) and (max-width: 480px) { - #navbar-breadcrumbs li > a { - width: 100px; - } -} -@media (max-width: 767px) { - .toggle-sidebar { - margin-right: 0; - } - body[data-route^="Form"] .page-title .title-text { - font-size: 16px; - width: calc(100% - 90px); - } - body[data-route^="Form"] .page-title .indicator { - float: left; - margin-top: 10px; - margin-right: 5px; - } - .modal .modal-dialog { - margin: 0px; - padding: 0px; - width: 100%; - background-color: #fff; - } - .modal .modal-content { - border-radius: 0px; - border: none; - height: 100%; - } - .modal .modal-body .form-layout { - margin: -15px; - } - .modal .file-upload .input-upload { - width: 100%; - text-align: center; - } - .modal .file-upload .input-upload .btn-browse { - width: 100%; - } - .modal .file-upload .web-link-wrapper { - display: block; - width: 100% !important; - text-align: center; - } - .modal .file-upload .web-link-wrapper .file-upload-or { - display: block; - margin: 15px 24px; - } - .modal .file-upload .web-link-wrapper .input-link { - width: 100% !important; - } - .layout-main-section-wrapper { - padding: 0px; - } - .layout-main-section { - border-left-color: transparent !important; - border-right-color: transparent !important; - } - .list-row { - padding: 13px 15px !important; - } - .doclist-row { - position: relative; - padding-right: 10px; - } - .doclist-row .list-id { - font-weight: normal; - } - .doclist-row .list-row-id { - left: 18px; - text-align: left; - margin-top: 3px; - } - .doclist-row.has-checkbox .list-row-id { - left: 40px; - } - .doclist-row .list-row-indicator { - position: absolute; - right: 0px; - top: -20px; - } - .doclist-row .list-row-modified { - margin-right: -10px; - } - .doclist-row .list-row-left { - z-index: 1; - } - .doclist-row .list-row-right { - float: right; - } - .doclist-row .list-row-right .list-row-indicator { - top: 4px; - } - .doclist-row .list-row-right .list-row-indicator .indicator::before, - .doclist-row .list-row-right .list-row-indicator .indicator::after { - height: 12px; - width: 12px; - border-radius: 12px; - } - .doclist-row .list-row-right.no-right-column { - position: absolute; - top: 0; - right: 10px; - left: -10px; - width: 100%; - } - body[data-route^="chat"] .navbar-center { - display: block !important; - position: absolute; - top: 10px; - left: 25%; - right: 25%; - text-align: center; - } - #page-chat .layout-side-section { - position: relative; - left: 0px; - border-right: 1px solid #d1d8dd; - padding-left: 0px; - float: left; - width: 76px; - } - #page-chat .layout-main-section-wrapper { - position: absolute; - left: 75px; - right: 0px; - border-left: 1px solid #d1d8dd; - float: left; - } - #page-chat .module-sidebar-item { - margin: 0px; - } - #page-chat .module-sidebar-item .chat-sidebar-link { - padding: 15px; - } - #page-chat .timeline-head { - padding: 15px 15px 7px; - } - #page-chat .list-row { - padding: 7px 0px; - } - #page-chat .message-row-right { - margin-top: 10px; - text-align: left; - } - body[data-route^="Form"] .page-head .sub-heading { - right: 90px; - } - .timeline::before { - content: none; - } - .timeline .timeline-new-email { - margin: 20px 0; - padding-left: 15px; - } - .timeline .timeline-new-email::before { - content: none; - } - .timeline .timeline-item.user-content { - margin: 20px 15px; - } - .timeline .timeline-item.user-content .media-body { - margin-left: 0; - max-width: 100%; - overflow: hidden; - } - .timeline .timeline-item.user-content .media-body:before { - content: none; - } - .timeline .timeline-item.user-content .action-btns { - padding: 7px 10px 2px 5px; - } - .timeline .timeline-item.user-content .action-btns .edit-btn-container { - margin-right: 0; - } - .timeline .timeline-item.user-content .comment-header { - padding: 7px 10px; - } - .timeline .timeline-item.user-content .comment-header .links-active { - padding-right: 10px; - } - .timeline .timeline-item.user-content .comment-header .reply-link { - margin-left: 0; - } - .timeline .timeline-item.user-content .comment-header .asset-details { - width: calc(100% - 30px); - } - .timeline .timeline-item.user-content .avatar-medium { - margin-right: 10px; - } - .timeline .timeline-item.user-content .reply { - padding: 10px; - } - .timeline .timeline-item.user-content .commented-on-small { - display: inline-block; - } - .timeline .timeline-item.user-content .commented-on-small { - display: inline-block; - } - .timeline .timeline-item.notification-content { - padding-left: 15px; - margin: 20px 0; - } - .timeline .timeline-item.notification-content::before { - content: none; - } - .timeline .timeline-item.notification-content .small { - padding-left: 0; - } - .timeline .timeline-item .delivery-status-indicator { - float: left; - margin: 0 5px 0 0; - } - .timeline .asset-details { - line-height: 24px; - /*Height of avtar image -36px to align text center vertically*/ - } -} diff --git a/frappe/public/css/variables.css b/frappe/public/css/variables.css deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/public/icons/timeless/symbol-defs.svg b/frappe/public/icons/timeless/symbol-defs.svg index 2a686f7a78..f5539d2bab 100644 --- a/frappe/public/icons/timeless/symbol-defs.svg +++ b/frappe/public/icons/timeless/symbol-defs.svg @@ -138,7 +138,10 @@ - + + + +

\ -

Search via Google

'); - modal.find(".search-input").on("keyup", function(e) { - if(e.which===13) { - modal.find(".btn-search").trigger("click"); - } - if(e.which===9) { - e.preventDefault(); - modal.find(".btn-search").focus(); - return false; - } - var text = $(this).val(); - modal.find(".btn-search").attr("href", "https://google.com/search?q=" - + text + "+site:" + (window.docs_base_url || "")); - }); - modal.modal("show"); - return false; - }); - -}); - -frappe = { - get_modal: function(title, body_html) { - var modal = $('').appendTo(document.body); - - return modal; - }, -}; diff --git a/frappe/public/js/frappe/file_uploader/FilePreview.vue b/frappe/public/js/frappe/file_uploader/FilePreview.vue index 7161dc8dc5..cca7dfde2a 100644 --- a/frappe/public/js/frappe/file_uploader/FilePreview.vue +++ b/frappe/public/js/frappe/file_uploader/FilePreview.vue @@ -11,17 +11,18 @@
- + {{ file.name | file_name }} - +
{{ file.name | file_name }} - - - +
+
{{ file.file_obj.size | file_size }} @@ -30,7 +31,7 @@
-
@@ -217,15 +209,19 @@ export default { on_file_input(e) { this.add_files(this.$refs.file_input.files); }, - remove_file(i) { - this.files = this.files.filter((file, j) => i !== j); + remove_file(file) { + this.files = this.files.filter(f => f !== file); }, - toggle_private(i) { - this.files[i].private = !this.files[i].private; - }, - toggle_all_private(flag) { - if (flag == null) { - flag = this.files.every(file => file.private); + toggle_all_private() { + let flag; + let private_values = this.files.filter(file => file.private); + if (private_values.length < this.files.length) { + // there are some private and some public + // set all to private + flag = true; + } else { + // all are private, set all to public + flag = false; } this.files = this.files.map(file => { file.private = flag; diff --git a/frappe/public/js/frappe/file_uploader/index.js b/frappe/public/js/frappe/file_uploader/index.js index 0a822971e1..28ce96cd44 100644 --- a/frappe/public/js/frappe/file_uploader/index.js +++ b/frappe/public/js/frappe/file_uploader/index.js @@ -48,6 +48,13 @@ export default class FileUploader { this.uploader = this.$fileuploader.$children[0]; + this.uploader.$watch('files', (files) => { + let all_private = files.every(file => file.private); + if (this.dialog) { + this.dialog.set_secondary_action_label(all_private ? __('Set all public') : __('Set all private')); + } + }, { deep: true }); + if (files && files.length) { this.uploader.add_files(files); } @@ -66,8 +73,10 @@ export default class FileUploader { title: __('Upload'), primary_action_label: __('Upload'), primary_action: () => this.upload_files(), - secondary_action_label: __('Toggle Private'), - secondary_action: () => this.uploader.toggle_all_private() + secondary_action_label: __('Set all private'), + secondary_action: () => { + this.uploader.toggle_all_private(); + } }); this.wrapper = this.dialog.body; diff --git a/frappe/public/js/frappe/form/controls/barcode.js b/frappe/public/js/frappe/form/controls/barcode.js index c2314d6664..8ac812c0ed 100644 --- a/frappe/public/js/frappe/form/controls/barcode.js +++ b/frappe/public/js/frappe/form/controls/barcode.js @@ -8,7 +8,7 @@ frappe.ui.form.ControlBarcode = frappe.ui.form.ControlData.extend({ this.default_svg = ''; let $input_wrapper = this.$wrapper.find('.control-input-wrapper'); this.barcode_area = $( - `
${this.default_svg}
` + `
${this.default_svg}
` ); this.barcode_area.appendTo($input_wrapper); }, @@ -55,7 +55,14 @@ frappe.ui.form.ControlBarcode = frappe.ui.form.ControlData.extend({ get_options(value) { // get JsBarcode options - let options = JSON.parse('{ "height" : 40 }'); + let options = {}; + options.background = "var(--control-bg)"; + options.lineColor = "var(--text-color)"; + options.font = "var(--font-stack)"; + options.fontSize = "16"; + options.width = "3"; + options.height = "50"; + if (frappe.utils.is_json(this.df.options)) { options = JSON.parse(this.df.options); if (options.format && options.format === 'EAN') { diff --git a/frappe/public/js/frappe/form/controls/base_control.js b/frappe/public/js/frappe/form/controls/base_control.js index d7f873bee0..9981398b84 100644 --- a/frappe/public/js/frappe/form/controls/base_control.js +++ b/frappe/public/js/frappe/form/controls/base_control.js @@ -74,7 +74,7 @@ frappe.ui.form.Control = Class.extend({ frappe.model.get_doc(this.doctype, this.docname), this.perm || (this.frm && this.frm.perm), explain); // Match parent grid controls read only status - if (status === 'Write' && (this.grid || (this.layout && this.layout.grid))) { + if (status === 'Write' && (this.grid || (this.layout && this.layout.grid) && !cint(this.df.allow_on_submit))) { var grid = this.grid || this.layout.grid; if (grid.display_status == 'Read') { status = 'Read'; diff --git a/frappe/public/js/frappe/form/controls/int.js b/frappe/public/js/frappe/form/controls/int.js index 9b59444fd3..aca3a85603 100644 --- a/frappe/public/js/frappe/form/controls/int.js +++ b/frappe/public/js/frappe/form/controls/int.js @@ -1,4 +1,5 @@ frappe.ui.form.ControlInt = frappe.ui.form.ControlData.extend({ + trigger_change_on_input_event: false, make: function () { this._super(); // $(this.label_area).addClass('pull-right'); diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 670bb8b3e0..e0a72ed8c1 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -136,7 +136,7 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ return $('
  • ') .data('item.autocomplete', d) .prop('aria-selected', 'false') - .html('

    ' + html + '

    ') + .html(`

    ${html}

    `) .get(0); }, sort: function() { diff --git a/frappe/public/js/frappe/form/controls/multiselect_pills.js b/frappe/public/js/frappe/form/controls/multiselect_pills.js index e73810c4dc..a0b4a1e2d8 100644 --- a/frappe/public/js/frappe/form/controls/multiselect_pills.js +++ b/frappe/public/js/frappe/form/controls/multiselect_pills.js @@ -84,12 +84,12 @@ frappe.ui.form.ControlMultiSelectPills = frappe.ui.form.ControlAutocomplete.exte get_pill_html(value) { const encoded_value = encodeURIComponent(value); - return `
    - - -
    `; + `; }, get_awesomplete_settings() { diff --git a/frappe/public/js/frappe/form/controls/rating.js b/frappe/public/js/frappe/form/controls/rating.js index 351c4f7122..8117bf24bf 100644 --- a/frappe/public/js/frappe/form/controls/rating.js +++ b/frappe/public/js/frappe/form/controls/rating.js @@ -52,7 +52,7 @@ frappe.ui.form.ControlRating = frappe.ui.form.ControlInt.extend({ return cint(this.value, null); }, set_formatted_input(value) { - let el = $(this.input_area).find('i'); + let el = $(this.input_area).find('svg'); el.children('svg').prevObject.each( function(e) { if (e < value) { $(this).addClass('star-click'); diff --git a/frappe/public/js/frappe/form/controls/signature.js b/frappe/public/js/frappe/form/controls/signature.js index c2bd560943..51ddf0d557 100644 --- a/frappe/public/js/frappe/form/controls/signature.js +++ b/frappe/public/js/frappe/form/controls/signature.js @@ -29,15 +29,21 @@ frappe.ui.form.ControlSignature = frappe.ui.form.ControlData.extend({ let width = this.body.width(); if (width > 0 && !this.$pad) { this.$pad = this.body.jSignature({ - height: 300, + height: 200, + color: "var(--text-color)", width: this.body.width(), - lineWidth: 3 + lineWidth: 2, + "background-color": "var(--control-bg)" }).on('change', this.on_save_sign.bind(this)); this.load_pad(); - this.$reset_button_wrapper = $(`
    - - `) + this.$reset_button_wrapper = $(` + + `) .appendTo(this.$pad) .on("click", '.signature-reset', () => { this.on_reset_sign(); diff --git a/frappe/public/js/frappe/form/controls/table_multiselect.js b/frappe/public/js/frappe/form/controls/table_multiselect.js index 10e22eacb6..c306146f90 100644 --- a/frappe/public/js/frappe/form/controls/table_multiselect.js +++ b/frappe/public/js/frappe/form/controls/table_multiselect.js @@ -123,12 +123,12 @@ frappe.ui.form.ControlTableMultiSelect = frappe.ui.form.ControlLink.extend({ }, get_pill_html(value) { const encoded_value = encodeURIComponent(value); - return `
    - - -
    `; + `; }, get_options() { return (this.get_link_field() || {}).options; diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index a70797e295..8d96054d16 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -1498,7 +1498,7 @@ frappe.ui.form.Form = class FrappeForm { const escaped_name = encodeURIComponent(value); - return `${label}'`; + return `${label}`; } else { return ''; } diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index 30ed7f15e4..5386f97528 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -61,11 +61,14 @@ frappe.form.formatters = { return frappe.form.formatters._right(flt(value, precision) + "%", options); }, Rating: function(value) { - return ` - ${Array.from(new Array(5)).map((_, i) => - `` - ).join('')} - `; + const rating_html = `${[1, 2, 3, 4, 5].map(i => + ` + + ` + ).join('')}`; + return `
    + ${rating_html} +
    `; }, Currency: function (value, docfield, options, doc) { var currency = frappe.meta.get_field_currency(docfield, doc); diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 675cb6f77c..9fdd4a8e36 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -229,7 +229,7 @@ export default class GridRow { this.open_form_button = $(`
    ${frappe.utils.icon('edit', 'xs')} - +
    `) .appendTo($('
    ').appendTo(this.row)) diff --git a/frappe/public/js/frappe/form/script_manager.js b/frappe/public/js/frappe/form/script_manager.js index 7ac3673b08..770319ba53 100644 --- a/frappe/public/js/frappe/form/script_manager.js +++ b/frappe/public/js/frappe/form/script_manager.js @@ -176,7 +176,7 @@ frappe.ui.form.ScriptManager = Class.extend({ eval(doctype.__custom_js); } catch(e) { frappe.msgprint({ - title: __('Error in Custom Script'), + title: __('Error in Client Script'), indicator: 'orange', message: '
    ' + e.stack  + '
    ' }); diff --git a/frappe/public/js/frappe/form/sidebar/form_sidebar.js b/frappe/public/js/frappe/form/sidebar/form_sidebar.js index 40f9e65bb0..d6a9d95801 100644 --- a/frappe/public/js/frappe/form/sidebar/form_sidebar.js +++ b/frappe/public/js/frappe/form/sidebar/form_sidebar.js @@ -210,7 +210,7 @@ frappe.ui.form.Sidebar = class { if (follow == null) { follow = this.frm.get_docinfo().is_document_followed; } - this.follow_button.text(follow ? "Unfollow" : "Follow"); + this.follow_button.text(follow ? __("Unfollow") : __("Follow")); } refresh_like() { diff --git a/frappe/public/js/frappe/list/list_sidebar.html b/frappe/public/js/frappe/list/list_sidebar.html index cd80280d7b..0428f32e60 100644 --- a/frappe/public/js/frappe/list/list_sidebar.html +++ b/frappe/public/js/frappe/list/list_sidebar.html @@ -56,7 +56,7 @@