mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-12 10:35:03 +00:00
general fixes and changes
This commit is contained in:
parent
71b126104e
commit
1fac660bf8
@ -0,0 +1,20 @@
|
||||
package stirling.software.proprietary.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class TeamWithUserCountDTO {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Long userCount;
|
||||
|
||||
// Constructor for JPQL projection
|
||||
public TeamWithUserCountDTO(Long id, String name, Long userCount) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.userCount = userCount;
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.model.enumeration.Role;
|
||||
import stirling.software.common.model.exception.UnsupportedProviderException;
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
||||
import stirling.software.proprietary.security.model.AuthenticationType;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
import stirling.software.proprietary.security.model.api.user.UsernameAndPass;
|
||||
@ -54,7 +55,7 @@ public class UserController {
|
||||
private final SessionPersistentRegistry sessionRegistry;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final TeamRepository teamRepository;
|
||||
|
||||
private final UserRepository userRepository;
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@PostMapping("/register")
|
||||
public String register(@ModelAttribute UsernameAndPass requestModel, Model model)
|
||||
@ -210,6 +211,7 @@ public class UserController {
|
||||
@RequestParam(name = "username", required = true) String username,
|
||||
@RequestParam(name = "password", required = false) String password,
|
||||
@RequestParam(name = "role") String role,
|
||||
@RequestParam(name = "teamId", required = false) Long teamId,
|
||||
@RequestParam(name = "authType") String authType,
|
||||
@RequestParam(name = "forceChange", required = false, defaultValue = "false")
|
||||
boolean forceChange)
|
||||
@ -243,14 +245,23 @@ public class UserController {
|
||||
// If the role ID is not valid, redirect with an error message
|
||||
return new RedirectView("/adminSettings?messageType=invalidRole", true);
|
||||
}
|
||||
Team team = teamRepository.findByName(TeamService.DEFAULT_TEAM_NAME).orElse(null);
|
||||
|
||||
// Use teamId if provided, otherwise use default team
|
||||
Long effectiveTeamId = teamId;
|
||||
if (effectiveTeamId == null) {
|
||||
Team defaultTeam = teamRepository.findByName(TeamService.DEFAULT_TEAM_NAME).orElse(null);
|
||||
if (defaultTeam != null) {
|
||||
effectiveTeamId = defaultTeam.getId();
|
||||
}
|
||||
}
|
||||
|
||||
if (authType.equalsIgnoreCase(AuthenticationType.SSO.toString())) {
|
||||
userService.saveUser(username, AuthenticationType.SSO, team, role);
|
||||
userService.saveUser(username, AuthenticationType.SSO, effectiveTeamId, role);
|
||||
} else {
|
||||
if (password.isBlank()) {
|
||||
return new RedirectView("/adminSettings?messageType=invalidPassword", true);
|
||||
}
|
||||
userService.saveUser(username, password, team, role, forceChange);
|
||||
userService.saveUser(username, password, effectiveTeamId, role, forceChange);
|
||||
}
|
||||
return new RedirectView(
|
||||
"/adminSettings", // Redirect to account page after adding the user
|
||||
@ -262,6 +273,7 @@ public class UserController {
|
||||
public RedirectView changeRole(
|
||||
@RequestParam(name = "username") String username,
|
||||
@RequestParam(name = "role") String role,
|
||||
@RequestParam(name = "teamId", required = false) Long teamId,
|
||||
Authentication authentication)
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
||||
@ -289,6 +301,15 @@ public class UserController {
|
||||
return new RedirectView("/adminSettings?messageType=invalidRole", true);
|
||||
}
|
||||
User user = userOpt.get();
|
||||
|
||||
// Update the team if a teamId is provided
|
||||
if (teamId != null) {
|
||||
teamRepository.findById(teamId).ifPresent(team -> {
|
||||
user.setTeam(team);
|
||||
userRepository.save(user);
|
||||
});
|
||||
}
|
||||
|
||||
userService.changeRole(user, role);
|
||||
return new RedirectView(
|
||||
"/adminSettings", // Redirect to account page after adding the user
|
||||
|
@ -2,7 +2,6 @@ package stirling.software.proprietary.security.controller.web;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -14,8 +13,10 @@ import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.model.dto.TeamWithUserCountDTO;
|
||||
import stirling.software.proprietary.security.database.repository.SessionRepository;
|
||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
@ -24,17 +25,18 @@ import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
@Controller
|
||||
@RequestMapping("/teams")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class TeamWebController {
|
||||
|
||||
private final TeamRepository teamRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final SessionRepository sessionRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
public String listTeams(Model model) {
|
||||
// Get all teams with their users
|
||||
List<Team> teams = teamRepository.findAllWithUsers();
|
||||
// Get teams with user counts using a DTO projection
|
||||
List<TeamWithUserCountDTO> teamsWithCounts = teamRepository.findAllTeamsWithUserCount();
|
||||
|
||||
// Get the latest activity for each team
|
||||
List<Object[]> teamActivities = sessionRepository.findLatestActivityByTeam();
|
||||
@ -42,45 +44,41 @@ public class TeamWebController {
|
||||
// Convert the query results to a map for easy access in the view
|
||||
Map<Long, Date> teamLastRequest = new HashMap<>();
|
||||
for (Object[] result : teamActivities) {
|
||||
// For JPQL query with aliases
|
||||
Long teamId = (Long) result[0]; // teamId alias
|
||||
Date lastActivity = (Date) result[1]; // lastActivity alias
|
||||
|
||||
teamLastRequest.put(teamId, lastActivity);
|
||||
}
|
||||
|
||||
model.addAttribute("teams", teams);
|
||||
// Add data to the model
|
||||
model.addAttribute("teamsWithCounts", teamsWithCounts);
|
||||
model.addAttribute("teamLastRequest", teamLastRequest);
|
||||
|
||||
return "enterprise/teams";
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
public String viewTeamDetails(@PathVariable("id") Long id, Model model) {
|
||||
// Get the team with its users
|
||||
Team team =
|
||||
teamRepository
|
||||
.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Team not found"));
|
||||
|
||||
List<User> members = userRepository.findAllByTeam(team);
|
||||
team.setUsers(new HashSet<>(members));
|
||||
|
||||
// Get the team
|
||||
Team team = teamRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Team not found"));
|
||||
|
||||
// Get users for this team directly using the direct query
|
||||
List<User> teamUsers = userRepository.findAllByTeamId(id);
|
||||
|
||||
// Get the latest session for each user in the team
|
||||
List<Object[]> userSessions = sessionRepository.findLatestSessionByTeamId(id);
|
||||
|
||||
// Create a map of username to last request date
|
||||
Map<String, Date> userLastRequest = new HashMap<>();
|
||||
|
||||
// Process results from JPQL query
|
||||
for (Object[] result : userSessions) {
|
||||
String username = (String) result[0]; // username alias
|
||||
Date lastRequest = (Date) result[1]; // lastRequest alias
|
||||
|
||||
userLastRequest.put(username, lastRequest);
|
||||
}
|
||||
|
||||
model.addAttribute("team", team);
|
||||
model.addAttribute("teamUsers", teamUsers);
|
||||
model.addAttribute("userLastRequest", userLastRequest);
|
||||
return "enterprise/team-details";
|
||||
}
|
||||
|
@ -27,8 +27,11 @@ public interface UserRepository extends JpaRepository<User, Long> {
|
||||
@Query("SELECT u FROM User u WHERE u.team IS NULL")
|
||||
List<User> findAllWithoutTeam();
|
||||
|
||||
@Query("SELECT u FROM User u LEFT JOIN FETCH u.team")
|
||||
@Query(value = "SELECT u FROM User u LEFT JOIN FETCH u.team")
|
||||
List<User> findAllWithTeam();
|
||||
|
||||
@Query("SELECT u FROM User u JOIN FETCH u.authorities WHERE u.team.id = :teamId")
|
||||
List<User> findAllByTeamId(@Param("teamId") Long teamId);
|
||||
|
||||
long countByTeam(Team team);
|
||||
|
||||
|
@ -5,16 +5,19 @@ import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.model.dto.TeamWithUserCountDTO;
|
||||
|
||||
@Repository
|
||||
public interface TeamRepository extends JpaRepository<Team, Long> {
|
||||
Optional<Team> findByName(String name);
|
||||
|
||||
@Query("SELECT t FROM Team t LEFT JOIN FETCH t.users")
|
||||
List<Team> findAllWithUsers();
|
||||
@Query("SELECT new stirling.software.proprietary.model.dto.TeamWithUserCountDTO(t.id, t.name, COUNT(u)) " +
|
||||
"FROM Team t LEFT JOIN t.users u GROUP BY t.id, t.name")
|
||||
List<TeamWithUserCountDTO> findAllTeamsWithUserCount();
|
||||
|
||||
boolean existsByNameIgnoreCase(String name);
|
||||
}
|
||||
|
@ -24,13 +24,9 @@
|
||||
|
||||
<div class="data-body">
|
||||
<div class="data-stats">
|
||||
<div class="data-stat-card">
|
||||
<div class="data-stat-label">Team ID:</div>
|
||||
<div class="data-stat-value" th:text="${team.id}">1</div>
|
||||
</div>
|
||||
<div class="data-stat-card">
|
||||
<div class="data-stat-label">Total Members:</div>
|
||||
<div class="data-stat-value" th:text="${team.users.size()}">1</div>
|
||||
<div class="data-stat-value" th:text="${teamUsers.size()}">1</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -55,7 +51,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="user : ${team.users}">
|
||||
<tr th:each="user : ${teamUsers}">
|
||||
<td th:text="${user.id}">1</td>
|
||||
<td th:text="${user.username}">username</td>
|
||||
<td th:text="#{${user.roleName}}">Role</td>
|
||||
@ -76,7 +72,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Empty state for when there are no team members -->
|
||||
<div th:if="${team.users.empty}" class="data-empty">
|
||||
<div th:if="${teamUsers.empty}" class="data-empty">
|
||||
<span class="material-symbols-rounded data-empty-icon">person_off</span>
|
||||
<p class="data-empty-text">This team has no members yet.</p>
|
||||
<a th:href="@{'/admin/users'}" class="data-btn data-btn-primary">
|
||||
|
@ -23,6 +23,14 @@
|
||||
</div>
|
||||
|
||||
<div class="data-body">
|
||||
<!-- Back Button -->
|
||||
<div class="data-actions data-actions-start">
|
||||
<a href="/adminSettings" class="data-btn data-btn-secondary">
|
||||
<span class="material-symbols-rounded">arrow_back</span>
|
||||
<span>Back to Settings</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Create New Team Button -->
|
||||
<div class="data-actions">
|
||||
<a href="#"
|
||||
@ -47,18 +55,19 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="team : ${teams}">
|
||||
<td th:text="${team.name}"></td>
|
||||
<td th:text="${team.users.size()}"></td>
|
||||
<td th:text="${@runningProOrHigher} ? (${teamLastRequest[team.id] != null ? #dates.format(teamLastRequest[team.id], 'yyyy-MM-dd HH:mm:ss') : 'N/A'}) : 'hidden'"></td>
|
||||
<!-- Try approach 1 - DTO projection -->
|
||||
<tr th:each="teamDto : ${teamsWithCounts}">
|
||||
<td th:text="${teamDto.name}"></td>
|
||||
<td th:text="${teamDto.userCount}"></td>
|
||||
<td th:text="${@runningProOrHigher} ? (${teamLastRequest[teamDto.id] != null ? #dates.format(teamLastRequest[teamDto.id], 'yyyy-MM-dd HH:mm:ss') : 'N/A'}) : 'hidden'"></td>
|
||||
<td>
|
||||
<div class="data-action-cell">
|
||||
<a th:href="@{'/teams/' + ${team.id}}" class="data-btn data-btn-secondary data-btn-sm" th:title="#{adminUserSettings.viewTeam}">
|
||||
<a th:href="@{'/teams/' + ${teamDto.id}}" class="data-btn data-btn-secondary data-btn-sm" th:title="#{adminUserSettings.viewTeam}">
|
||||
<span class="material-symbols-rounded">search</span> <span th:text="#{view}">View</span>
|
||||
</a>
|
||||
<form th:action="@{'/api/v1/team/delete'}" method="post" style="display:inline-block"
|
||||
onsubmit="return confirmDeleteTeam()">
|
||||
<input type="hidden" name="teamId" th:value="${team.id}" />
|
||||
<input type="hidden" name="teamId" th:value="${teamDto.id}" />
|
||||
<button type="submit" class="data-btn data-btn-danger data-btn-sm"
|
||||
th:disabled="${!@runningProOrHigher}"
|
||||
th:classappend="${!@runningProOrHigher} ? 'disabled' : ''"
|
||||
|
Loading…
x
Reference in New Issue
Block a user