diff --git a/cypress/integration/form.js b/cypress/integration/form.js index 9c63fe4e8b..5302ed0964 100644 --- a/cypress/integration/form.js +++ b/cypress/integration/form.js @@ -42,18 +42,33 @@ context('Form', () => { it('validates behaviour of Data options validations in child table', () => { // test email validations for set_invalid controller let website_input = 'website.in'; + let valid_email = 'user@email.com'; let expectBackgroundColor = 'rgb(255, 245, 245)'; cy.visit('/app/contact/new'); cy.get('.frappe-control[data-fieldname="email_ids"]').as('table'); cy.get('@table').find('button.grid-add-row').click(); - cy.get('.grid-body .rows [data-fieldname="email_id"]').click(); - cy.get('@table').find('input.input-with-feedback.form-control').as('email_input'); - cy.get('@email_input').type(website_input, { waitForAnimations: false }); + cy.get('@table').find('button.grid-add-row').click(); + cy.get('@table').find('[data-idx="1"]').as('row1'); + cy.get('@table').find('[data-idx="2"]').as('row2'); + cy.get('@row1').click(); + cy.get('@row1').find('input.input-with-feedback.form-control').as('email_input1'); + + cy.get('@email_input1').type(website_input, { waitForAnimations: false }); cy.fill_field('company_name', 'Test Company'); - cy.get('@email_input').should($div => { + + cy.get('@row2').click(); + cy.get('@row2').find('input.input-with-feedback.form-control').as('email_input2'); + cy.get('@email_input2').type(valid_email, { waitForAnimations: false }); + + cy.get('@row1').click(); + cy.get('@email_input1').should($div => { const style = window.getComputedStyle($div[0]); expect(style.backgroundColor).to.equal(expectBackgroundColor); }); + cy.get('@email_input1').should('have.class', 'invalid'); + + cy.get('@row2').click(); + cy.get('@email_input2').should('not.have.class', 'invalid'); }); }); diff --git a/frappe/__init__.py b/frappe/__init__.py index 844a9238e3..785d5ee7e5 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -555,8 +555,15 @@ def whitelist(allow_guest=False, xss_safe=False, methods=None): def innerfn(fn): global whitelisted, guest_methods, xss_safe_methods, allowed_http_methods_for_whitelisted_func - whitelisted.append(fn) + # get function from the unbound / bound method + # this is needed because functions can be compared, but not methods + method = None + if hasattr(fn, '__func__'): + method = fn + fn = method.__func__ + + whitelisted.append(fn) allowed_http_methods_for_whitelisted_func[fn] = methods if allow_guest: @@ -565,10 +572,24 @@ def whitelist(allow_guest=False, xss_safe=False, methods=None): if xss_safe: xss_safe_methods.append(fn) - return fn + return method or fn return innerfn +def is_whitelisted(method): + from frappe.utils import sanitize_html + + is_guest = session['user'] == 'Guest' + if method not in whitelisted or is_guest and method not in guest_methods: + throw(_("Not permitted"), PermissionError) + + if is_guest and method not in xss_safe_methods: + # strictly sanitize form_dict + # escapes html characters like <> except for predefined tags like a, b, ul etc. + for key, value in form_dict.items(): + if isinstance(value, string_types): + form_dict[key] = sanitize_html(value) + def read_only(): def innfn(fn): def wrapper_fn(*args, **kwargs): @@ -1378,7 +1399,7 @@ def get_list(doctype, *args, **kwargs): frappe.get_list("ToDo", fields="*", filters = {"description": ("like", "test%")}) """ import frappe.model.db_query - return frappe.model.db_query.DatabaseQuery(doctype).execute(None, *args, **kwargs) + return frappe.model.db_query.DatabaseQuery(doctype).execute(*args, **kwargs) def get_all(doctype, *args, **kwargs): """List database query via `frappe.model.db_query`. Will **not** check for permissions. diff --git a/frappe/auth.py b/frappe/auth.py index 946a8c52d5..ca97bbc17d 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -215,35 +215,25 @@ class LoginManager: if not (user and pwd): self.fail(_('Incomplete login details'), user=user) - # Ignore password check if tmp_id is set, 2FA takes care of authentication. - validate_password = not bool(frappe.form_dict.get('tmp_id')) - user = User.find_by_credentials(user, pwd, validate_password=validate_password) + user = User.find_by_credentials(user, pwd) if not user: self.fail('Invalid login credentials') - sys_settings = frappe.get_doc("System Settings") - track_login_attempts = (sys_settings.allow_consecutive_login_attempts >0) - - tracker_kwargs = {} - if track_login_attempts: - tracker_kwargs['lock_interval'] = sys_settings.allow_login_after_fail - tracker_kwargs['max_consecutive_login_attempts'] = sys_settings.allow_consecutive_login_attempts - - tracker = LoginAttemptTracker(user.name, **tracker_kwargs) - - if track_login_attempts and not tracker.is_user_allowed(): - frappe.throw(_("Your account has been locked and will resume after {0} seconds") - .format(sys_settings.allow_login_after_fail), frappe.SecurityException) + # Current login flow uses cached credentials for authentication while checking OTP. + # Incase of OTP check, tracker for auth needs to be disabled(If not, it can remove tracker history as it is going to succeed anyway) + # Tracker is activated for 2FA incase of OTP. + ignore_tracker = should_run_2fa(user.name) and ('otp' in frappe.form_dict) + tracker = None if ignore_tracker else get_login_attempt_tracker(user.name) if not user.is_authenticated: - tracker.add_failure_attempt() + tracker and tracker.add_failure_attempt() self.fail('Invalid login credentials', user=user.name) elif not (user.name == 'Administrator' or user.enabled): - tracker.add_failure_attempt() + tracker and tracker.add_failure_attempt() self.fail('User disabled or missing', user=user.name) else: - tracker.add_success_attempt() + tracker and tracker.add_success_attempt() self.user = user.name def force_user_to_reset_password(self): @@ -406,6 +396,27 @@ def validate_ip_address(user): frappe.throw(_("Access not allowed from this IP Address"), frappe.AuthenticationError) +def get_login_attempt_tracker(user_name: str, raise_locked_exception: bool = True): + """Get login attempt tracker instance. + + :param user_name: Name of the loggedin user + :param raise_locked_exception: If set, raises an exception incase of user not allowed to login + """ + sys_settings = frappe.get_doc("System Settings") + track_login_attempts = (sys_settings.allow_consecutive_login_attempts >0) + tracker_kwargs = {} + + if track_login_attempts: + tracker_kwargs['lock_interval'] = sys_settings.allow_login_after_fail + tracker_kwargs['max_consecutive_login_attempts'] = sys_settings.allow_consecutive_login_attempts + + tracker = LoginAttemptTracker(user_name, **tracker_kwargs) + + if raise_locked_exception and track_login_attempts and not tracker.is_user_allowed(): + frappe.throw(_("Your account has been locked and will resume after {0} seconds") + .format(sys_settings.allow_login_after_fail), frappe.SecurityException) + return tracker + class LoginAttemptTracker(object): """Track login attemts of a user. diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index 281e699640..bf05baf5b6 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -118,6 +118,7 @@ class AutoRepeat(Document): def is_completed(self): return self.end_date and getdate(self.end_date) < getdate(today()) + @frappe.whitelist() def get_auto_repeat_schedule(self): schedule_details = [] start_date = getdate(self.start_date) @@ -328,6 +329,7 @@ class AutoRepeat(Document): make(doctype=new_doc.doctype, name=new_doc.name, recipients=recipients, subject=subject, content=message, attachments=attachments, send_email=1) + @frappe.whitelist() def fetch_linked_contacts(self): if self.reference_doctype and self.reference_document: res = get_contacts_linking_to(self.reference_doctype, self.reference_document, fields=['email_id']) diff --git a/frappe/client.py b/frappe/client.py index 2217b53673..58cfbd2edd 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -8,6 +8,8 @@ import frappe.model import frappe.utils import json, os from frappe.utils import get_safe_filters +from frappe.desk.reportview import validate_args +from frappe.model.db_query import check_parent_permission from six import iteritems, string_types, integer_types @@ -19,7 +21,7 @@ Requests via FrappeClient are also handled here. @frappe.whitelist() def get_list(doctype, fields=None, filters=None, order_by=None, - limit_start=None, limit_page_length=20, parent=None): + limit_start=None, limit_page_length=20, parent=None, debug=False, as_dict=True): '''Returns a list of records by filters, fields, ordering and limit :param doctype: DocType of the data to be queried @@ -31,8 +33,19 @@ def get_list(doctype, fields=None, filters=None, order_by=None, if frappe.is_table(doctype): check_parent_permission(parent, doctype) - return frappe.get_list(doctype, fields=fields, filters=filters, order_by=order_by, - limit_start=limit_start, limit_page_length=limit_page_length, ignore_permissions=False) + args = frappe._dict( + doctype=doctype, + fields=fields, + filters=filters, + order_by=order_by, + limit_start=limit_start, + limit_page_length=limit_page_length, + debug=debug, + as_list=not as_dict + ) + + validate_args(args) + return frappe.get_list(**args) @frappe.whitelist() def get_count(doctype, filters=None, debug=False, cache=False): @@ -91,14 +104,15 @@ def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, paren if frappe.get_meta(doctype).issingle: value = frappe.db.get_values_from_single(fields, filters, doctype, as_dict=as_dict, debug=debug) else: - value = frappe.get_list(doctype, filters=filters, fields=fields, debug=debug, limit=1) + value = get_list(doctype, filters=filters, fields=fields, debug=debug, limit_page_length=1, as_dict=as_dict) if as_dict: - value = value[0] if value else {} - else: - value = value[0].fieldname + return value[0] if value else {} - return value + if not value: + return + + return value[0] if len(fields) > 1 else value[0][0] @frappe.whitelist() def get_single_value(doctype, field): @@ -378,18 +392,6 @@ def attach_file(filename=None, filedata=None, doctype=None, docname=None, folder def get_hooks(hook, app_name=None): return frappe.get_hooks(hook, app_name) -def check_parent_permission(parent, child_doctype): - if parent: - # User may pass fake parent and get the information from the child table - if child_doctype and not frappe.db.exists('DocField', - {'parent': parent, 'options': child_doctype}): - raise frappe.PermissionError - - if frappe.permissions.has_permission(parent): - return - # Either parent not passed or the user doesn't have permission on parent doctype of child table! - raise frappe.PermissionError - @frappe.whitelist() def is_document_amended(doctype, docname): if frappe.permissions.has_permission(doctype): @@ -400,4 +402,4 @@ def is_document_amended(doctype, docname): except frappe.db.InternalError: pass - return False \ No newline at end of file + return False diff --git a/frappe/config/__init__.py b/frappe/config/__init__.py index cc9d0e6c67..30be82d0df 100644 --- a/frappe/config/__init__.py +++ b/frappe/config/__init__.py @@ -36,48 +36,10 @@ def get_modules_from_all_apps(): return modules_list def get_modules_from_app(app): - try: - modules = frappe.get_attr(app + '.config.desktop.get_data')() or {} - except ImportError: - return [] - - active_domains = frappe.get_active_domains() - - if isinstance(modules, dict): - active_modules_list = [] - for m, module in iteritems(modules): - module['module_name'] = m - module['app'] = app - active_modules_list.append(module) - else: - for m in modules: - if m.get("type") == "module" and "category" not in m: - m["category"] = "Modules" - - # Only newly formatted modules that have a category to be shown on desk - modules = [m for m in modules if m.get("category")] - active_modules_list = [] - - for m in modules: - to_add = True - module_name = m.get("module_name") - - # Check Domain - if is_domain(m) and module_name not in active_domains: - to_add = False - - # Check if config - if is_module(m) and not config_exists(app, frappe.scrub(module_name)): - to_add = False - - if "condition" in m and not m["condition"]: - to_add = False - - if to_add: - m["app"] = app - active_modules_list.append(m) - - return active_modules_list + return frappe.get_all('Module Def', + filters={'app_name': app}, + fields=['module_name', 'app_name as app'] + ) def get_all_empty_tables_by_module(): empty_tables = set(r[0] for r in frappe.db.multisql({ diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index e4fd181733..ad5d60500b 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -159,7 +159,7 @@ def update_comments_in_parent(reference_doctype, reference_name, _comments): """Updates `_comments` property in parent Document with given dict. :param _comments: Dict of comments.""" - if not reference_doctype or not reference_name or frappe.db.get_value("DocType", reference_doctype, "issingle"): + if not reference_doctype or not reference_name or frappe.db.get_value("DocType", reference_doctype, "issingle") or frappe.db.get_value("DocType", reference_doctype, "is_virtual"): return try: diff --git a/frappe/core/doctype/data_import/data_import.js b/frappe/core/doctype/data_import/data_import.js index 0e827a42d8..e03c22a898 100644 --- a/frappe/core/doctype/data_import/data_import.js +++ b/frappe/core/doctype/data_import/data_import.js @@ -479,43 +479,4 @@ frappe.ui.form.on('Data Import', { `); }, - - show_missing_link_values(frm, missing_link_values) { - let can_be_created_automatically = missing_link_values.every( - d => d.has_one_mandatory_field - ); - - let html = missing_link_values - .map(d => { - let doctype = d.doctype; - let values = d.missing_values; - return ` -
${doctype}
- - `; - }) - .join(''); - - if (can_be_created_automatically) { - // prettier-ignore - let message = __('There are some linked records which needs to be created before we can import your file. Do you want to create the following missing records automatically?'); - frappe.confirm(message + html, () => { - frm - .call('create_missing_link_values', { - missing_link_values - }) - .then(r => { - let records = r.message; - frappe.msgprint( - __('Created {0} records successfully.', [records.length]) - ); - }); - }); - } else { - frappe.msgprint( - // prettier-ignore - __('The following records needs to be created before we can import your file.') + html - ); - } - } }); diff --git a/frappe/core/doctype/data_import/data_import.py b/frappe/core/doctype/data_import/data_import.py index a9761c3430..1c56f54303 100644 --- a/frappe/core/doctype/data_import/data_import.py +++ b/frappe/core/doctype/data_import/data_import.py @@ -38,6 +38,7 @@ class DataImport(Document): return validate_google_sheets_url(self.google_sheets_url) + @frappe.whitelist() def get_preview_from_template(self, import_file=None, google_sheets_url=None): if import_file: self.import_file = import_file diff --git a/frappe/core/doctype/doctype/boilerplate/controller._py b/frappe/core/doctype/doctype/boilerplate/controller._py index 97e23c0037..583bd30908 100644 --- a/frappe/core/doctype/doctype/boilerplate/controller._py +++ b/frappe/core/doctype/doctype/boilerplate/controller._py @@ -7,4 +7,4 @@ from __future__ import unicode_literals {base_class_import} class {classname}({base_class}): - pass + {custom_controller} diff --git a/frappe/core/doctype/doctype/doctype.js b/frappe/core/doctype/doctype/doctype.js index 3e2a423b06..def1cb49bd 100644 --- a/frappe/core/doctype/doctype/doctype.js +++ b/frappe/core/doctype/doctype/doctype.js @@ -18,6 +18,7 @@ frappe.ui.form.on('DocType', { frm.set_value("custom", 1); } frm.toggle_enable("custom", 0); + frm.toggle_enable("is_virtual", 0); frm.toggle_enable("beta", 0); } diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 1533829b3c..276ce7bee7 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -22,6 +22,7 @@ "track_views", "custom", "beta", + "is_virtual", "fields_section_break", "fields", "sb1", @@ -528,6 +529,12 @@ "fieldname": "index_web_pages_for_search", "fieldtype": "Check", "label": "Index Web Pages for Search" + }, + { + "default": "0", + "fieldname": "is_virtual", + "fieldtype": "Check", + "label": "Is Virtual" } ], "icon": "fa fa-bolt", @@ -609,7 +616,7 @@ "link_fieldname": "reference_doctype" } ], - "modified": "2021-02-04 15:10:09.227205", + "modified": "2021-02-17 20:18:06.212232", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index c0a82c594a..c078ecb817 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -127,6 +127,10 @@ class DocType(Document): if not frappe.conf.get("developer_mode") and not self.custom: frappe.throw(_("Not in Developer Mode! Set in site_config.json or make 'Custom' DocType."), CannotCreateStandardDoctypeError) + if self.is_virtual and self.custom: + frappe.throw(_("Not allowed to create custom Virtual DocType."), CannotCreateStandardDoctypeError) + + if frappe.conf.get('developer_mode'): self.owner = 'Administrator' self.modified_by = 'Administrator' diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index ec88a2d14c..bfa9d0ec8a 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -480,8 +480,19 @@ class TestDocType(unittest.TestCase): 'link_doctype': "User", 'link_fieldname': "a_field_that_does_not_exists" }) + self.assertRaises(InvalidFieldNameError, validate_links_table_fieldnames, doc) + def test_create_virtual_doctype(self): + """Test virtual DOcTYpe.""" + virtual_doc = new_doctype('Test Virtual Doctype') + virtual_doc.is_virtual = 1 + virtual_doc.insert() + virtual_doc.save() + doc = frappe.get_doc("DocType", "Test Virtual Doctype") + + self.assertEqual(doc.is_virtual, 1) + self.assertFalse(frappe.db.table_exists('Test Virtual Doctype')) def new_doctype(name, unique=0, depends_on='', fields=None): doc = frappe.get_doc({ diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 0cf38508b8..f55214d160 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -94,52 +94,89 @@ class File(Document): self.set_file_name() self.validate_duplicate_entry() self.validate_attachment_limit() + self.validate_folder() - if not self.file_url and not self.flags.ignore_file_validate: - if not self.is_folder: + if self.is_folder: + self.file_url = "" + else: + self.validate_url() + + self.file_size = frappe.form_dict.file_size or self.file_size + + def validate_url(self): + if not self.file_url or self.file_url.startswith(("http://", "https://")): + if not self.flags.ignore_file_validate: self.validate_file() - self.generate_content_hash() - if frappe.db.exists('File', {'name': self.name, 'is_folder': 0}): - old_file_url = self.file_url - if not self.is_folder and (self.is_private != self.db_get('is_private')): - private_files = frappe.get_site_path('private', 'files') - public_files = frappe.get_site_path('public', 'files') + return - file_name = self.file_url.split('/')[-1] - if not self.is_private: - shutil.move(os.path.join(private_files, file_name), - os.path.join(public_files, file_name)) + # Probably an invalid web URL + if not self.file_url.startswith(("/files/", "/private/files/")): + frappe.throw( + _("URL must start with http:// or https://"), + title=_('Invalid URL') + ) - self.file_url = "/files/{0}".format(file_name) + # Ensure correct formatting and type + self.file_url = unquote(self.file_url) + self.is_private = cint(self.is_private) - else: - shutil.move(os.path.join(public_files, file_name), - os.path.join(private_files, file_name)) + self.handle_is_private_changed() - self.file_url = "/private/files/{0}".format(file_name) + base_path = os.path.realpath(get_files_path(is_private=self.is_private)) + if not os.path.realpath(self.get_full_path()).startswith(base_path): + frappe.throw( + _("The File URL you've entered is incorrect"), + title=_('Invalid File URL') + ) - update_existing_file_docs(self) + def handle_is_private_changed(self): + if not frappe.db.exists( + 'File', { + 'name': self.name, + 'is_private': cint(not self.is_private) + } + ): + return - # update documents image url with new file url - if self.attached_to_doctype and self.attached_to_name: - if not self.attached_to_field: - field_name = None - reference_dict = frappe.get_doc(self.attached_to_doctype, self.attached_to_name).as_dict() - for key, value in reference_dict.items(): - if value == old_file_url: - field_name = key - break - self.attached_to_field = field_name - if self.attached_to_field: - frappe.db.set_value(self.attached_to_doctype, self.attached_to_name, - self.attached_to_field, self.file_url) + old_file_url = self.file_url - self.validate_url() + file_name = self.file_url.split('/')[-1] + private_file_path = frappe.get_site_path('private', 'files', file_name) + public_file_path = frappe.get_site_path('public', 'files', file_name) - if self.file_url and (self.is_private != self.file_url.startswith('/private')): - frappe.throw(_('Invalid file URL. Please contact System Administrator.')) + if self.is_private: + shutil.move(public_file_path, private_file_path) + url_starts_with = "/private/files/" + else: + shutil.move(private_file_path, public_file_path) + url_starts_with = "/files/" + + self.file_url = "{0}{1}".format(url_starts_with, file_name) + update_existing_file_docs(self) + + if ( + not self.attached_to_doctype + or not self.attached_to_name + or not self.fetch_attached_to_field(old_file_url) + ): + return + + frappe.db.set_value(self.attached_to_doctype, self.attached_to_name, + self.attached_to_field, self.file_url) + + def fetch_attached_to_field(self, old_file_url): + if self.attached_to_field: + return True + + reference_dict = frappe.get_doc( + self.attached_to_doctype, self.attached_to_name).as_dict() + + for key, value in reference_dict.items(): + if value == old_file_url: + self.attached_to_field = key + return True def validate_attachment_limit(self): attachment_limit = 0 @@ -335,8 +372,13 @@ class File(Document): def get_content(self): """Returns [`file_name`, `content`] for given file name `fname`""" + if self.is_folder: + frappe.throw(_("Cannot get file contents of a Folder")) + if self.get('content'): return self.content + + self.validate_url() file_path = self.get_full_path() # read the file @@ -423,23 +465,6 @@ class File(Document): else: raise Exception - - def validate_url(self, df=None): - if self.file_url: - if not self.file_url.startswith(("http://", "https://", "/files/", "/private/files/")): - frappe.throw(_("URL must start with 'http://' or 'https://'")) - return - - if not self.file_url.startswith(("http://", "https://")): - # local file - root_files_path = get_files_path(is_private=self.is_private) - if not os.path.commonpath([root_files_path]) == os.path.commonpath([root_files_path, self.get_full_path()]): - # basically the file url is skewed to not point to /files/ or /private/files - frappe.throw(_("{0} is not a valid file url").format(self.file_url)) - self.file_url = unquote(self.file_url) - self.file_size = frappe.form_dict.file_size or self.file_size - - def get_uploaded_content(self): # should not be unicode when reading a file, hence using frappe.form if 'filedata' in frappe.form_dict: diff --git a/frappe/core/doctype/file/test_file.py b/frappe/core/doctype/file/test_file.py index e627558680..216dfd5495 100644 --- a/frappe/core/doctype/file/test_file.py +++ b/frappe/core/doctype/file/test_file.py @@ -192,13 +192,10 @@ class TestSameContent(unittest.TestCase): class TestFile(unittest.TestCase): - - def setUp(self): self.delete_test_data() self.upload_file() - def tearDown(self): try: frappe.get_doc("File", {"file_name": "file_copy.txt"}).delete() @@ -352,6 +349,22 @@ class TestFile(unittest.TestCase): self.assertEqual(file1.file_url, file2.file_url) self.assertTrue(os.path.exists(file2.get_full_path())) + def test_parent_directory_validation_in_file_url(self): + file1 = frappe.get_doc({ + "doctype": "File", + "file_name": 'parent_dir.txt', + "attached_to_doctype": "", + "attached_to_name": "", + "is_private": 1, + "content": test_content1}).insert() + + file1.file_url = '/private/files/../test.txt' + self.assertRaises(frappe.exceptions.ValidationError, file1.save) + + # No validation to see if file exists + file1.reload() + file1.file_url = '/private/files/parent_dir2.txt' + file1.save() class TestAttachment(unittest.TestCase): test_doctype = 'Test For Attachment' diff --git a/frappe/core/doctype/report/report.py b/frappe/core/doctype/report/report.py index 01c32bcb57..fb44e61cc8 100644 --- a/frappe/core/doctype/report/report.py +++ b/frappe/core/doctype/report/report.py @@ -58,6 +58,7 @@ class Report(Document): def get_columns(self): return [d.as_dict(no_default_fields = True) for d in self.columns] + @frappe.whitelist() def set_doctype_roles(self): if not self.get('roles') and self.is_standard == 'No': meta = frappe.get_meta(self.ref_doctype) @@ -304,7 +305,7 @@ class Report(Document): return data - @Document.whitelist + @frappe.whitelist() def toggle_disable(self, disable): self.db_set("disabled", cint(disable)) diff --git a/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py b/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py index f5081ef595..77b523987c 100644 --- a/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py +++ b/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py @@ -8,6 +8,7 @@ from frappe.core.doctype.report.report import is_prepared_report_disabled from frappe.model.document import Document class RolePermissionforPageandReport(Document): + @frappe.whitelist() def set_report_page_data(self): self.set_custom_roles() self.check_prepared_report_disabled() @@ -35,12 +36,14 @@ class RolePermissionforPageandReport(Document): doc = frappe.get_doc(doctype, docname) return doc.roles + @frappe.whitelist() def reset_roles(self): roles = self.get_standard_roles() self.set('roles', roles) self.update_custom_roles() self.update_disable_prepared_report() + @frappe.whitelist() def update_report_page_data(self): self.update_custom_roles() self.update_disable_prepared_report() diff --git a/frappe/core/doctype/system_settings/system_settings.json b/frappe/core/doctype/system_settings/system_settings.json index 7116cd908c..4b53983702 100644 --- a/frappe/core/doctype/system_settings/system_settings.json +++ b/frappe/core/doctype/system_settings/system_settings.json @@ -1,7 +1,5 @@ { "actions": [], - "allow_read": 1, - "allow_workflow": 1, "creation": "2014-04-17 16:53:52.640856", "doctype": "DocType", "document_type": "System", @@ -460,9 +458,11 @@ }, { "default": "Frappe", + "description": "The application name will be used in the Login page.", "fieldname": "app_name", "fieldtype": "Data", - "label": "App Name" + "hidden": 1, + "label": "Application Name" }, { "default": "1", @@ -474,7 +474,7 @@ "icon": "fa fa-cog", "issingle": 1, "links": [], - "modified": "2021-03-25 17:54:32.668876", + "modified": "2021-03-30 11:47:47.330437", "modified_by": "Administrator", "module": "Core", "name": "System Settings", @@ -492,4 +492,4 @@ "sort_field": "modified", "sort_order": "ASC", "track_changes": 1 -} \ No newline at end of file +} diff --git a/frappe/core/doctype/test/__init__.py b/frappe/core/doctype/test/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/core/doctype/test/test.js b/frappe/core/doctype/test/test.js new file mode 100644 index 0000000000..e423c58686 --- /dev/null +++ b/frappe/core/doctype/test/test.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe Technologies and contributors +// For license information, please see license.txt + +frappe.ui.form.on('test', { + // refresh: function(frm) { + + // } +}); diff --git a/frappe/core/doctype/test/test.json b/frappe/core/doctype/test/test.json new file mode 100644 index 0000000000..31a57c9964 --- /dev/null +++ b/frappe/core/doctype/test/test.json @@ -0,0 +1,42 @@ +{ + "actions": [], + "creation": "2021-03-31 10:06:57.919697", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "test" + ], + "fields": [ + { + "fieldname": "test", + "fieldtype": "Data", + "label": "Test" + } + ], + "index_web_pages_for_search": 1, + "is_virtual": 1, + "links": [], + "modified": "2021-03-31 10:06:57.919697", + "modified_by": "Administrator", + "module": "Core", + "name": "test", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/core/doctype/test/test.py b/frappe/core/doctype/test/test.py new file mode 100644 index 0000000000..7e91b1cd4a --- /dev/null +++ b/frappe/core/doctype/test/test.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document +import json + + +class test(Document): + + def db_insert(self): + d = self.get_valid_dict(convert_dates_to_str=True) + with open("data_file.json", "w+") as read_file: + json.dump(d, read_file) + + def load_from_db(self): + with open("data_file.json", "r") as read_file: + d = json.load(read_file) + super(Document, self).__init__(d) + + def db_update(self): + d = self.get_valid_dict(convert_dates_to_str=True) + with open("data_file.json", "w+") as read_file: + json.dump(d, read_file) + + def get_list(self, args): + with open("data_file.json", "r") as read_file: + return [json.load(read_file)] + + def get_value(self, fields, filters, **kwargs): + # return [] + with open("data_file.json", "r") as read_file: + return [json.load(read_file)] \ No newline at end of file diff --git a/frappe/core/doctype/test/test_test.py b/frappe/core/doctype/test/test_test.py new file mode 100644 index 0000000000..2a9b43bf95 --- /dev/null +++ b/frappe/core/doctype/test/test_test.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class Testtest(unittest.TestCase): + pass diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index c7bc6c43c0..64ac7f5bca 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -558,7 +558,7 @@ class User(Document): user['is_authenticated'] = True if validate_password: try: - check_password(user['name'], password) + check_password(user['name'], password, delete_tracker_cache=False) except frappe.AuthenticationError: user['is_authenticated'] = False diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index ad8d80e675..c79c965aae 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -24,6 +24,7 @@ class CustomizeForm(Document): frappe.db.sql("delete from tabSingles where doctype='Customize Form'") frappe.db.sql("delete from `tabCustomize Form Field`") + @frappe.whitelist() def fetch_to_customize(self): self.clear_existing_doc() if not self.doc_type: @@ -133,6 +134,7 @@ class CustomizeForm(Document): self.doc_type = doc_type self.name = "Customize Form" + @frappe.whitelist() def save_customization(self): if not self.doc_type: return @@ -448,6 +450,7 @@ class CustomizeForm(Document): self.flags.update_db = True + @frappe.whitelist() def reset_to_defaults(self): if not self.doc_type: return diff --git a/frappe/data_migration/doctype/data_migration_run/data_migration_run.py b/frappe/data_migration/doctype/data_migration_run/data_migration_run.py index 473acfb3d0..aed9c6cb1d 100644 --- a/frappe/data_migration/doctype/data_migration_run/data_migration_run.py +++ b/frappe/data_migration/doctype/data_migration_run/data_migration_run.py @@ -10,6 +10,7 @@ from frappe.utils import cstr from frappe.data_migration.doctype.data_migration_mapping.data_migration_mapping import get_source_value class DataMigrationRun(Document): + @frappe.whitelist() def run(self): self.begin() if self.total_pages > 0: diff --git a/frappe/database/database.py b/frappe/database/database.py index 4fcf10efda..7fe76c799b 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -455,6 +455,10 @@ class Database(object): elif (not ignore) and frappe.db.is_table_missing(e): # table not found, look in singles out = self.get_values_from_single(fields, filters, doctype, as_dict, debug, update) + if not out: + # check for virtual doctype + out = self.get_values_from_virtual_doctype(fields, filters, doctype, as_dict, debug, update) + else: raise else: @@ -507,6 +511,10 @@ class Database(object): else: return r and [[i[1] for i in r]] or [] + def get_values_from_virtual_doctype(self, fields, filters, doctype, as_dict=False, debug=False, update=None): + """Reture single values from virtual doctype.""" + return frappe.get_doc(doctype).get_value(fields, filters, as_dict=False, debug=False, update=None) + def get_singles_dict(self, doctype, debug = False): """Get Single DocType as dict. diff --git a/frappe/database/schema.py b/frappe/database/schema.py index daabbaa61c..5f5ba06d8b 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -30,6 +30,9 @@ class DBTable: self.get_columns_from_docfields() def sync(self): + if self.meta.get('is_virtual'): + # no schema to sync for virtual doctypes + return if self.is_new(): self.create() else: diff --git a/frappe/desk/doctype/notification_log/notification_log.py b/frappe/desk/doctype/notification_log/notification_log.py index a6126f1f9b..20551559fd 100644 --- a/frappe/desk/doctype/notification_log/notification_log.py +++ b/frappe/desk/doctype/notification_log/notification_log.py @@ -61,7 +61,7 @@ def make_notification_logs(doc, users): from frappe.social.doctype.energy_point_settings.energy_point_settings import is_energy_point_enabled for user in users: - if frappe.db.exists('User', {"name": user, "enabled": 1}): + if frappe.db.exists('User', {"email": user, "enabled": 1}): if is_notifications_enabled(user): if doc.type == 'Energy Point' and not is_energy_point_enabled(): return diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index 1f5c437330..c1429d361f 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -100,6 +100,7 @@ def get_docinfo(doc=None, doctype=None, name=None): "assignment_logs": get_comments(doc.doctype, doc.name, 'assignment'), "permissions": get_doc_permissions(doc), "shared": frappe.share.get_users(doc.doctype, doc.name), + "info_logs": get_comments(doc.doctype, doc.name, 'Info'), "share_logs": get_comments(doc.doctype, doc.name, 'share'), "like_logs": get_comments(doc.doctype, doc.name, 'Like'), "views": get_view_logs(doc.doctype, doc.name), diff --git a/frappe/desk/form/run_method.py b/frappe/desk/form/run_method.py deleted file mode 100644 index 7952f3b68d..0000000000 --- a/frappe/desk/form/run_method.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals -import json, inspect -import frappe -from frappe import _ -from frappe.utils import cint -from six import text_type, string_types - -@frappe.whitelist() -def runserverobj(method, docs=None, dt=None, dn=None, arg=None, args=None): - """run controller method - old style""" - if not args: args = arg or "" - - if dt: # not called from a doctype (from a page) - if not dn: dn = dt # single - doc = frappe.get_doc(dt, dn) - - else: - doc = frappe.get_doc(json.loads(docs)) - doc._original_modified = doc.modified - doc.check_if_latest() - - if not doc.has_permission("read"): - frappe.msgprint(_("Not permitted"), raise_exception = True) - - if doc: - try: - args = json.loads(args) - except ValueError: - args = args - - try: - fnargs, varargs, varkw, defaults = inspect.getargspec(getattr(doc, method)) - except ValueError: - fnargs = inspect.getfullargspec(getattr(doc, method)).args - varargs = inspect.getfullargspec(getattr(doc, method)).varargs - varkw = inspect.getfullargspec(getattr(doc, method)).varkw - defaults = inspect.getfullargspec(getattr(doc, method)).defaults - - if not fnargs or (len(fnargs)==1 and fnargs[0]=="self"): - r = doc.run_method(method) - - elif "args" in fnargs or not isinstance(args, dict): - r = doc.run_method(method, args) - - else: - r = doc.run_method(method, **args) - - if r: - #build output as csv - if cint(frappe.form_dict.get('as_csv')): - make_csv_output(r, doc.doctype) - else: - frappe.response['message'] = r - - frappe.response.docs.append(doc) - -def make_csv_output(res, dt): - """send method response as downloadable CSV file""" - import frappe - - from six import StringIO - import csv - - f = StringIO() - writer = csv.writer(f) - for r in res: - row = [] - for v in r: - if isinstance(v, string_types): - v = v.encode("utf-8") - row.append(v) - writer.writerow(row) - - f.seek(0) - - frappe.response['result'] = text_type(f.read(), 'utf-8') - frappe.response['type'] = 'csv' - frappe.response['doctype'] = dt.replace(' ','') diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 3003385601..4bc71a7796 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -8,30 +8,177 @@ import frappe, json from six.moves import range import frappe.permissions from frappe.model.db_query import DatabaseQuery +from frappe.model import default_fields, optional_fields from frappe import _ from six import string_types, StringIO from frappe.core.doctype.access_log.access_log import make_access_log from frappe.utils import cstr, format_duration +from frappe.model.base_document import get_controller @frappe.whitelist(allow_guest=True) @frappe.read_only() def get(): args = get_form_params() - - data = compress(execute(**args), args = args) - + # If virtual doctype get data from controller het_list method + if frappe.db.get_value("DocType", filters={"name": args.doctype}, fieldname="is_virtual"): + controller = get_controller(args.doctype) + data = compress(controller(args.doctype).get_list(args)) + else: + data = compress(execute(**args), args=args) return data +@frappe.whitelist() +@frappe.read_only() +def get_list(): + # uncompressed (refactored from frappe.model.db_query.get_list) + return execute(**get_form_params()) + +@frappe.whitelist() +@frappe.read_only() +def get_count(): + args = get_form_params() + + distinct = 'distinct ' if args.distinct=='true' else '' + args.fields = [f"count({distinct}`tab{args.doctype}`.name) as total_count"] + return execute(**args)[0].get('total_count') + def execute(doctype, *args, **kwargs): return DatabaseQuery(doctype).execute(*args, **kwargs) def get_form_params(): """Stringify GET request parameters.""" data = frappe._dict(frappe.local.form_dict) + clean_params(data) + validate_args(data) + return data - is_report = data.get('view') == 'Report' +def validate_args(data): + parse_json(data) + setup_group_by(data) + validate_fields(data) + if data.filters: + validate_filters(data, data.filters) + if data.or_filters: + validate_filters(data, data.or_filters) + + data.strict = None + + return data + +def validate_fields(data): + wildcard = update_wildcard_field_param(data) + + for field in data.fields or []: + fieldname = extract_fieldname(field) + if is_standard(fieldname): + continue + + meta, df = get_meta_and_docfield(fieldname, data) + + if not df: + if wildcard: + continue + else: + raise_invalid_field(fieldname) + + # remove the field from the query if the report hide flag is set and current view is Report + if df.report_hide and data.view == 'Report': + data.fields.remove(field) + continue + + if df.fieldname in [_df.fieldname for _df in meta.get_high_permlevel_fields()]: + if df.get('permlevel') not in meta.get_permlevel_access(parenttype=data.doctype): + data.fields.remove(field) + +def validate_filters(data, filters): + if isinstance(filters, list): + # filters as list + for condition in filters: + if len(condition)==3: + # [fieldname, condition, value] + fieldname = condition[0] + if is_standard(fieldname): + continue + meta, df = get_meta_and_docfield(fieldname, data) + if not df: + raise_invalid_field(condition[0]) + else: + # [doctype, fieldname, condition, value] + fieldname = condition[1] + if is_standard(fieldname): + continue + meta = frappe.get_meta(condition[0]) + if not meta.get_field(fieldname): + raise_invalid_field(fieldname) + + else: + for fieldname in filters: + if is_standard(fieldname): + continue + meta, df = get_meta_and_docfield(fieldname, data) + if not df: + raise_invalid_field(fieldname) + +def setup_group_by(data): + '''Add columns for aggregated values e.g. count(name)''' + if data.group_by: + if data.aggregate_function.lower() not in ('count', 'sum', 'avg'): + frappe.throw(_('Invalid aggregate function')) + if '`' in data.aggregate_on: + raise_invalid_field(data.aggregate_on) + data.fields.append('{aggregate_function}(`tab{doctype}`.`{aggregate_on}`) AS _aggregate_column'.format(**data)) + if data.aggregate_on: + data.fields.append(data.aggregate_on) + + data.pop('aggregate_on') + data.pop('aggregate_function') + +def raise_invalid_field(fieldname): + frappe.throw(_('Field not permitted in query') + ': {0}'.format(fieldname), frappe.DataError) + +def is_standard(fieldname): + if '.' in fieldname: + parenttype, fieldname = get_parenttype_and_fieldname(fieldname, None) + return fieldname in default_fields or fieldname in optional_fields + +def extract_fieldname(field): + for text in (',', '/*', '#'): + if text in field: + raise_invalid_field(field) + + fieldname = field + for sep in (' as ', ' AS '): + if sep in fieldname: + fieldname = fieldname.split(sep)[0] + + # certain functions allowed, extract the fieldname from the function + if (fieldname.startswith('count(') + or fieldname.startswith('sum(') + or fieldname.startswith('avg(')): + if not fieldname.strip().endswith(')'): + raise_invalid_field(field) + fieldname = fieldname.split('(', 1)[1][:-1] + + return fieldname + +def get_meta_and_docfield(fieldname, data): + parenttype, fieldname = get_parenttype_and_fieldname(fieldname, data) + meta = frappe.get_meta(parenttype) + df = meta.get_field(fieldname) + return meta, df + +def update_wildcard_field_param(data): + if ((isinstance(data.fields, string_types) and data.fields == "*") + or (isinstance(data.fields, (list, tuple)) and len(data.fields) == 1 and data.fields[0] == "*")): + data.fields = frappe.db.get_table_columns(data.doctype) + return True + + return False + + +def clean_params(data): data.pop('cmd', None) data.pop('data', None) data.pop('ignore_permissions', None) @@ -41,8 +188,12 @@ def get_form_params(): if "csrf_token" in data: del data["csrf_token"] + +def parse_json(data): if isinstance(data.get("filters"), string_types): data["filters"] = json.loads(data["filters"]) + if isinstance(data.get("or_filters"), string_types): + data["or_filters"] = json.loads(data["or_filters"]) if isinstance(data.get("fields"), string_types): data["fields"] = json.loads(data["fields"]) if isinstance(data.get("docstatus"), string_types): @@ -52,47 +203,8 @@ def get_form_params(): else: data["save_user_settings"] = True - fields = data["fields"] - if ((isinstance(fields, string_types) and fields == "*") - or (isinstance(fields, (list, tuple)) and len(fields) == 1 and fields[0] == "*")): - parenttype = data.doctype - data["fields"] = frappe.db.get_table_columns(parenttype) - fields = data["fields"] - - for field in fields: - key = field.split(" as ")[0] - - if key.startswith('count('): continue - if key.startswith('sum('): continue - if key.startswith('avg('): continue - - parenttype, fieldname = get_parent_dt_and_field(key, data) - - if fieldname == "*": - # * inside list is not allowed with other fields - fields.remove(field) - - meta = frappe.get_meta(parenttype) - df = meta.get_field(fieldname) - - report_hide = df.report_hide if df else None - - # remove the field from the query if the report hide flag is set and current view is Report - if report_hide and is_report: - fields.remove(field) - - if df and fieldname in [df.fieldname for df in meta.get_high_permlevel_fields()]: - if df.get('permlevel') not in meta.get_permlevel_access(parenttype=data.doctype) and field in fields: - fields.remove(field) - - # queries must always be server side - data.query = None - data.strict = None - - return data - -def get_parent_dt_and_field(field, data): +def get_parenttype_and_fieldname(field, data): if "." in field: parenttype, fieldname = field.split(".")[0][4:-1], field.split(".")[1].strip("`") else: @@ -101,7 +213,6 @@ def get_parent_dt_and_field(field, data): return parenttype, fieldname - def compress(data, args = {}): """separate keys and values""" from frappe.desk.query_report import add_total_row @@ -327,8 +438,9 @@ def get_stats(stats, doctype, filters=[]): try: columns = frappe.db.get_table_columns(doctype) - except frappe.db.InternalError: + except (frappe.db.InternalError, frappe.db.ProgrammingError): # raised when _user_tags column is added on the fly + # raised if its a virtual doctype columns = [] for tag in tags: diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 6faa827dde..6181261fc2 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -6,8 +6,7 @@ from __future__ import unicode_literals import frappe, json from frappe.utils import cstr, unique, cint from frappe.permissions import has_permission -from frappe.handler import is_whitelisted -from frappe import _ +from frappe import _, is_whitelisted from six import string_types import re import wrapt @@ -221,4 +220,4 @@ def validate_and_sanitize_search_inputs(fn, instance, args, kwargs): if kwargs['doctype'] and not frappe.db.exists('DocType', kwargs['doctype']): return [] - return fn(**kwargs) \ No newline at end of file + return fn(**kwargs) diff --git a/frappe/desk/treeview.py b/frappe/desk/treeview.py index e0b6ca240a..12fdb0dadc 100644 --- a/frappe/desk/treeview.py +++ b/frappe/desk/treeview.py @@ -36,20 +36,27 @@ def get_all_nodes(doctype, label, parent, tree_method, **filters): return out @frappe.whitelist() -def get_children(doctype, parent='', **filters): +def get_children(doctype, parent=''): + return _get_children(doctype, parent) + +def _get_children(doctype, parent='', ignore_permissions=False): parent_field = 'parent_' + doctype.lower().replace(' ', '_') - filters=[['ifnull(`{0}`,"")'.format(parent_field), '=', parent], + filters = [['ifnull(`{0}`,"")'.format(parent_field), '=', parent], ['docstatus', '<' ,'2']] - doctype_meta = frappe.get_meta(doctype) - data = frappe.get_list(doctype, fields=[ - 'name as value', - '{0} as title'.format(doctype_meta.get('title_field') or 'name'), - 'is_group as expandable'], - filters=filters, - order_by='name') + meta = frappe.get_meta(doctype) - return data + return frappe.get_list( + doctype, + fields=[ + 'name as value', + '{0} as title'.format(meta.get('title_field') or 'name'), + 'is_group as expandable' + ], + filters=filters, + order_by='name', + ignore_permissions=ignore_permissions + ) @frappe.whitelist() def add_node(): diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index ad985ee20e..c792347c09 100755 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -29,6 +29,7 @@ class Newsletter(WebsiteGenerator): self.queue_all(test_email=True) frappe.msgprint(_("Test email sent to {0}").format(self.test_email_id)) + @frappe.whitelist() def send_emails(self): """send emails to leads and customers""" if self.email_sent: diff --git a/frappe/frappeclient.py b/frappe/frappeclient.py index 919c334e51..054a8c9369 100644 --- a/frappe/frappeclient.py +++ b/frappe/frappeclient.py @@ -86,7 +86,7 @@ class FrappeClient(object): 'cmd': 'logout', }, verify=self.verify, headers=self.headers) - def get_list(self, doctype, fields='"*"', filters=None, limit_start=0, limit_page_length=0): + def get_list(self, doctype, fields='["name"]', filters=None, limit_start=0, limit_page_length=0): """Returns list of records of a particular type""" if not isinstance(fields, string_types): fields = json.dumps(fields) diff --git a/frappe/handler.py b/frappe/handler.py index cac9c3a460..82c1ea65c6 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -2,17 +2,19 @@ # MIT License. See license.txt from __future__ import unicode_literals + +from werkzeug.wrappers import Response + import frappe -from frappe import _ import frappe.utils import frappe.sessions -import frappe.desk.form.run_method -from frappe.utils.response import build_response -from frappe.api import validate_auth from frappe.utils import cint +from frappe.api import validate_auth +from frappe import _, is_whitelisted +from frappe.utils.response import build_response +from frappe.utils.csvutils import build_csv_response from frappe.core.doctype.server_script.server_script_utils import run_server_script_api -from werkzeug.wrappers import Response -from six import string_types + ALLOWED_MIMETYPES = ('image/png', 'image/jpeg', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', @@ -54,18 +56,14 @@ def execute_cmd(cmd, from_async=False): try: method = get_attr(cmd) except Exception as e: - if frappe.local.conf.developer_mode: - raise e - else: - frappe.respond_as_web_page(title='Invalid Method', html='Method not found', - indicator_color='red', http_status_code=404) - return + frappe.throw(_('Invalid Method')) if from_async: method = method.queue - is_whitelisted(method) - is_valid_http_method(method) + if method != run_doc_method: + is_whitelisted(method) + is_valid_http_method(method) return frappe.call(method, **frappe.form_dict) @@ -73,33 +71,15 @@ def is_valid_http_method(method): http_method = frappe.local.request.method if http_method not in frappe.allowed_http_methods_for_whitelisted_func[method]: - frappe.throw(_("Not permitted"), frappe.PermissionError) + throw_permission_error() -def is_whitelisted(method): - # check if whitelisted - if frappe.session['user'] == 'Guest': - if (method not in frappe.guest_methods): - frappe.throw(_("Not permitted"), frappe.PermissionError) - - if method not in frappe.xss_safe_methods: - # strictly sanitize form_dict - # escapes html characters like <> except for predefined tags like a, b, ul etc. - for key, value in frappe.form_dict.items(): - if isinstance(value, string_types): - frappe.form_dict[key] = frappe.utils.sanitize_html(value) - - else: - if not method in frappe.whitelisted: - frappe.throw(_("Not permitted"), frappe.PermissionError) +def throw_permission_error(): + frappe.throw(_("Not permitted"), frappe.PermissionError) @frappe.whitelist(allow_guest=True) def version(): return frappe.__version__ -@frappe.whitelist() -def runserverobj(method, docs=None, dt=None, dn=None, arg=None, args=None): - frappe.desk.form.run_method.runserverobj(method, docs=docs, dt=dt, dn=dn, arg=arg, args=args) - @frappe.whitelist(allow_guest=True) def logout(): frappe.local.login_manager.logout() @@ -112,15 +92,6 @@ def web_logout(): frappe.respond_as_web_page(_("Logged Out"), _("You have been successfully logged out"), indicator_color='green') -@frappe.whitelist(allow_guest=True) -def run_custom_method(doctype, name, custom_method): - """cmd=run_custom_method&doctype={doctype}&name={name}&custom_method={custom_method}""" - doc = frappe.get_doc(doctype, name) - if getattr(doc, custom_method, frappe._dict()).is_whitelisted: - frappe.call(getattr(doc, custom_method), **frappe.local.form_dict) - else: - frappe.throw(_("Not permitted"), frappe.PermissionError) - @frappe.whitelist() def uploadfile(): ret = None @@ -222,6 +193,66 @@ def get_attr(cmd): frappe.log("method:" + cmd) return method -@frappe.whitelist(allow_guest = True) +@frappe.whitelist(allow_guest=True) def ping(): return "pong" + + +def run_doc_method(method, docs=None, dt=None, dn=None, arg=None, args=None): + """run a whitelisted controller method""" + import json + import inspect + + if not args: + args = arg or "" + + if dt: # not called from a doctype (from a page) + if not dn: + dn = dt # single + doc = frappe.get_doc(dt, dn) + + else: + doc = frappe.get_doc(json.loads(docs)) + doc._original_modified = doc.modified + doc.check_if_latest() + + if not doc or not doc.has_permission("read"): + throw_permission_error() + + try: + args = json.loads(args) + except ValueError: + args = args + + method_obj = getattr(doc, method) + fn = getattr(method_obj, '__func__', method_obj) + is_whitelisted(fn) + is_valid_http_method(fn) + + try: + fnargs = inspect.getargspec(method_obj)[0] + except ValueError: + fnargs = inspect.getfullargspec(method_obj).args + + if not fnargs or (len(fnargs)==1 and fnargs[0]=="self"): + response = doc.run_method(method) + + elif "args" in fnargs or not isinstance(args, dict): + response = doc.run_method(method, args) + + else: + response = doc.run_method(method, **args) + + frappe.response.docs.append(doc) + if not response: + return + + # build output as csv + if cint(frappe.form_dict.get('as_csv')): + build_csv_response(response, doc.doctype.replace(' ', '')) + return + + frappe.response['message'] = response + +# for backwards compatibility +runserverobj = run_doc_method diff --git a/frappe/integrations/doctype/connected_app/connected_app.py b/frappe/integrations/doctype/connected_app/connected_app.py index ec08f8e4be..95077ece77 100644 --- a/frappe/integrations/doctype/connected_app/connected_app.py +++ b/frappe/integrations/doctype/connected_app/connected_app.py @@ -44,6 +44,7 @@ class ConnectedApp(Document): scope=self.get_scopes() ) + @frappe.whitelist() def initiate_web_application_flow(self, user=None, success_uri=None): """Return an authorization URL for the user. Save state in Token Cache.""" user = user or frappe.session.user diff --git a/frappe/integrations/doctype/social_login_key/social_login_key.py b/frappe/integrations/doctype/social_login_key/social_login_key.py index d84e6ef11d..dffb730513 100644 --- a/frappe/integrations/doctype/social_login_key/social_login_key.py +++ b/frappe/integrations/doctype/social_login_key/social_login_key.py @@ -49,6 +49,7 @@ class SocialLoginKey(Document): icon_file = icon_map[self.provider_name] self.icon = '/assets/frappe/icons/social/{0}'.format(icon_file) + @frappe.whitelist() def get_social_login_provider(self, provider, initialize=False): providers = {} diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index de0c1e0e1c..983511f7a4 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -555,22 +555,25 @@ class BaseDocument(object): not _df.get('fetch_if_empty') or (_df.get('fetch_if_empty') and not self.get(_df.fieldname)) ] + if not frappe.get_meta(doctype).get('is_virtual'): + if not fields_to_fetch: + # cache a single value type + values = frappe._dict(name=frappe.db.get_value(doctype, docname, + 'name', cache=True)) + else: + values_to_fetch = ['name'] + [_df.fetch_from.split('.')[-1] + for _df in fields_to_fetch] - if not fields_to_fetch: - # cache a single value type - values = frappe._dict(name=frappe.db.get_value(doctype, docname, - 'name', cache=True)) - else: - values_to_fetch = ['name'] + [_df.fetch_from.split('.')[-1] - for _df in fields_to_fetch] - - # don't cache if fetching other values too - values = frappe.db.get_value(doctype, docname, - values_to_fetch, as_dict=True) + # don't cache if fetching other values too + values = frappe.db.get_value(doctype, docname, + values_to_fetch, as_dict=True) if frappe.get_meta(doctype).issingle: values.name = doctype + if frappe.get_meta(doctype).get('is_virtual'): + values = frappe.get_doc(doctype, docname) + if values: setattr(self, df.fieldname, values.name) @@ -792,7 +795,7 @@ class BaseDocument(object): def _save_passwords(self): """Save password field values in __Auth table""" - from frappe.utils.password import set_encrypted_password + from frappe.utils.password import set_encrypted_password, remove_encrypted_password if self.flags.ignore_save_passwords is True: return @@ -800,6 +803,10 @@ class BaseDocument(object): for df in self.meta.get('fields', {'fieldtype': ('=', 'Password')}): if self.flags.ignore_save_passwords and df.fieldname in self.flags.ignore_save_passwords: continue new_password = self.get(df.fieldname) + + if not new_password: + remove_encrypted_password(self.doctype, self.name, df.fieldname) + if new_password and not self.is_dummy_password(new_password): # is not a dummy password like '*****' set_encrypted_password(self.doctype, self.name, new_password, df.fieldname) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 8eac75eb65..b29e143759 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -14,7 +14,6 @@ import frappe.permissions from datetime import datetime import frappe, json, copy, re from frappe.model import optional_fields -from frappe.client import check_parent_permission from frappe.model.utils.user_settings import get_user_settings, update_user_settings from frappe.utils import flt, cint, get_time, make_filter_tuple, get_filter, add_to_date, cstr, get_timespan_date_range from frappe.model.meta import get_table_columns @@ -32,7 +31,7 @@ class DatabaseQuery(object): self.flags = frappe._dict() self.reference_doctype = None - def execute(self, query=None, fields=None, filters=None, or_filters=None, + def execute(self, fields=None, filters=None, or_filters=None, docstatus=None, group_by=None, order_by=None, limit_start=False, limit_page_length=None, as_list=False, with_childnames=False, debug=False, ignore_permissions=False, user=None, with_comment_count=False, @@ -104,12 +103,9 @@ class DatabaseQuery(object): # no table & ignore_ddl, return if not self.columns: return [] - if query: - result = self.run_custom_query(query) - else: - result = self.build_and_run() - if return_query: - return result + result = self.build_and_run() + if return_query: + return result if with_comment_count and not as_list and self.doctype: self.add_comment_count(result) @@ -707,12 +703,6 @@ class DatabaseQuery(object): return " and ".join(conditions) if conditions else "" - - def run_custom_query(self, query): - if '%(key)s' in query: - query = query.replace('%(key)s', '`name`') - return frappe.db.sql(query, as_dict = (not self.as_list)) - def set_order_by(self, args): meta = frappe.get_meta(self.doctype) @@ -754,7 +744,7 @@ class DatabaseQuery(object): return _lower = parameters.lower() - if 'select' in _lower and ' from ' in _lower: + if 'select' in _lower and 'from' in _lower: frappe.throw(_('Cannot use sub-query in order by')) if re.compile(r".*[^a-z0-9-_ ,`'\"\.\(\)].*").match(_lower): @@ -795,6 +785,18 @@ class DatabaseQuery(object): update_user_settings(self.doctype, user_settings) +def check_parent_permission(parent, child_doctype): + if parent: + # User may pass fake parent and get the information from the child table + if child_doctype and not frappe.db.exists('DocField', + {'parent': parent, 'options': child_doctype}): + raise frappe.PermissionError + + if frappe.permissions.has_permission(parent): + return + # Either parent not passed or the user doesn't have permission on parent doctype of child table! + raise frappe.PermissionError + def get_order_by(doctype, meta): order_by = "" @@ -819,30 +821,6 @@ def get_order_by(doctype, meta): return order_by - -@frappe.whitelist() -def get_list(doctype, *args, **kwargs): - '''wrapper for DatabaseQuery''' - kwargs.pop('cmd', None) - kwargs.pop('ignore_permissions', None) - kwargs.pop('data', None) - kwargs.pop('strict', None) - kwargs.pop('user', None) - - # If doctype is child table - if frappe.is_table(doctype): - # Example frappe.db.get_list('Purchase Receipt Item', {'parent': 'Purchase Receipt'}) - # Here purchase receipt is the parent doctype of the child doctype Purchase Receipt Item - - if not kwargs.get('parent'): - frappe.flags.error_message = _('Parent is required to get child table data') - raise frappe.PermissionError(doctype) - - check_parent_permission(kwargs.get('parent'), doctype) - del kwargs['parent'] - - return DatabaseQuery(doctype).execute(None, *args, **kwargs) - def is_parent_only_filter(doctype, filters): #check if filters contains only parent doctype only_parent_doctype = True diff --git a/frappe/model/document.py b/frappe/model/document.py index 50025597c4..4169919091 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals, print_function import frappe import time -from frappe import _, msgprint +from frappe import _, msgprint, is_whitelisted from frappe.utils import flt, cstr, now, get_datetime_str, file_lock, date_diff from frappe.model.base_document import BaseDocument, get_controller from frappe.model.naming import set_new_name @@ -126,10 +126,10 @@ class Document(BaseDocument): raise ValueError('Illegal arguments') @staticmethod - def whitelist(f): + def whitelist(fn): """Decorator: Whitelist method to be called remotely via REST API.""" - f.whitelisted = True - return f + frappe.whitelist()(fn) + return fn def reload(self): """Reload document from database""" @@ -697,7 +697,7 @@ class Document(BaseDocument): `self.check_docstatus_transition`.""" conflict = False self._action = "save" - if not self.get('__islocal'): + if not self.get('__islocal') and not self.meta.get('is_virtual'): if self.meta.issingle: modified = frappe.db.sql("""select value from tabSingles where doctype=%s and field='modified' for update""", self.doctype) @@ -1148,12 +1148,12 @@ class Document(BaseDocument): return composer - def is_whitelisted(self, method): - fn = getattr(self, method, None) - if not fn: - raise NotFound("Method {0} not found".format(method)) - elif not getattr(fn, "whitelisted", False): - raise Forbidden("Method {0} not whitelisted".format(method)) + def is_whitelisted(self, method_name): + method = getattr(self, method_name, None) + if not method: + raise NotFound("Method {0} not found".format(method_name)) + + is_whitelisted(getattr(method, '__func__', method)) def validate_value(self, fieldname, condition, val2, doc=None, raise_exception=None): """Check that value of fieldname should be 'condition' val2 diff --git a/frappe/modules/utils.py b/frappe/modules/utils.py index b3debfc43c..132aa1e2a5 100644 --- a/frappe/modules/utils.py +++ b/frappe/modules/utils.py @@ -247,6 +247,21 @@ def make_boilerplate(template, doc, opts=None): base_class = 'NestedSet' base_class_import = 'from frappe.utils.nestedset import NestedSet' + custom_controller = 'pass' + if doc.get('is_virtual'): + custom_controller = """ + def db_insert(self): + pass + + def load_from_db(self): + pass + + def db_update(self): + pass + + def get_list(self, args): + pass""" + with open(target_file_path, 'w') as target: with open(os.path.join(get_module_path("core"), "doctype", scrub(doc.doctype), "boilerplate", template), 'r') as source: @@ -257,5 +272,6 @@ def make_boilerplate(template, doc, opts=None): classname=doc.name.replace(" ", ""), base_class_import=base_class_import, base_class=base_class, - doctype=doc.name, **opts) + doctype=doc.name, **opts, + custom_controller=custom_controller) )) 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 index fcf8afc826..7c3aec9510 100644 --- 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 @@ -7,6 +7,9 @@ import frappe def execute(): if frappe.db.table_exists('List View Setting'): + if not frappe.db.table_exists('List View Settings'): + frappe.reload_doc("desk", "doctype", "List View Settings") + 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') @@ -16,5 +19,6 @@ def execute(): # 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 51a2f55a37..f2252b8dfe 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -79,9 +79,9 @@ "public/less/controls.less", "public/less/chat.less", "public/css/fonts/inter/inter.css", - "public/scss/desk.scss", "node_modules/frappe-charts/dist/frappe-charts.min.css", - "node_modules/plyr/dist/plyr.css" + "node_modules/plyr/dist/plyr.css", + "public/scss/desk.scss" ], "css/frappe-rtl.css": [ "public/css/bootstrap-rtl.css", diff --git a/frappe/public/icons/timeless/symbol-defs.svg b/frappe/public/icons/timeless/symbol-defs.svg index 2b0cc8b696..d2c162161f 100644 --- a/frappe/public/icons/timeless/symbol-defs.svg +++ b/frappe/public/icons/timeless/symbol-defs.svg @@ -693,4 +693,10 @@ + + + + + + diff --git a/frappe/public/js/frappe/db.js b/frappe/public/js/frappe/db.js index cf716c67e5..89054e3791 100644 --- a/frappe/public/js/frappe/db.js +++ b/frappe/public/js/frappe/db.js @@ -15,7 +15,7 @@ frappe.db = { } return new Promise ((resolve) => { frappe.call({ - method: 'frappe.model.db_query.get_list', + method: 'frappe.desk.reportview.get_list', args: args, type: 'GET', callback: function(r) { @@ -92,25 +92,19 @@ frappe.db = { }, count: function(doctype, args={}) { let filters = args.filters || {}; - const with_child_table_filter = Array.isArray(filters) && filters.some(filter => { + + // has a filter with childtable? + const distinct = Array.isArray(filters) && filters.some(filter => { return filter[0] !== doctype; }); - const fields = [ - // cannot break this line as it adds extra \n's and \t's which breaks the query - `count(${with_child_table_filter ? 'distinct': ''} ${frappe.model.get_full_column_name('name', doctype)}) AS total_count` - ]; + const fields = []; - return frappe.call({ - type: 'GET', - method: 'frappe.desk.reportview.get', - args: { - doctype, - filters, - fields, - } - }).then(r => { - return r.message.values[0][0]; + return frappe.xcall('frappe.desk.reportview.get_count', { + doctype, + filters, + fields, + distinct, }); }, get_link_options(doctype, txt = '', filters={}) { diff --git a/frappe/public/js/frappe/form/controls/button.js b/frappe/public/js/frappe/form/controls/button.js index 28814531da..b44c9d9dcd 100644 --- a/frappe/public/js/frappe/form/controls/button.js +++ b/frappe/public/js/frappe/form/controls/button.js @@ -34,7 +34,7 @@ frappe.ui.form.ControlButton = frappe.ui.form.ControlData.extend({ var me = this; if(this.frm && this.frm.docname) { frappe.call({ - method: "runserverobj", + method: "run_doc_method", args: {'docs': this.frm.doc, 'method': this.df.options }, btn: this.$input, callback: function(r) { diff --git a/frappe/public/js/frappe/form/controls/color.js b/frappe/public/js/frappe/form/controls/color.js index 7890ea755c..bf04581abd 100644 --- a/frappe/public/js/frappe/form/controls/color.js +++ b/frappe/public/js/frappe/form/controls/color.js @@ -76,7 +76,7 @@ frappe.ui.form.ControlColor = frappe.ui.form.ControlData.extend({ refresh() { this._super(); let color = this.get_color(); - if (this.picker.color !== color) { + if (this.picker && this.picker.color !== color) { this.picker.color = color; this.picker.refresh(); } diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index e0a72ed8c1..1a483c5968 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -83,11 +83,16 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ var doctype = this.get_options(); var me = this; - if(!doctype) return; + if (!doctype) return; + let df = this.df; + if (this.frm && this.frm.doctype !== this.df.parent) { + // incase of grid use common df set in grid + df = this.frm.get_docfield(this.doc.parentfield, this.df.fieldname); + } // set values to fill in the new document - if(this.df.get_route_options_for_new_doc) { - frappe.route_options = this.df.get_route_options_for_new_doc(this); + if (df && df.get_route_options_for_new_doc) { + frappe.route_options = df.get_route_options_for_new_doc(this); } else { frappe.route_options = {}; } diff --git a/frappe/public/js/frappe/form/controls/table.js b/frappe/public/js/frappe/form/controls/table.js index f974a90119..075608aa8c 100644 --- a/frappe/public/js/frappe/form/controls/table.js +++ b/frappe/public/js/frappe/form/controls/table.js @@ -24,13 +24,17 @@ frappe.ui.form.ControlTable = frappe.ui.form.Control.extend({ const grid_rows = grid.grid_rows; const doctype = grid.doctype; const row_docname = $(e.target).closest('.grid-row').data('name'); + const in_grid_form = $(e.target).closest('.form-in-grid').length; let clipboard_data = e.clipboardData || window.clipboardData || e.originalEvent.clipboardData; let pasted_data = clipboard_data.getData('Text'); - if (!pasted_data) return; + if (!pasted_data || in_grid_form) return; let data = frappe.utils.csv_to_array(pasted_data, '\t'); + + if (data.length === 1 && data[0].length === 1) return; + let fieldnames = []; // for raw data with column header if (this.get_field(data[0][0])) { @@ -49,30 +53,30 @@ frappe.ui.form.ControlTable = frappe.ui.form.Control.extend({ } let row_idx = locals[doctype][row_docname].idx; + let data_length = data.length; data.forEach((row, i) => { - let blank_row = !row.filter(Boolean).length; - if (blank_row) return; - setTimeout(() => { - if (row_idx > this.frm.doc[table_field].length) { - this.grid.add_new_row(); - } - if (row_idx > 1 && (row_idx - 1) % grid_pagination.page_length === 0) { - grid_pagination.go_to_page(grid_pagination.page_index + 1); - } - - const row_name = grid_rows[row_idx - 1].doc.name; - row.forEach((value, data_index) => { - if (fieldnames[data_index]) { - frappe.model.set_value(doctype, row_name, fieldnames[data_index], value); + let blank_row = !row.filter(Boolean).length; + if (!blank_row) { + if (row_idx > this.frm.doc[table_field].length) { + this.grid.add_new_row(); } - }); - row_idx++; - let progress = i + 1; - frappe.show_progress(__('Processing'), progress, data.length); - if (progress === data.length) { - frappe.hide_progress(); + if (row_idx > 1 && (row_idx - 1) % grid_pagination.page_length === 0) { + grid_pagination.go_to_page(grid_pagination.page_index + 1); + } + + const row_name = grid_rows[row_idx - 1].doc.name; + row.forEach((value, data_index) => { + if (fieldnames[data_index]) { + frappe.model.set_value(doctype, row_name, fieldnames[data_index], value); + } + }); + row_idx++; + if (data_length >= 10) { + let progress = i + 1; + frappe.show_progress(__('Processing'), progress, data_length, null, true); + } } }, 0); }); diff --git a/frappe/public/js/frappe/form/footer/form_timeline.js b/frappe/public/js/frappe/form/footer/form_timeline.js index 7b8d36d90b..1da59a2fdf 100644 --- a/frappe/public/js/frappe/form/footer/form_timeline.js +++ b/frappe/public/js/frappe/form/footer/form_timeline.js @@ -139,6 +139,7 @@ class FormTimeline extends BaseTimeline { this.timeline_items.push(...this.get_custom_timeline_contents()); this.timeline_items.push(...this.get_assignment_timeline_contents()); this.timeline_items.push(...this.get_attachment_timeline_contents()); + this.timeline_items.push(...this.get_info_timeline_contents()); this.timeline_items.push(...this.get_milestone_timeline_contents()); } } @@ -269,6 +270,17 @@ class FormTimeline extends BaseTimeline { return assignment_timeline_contents; } + get_info_timeline_contents() { + let info_timeline_contents = []; + (this.doc_info.info_logs || []).forEach(info_log => { + info_timeline_contents.push({ + creation: info_log.creation, + content: `${this.get_user_link(info_log.comment_email)} ${info_log.content}`, + }); + }); + return info_timeline_contents; + } + get_attachment_timeline_contents() { let attachment_timeline_contents = []; (this.doc_info.attachment_logs || []).forEach(attachment_log => { diff --git a/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js b/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js index 0f57998475..a563286413 100644 --- a/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js +++ b/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js @@ -144,6 +144,27 @@ function get_version_timeline_content(version_doc, frm) { function get_version_comment(version_doc, text) { + // TODO: Replace with a better solution + if (text.includes(" { + if ($(element).is('a')) { + version_comment += unlinked_content ? frappe.utils.get_form_link('Version', version_doc.name, true, unlinked_content) : ""; + unlinked_content = ""; + version_comment += element.outerHTML; + } else { + unlinked_content += element.outerHTML || element.textContent; + } + }); + if (unlinked_content) { + version_comment += frappe.utils.get_form_link('Version', version_doc.name, true, unlinked_content); + } + return version_comment; + } return frappe.utils.get_form_link('Version', version_doc.name, true, text); } @@ -164,4 +185,5 @@ function get_user_link(doc) { return frappe.utils.get_form_link('User', user, true, user_display_text); } -export { get_version_timeline_content }; \ No newline at end of file +export { get_version_timeline_content }; + diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 49b234d540..c40838e9f3 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -451,7 +451,7 @@ frappe.ui.form.Form = class FrappeForm { return this.script_manager.trigger("onload_post_render"); } }, - () => this.focus_on_first_input(), + () => this.is_new() && this.focus_on_first_input(), () => this.run_after_load_hook(), () => this.dashboard.after_refresh() ]); @@ -1075,7 +1075,7 @@ frappe.ui.form.Form = class FrappeForm { } refresh_field(fname) { - if(this.fields_dict[fname] && this.fields_dict[fname].refresh) { + if (this.fields_dict[fname] && this.fields_dict[fname].refresh) { this.fields_dict[fname].refresh(); this.layout.refresh_dependency(); } @@ -1241,20 +1241,22 @@ frappe.ui.form.Form = class FrappeForm { } } - set_df_property(fieldname, property, value, docname, table_field) { - var df; + set_df_property(fieldname, property, value, docname, table_field, table_row_name=null) { + let df; if (!docname || !table_field) { df = this.get_docfield(fieldname); } else { - var grid = this.fields_dict[fieldname].grid, - fname = frappe.utils.filter_dict(grid.docfields, {'fieldname': table_field}); - if (fname && fname.length) - df = frappe.meta.get_docfield(fname[0].parent, table_field, docname); + const grid = this.fields_dict[fieldname].grid; + const filtered_fields = frappe.utils.filter_dict(grid.docfields, {'fieldname': table_field}); + if (filtered_fields.length) { + df = frappe.meta.get_docfield(filtered_fields[0].parent, table_field, table_row_name); + } } if (df && df[property] != value) { df[property] = value; - if (!docname || !table_field) { - // do not refresh childtable fields since `this.fields_dict` doesn't have child table fields + if (table_field && table_row_name) { + this.fields_dict[fieldname].grid.grid_rows_by_docname[table_row_name].refresh_field(fieldname); + } else { this.refresh_field(fieldname); } } diff --git a/frappe/public/js/frappe/form/form_viewers.js b/frappe/public/js/frappe/form/form_viewers.js index d9d5ba6e68..3d488e4729 100644 --- a/frappe/public/js/frappe/form/form_viewers.js +++ b/frappe/public/js/frappe/form/form_viewers.js @@ -6,11 +6,10 @@ frappe.ui.form.FormViewers = class FormViewers { } refresh() { - // REDESIGN-TODO: fix this - // let users = this.frm.get_docinfo()['viewers']; - // let currently_viewing = users.current.filter(user => user != frappe.session.user); - // let avatar_group = frappe.avatar_group(currently_viewing, 5, {'align': 'left', 'overlap': true}); - this.parent.empty(); //.append(avatar_group); + let users = this.frm.get_docinfo()['viewers']; + let currently_viewing = users.current.filter(user => user != frappe.session.user); + let avatar_group = frappe.avatar_group(currently_viewing, 5, {'align': 'left', 'overlap': true}); + this.parent.empty().append(avatar_group); } }; diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index 4578cf2ded..f792d5b173 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -293,6 +293,12 @@ frappe.form.formatters = { return frappe.format(value, link_field, options, row); }); return formatted_values.join(', '); + }, + Color: (value) => { + return `
+
+ ${value} +
`; } } diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 9fdd4a8e36..bebf46e93d 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -4,9 +4,12 @@ export default class GridRow { constructor(opts) { this.on_grid_fields_dict = {}; this.on_grid_fields = []; + $.extend(this, opts); + if (this.doc) { + this.docfields = frappe.meta.get_docfields(this.parent_df.options, this.doc.name); + } this.columns = {}; this.columns_list = []; - $.extend(this, opts); this.row_check_html = ''; this.make(); } @@ -153,7 +156,7 @@ export default class GridRow { this.render_row(true); } - // refersh form fields + // refresh form fields if(this.grid_form) { this.grid_form.layout && this.grid_form.layout.refresh(this.doc); } @@ -249,27 +252,28 @@ export default class GridRow { this.focus_set = false; this.grid.setup_visible_columns(); - for(var ci in this.grid.visible_columns) { - var df = this.grid.visible_columns[ci][0], - colsize = this.grid.visible_columns[ci][1], - txt = this.doc ? - frappe.format(this.doc[df.fieldname], df, null, this.doc) : - __(df.label); + this.grid.visible_columns.forEach((col, ci) => { + // to get update df for the row + let df = this.docfields.find(field => field.fieldname === col[0].fieldname); + let colsize = col[1]; + let txt = this.doc ? + frappe.format(this.doc[df.fieldname], df, null, this.doc) : + __(df.label); - if(this.doc && df.fieldtype === "Select") { + if (this.doc && df.fieldtype === "Select") { txt = __(txt); } - - if(!this.columns[df.fieldname]) { - var column = this.make_column(df, colsize, txt, ci); + let column; + if (!this.columns[df.fieldname]) { + column = this.make_column(df, colsize, txt, ci); } else { - var column = this.columns[df.fieldname]; + column = this.columns[df.fieldname]; this.refresh_field(df.fieldname, txt); } - // background color for cellz - if(this.doc) { - if(df.reqd && !txt) { + // background color for cell + if (this.doc) { + if (df.reqd && !txt) { column.addClass('error'); } if (column.is_invalid) { @@ -278,7 +282,7 @@ export default class GridRow { column.addClass('bold'); } } - } + }); } make_column(df, colsize, txt, ci) { @@ -403,9 +407,9 @@ export default class GridRow { if (!field.df.onchange_modified) { var field_on_change_function = field.df.onchange; - field.df.onchange = function(e) { + field.df.onchange = (e) => { field_on_change_function && field_on_change_function(e); - me.grid.grid_rows[this.doc.idx - 1].refresh_field(this.df.fieldname); + this.refresh_field(field.df.fieldname); }; field.df.onchange_modified = true; @@ -589,42 +593,37 @@ export default class GridRow { } } refresh_field(fieldname, txt) { - var df = this.grid.get_docfield(fieldname) || undefined; + let df = this.docfields.find(col => { + return col.fieldname === fieldname; + }); // format values if no frm - if(!df) { - df = this.grid.visible_columns.find((col) => { - return col[0].fieldname === fieldname; - }); - if(df && this.doc) { - var txt = frappe.format(this.doc[fieldname], df[0], - null, this.doc); - } + if (df && this.doc) { + txt = frappe.format(this.doc[fieldname], df, null, this.doc); } - if(txt===undefined && this.frm) { - var txt = frappe.format(this.doc[fieldname], df, - null, this.frm.doc); + if (!txt && this.frm) { + txt = frappe.format(this.doc[fieldname], df, null, this.frm.doc); } // reset static value - var column = this.columns[fieldname]; - if(column) { + let column = this.columns[fieldname]; + if (column) { column.static_area.html(txt || ""); - if(df && df.reqd) { - column.toggleClass('error', !!(txt===null || txt==='')); + if (df && df.reqd) { + column.toggleClass('error', !!(txt === null || txt === '')); } } + let field = this.on_grid_fields_dict[fieldname]; // reset field value - var field = this.on_grid_fields_dict[fieldname]; - if(field) { + if (field) { field.docname = this.doc.name; field.refresh(); } // in form - if(this.grid_form) { + if (this.grid_form) { this.grid_form.refresh_field(fieldname); } } diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index d3480b1b75..8b6c627882 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -319,7 +319,7 @@ frappe.ui.form.Layout = Class.extend({ fieldobj.doctype = me.doc.doctype; fieldobj.docname = me.doc.name; fieldobj.df = frappe.meta.get_docfield(me.doc.doctype, - fieldobj.df.fieldname, me.frm ? me.frm.doc.name : me.doc.name) || fieldobj.df; + fieldobj.df.fieldname, me.doc.name) || fieldobj.df; // on form change, permissions can change if (me.frm) { @@ -512,7 +512,7 @@ frappe.ui.form.Layout = Class.extend({ if (form_obj) { if (this.doc && this.doc.parent) { form_obj.setting_dependency = true; - form_obj.set_df_property(this.doc.parentfield, property, value, this.doc.parent, fieldname); + form_obj.set_df_property(this.doc.parentfield, property, value, this.doc.parent, fieldname, this.doc.name); form_obj.setting_dependency = false; // refresh child fields this.fields_dict[fieldname] && this.fields_dict[fieldname].refresh(); diff --git a/frappe/public/js/frappe/form/multi_select_dialog.js b/frappe/public/js/frappe/form/multi_select_dialog.js index 26baee05ea..dd96b57fb5 100644 --- a/frappe/public/js/frappe/form/multi_select_dialog.js +++ b/frappe/public/js/frappe/form/multi_select_dialog.js @@ -251,7 +251,6 @@ frappe.ui.form.MultiSelectDialog = class MultiSelectDialog { head ? $row.addClass('list-item--head') : $row = $(`
`).append($row); - $(".modal-dialog .list-item--head").css("z-index", 0); return $row; } @@ -264,6 +263,7 @@ frappe.ui.form.MultiSelectDialog = class MultiSelectDialog { this.empty_list(); } more_btn.hide(); + $(".modal-dialog .list-item--head").css("z-index", 0); if (results.length === 0) return; if (more) more_btn.show(); diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index 8ac0a0109b..65d84e2202 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -112,7 +112,6 @@ frappe.ui.form.save = function (frm, action, callback, btn) { }; var check_mandatory = function () { - var me = this; var has_errors = false; frm.scroll_set = false; @@ -124,8 +123,8 @@ frappe.ui.form.save = function (frm, action, callback, btn) { $.each(frappe.meta.docfield_list[doc.doctype] || [], function (i, docfield) { if (docfield.fieldname) { - var df = frappe.meta.get_docfield(doc.doctype, - docfield.fieldname, frm.doc.name); + const df = frappe.meta.get_docfield(doc.doctype, + docfield.fieldname, doc.name); if (df.fieldtype === "Fold") { folded = frm.layout.folded; diff --git a/frappe/public/js/frappe/form/script_manager.js b/frappe/public/js/frappe/form/script_manager.js index 770319ba53..6af178f11e 100644 --- a/frappe/public/js/frappe/form/script_manager.js +++ b/frappe/public/js/frappe/form/script_manager.js @@ -184,7 +184,7 @@ frappe.ui.form.ScriptManager = Class.extend({ } function setup_add_fetch(df) { - if((['Data', 'Read Only', 'Text', 'Small Text', 'Currency', + if ((['Data', 'Read Only', 'Text', 'Small Text', 'Currency', 'Check', 'Text Editor', 'Code', 'Link', 'Float', 'Int', 'Date', 'Select'].includes(df.fieldtype) || df.read_only==1) && df.fetch_from && df.fetch_from.indexOf(".")!=-1) { var parts = df.fetch_from.split("."); diff --git a/frappe/public/js/frappe/form/toolbar.js b/frappe/public/js/frappe/form/toolbar.js index 7e2502e58a..2f5b84fb1a 100644 --- a/frappe/public/js/frappe/form/toolbar.js +++ b/frappe/public/js/frappe/form/toolbar.js @@ -210,7 +210,10 @@ frappe.ui.form.Toolbar = class Toolbar { } make_viewers() { - if (this.frm.viewers) return; + if (this.frm.viewers) { + this.frm.viewers.parent.empty(); + return; + } this.frm.viewers = new frappe.ui.form.FormViewers({ frm: this.frm, parent: $('
').prependTo(this.frm.page.page_actions) diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js index 24e14ffc38..beacb136e6 100644 --- a/frappe/public/js/frappe/list/base_list.js +++ b/frappe/public/js/frappe/list/base_list.js @@ -179,7 +179,8 @@ frappe.views.BaseList = class BaseList { 'Calendar': 'calendar', 'Gantt': 'gantt', 'Kanban': 'kanban', - 'Dashboard': 'dashboard' + 'Dashboard': 'dashboard', + 'Map': 'map', }; if (frappe.boot.desk_settings.view_switcher) { @@ -285,6 +286,7 @@ frappe.views.BaseList = class BaseList { } setup_filter_area() { + if (this.hide_filters) return; this.filter_area = new FilterArea(this); if (this.filters && this.filters.length > 0) { @@ -293,6 +295,7 @@ frappe.views.BaseList = class BaseList { } setup_sort_selector() { + if (this.hide_sort_selector) return; this.sort_selector = new frappe.ui.SortSelector({ parent: this.$filter_section, doctype: this.doctype, @@ -410,7 +413,7 @@ frappe.views.BaseList = class BaseList { doctype: this.doctype, fields: this.get_fields(), filters: this.get_filters_for_args(), - order_by: this.sort_selector.get_sql_string(), + order_by: this.sort_selector && this.sort_selector.get_sql_string(), start: this.start, page_length: this.page_length, view: this.view, @@ -821,6 +824,7 @@ frappe.views.view_modes = [ "Image", "Inbox", "Tree", + "Map", ]; frappe.views.is_valid = (view_mode) => frappe.views.view_modes.includes(view_mode); diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 48ae8b1d08..c55ec4b3ab 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -417,11 +417,11 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { get_no_result_message() { let help_link = this.get_documentation_link(); - let filters = this.filter_area.get(); - let no_result_message = filters.length + let filters = this.filter_area && this.filter_area.get(); + let no_result_message = filters && filters.length ? __("No {0} found", [__(this.doctype)]) : __("You haven't created a {0} yet", [__(this.doctype)]); - let new_button_label = filters.length + let new_button_label = filters && filters.length ? __("Create a new {0}", [__(this.doctype)]) : __("Create your first {0}", [__(this.doctype)]); let empty_state_image = @@ -461,7 +461,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { } before_refresh() { - if (frappe.route_options) { + if (frappe.route_options && this.filter_area) { this.filters = this.parse_filters_from_route_options(); frappe.route_options = null; @@ -527,9 +527,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { this.view_name ); this.save_view_user_settings({ - filters: this.filter_area.get(), - sort_by: this.sort_selector.sort_by, - sort_order: this.sort_selector.sort_order, + filters: this.filter_area && this.filter_area.get(), + sort_by: this.sort_selector && this.sort_selector.sort_by, + sort_order: this.sort_selector && this.sort_selector.sort_order, }); this.toggle_paging && this.$paging_area.toggle(false); } diff --git a/frappe/public/js/frappe/list/list_view_select.js b/frappe/public/js/frappe/list/list_view_select.js index 826158ff3f..9607be6e90 100644 --- a/frappe/public/js/frappe/list/list_view_select.js +++ b/frappe/public/js/frappe/list/list_view_select.js @@ -123,7 +123,14 @@ frappe.views.ListViewSelect = class ListViewSelect { kanbans => this.setup_kanban_switcher(kanbans) ); } - } + }, + Map: { + condition: this.list_view.settings.get_coords_method || + (this.list_view.meta.fields.find(i => i.fieldname === "latitude") && + this.list_view.meta.fields.find(i => i.fieldname === "longitude")) || + (this.list_view.meta.fields.find(i => i.fieldname === 'location' && i.fieldtype == 'Geolocation')), + action: () => this.set_route("map") + }, }; frappe.views.view_modes.forEach(view => { diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 88912e12da..12fa9c8e21 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -55,7 +55,7 @@ frappe.call = function(opts) { args.cmd = opts.module+'.page.'+opts.page+'.'+opts.page+'.'+opts.method; } else if(opts.doc) { $.extend(args, { - cmd: "runserverobj", + cmd: "run_doc_method", docs: frappe.get_doc(opts.doc.doctype, opts.doc.name), method: opts.method, args: opts.args, diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index e4c0f0c55c..5378294855 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -364,7 +364,7 @@ frappe.router = { // return clean sub_path from hash or url // supports both v1 and v2 routing if (!route) { - route = window.location.hash || window.location.pathname; + route = window.location.hash || (window.location.pathname + window.location.search); } return this.strip_prefix(route); diff --git a/frappe/public/js/frappe/socketio_client.js b/frappe/public/js/frappe/socketio_client.js index c9c98bd937..606ed42444 100644 --- a/frappe/public/js/frappe/socketio_client.js +++ b/frappe/public/js/frappe/socketio_client.js @@ -159,9 +159,12 @@ frappe.socketio = { }, doc_open: function(doctype, docname) { // notify that the user has opened this doc, if not already notified - if(!frappe.socketio.last_doc - || (frappe.socketio.last_doc[0]!=doctype && frappe.socketio.last_doc[1]!=docname)) { + if (!frappe.socketio.last_doc + || (frappe.socketio.last_doc[0] != doctype || frappe.socketio.last_doc[1] != docname)) { frappe.socketio.socket.emit('doc_open', doctype, docname); + + frappe.socketio.last_doc && + frappe.socketio.doc_close(frappe.socketio.last_doc[0], frappe.socketio.last_doc[1]); } frappe.socketio.last_doc = [doctype, docname]; }, diff --git a/frappe/public/js/frappe/ui/filters/field_select.js b/frappe/public/js/frappe/ui/filters/field_select.js index ed271a73aa..c362214ce2 100644 --- a/frappe/public/js/frappe/ui/filters/field_select.js +++ b/frappe/public/js/frappe/ui/filters/field_select.js @@ -36,6 +36,18 @@ frappe.ui.FieldSelect = Class.extend({ var item = me.awesomplete.get_item(value); me.$input.val(item.label); }); + this.$input.on("awesomplete-open", () => { + let modal = this.$input.parents('.modal-dialog')[0]; + if (modal) { + $(modal).removeClass("modal-dialog-scrollable"); + } + }); + this.$input.on("awesomplete-close", () => { + let modal = this.$input.parents('.modal-dialog')[0]; + if (modal) { + $(modal).addClass("modal-dialog-scrollable"); + } + }); if(this.filter_fields) { for(var i in this.filter_fields) diff --git a/frappe/public/js/frappe/ui/filters/filter.js b/frappe/public/js/frappe/ui/filters/filter.js index 945115af82..b2b2b623e2 100644 --- a/frappe/public/js/frappe/ui/filters/filter.js +++ b/frappe/public/js/frappe/ui/filters/filter.js @@ -495,6 +495,7 @@ frappe.ui.filter_utils = { 'Dynamic Link', 'Read Only', 'Assign', + 'Color', ].indexOf(df.fieldtype) != -1 ) { df.fieldtype = 'Data'; diff --git a/frappe/public/js/frappe/ui/filters/filter_list.js b/frappe/public/js/frappe/ui/filters/filter_list.js index 0b2218ceda..611ab024bf 100644 --- a/frappe/public/js/frappe/ui/filters/filter_list.js +++ b/frappe/public/js/frappe/ui/filters/filter_list.js @@ -283,6 +283,7 @@ frappe.ui.FilterGroup = class { } get_filter_area_template() { + /* eslint-disable indent */ return $(`
@@ -293,19 +294,23 @@ frappe.ui.FilterGroup = class {
- + ${this.filter_button ? + `` + : '' + }
` ); + /* eslint-disable indent */ } get_filters_as_object() { diff --git a/frappe/public/js/frappe/ui/group_by/group_by.js b/frappe/public/js/frappe/ui/group_by/group_by.js index fa699b1a91..53e4914f0d 100644 --- a/frappe/public/js/frappe/ui/group_by/group_by.js +++ b/frappe/public/js/frappe/ui/group_by/group_by.js @@ -286,15 +286,6 @@ frappe.ui.GroupBy = class { set_args(args) { if (this.aggregate_function && this.group_by) { - let aggregate_column, aggregate_on_field; - - if (this.aggregate_function === 'count') { - aggregate_column = 'count(`tab' + this.doctype + '`.`name`)'; - } else { - aggregate_column = `${this.aggregate_function}(${this.aggregate_on})`; - aggregate_on_field = this.aggregate_on; - } - this.report_view.group_by = this.group_by; this.report_view.sort_by = '_aggregate_column'; this.report_view.sort_order = 'desc'; @@ -316,17 +307,14 @@ frappe.ui.GroupBy = class { '_aggregate_column', this.aggregate_on_doctype || this.doctype, ]); - args.fields.push(aggregate_column + ' as _aggregate_column'); - - if (aggregate_on_field) { - args.fields.push(aggregate_on_field); - } // setup columns in datatable this.report_view.setup_columns(); Object.assign(args, { with_comment_count: false, + aggregate_on: this.aggregate_on || 'name', + aggregate_function: this.aggregate_function || 'count', group_by: this.report_view.group_by || null, order_by: '_aggregate_column desc', }); diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js index b3cbef1b8e..2e8ba7d206 100644 --- a/frappe/public/js/frappe/ui/messages.js +++ b/frappe/public/js/frappe/ui/messages.js @@ -316,12 +316,17 @@ frappe.verify_password = function(callback) { }, __("Verify Password"), __("Verify")) } -frappe.show_progress = function(title, count, total=100, description) { - if(frappe.cur_progress && frappe.cur_progress.title === title && frappe.cur_progress.is_visible) { - var dialog = frappe.cur_progress; +frappe.show_progress = (title, count, total = 100, description, hide_on_completion = false) => { + let dialog; + if ( + frappe.cur_progress && + frappe.cur_progress.title === title && + frappe.cur_progress.is_visible + ) { + dialog = frappe.cur_progress; } else { - var dialog = new frappe.ui.Dialog({ - title: title, + dialog = new frappe.ui.Dialog({ + title: title }); dialog.progress = $(`
@@ -329,19 +334,24 @@ frappe.show_progress = function(title, count, total=100, description) {

frappe.format(value, { fieldtype: this.report_result.chart.fieldtype, options: this.report_result.chart.options }, { always_show_decimals: true, inline: true }) - } - }; + }; + } if (this.chart_doc.type == "Heatmap") { const heatmap_year = parseInt(this.selected_heatmap_year || this.chart_settings.heatmap_year || this.chart_doc.heatmap_year); diff --git a/frappe/public/scss/desk/awesomebar.scss b/frappe/public/scss/common/awesomeplete.scss similarity index 100% rename from frappe/public/scss/desk/awesomebar.scss rename to frappe/public/scss/common/awesomeplete.scss diff --git a/frappe/public/scss/common/color_picker.scss b/frappe/public/scss/common/color_picker.scss index 627ab12a2e..7ab9b0c504 100644 --- a/frappe/public/scss/common/color_picker.scss +++ b/frappe/public/scss/common/color_picker.scss @@ -92,7 +92,7 @@ } } -.frappe-control[data-fieldtype='Color'] { +.frappe-control[data-fieldtype='Color'] { input { padding-left: 40px; } @@ -104,11 +104,20 @@ background-color: red; position: absolute; top: calc(50% + 1px); - left: 5px; + left: 8px; content: ' '; &.no-value { background: url('/assets/frappe/images/color-circle.png'); background-size: contain; } } -} \ No newline at end of file + .like-disabled-input { + .color-value { + padding-left: 25px; + } + .selected-color { + top: 20%; + cursor: default; + } + } +} diff --git a/frappe/public/scss/desk/dark.scss b/frappe/public/scss/desk/dark.scss index 7bbf582af0..743107af47 100644 --- a/frappe/public/scss/desk/dark.scss +++ b/frappe/public/scss/desk/dark.scss @@ -11,8 +11,8 @@ --gray-900: #161a1f; // Type Colors - --text-muted: var(--gray-300); - --text-light: var(--gray-400); + --text-muted: var(--gray-400); + --text-light: var(--gray-300); --text-color: var(--gray-50); --heading-color: var(--gray-50); @@ -114,19 +114,21 @@ // --criticism-bg: var(--red-600); // Frappe Charts Colors - --charts-label-color: var(--gray-300); - --charts-axis-line-color: var(--gray-500); + .chart-container { + --charts-label-color: var(--gray-300); + --charts-axis-line-color: var(--gray-500); - --charts-stroke-width: 5px; - --charts-dataset-circle-stroke: #ffffff; - --charts-dataset-circle-stroke-width: var(--charts-stroke-width); + --charts-stroke-width: 5px; + --charts-dataset-circle-stroke: #ffffff; + --charts-dataset-circle-stroke-width: var(--charts-stroke-width); - --charts-tooltip-title: var(--charts-label-color); - --charts-tooltip-label: var(--charts-label-color); - --charts-tooltip-value: white; - --charts-tooltip-bg: var(--gray-900); + --charts-tooltip-title: var(--charts-label-color); + --charts-tooltip-label: var(--charts-label-color); + --charts-tooltip-value: white; + --charts-tooltip-bg: var(--gray-900); - --charts-legend-label: var(--charts-label-color); + --charts-legend-label: var(--charts-label-color); + } // find better fix .heatmap-chart { diff --git a/frappe/public/scss/desk/frappe_datatable.scss b/frappe/public/scss/desk/frappe_datatable.scss index 7e4e5b0ef8..48645bbbfc 100644 --- a/frappe/public/scss/desk/frappe_datatable.scss +++ b/frappe/public/scss/desk/frappe_datatable.scss @@ -123,6 +123,10 @@ font-feature-settings: "tnum"; } + .dt-cell__content--header-0, .dt-cell__content--col-0 { + padding: 0.5rem; + } + .dt-scrollable--highlight-all { .dt-cell__content { background: var(--dt-selection-highlight-color); diff --git a/frappe/public/scss/desk/index.scss b/frappe/public/scss/desk/index.scss index f7449640fd..31eae63776 100644 --- a/frappe/public/scss/desk/index.scss +++ b/frappe/public/scss/desk/index.scss @@ -23,7 +23,7 @@ @import "notification"; @import "global_search"; @import "desktop"; -@import "awesomebar"; +@import "../common/awesomeplete"; @import "sidebar"; @import "filters"; @import "list"; diff --git a/frappe/public/scss/desk/list.scss b/frappe/public/scss/desk/list.scss index b7a195718e..0230138e8f 100644 --- a/frappe/public/scss/desk/list.scss +++ b/frappe/public/scss/desk/list.scss @@ -261,7 +261,6 @@ input.list-check-all, input.list-row-checkbox { input[type=checkbox] { margin: 0; margin-right: 5px; - flex: 0 0 12px; } .liked-by, .liked-by-filter-button { @@ -332,10 +331,6 @@ input.list-check-all, input.list-row-checkbox { } .page-form { - // .awesomplete > ul { - // min-width: 300px; - // } - .standard-filter-section { flex-wrap: wrap; // width: 65%; diff --git a/frappe/public/scss/desk/page.scss b/frappe/public/scss/desk/page.scss index 85831dc2a0..65d535facc 100644 --- a/frappe/public/scss/desk/page.scss +++ b/frappe/public/scss/desk/page.scss @@ -117,6 +117,10 @@ display: none; } } + + .awesomplete > ul { + min-width: 300px; + } } .form-inner-toolbar { diff --git a/frappe/public/scss/website/doc.scss b/frappe/public/scss/website/doc.scss index 1585f428f9..8cc12e7e55 100644 --- a/frappe/public/scss/website/doc.scss +++ b/frappe/public/scss/website/doc.scss @@ -169,25 +169,6 @@ $navbar-height-lg: 4.5rem; margin-top: 3rem; } - h1 { - font-size: $font-size-3xl; - font-weight: 500; - } - - h1 + p { - font-size: $font-size-lg; - } - - h2 { - font-size: $font-size-2xl; - font-weight: 500; - } - - h3 { - font-size: $font-size-xl; - font-weight: 500; - } - h1, h2, h3, @@ -202,36 +183,6 @@ $navbar-height-lg: 4.5rem; visibility: hidden; } } - - h4 { - font-size: $font-size-lg; - font-weight: 500; - } - - strong { - font-weight: 600; - } - - table { - border-color: $gray-200; - } - - table thead { - background-color: $light; - } - - .table-bordered, - .table-bordered th, - .table-bordered td { - border-left: none; - border-right: none; - border-color: $gray-200; - } - - .table-bordered thead th, - .table-bordered thead td { - border-bottom-width: 1px; - } } // next links diff --git a/frappe/public/scss/website/index.scss b/frappe/public/scss/website/index.scss index de81174d3b..1fb5badc6c 100644 --- a/frappe/public/scss/website/index.scss +++ b/frappe/public/scss/website/index.scss @@ -10,6 +10,7 @@ @import "../common/modal"; @import "../common/indicator"; @import "../common/controls"; +@import "../common/awesomeplete"; @import 'multilevel_dropdown'; @import 'website_image'; @import 'website_avatar'; diff --git a/frappe/public/scss/website/markdown.scss b/frappe/public/scss/website/markdown.scss index 4b0c20cbc4..c5f44d20d8 100644 --- a/frappe/public/scss/website/markdown.scss +++ b/frappe/public/scss/website/markdown.scss @@ -1,9 +1,29 @@ +$font-sizes-desktop: ( + "sm": 0.75rem, + "base": 1rem, + "lg": 1.125rem, + "xl": 1.41rem, + "2xl": 1.6rem, + "3xl": 2rem +); + +$font-sizes-mobile: ( + "sm": 0.75rem, + "base": 1rem, + "lg": 1.125rem, + "xl": 1.25rem, + "2xl": 1.5rem, + "3xl": 1.75rem +); + .from-markdown { color: $gray-700; - line-height: 1.625; + line-height: 1.7; + letter-spacing: -0.011em; > * + * { - margin-top: 1rem; + margin-top: 0.75rem; + margin-bottom: 0; } > :first-child { @@ -16,7 +36,7 @@ ul, ol { - padding-left: 2.5rem; + padding-left: 2rem; } ul { @@ -27,17 +47,27 @@ list-style: decimal; } - li > * + * { - margin-top: 1rem; + li { + text-indent: 0.25rem; + padding-top: 1px; + padding-bottom: 1px; } - > ul > * + *, - > ol > * + * { - margin-top: 1rem; + li > ul, li > ol { + padding-left: 1.5rem; + } + + ul > li:first-child { + margin-top: 3px; + } + + ul > * + *, + ol > * + * { + margin-top: 2px; } > blockquote { - padding: 1.25rem 1rem; + padding: 0.75rem 1rem 0.75rem 1.25rem; font-size: $font-size-sm; font-weight: 500; border: 1px solid $gray-200; @@ -55,60 +85,87 @@ b, strong { color: $gray-800; + font-weight: 600; } h1, h2, h3, h4, h5, h6 { color: $gray-900; } - h1 + p { - margin-top: 0.75rem; - font-size: $font-size-base; + h2, h3, h4, h5, h6 { + font-weight: 600; + } + + h1 { + font-size: map-get($font-sizes-mobile, '3xl'); + line-height: 1.5; + letter-spacing: -0.021em; + font-weight: 700; - @include media-breakpoint-up(sm) { - margin-top: 1.25rem; - font-size: 1.125rem; - } @include media-breakpoint-up(md) { - font-size: 1.25rem; + font-size: map-get($font-sizes-desktop, '3xl'); + letter-spacing: -0.024em; + } + + // for byline + & + p { + margin-top: 1.5rem; + font-size: map-get($font-sizes-mobile, 'xl'); + letter-spacing: -0.014em; + line-height: 1.4; + + @include media-breakpoint-up(md) { + font-size: map-get($font-sizes-desktop, 'xl'); + letter-spacing: -0.0175em; + } } } h2 { - margin-bottom: 1rem; - margin-top: 3.5rem; + font-size: map-get($font-sizes-mobile, '2xl'); + line-height: 1.56; + letter-spacing: -0.015em; + margin-top: 4rem; + + @include media-breakpoint-up(md) { + font-size: map-get($font-sizes-desktop, '2xl'); + letter-spacing: -0.0195em; + } } h3 { - margin-top: 3rem; - margin-bottom: 1rem; - font-weight: 600; - line-height: 1.25; - font-size: $font-size-xl; + font-size: map-get($font-sizes-mobile, 'xl'); + line-height: 1.56; + letter-spacing: -0.014em; + margin-top: 2.25rem; + + @include media-breakpoint-up(md) { + font-size: map-get($font-sizes-desktop, 'xl'); + letter-spacing: -0.0175em; + } } h4 { + font-size: map-get($font-sizes-mobile, 'lg'); + line-height: 1.56; + letter-spacing: -0.014em; margin-top: 2.5rem; - margin-bottom: 1rem; - font-size: 1.125rem; - font-weight: 600; - line-height: 1.25; } h5 { - margin-top: 2rem; - margin-bottom: 1rem; - font-size: $font-size-base; + font-size: map-get($font-sizes-mobile, 'base'); + line-height: 1.5; + letter-spacing: -0.011em; font-weight: 600; - line-height: 1.25; + margin-top: 2rem; } h6 { - margin-top: 1.5rem; - margin-bottom: 1rem; - font-size: $font-size-sm; + font-size: map-get($font-sizes-mobile, 'sm'); + line-height: 1.35; font-weight: 600; - line-height: 1.25; + text-transform: uppercase; + margin-top: 1.5rem; } tr > td, @@ -124,6 +181,7 @@ .screenshot { border: 1px solid $gray-400; border-radius: 0.375rem; + margin-top: 0.5rem; } .screenshot + em { @@ -138,4 +196,25 @@ background: $light; border-radius: 0.125rem; } + + table { + border-color: $gray-200; + } + + table thead { + background-color: $light; + } + + .table-bordered, + .table-bordered th, + .table-bordered td { + border-left: none; + border-right: none; + border-color: $gray-200; + } + + .table-bordered thead th, + .table-bordered thead td { + border-bottom-width: 1px; + } } diff --git a/frappe/sessions.py b/frappe/sessions.py index 3babf1db12..5f13dfb7af 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -296,8 +296,7 @@ class Session: expiry = get_expiry_in_seconds(session_data.get("session_expiry")) if self.time_diff > expiry: - print('deleting...') - self.delete_session() + self._delete_session() data = None return data and data.data @@ -316,12 +315,12 @@ class Session: data = frappe._dict(eval(rec and rec[0][1] or '{}')) data.user = rec[0][0] else: - self.delete_session() + self._delete_session() data = None return data - def delete_session(self): + def _delete_session(self): delete_session(self.sid, reason="Session Expired") def start_as_guest(self): diff --git a/frappe/social/doctype/energy_point_log/energy_point_log.py b/frappe/social/doctype/energy_point_log/energy_point_log.py index e9425cec86..2e2289aed4 100644 --- a/frappe/social/doctype/energy_point_log/energy_point_log.py +++ b/frappe/social/doctype/energy_point_log/energy_point_log.py @@ -52,6 +52,7 @@ class EnergyPointLog(Document): reference_log.reverted = 0 reference_log.save() + @frappe.whitelist() def revert(self, reason, ignore_permissions=False): if not ignore_permissions: frappe.only_for('System Manager') diff --git a/frappe/templates/doc.html b/frappe/templates/doc.html index 3a566a1227..f3a8ab8cc8 100644 --- a/frappe/templates/doc.html +++ b/frappe/templates/doc.html @@ -23,21 +23,6 @@
`); target.empty(); diff --git a/frappe/website/web_form/request_data/request_data.json b/frappe/website/web_form/request_data/request_data.json index c3b0155d51..591ef4a031 100644 --- a/frappe/website/web_form/request_data/request_data.json +++ b/frappe/website/web_form/request_data/request_data.json @@ -8,18 +8,20 @@ "allow_print": 0, "amount": 0.0, "amount_based_on_field": 0, + "apply_document_permissions": 0, "breadcrumbs": "", - "button_label": "Submit", + "button_label": "Request Data", "creation": "2019-01-24 16:19:26.886096", "currency": "INR", "doc_type": "Personal Data Download Request", "docstatus": 0, "doctype": "Web Form", "idx": 0, + "introduction_text": "

Request a file containing your personally identifiable information (PII) that is saved on our system. The file will be in JSON format and is sent to you by email. If you would like to have your PII deleted from our system, please make a request to delete data.

", "is_standard": 1, "login_required": 0, "max_attachment_size": 0, - "modified": "2019-07-16 12:41:53.782126", + "modified": "2021-03-25 10:52:13.149538", "modified_by": "Administrator", "module": "Website", "name": "request-data", @@ -45,7 +47,7 @@ "max_length": 0, "max_value": 0, "options": "", - "read_only": 1, + "read_only": 0, "reqd": 1, "show_in_filter": 0 } diff --git a/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json b/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json index 0aa1446ce3..b0180d833c 100644 --- a/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json +++ b/frappe/website/web_form/request_to_delete_data/request_to_delete_data.json @@ -8,6 +8,7 @@ "allow_print": 0, "amount": 0.0, "amount_based_on_field": 0, + "apply_document_permissions": 0, "button_label": "Submit", "creation": "2019-01-25 14:24:12.588810", "currency": "INR", @@ -15,10 +16,11 @@ "docstatus": 0, "doctype": "Web Form", "idx": 0, + "introduction_text": "

Send a request to delete your personally identifiable information (PII) that is stored on our system. You will receive an email to verify your request. Once the request is verified we will take care of deleting your PII. If you just want to check what PII we have stored, you can request your data.

", "is_standard": 1, "login_required": 0, "max_attachment_size": 0, - "modified": "2019-02-21 17:14:55.095337", + "modified": "2021-03-25 11:08:49.580621", "modified_by": "Administrator", "module": "Website", "name": "request-to-delete-data", diff --git a/package.json b/package.json index 8e2989d1d7..19b9fdf227 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "qz-tray": "^2.0.8", "redis": "^2.8.0", "showdown": "^1.9.1", - "snyk": "^1.465.0", + "snyk": "^1.518.0", "socket.io": "^2.4.0", "superagent": "^3.8.2", "touch": "^3.1.0", diff --git a/socketio.js b/socketio.js index 796b8aa94d..d5d6523752 100644 --- a/socketio.js +++ b/socketio.js @@ -2,24 +2,12 @@ var app = require('express')(); var server = require('http').Server(app); var io = require('socket.io')(server); var cookie = require('cookie'); -var fs = require('fs'); -var path = require('path'); var request = require('superagent'); var { get_conf, get_redis_subscriber } = require('./node_utils'); const log = console.log; // eslint-disable-line var conf = get_conf(); -var files_struct = { - name: null, - type: null, - size: 0, - data: [], - slice: 0, - site_name: null, - is_private: 0 -}; - var subscriber = get_redis_subscriber(); // serve socketio @@ -43,7 +31,6 @@ io.on('connection', function (socket) { } socket.user = cookie.parse(socket.request.headers.cookie).user_id; - socket.files = {}; // frappe.chat socket.on("frappe.chat.room:subscribe", function (rooms) { @@ -97,10 +84,6 @@ io.on('connection', function (socket) { join_chat_room(); - socket.on('disconnect', function () { - delete socket.files; - }); - socket.on('task_subscribe', function (task_id) { var room = get_task_room(socket, task_id); socket.join(room); diff --git a/yarn.lock b/yarn.lock index 3999616099..4f6f62ac0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -116,10 +116,10 @@ js-yaml "^3.13.1" tslib "^1.10.0" -"@snyk/code-client@3.1.5": - version "3.1.5" - resolved "https://registry.yarnpkg.com/@snyk/code-client/-/code-client-3.1.5.tgz#019ef3b4d2f53f02f890d2df933fc7c2cf5cf4a5" - integrity sha512-bJb00zZ7956MzIjW/4DPaMolk2/r7eox+5Bvq0bpcu1NFUFYYQPZeEPsPgh5YzK4te2v6W5hZBtjUUNIY+AQYg== +"@snyk/code-client@3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@snyk/code-client/-/code-client-3.4.0.tgz#7741abef6dcf5dfc50a1a0538918972360c5a1e7" + integrity sha512-RY2IftAiWB7tp36Mcq7WiEwqoD8A/mqrD6N7oDWTxBOIqsH0t4djo/UibiWDJotaffO9aXXndOf3iZ/kTt+Rdg== dependencies: "@deepcode/dcignore" "^1.0.2" "@snyk/fast-glob" "^3.2.6-patch" @@ -161,7 +161,7 @@ source-map-support "^0.5.19" tslib "^1.13.0" -"@snyk/dep-graph@^1.21.0", "@snyk/dep-graph@^1.23.0", "@snyk/dep-graph@^1.23.1", "@snyk/dep-graph@^1.27.1": +"@snyk/dep-graph@^1.21.0", "@snyk/dep-graph@^1.23.0", "@snyk/dep-graph@^1.23.1", "@snyk/dep-graph@^1.27.1", "@snyk/dep-graph@^1.28.0": version "1.28.0" resolved "https://registry.yarnpkg.com/@snyk/dep-graph/-/dep-graph-1.28.0.tgz#d68c0576cb3562c6e819ca8a8c7ac29ee11d9776" integrity sha512-Oup9nAvb558jdNvbZah/vaBtOtCcizkdeS+OBQeBIqIffyer4mc4juSn4b1SFjCpu7AG7piio8Lj8k1B9ps6Tg== @@ -207,6 +207,19 @@ micromatch "^4.0.2" picomatch "^2.2.1" +"@snyk/fix@1.518.0": + version "1.518.0" + resolved "https://registry.yarnpkg.com/@snyk/fix/-/fix-1.518.0.tgz#8af97a17da737739b1f31fe2d2129c99fcb574ed" + integrity sha512-Cwh0wU8SxZgx1+qRgcGkMctNx9F6UCdUJYcCvKaYJNDEYQwpQat4nsLZsJeODYNx7Byh0ZnPrqakUck4qFrPvA== + dependencies: + "@snyk/dep-graph" "^1.21.0" + chalk "4.1.0" + debug "^4.3.1" + micromatch "4.0.2" + ora "5.3.0" + p-map "^4.0.0" + strip-ansi "6.0.0" + "@snyk/gemfile@1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@snyk/gemfile/-/gemfile-1.2.0.tgz#919857944973cce74c650e5428aaf11bcd5c0457" @@ -315,6 +328,14 @@ tslib "^1.9.3" xml-js "^1.6.11" +"@snyk/mix-parser@^1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@snyk/mix-parser/-/mix-parser-1.1.1.tgz#3493222b4e3d84a6fb56bce5238922a532a81191" + integrity sha512-KmX4Le+1M01m6kM2UeDColzMZctrSqoMGajqcRHR3dLpCyHE3nzZzPeOWjbUVgjQlTX07oQvq9udSJGZJ/+Gdg== + dependencies: + "@snyk/dep-graph" "^1.28.0" + tslib "^2.0.0" + "@snyk/rpm-parser@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@snyk/rpm-parser/-/rpm-parser-2.0.0.tgz#4ded7fa4b0a8efca7699359e4ca7a79bfbe38bc1" @@ -343,6 +364,16 @@ tar-stream "^2.1.2" tmp "^0.1.0" +"@snyk/snyk-hex-plugin@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@snyk/snyk-hex-plugin/-/snyk-hex-plugin-1.0.0.tgz#617c91b5c19a6ccbb3a9a1a87d9e87b84621bdb3" + integrity sha512-ZydVdZ5kDpPDoehQnNHN3wZ6c470k5DPLJtWMoyfzlnCU2+y1rsUEdn4yhttn60RPx3JiLGwmckeDvZw8BqnGQ== + dependencies: + "@snyk/dep-graph" "^1.28.0" + "@snyk/mix-parser" "^1.1.0" + debug "^4.3.1" + tslib "^2.0.0" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -558,19 +589,13 @@ after@0.8.2: resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= -agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" - integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: - es6-promisify "^5.0.0" - -agent-base@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" - integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== - dependencies: - es6-promisify "^5.0.0" + clean-stack "^2.0.0" + indent-string "^4.0.0" "air-datepicker@github:frappe/air-datepicker": version "2.2.3" @@ -749,11 +774,6 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -ast-types@0.x.x: - version "0.13.2" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" - integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== - async-array-reduce@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/async-array-reduce/-/async-array-reduce-0.2.1.tgz#c8be010a2b5cd00dea96c81116034693dfdd82d1" @@ -902,6 +922,15 @@ bl@^4.0.1: inherits "^2.0.4" readable-stream "^3.4.0" +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + blob@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" @@ -940,6 +969,11 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +boolean@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.3.tgz#0fee0c9813b66bef25a8a6a904bb46736d05f024" + integrity sha512-EqrTKXQX6Z3A2nRmMEIlAIfjQOgFnVO2nqZGpbcsPnYGWBwpFqzlrozU1dy+S2iqfYDLh26ef4KrgTxu9xQrxA== + bootstrap@4: version "4.5.0" resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.0.tgz#97d9dbcb5a8972f8722c9962483543b907d9b9ec" @@ -1164,6 +1198,14 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +chalk@4.1.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -1192,14 +1234,6 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -1237,6 +1271,11 @@ clean-css@^4.1.11: dependencies: source-map "~0.6.0" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + cli-boxes@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d" @@ -1254,6 +1293,11 @@ cli-spinner@0.2.10: resolved "https://registry.yarnpkg.com/cli-spinner/-/cli-spinner-0.2.10.tgz#f7d617a36f5c47a7bc6353c697fc9338ff782a47" integrity sha512-U0sSQ+JJvSLi1pAYuJykwiA8Dsr15uHEy85iCJ6A+0DjVxivr3d+N2Wjvodeg89uP5K6TswFkKBfAD7B3YSn/Q== +cli-spinners@^2.5.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" + integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== + cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" @@ -1294,6 +1338,11 @@ clone-stats@^1.0.0: resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + clone@^2.1.1, clone@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" @@ -1308,11 +1357,6 @@ cloneable-readable@^1.0.0: process-nextick-args "^2.0.0" readable-stream "^2.3.5" -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - coa@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" @@ -1815,11 +1859,6 @@ data-uri-to-buffer@0.0.3: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz#18ae979a6a0ca994b0625853916d2662bbae0b1a" integrity sha1-GK6XmmoMqZSwYlhTkW0mYruuCxo= -data-uri-to-buffer@1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" - integrity sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ== - dateformat@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -1830,7 +1869,7 @@ de-indent@^1.0.2: resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0= -debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -1844,13 +1883,6 @@ debug@3.1.0, debug@~3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.1.1, debug@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - debug@^3.0.1: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -1865,7 +1897,14 @@ debug@^3.1.0, debug@^3.2.6: dependencies: ms "^2.1.1" -debug@^4.2.0: +debug@^4.1.1, debug@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +debug@^4.2.0, debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -1926,10 +1965,12 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" defer-to-connect@^1.0.1: version "1.1.3" @@ -1970,15 +2011,6 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" -degenerator@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" - integrity sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU= - dependencies: - ast-types "0.x.x" - escodegen "1.x.x" - esprima "3.x.x" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -1999,6 +2031,11 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +detect-node@^2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.5.tgz#9d270aa7eaa5af0b72c4c9d9b814e7f4ce738b79" + integrity sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw== + diff@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -2289,18 +2326,16 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es6-error@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= - dependencies: - es6-promise "^4.0.3" - escalade@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" @@ -2321,33 +2356,16 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@1.x.x: - version "1.14.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" - integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -esprima@3.x.x: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= - -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -estraverse@^4.2.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - estree-walker@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.2.tgz#d3850be7529c9580d815600b53126515e146dd39" @@ -2549,11 +2567,6 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - fast-safe-stringify@^2.0.4: version "2.0.7" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" @@ -2590,11 +2603,6 @@ figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" -file-uri-to-path@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -2751,14 +2759,6 @@ fstream@^1.0.0, fstream@^1.0.12: mkdirp ">=0.5 0" rimraf "2" -ftp@~0.3.10: - version "0.3.10" - resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" - integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= - dependencies: - readable-stream "1.1.x" - xregexp "2.0.0" - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2843,18 +2843,6 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-uri@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.4.tgz#d4937ab819e218d4cb5ae18e4f5962bef169cc6a" - integrity sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q== - dependencies: - data-uri-to-buffer "1" - debug "2" - extend "~3.0.2" - file-uri-to-path "1" - ftp "~0.3.10" - readable-stream "2" - get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -2906,6 +2894,19 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@~7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" +global-agent@^2.1.12: + version "2.1.12" + resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.1.12.tgz#e4ae3812b731a9e81cbf825f9377ef450a8e4195" + integrity sha512-caAljRMS/qcDo69X9BfkgrihGUgGx44Fb4QQToNQjsiWh+YlQ66uqYVAdA8Olqit+5Ng0nkz09je3ZzANMZcjg== + dependencies: + boolean "^3.0.1" + core-js "^3.6.5" + es6-error "^4.1.1" + matcher "^3.0.0" + roarr "^2.15.3" + semver "^7.3.2" + serialize-error "^7.0.1" + global-dirs@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" @@ -2933,6 +2934,13 @@ global-prefix@^1.0.1: is-windows "^1.0.1" which "^1.2.14" +globalthis@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" + integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ== + dependencies: + define-properties "^1.1.3" + globule@^1.0.0: version "1.3.2" resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.2.tgz#d8bdd9e9e4eef8f96e245999a5dee7eb5d8529c4" @@ -3216,7 +3224,7 @@ http-errors@1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-errors@1.7.3, http-errors@~1.7.2: +http-errors@~1.7.2: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== @@ -3227,14 +3235,6 @@ http-errors@1.7.3, http-errors@~1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-proxy-agent@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" - integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== - dependencies: - agent-base "4" - debug "3.1.0" - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -3252,14 +3252,6 @@ http2-wrapper@^1.0.0-beta.4.5: quick-lru "^5.1.1" resolve-alpn "^1.0.0" -https-proxy-agent@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz#b8c286433e87602311b01c8ea34413d856a4af81" - integrity sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg== - dependencies: - agent-base "^4.3.0" - debug "^3.1.0" - hyperlist@^1.0.0-beta: version "1.0.0-beta" resolved "https://registry.yarnpkg.com/hyperlist/-/hyperlist-1.0.0-beta.tgz#2cbbd77f4498c2ecc290b7f3c6745b3f0288247e" @@ -3341,6 +3333,11 @@ indent-string@^2.1.0: dependencies: repeating "^2.0.0" +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" @@ -3359,7 +3356,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3384,11 +3381,6 @@ iota-array@^1.0.0: resolved "https://registry.yarnpkg.com/iota-array/-/iota-array-1.0.0.tgz#81ef57fe5d05814cd58c2483632a99c30a0e8087" integrity sha1-ge9X/l0FgUzVjCSDYyqZwwoOgIc= -ip@1.1.5, ip@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= - ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -3595,6 +3587,11 @@ is-installed-globally@^0.3.1: global-dirs "^2.0.1" is-path-inside "^3.0.1" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-map@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" @@ -3717,6 +3714,11 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -3754,11 +3756,6 @@ is-yarn-global@^0.3.0: resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -3882,7 +3879,7 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= -json-stringify-safe@~5.0.1: +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -4005,14 +4002,6 @@ less@^3.11.1: request "^2.83.0" source-map "~0.6.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - lie@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" @@ -4418,6 +4407,14 @@ lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +log-symbols@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + logform@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2" @@ -4531,6 +4528,13 @@ matched@^1.0.2: is-valid-glob "^1.0.0" resolve-dir "^1.0.0" +matcher@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" + integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== + dependencies: + escape-string-regexp "^4.0.0" + mdn-data@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" @@ -4881,11 +4885,6 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -netmask@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" - integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= - nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -5152,17 +5151,19 @@ open@^7.0.3: is-docker "^2.0.0" is-wsl "^2.1.1" -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== +ora@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f" + integrity sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" + bl "^4.0.3" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + log-symbols "^4.0.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" @@ -5243,6 +5244,13 @@ p-map@2.1.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-queue@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-2.4.2.tgz#03609826682b743be9a22dba25051bd46724fc34" @@ -5253,31 +5261,6 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pac-proxy-agent@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz#115b1e58f92576cac2eba718593ca7b0e37de2ad" - integrity sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ== - dependencies: - agent-base "^4.2.0" - debug "^4.1.1" - get-uri "^2.0.0" - http-proxy-agent "^2.1.0" - https-proxy-agent "^3.0.0" - pac-resolver "^3.0.0" - raw-body "^2.2.0" - socks-proxy-agent "^4.0.1" - -pac-resolver@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26" - integrity sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA== - dependencies: - co "^4.6.0" - degenerator "^1.0.4" - ip "^1.1.5" - netmask "^1.0.6" - thunkify "^2.1.2" - package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" @@ -5869,11 +5852,6 @@ postcss@^7.0.32: source-map "^0.6.1" supports-color "^6.1.0" -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - prepend-http@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" @@ -5941,20 +5919,6 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.1" -proxy-agent@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.1.1.tgz#7e04e06bf36afa624a1540be247b47c970bd3014" - integrity sha512-WudaR0eTsDx33O3EJE16PjBRZWcX8GqCEeERw1W3hZJgH/F2a46g7jty6UGty6NeJ4CKQy8ds2CJPMiyeqaTvw== - dependencies: - agent-base "^4.2.0" - debug "4" - http-proxy-agent "^2.1.0" - https-proxy-agent "^3.0.0" - lru-cache "^5.1.1" - pac-proxy-agent "^3.0.1" - proxy-from-env "^1.0.0" - socks-proxy-agent "^4.0.1" - proxy-from-env@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" @@ -6143,16 +6107,6 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@^2.2.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" - integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== - dependencies: - bytes "3.1.0" - http-errors "1.7.3" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-loader@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" @@ -6185,17 +6139,7 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -readable-stream@1.1.x: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.5, readable-stream@^2.3.7, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.5, readable-stream@^2.3.7, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -6545,6 +6489,18 @@ rimraf@^3.0.0: dependencies: glob "^7.1.3" +roarr@^2.15.3: + version "2.15.4" + resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" + integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== + dependencies: + boolean "^3.0.1" + detect-node "^2.0.4" + globalthis "^1.0.1" + json-stringify-safe "^5.0.1" + semver-compare "^1.0.0" + sprintf-js "^1.1.2" + rollup-plugin-buble@^0.19.2: version "0.19.6" resolved "https://registry.yarnpkg.com/rollup-plugin-buble/-/rollup-plugin-buble-0.19.6.tgz#55ee0995d8870d536f01f4277c3eef4276e8747e" @@ -6705,6 +6661,11 @@ scss-tokenizer@^0.2.3: js-base64 "^2.1.8" source-map "^0.4.2" +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= + semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" @@ -6722,7 +6683,7 @@ semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.0.0, semver@^7.3.4: +semver@^7.0.0, semver@^7.3.2, semver@^7.3.4: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -6753,6 +6714,13 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" +serialize-error@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" + integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== + dependencies: + type-fest "^0.13.1" + serialize-javascript@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" @@ -6891,11 +6859,6 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -smart-buffer@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" - integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== - snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -6990,13 +6953,13 @@ snyk-go-plugin@1.17.0: tmp "0.2.1" tslib "^1.10.0" -snyk-gradle-plugin@3.13.2: - version "3.13.2" - resolved "https://registry.yarnpkg.com/snyk-gradle-plugin/-/snyk-gradle-plugin-3.13.2.tgz#c5166f6a9416702cf024cc11e3b88acf198111c8" - integrity sha512-t7lBFgWwS3KU7SgmAeTJnTR44Wew84/IvNbNZ2fF0f+lXd1kZxMG1Ty2brETvxpl+U2JxC8ISILohGXsET+ySg== +snyk-gradle-plugin@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/snyk-gradle-plugin/-/snyk-gradle-plugin-3.14.0.tgz#26c9833c97ef50e83b022b2e3077a9056f8674e1" + integrity sha512-2A8ifM91TyzSx/U2fYvHXbaCRVsEx60hGFQjbSH9Hl9AokxEzMi2qti7wsObs1jUX2m198D1mdXu4k/Y1jWxXg== dependencies: "@snyk/cli-interface" "2.11.0" - "@snyk/dep-graph" "^1.23.1" + "@snyk/dep-graph" "^1.28.0" "@snyk/java-call-graph-builder" "1.20.0" "@types/debug" "^4.1.4" chalk "^3.0.0" @@ -7203,19 +7166,21 @@ snyk-try-require@^2.0.0: lodash.clonedeep "^4.3.0" lru-cache "^5.1.1" -snyk@^1.465.0: - version "1.509.0" - resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.509.0.tgz#7af0ef470cc429ffe9b2b44860d291e0542bfcab" - integrity sha512-3vdfa79Phr16O6Laun5zkNOxhQ7VIPeqb+aWwREkY3xOldLiZmOgQxfwKkllc/kImDmxB1CdDmRRwSJvPGMJ3Q== +snyk@^1.518.0: + version "1.520.0" + resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.520.0.tgz#2cc0a9981a47f1140379c48e9a1116c68328ccd2" + integrity sha512-+NSZDb1sbG+AxzK//GLAKHF0oyPyDlUzQgwy8tyet4tiUsnDKKEruCsX4R9c0outy5qfAExKG77aZego3N9p/Q== dependencies: "@open-policy-agent/opa-wasm" "^1.2.0" "@snyk/cli-interface" "2.11.0" - "@snyk/code-client" "3.1.5" + "@snyk/code-client" "3.4.0" "@snyk/dep-graph" "^1.27.1" + "@snyk/fix" "1.518.0" "@snyk/gemfile" "1.2.0" "@snyk/graphlib" "^2.1.9-patch.3" "@snyk/inquirer" "^7.3.3-patch" "@snyk/snyk-cocoapods-plugin" "2.5.2" + "@snyk/snyk-hex-plugin" "1.0.0" abbrev "^1.1.1" ansi-escapes "3.2.0" chalk "^2.4.2" @@ -7223,6 +7188,7 @@ snyk@^1.465.0: configstore "^5.0.1" debug "^4.1.1" diff "^4.0.1" + global-agent "^2.1.12" hcl-to-json "^0.1.1" lodash.assign "^4.2.0" lodash.camelcase "^4.3.0" @@ -7244,16 +7210,17 @@ snyk@^1.465.0: micromatch "4.0.2" needle "2.6.0" open "^7.0.3" + ora "5.3.0" os-name "^3.0.0" promise-queue "^2.2.5" - proxy-agent "^3.1.1" proxy-from-env "^1.0.0" + rimraf "^2.6.3" semver "^6.0.0" snyk-config "4.0.0" snyk-cpp-plugin "2.2.1" snyk-docker-plugin "4.19.3" snyk-go-plugin "1.17.0" - snyk-gradle-plugin "3.13.2" + snyk-gradle-plugin "3.14.0" snyk-module "3.1.0" snyk-mvn-plugin "2.25.3" snyk-nodejs-lockfile-parser "1.31.1" @@ -7326,22 +7293,6 @@ socket.io@^2.4.0: socket.io-client "2.4.0" socket.io-parser "~3.4.0" -socks-proxy-agent@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" - integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== - dependencies: - agent-base "~4.2.1" - socks "~2.3.2" - -socks@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.3.tgz#01129f0a5d534d2b897712ed8aceab7ee65d78e3" - integrity sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA== - dependencies: - ip "1.1.5" - smart-buffer "^4.1.0" - sortablejs@^1.7.0: version "1.8.3" resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.8.3.tgz#5ae908ef96300966e95440a143340f5dd565a0df" @@ -7601,11 +7552,6 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -7613,6 +7559,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +strip-ansi@6.0.0, strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -7634,13 +7587,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -7906,11 +7852,6 @@ through@^2.3.4, through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -thunkify@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" - integrity sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0= - timsort@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" @@ -8071,18 +8012,16 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - type-fest@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -8344,6 +8283,13 @@ vue@^2.6.11: resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5" integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ== +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + which-boxed-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" @@ -8433,11 +8379,6 @@ winston@^3.1.0: triple-beam "^1.3.0" winston-transport "^4.4.0" -word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -8515,26 +8456,16 @@ xpath@^0.0.27: resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92" integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ== -xregexp@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" - integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= - xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -"y18n@^3.2.1 || ^4.0.0": +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== -y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== - yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"