diff --git a/README.md b/README.md index 785a9752..83b9c9ba 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,6 @@ A tool for extending [SonarQube](https://www.sonarqube.org/) by a gamification s * more about SonarQuest's game concepts and application scenarios: https://www.viadee.de/sonarquest_en * Sonar Quest info package: https://github.com/viadee/sonarQuest/blob/master/docs/SonarQuest_info_package.pdf -## News - -*2019-02-27* - -German SonarQuest Coders are invited to the 1st Coding Night: https://lp.viadee.de/sonar-quest-coding-night-anmeldung -Coding Night starts at 1800 hrs today! (Chris) - ## Goal Reduce technical debts in your code project by converting the refactoring process into a game. Create quests from SonarQube issues and write your own story. diff --git a/sonarQuest-backend/src/main/java/com/viadee/sonarquest/controllers/login/LoginController.java b/sonarQuest-backend/src/main/java/com/viadee/sonarquest/controllers/login/LoginController.java index ce2ff2e8..40c02b1c 100644 --- a/sonarQuest-backend/src/main/java/com/viadee/sonarquest/controllers/login/LoginController.java +++ b/sonarQuest-backend/src/main/java/com/viadee/sonarquest/controllers/login/LoginController.java @@ -20,6 +20,7 @@ import com.viadee.sonarquest.controllers.PathConstants; import com.viadee.sonarquest.security.JwtHelper; +import com.viadee.sonarquest.services.UserService; @RestController @RequestMapping(PathConstants.LOGIN_URL) @@ -33,6 +34,9 @@ public class LoginController { @Autowired private JwtHelper jwtHelper; + @Autowired + private UserService userService; + @GetMapping public String info() { return "Dies ist eine Login Seite"; @@ -41,10 +45,11 @@ public String info() { @PostMapping public Token login(@Valid @RequestBody final UserCredentials credentials) { - String username = credentials.getUsername(); + final String username = credentials.getUsername(); LOGGER.info("Log-In request received from user {}", Objects.hashCode(username)); final User authenticatedUser = authentificateUser(credentials); final Token token = createTokenForUser(authenticatedUser); + userService.updateLastLogin(authenticatedUser.getUsername()); LOGGER.info("Log-In request successful for user {}", Objects.hashCode(username)); return token; } @@ -55,12 +60,12 @@ private User authentificateUser(final UserCredentials credentials) { } private Authentication authenticate(final UserCredentials credentials) { - String username = credentials.getUsername(); - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, + final String username = credentials.getUsername(); + final UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, credentials.getPassword()); try { return authenticationManager.authenticate(authToken); - } catch (BadCredentialsException ex) { + } catch (final BadCredentialsException ex) { LOGGER.warn(String.format("Log-In request denied with bad credentials for user %s", Objects.hashCode(username))); throw ex; diff --git a/sonarQuest-backend/src/main/java/com/viadee/sonarquest/entities/User.java b/sonarQuest-backend/src/main/java/com/viadee/sonarquest/entities/User.java index d97dbadf..46e9436f 100644 --- a/sonarQuest-backend/src/main/java/com/viadee/sonarquest/entities/User.java +++ b/sonarQuest-backend/src/main/java/com/viadee/sonarquest/entities/User.java @@ -1,5 +1,6 @@ package com.viadee.sonarquest.entities; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -26,268 +27,278 @@ @Table(name = "SQUser") public class User { - @Id - @GeneratedValue - private Long id; + @Id + @GeneratedValue + private Long id; - @Column(name = "username") - private String username; + @Column(name = "username") + private String username; - @Column(name = "password") - private String password; + @Column(name = "password") + private String password; - @ManyToOne - @JoinColumn(name = "role_id") - private Role role; + @ManyToOne + @JoinColumn(name = "role_id") + private Role role; - @Column(name = "picture") - private String picture; + @Column(name = "picture") + private String picture; - @Column(name = "about_me") - private String aboutMe; + @Column(name = "about_me") + private String aboutMe; - @ManyToOne - @JoinColumn(name = "avatar_class_id") - private AvatarClass avatarClass; + @ManyToOne + @JoinColumn(name = "avatar_class_id") + private AvatarClass avatarClass; - @ManyToOne - @JoinColumn(name = "avatar_race_id") - private AvatarRace avatarRace; + @ManyToOne + @JoinColumn(name = "avatar_race_id") + private AvatarRace avatarRace; - @Column(name = "gold") - private Long gold; + @Column(name = "gold") + private Long gold; - @Column(name = "xp") - private Long xp; + @Column(name = "xp") + private Long xp; - @ManyToOne - @JoinColumn(name = "level_id") - private Level level; + @ManyToOne + @JoinColumn(name = "level_id") + private Level level; - @ManyToOne - @JoinColumn(name = "current_world_id") - private World currentWorld; + @ManyToOne + @JoinColumn(name = "current_world_id") + private World currentWorld; - @JsonIgnore - @ManyToMany(cascade = CascadeType.ALL) - @JoinTable(name = "User_To_World", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "world_id", referencedColumnName = "id")) - private List worlds = new ArrayList<>(0); + @Column(name = "last_login") + private Timestamp lastLogin; - @ManyToMany(cascade = CascadeType.ALL) - @JoinTable(name = "User_Artefact", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "artefact_id", referencedColumnName = "id")) - private List artefacts = new ArrayList<>(0); + @JsonIgnore + @ManyToMany(cascade = CascadeType.ALL) + @JoinTable(name = "User_To_World", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "world_id", referencedColumnName = "id")) + private List worlds = new ArrayList<>(0); - @JsonIgnore - @ManyToMany(mappedBy = "users", cascade = CascadeType.ALL) - private List adventures = new ArrayList<>(0); + @ManyToMany(cascade = CascadeType.ALL) + @JoinTable(name = "User_Artefact", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "artefact_id", referencedColumnName = "id")) + private List artefacts = new ArrayList<>(0); - @JsonIgnore - @OneToMany(cascade = CascadeType.ALL, mappedBy = "user") - private List participations = new ArrayList<>(0); - - @OneToOne(mappedBy = "user", orphanRemoval = true) - private UiDesign uiDesign; - - public Long getId() { - return id; - } - - public void setId(final Long id) { - this.id = id; - } - - public String getUsername() { - return username; - } - - public void setUsername(final String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(final String password) { - this.password = password; - } - - public Role getRole() { - return role; - } - - public void setRole(final Role role) { - this.role = role; - } - - public String getPicture() { - return picture; - } - - public void setPicture(final String picture) { - this.picture = picture; - } - - public String getAboutMe() { - return aboutMe; - } - - public void setAboutMe(final String aboutMe) { - this.aboutMe = aboutMe; - } - - public AvatarClass getAvatarClass() { - return avatarClass; - } - - public void setAvatarClass(final AvatarClass avatarClass) { - this.avatarClass = avatarClass; - } - - public AvatarRace getAvatarRace() { - return avatarRace; - } - - public void setAvatarRace(final AvatarRace avatarRace) { - this.avatarRace = avatarRace; - } - - public Long getGold() { - return gold; - } - - public void setGold(final Long gold) { - this.gold = gold; - } - - public Long getXp() { - return xp; - } - - public void setXp(final Long xp) { - this.xp = xp; - } - - /** - * Adds the specified amount of gold. - * - * @param gold - * the amount to add, must be positive or zero. - */ - public void addGold(final long gold) { - Validate.isTrue(gold >= 0); - this.gold += gold; - } - - /** - * Adds the specified amount of XPerience Points. - * - * @param xp - * the amount to add, must be positive or zero. - */ - public void addXp(final long xp) { - Validate.isTrue(xp >= 0); - this.xp += xp; - } - - public Level getLevel() { - return level; - } - - public void setLevel(final Level level) { - this.level = level; - } - - public List getWorlds() { - return worlds; - } - - public void setWorlds(final List worlds) { - this.worlds = worlds; - } - - public void addWorld(final World world) { - getWorlds().add(world); - } - - public void removeWorld(final World world) { - getWorlds().remove(world); - } - - public List getArtefacts() { - return artefacts; - } - - public void setArtefacts(final List artefacts) { - this.artefacts = artefacts; - } - - public List getAdventures() { - return adventures; - } - - public void setAdventures(final List adventures) { - this.adventures = adventures; - } - - public List getParticipations() { - return participations; - } - - public void setParticipations(final List participations) { - this.participations = participations; - } - - public World getCurrentWorld() { - return currentWorld; - } - - public void setCurrentWorld(final World currentWorld) { - this.currentWorld = currentWorld; - } - - public UiDesign getUiDesign() { - return uiDesign; - } - - public void setUiDesign(final UiDesign uiDesign) { - this.uiDesign = uiDesign; - } - - public boolean isGamemaster() { - return getRole().getName() == RoleName.GAMEMASTER; - } - - public boolean isAdmin() { - return getRole().getName() == RoleName.ADMIN; - } - - public boolean isDeveloper() { - return getRole().getName() == RoleName.DEVELOPER; - } - - /** - * Looks up the names of all joined ("active") worlds and returns them in a - * list. - */ - public List getJoinedWorlds() { - if (worlds != null) { - return worlds.stream().map(World::getName).collect(Collectors.toList()); - } else { - return new ArrayList<>(); - } - } - - @Override - public int hashCode() { - return this.getId() == null ? super.hashCode() : Objects.hashCode(this.getId()); - } - - @Override - public boolean equals(final Object that) { - return this.getId() == null ? this == that - : that != null && this.getClass().isInstance(that) - && Objects.equal(this.getId(), ((User) that).getId()); - } + @JsonIgnore + @ManyToMany(mappedBy = "users", cascade = CascadeType.ALL) + private List adventures = new ArrayList<>(0); + + @JsonIgnore + @OneToMany(cascade = CascadeType.ALL, mappedBy = "user") + private List participations = new ArrayList<>(0); + + @OneToOne(mappedBy = "user", orphanRemoval = true) + private UiDesign uiDesign; + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(final String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(final String password) { + this.password = password; + } + + public Role getRole() { + return role; + } + + public void setRole(final Role role) { + this.role = role; + } + + public String getPicture() { + return picture; + } + + public void setPicture(final String picture) { + this.picture = picture; + } + + public String getAboutMe() { + return aboutMe; + } + + public void setAboutMe(final String aboutMe) { + this.aboutMe = aboutMe; + } + + public AvatarClass getAvatarClass() { + return avatarClass; + } + + public void setAvatarClass(final AvatarClass avatarClass) { + this.avatarClass = avatarClass; + } + + public AvatarRace getAvatarRace() { + return avatarRace; + } + + public void setAvatarRace(final AvatarRace avatarRace) { + this.avatarRace = avatarRace; + } + + public Long getGold() { + return gold; + } + + public void setGold(final Long gold) { + this.gold = gold; + } + + public Long getXp() { + return xp; + } + + public void setXp(final Long xp) { + this.xp = xp; + } + + /** + * Adds the specified amount of gold. + * + * @param gold + * the amount to add, must be positive or zero. + */ + public void addGold(final long gold) { + Validate.isTrue(gold >= 0); + this.gold += gold; + } + + /** + * Adds the specified amount of XPerience Points. + * + * @param xp + * the amount to add, must be positive or zero. + */ + public void addXp(final long xp) { + Validate.isTrue(xp >= 0); + this.xp += xp; + } + + public Level getLevel() { + return level; + } + + public void setLevel(final Level level) { + this.level = level; + } + + public List getWorlds() { + return worlds; + } + + public void setWorlds(final List worlds) { + this.worlds = worlds; + } + + public void addWorld(final World world) { + getWorlds().add(world); + } + + public void removeWorld(final World world) { + getWorlds().remove(world); + } + + public List getArtefacts() { + return artefacts; + } + + public void setArtefacts(final List artefacts) { + this.artefacts = artefacts; + } + + public List getAdventures() { + return adventures; + } + + public void setAdventures(final List adventures) { + this.adventures = adventures; + } + + public List getParticipations() { + return participations; + } + + public void setParticipations(final List participations) { + this.participations = participations; + } + + public World getCurrentWorld() { + return currentWorld; + } + + public void setCurrentWorld(final World currentWorld) { + this.currentWorld = currentWorld; + } + + public UiDesign getUiDesign() { + return uiDesign; + } + + public void setUiDesign(final UiDesign uiDesign) { + this.uiDesign = uiDesign; + } + + public boolean isGamemaster() { + return getRole().getName() == RoleName.GAMEMASTER; + } + + public boolean isAdmin() { + return getRole().getName() == RoleName.ADMIN; + } + + public boolean isDeveloper() { + return getRole().getName() == RoleName.DEVELOPER; + } + + public Timestamp getLastLogin() { + return lastLogin; + } + + public void setLastLogin(final Timestamp lastLogin) { + this.lastLogin = lastLogin; + } + + /** + * Looks up the names of all joined ("active") worlds and returns them in a list. + */ + public List getJoinedWorlds() { + if (worlds != null) { + return worlds.stream().map(World::getName).collect(Collectors.toList()); + } else { + return new ArrayList<>(); + } + } + + @Override + public int hashCode() { + return this.getId() == null ? super.hashCode() : Objects.hashCode(this.getId()); + } + + @Override + public boolean equals(final Object that) { + return this.getId() == null ? this == that + : that != null && this.getClass().isInstance(that) + && Objects.equal(this.getId(), ((User) that).getId()); + } } diff --git a/sonarQuest-backend/src/main/java/com/viadee/sonarquest/services/UserService.java b/sonarQuest-backend/src/main/java/com/viadee/sonarquest/services/UserService.java index 96fd129f..68209518 100644 --- a/sonarQuest-backend/src/main/java/com/viadee/sonarquest/services/UserService.java +++ b/sonarQuest-backend/src/main/java/com/viadee/sonarquest/services/UserService.java @@ -1,150 +1,157 @@ -package com.viadee.sonarquest.services; - -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.stereotype.Service; - -import com.viadee.sonarquest.entities.Level; -import com.viadee.sonarquest.entities.Permission; -import com.viadee.sonarquest.entities.Role; -import com.viadee.sonarquest.entities.RoleName; -import com.viadee.sonarquest.entities.User; -import com.viadee.sonarquest.entities.World; -import com.viadee.sonarquest.repositories.UserRepository; - -@Service -public class UserService implements UserDetailsService { - - private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class); - - @Autowired - private RoleService roleService; - - @Autowired - private UserRepository userRepository; - - @Autowired - private WorldService worldService; - - @Autowired - private LevelService levelService; - - @Autowired - private PermissionService permissionService; - - @Override - public UserDetails loadUserByUsername(final String username) { - final User user = findByUsername(username); - final Set permissions = permissionService.getAccessPermissions(user); - - final List authoritys = permissions.stream() - .map(berechtigung -> new SimpleGrantedAuthority(berechtigung.getPermission())) - .collect(Collectors.toList()); - - return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), true, - true, true, true, authoritys); - } - - public User findByUsername(final String username) { - return userRepository.findByUsername(username); - } - - private User findById(final Long id) { - return userRepository.findOne(id); - } - - public World updateUsersCurrentWorld(final User user, final Long worldId) { - final World world = worldService.findById(worldId); - user.setCurrentWorld(world); - userRepository.saveAndFlush(user); - return user.getCurrentWorld(); - } - - public synchronized User save(final User user) { - User toBeSaved = null; - final String username = user.getUsername(); - final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); - if (user.getId() == null) { - // Only the password hash needs to be saved - final String password = encoder.encode(user.getPassword()); - final Role role = user.getRole(); - final RoleName roleName = role.getName(); - final Role userRole = roleService.findByName(roleName); - toBeSaved = usernameFree(username) ? user : null; - if (toBeSaved != null) { - toBeSaved.setPassword(password); - toBeSaved.setRole(userRole); - toBeSaved.setCurrentWorld(user.getCurrentWorld()); - toBeSaved.setGold(0l); - toBeSaved.setXp(0l); - toBeSaved.setLevel(levelService.getLevelByUserXp(0l)); - } - } else { - toBeSaved = findById(user.getId()); - if (toBeSaved != null) { - if (!username.equals(toBeSaved.getUsername()) && usernameFree(username)) { - toBeSaved.setUsername(username); - } - // if there are identical hashes in the pw fields, do not touch them - if (!toBeSaved.getPassword().equals(user.getPassword())) { - // change password only if it differs from the old one - final String oldPassHash = toBeSaved.getPassword(); - if (!oldPassHash.equals(user.getPassword()) || !encoder.matches(user.getPassword(), oldPassHash)) { - final String password = encoder.encode(user.getPassword()); - toBeSaved.setPassword(password); - LOGGER.info("The password for user " + user.getUsername() + " (id: " + user.getId() - + ") has been changed."); - } - } - final Role role = user.getRole(); - final RoleName roleName = role.getName(); - final Role userRole = roleService.findByName(roleName); - toBeSaved.setRole(userRole); - toBeSaved.setAboutMe(user.getAboutMe()); - toBeSaved.setPicture(user.getPicture()); - toBeSaved.setCurrentWorld(user.getCurrentWorld()); - toBeSaved.setWorlds(user.getWorlds()); - toBeSaved.setGold(user.getGold()); - toBeSaved.setXp(user.getXp()); - toBeSaved.setLevel(levelService.getLevelByUserXp(user.getXp())); - } - } - - return toBeSaved != null ? userRepository.saveAndFlush(toBeSaved) : null; - } - - private boolean usernameFree(final String username) { - return userRepository.findByUsername(username) == null; - } - - public void delete(final Long userId) { - final User user = findById(userId); - userRepository.delete(user); - } - - public User findById(final long userId) { - return userRepository.findOne(userId); - } - - public List findAll() { - return userRepository.findAll(); - } - - public List findByRole(final RoleName roleName) { - return findAll().stream().filter(user -> user.getRole().getName() == roleName).collect(Collectors.toList()); - } - - public Level getLevel(final long xp) { - return levelService.getLevelByUserXp(xp); - } - -} +package com.viadee.sonarquest.services; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; + +import com.viadee.sonarquest.entities.Level; +import com.viadee.sonarquest.entities.Permission; +import com.viadee.sonarquest.entities.Role; +import com.viadee.sonarquest.entities.RoleName; +import com.viadee.sonarquest.entities.User; +import com.viadee.sonarquest.entities.World; +import com.viadee.sonarquest.repositories.UserRepository; + +@Service +public class UserService implements UserDetailsService { + + private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class); + + @Autowired + private RoleService roleService; + + @Autowired + private UserRepository userRepository; + + @Autowired + private WorldService worldService; + + @Autowired + private LevelService levelService; + + @Autowired + private PermissionService permissionService; + + @Override + public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { + final User user = findByUsername(username); + final Set permissions = permissionService.getAccessPermissions(user); + + final List authoritys = permissions.stream() + .map(berechtigung -> new SimpleGrantedAuthority(berechtigung.getPermission())) + .collect(Collectors.toList()); + + return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), true, + true, true, true, authoritys); + } + + public User findByUsername(final String username) { + return userRepository.findByUsername(username); + } + + private User findById(final Long id) { + return userRepository.findOne(id); + } + + public World updateUsersCurrentWorld(final User user, final Long worldId) { + final World world = worldService.findById(worldId); + user.setCurrentWorld(world); + userRepository.saveAndFlush(user); + return user.getCurrentWorld(); + } + + public synchronized User save(final User user) { + User toBeSaved = null; + final String username = user.getUsername(); + final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + if (user.getId() == null) { + // Only the password hash needs to be saved + final String password = encoder.encode(user.getPassword()); + final Role role = user.getRole(); + final RoleName roleName = role.getName(); + final Role userRole = roleService.findByName(roleName); + toBeSaved = usernameFree(username) ? user : null; + if (toBeSaved != null) { + toBeSaved.setPassword(password); + toBeSaved.setRole(userRole); + toBeSaved.setCurrentWorld(user.getCurrentWorld()); + toBeSaved.setGold(0l); + toBeSaved.setXp(0l); + toBeSaved.setLevel(levelService.getLevelByUserXp(0l)); + } + } else { + toBeSaved = findById(user.getId()); + if (toBeSaved != null) { + if (!username.equals(toBeSaved.getUsername()) && usernameFree(username)) { + toBeSaved.setUsername(username); + } + // if there are identical hashes in the pw fields, do not touch them + if (!toBeSaved.getPassword().equals(user.getPassword())) { + // change password only if it differs from the old one + final String oldPassHash = toBeSaved.getPassword(); + if (!oldPassHash.equals(user.getPassword()) || !encoder.matches(user.getPassword(), oldPassHash)) { + final String password = encoder.encode(user.getPassword()); + toBeSaved.setPassword(password); + LOGGER.info("The password for user " + user.getUsername() + " (id: " + user.getId() + + ") has been changed."); + } + } + final Role role = user.getRole(); + final RoleName roleName = role.getName(); + final Role userRole = roleService.findByName(roleName); + toBeSaved.setRole(userRole); + toBeSaved.setAboutMe(user.getAboutMe()); + toBeSaved.setPicture(user.getPicture()); + toBeSaved.setCurrentWorld(user.getCurrentWorld()); + toBeSaved.setWorlds(user.getWorlds()); + toBeSaved.setGold(user.getGold()); + toBeSaved.setXp(user.getXp()); + toBeSaved.setLevel(levelService.getLevelByUserXp(user.getXp())); + } + } + + return toBeSaved != null ? userRepository.saveAndFlush(toBeSaved) : null; + } + + private boolean usernameFree(final String username) { + return userRepository.findByUsername(username) == null; + } + + public void delete(final Long userId) { + final User user = findById(userId); + userRepository.delete(user); + } + + public User findById(final long userId) { + return userRepository.findOne(userId); + } + + public List findAll() { + return userRepository.findAll(); + } + + public List findByRole(final RoleName roleName) { + return findAll().stream().filter(user -> user.getRole().getName() == roleName).collect(Collectors.toList()); + } + + public Level getLevel(final long xp) { + return levelService.getLevelByUserXp(xp); + } + + public void updateLastLogin(final String username) { + final User user = findByUsername(username); + user.setLastLogin(Timestamp.valueOf(LocalDateTime.now())); + save(user); + } +} diff --git a/sonarQuest-backend/src/main/resources/db/schema/V0_0_8__SQUser_login_timestamp.sql b/sonarQuest-backend/src/main/resources/db/schema/V0_0_8__SQUser_login_timestamp.sql new file mode 100644 index 00000000..49494e94 --- /dev/null +++ b/sonarQuest-backend/src/main/resources/db/schema/V0_0_8__SQUser_login_timestamp.sql @@ -0,0 +1,2 @@ +-- Layout of quests can be set in the world properties (from the admin area) +ALTER TABLE SQUser ADD COLUMN last_login TIMESTAMP; diff --git a/sonarQuest-frontend/src/app/Interfaces/User.ts b/sonarQuest-frontend/src/app/Interfaces/User.ts index 58ebb94a..f322aa3c 100644 --- a/sonarQuest-frontend/src/app/Interfaces/User.ts +++ b/sonarQuest-frontend/src/app/Interfaces/User.ts @@ -19,7 +19,8 @@ export interface User { artefacts?: Artefact[], password?: string, currentWorld?: World, - joinedWorlds?: string[] + joinedWorlds?: string[], + lastLogin?: string } diff --git a/sonarQuest-frontend/src/app/pages/admin-page/components/admin-developer/admin-developer.component.ts b/sonarQuest-frontend/src/app/pages/admin-page/components/admin-developer/admin-developer.component.ts index ba8fb178..38d2410c 100644 --- a/sonarQuest-frontend/src/app/pages/admin-page/components/admin-developer/admin-developer.component.ts +++ b/sonarQuest-frontend/src/app/pages/admin-page/components/admin-developer/admin-developer.component.ts @@ -1,113 +1,115 @@ -import { TranslateService } from '@ngx-translate/core'; -import { AdminDeveloperDeleteComponent } from './components/admin-developer-delete/admin-developer-delete.component'; -import { AdminDeveloperEditComponent } from './components/admin-developer-edit/admin-developer-edit.component'; -import { TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn, TdDataTableService } from '@covalent/core'; -import { AdminDeveloperCreateComponent } from './components/admin-developer-create/admin-developer-create.component'; -import { Component, OnInit } from '@angular/core'; -import { MatDialog } from '@angular/material'; -import { User } from '../../../../Interfaces/User'; -import { UserService } from '../../../../services/user.service'; - -@Component({ - selector: 'app-admin-developer', - templateUrl: './admin-developer.component.html', - styleUrls: ['./admin-developer.component.css'] -}) -export class AdminDeveloperComponent implements OnInit { - - public users: User[]; - - columns: ITdDataTableColumn[] = [ - { name: 'username', label: 'Username'}, - { name: 'role.name', label: 'Role'}, - { name: 'level.level', label: 'Level'}, - { name: 'xp', label: 'XP'}, - { name: 'gold', label: 'Gold'}, - { name: 'currentWorld.name', label: 'Current World' }, - { name: 'joinedWorlds', label: 'Active Worlds' }, - { name: 'edit', label: '' } - ]; - - sortBy = 'username'; - sortOrder: TdDataTableSortingOrder = TdDataTableSortingOrder.Ascending; - selectedRows: any[] = []; - filteredData: any[]; - filteredTotal: number; - searchTerm = ''; - fromRow = 1; - currentPage = 1; - pageSize = 50; - - constructor( - private userService: UserService, - private dialog: MatDialog, - private translateService: TranslateService, - private _dataTableService: TdDataTableService) { - } - - ngOnInit() { - this.translateTable(); - this.userService.getUsers().subscribe(users => this.setUsers(users)); - } - - translateTable() { - this.translateService.get('TABLE.COLUMNS').subscribe((col_names) => { - this.columns = [ - { name: 'username', label: col_names.USERNAME }, - { name: 'role.name', label: col_names.ROLE }, - { name: 'level.level', label: col_names.LEVEL, format: this.formatNullIntoOne() }, - { name: 'xp', label: col_names.XP }, - { name: 'gold', label: col_names.GOLD }, - { name: 'currentWorld.name', label: col_names.ACTIVE_WORLD }, - { name: 'joinedWorlds', label: col_names.JOINED }, - { name: 'edit', label: '' }] - }); - } - - private formatNullIntoOne(): (value: any) => any { - return v => v == null ? 1 : v; - } - - setUsers(users: User[]) { - this.users = users; - this.filter(); - } - - createUser() { - this.dialog.open(AdminDeveloperCreateComponent, { data: this.users, width: '500px' }).afterClosed() - .subscribe(() => this.ngOnInit()); - } - - editUser(user: User) { - this.dialog.open(AdminDeveloperEditComponent, { data: user, width: '500px' }).afterClosed() - .subscribe(() => this.ngOnInit()); - } - - deleteUser(user: User) { - this.dialog.open(AdminDeveloperDeleteComponent, { data: user, width: '500px' }).afterClosed() - .subscribe(() => this.ngOnInit()); - } - - sort(sortEvent: ITdDataTableSortChangeEvent): void { - this.sortBy = sortEvent.name; - this.sortOrder = sortEvent.order; - this.filter(); - } - - filter(): void { - let newData: any[] = this.users; - const excludedColumns: string[] = this.columns - .filter((column: ITdDataTableColumn) => { - return ((column.filter === undefined && column.hidden === true) || - (column.filter !== undefined && column.filter === false)); - }).map((column: ITdDataTableColumn) => { - return column.name; - }); - newData = this._dataTableService.filterData(newData, this.searchTerm, true, excludedColumns); - this.filteredTotal = newData.length; - newData = this._dataTableService.sortData(newData, this.sortBy, this.sortOrder); - newData = this._dataTableService.pageData(newData, this.fromRow, this.currentPage * this.pageSize); - this.filteredData = newData; - } - -} +import { TranslateService } from '@ngx-translate/core'; +import { AdminDeveloperDeleteComponent } from './components/admin-developer-delete/admin-developer-delete.component'; +import { AdminDeveloperEditComponent } from './components/admin-developer-edit/admin-developer-edit.component'; +import { TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn, TdDataTableService } from '@covalent/core'; +import { AdminDeveloperCreateComponent } from './components/admin-developer-create/admin-developer-create.component'; +import { Component, OnInit } from '@angular/core'; +import { MatDialog } from '@angular/material'; +import { User } from '../../../../Interfaces/User'; +import { UserService } from '../../../../services/user.service'; + +@Component({ + selector: 'app-admin-developer', + templateUrl: './admin-developer.component.html', + styleUrls: ['./admin-developer.component.css'] +}) +export class AdminDeveloperComponent implements OnInit { + + public users: User[]; + + columns: ITdDataTableColumn[] = [ + { name: 'username', label: 'Username'}, + { name: 'role.name', label: 'Role'}, + { name: 'level.level', label: 'Level'}, + { name: 'xp', label: 'XP'}, + { name: 'gold', label: 'Gold'}, + { name: 'currentWorld.name', label: 'Current World' }, + { name: 'joinedWorlds', label: 'Active Worlds' }, + { name: 'lastLogin', label: 'Last Login' }, + { name: 'edit', label: '' } + ]; + + sortBy = 'username'; + sortOrder: TdDataTableSortingOrder = TdDataTableSortingOrder.Ascending; + selectedRows: any[] = []; + filteredData: any[]; + filteredTotal: number; + searchTerm = ''; + fromRow = 1; + currentPage = 1; + pageSize = 50; + + constructor( + private userService: UserService, + private dialog: MatDialog, + private translateService: TranslateService, + private _dataTableService: TdDataTableService) { + } + + ngOnInit() { + this.translateTable(); + this.userService.getUsers().subscribe(users => this.setUsers(users)); + } + + translateTable() { + this.translateService.get('TABLE.COLUMNS').subscribe((col_names) => { + this.columns = [ + { name: 'username', label: col_names.USERNAME }, + { name: 'role.name', label: col_names.ROLE }, + { name: 'level.level', label: col_names.LEVEL, format: this.formatNullIntoOne() }, + { name: 'xp', label: col_names.XP }, + { name: 'gold', label: col_names.GOLD }, + { name: 'currentWorld.name', label: col_names.ACTIVE_WORLD }, + { name: 'joinedWorlds', label: col_names.JOINED }, + { name: 'lastLogin', label: col_names.LAST_LOGIN }, + { name: 'edit', label: '' }] + }); + } + + private formatNullIntoOne(): (value: any) => any { + return v => v === null ? 1 : v; + } + + setUsers(users: User[]) { + this.users = users; + this.filter(); + } + + createUser() { + this.dialog.open(AdminDeveloperCreateComponent, { data: this.users, width: '500px' }).afterClosed() + .subscribe(() => this.ngOnInit()); + } + + editUser(user: User) { + this.dialog.open(AdminDeveloperEditComponent, { data: user, width: '500px' }).afterClosed() + .subscribe(() => this.ngOnInit()); + } + + deleteUser(user: User) { + this.dialog.open(AdminDeveloperDeleteComponent, { data: user, width: '500px' }).afterClosed() + .subscribe(() => this.ngOnInit()); + } + + sort(sortEvent: ITdDataTableSortChangeEvent): void { + this.sortBy = sortEvent.name; + this.sortOrder = sortEvent.order; + this.filter(); + } + + filter(): void { + let newData: any[] = this.users; + const excludedColumns: string[] = this.columns + .filter((column: ITdDataTableColumn) => { + return ((column.filter === undefined && column.hidden === true) || + (column.filter !== undefined && column.filter === false)); + }).map((column: ITdDataTableColumn) => { + return column.name; + }); + newData = this._dataTableService.filterData(newData, this.searchTerm, true, excludedColumns); + this.filteredTotal = newData.length; + newData = this._dataTableService.sortData(newData, this.sortBy, this.sortOrder); + newData = this._dataTableService.pageData(newData, this.fromRow, this.currentPage * this.pageSize); + this.filteredData = newData; + } + +} \ No newline at end of file diff --git a/sonarQuest-frontend/src/app/services/user.service.ts b/sonarQuest-frontend/src/app/services/user.service.ts index fecebe98..d873e4c5 100644 --- a/sonarQuest-frontend/src/app/services/user.service.ts +++ b/sonarQuest-frontend/src/app/services/user.service.ts @@ -4,6 +4,8 @@ import {HttpClient} from '@angular/common/http'; import {environment} from '../../environments/environment'; import {Observable, Subscriber} from 'rxjs'; import {AuthenticationService} from '../login/authentication.service'; +import {Subscriber} from 'rxjs/Subscriber'; +import {map, tap} from 'rxjs/operators'; @Injectable() export class UserService { @@ -50,7 +52,11 @@ export class UserService { public getUsers(): Observable { const url = `${environment.endpoint}/user/all`; - return this.httpClient.get (url); + return this.httpClient.get (url).pipe(tap((users: User[]) => { + users.forEach(user => + user.lastLogin = user.lastLogin ? new Date(user.lastLogin).toTimeString() : null + ) + })); } public getImage(): Observable { diff --git a/sonarQuest-frontend/src/assets/i18n/de.json b/sonarQuest-frontend/src/assets/i18n/de.json index cd53e01e..2af1a03c 100644 --- a/sonarQuest-frontend/src/assets/i18n/de.json +++ b/sonarQuest-frontend/src/assets/i18n/de.json @@ -1,236 +1,237 @@ -{ - "APP_COMPONENT": { - "STARTPAGE": "Startseite-de", - "MY_AVATAR": "Mein Avatar", - "ADVENTURES": "Abenteuer", - "QUESTS": "Quests", - "MARKETPLACE": "Marktplatz", - "GAMEMASTER": "Spielleiter", - "ADMIN": "Admin", - "LOGIN": "Anmelden", - "LOGOUT": "Abmelden", - "BACKGROUND": "Schalte Hintergrund-Transparenz um", - "USER_PROFILE": "Öffne Benutzerprofil" - }, - "ADVENTURE_PAGE": { - "MY_ADVENTURES": "Meine Abenteuer", - "JOIN_ADVENTURE": "Dieses Abenteuer beginnen!", - "LEAVE_ADVENTURE": "Dieses Abenteuer verlassen...", - "MY_ADVENTURES_FOR_WORLD": "Meine Abenteuer für ", - "ADVENTURES_FOR_WORLD": "Alle Abenteuer für ", - "AVAILABLE_ADVENTURES": "Verfügbare Abenteuer", - "AVAILABLE_ADVENTURES_FOR_WORLD": "Verfügbare Abenteuer für " - }, - "QUEST_PAGE": { - "MY_QUESTS": "Meine Quests", - "SHOW_QUEST": "Quest anzeigen", - "MY_QUESTS_FOR_WORLD": "Meine Quests für ", - "QUESTS_FOR_WORLD": "Alle Quests für ", - "JOIN_QUEST": "Quest annehmen", - "AVAILABLE_QUESTS": "Verfügbare Quests", - "AVAILABLE_QUESTS_FOR_WORLD": "Verfügbare Quests für ", - "COMBATED_BY": "angenommen von ", - "SHOW_ISSUE_TOOLTIP":"Zeige SonarQube Issue", - "JOIN_ISSUE_TOOLTIP":"Kämpfe", - "CANCEL_ISSUE_TOOLTIP":"Höre auf zu kämpfen" - }, - "MARKETPLACE_PAGE": { - "MARKETPLACE": "Marktplatz", - "ALREADY_OWNED_ITEM": "Bereits in meinem Besitz!", - "BUY_ITEM_COMPULSORY": "Mit einem Click jetzt kaufen!", - "SHOW_ITEM": "Artefakt anzeigen" - }, - "ARTEFACT_VIEW_PAGE": { - "TITLE": "Artefaktsteckbrief" - }, - "ADMIN_PAGE": { - "ALL_DEVELOPER": "Alle Entwickler", - "CREATE_DEVELOPER": "Entwickler anlegen", - "EDIT_DEVELOPER": "Entwickler bearbeiten", - "DELETE_DEVELOPER": "Soll wirklich gelöscht werden?", - "SONAR_CUBE_HEADER": "SonarQube", - "SELECT_BACKGROUND": "Hintergrund wählen" - }, - "START_PAGE": { - "CURRENT_PROGRESS": "Bearbeitungsfortschritt", - "PLEASE_LOG_IN": "Bitte loggen Sie sich ein" - }, - "GAMEMASTER_PAGE": { - "ADVENTURE": { - "CREATE_ADVENTURE": "Abenteuer anlegen", - "EDIT_ADVENTURE": "Abenteuer bearbeiten" - }, - "QUEST": { - "SUGGEST_TASKS": "Auftrag vorschlagen", - "GENERATE": "Erzeugen", - "CREATE_QUEST": "Quest anlegen", - "EDIT_QUEST": "Quest bearbeiten", - "EDIT_SOLVED_QUEST": "Diese Quest ist gelöst. Du kannst sie nicht mehr bearbeiten." - }, - "TASK": { - "CREATE_SPECIALTASK": "Spezialauftrag anlegen", - "CREATE_STANDARDTASK": "Auftrag anlegen", - "EDIT_SPECIALTASK": "Spezialauftrag bearbeiten", - "EDIT_STANDARDTASK": "Auftrag bearbeiten", - "CONDITION": "Bedingung", - "AMOUNT": "Anzahl", - "ADD_FREE_TOOLTIP": "Füge freien Auftrag hinzu", - "SUGGEST_TASKS_TOOLTIP": "Schlage zufällige Aufträge vor", - "SOLVE_TASK_TOOLTIP": "Ändere Zustand zu 'Abgeschlossen'", - "REMOVE_TASK_FROM_QUEST_TOOLTIP": "Entferne Auftrag aus Quest" - } - }, - "TABLE": { - "COLUMNS": { - "TITLE": "Titel", - "GOLD": "Gold", - "XP": "XP", - "LEVEL": "Level", - "ADVENTURE": "Abenteuer", - "STORY": "Story", - "STATUS": "Status", - "ID": "Id", - "NAME": "Name", - "PROJECT": "Projekt", - "QUEST": "Quest", - "TYPE": "Typ", - "SEVERITY": "Schwere", - "WORLD": "Welt", - "ACTIVE_WORLD": "Letzte Spielwelt", - "ACTIVE": "Aktiv", - "UPDATE_TASKS": "Aufgaben aktualisieren", - "MISSION": "Mission", - "ABOUT_ME": "Kurzbio", - "USERNAME": "Username", - "JOINED": "Freigeschaltet", - "PLAYERS": "Spieler", - "ROLE": "Rolle", - "VISIBLE": "Sichtbar", - "USE_QUEST_CARDS": "Questcards", - "CREATORNAME": "Von" - }, - "INFO": { - "NO_ENTRIES": "keine Einträge", - "OF": "von", - "ENTRIES_OF_THIS_PAGE": "Einträge auf dieser Seite:" - } - }, - "GLOBAL": { - "CANCEL": "Abbrechen", - "CREATE": "Anlegen", - "EDIT": "Bearbeiten", - "DELETE": "Löschen", - "NO_ENTRIES": "keine Einträge", - "SEARCH": "Suche", - "SAVE": "Speichern", - "SOLVEALLTASKS" : "Alle Aufgaben lösen", - "IMAGE": "Bild", - "FILENAME_IMAGE": "Dateiname Bild", - "CLICK_TO_SELECT": "Klick zum Wählen", - "GOLD": "Gold", - "XP": "XP", - "CLOSE": "Schließen", - "CONFIRMATION_MESSAGE": "Wirklich löschen?" - }, - "DEVELOPER": { - "DEVELOPER": "Entwickler", - "GOLD": "Gold", - "XP": "XP", - "LEVEL": "Stufe", - "ABOUTME": "Über mich", - "AVATARRACE": "Rasse", - "AVATARCLASS": "Klasse", - "TROPHIES": "Trophäen", - "TOOLTIP_EDIT": "Eigene Einstellungen bearbeiten" - }, - "QUEST": { - "QUEST": "Quest", - "QUESTS": "Quests", - "NEW_QUEST": "Neue Quest", - "ADD_FREE_QUEST": "Freie Quest anlegen", - "NO_FREE_QUESTS_AVAILABLE": "Keine freien Quests gefunden", - "VISIBLE": "Sichtbar", - "TOOLTIP_VISIBLE": "Diese Quest bei den Spielern sichtbar machen?" - }, - "ADVENTURE": { - "ADVENTURE": "Abenteuer", - "ADVENTURES": "Abenteuer", - "NEW_ADVENTURE": "Neues Abenteuer", - "SOLVEADVENTURE": "Abenteuer abschließen" - }, - "WORLD": { - "WORLD": "Welt", - "WORLDS": "Welten", - "ACTIVE": "Aktiv", - "CHOOSE_WORLD": "Wähle deine Welt", - "UPDATE_WORLDS": "Aktualisiere Welten", - "USE_QUEST_CARDS": "Questcard-Layout", - "TOOLTIP_USE_QUEST_CARDS": "Wenn angehakt wird bei den Spielern bei der Questdarstellung ein Karten-Layout verwendet statt einer Tabelle", - "TOOLTIP_ACTIVE": "Wenn angehakt, ist diese Welt auswählbar als Spielwelt" - }, - "TASK": { - "TASK": "Auftrag", - "TASKS": "Aufträge", - "SPECIALTASK": "Spezialauftrag", - "STANDARDTASK": "Standardauftrag", - "NEW_SPECIALTASK": "Neuer Spezialauftrag", - "NEW_STANDARDTASK": "Neuer Standardauftrag", - "ADD_FREE_TASK": "Freien Auftrag anlegen", - "NO_FREE_TASKS_AVAILABLE": "Keine freien Aufträge gefunden", - "SPECIALTASK_FOR_WORLD": "Alle Spezialaufträge für ", - "STANDARDTASK_FOR_WORLD": "Alle Standardaufträge für ", - "AVAILABLE_TASKS": "Verfügbare Aufträge", - "MISSION": "Mission", - "UPDATE_STANDARD_TASKS_STATUS": "Update Issues vom SonarQube Server", - "UPDATE_STANDARD_TASKS_DISABLED_TOOLTIP": "Abrufen der SonarQube Issues nicht möglich, da keine Welt festgelegt wurde!", - "FREE_STANDARDTASKS": "Standardaufträge", - "FREE_SPECIALTASKS": "Spezialaufträge" - }, - "ARTEFACT": { - "ARTEFACTS": "Artefakte", - "NEW_ARTEFACT": "Neues Artefakt", - "CREATE_ARTEFACT": "Artefakt anlegen", - "EDIT_ARTEFACT": "Artefakt bearbeiten", - "SELECT_ICON": "Icon wählen", - "CLICK_TO_SELECT": "Klicken zum Auswählen", - "PRICE": "Preis", - "MIN_LEVEL": "Min. Level", - "QUANTITY": "Anzahl vorhanden", - "NAME": "Name", - "DESCRIPTION": "Beschreibung", - "SKILLS": "Kräfte" - }, - "SKILL": { - "MORE_GOLD": "Mehr Gold", - "MORE_XP": "Mehr XP", - "CREATE_SKILL": "Kraft anlegen", - "ADD_SKILL": "Kraft hinzufügen", - "SKILLS": "Kräfte", - "NEW_SKILL": "Neue Kraft", - "TYPE": "Typ", - "VALUE": "Betrag", - "NAME": "Name" - }, - "LOGIN": { - "LOGIN": "Login", - "USERNAME": "Benutzername", - "PASSWORD": "Passwort" - }, - "WIZARD": { - "WIZARD_SAYS": "Der Wizard meint...", - "MSG_NO_WORLDS_FOUND": "Keine Spielwelt vorhanden", - "HINT_NO_WORLDS_FOUND": "Um die SonarQube-Projekte abzurufen, muss der Admin im Reiter 'Welten' auf 'Aktualisiere Welten' klicken", - "MSG_NO_ACTIVE_WORLD": "Keine Spielwelt ist aktiv", - "HINT_NO_ACTIVE_WORLD": "Zum Aktivieren einer Welt muss der Admin im Reiter 'Welten' bei einer Welt den 'Aktiv'-Haken setzen!", - "MSG_NO_PLAYERS_ASSIGNED": "Der aktiven Welt sind keine Spieler zugeordnet", - "HINT_NO_PLAYERS_ASSIGNED": "Zum Freischalten eines Spielers muss der Admin im Reiter 'Entwickler' bei einem Spieler den Haken 'Freigeschaltet' bei der gewählten Welt setzen!", - "MSG_NO_GAMEMASTER_ASSIGNED": "Der aktiven Welt fehlt ein Spielleiter, der die Quests erstellen kann", - "HINT_NO_GAMEMASTER_ASSIGNED": "Im Reiter 'Entwickler' muss der Admin bei einem Spieler mit der Rolle 'Spielleiter' den Haken 'Freigeschaltet' bei der gewählten Welt setzen!", - "MSG_NO_TASKS_FOUND": "Es sind noch keine Aufgaben/Issues von SonarQube abgerufen worden", - "HINT_NO_TASKS_FOUND": "Der Spielleiter muss im Reiter 'Aufträge' auf 'Aktualisiere Status' klicken, um die Issues abzurufen!", - "MSG_NO_QUESTS_FOUND": "Es sind noch keine Quests erstellt worden", - "HINT_NO_QUESTS_FOUND": "Der Spielleiter muss im Reiter 'Quests' eine neue Quest anlegen und Aufträge zuordnen, die die Spieler lösen sollen!", - "MSG_NO_WORLD_SELECTED": "Keine aktive Spielwelt ist ausgewählt", - "HINT_NO_WORLD_SELECTED": "Oben links bei 'Welt' die aktive Spielwelt einstellen, um weitere Hinweise zu bekommen! Wenn das nicht geht, muss der Admin zuerst 'Entwickler' freischalten!" - } -} +{ + "APP_COMPONENT": { + "STARTPAGE": "Startseite-de", + "MY_AVATAR": "Mein Avatar", + "ADVENTURES": "Abenteuer", + "QUESTS": "Quests", + "MARKETPLACE": "Marktplatz", + "GAMEMASTER": "Spielleiter", + "ADMIN": "Admin", + "LOGIN": "Anmelden", + "LOGOUT": "Abmelden", + "BACKGROUND": "Schalte Hintergrund-Transparenz um", + "USER_PROFILE": "Öffne Benutzerprofil" + }, + "ADVENTURE_PAGE": { + "MY_ADVENTURES": "Meine Abenteuer", + "JOIN_ADVENTURE": "Dieses Abenteuer beginnen!", + "LEAVE_ADVENTURE": "Dieses Abenteuer verlassen...", + "MY_ADVENTURES_FOR_WORLD": "Meine Abenteuer für ", + "ADVENTURES_FOR_WORLD": "Alle Abenteuer für ", + "AVAILABLE_ADVENTURES": "Verfügbare Abenteuer", + "AVAILABLE_ADVENTURES_FOR_WORLD": "Verfügbare Abenteuer für " + }, + "QUEST_PAGE": { + "MY_QUESTS": "Meine Quests", + "SHOW_QUEST": "Quest anzeigen", + "MY_QUESTS_FOR_WORLD": "Meine Quests für ", + "QUESTS_FOR_WORLD": "Alle Quests für ", + "JOIN_QUEST": "Quest annehmen", + "AVAILABLE_QUESTS": "Verfügbare Quests", + "AVAILABLE_QUESTS_FOR_WORLD": "Verfügbare Quests für ", + "COMBATED_BY": "angenommen von ", + "SHOW_ISSUE_TOOLTIP":"Zeige SonarQube Issue", + "JOIN_ISSUE_TOOLTIP":"Kämpfe", + "CANCEL_ISSUE_TOOLTIP":"Höre auf zu kämpfen" + }, + "MARKETPLACE_PAGE": { + "MARKETPLACE": "Marktplatz", + "ALREADY_OWNED_ITEM": "Bereits in meinem Besitz!", + "BUY_ITEM_COMPULSORY": "Mit einem Click jetzt kaufen!", + "SHOW_ITEM": "Artefakt anzeigen" + }, + "ARTEFACT_VIEW_PAGE": { + "TITLE": "Artefaktsteckbrief" + }, + "ADMIN_PAGE": { + "ALL_DEVELOPER": "Alle Entwickler", + "CREATE_DEVELOPER": "Entwickler anlegen", + "EDIT_DEVELOPER": "Entwickler bearbeiten", + "DELETE_DEVELOPER": "Soll wirklich gelöscht werden?", + "SONAR_CUBE_HEADER": "SonarQube", + "SELECT_BACKGROUND": "Hintergrund wählen" + }, + "START_PAGE": { + "CURRENT_PROGRESS": "Bearbeitungsfortschritt", + "PLEASE_LOG_IN": "Bitte loggen Sie sich ein" + }, + "GAMEMASTER_PAGE": { + "ADVENTURE": { + "CREATE_ADVENTURE": "Abenteuer anlegen", + "EDIT_ADVENTURE": "Abenteuer bearbeiten" + }, + "QUEST": { + "SUGGEST_TASKS": "Auftrag vorschlagen", + "GENERATE": "Erzeugen", + "CREATE_QUEST": "Quest anlegen", + "EDIT_QUEST": "Quest bearbeiten", + "EDIT_SOLVED_QUEST": "Diese Quest ist gelöst. Du kannst sie nicht mehr bearbeiten." + }, + "TASK": { + "CREATE_SPECIALTASK": "Spezialauftrag anlegen", + "CREATE_STANDARDTASK": "Auftrag anlegen", + "EDIT_SPECIALTASK": "Spezialauftrag bearbeiten", + "EDIT_STANDARDTASK": "Auftrag bearbeiten", + "CONDITION": "Bedingung", + "AMOUNT": "Anzahl", + "ADD_FREE_TOOLTIP": "Füge freien Auftrag hinzu", + "SUGGEST_TASKS_TOOLTIP": "Schlage zufällige Aufträge vor", + "SOLVE_TASK_TOOLTIP": "Ändere Zustand zu 'Abgeschlossen'", + "REMOVE_TASK_FROM_QUEST_TOOLTIP": "Entferne Auftrag aus Quest" + } + }, + "TABLE": { + "COLUMNS": { + "TITLE": "Titel", + "GOLD": "Gold", + "XP": "XP", + "LEVEL": "Level", + "ADVENTURE": "Abenteuer", + "STORY": "Story", + "STATUS": "Status", + "ID": "Id", + "NAME": "Name", + "PROJECT": "Projekt", + "QUEST": "Quest", + "TYPE": "Typ", + "SEVERITY": "Schwere", + "WORLD": "Welt", + "ACTIVE_WORLD": "Letzte Spielwelt", + "ACTIVE": "Aktiv", + "UPDATE_TASKS": "Aufgaben aktualisieren", + "MISSION": "Mission", + "ABOUT_ME": "Kurzbio", + "USERNAME": "Username", + "JOINED": "Freigeschaltet", + "PLAYERS": "Spieler", + "ROLE": "Rolle", + "VISIBLE": "Sichtbar", + "USE_QUEST_CARDS": "Questcards", + "LAST_LOGIN": "Letzter Login", + "CREATORNAME": "Von" + }, + "INFO": { + "NO_ENTRIES": "keine Einträge", + "OF": "von", + "ENTRIES_OF_THIS_PAGE": "Einträge auf dieser Seite:" + } + }, + "GLOBAL": { + "CANCEL": "Abbrechen", + "CREATE": "Anlegen", + "EDIT": "Bearbeiten", + "DELETE": "Löschen", + "NO_ENTRIES": "keine Einträge", + "SEARCH": "Suche", + "SAVE": "Speichern", + "SOLVEALLTASKS" : "Alle Aufgaben lösen", + "IMAGE": "Bild", + "FILENAME_IMAGE": "Dateiname Bild", + "CLICK_TO_SELECT": "Klick zum Wählen", + "GOLD": "Gold", + "XP": "XP", + "CLOSE": "Schließen", + "CONFIRMATION_MESSAGE": "Wirklich löschen?" + }, + "DEVELOPER": { + "DEVELOPER": "Entwickler", + "GOLD": "Gold", + "XP": "XP", + "LEVEL": "Stufe", + "ABOUTME": "Über mich", + "AVATARRACE": "Rasse", + "AVATARCLASS": "Klasse", + "TROPHIES": "Trophäen", + "TOOLTIP_EDIT": "Eigene Einstellungen bearbeiten" + }, + "QUEST": { + "QUEST": "Quest", + "QUESTS": "Quests", + "NEW_QUEST": "Neue Quest", + "ADD_FREE_QUEST": "Freie Quest anlegen", + "NO_FREE_QUESTS_AVAILABLE": "Keine freien Quests gefunden", + "VISIBLE": "Sichtbar", + "TOOLTIP_VISIBLE": "Diese Quest bei den Spielern sichtbar machen?" + }, + "ADVENTURE": { + "ADVENTURE": "Abenteuer", + "ADVENTURES": "Abenteuer", + "NEW_ADVENTURE": "Neues Abenteuer", + "SOLVEADVENTURE": "Abenteuer abschließen" + }, + "WORLD": { + "WORLD": "Welt", + "WORLDS": "Welten", + "ACTIVE": "Aktiv", + "CHOOSE_WORLD": "Wähle deine Welt", + "UPDATE_WORLDS": "Aktualisiere Welten", + "USE_QUEST_CARDS": "Questcard-Layout", + "TOOLTIP_USE_QUEST_CARDS": "Wenn angehakt wird bei den Spielern bei der Questdarstellung ein Karten-Layout verwendet statt einer Tabelle", + "TOOLTIP_ACTIVE": "Wenn angehakt, ist diese Welt auswählbar als Spielwelt" + }, + "TASK": { + "TASK": "Auftrag", + "TASKS": "Aufträge", + "SPECIALTASK": "Spezialauftrag", + "STANDARDTASK": "Standardauftrag", + "NEW_SPECIALTASK": "Neuer Spezialauftrag", + "NEW_STANDARDTASK": "Neuer Standardauftrag", + "ADD_FREE_TASK": "Freien Auftrag anlegen", + "NO_FREE_TASKS_AVAILABLE": "Keine freien Aufträge gefunden", + "SPECIALTASK_FOR_WORLD": "Alle Spezialaufträge für ", + "STANDARDTASK_FOR_WORLD": "Alle Standardaufträge für ", + "AVAILABLE_TASKS": "Verfügbare Aufträge", + "MISSION": "Mission", + "UPDATE_STANDARD_TASKS_STATUS": "Update Issues vom SonarQube Server", + "UPDATE_STANDARD_TASKS_DISABLED_TOOLTIP": "Abrufen der SonarQube Issues nicht möglich, da keine Welt festgelegt wurde!", + "FREE_STANDARDTASKS": "Standardaufträge", + "FREE_SPECIALTASKS": "Spezialaufträge" + }, + "ARTEFACT": { + "ARTEFACTS": "Artefakte", + "NEW_ARTEFACT": "Neues Artefakt", + "CREATE_ARTEFACT": "Artefakt anlegen", + "EDIT_ARTEFACT": "Artefakt bearbeiten", + "SELECT_ICON": "Icon wählen", + "CLICK_TO_SELECT": "Klicken zum Auswählen", + "PRICE": "Preis", + "MIN_LEVEL": "Min. Level", + "QUANTITY": "Anzahl vorhanden", + "NAME": "Name", + "DESCRIPTION": "Beschreibung", + "SKILLS": "Kräfte" + }, + "SKILL": { + "MORE_GOLD": "Mehr Gold", + "MORE_XP": "Mehr XP", + "CREATE_SKILL": "Kraft anlegen", + "ADD_SKILL": "Kraft hinzufügen", + "SKILLS": "Kräfte", + "NEW_SKILL": "Neue Kraft", + "TYPE": "Typ", + "VALUE": "Betrag", + "NAME": "Name" + }, + "LOGIN": { + "LOGIN": "Login", + "USERNAME": "Benutzername", + "PASSWORD": "Passwort" + }, + "WIZARD": { + "WIZARD_SAYS": "Der Wizard meint...", + "MSG_NO_WORLDS_FOUND": "Keine Spielwelt vorhanden", + "HINT_NO_WORLDS_FOUND": "Um die SonarQube-Projekte abzurufen, muss der Admin im Reiter 'Welten' auf 'Aktualisiere Welten' klicken", + "MSG_NO_ACTIVE_WORLD": "Keine Spielwelt ist aktiv", + "HINT_NO_ACTIVE_WORLD": "Zum Aktivieren einer Welt muss der Admin im Reiter 'Welten' bei einer Welt den 'Aktiv'-Haken setzen!", + "MSG_NO_PLAYERS_ASSIGNED": "Der aktiven Welt sind keine Spieler zugeordnet", + "HINT_NO_PLAYERS_ASSIGNED": "Zum Freischalten eines Spielers muss der Admin im Reiter 'Entwickler' bei einem Spieler den Haken 'Freigeschaltet' bei der gewählten Welt setzen!", + "MSG_NO_GAMEMASTER_ASSIGNED": "Der aktiven Welt fehlt ein Spielleiter, der die Quests erstellen kann", + "HINT_NO_GAMEMASTER_ASSIGNED": "Im Reiter 'Entwickler' muss der Admin bei einem Spieler mit der Rolle 'Spielleiter' den Haken 'Freigeschaltet' bei der gewählten Welt setzen!", + "MSG_NO_TASKS_FOUND": "Es sind noch keine Aufgaben/Issues von SonarQube abgerufen worden", + "HINT_NO_TASKS_FOUND": "Der Spielleiter muss im Reiter 'Aufträge' auf 'Aktualisiere Status' klicken, um die Issues abzurufen!", + "MSG_NO_QUESTS_FOUND": "Es sind noch keine Quests erstellt worden", + "HINT_NO_QUESTS_FOUND": "Der Spielleiter muss im Reiter 'Quests' eine neue Quest anlegen und Aufträge zuordnen, die die Spieler lösen sollen!", + "MSG_NO_WORLD_SELECTED": "Keine aktive Spielwelt ist ausgewählt", + "HINT_NO_WORLD_SELECTED": "Oben links bei 'Welt' die aktive Spielwelt einstellen, um weitere Hinweise zu bekommen! Wenn das nicht geht, muss der Admin zuerst 'Entwickler' freischalten!" + } +} diff --git a/sonarQuest-frontend/src/assets/i18n/en.json b/sonarQuest-frontend/src/assets/i18n/en.json index d9bf6b6d..a92a8b79 100644 --- a/sonarQuest-frontend/src/assets/i18n/en.json +++ b/sonarQuest-frontend/src/assets/i18n/en.json @@ -1,236 +1,237 @@ -{ - "APP_COMPONENT": { - "STARTPAGE": "Startpage", - "MY_AVATAR": "My Avatar", - "ADVENTURES": "Adventures", - "QUESTS": "Quests", - "MARKETPLACE": "Marketplace", - "GAMEMASTER": "Gamemaster", - "ADMIN": "Admin", - "LOGIN": "Login", - "LOGOUT": "Logout", - "BACKGROUND": "Switch transparency of background", - "USER_PROFILE": "Open user profile" - }, - "ADVENTURE_PAGE": { - "MY_ADVENTURES": "My Adventures", - "JOIN_ADVENTURE": "Join this adventure!", - "LEAVE_ADVENTURE": "Leave this adventure...", - "MY_ADVENTURES_FOR_WORLD": "My Adventures for ", - "ADVENTURES_FOR_WORLD": "All Adventures for ", - "AVAILABLE_ADVENTURES": "Available Adventures", - "AVAILABLE_ADVENTURES_FOR_WORLD": "Available Adventures for " - }, - "QUEST_PAGE": { - "MY_QUESTS": "My Quests", - "SHOW_QUEST": "Show Quest", - "MY_QUESTS_FOR_WORLD": "My Quests for ", - "QUESTS_FOR_WORLD": "All Quests for ", - "JOIN_QUEST": "Join Quest", - "AVAILABLE_QUESTS": "Available Quests", - "AVAILABLE_QUESTS_FOR_WORLD": "Available Quests for ", - "COMBATED_BY": "combated by", - "SHOW_ISSUE_TOOLTIP":"Show SonarQube issue", - "JOIN_ISSUE_TOOLTIP":"Fight", - "CANCEL_ISSUE_TOOLTIP":"Stop fighting" - }, - "MARKETPLACE_PAGE": { - "MARKETPLACE": "Marketplace", - "ALREADY_OWNED_ITEM": "Already mine!", - "BUY_ITEM_COMPULSORY": "One click buy now!", - "SHOW_ITEM": "Show artefact details" - }, - "ARTEFACT_VIEW_PAGE": { - "TITLE": "Artefact description" - }, - "ADMIN_PAGE": { - "ALL_DEVELOPER": "All Developer", - "CREATE_DEVELOPER": "Create Developer", - "EDIT_DEVELOPER": "Edit Developer", - "DELETE_DEVELOPER": "Are you sure to delete?", - "SONAR_CUBE_HEADER": "SonarQube", - "SELECT_BACKGROUND": "Select Background" - }, - "START_PAGE": { - "CURRENT_PROGRESS": "Current Progress", - "PLEASE_LOG_IN": "Feel free to log in" - }, - "GAMEMASTER_PAGE": { - "ADVENTURE": { - "CREATE_ADVENTURE": "Create adventure", - "EDIT_ADVENTURE": "Edit adventure" - }, - "QUEST": { - "SUGGEST_TASKS": "Suggest tasks", - "GENERATE": "Generate", - "CREATE_QUEST": "Create quest", - "EDIT_QUEST": "Edit quest", - "EDIT_SOLVED_QUEST": "This quest is solved. You cannot edit it anymore." - }, - "TASK": { - "CREATE_SPECIALTASK": "Create Special Task", - "CREATE_STANDARDTASK": "Create Standard Task", - "EDIT_SPECIALTASK": "Edit Special Task", - "EDIT_STANDARDTASK": "Edit Standard Task", - "CONDITION": "Condition", - "AMOUNT": "Amount", - "ADD_FREE_TOOLTIP": "Add free task", - "SUGGEST_TASKS_TOOLTIP": "Suggest random tasks", - "SOLVE_TASK_TOOLTIP": "Change status to 'closed'", - "REMOVE_TASK_FROM_QUEST_TOOLTIP": "Remove task from quest" - } - }, - "TABLE": { - "COLUMNS": { - "TITLE": "Title", - "GOLD": "Gold", - "XP": "XP", - "LEVEL": "Level", - "ADVENTURE": "Adventure", - "STORY": "Story", - "STATUS": "Status", - "ID": "Id", - "NAME": "Name", - "PROJECT": "Project", - "QUEST": "Quest", - "TYPE": "Type", - "SEVERITY": "Severity", - "WORLD": "World", - "ACTIVE_WORLD": "Last active world", - "ACTIVE": "Active", - "UPDATE_TASKS": "Update Tasks", - "MISSION": "Mission", - "ABOUT_ME": "About Me", - "USERNAME": "Username", - "JOINED": "Joined", - "PLAYERS": "Players", - "ROLE": "Role", - "VISIBLE": "Visible", - "USE_QUEST_CARDS": "Questcards", - "CREATORNAME": "By" - }, - "INFO": { - "NO_ENTRIES": "no entries", - "OF": "of", - "ENTRIES_OF_THIS_PAGE": "Entries of this page:" - } - }, - "GLOBAL": { - "CANCEL": "Cancel", - "CREATE": "Create", - "EDIT": "Edit", - "DELETE": "Delete", - "NO_ENTRIES": "no entries", - "SEARCH": "Search", - "SAVE": "Save", - "SOLVEALLTASKS" : "Solve all tasks", - "IMAGE": "Image", - "FILENAME_IMAGE": "Filename image", - "CLICK_TO_SELECT": "clickt to select", - "GOLD": "Gold", - "XP": "XP", - "CLOSE": "Close", - "CONFIRMATION_MESSAGE": "Really delete this?" - }, - "DEVELOPER": { - "DEVELOPER": "Developer", - "GOLD": "Gold", - "XP": "XP", - "LEVEL": "Level", - "ABOUTME": "About me", - "AVATARRACE": "Race", - "AVATARCLASS": "Class", - "TROPHIES": "Trophies", - "TOOLTIP_EDIT": "Edit personal settings" - }, - "QUEST": { - "QUEST": "Quest", - "QUESTS": "Quests", - "NEW_QUEST": "New Quest", - "ADD_FREE_QUEST": "Add free quest", - "NO_FREE_QUESTS_AVAILABLE": "No free quests available", - "VISIBLE": "Visible", - "TOOLTIP_VISIBLE": "Make this quest visible to the players?" - }, - "ADVENTURE": { - "ADVENTURE": "Adventure", - "ADVENTURES": "Adventures", - "NEW_ADVENTURE": "New Adventure", - "SOLVEADVENTURE": "Solve Adventure" - }, - "WORLD": { - "WORLD": "World", - "WORLDS": "Worlds", - "ACTIVE": "Active", - "CHOOSE_WORLD": "Choose your current world", - "UPDATE_WORLDS": "Update Worlds", - "USE_QUEST_CARDS": "Use Questcard UI?", - "TOOLTIP_USE_QUEST_CARDS": "When checked, the players will see their quests in a matrix with cards instead of a table", - "TOOLTIP_ACTIVE": "When checked this world is selectable for playing" - }, - "TASK": { - "TASK": "Task", - "TASKS": "Tasks", - "SPECIALTASK": "Special Task", - "STANDARDTASK": "Standard Task", - "NEW_SPECIALTASK": "New Special Task", - "NEW_STANDARDTASK": "New Standard Task", - "ADD_FREE_TASK": "Add free task", - "NO_FREE_TASKS_AVAILABLE": "No free tasks available", - "SPECIALTASK_FOR_WORLD": "All Special Tasks for ", - "STANDARDTASK_FOR_WORLD": "All Standard Tasks for ", - "AVAILABLE_TASKS": "Available Tasks", - "MISSION": "Mission", - "UPDATE_STANDARD_TASKS_STATUS": "Update Status with SonarQube", - "UPDATE_STANDARD_TASKS_DISABLED_TOOLTIP": "Updating issues from SonarQube is not possible since no world has been set!", - "FREE_STANDARDTASKS": "Standard Tasks", - "FREE_SPECIALTASKS": "Special Tasks" - }, - "ARTEFACT": { - "ARTEFACTS": "Artefacts", - "NEW_ARTEFACT": "New Artefact", - "CREATE_ARTEFACT": "Create Artefact", - "EDIT_ARTEFACT": "Edit Artefact", - "SELECT_ICON": "Select icon", - "CLICK_TO_SELECT": "Click to select", - "PRICE": "Price", - "MIN_LEVEL": "Level req.", - "QUANTITY": "Available", - "NAME": "Name", - "DESCRIPTION": "Description", - "SKILLS": "Powers" - }, - "SKILL": { - "MORE_GOLD": "More Gold", - "MORE_XP": "More XP", - "CREATE_SKILL": "Create Power", - "ADD_SKILL": "Add Power", - "SKILLS": "Powers", - "NEW_SKILL": "New Power", - "TYPE": "Type", - "VALUE": "Amount", - "NAME": "Name" - }, - "LOGIN": { - "LOGIN": "Login", - "USERNAME": "Username", - "PASSWORD": "Password" - }, - "WIZARD": { - "WIZARD_SAYS": "The wizard thinks...", - "MSG_NO_WORLDS_FOUND": "No game world found", - "HINT_NO_WORLDS_FOUND": "To download SonarQube-Projects as worlds, the admin clicks the button 'Update worlds' in the worlds tab.", - "MSG_NO_ACTIVE_WORLD": "No active world found", - "HINT_NO_ACTIVE_WORLD": "To activate a world, the admin hits the worlds tab and edits a world and sets 'active' to play it!", - "MSG_NO_PLAYERS_ASSIGNED": "No players have been assigned to the active world yet", - "HINT_NO_PLAYERS_ASSIGNED": "To activate a player the admin hits the developers tab, selects a player and clicks 'active' at the chosen world!", - "MSG_NO_GAMEMASTER_ASSIGNED": "The current world is missing a gamemaster who will be creating the quests", - "HINT_NO_GAMEMASTER_ASSIGNED": "The admin hits the tab 'developers' and sets a player with the role 'gamemaster' as active for the world!", - "MSG_NO_TASKS_FOUND": "No tasks/issues from SonarQube have been retrieved yet", - "HINT_NO_TASKS_FOUND": "The gamemaster must hit the tab 'Tasks' and use the button 'Update Status' to connect to SonarQube and get the issues!", - "MSG_NO_QUESTS_FOUND": "No quests found", - "HINT_NO_QUESTS_FOUND": "The gamemaster should hit the tab 'quests' and create a quest with solvable tasks for the players!", - "MSG_NO_WORLD_SELECTED": "No active world selected", - "HINT_NO_WORLD_SELECTED": "In the top left menu item select your preferred world to get even more hints from the wizard! If that is not possible the admin has to activate players for that world first." - } -} +{ + "APP_COMPONENT": { + "STARTPAGE": "Startpage", + "MY_AVATAR": "My Avatar", + "ADVENTURES": "Adventures", + "QUESTS": "Quests", + "MARKETPLACE": "Marketplace", + "GAMEMASTER": "Gamemaster", + "ADMIN": "Admin", + "LOGIN": "Login", + "LOGOUT": "Logout", + "BACKGROUND": "Switch transparency of background", + "USER_PROFILE": "Open user profile" + }, + "ADVENTURE_PAGE": { + "MY_ADVENTURES": "My Adventures", + "JOIN_ADVENTURE": "Join this adventure!", + "LEAVE_ADVENTURE": "Leave this adventure...", + "MY_ADVENTURES_FOR_WORLD": "My Adventures for ", + "ADVENTURES_FOR_WORLD": "All Adventures for ", + "AVAILABLE_ADVENTURES": "Available Adventures", + "AVAILABLE_ADVENTURES_FOR_WORLD": "Available Adventures for " + }, + "QUEST_PAGE": { + "MY_QUESTS": "My Quests", + "SHOW_QUEST": "Show Quest", + "MY_QUESTS_FOR_WORLD": "My Quests for ", + "QUESTS_FOR_WORLD": "All Quests for ", + "JOIN_QUEST": "Join Quest", + "AVAILABLE_QUESTS": "Available Quests", + "AVAILABLE_QUESTS_FOR_WORLD": "Available Quests for ", + "COMBATED_BY": "combated by", + "SHOW_ISSUE_TOOLTIP":"Show SonarQube issue", + "JOIN_ISSUE_TOOLTIP":"Fight", + "CANCEL_ISSUE_TOOLTIP":"Stop fighting" + }, + "MARKETPLACE_PAGE": { + "MARKETPLACE": "Marketplace", + "ALREADY_OWNED_ITEM": "Already mine!", + "BUY_ITEM_COMPULSORY": "One click buy now!", + "SHOW_ITEM": "Show artefact details" + }, + "ARTEFACT_VIEW_PAGE": { + "TITLE": "Artefact description" + }, + "ADMIN_PAGE": { + "ALL_DEVELOPER": "All Developer", + "CREATE_DEVELOPER": "Create Developer", + "EDIT_DEVELOPER": "Edit Developer", + "DELETE_DEVELOPER": "Are you sure to delete?", + "SONAR_CUBE_HEADER": "SonarQube", + "SELECT_BACKGROUND": "Select Background" + }, + "START_PAGE": { + "CURRENT_PROGRESS": "Current Progress", + "PLEASE_LOG_IN": "Feel free to log in" + }, + "GAMEMASTER_PAGE": { + "ADVENTURE": { + "CREATE_ADVENTURE": "Create adventure", + "EDIT_ADVENTURE": "Edit adventure" + }, + "QUEST": { + "SUGGEST_TASKS": "Suggest tasks", + "GENERATE": "Generate", + "CREATE_QUEST": "Create quest", + "EDIT_QUEST": "Edit quest", + "EDIT_SOLVED_QUEST": "This quest is solved. You cannot edit it anymore." + }, + "TASK": { + "CREATE_SPECIALTASK": "Create Special Task", + "CREATE_STANDARDTASK": "Create Standard Task", + "EDIT_SPECIALTASK": "Edit Special Task", + "EDIT_STANDARDTASK": "Edit Standard Task", + "CONDITION": "Condition", + "AMOUNT": "Amount", + "ADD_FREE_TOOLTIP": "Add free task", + "SUGGEST_TASKS_TOOLTIP": "Suggest random tasks", + "SOLVE_TASK_TOOLTIP": "Change status to 'closed'", + "REMOVE_TASK_FROM_QUEST_TOOLTIP": "Remove task from quest" + } + }, + "TABLE": { + "COLUMNS": { + "TITLE": "Title", + "GOLD": "Gold", + "XP": "XP", + "LEVEL": "Level", + "ADVENTURE": "Adventure", + "STORY": "Story", + "STATUS": "Status", + "ID": "Id", + "NAME": "Name", + "PROJECT": "Project", + "QUEST": "Quest", + "TYPE": "Type", + "SEVERITY": "Severity", + "WORLD": "World", + "ACTIVE_WORLD": "Last active world", + "ACTIVE": "Active", + "UPDATE_TASKS": "Update Tasks", + "MISSION": "Mission", + "ABOUT_ME": "About Me", + "USERNAME": "Username", + "JOINED": "Joined", + "PLAYERS": "Players", + "ROLE": "Role", + "VISIBLE": "Visible", + "USE_QUEST_CARDS": "Questcards", + "LAST_LOGIN": "Last Login", + "CREATORNAME": "By" + }, + "INFO": { + "NO_ENTRIES": "no entries", + "OF": "of", + "ENTRIES_OF_THIS_PAGE": "Entries of this page:" + } + }, + "GLOBAL": { + "CANCEL": "Cancel", + "CREATE": "Create", + "EDIT": "Edit", + "DELETE": "Delete", + "NO_ENTRIES": "no entries", + "SEARCH": "Search", + "SAVE": "Save", + "SOLVEALLTASKS" : "Solve all tasks", + "IMAGE": "Image", + "FILENAME_IMAGE": "Filename image", + "CLICK_TO_SELECT": "clickt to select", + "GOLD": "Gold", + "XP": "XP", + "CLOSE": "Close", + "CONFIRMATION_MESSAGE": "Really delete this?" + }, + "DEVELOPER": { + "DEVELOPER": "Developer", + "GOLD": "Gold", + "XP": "XP", + "LEVEL": "Level", + "ABOUTME": "About me", + "AVATARRACE": "Race", + "AVATARCLASS": "Class", + "TROPHIES": "Trophies", + "TOOLTIP_EDIT": "Edit personal settings" + }, + "QUEST": { + "QUEST": "Quest", + "QUESTS": "Quests", + "NEW_QUEST": "New Quest", + "ADD_FREE_QUEST": "Add free quest", + "NO_FREE_QUESTS_AVAILABLE": "No free quests available", + "VISIBLE": "Visible", + "TOOLTIP_VISIBLE": "Make this quest visible to the players?" + }, + "ADVENTURE": { + "ADVENTURE": "Adventure", + "ADVENTURES": "Adventures", + "NEW_ADVENTURE": "New Adventure", + "SOLVEADVENTURE": "Solve Adventure" + }, + "WORLD": { + "WORLD": "World", + "WORLDS": "Worlds", + "ACTIVE": "Active", + "CHOOSE_WORLD": "Choose your current world", + "UPDATE_WORLDS": "Update Worlds", + "USE_QUEST_CARDS": "Use Questcard UI?", + "TOOLTIP_USE_QUEST_CARDS": "When checked, the players will see their quests in a matrix with cards instead of a table", + "TOOLTIP_ACTIVE": "When checked this world is selectable for playing" + }, + "TASK": { + "TASK": "Task", + "TASKS": "Tasks", + "SPECIALTASK": "Special Task", + "STANDARDTASK": "Standard Task", + "NEW_SPECIALTASK": "New Special Task", + "NEW_STANDARDTASK": "New Standard Task", + "ADD_FREE_TASK": "Add free task", + "NO_FREE_TASKS_AVAILABLE": "No free tasks available", + "SPECIALTASK_FOR_WORLD": "All Special Tasks for ", + "STANDARDTASK_FOR_WORLD": "All Standard Tasks for ", + "AVAILABLE_TASKS": "Available Tasks", + "MISSION": "Mission", + "UPDATE_STANDARD_TASKS_STATUS": "Update Status with SonarQube", + "UPDATE_STANDARD_TASKS_DISABLED_TOOLTIP": "Updating issues from SonarQube is not possible since no world has been set!", + "FREE_STANDARDTASKS": "Standard Tasks", + "FREE_SPECIALTASKS": "Special Tasks" + }, + "ARTEFACT": { + "ARTEFACTS": "Artefacts", + "NEW_ARTEFACT": "New Artefact", + "CREATE_ARTEFACT": "Create Artefact", + "EDIT_ARTEFACT": "Edit Artefact", + "SELECT_ICON": "Select icon", + "CLICK_TO_SELECT": "Click to select", + "PRICE": "Price", + "MIN_LEVEL": "Level req.", + "QUANTITY": "Available", + "NAME": "Name", + "DESCRIPTION": "Description", + "SKILLS": "Powers" + }, + "SKILL": { + "MORE_GOLD": "More Gold", + "MORE_XP": "More XP", + "CREATE_SKILL": "Create Power", + "ADD_SKILL": "Add Power", + "SKILLS": "Powers", + "NEW_SKILL": "New Power", + "TYPE": "Type", + "VALUE": "Amount", + "NAME": "Name" + }, + "LOGIN": { + "LOGIN": "Login", + "USERNAME": "Username", + "PASSWORD": "Password" + }, + "WIZARD": { + "WIZARD_SAYS": "The wizard thinks...", + "MSG_NO_WORLDS_FOUND": "No game world found", + "HINT_NO_WORLDS_FOUND": "To download SonarQube-Projects as worlds, the admin clicks the button 'Update worlds' in the worlds tab.", + "MSG_NO_ACTIVE_WORLD": "No active world found", + "HINT_NO_ACTIVE_WORLD": "To activate a world, the admin hits the worlds tab and edits a world and sets 'active' to play it!", + "MSG_NO_PLAYERS_ASSIGNED": "No players have been assigned to the active world yet", + "HINT_NO_PLAYERS_ASSIGNED": "To activate a player the admin hits the developers tab, selects a player and clicks 'active' at the chosen world!", + "MSG_NO_GAMEMASTER_ASSIGNED": "The current world is missing a gamemaster who will be creating the quests", + "HINT_NO_GAMEMASTER_ASSIGNED": "The admin hits the tab 'developers' and sets a player with the role 'gamemaster' as active for the world!", + "MSG_NO_TASKS_FOUND": "No tasks/issues from SonarQube have been retrieved yet", + "HINT_NO_TASKS_FOUND": "The gamemaster must hit the tab 'Tasks' and use the button 'Update Status' to connect to SonarQube and get the issues!", + "MSG_NO_QUESTS_FOUND": "No quests found", + "HINT_NO_QUESTS_FOUND": "The gamemaster should hit the tab 'quests' and create a quest with solvable tasks for the players!", + "MSG_NO_WORLD_SELECTED": "No active world selected", + "HINT_NO_WORLD_SELECTED": "In the top left menu item select your preferred world to get even more hints from the wizard! If that is not possible the admin has to activate players for that world first." + } +}