Compare commits
2 Commits
c358a43bcf
...
af43204063
| Author | SHA1 | Date | |
|---|---|---|---|
| af43204063 | |||
| 8a6b444003 |
@ -1,4 +1,4 @@
|
|||||||
@import "../common.less";
|
@import "../styles/config";
|
||||||
|
|
||||||
#mainMenu {
|
#mainMenu {
|
||||||
border-bottom: 1px solid black;
|
border-bottom: 1px solid black;
|
||||||
@ -19,12 +19,4 @@
|
|||||||
background-color: lightskyblue;
|
background-color: lightskyblue;
|
||||||
}
|
}
|
||||||
|
|
||||||
.userHasPassword {
|
|
||||||
color: green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.userNoPassword {
|
|
||||||
color: indianred;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="tileContent">
|
<div class="tileContent">
|
||||||
<table>
|
<table>
|
||||||
<tr [class.user_owner]="group.isOwner(user)" *ngFor="let user of group.usersByNameOwnerFirst()">
|
<tr *ngFor="let user of group.usersByNameOwnerFirst()">
|
||||||
<td (click)="userService.goto(user)">
|
<td (click)="userService.goto(user)">
|
||||||
{{ user.name }}
|
{{ user.name }}
|
||||||
</td>
|
</td>
|
||||||
@ -77,7 +77,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="tileContent">
|
<div class="tileContent">
|
||||||
<table>
|
<table>
|
||||||
<tr [class.user_owner]="group.isOwner(user)" *ngFor="let user of group.bannedByName()">
|
<tr *ngFor="let user of group.bannedByName()">
|
||||||
<td (click)="userService.goto(user)">
|
<td (click)="userService.goto(user)">
|
||||||
{{ user.name }}
|
{{ user.name }}
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
@import "../../../../common.less";
|
@import "../../../../styles/config";
|
||||||
|
|
||||||
th {
|
th {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
@import "../../../../common.less";
|
@import "../../../../styles/config";
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
@import "../../../../../common.less";
|
@import "../../../../../styles/config";
|
||||||
|
|
||||||
td {
|
td {
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
@import "../../../styles/config";
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
@import "../../../../styles/config";
|
||||||
|
|
||||||
.huge {
|
.huge {
|
||||||
font-size: 50vmin;
|
font-size: 50vmin;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
@import "../../../../../common.less";
|
@import "../../../../../styles/config";
|
||||||
|
|
||||||
@page {
|
@page {
|
||||||
size: A4;
|
size: A4;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
@import "../../../../common.less";
|
@import "../../../../styles/config";
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
@import "../../../../common.less";
|
@import "../../../../styles/config";
|
||||||
|
|
||||||
#VoltageDropInputs {
|
#VoltageDropInputs {
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
@import "../../../common.less";
|
@import "../../../styles/config";
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
@import "../../../common";
|
@import "../../../styles/config";
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
@import "../../../common.less";
|
@import "../../../styles/config";
|
||||||
|
|
||||||
.unsaved {
|
.unsaved {
|
||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
|
|||||||
@ -1,89 +1,4 @@
|
|||||||
@import "./common.less";
|
@import "./styles/basic";
|
||||||
|
@import "./styles/tiles";
|
||||||
body {
|
@import "./styles/button";
|
||||||
font-family: sans-serif;
|
@import "./styles/user";
|
||||||
font-size: 4vw;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
all: unset;
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid lightgray;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tileContainer {
|
|
||||||
padding: @halfSpace;
|
|
||||||
|
|
||||||
.tile {
|
|
||||||
width: 100%;
|
|
||||||
padding: @halfSpace;
|
|
||||||
|
|
||||||
.tileInner {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: @space;
|
|
||||||
background-color: #fbfbfb;
|
|
||||||
|
|
||||||
.tileTitle {
|
|
||||||
font-weight: bold;
|
|
||||||
padding: @halfSpace @space;
|
|
||||||
background-color: lightskyblue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tileContent {
|
|
||||||
padding: @halfSpace;
|
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
white-space: nowrap;
|
|
||||||
border: 0.2em solid white;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.tileFooter {
|
|
||||||
padding: @halfSpace @space;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1000px) {
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tileContainer {
|
|
||||||
|
|
||||||
.tile {
|
|
||||||
float: left;
|
|
||||||
width: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
38
src/main/angular/src/styles/basic.less
Normal file
38
src/main/angular/src/styles/basic.less
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
@import "./config";
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 4vw;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
all: unset;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1000px) {
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
@import "./user.less";
|
@import "./config";
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
margin-bottom: @halfSpace;
|
margin-bottom: @halfSpace;
|
||||||
60
src/main/angular/src/styles/tiles.less
Normal file
60
src/main/angular/src/styles/tiles.less
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
@import "./config";
|
||||||
|
|
||||||
|
.tileContainer {
|
||||||
|
padding: @halfSpace;
|
||||||
|
|
||||||
|
.tile {
|
||||||
|
width: 100%;
|
||||||
|
padding: @halfSpace;
|
||||||
|
|
||||||
|
.tileInner {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: @space;
|
||||||
|
background-color: #fbfbfb;
|
||||||
|
|
||||||
|
.tileTitle {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: @halfSpace @space;
|
||||||
|
background-color: lightskyblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tileContent {
|
||||||
|
padding: @halfSpace;
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0.2em solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.tileFooter {
|
||||||
|
padding: @halfSpace @space;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1000px) {
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tileContainer {
|
||||||
|
|
||||||
|
.tile {
|
||||||
|
float: left;
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
9
src/main/angular/src/styles/user.less
Normal file
9
src/main/angular/src/styles/user.less
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
@import "./config";
|
||||||
|
|
||||||
|
.userHasPassword {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userNoPassword {
|
||||||
|
color: indianred;
|
||||||
|
}
|
||||||
@ -1,18 +0,0 @@
|
|||||||
@import "./config.less";
|
|
||||||
|
|
||||||
.user {
|
|
||||||
float: left;
|
|
||||||
padding: @halfSpace;
|
|
||||||
background-color: lightskyblue;
|
|
||||||
border-radius: @halfSpace;
|
|
||||||
margin-right: @halfSpace;
|
|
||||||
margin-bottom: @halfSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user_owner {
|
|
||||||
border: 1px solid black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user:hover {
|
|
||||||
background-color: dodgerblue;
|
|
||||||
}
|
|
||||||
28
src/main/java/de/ph87/tools/common/EmailHelper.java
Normal file
28
src/main/java/de/ph87/tools/common/EmailHelper.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package de.ph87.tools.common;
|
||||||
|
|
||||||
|
import lombok.NonNull;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class EmailHelper {
|
||||||
|
|
||||||
|
private static final Pattern REGEX = Pattern.compile("(?<username>(?<usernameFirst>[^@])[^@]*)@(?<domain>(?<domainFirst>[^.]).*(?<tld>\\.[^.]+))");
|
||||||
|
|
||||||
|
public static boolean isEmailValid(@NonNull final String email) {
|
||||||
|
return REGEX.matcher(email).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static String obfuscateEmail(@NonNull final String email) {
|
||||||
|
final Matcher matcher = REGEX.matcher(email);
|
||||||
|
if (!matcher.find()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
final String usernameFirst = matcher.group("usernameFirst");
|
||||||
|
final String domainFirst = matcher.group("domainFirst");
|
||||||
|
final String tld = matcher.group("tld");
|
||||||
|
return "%s...@%s...%s".formatted(usernameFirst, domainFirst, tld);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -72,6 +72,12 @@ public class User extends UserPublicAbstract {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String password = "";
|
private String password = "";
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@NonNull
|
||||||
|
@ToString.Exclude
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String email = "";
|
||||||
|
|
||||||
public User(@NonNull final String name) {
|
public User(@NonNull final String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,6 +49,12 @@ public class UserController {
|
|||||||
return userService.changePassword(userUuid, password);
|
return userService.changePassword(userUuid, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@PostMapping("changeEmail")
|
||||||
|
public UserPrivateDto changeEmail(@NonNull final UserPrivateUuid userUuid, @NonNull @RequestBody final String email) {
|
||||||
|
return userService.changeEmail(userUuid, email);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("delete")
|
@GetMapping("delete")
|
||||||
public void delete(@NonNull final UserPrivateUuid userUuid, @NonNull final HttpServletResponse response) {
|
public void delete(@NonNull final UserPrivateUuid userUuid, @NonNull final HttpServletResponse response) {
|
||||||
userService.delete(userUuid, response);
|
userService.delete(userUuid, response);
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import lombok.ToString;
|
|||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
|
import static de.ph87.tools.common.EmailHelper.obfuscateEmail;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@ToString
|
@ToString
|
||||||
public class UserPrivateDto extends UserPublicAbstract {
|
public class UserPrivateDto extends UserPublicAbstract {
|
||||||
@ -35,6 +37,8 @@ public class UserPrivateDto extends UserPublicAbstract {
|
|||||||
|
|
||||||
private final boolean password;
|
private final boolean password;
|
||||||
|
|
||||||
|
private final String email;
|
||||||
|
|
||||||
private final boolean admin;
|
private final boolean admin;
|
||||||
|
|
||||||
public UserPrivateDto(@NonNull final User user) {
|
public UserPrivateDto(@NonNull final User user) {
|
||||||
@ -43,6 +47,7 @@ public class UserPrivateDto extends UserPublicAbstract {
|
|||||||
this.name = user.getName();
|
this.name = user.getName();
|
||||||
this.created = user.getCreated();
|
this.created = user.getCreated();
|
||||||
this.password = !user.getPassword().isEmpty();
|
this.password = !user.getPassword().isEmpty();
|
||||||
|
this.email = obfuscateEmail(user.getEmail());
|
||||||
this.admin = user.isAdmin();
|
this.admin = user.isAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import java.util.Random;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static de.ph87.tools.common.EmailHelper.isEmailValid;
|
||||||
import static de.ph87.tools.user.uuid.UserPrivateUuidArgumentResolver.USER_UUID_COOKIE_NAME;
|
import static de.ph87.tools.user.uuid.UserPrivateUuidArgumentResolver.USER_UUID_COOKIE_NAME;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -131,6 +132,18 @@ public class UserService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public UserPrivateDto changeEmail(@NonNull final UserPrivateUuid privateUuid, @NonNull final String email) {
|
||||||
|
return modify(privateUuid, user -> {
|
||||||
|
if (!isEmailValid(email)) {
|
||||||
|
log.warn("Cannot change User email: not valid, user={}", user);
|
||||||
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
user.setEmail(email);
|
||||||
|
log.info("User email changed: user={}", user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void delete(@NonNull final UserPrivateUuid privateUuid, @NonNull final HttpServletResponse response) {
|
public void delete(@NonNull final UserPrivateUuid privateUuid, @NonNull final HttpServletResponse response) {
|
||||||
final User user = userRepository.findByPrivateUuid(privateUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
final User user = userRepository.findByPrivateUuid(privateUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||||
deleteUnchecked(response, user);
|
deleteUnchecked(response, user);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user