diff --git a/frappe/public/js/frappe/recorder/RecorderDetail.vue b/frappe/public/js/frappe/recorder/RecorderDetail.vue index 5d934d7e1e..d17a8f0ec4 100644 --- a/frappe/public/js/frappe/recorder/RecorderDetail.vue +++ b/frappe/public/js/frappe/recorder/RecorderDetail.vue @@ -155,7 +155,7 @@ export default { number: 1, status: (current_page == 1) ? "disabled" : "", },{ - label: __("Previous)", + label: __("Previous"), number: Math.max(current_page - 1, 1), status: (current_page == 1) ? "disabled" : "", }, { diff --git a/frappe/public/js/frappe/recorder/recorder.js b/frappe/public/js/frappe/recorder/recorder.js index c80fad62f6..eed57ddd4f 100644 --- a/frappe/public/js/frappe/recorder/recorder.js +++ b/frappe/public/js/frappe/recorder/recorder.js @@ -6,6 +6,9 @@ import RecorderRoot from "./RecorderRoot.vue"; import RecorderDetail from "./RecorderDetail.vue"; import RequestDetail from "./RequestDetail.vue"; +Vue.prototype.__ = window.__; +Vue.prototype.frappe = window.frappe; + Vue.use(VueRouter); const routes = [ { diff --git a/frappe/translate.py b/frappe/translate.py index 4baf4bdd89..f5f9797d07 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -443,8 +443,15 @@ def get_messages_from_report(name): messages = _get_messages_from_page_or_report("Report", name, frappe.db.get_value("DocType", report.ref_doctype, "module")) + if report.columns: + messages.extend([(None, report_column.label) for report_column in report.columns]) + + if report.filters: + messages.extend([(None, report_filter.label) for report_filter in report.filters]) + if report.query: messages.extend([(None, message) for message in re.findall('"([^:,^"]*):', report.query) if is_translatable(message)]) + messages.append((None,report.report_name)) return messages diff --git a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.json b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.json index e1439fd2dc..0cb11068f5 100644 --- a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.json +++ b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.json @@ -6,7 +6,9 @@ "engine": "InnoDB", "field_order": [ "email", - "status" + "status", + "anonymization_matrix", + "deletion_steps" ], "fields": [ { @@ -27,10 +29,23 @@ "label": "Status", "options": "Pending Verification\nPending Approval\nDeleted", "read_only": 1 + }, + { + "fieldname": "anonymization_matrix", + "fieldtype": "Code", + "label": "Anonymization Matrix", + "options": "JSON", + "read_only": 1 + }, + { + "fieldname": "deletion_steps", + "fieldtype": "Table", + "label": "Deletion Steps ", + "options": "Personal Data Deletion Step" } ], "links": [], - "modified": "2021-02-28 12:36:08.219719", + "modified": "2021-04-23 13:25:53.629308", "modified_by": "Administrator", "module": "Website", "name": "Personal Data Deletion Request", diff --git a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py index 23857a5e66..6fbdcc839b 100644 --- a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py +++ b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py @@ -10,6 +10,8 @@ from frappe.model.document import Document from frappe.utils import get_fullname from frappe.utils.user import get_system_managers from frappe.utils.verified_command import get_signed_params, verify_request +import json +from frappe.core.utils import find class PersonalDataDeletionRequest(Document): @@ -118,6 +120,24 @@ class PersonalDataDeletionRequest(Document): now=frappe.flags.in_test, ) + def add_deletion_steps(self): + if self.deletion_steps: + return + + for step in self.full_match_privacy_docs + self.partial_privacy_docs: + row_data = { + "status": "Pending", + "document_type": step.get("doctype"), + "partial": step.get("partial") or False, + "fields": json.dumps(step.get("redact_fields", [])), + "filtered_by": step.get("filtered_by") or "", + } + self.append("deletion_steps", row_data) + + self.anonymization_matrix = json.dumps(self.anonymization_value_map, indent=4) + self.save() + self.reload() + def redact_partial_match_data(self, doctype): self.__redact_partial_match_data(doctype) self.rename_documents(doctype) @@ -207,21 +227,57 @@ class PersonalDataDeletionRequest(Document): ref["doctype"], doc["name"], self.anon, force=True, show_alert=False ) - def _anonymize_data(self, email=None, anon=None, set_data=True): + def _anonymize_data(self, email=None, anon=None, set_data=True, commit=False): email = email or self.email anon = anon or self.name if set_data: self.__set_anonymization_data(email, anon) - for doctype in self.full_match_privacy_docs: - self.redact_full_match_data(doctype, email) + self.add_deletion_steps() - for doctype in self.partial_privacy_docs: + self.full_match_doctypes = ( + x + for x in self.full_match_privacy_docs + if filter( + lambda x: x.document_type == x and x.status == "Pending", self.deletion_steps + ) + ) + + self.partial_match_doctypes = ( + x + for x in self.partial_privacy_docs + if filter( + lambda x: x.document_type == x and x.status == "Pending", self.deletion_steps + ) + ) + + for doctype in self.full_match_doctypes: + self.redact_full_match_data(doctype, email) + self.set_step_status(doctype["doctype"]) + if commit: + frappe.db.commit() + + for doctype in self.partial_match_doctypes: self.redact_partial_match_data(doctype) + self.set_step_status(doctype["doctype"]) + if commit: + frappe.db.commit() frappe.rename_doc("User", email, anon, force=True, show_alert=False) self.db_set("status", "Deleted") + if commit: + frappe.db.commit() + + def set_step_status(self, step, status="Deleted"): + del_step = find(self.deletion_steps, lambda x: x.document_type == step and x.status != status) + + if not del_step: + del_step = find(self.deletion_steps, lambda x: x.document_type == step) + + del_step.status = status + self.save() + self.reload() def __set_anonymization_data(self, email, anon): self.anon = anon or self.name @@ -290,9 +346,8 @@ def confirm_deletion(email, name, host_name): frappe.db.commit() frappe.respond_as_web_page( _("Confirmed"), - _( - "The process for deletion of {0} data associated with {1} has been initiated." - ).format(host_name, email), + _("The process for deletion of {0} data associated with {1} has been initiated.") + .format(host_name, email), indicator_color="green", ) diff --git a/frappe/website/doctype/personal_data_deletion_step/__init__.py b/frappe/website/doctype/personal_data_deletion_step/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/website/doctype/personal_data_deletion_step/personal_data_deletion_step.json b/frappe/website/doctype/personal_data_deletion_step/personal_data_deletion_step.json new file mode 100644 index 0000000000..5d38ea6222 --- /dev/null +++ b/frappe/website/doctype/personal_data_deletion_step/personal_data_deletion_step.json @@ -0,0 +1,66 @@ +{ + "actions": [], + "creation": "2021-04-23 13:25:26.162797", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "status", + "partial", + "fields", + "filtered_by" + ], + "fields": [ + { + "allow_in_quick_entry": 1, + "fieldname": "document_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Document Type", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_preview": 1, + "label": "Status", + "options": "Pending\nDeleted", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "partial", + "fieldtype": "Check", + "in_preview": 1, + "label": "Partial", + "read_only": 1 + }, + { + "fieldname": "fields", + "fieldtype": "Small Text", + "label": "Fields", + "read_only": 1 + }, + { + "fieldname": "filtered_by", + "fieldtype": "Data", + "label": "Filtered By", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-04-23 13:48:59.658681", + "modified_by": "Administrator", + "module": "Website", + "name": "Personal Data Deletion Step", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/website/doctype/personal_data_deletion_step/personal_data_deletion_step.py b/frappe/website/doctype/personal_data_deletion_step/personal_data_deletion_step.py new file mode 100644 index 0000000000..2a7451473d --- /dev/null +++ b/frappe/website/doctype/personal_data_deletion_step/personal_data_deletion_step.py @@ -0,0 +1,10 @@ +# -*- 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 + +class PersonalDataDeletionStep(Document): + pass