Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added context-path support #103

Merged
merged 2 commits into from
Oct 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,32 @@ spring:

server:
port: 8081
servlet:
context-path: /
```

#### Change the Context-Path

The context-path or base-path of the application can be changed using the following property:

```
server:
servlet:
context-path: /tasklist/
```

It is then available under http://localhost:8081/tasklist.

#### Customize the Look & Feel

You can customize the look & feel of the Zeebe Simple Tasklist (aka. white-labeling). For example, to change the logo or
alter the background color. The following configurations are available:

```
- white-label.logo.path=img/logo.png
- white-label.custom.title=Zeebe Simple Tasklist
- white-label.custom.css.path=css/custom.css
- white-label.custom.js.path=js/custom.js
```

#### Change the Database
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@

<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
<version>0.52</version>
<artifactId>webjars-locator</artifactId>
<version>0.42</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/zeebe/tasklist/WebSecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**", "/img/**", "/js/**", "/fonts/**", "/favicon.ico")
.antMatchers("/css/**", "/img/**", "/js/**", "/fonts/**", "/favicon.ico", "/webjars/**")
.permitAll()
.antMatchers("/login", "/login-error")
.antMatchers("/login", "/login-error", "/notifications/**")
.permitAll()
.antMatchers("/views/users", "/api/users", "/views/groups", "/api/groups")
.hasRole(Roles.ADMIN.name())
Expand Down
59 changes: 59 additions & 0 deletions src/main/java/io/zeebe/tasklist/view/AbstractViewController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.zeebe.tasklist.view;

import io.zeebe.tasklist.Roles;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

abstract class AbstractViewController {

private static final int FIRST_PAGE = 0;
private static final int PAGE_RANGE = 2;

@Autowired private WhitelabelProperties whitelabelProperties;
@Autowired private WhitelabelPropertiesMapper whitelabelPropertiesMapper;

protected void addPaginationToModel(
final Map<String, Object> model, final Pageable pageable, final long count) {

final int currentPage = pageable.getPageNumber();
model.put("currentPage", currentPage);
model.put("page", currentPage + 1);
if (currentPage > 0) {
model.put("prevPage", currentPage - 1);
}
if (count > (1 + currentPage) * pageable.getPageSize()) {
model.put("nextPage", currentPage + 1);
}
}

/*
* Needs to be added manually, since Spring does not detect @ModelAttribute in abstract classes.
*/
protected void addDefaultAttributesToModel(Map<String, Object> model) {
whitelabelPropertiesMapper.addPropertiesToModel(model, whitelabelProperties);
}

protected void addCommonsToModel(Map<String, Object> model) {

final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
final List<String> authorities =
authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());

final UserDto userDto = new UserDto();
userDto.setName(authentication.getName());

final boolean isAdmin = authorities.contains("ROLE_" + Roles.ADMIN);
userDto.setAdmin(isAdmin);

model.put("user", userDto);
}
}
18 changes: 18 additions & 0 deletions src/main/java/io/zeebe/tasklist/view/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.zeebe.tasklist.view;

public class ErrorMessage {

private String message;

public ErrorMessage(String message) {
this.message = message;
}

public String getMessage() {
return message;
}

public void setMessage(final String message) {
this.message = message;
}
}
50 changes: 50 additions & 0 deletions src/main/java/io/zeebe/tasklist/view/ExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.zeebe.tasklist.view;


import io.camunda.zeebe.client.api.command.ClientException;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class ExceptionHandler {

private static final Logger LOG = LoggerFactory.getLogger(ExceptionHandler.class);

private final WhitelabelProperties whitelabelProperties;
private final WhitelabelPropertiesMapper whitelabelPropertiesMapper;

public ExceptionHandler(
WhitelabelProperties whitelabelProperties,
WhitelabelPropertiesMapper whitelabelPropertiesMapper) {
this.whitelabelProperties = whitelabelProperties;
this.whitelabelPropertiesMapper = whitelabelPropertiesMapper;
}

@org.springframework.web.bind.annotation.ExceptionHandler(value = {ClientException.class})
protected ResponseEntity<Object> handleZeebeClientException(
final RuntimeException ex, final WebRequest request) {
LOG.debug("Zeebe Client Exception caught and forwarding to UI.", ex);
return ResponseEntity.status(HttpStatus.FAILED_DEPENDENCY)
.contentType(MediaType.APPLICATION_JSON)
.body(new ErrorMessage(ex.getMessage()));
}

@org.springframework.web.bind.annotation.ExceptionHandler(RuntimeException.class)
public String handleRuntimeException(final RuntimeException exc, final Model model) {
LOG.error(exc.getMessage(), exc);

model.addAttribute("error", exc.getClass().getSimpleName());
model.addAttribute("message", exc.getMessage());
model.addAttribute("trace", ExceptionUtils.getStackTrace(exc));

whitelabelPropertiesMapper.addPropertiesToModel(model, whitelabelProperties);
return "error";
}
}
6 changes: 4 additions & 2 deletions src/main/java/io/zeebe/tasklist/view/LoginController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {
public class LoginController extends AbstractViewController {

@GetMapping("/login")
public String login(Map<String, Object> model) {

addDefaultAttributesToModel(model);
return "login";
}

@GetMapping("/login-error")
public String loginError(Map<String, Object> model) {

model.put("error", "Username or password is invalid.");

addDefaultAttributesToModel(model);
return "login";
}
}
12 changes: 9 additions & 3 deletions src/main/java/io/zeebe/tasklist/view/NotificationService.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
package io.zeebe.tasklist.view;

import io.zeebe.tasklist.repository.TaskRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;

@Controller
public class NotificationService {

private final String basePath;

public NotificationService(@Value("${server.servlet.context-path}") final String basePath) {
this.basePath = basePath.endsWith("/") ? basePath : basePath + "/";
}

@Autowired private SimpMessagingTemplate webSocket;

public void sendNewTask() {
final TaskNotification notification = new TaskNotification("new tasks");

webSocket.convertAndSend("/notifications/tasks", notification);
webSocket.convertAndSend(basePath +"notifications/tasks", notification);
}

public void sendTaskCanceled() {
final TaskNotification notification = new TaskNotification("tasks canceled");

webSocket.convertAndSend("/notifications/tasks", notification);
webSocket.convertAndSend(basePath +"notifications/tasks", notification);
}
}
70 changes: 19 additions & 51 deletions src/main/java/io/zeebe/tasklist/view/ViewController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,36 @@

import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import io.zeebe.tasklist.Roles;
import io.zeebe.tasklist.TaskDataSerializer;
import io.zeebe.tasklist.entity.GroupEntity;
import io.zeebe.tasklist.entity.TaskEntity;
import io.zeebe.tasklist.entity.UserEntity;
import io.zeebe.tasklist.repository.GroupRepository;
import io.zeebe.tasklist.repository.TaskRepository;
import io.zeebe.tasklist.repository.UserRepository;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.servlet.view.RedirectView;

import javax.annotation.PostConstruct;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

@Controller
public class ViewController {
public class ViewController extends AbstractViewController {

private final TaskDataSerializer serializer = new TaskDataSerializer();

Expand Down Expand Up @@ -73,7 +66,7 @@ public void loadTemplate() {

@GetMapping("/")
public RedirectView index() {
return new RedirectView("/views/all-tasks/");
return new RedirectView("./views/all-tasks/");
}

@GetMapping("/views/my-tasks")
Expand All @@ -91,6 +84,7 @@ public String taskList(Map<String, Object> model, @PageableDefault(size = 10) Pa
model.put("count", count);

addPaginationToModel(model, pageable, count);
addDefaultAttributesToModel(model);
addCommonsToModel(model);

return "task-list-view";
Expand Down Expand Up @@ -127,6 +121,7 @@ public String taskList(
model.put("count", count);

addPaginationToModel(model, pageable, count);
addDefaultAttributesToModel(model);
addCommonsToModel(model);

return "task-list-view";
Expand Down Expand Up @@ -230,6 +225,7 @@ public String allTaskList(
model.put("count", count);

addPaginationToModel(model, pageable, count);
addDefaultAttributesToModel(model);
addCommonsToModel(model);

return "task-list-view";
Expand Down Expand Up @@ -266,6 +262,7 @@ public String allTaskList(
model.put("count", count);

addPaginationToModel(model, pageable, count);
addDefaultAttributesToModel(model);
addCommonsToModel(model);

return "task-list-view";
Expand Down Expand Up @@ -300,6 +297,7 @@ public String userList(Map<String, Object> model, @PageableDefault(size = 10) Pa
model.put("availableGroups", groupNames);

addPaginationToModel(model, pageable, count);
addDefaultAttributesToModel(model);
addCommonsToModel(model);

return "user-view";
Expand All @@ -320,42 +318,12 @@ public String groupList(
model.put("count", count);

addPaginationToModel(model, pageable, count);
addDefaultAttributesToModel(model);
addCommonsToModel(model);

return "group-view";
}

private void addPaginationToModel(
Map<String, Object> model, Pageable pageable, final long count) {

final int currentPage = pageable.getPageNumber();
model.put("currentPage", currentPage);
model.put("page", currentPage + 1);
if (currentPage > 0) {
model.put("prevPage", currentPage - 1);
}
if (count > (1 + currentPage) * pageable.getPageSize()) {
model.put("nextPage", currentPage + 1);
}
}

private void addCommonsToModel(Map<String, Object> model) {

final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
final List<String> authorities =
authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());

final UserDto userDto = new UserDto();
userDto.setName(authentication.getName());

final boolean isAdmin = authorities.contains("ROLE_" + Roles.ADMIN);
userDto.setAdmin(isAdmin);

model.put("user", userDto);
}

private String getUsername() {
final String username = SecurityContextHolder.getContext().getAuthentication().getName();
return username;
Expand Down
Loading