Feature: Automatische Aktualisierung des Projekte-Menüs und Validierung der Projekt-Verzeichnisse
- AppSettings: Menü-Update nach jedem app_settings.save() Aufruf - MainWindow: Prüfung der Projekt-Verzeichnis-Existenz beim Start - Nur gültige Projekte werden im Menü angezeigt - Ungültige Projekte werden ausgeblendet mit Logging-Warnung - Memory-Leak-Prävention durch korrektes Aufräumen alter Menüs
This commit is contained in:
+103
-64
@@ -116,7 +116,9 @@ class AppSettingsDlg(QDialog):
|
||||
self.ui.tableDiffPdfs.doubleClicked.connect(self._edit_diff_pdf)
|
||||
|
||||
# PDF-Projekte Tabelle
|
||||
self.ui.tablePdfProjects.setHorizontalHeaderLabels(["Name", "Projekt-Ordner", "XSL-Ordner", "Java-VM", "Saxon", "Apache FOP", "Diff-PDF"])
|
||||
self.ui.tablePdfProjects.setHorizontalHeaderLabels(
|
||||
["Name", "Projekt-Ordner", "XSL-Ordner", "Java-VM", "Saxon", "Apache FOP", "Diff-PDF"]
|
||||
)
|
||||
self.ui.tablePdfProjects.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
|
||||
self.ui.tablePdfProjects.doubleClicked.connect(self._edit_pdf_project)
|
||||
|
||||
@@ -142,7 +144,7 @@ class AppSettingsDlg(QDialog):
|
||||
name_item = QTableWidgetItem(xsl_dir.name)
|
||||
name_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableXsls.setItem(row, 0, name_item)
|
||||
|
||||
|
||||
path_item = QTableWidgetItem(str(xsl_dir.path_to_root_dir))
|
||||
path_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableXsls.setItem(row, 1, path_item)
|
||||
@@ -155,7 +157,7 @@ class AppSettingsDlg(QDialog):
|
||||
version_item = QTableWidgetItem(java_vm.version)
|
||||
version_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableJavaVms.setItem(row, 0, version_item)
|
||||
|
||||
|
||||
path_item = QTableWidgetItem(str(java_vm.path_to_binary_file))
|
||||
path_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableJavaVms.setItem(row, 1, path_item)
|
||||
@@ -168,11 +170,11 @@ class AppSettingsDlg(QDialog):
|
||||
version_item = QTableWidgetItem(saxon.version)
|
||||
version_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableSaxons.setItem(row, 0, version_item)
|
||||
|
||||
|
||||
path_item = QTableWidgetItem(str(saxon.path_to_jar_file))
|
||||
path_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableSaxons.setItem(row, 1, path_item)
|
||||
|
||||
|
||||
extension_item = QTableWidgetItem(saxon.output_file_extension)
|
||||
extension_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableSaxons.setItem(row, 2, extension_item)
|
||||
@@ -185,11 +187,11 @@ class AppSettingsDlg(QDialog):
|
||||
version_item = QTableWidgetItem(fop.version)
|
||||
version_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableApacheFops.setItem(row, 0, version_item)
|
||||
|
||||
|
||||
path_item = QTableWidgetItem(str(fop.path_to_dir))
|
||||
path_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableApacheFops.setItem(row, 1, path_item)
|
||||
|
||||
|
||||
extension_item = QTableWidgetItem(fop.output_file_extension)
|
||||
extension_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableApacheFops.setItem(row, 2, extension_item)
|
||||
@@ -202,15 +204,15 @@ class AppSettingsDlg(QDialog):
|
||||
version_item = QTableWidgetItem(diff_pdf.version)
|
||||
version_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableDiffPdfs.setItem(row, 0, version_item)
|
||||
|
||||
|
||||
path_item = QTableWidgetItem(str(diff_pdf.path_to_binary_file))
|
||||
path_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableDiffPdfs.setItem(row, 1, path_item)
|
||||
|
||||
|
||||
params_item = QTableWidgetItem(", ".join(diff_pdf.default_params))
|
||||
params_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableDiffPdfs.setItem(row, 2, params_item)
|
||||
|
||||
|
||||
extension_item = QTableWidgetItem(diff_pdf.output_file_extension)
|
||||
extension_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tableDiffPdfs.setItem(row, 3, extension_item)
|
||||
@@ -223,27 +225,27 @@ class AppSettingsDlg(QDialog):
|
||||
name_item = QTableWidgetItem(pdf_project.name)
|
||||
name_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tablePdfProjects.setItem(row, 0, name_item)
|
||||
|
||||
|
||||
project_dir_item = QTableWidgetItem(str(pdf_project.project_dir))
|
||||
project_dir_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tablePdfProjects.setItem(row, 1, project_dir_item)
|
||||
|
||||
|
||||
xsl_item = QTableWidgetItem(pdf_project.getXsl())
|
||||
xsl_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tablePdfProjects.setItem(row, 2, xsl_item)
|
||||
|
||||
|
||||
java_vm_item = QTableWidgetItem(pdf_project.getJavaVm())
|
||||
java_vm_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tablePdfProjects.setItem(row, 3, java_vm_item)
|
||||
|
||||
|
||||
saxon_item = QTableWidgetItem(pdf_project.getSaxon())
|
||||
saxon_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tablePdfProjects.setItem(row, 4, saxon_item)
|
||||
|
||||
|
||||
apache_fop_item = QTableWidgetItem(pdf_project.getApacheFop())
|
||||
apache_fop_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tablePdfProjects.setItem(row, 5, apache_fop_item)
|
||||
|
||||
|
||||
diff_pdf_item = QTableWidgetItem(pdf_project.getDiffPdf())
|
||||
diff_pdf_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.tablePdfProjects.setItem(row, 6, diff_pdf_item)
|
||||
@@ -291,6 +293,11 @@ class AppSettingsDlg(QDialog):
|
||||
# FopWorkerPool-Checkbox setzen
|
||||
self.ui.checkBoxUseFopPool.setChecked(self.settings.use_fop_worker_pool)
|
||||
|
||||
def _refresh_main_window_projects_menu(self):
|
||||
"""Aktualisiert das Projekte-Menü im MainWindow, falls vorhanden."""
|
||||
if self.parent() and hasattr(self.parent(), "_setup_projects_menu"):
|
||||
self.parent()._setup_projects_menu()
|
||||
|
||||
# XSL-Ordner Methoden
|
||||
def _add_xsl_dir(self):
|
||||
"""Fügt einen neuen XSL-Ordner hinzu."""
|
||||
@@ -303,9 +310,10 @@ class AppSettingsDlg(QDialog):
|
||||
new_xsl_dir = XslDir(id=new_id, name=data["name"], path_to_root_dir=data["path_to_root_dir"])
|
||||
self.temp_xsl_dirs.append(new_xsl_dir)
|
||||
self._populate_xsl_table()
|
||||
|
||||
|
||||
self.settings.xsl_dirs = self.temp_xsl_dirs.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _remove_xsl_dir(self):
|
||||
"""Entfernt den ausgewählten XSL-Ordner."""
|
||||
@@ -314,9 +322,10 @@ class AppSettingsDlg(QDialog):
|
||||
del self.temp_xsl_dirs[current_row]
|
||||
self._populate_xsl_table()
|
||||
self._update_xsl_buttons()
|
||||
|
||||
|
||||
self.settings.xsl_dirs = self.temp_xsl_dirs.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _update_xsl_buttons(self):
|
||||
"""Aktualisiert den Status der XSL-Buttons."""
|
||||
@@ -336,9 +345,10 @@ class AppSettingsDlg(QDialog):
|
||||
)
|
||||
self.temp_java_vms.append(new_java_vm)
|
||||
self._populate_java_vm_table()
|
||||
|
||||
|
||||
self.settings.java_vms = self.temp_java_vms.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _remove_java_vm(self):
|
||||
"""Entfernt die ausgewählte Java VM."""
|
||||
@@ -347,9 +357,10 @@ class AppSettingsDlg(QDialog):
|
||||
del self.temp_java_vms[current_row]
|
||||
self._populate_java_vm_table()
|
||||
self._update_java_vm_buttons()
|
||||
|
||||
|
||||
self.settings.java_vms = self.temp_java_vms.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _update_java_vm_buttons(self):
|
||||
"""Aktualisiert den Status der Java VM-Buttons."""
|
||||
@@ -372,9 +383,10 @@ class AppSettingsDlg(QDialog):
|
||||
)
|
||||
self.temp_saxon_jars.append(new_saxon)
|
||||
self._populate_saxon_table()
|
||||
|
||||
|
||||
self.settings.saxon_jars = self.temp_saxon_jars.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _remove_saxon(self):
|
||||
"""Entfernt die ausgewählte Saxon JAR."""
|
||||
@@ -383,9 +395,10 @@ class AppSettingsDlg(QDialog):
|
||||
del self.temp_saxon_jars[current_row]
|
||||
self._populate_saxon_table()
|
||||
self._update_saxon_buttons()
|
||||
|
||||
|
||||
self.settings.saxon_jars = self.temp_saxon_jars.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _update_saxon_buttons(self):
|
||||
"""Aktualisiert den Status der Saxon-Buttons."""
|
||||
@@ -408,9 +421,10 @@ class AppSettingsDlg(QDialog):
|
||||
)
|
||||
self.temp_apache_fops.append(new_fop)
|
||||
self._populate_apache_fop_table()
|
||||
|
||||
|
||||
self.settings.apache_fops = self.temp_apache_fops.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _remove_apache_fop(self):
|
||||
"""Entfernt die ausgewählte Apache FOP Konfiguration."""
|
||||
@@ -419,9 +433,10 @@ class AppSettingsDlg(QDialog):
|
||||
del self.temp_apache_fops[current_row]
|
||||
self._populate_apache_fop_table()
|
||||
self._update_apache_fop_buttons()
|
||||
|
||||
|
||||
self.settings.apache_fops = self.temp_apache_fops.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _update_apache_fop_buttons(self):
|
||||
"""Aktualisiert den Status der Apache FOP-Buttons."""
|
||||
@@ -445,9 +460,10 @@ class AppSettingsDlg(QDialog):
|
||||
)
|
||||
self.temp_diff_pdfs.append(new_diff_pdf)
|
||||
self._populate_diff_pdf_table()
|
||||
|
||||
|
||||
self.settings.diff_pdfs = self.temp_diff_pdfs.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _remove_diff_pdf(self):
|
||||
"""Entfernt die ausgewählte Diff PDF Konfiguration."""
|
||||
@@ -456,9 +472,10 @@ class AppSettingsDlg(QDialog):
|
||||
del self.temp_diff_pdfs[current_row]
|
||||
self._populate_diff_pdf_table()
|
||||
self._update_diff_pdf_buttons()
|
||||
|
||||
|
||||
self.settings.diff_pdfs = self.temp_diff_pdfs.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _update_diff_pdf_buttons(self):
|
||||
"""Aktualisiert den Status der Diff PDF-Buttons."""
|
||||
@@ -471,29 +488,30 @@ class AppSettingsDlg(QDialog):
|
||||
dialog = PdfProjectDlg(self)
|
||||
if dialog.exec() == PdfProjectDlg.DialogCode.Accepted:
|
||||
project_data = dialog.get_project_data()
|
||||
|
||||
|
||||
# Neue ID generieren
|
||||
new_id = max([p.id for p in self.temp_pdf_projects], default=0) + 1
|
||||
|
||||
|
||||
# Erstelle PdfProject-Objekt
|
||||
new_project = Project(
|
||||
id=new_id,
|
||||
name=project_data['name'],
|
||||
project_dir=Path(project_data['project_dir']),
|
||||
java_vm_id=project_data['java_vm_id'] if project_data['java_vm_id'] != -1 else 1,
|
||||
diff_pdf_id=project_data['diff_pdf_id'] if project_data['diff_pdf_id'] != -1 else 1,
|
||||
saxon_jar_id=project_data['saxon_jar_id'] if project_data['saxon_jar_id'] != -1 else 1,
|
||||
apache_fop_id=project_data['apache_fop_id'] if project_data['apache_fop_id'] != -1 else 1,
|
||||
xsl_dir_id=project_data['xsl_dir_id'] if project_data['xsl_dir_id'] != -1 else 1,
|
||||
postgre_sql_db_id=project_data['postgre_sql_db_id'] if project_data['postgre_sql_db_id'] != -1 else 1,
|
||||
fop_config_dir=Path(project_data['fop_config_dir']) if project_data.get('fop_config_dir') else None,
|
||||
name=project_data["name"],
|
||||
project_dir=Path(project_data["project_dir"]),
|
||||
java_vm_id=project_data["java_vm_id"] if project_data["java_vm_id"] != -1 else 1,
|
||||
diff_pdf_id=project_data["diff_pdf_id"] if project_data["diff_pdf_id"] != -1 else 1,
|
||||
saxon_jar_id=project_data["saxon_jar_id"] if project_data["saxon_jar_id"] != -1 else 1,
|
||||
apache_fop_id=project_data["apache_fop_id"] if project_data["apache_fop_id"] != -1 else 1,
|
||||
xsl_dir_id=project_data["xsl_dir_id"] if project_data["xsl_dir_id"] != -1 else 1,
|
||||
postgre_sql_db_id=project_data["postgre_sql_db_id"] if project_data["postgre_sql_db_id"] != -1 else 1,
|
||||
fop_config_dir=Path(project_data["fop_config_dir"]) if project_data.get("fop_config_dir") else None,
|
||||
)
|
||||
|
||||
|
||||
self.temp_pdf_projects.append(new_project)
|
||||
self._populate_pdf_project_table()
|
||||
|
||||
|
||||
self.settings.pdf_projects = self.temp_pdf_projects.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _remove_pdf_project(self):
|
||||
"""Entfernt das ausgewählte PDF-Projekt."""
|
||||
@@ -502,9 +520,10 @@ class AppSettingsDlg(QDialog):
|
||||
del self.temp_pdf_projects[current_row]
|
||||
self._populate_pdf_project_table()
|
||||
self._update_pdf_project_buttons()
|
||||
|
||||
|
||||
self.settings.pdf_projects = self.temp_pdf_projects.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _update_pdf_project_buttons(self):
|
||||
"""Aktualisiert den Status der PDF-Projekt-Buttons."""
|
||||
@@ -629,40 +648,57 @@ class AppSettingsDlg(QDialog):
|
||||
row = index.row()
|
||||
if 0 <= row < len(self.temp_pdf_projects):
|
||||
pdf_project = self.temp_pdf_projects[row]
|
||||
|
||||
|
||||
# Projektdaten für Dialog vorbereiten
|
||||
project_data = {
|
||||
'name': pdf_project.name,
|
||||
'project_dir': str(pdf_project.project_dir),
|
||||
'java_vm_id': pdf_project.java_vm_id,
|
||||
'diff_pdf_id': pdf_project.diff_pdf_id,
|
||||
'saxon_jar_id': pdf_project.saxon_jar_id,
|
||||
'apache_fop_id': pdf_project.apache_fop_id,
|
||||
'xsl_dir_id': pdf_project.xsl_dir_id,
|
||||
'postgre_sql_db_id': pdf_project.postgre_sql_db_id,
|
||||
'fop_config_dir': str(pdf_project.fop_config_dir) if pdf_project.fop_config_dir else None
|
||||
"name": pdf_project.name,
|
||||
"project_dir": str(pdf_project.project_dir),
|
||||
"java_vm_id": pdf_project.java_vm_id,
|
||||
"diff_pdf_id": pdf_project.diff_pdf_id,
|
||||
"saxon_jar_id": pdf_project.saxon_jar_id,
|
||||
"apache_fop_id": pdf_project.apache_fop_id,
|
||||
"xsl_dir_id": pdf_project.xsl_dir_id,
|
||||
"postgre_sql_db_id": pdf_project.postgre_sql_db_id,
|
||||
"fop_config_dir": str(pdf_project.fop_config_dir) if pdf_project.fop_config_dir else None,
|
||||
}
|
||||
|
||||
|
||||
# Dialog im Edit-Modus öffnen (Projekt-Name und -Ordner deaktiviert)
|
||||
dialog = PdfProjectDlg(self, project_data, edit_mode=True)
|
||||
|
||||
|
||||
if dialog.exec() == PdfProjectDlg.DialogCode.Accepted:
|
||||
new_data = dialog.get_project_data()
|
||||
|
||||
|
||||
# Nur die Einstellungen aktualisieren (Name und Ordner bleiben unverändert)
|
||||
pdf_project.java_vm_id = new_data['java_vm_id'] if new_data['java_vm_id'] != -1 else pdf_project.java_vm_id
|
||||
pdf_project.diff_pdf_id = new_data['diff_pdf_id'] if new_data['diff_pdf_id'] != -1 else pdf_project.diff_pdf_id
|
||||
pdf_project.saxon_jar_id = new_data['saxon_jar_id'] if new_data['saxon_jar_id'] != -1 else pdf_project.saxon_jar_id
|
||||
pdf_project.apache_fop_id = new_data['apache_fop_id'] if new_data['apache_fop_id'] != -1 else pdf_project.apache_fop_id
|
||||
pdf_project.xsl_dir_id = new_data['xsl_dir_id'] if new_data['xsl_dir_id'] != -1 else pdf_project.xsl_dir_id
|
||||
pdf_project.postgre_sql_db_id = new_data['postgre_sql_db_id'] if new_data['postgre_sql_db_id'] != -1 else pdf_project.postgre_sql_db_id
|
||||
pdf_project.fop_config_dir = Path(new_data['fop_config_dir']) if new_data.get('fop_config_dir') else None
|
||||
pdf_project.java_vm_id = (
|
||||
new_data["java_vm_id"] if new_data["java_vm_id"] != -1 else pdf_project.java_vm_id
|
||||
)
|
||||
pdf_project.diff_pdf_id = (
|
||||
new_data["diff_pdf_id"] if new_data["diff_pdf_id"] != -1 else pdf_project.diff_pdf_id
|
||||
)
|
||||
pdf_project.saxon_jar_id = (
|
||||
new_data["saxon_jar_id"] if new_data["saxon_jar_id"] != -1 else pdf_project.saxon_jar_id
|
||||
)
|
||||
pdf_project.apache_fop_id = (
|
||||
new_data["apache_fop_id"] if new_data["apache_fop_id"] != -1 else pdf_project.apache_fop_id
|
||||
)
|
||||
pdf_project.xsl_dir_id = (
|
||||
new_data["xsl_dir_id"] if new_data["xsl_dir_id"] != -1 else pdf_project.xsl_dir_id
|
||||
)
|
||||
pdf_project.postgre_sql_db_id = (
|
||||
new_data["postgre_sql_db_id"]
|
||||
if new_data["postgre_sql_db_id"] != -1
|
||||
else pdf_project.postgre_sql_db_id
|
||||
)
|
||||
pdf_project.fop_config_dir = (
|
||||
Path(new_data["fop_config_dir"]) if new_data.get("fop_config_dir") else None
|
||||
)
|
||||
|
||||
self._populate_pdf_project_table()
|
||||
|
||||
# Einstellungen speichern
|
||||
self.settings.pdf_projects = self.temp_pdf_projects.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
# PostgreSQL Methoden
|
||||
def _add_postgresql_db(self):
|
||||
@@ -679,13 +715,14 @@ class AppSettingsDlg(QDialog):
|
||||
port=data["port"],
|
||||
database=data["database"],
|
||||
username=data["username"],
|
||||
password=data["password"]
|
||||
password=data["password"],
|
||||
)
|
||||
self.temp_postgresql_dbs.append(new_postgresql_db)
|
||||
self._populate_postgresql_db_table()
|
||||
|
||||
|
||||
self.settings.postgresql_dbs = self.temp_postgresql_dbs.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _remove_postgresql_db(self):
|
||||
"""Entfernt die ausgewählte PostgreSQL-Datenbank."""
|
||||
@@ -694,9 +731,10 @@ class AppSettingsDlg(QDialog):
|
||||
del self.temp_postgresql_dbs[current_row]
|
||||
self._populate_postgresql_db_table()
|
||||
self._update_postgresql_db_buttons()
|
||||
|
||||
|
||||
self.settings.postgresql_dbs = self.temp_postgresql_dbs.copy()
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
def _update_postgresql_db_buttons(self):
|
||||
"""Aktualisiert den Status der PostgreSQL-Buttons."""
|
||||
@@ -717,7 +755,7 @@ class AppSettingsDlg(QDialog):
|
||||
"port": postgresql_db.port,
|
||||
"database": postgresql_db.database,
|
||||
"username": postgresql_db.username,
|
||||
"password": postgresql_db.password
|
||||
"password": postgresql_db.password,
|
||||
}
|
||||
dialog.set_data(data)
|
||||
|
||||
@@ -756,6 +794,7 @@ class AppSettingsDlg(QDialog):
|
||||
self.settings.use_fop_worker_pool = self.ui.checkBoxUseFopPool.isChecked()
|
||||
|
||||
self.settings.save()
|
||||
self._refresh_main_window_projects_menu()
|
||||
|
||||
super().accept()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user