Erweiterungen
This commit is contained in:
@@ -7,6 +7,8 @@ import com.vaadin.flow.component.formlayout.FormLayout;
|
|||||||
import com.vaadin.flow.component.html.Div;
|
import com.vaadin.flow.component.html.Div;
|
||||||
import com.vaadin.flow.component.html.Paragraph;
|
import com.vaadin.flow.component.html.Paragraph;
|
||||||
import com.vaadin.flow.component.html.Span;
|
import com.vaadin.flow.component.html.Span;
|
||||||
|
import com.vaadin.flow.component.html.H3;
|
||||||
|
import com.vaadin.flow.component.html.IFrame;
|
||||||
import com.vaadin.flow.component.icon.Icon;
|
import com.vaadin.flow.component.icon.Icon;
|
||||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||||
import com.vaadin.flow.component.notification.Notification;
|
import com.vaadin.flow.component.notification.Notification;
|
||||||
@@ -14,8 +16,14 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
|||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
import com.vaadin.flow.component.textfield.EmailField;
|
import com.vaadin.flow.component.textfield.EmailField;
|
||||||
import com.vaadin.flow.component.textfield.TextField;
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import com.vaadin.flow.component.textfield.TextArea;
|
||||||
|
import com.vaadin.flow.component.tabs.TabSheet;
|
||||||
import com.vaadin.flow.data.binder.Binder;
|
import com.vaadin.flow.data.binder.Binder;
|
||||||
import com.vaadin.flow.data.validator.EmailValidator;
|
import com.vaadin.flow.data.validator.EmailValidator;
|
||||||
|
import com.vaadin.flow.data.value.ValueChangeMode;
|
||||||
import com.vaadin.flow.router.PageTitle;
|
import com.vaadin.flow.router.PageTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import de.assecutor.votianlt.model.User;
|
import de.assecutor.votianlt.model.User;
|
||||||
@@ -27,14 +35,20 @@ import jakarta.annotation.security.RolesAllowed;
|
|||||||
@Route(value = "edit-profile", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
@Route(value = "edit-profile", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
||||||
@RolesAllowed({"USER","ADMIN"})
|
@RolesAllowed({"USER","ADMIN"})
|
||||||
public class EditProfileView extends HorizontalLayout {
|
public class EditProfileView extends HorizontalLayout {
|
||||||
private final UserService userService;
|
private final TextField prefixField;
|
||||||
private final SecurityService securityService;
|
private final TextField ustIdField;
|
||||||
|
private final TextField taxNumberField;
|
||||||
|
private final TextField bankNameField;
|
||||||
|
private final TextField ibanField;
|
||||||
|
private final TextField taxRateField;
|
||||||
|
private final TextArea introTextArea;
|
||||||
|
private final TextArea termsTextArea;
|
||||||
|
private final IFrame pdfFrame;
|
||||||
|
|
||||||
private final Binder<User> binder = new Binder<>(User.class);
|
private final Binder<User> binder = new Binder<>(User.class);
|
||||||
private final User currentUser;
|
private final User currentUser;
|
||||||
|
|
||||||
public EditProfileView(UserService userService, SecurityService securityService) {
|
public EditProfileView(UserService userService, SecurityService securityService) {
|
||||||
this.userService = userService;
|
|
||||||
this.securityService = securityService;
|
|
||||||
this.currentUser = securityService.getCurrentDatabaseUser();
|
this.currentUser = securityService.getCurrentDatabaseUser();
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
@@ -49,9 +63,13 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
|
|
||||||
// Linke Spalte: Formular
|
// Linke Spalte: Formular
|
||||||
VerticalLayout formColumn = new VerticalLayout();
|
VerticalLayout formColumn = new VerticalLayout();
|
||||||
formColumn.setWidth("48%");
|
formColumn.setWidth("68%");
|
||||||
formColumn.setPadding(false);
|
formColumn.setPadding(false);
|
||||||
formColumn.setSpacing(false);
|
formColumn.setSpacing(false);
|
||||||
|
// TabSheet
|
||||||
|
TabSheet tabSheet = new TabSheet();
|
||||||
|
tabSheet.setSizeFull();
|
||||||
|
|
||||||
|
|
||||||
FormLayout form = new FormLayout();
|
FormLayout form = new FormLayout();
|
||||||
form.setWidthFull();
|
form.setWidthFull();
|
||||||
@@ -72,10 +90,6 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
TextField zipField = new TextField("Postleitzahl");
|
TextField zipField = new TextField("Postleitzahl");
|
||||||
TextField cityField = new TextField("Stadt");
|
TextField cityField = new TextField("Stadt");
|
||||||
|
|
||||||
// Pflichtfeldhinweis
|
|
||||||
Paragraph pflichtHinweis = new Paragraph("Die mit (*) gekennzeichneten Felder sind Pflichtfelder.");
|
|
||||||
pflichtHinweis.getStyle().set("color", "#d32f2f").set("fontWeight", "bold");
|
|
||||||
|
|
||||||
// Abweichende Rechnungsadresse
|
// Abweichende Rechnungsadresse
|
||||||
Checkbox diffInvoiceAddress = new Checkbox("Abweichende Rechnungsadresse");
|
Checkbox diffInvoiceAddress = new Checkbox("Abweichende Rechnungsadresse");
|
||||||
diffInvoiceAddress.getStyle().set("marginTop", "1em");
|
diffInvoiceAddress.getStyle().set("marginTop", "1em");
|
||||||
@@ -194,39 +208,126 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
form.add(diffInvoiceAddress, 2);
|
form.add(diffInvoiceAddress, 2);
|
||||||
form.add(invCompanyField, 2);
|
form.add(invCompanyField, 2);
|
||||||
form.add(invCompanyAddField, 2);
|
form.add(invCompanyAddField, 2);
|
||||||
|
// Tabs hinzufügen
|
||||||
form.add(invFirstnameField, invLastnameField);
|
form.add(invFirstnameField, invLastnameField);
|
||||||
form.add(invStreetField, invHouseNumberField);
|
form.add(invStreetField, invHouseNumberField);
|
||||||
form.add(invAddressAddField, 2);
|
form.add(invAddressAddField, 2);
|
||||||
form.add(invZipField, invCityField);
|
form.add(invZipField, invCityField);
|
||||||
|
|
||||||
formColumn.add(form, pflichtHinweis);
|
tabSheet.add("Stammdaten", form);
|
||||||
|
|
||||||
// Schalter
|
// Karte (2. Tab)
|
||||||
Checkbox locateAppUser = new Checkbox("App-Nutzer orten");
|
|
||||||
Checkbox digitalProcess = new Checkbox("Digitale Abwicklung");
|
|
||||||
Checkbox invoiceViaVotianlt = new Checkbox("Rechnungslegung über votianlt");
|
|
||||||
formColumn.add(locateAppUser, digitalProcess, invoiceViaVotianlt);
|
|
||||||
|
|
||||||
// Rechte Spalte: Karte und Aktionen
|
|
||||||
VerticalLayout rightColumn = new VerticalLayout();
|
|
||||||
rightColumn.setWidth("48%");
|
|
||||||
rightColumn.setAlignItems(Alignment.CENTER);
|
|
||||||
rightColumn.setSpacing(true);
|
|
||||||
rightColumn.setPadding(false);
|
|
||||||
|
|
||||||
// Koordinatenanzeige
|
|
||||||
Span coordsLabel = new Span("53°36'25.1\"N 10°06'46.9\"E");
|
Span coordsLabel = new Span("53°36'25.1\"N 10°06'46.9\"E");
|
||||||
coordsLabel.getStyle().set("fontWeight", "bold").set("marginTop", "1em");
|
coordsLabel.getStyle().set("fontWeight", "bold").set("marginTop", "1em");
|
||||||
rightColumn.add(coordsLabel);
|
|
||||||
|
|
||||||
// Google Maps Platzhalter (iFrame)
|
|
||||||
Div mapDiv = new Div();
|
Div mapDiv = new Div();
|
||||||
mapDiv.setWidth("400px");
|
mapDiv.setWidth("100%");
|
||||||
mapDiv.setHeight("400px");
|
mapDiv.setHeight("400px");
|
||||||
mapDiv.getElement().setProperty("innerHTML",
|
mapDiv.getElement().setProperty("innerHTML",
|
||||||
"<iframe width='100%' height='100%' frameborder='0' style='border:0' " +
|
"<iframe width='100%' height='100%' frameborder='0' style='border:0' " +
|
||||||
"src='https://www.google.com/maps/embed/v1/place?key=AIzaSyDnbitL06iLp3elmj-WtPudCykX9xvXcVE&q=53.6070,10.1125' allowfullscreen></iframe>");
|
"src='https://www.google.com/maps/embed/v1/place?key=AIzaSyDnbitL06iLp3elmj-WtPudCykX9xvXcVE&q=53.6070,10.1125' allowfullscreen></iframe>");
|
||||||
rightColumn.add(mapDiv);
|
VerticalLayout mapTab = new VerticalLayout();
|
||||||
|
mapTab.setPadding(false);
|
||||||
|
mapTab.setSpacing(true);
|
||||||
|
mapTab.add(coordsLabel, mapDiv);
|
||||||
|
tabSheet.add("Karte", mapTab);
|
||||||
|
// Dritter Tab: Rechnungsstellung
|
||||||
|
HorizontalLayout billingTab = new HorizontalLayout();
|
||||||
|
billingTab.setWidthFull();
|
||||||
|
billingTab.setSpacing(true);
|
||||||
|
billingTab.setPadding(false);
|
||||||
|
|
||||||
|
// Linke Spalte: Rechnungsbestandteile
|
||||||
|
VerticalLayout billingLeft = new VerticalLayout();
|
||||||
|
billingLeft.setWidth("45%");
|
||||||
|
billingLeft.setPadding(false);
|
||||||
|
billingLeft.setSpacing(true);
|
||||||
|
H3 partsTitle = new H3("Rechnungsbestandteile");
|
||||||
|
partsTitle.getStyle().set("margin", "0 0 var(--lumo-space-s) 0");
|
||||||
|
|
||||||
|
// Felder für Rechnungsstellung (für Live-Update)
|
||||||
|
Checkbox billingEnabled = new Checkbox("Rechnungslegung über votianLT");
|
||||||
|
billingEnabled.setValue(false);
|
||||||
|
billingEnabled.addValueChangeListener(e -> toggleBilling(e.getValue()));
|
||||||
|
|
||||||
|
prefixField = new TextField("Rechnungs Präfix");
|
||||||
|
ustIdField = new TextField("USt-IdNr.");
|
||||||
|
taxNumberField = new TextField("Steuernummer");
|
||||||
|
bankNameField = new TextField("Bankname");
|
||||||
|
ibanField = new TextField("IBAN");
|
||||||
|
taxRateField = new TextField("Steuersatz");
|
||||||
|
introTextArea = new TextArea("Einleitungstext");
|
||||||
|
termsTextArea = new TextArea("Zahlungsbedingungen");
|
||||||
|
introTextArea.setWidthFull();
|
||||||
|
termsTextArea.setWidthFull();
|
||||||
|
|
||||||
|
// Anfangszustand: Felder deaktiviert bis Checkbox aktiv
|
||||||
|
setBillingFieldsEnabled(false);
|
||||||
|
|
||||||
|
// Live-Update: EAGER und Listener
|
||||||
|
prefixField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||||
|
ustIdField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||||
|
taxNumberField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||||
|
bankNameField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||||
|
ibanField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||||
|
taxRateField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||||
|
introTextArea.setValueChangeMode(ValueChangeMode.EAGER);
|
||||||
|
termsTextArea.setValueChangeMode(ValueChangeMode.EAGER);
|
||||||
|
prefixField.addValueChangeListener(e -> refreshPdf());
|
||||||
|
ustIdField.addValueChangeListener(e -> refreshPdf());
|
||||||
|
taxNumberField.addValueChangeListener(e -> refreshPdf());
|
||||||
|
bankNameField.addValueChangeListener(e -> refreshPdf());
|
||||||
|
ibanField.addValueChangeListener(e -> refreshPdf());
|
||||||
|
taxRateField.addValueChangeListener(e -> refreshPdf());
|
||||||
|
introTextArea.addValueChangeListener(e -> refreshPdf());
|
||||||
|
termsTextArea.addValueChangeListener(e -> refreshPdf());
|
||||||
|
|
||||||
|
billingLeft.add(partsTitle, billingEnabled, prefixField, ustIdField, taxNumberField, bankNameField,
|
||||||
|
ibanField, taxRateField, introTextArea, termsTextArea);
|
||||||
|
|
||||||
|
// Rechte Spalte: Vorschau
|
||||||
|
VerticalLayout billingRight = new VerticalLayout();
|
||||||
|
billingRight.setWidth("55%");
|
||||||
|
billingRight.setPadding(false);
|
||||||
|
billingRight.setSpacing(false);
|
||||||
|
billingRight.setHeight("70vh");
|
||||||
|
H3 previewTitle = new H3("Rechnungsvorschau");
|
||||||
|
previewTitle.getStyle().set("margin", "0 0 var(--lumo-space-s) 0");
|
||||||
|
|
||||||
|
// Echte PDF-Vorschau mittels StreamResource und iframe
|
||||||
|
Div previewWrapper = new Div();
|
||||||
|
previewWrapper.setWidth("100%");
|
||||||
|
previewWrapper.setHeight("100%");
|
||||||
|
previewWrapper.getStyle()
|
||||||
|
.set("overflow", "auto");
|
||||||
|
|
||||||
|
// Initial noch keine PDF laden (erst bei aktiver Checkbox)
|
||||||
|
pdfFrame = new IFrame();
|
||||||
|
pdfFrame.setWidth("100%");
|
||||||
|
pdfFrame.setHeight("100%");
|
||||||
|
previewWrapper.removeAll();
|
||||||
|
previewWrapper.add(pdfFrame);
|
||||||
|
|
||||||
|
billingRight.add(previewTitle, previewWrapper);
|
||||||
|
|
||||||
|
|
||||||
|
billingTab.add(billingLeft, billingRight);
|
||||||
|
tabSheet.add("Rechnungsstellung", billingTab);
|
||||||
|
|
||||||
|
|
||||||
|
// Zweiter Tab: Einstellungen (Beispiel mit Schaltern)
|
||||||
|
VerticalLayout switches = new VerticalLayout();
|
||||||
|
switches.setPadding(false);
|
||||||
|
switches.setSpacing(true);
|
||||||
|
Checkbox locateAppUser = new Checkbox("App-Nutzer orten");
|
||||||
|
Checkbox digitalProcess = new Checkbox("Digitale Abwicklung");
|
||||||
|
Checkbox invoiceViaVotianlt = new Checkbox("Rechnungslegung über votianlt");
|
||||||
|
switches.add(locateAppUser, digitalProcess, invoiceViaVotianlt);
|
||||||
|
tabSheet.add("Einstellungen", switches);
|
||||||
|
|
||||||
|
// Sicherheit-Tab (2FA, Passwort, Konto)
|
||||||
|
VerticalLayout securityTab = new VerticalLayout();
|
||||||
|
securityTab.setPadding(false);
|
||||||
|
securityTab.setSpacing(true);
|
||||||
|
|
||||||
// 2-Faktor Auth
|
// 2-Faktor Auth
|
||||||
Checkbox twoFactor = new Checkbox("2-Faktor-Authentifizierung");
|
Checkbox twoFactor = new Checkbox("2-Faktor-Authentifizierung");
|
||||||
@@ -234,17 +335,19 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
twoFactorInfo.getStyle().set("marginLeft", "0.3em");
|
twoFactorInfo.getStyle().set("marginLeft", "0.3em");
|
||||||
HorizontalLayout twoFactorLayout = new HorizontalLayout(twoFactor, twoFactorInfo);
|
HorizontalLayout twoFactorLayout = new HorizontalLayout(twoFactor, twoFactorInfo);
|
||||||
twoFactorLayout.setAlignItems(Alignment.CENTER);
|
twoFactorLayout.setAlignItems(Alignment.CENTER);
|
||||||
rightColumn.add(twoFactorLayout);
|
securityTab.add(twoFactorLayout);
|
||||||
|
|
||||||
// Passwort ändern Button
|
// Passwort ändern Button
|
||||||
Button changePassword = new Button("Passwort ändern");
|
Button changePassword = new Button("Passwort ändern");
|
||||||
changePassword.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
changePassword.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
rightColumn.add(changePassword);
|
securityTab.add(changePassword);
|
||||||
|
|
||||||
// Benutzerkonto löschen Button
|
// Benutzerkonto löschen Button
|
||||||
Button deleteAccount = new Button("Benutzerkonto löschen");
|
Button deleteAccount = new Button("Benutzerkonto löschen");
|
||||||
deleteAccount.addThemeVariants(ButtonVariant.LUMO_ERROR);
|
deleteAccount.addThemeVariants(ButtonVariant.LUMO_ERROR);
|
||||||
rightColumn.add(deleteAccount);
|
securityTab.add(deleteAccount);
|
||||||
|
|
||||||
|
tabSheet.add("Sicherheit", securityTab);
|
||||||
|
|
||||||
// Profil speichern Button (unten rechts)
|
// Profil speichern Button (unten rechts)
|
||||||
Button saveProfile = new Button("Profiländerungen speichern");
|
Button saveProfile = new Button("Profiländerungen speichern");
|
||||||
@@ -260,8 +363,91 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// Tabs dem linken Bereich hinzufügen
|
||||||
|
formColumn.add(tabSheet);
|
||||||
|
|
||||||
saveProfile.getStyle().set("position", "absolute").set("right", "2em").set("bottom", "2em");
|
|
||||||
add(formColumn, rightColumn, saveProfile);
|
// Button horizontal zentrieren unterhalb der Tabs
|
||||||
|
HorizontalLayout buttonBar = new HorizontalLayout(saveProfile);
|
||||||
|
buttonBar.setWidthFull();
|
||||||
|
buttonBar.setJustifyContentMode(JustifyContentMode.CENTER);
|
||||||
|
buttonBar.setPadding(false);
|
||||||
|
buttonBar.setSpacing(false);
|
||||||
|
// 20px Abstand nach oben
|
||||||
|
buttonBar.getStyle().set("margin-top", "20px");
|
||||||
|
// Fixieren am unteren Rand
|
||||||
|
buttonBar.getStyle().set("position", "sticky");
|
||||||
|
buttonBar.getStyle().set("bottom", "0");
|
||||||
|
buttonBar.getStyle().set("background", "var(--lumo-base-color)");
|
||||||
|
buttonBar.getStyle().set("padding", "var(--lumo-space-s) 0");
|
||||||
|
buttonBar.getStyle().set("z-index", "1");
|
||||||
|
formColumn.add(buttonBar);
|
||||||
|
add(formColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PDF neu rendern und iframe aktualisieren
|
||||||
|
// Felder im Billing-Tab aktivieren/deaktivieren
|
||||||
|
private void setBillingFieldsEnabled(boolean enabled) {
|
||||||
|
if (prefixField != null) prefixField.setEnabled(enabled);
|
||||||
|
if (ustIdField != null) ustIdField.setEnabled(enabled);
|
||||||
|
if (taxNumberField != null) taxNumberField.setEnabled(enabled);
|
||||||
|
if (bankNameField != null) bankNameField.setEnabled(enabled);
|
||||||
|
if (ibanField != null) ibanField.setEnabled(enabled);
|
||||||
|
if (taxRateField != null) taxRateField.setEnabled(enabled);
|
||||||
|
if (introTextArea != null) introTextArea.setEnabled(enabled);
|
||||||
|
if (termsTextArea != null) termsTextArea.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checkbox steuert Aktivierung und PDF
|
||||||
|
private void toggleBilling(boolean enabled) {
|
||||||
|
setBillingFieldsEnabled(enabled);
|
||||||
|
if (enabled) {
|
||||||
|
refreshPdf();
|
||||||
|
} else {
|
||||||
|
if (pdfFrame != null) {
|
||||||
|
pdfFrame.setSrc((String) null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshPdf() {
|
||||||
|
byte[] bytes = generatePreviewPdf();
|
||||||
|
String dataUrl = "data:application/pdf;base64," + Base64.getEncoder().encodeToString(bytes) + "#toolbar=0&navpanes=0&zoom=page-width&view=FitH";
|
||||||
|
if (pdfFrame != null) {
|
||||||
|
pdfFrame.setSrc(dataUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Einfache PDF-Vorschau generieren (kann später durch echte Logik ersetzt werden)
|
||||||
|
private byte[] generatePreviewPdf() {
|
||||||
|
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||||
|
com.lowagie.text.Document document = new com.lowagie.text.Document();
|
||||||
|
com.lowagie.text.pdf.PdfWriter.getInstance(document, out);
|
||||||
|
document.open();
|
||||||
|
document.add(new com.lowagie.text.Paragraph("Rechnungsvorschau"));
|
||||||
|
document.add(new com.lowagie.text.Paragraph(" "));
|
||||||
|
document.add(new com.lowagie.text.Paragraph("Rechnungs Präfix: " + safe(prefixField)));
|
||||||
|
document.add(new com.lowagie.text.Paragraph("USt-IdNr.: " + safe(ustIdField)));
|
||||||
|
document.add(new com.lowagie.text.Paragraph("Steuernummer: " + safe(taxNumberField)));
|
||||||
|
document.add(new com.lowagie.text.Paragraph("Bankname: " + safe(bankNameField)));
|
||||||
|
document.add(new com.lowagie.text.Paragraph("IBAN: " + safe(ibanField)));
|
||||||
|
document.add(new com.lowagie.text.Paragraph("Steuersatz: " + safe(taxRateField)));
|
||||||
|
document.add(new com.lowagie.text.Paragraph(" "));
|
||||||
|
document.add(new com.lowagie.text.Paragraph("Einleitungstext:"));
|
||||||
|
document.add(new com.lowagie.text.Paragraph(safe(introTextArea)));
|
||||||
|
document.add(new com.lowagie.text.Paragraph(" "));
|
||||||
|
document.add(new com.lowagie.text.Paragraph("Zahlungsbedingungen:"));
|
||||||
|
document.add(new com.lowagie.text.Paragraph(safe(termsTextArea)));
|
||||||
|
document.close();
|
||||||
|
return out.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("PDF-Generierung fehlgeschlagen: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility: safe getter für TextField/TextArea
|
||||||
|
private String safe(TextField f) { return f != null && f.getValue() != null ? f.getValue() : ""; }
|
||||||
|
private String safe(TextArea f) { return f != null && f.getValue() != null ? f.getValue() : ""; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user