much progress on projects page

rest requires XHR/apiv2
This commit is contained in:
MiniDigger 2020-07-26 15:16:38 +02:00
parent 678e552bb6
commit 78715b4d1b
14 changed files with 296 additions and 92 deletions

View File

@ -3,6 +3,7 @@
[#-- @ftlvariable name="alerts" type="java.util.Map" --] [#-- @ftlvariable name="alerts" type="java.util.Map" --]
[#-- @ftlvariable name="routes" type="me.minidigger.hangar.util.RouteHelper" --] [#-- @ftlvariable name="routes" type="me.minidigger.hangar.util.RouteHelper" --]
[#-- @ftlvariable name="templateHelper" type="me.minidigger.hangar.util.TemplateHelper" --] [#-- @ftlvariable name="templateHelper" type="me.minidigger.hangar.util.TemplateHelper" --]
[#-- @ftlvariable name="modelData" type="me.minidigger.hangar.model.generated.ModelData" --] [#-- @ftlvariable name="headerData" type="me.minidigger.hangar.model.viewhelpers.HeaderData" --]
[#-- @ftlvariable name="rc" type="org.springframework.web.servlet.support.RequestContext" --] [#-- @ftlvariable name="rc" type="org.springframework.web.servlet.support.RequestContext" --]
[#-- @ftlvariable name="user" type="me.minidigger.hangar.model.generated.User" --] [#-- @ftlvariable name="user" type="me.minidigger.hangar.model.generated.User" --]
[#-- @ftlvariable name="config" type="me.minidigger.hangar.config.HangarConfig" --]

View File

@ -39,6 +39,12 @@ public class HangarConfig {
@Value("${authorPageSize:25}") @Value("${authorPageSize:25}")
private long authorPageSize; private long authorPageSize;
@Value("${maxTaglineLen:100}")
private long maxTaglineLen;
@Value("${authUrl:https://hangarauth.minidigger.me}")
private String authUrl;
public boolean isFakeUserEnabled() { public boolean isFakeUserEnabled() {
return fakeUserEnabled; return fakeUserEnabled;
} }
@ -84,4 +90,12 @@ public class HangarConfig {
public long getAuthorPageSize() { public long getAuthorPageSize() {
return authorPageSize; return authorPageSize;
} }
public long getMaxTaglineLen() {
return maxTaglineLen;
}
public String getAuthUrl() {
return authUrl;
}
} }

View File

@ -61,6 +61,7 @@ public class MvcConfig implements WebMvcConfigurer {
String message = te.getMessage(); String message = te.getMessage();
if (message.contains("org.springframework.web.servlet.support.RequestContext.getMessage")) { if (message.contains("org.springframework.web.servlet.support.RequestContext.getMessage")) {
System.out.println("[Template Error, most likely missing key] " + message); System.out.println("[Template Error, most likely missing key] " + message);
te.getCause().printStackTrace();
} else if (message.contains(" see cause exception in the Java stack trace.")) { } else if (message.contains(" see cause exception in the Java stack trace.")) {
te.printStackTrace(); te.printStackTrace();
} else { } else {

View File

@ -9,6 +9,7 @@ import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import me.minidigger.hangar.config.HangarConfig;
import me.minidigger.hangar.service.UserService; import me.minidigger.hangar.service.UserService;
import me.minidigger.hangar.util.RouteHelper; import me.minidigger.hangar.util.RouteHelper;
import me.minidigger.hangar.util.TemplateHelper; import me.minidigger.hangar.util.TemplateHelper;
@ -21,6 +22,8 @@ public abstract class HangarController {
private UserService userService; private UserService userService;
@Autowired @Autowired
private TemplateHelper templateHelper; private TemplateHelper templateHelper;
@Autowired
private HangarConfig hangarConfig;
protected ModelAndView fillModel(ModelAndView mav) { protected ModelAndView fillModel(ModelAndView mav) {
// helpers // helpers
@ -30,6 +33,7 @@ public abstract class HangarController {
builder.setExposeFields(true); builder.setExposeFields(true);
builder.setUseModelCache(true); builder.setUseModelCache(true);
mav.addObject("@helper", builder.build().getStaticModels()); mav.addObject("@helper", builder.build().getStaticModels());
mav.addObject("config", hangarConfig);
// alerts // alerts
if (mav.getModelMap().getAttribute("alerts") == null) { if (mav.getModelMap().getAttribute("alerts") == null) {
@ -39,7 +43,7 @@ public abstract class HangarController {
// user data // user data
mav.addObject("user", userService.getCurrentUser()); // TODO this is wrong mav.addObject("user", userService.getCurrentUser()); // TODO this is wrong
mav.addObject("cu", userService.getCurrentUser()); mav.addObject("cu", userService.getCurrentUser());
mav.addObject("modelData", userService.getModelData()); mav.addObject("headerData", userService.getHeaderData());
return mav; return mav;
} }

View File

@ -16,6 +16,7 @@ import me.minidigger.hangar.config.HangarConfig;
import me.minidigger.hangar.db.dao.HangarDao; import me.minidigger.hangar.db.dao.HangarDao;
import me.minidigger.hangar.db.dao.UserDao; import me.minidigger.hangar.db.dao.UserDao;
import me.minidigger.hangar.db.model.UsersTable; import me.minidigger.hangar.db.model.UsersTable;
import me.minidigger.hangar.model.viewhelpers.UserData;
import me.minidigger.hangar.service.AuthenticationService; import me.minidigger.hangar.service.AuthenticationService;
import me.minidigger.hangar.service.UserService; import me.minidigger.hangar.service.UserService;
@ -112,15 +113,13 @@ public class UsersController extends HangarController {
@RequestMapping("/{user}") @RequestMapping("/{user}")
public Object showProjects(@PathVariable String user) { public Object showProjects(@PathVariable String user) {
// TODO hacky test shit
UsersTable dbUser = userDao.get().getByName(user); UsersTable dbUser = userDao.get().getByName(user);
if (dbUser == null) { if (dbUser == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND); throw new ResponseStatusException(HttpStatus.NOT_FOUND);
} }
System.out.println(dbUser);
ModelAndView mav = new ModelAndView("users/projects"); ModelAndView mav = new ModelAndView("users/projects");
mav.addObject("u", dbUser); // TODO proper frontend model mav.addObject("u", userService.getUserData(dbUser));
mav.addObject("o", null); mav.addObject("o", null);
return fillModel(mav); // TODO implement showProjects request controller return fillModel(mav); // TODO implement showProjects request controller
} }

View File

@ -113,7 +113,7 @@ public class UsersTable implements Serializable {
} }
public boolean getIsLocked() { public boolean isLocked() {
return isLocked; return isLocked;
} }

View File

@ -1,24 +0,0 @@
package me.minidigger.hangar.model.generated;
public class ModelData {
public boolean hasNotice() {
return true;
}
public boolean hasUnreadNotifications() {
return true;
}
public boolean unresolvedFlags() {
return true;
}
public boolean hasProjectApprovals() {
return true;
}
public boolean hasReviewQueue() {
return true;
}
}

View File

@ -0,0 +1,87 @@
package me.minidigger.hangar.model.viewhelpers;
import me.minidigger.hangar.db.model.UsersTable;
import me.minidigger.hangar.model.Permission;
public class HeaderData {
private UsersTable currentUser = null;
private Permission globalPermission = Permission.None;
private boolean hasNotice = false;
private boolean hasUnreadNotifications = false;
private boolean unresolvedFlags = false;
private boolean hasProjectApprovals = false;
private boolean hasReviewQueue = false;
public boolean isAuthenticated() {
return currentUser != null;
}
public boolean hasUser() {
return currentUser != null;
}
public boolean isCurrentUser(UsersTable usersTable) {
return hasUser() && currentUser.getId() == usersTable.getId();
}
public boolean globalPerm(Permission permission) {
return globalPermission.has(permission);
}
public UsersTable getCurrentUser() {
return currentUser;
}
public void setCurrentUser(UsersTable currentUser) {
this.currentUser = currentUser;
}
public Permission getGlobalPermission() {
return globalPermission;
}
public void setGlobalPermission(Permission globalPermission) {
this.globalPermission = globalPermission;
}
public boolean hasNotice() {
return hasNotice;
}
public void setHasNotice(boolean hasNotice) {
this.hasNotice = hasNotice;
}
public boolean hasUnreadNotifications() {
return hasUnreadNotifications;
}
public void setHasUnreadNotifications(boolean hasUnreadNotifications) {
this.hasUnreadNotifications = hasUnreadNotifications;
}
public boolean hasUnresolvedFlags() {
return unresolvedFlags;
}
public void setUnresolvedFlags(boolean unresolvedFlags) {
this.unresolvedFlags = unresolvedFlags;
}
public boolean hasProjectApprovals() {
return hasProjectApprovals;
}
public void setHasProjectApprovals(boolean hasProjectApprovals) {
this.hasProjectApprovals = hasProjectApprovals;
}
public boolean hasReviewQueue() {
return hasReviewQueue;
}
public void setHasReviewQueue(boolean hasReviewQueue) {
this.hasReviewQueue = hasReviewQueue;
}
}

View File

@ -0,0 +1,112 @@
package me.minidigger.hangar.model.viewhelpers;
import java.util.ArrayList;
import java.util.List;
import me.minidigger.hangar.db.model.UsersTable;
import me.minidigger.hangar.model.Permission;
import me.minidigger.hangar.model.Role;
import me.minidigger.hangar.model.generated.Organization;
public class UserData {
private HeaderData headerData;
private UsersTable user;
private boolean isOrga;
private int projectCount;
private List<Organization> orgas;
private List<Role> globalRoles;
private Permission userPerm;
private Permission orgaPerm;
public static UserData of(UsersTable user, HeaderData headerData) {
return new UserData(headerData, user, false, 0, new ArrayList<>(), new ArrayList<>(), Permission.None, Permission.None);
}
public UserData(HeaderData headerData, UsersTable user, boolean isOrga, int projectCount, List<Organization> orgas, List<Role> globalRoles, Permission userPerm, Permission orgaPerm) {
this.headerData = headerData;
this.user = user;
this.isOrga = isOrga;
this.projectCount = projectCount;
this.orgas = orgas;
this.globalRoles = globalRoles;
this.userPerm = userPerm;
this.orgaPerm = orgaPerm;
}
public boolean hasUser() {
return headerData.hasUser();
}
public UsersTable currentUser() {
return headerData.getCurrentUser();
}
public boolean isCurrent() {
return headerData.isCurrentUser(user);
}
public HeaderData getHeaderData() {
return headerData;
}
public void setHeaderData(HeaderData headerData) {
this.headerData = headerData;
}
public UsersTable getUser() {
return user;
}
public void setUser(UsersTable user) {
this.user = user;
}
public boolean isOrga() {
return isOrga;
}
public void setOrga(boolean orga) {
isOrga = orga;
}
public int getProjectCount() {
return projectCount;
}
public void setProjectCount(int projectCount) {
this.projectCount = projectCount;
}
public List<Organization> getOrgas() {
return orgas;
}
public void setOrgas(List<Organization> orgas) {
this.orgas = orgas;
}
public List<Role> getGlobalRoles() {
return globalRoles;
}
public void setGlobalRoles(List<Role> globalRoles) {
this.globalRoles = globalRoles;
}
public Permission getUserPerm() {
return userPerm;
}
public void setUserPerm(Permission userPerm) {
this.userPerm = userPerm;
}
public Permission getOrgaPerm() {
return orgaPerm;
}
public void setOrgaPerm(Permission orgaPerm) {
this.orgaPerm = orgaPerm;
}
}

View File

@ -8,6 +8,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import me.minidigger.hangar.config.CacheConfig; import me.minidigger.hangar.config.CacheConfig;
@ -15,10 +16,14 @@ import me.minidigger.hangar.config.HangarConfig;
import me.minidigger.hangar.db.dao.HangarDao; import me.minidigger.hangar.db.dao.HangarDao;
import me.minidigger.hangar.db.dao.UserDao; import me.minidigger.hangar.db.dao.UserDao;
import me.minidigger.hangar.db.model.UsersTable; import me.minidigger.hangar.db.model.UsersTable;
import me.minidigger.hangar.model.Permission;
import me.minidigger.hangar.model.Role;
import me.minidigger.hangar.model.UserOrdering; import me.minidigger.hangar.model.UserOrdering;
import me.minidigger.hangar.model.generated.ModelData; import me.minidigger.hangar.model.generated.Organization;
import me.minidigger.hangar.model.viewhelpers.Author; import me.minidigger.hangar.model.viewhelpers.Author;
import me.minidigger.hangar.model.viewhelpers.HeaderData;
import me.minidigger.hangar.model.viewhelpers.Staff; import me.minidigger.hangar.model.viewhelpers.Staff;
import me.minidigger.hangar.model.viewhelpers.UserData;
import me.minidigger.hangar.security.HangarAuthentication; import me.minidigger.hangar.security.HangarAuthentication;
@Service @Service
@ -47,10 +52,13 @@ public class UserService {
return null; return null;
} }
public ModelData getModelData() { public HeaderData getHeaderData() {
ModelData modelData = new ModelData(); HeaderData headerData = new HeaderData();
return modelData; headerData.setCurrentUser(getCurrentUser());
// TODO fill headerdata
return headerData;
} }
@CacheEvict(value = CacheConfig.AUTHORS_CACHE, allEntries = true) @CacheEvict(value = CacheConfig.AUTHORS_CACHE, allEntries = true)
@ -106,4 +114,15 @@ public class UserService {
return " "; return " ";
} }
} }
public UserData getUserData(UsersTable user) {
// TODO getUserData
boolean isOrga = false;
int projectCount = 1;
List<Organization> orgas = new ArrayList<>();
List<Role> globalRoles = List.of(Role.HANGAR_ADMIN);
Permission userPerm = Permission.All;
Permission orgaPerm = Permission.None;
return new UserData(getHeaderData(), user, isOrga, projectCount, orgas, globalRoles, userPerm, orgaPerm);
}
} }

View File

@ -36,7 +36,7 @@ public class ProjectFactory {
} }
public String getUploadError(UsersTable user) { public String getUploadError(UsersTable user) {
if (user.getIsLocked()) { if (user.isLocked()) {
return "error.user.locked"; return "error.user.locked";
} else { } else {
return null; return null;

View File

@ -85,7 +85,7 @@
<#-- Show user controls --> <#-- Show user controls -->
<li class="dropdown user-controls nav-icon"> <li class="dropdown user-controls nav-icon">
<a href="#" class="drop-down-toggle user-toggle" data-toggle="dropdown"> <a href="#" class="drop-down-toggle user-toggle" data-toggle="dropdown">
<#if modelData.hasNotice()> <#if headerData.hasNotice()>
<span class="unread"></span> <span class="unread"></span>
</#if> </#if>
<img height="32" width="32" class="user-avatar" src="${cu.getAvatarUrl()}" alt="${cu.name}"/> <img height="32" width="32" class="user-avatar" src="${cu.getAvatarUrl()}" alt="${cu.name}"/>
@ -104,7 +104,7 @@
<a href="${routes.getRouteUrl("users.showNotifications", "", "")}"> <a href="${routes.getRouteUrl("users.showNotifications", "", "")}">
<i class="fas fa-bell mr-1"></i> <i class="fas fa-bell mr-1"></i>
<span><@spring.message "notification.plural" /> <span><@spring.message "notification.plural" />
<#if modelData.hasUnreadNotifications()> <#if headerData.hasUnreadNotifications()>
<span class="unread"></span> <span class="unread"></span>
</#if> </#if>
</span> </span>
@ -116,7 +116,7 @@
<a href="${routes.getRouteUrl("showFlags")}"> <a href="${routes.getRouteUrl("showFlags")}">
<i class="fas fa-flag mr-1"></i> <i class="fas fa-flag mr-1"></i>
<span><@spring.message "project.flag.plural" /> <span><@spring.message "project.flag.plural" />
<#if modelData.unresolvedFlags()> <#if headerData.hasUnresolvedFlags()>
<span class="unread"></span> <span class="unread"></span>
</#if> </#if>
</span> </span>
@ -130,7 +130,7 @@
<i class="fas fa-thumbs-up mr-1"></i> <i class="fas fa-thumbs-up mr-1"></i>
<span> <span>
Project approvals Project approvals
<#if modelData.hasProjectApprovals()> <#if headerData.hasProjectApprovals()>
<span class="unread"></span> <span class="unread"></span>
</#if> </#if>
</span> </span>
@ -143,7 +143,7 @@
<a href="${routes.getRouteUrl("showQueue")}"> <a href="${routes.getRouteUrl("showQueue")}">
<i class="far fa-thumbs-up mr-1"></i> <i class="far fa-thumbs-up mr-1"></i>
<span><@spring.message "user.queue" /> <span><@spring.message "user.queue" />
<#if modelData.hasReviewQueue()> <#if headerData.hasReviewQueue()>
<span class="unread"></span> <span class="unread"></span>
</#if> </#if>
</span> </span>

View File

@ -14,32 +14,26 @@
@(u: UserData, o: Option[(OrganizationData, ScopedOrganizationData)])( @(u: UserData, o: Option[(OrganizationData, ScopedOrganizationData)])(
implicit messages: Messages, flash: Flash, request: OreRequest[_], config: OreConfig, assetsFinder: AssetsFinder)--> implicit messages: Messages, flash: Flash, request: OreRequest[_], config: OreConfig, assetsFinder: AssetsFinder)-->
<#-- <#function canEditOrgMembers>
@canEditOrgMembers = @{ <#return u.isOrga() && o?? && o.scopedData.permissions.has(Permissions.ManageOrganizationMembers)>
u.isOrga && </#function>
o.exists { case (_, scopedData) =>
scopedData.permissions.has(Permission.sManageOrganizationMembers)
}
}
-->
<#assign scriptsVar> <#assign scriptsVar>
<script type="text/javascript" src="<@hangar.url "build/user-profile.js" />"></script> <script type="text/javascript" src="<@hangar.url "build/user-profile.js" />"></script>
<#if u.isOrga && o?? && canEditOrgMembers> <#if u.isOrga() && o?? && canEditOrgMembers()>
<script type="text/javascript" src="<@hangar.url "javascripts/orgInvites.js" />"></script> <script type="text/javascript" src="<@hangar.url "javascripts/orgInvites.js" />"></script>
</#if> </#if>
<script type="text/javascript" src="<@hangar.url "javascripts/userSearch.js" />"></script> <script type="text/javascript" src="<@hangar.url "javascripts/userSearch.js" />"></script>
<script type="text/javascript" src="<@hangar.url "javascripts/memberList.js" />"></script> <script type="text/javascript" src="<@hangar.url "javascripts/memberList.js" />"></script>
</#assign> </#assign>
<#assign o={} /><#-- todo dummy value -->
<@users.view u=u o=o additionalScripts=scriptsVar> <@users.view u=u o=o additionalScripts=scriptsVar>
<div class="row"> <div class="row">
<div class="col-md-8"> <div class="col-md-8">
<div id="user-profile"></div> <div id="user-profile"></div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<#if u.isOrga && o?? && canEditOrgMembers> <#if u.isOrga() && o?? && canEditOrgMembers()>
<div class="panel-user-info panel panel-default"> <div class="panel-user-info panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"><@spring.message "project.manager" /></h3> <h3 class="panel-title"><@spring.message "project.manager" /></h3>
@ -67,17 +61,17 @@
</table> </table>
</div> </div>
</#if> </#if>
<#if !u.isOrga!false> <#if !u.isOrga()>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"> <@spring.message "org.plural" /></h3> <h3 class="panel-title"> <@spring.message "org.plural" /></h3>
</div> </div>
<table class="table panel-body"> <table class="table panel-body">
<tbody> <tbody>
<#if u.orgas.isEmpty> <#if u.orgas?size == 0>
<tr><td><i class="minor"><i class="fas fa-star"></i> <@spring.messageArgs "user.noOrganizations" u.user.name /></i></td></tr> <tr><td><i class="minor"><i class="fas fa-star"></i> <@spring.messageArgs code="user.noOrganizations" args=[u.user.name] /></i></td></tr>
<#else> <#else>
@u.orgas.map { case (organization, user, role, _) => <#list u.orgas as organization> <#-- , user, role -->
<tr> <tr>
<td> <td>
<#import "*/utils/userAvatar.ftlh" as userAvatar> <#import "*/utils/userAvatar.ftlh" as userAvatar>
@ -90,7 +84,7 @@
</div> </div>
</td> </td>
</tr> </tr>
} </#list>
</#if> </#if>
</tbody> </tbody>
</table> </table>
@ -136,11 +130,10 @@
</div> </div>
</div> </div>
<#else> <#else>
@o.map { case (orgData, scopedData) => <#assign orgData=o> <#-- todo fix scopeddata -->
<@memberList.memberList j=orgData editable=true perms=scopedData.permissions <@memberList.memberList j=orgData editable=true perms=scopedData.permissions
saveCall=routes.getRouteUrl("org.updateMembers", orgData.orga.name) saveCall=routes.getRouteUrl("org.updateMembers", orgData.orga.name)
removeCall=routes.getRouteUrl("org.removeMember", orgData.orga.name) /> removeCall=routes.getRouteUrl("org.removeMember", orgData.orga.name) />
}
</#if> </#if>
</div> </div>
</div> </div>

View File

@ -7,23 +7,21 @@
<#import "*/utils/form.ftlh" as form> <#import "*/utils/form.ftlh" as form>
<#import "*/utils/csrf.ftlh" as csrf> <#import "*/utils/csrf.ftlh" as csrf>
<#-- <#-- @ftlvariable name="u" type="me.minidigger.hangar.model.viewhelpers.UserData" -->
@(u: UserData, o: Option[(OrganizationData, ScopedOrganizationData)], additionalScripts: Html = Html(""))(content: Html)(implicit messages: Messages, flash: Flash, request: OreRequest[_], config: OreConfig, assetsFinder: AssetsFinder) <#-- @ftlvariable name="Permission" type="me.minidigger.hangar.model.Permission" -->
--> <#assign Permission=@helper["me.minidigger.hangar.model.Permission"]>
<#function canEditOrgSettings u o={}>
<#return u.isOrga() && o?? && o.scopedData.permissions.has(Permission.EditOrganizationSettings)>
</#function>
<#macro view u o additionalScripts=""> <#macro view u o={} additionalScripts="">
<#--@canEditOrgSettings = @{
u.isOrga &&
o.exists { case (_, scopedData) =>
scopedData.permissions.has(Permission.EditOrganizationSettings)
}
}-->
<#assign scriptsVar> <#assign scriptsVar>
<script type="text/javascript" src="<@hangar.url "javascripts/userPage.js" />"></script> <script type="text/javascript" src="<@hangar.url "javascripts/userPage.js" />"></script>
<script <#-- @CSPNonce.attr -->> <script <#-- @CSPNonce.attr -->>
window.USERNAME ='${u.user.name}'; window.USERNAME ='${u.user.name}';
<#list Category.values as category> <#-- todo --> <#assign Category=@helper["me.minidigger.hangar.model.Category"]>
<#list Category.values() as category>
CATEGORY_TITLE['${category.apiName}'] = '${category.title}'; CATEGORY_TITLE['${category.apiName}'] = '${category.title}';
CATEGORY_ICON['${category.apiName}'] = '${category.icon}'; CATEGORY_ICON['${category.apiName}'] = '${category.icon}';
</#list> </#list>
@ -47,16 +45,16 @@
<!-- Title --> <!-- Title -->
<span class="user-badge"> <span class="user-badge">
<#assign avatarClass> <#assign avatarClass>
user-avatar-md <#if canEditOrgSettings>organization-avatar</#if> user-avatar-md <#if canEditOrgSettings(u, o)>organization-avatar</#if>
</#assign> </#assign>
<@userAvatar.userAvatar userName=u.user.name avatarUrl=u.user.avatarUrl clazz=avatarClass /> <@userAvatar.userAvatar userName=u.user.name avatarUrl=u.user.avatarUrl clazz=avatarClass />
<#if canEditOrgSettings> <#if canEditOrgSettings(u, o)>
<div class="edit-avatar" style="display: none;"> <div class="edit-avatar" style="display: none;">
<a href="${routes.getRouteUrl("org.updateAvatar", u.user.name)}"><i class="fas fa-edit"></i> <@spring.message "user.editAvatar" /></a> <a href="${routes.getRouteUrl("org.updateAvatar", u.user.name)}"><i class="fas fa-edit"></i> <@spring.message "user.editAvatar" /></a>
</div> </div>
<#if !u.currentUser.readPrompts.contains(Prompt.ChangeAvatar)> <#if !u.headerData.currentUser.readPrompts.contains(Prompt.ChangeAvatar)>
<@prompt.prompt prompt=Prompt.ChangeAvatar id="popover-avatar" /> <@prompt.prompt prompt=Prompt.ChangeAvatar id="popover-avatar" />
</#if> </#if>
</#if> </#if>
@ -65,14 +63,14 @@
<h1 class="username"> <h1 class="username">
${u.user.name} ${u.user.name}
<#if u.isCurrent && !u.isOrga> <#if u.isCurrent() && !u.isOrga()>
<a class="user-settings" href="${config.security.api.url}/accounts/settings"> <a class="user-settings" href="${config.getAuthUrl()}/accounts/settings">
<i class="fas fa-cog" data-toggle="tooltip" <i class="fas fa-cog" data-toggle="tooltip"
data-placement="top" title="Settings"></i> data-placement="top" title="Settings"></i>
</a> </a>
<span data-toggle="modal" data-target="#modal-lock"> <span data-toggle="modal" data-target="#modal-lock">
<i class="fas @{if (u.user.isLocked) "fa-lock" else "fa-unlock-alt"} action-lock-account" data-toggle="tooltip" <i class="fas <#if u.user.isLocked()>fa-lock<#else>fa-unlock-alt</#if> action-lock-account" data-toggle="tooltip"
data-placement="top" title="<@spring.message "user.lock" />"></i> data-placement="top" title="<@spring.message "user.lock" />"></i>
</span> </span>
@ -81,7 +79,7 @@
</a> </a>
</#if> </#if>
<#if u.hasUser> <#if u.hasUser()>
<#if u.userPerm.has(Permission.ModNotesAndFlags) || u.userPerm.has(Permission.Reviewer)> <#if u.userPerm.has(Permission.ModNotesAndFlags) || u.userPerm.has(Permission.Reviewer)>
<a class="user-settings" href="${routes.getRouteUrl("showActivities", u.user.name)}"> <a class="user-settings" href="${routes.getRouteUrl("showActivities", u.user.name)}">
<i class="fas fa-calendar" data-toggle="tooltip" <i class="fas fa-calendar" data-toggle="tooltip"
@ -90,7 +88,7 @@
</#if> </#if>
</#if> </#if>
<#if request.headerData.globalPerm(Permission.EditAllUserSettings)> <#if u.headerData.globalPerm(Permission.EditAllUserSettings)>
<a class="user-settings" href="${routes.getRouteUrl("userAdmin", u.user.name)}"> <a class="user-settings" href="${routes.getRouteUrl("userAdmin", u.user.name)}">
<i class="fas fa-wrench" data-toggle="tooltip" <i class="fas fa-wrench" data-toggle="tooltip"
data-placement="top" title="User Admin"></i> data-placement="top" title="User Admin"></i>
@ -101,13 +99,13 @@
<div class="user-tag"> <div class="user-tag">
<i class="minor"> <i class="minor">
<#if u.user.tagline??> <#if u.user.tagline??>
${u.user.tagline.get} ${u.user.tagline}
<#elseif u.isCurrent || canEditOrgSettings> <#elseif u.isCurrent() || canEditOrgSettings(u, o)>
Add a tagline Add a tagline
</#if> </#if>
</i> </i>
<#if u.isCurrent || canEditOrgSettings> <#if u.isCurrent() || canEditOrgSettings(u, o)>
<a href="#" data-toggle="modal" data-target="#modal-tagline"> <a href="#" data-toggle="modal" data-target="#modal-tagline">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</a> </a>
@ -118,17 +116,15 @@
<!-- Roles --> <!-- Roles -->
<ul class="user-roles"> <ul class="user-roles">
@defining(u.globalRoles.toSeq.sortBy(_.permissions: Long).lastOption) { roleOpt => <#list u.globalRoles?sort_by("value") as role>
@roleOpt.map { role =>
<li class="user-role channel" style="background-color: ${role.color.hex}">${role.title}</li> <li class="user-role channel" style="background-color: ${role.color.hex}">${role.title}</li>
} </#list>
}
</ul> </ul>
<div class="user-info"> <div class="user-info">
<i class="minor">${u.projectCount}&nbsp;<#if u.projectCount == 1>project<#else>projects</#if></i><br/> <i class="minor">${u.projectCount}&nbsp;<#if u.projectCount == 1>project<#else>projects</#if></i><br/>
<i class="minor"> <i class="minor">
<@spring.messageArgs "user.memberSince" u.user.joinDate.map(prettifyDate)!prettifyDate(u.user.createdAt) /> <@spring.messageArgs code="user.memberSince" args=[(u.user.joinDate!u.user.createdAt).format("yyyy-MM-dd")] />
</i><br/> </i><br/>
<a href="https://papermc.io/forums/users/${u.user.name}"> <a href="https://papermc.io/forums/users/${u.user.name}">
<@spring.message "user.viewOnForums" /> <i class="fas fa-external-link-alt"></i> <@spring.message "user.viewOnForums" /> <i class="fas fa-external-link-alt"></i>
@ -140,16 +136,18 @@
<#nested /> <#nested />
<#assign lockModalTitle> <#assign lockModalTitle>
<#if u.user.isLocked> <#compress>
<#if u.user.isLocked()>
user.unlock user.unlock
<#else> <#else>
user.lock user.lock
</#if> </#if>
</#compress>
</#assign> </#assign>
<@modal.modal lockModalTitle "modal-lock" "label-lock"> <@modal.modal lockModalTitle "modal-lock" "label-lock">
<div class="modal-body"> <div class="modal-body">
<p class="minor"> <p class="minor">
<#if u.user.isLocked> <#if u.user.isLocked()>
<@spring.message "user.unlock.confirm" /> <@spring.message "user.unlock.confirm" />
<#else> <#else>
<@spring.message "user.lock.confirm" /> <@spring.message "user.lock.confirm" />
@ -159,7 +157,7 @@
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><@spring.message "general.close" /></button> <button type="button" class="btn btn-default" data-dismiss="modal"><@spring.message "general.close" /></button>
<@form.form method="POST" action=routes.getRouteUrl("users.verify", routes.getRouteUrl("users.setLocked", u.user.name, !u.user.isLocked, "", "")) class="form-inline"> <@form.form method="POST" action=routes.getRouteUrl("users.verify", routes.getRouteUrl("users.setLocked", u.user.name, (!u.user.isLocked())?string, "", "")) class="form-inline">
<@csrf.formField /> <@csrf.formField />
<button type="submit" class="btn btn-primary"><@spring.message "general.continue" /></button> <button type="submit" class="btn btn-primary"><@spring.message "general.continue" /></button>
</@form.form> </@form.form>
@ -176,7 +174,7 @@
<p><@spring.message "user.tagline.info" /></p> <p><@spring.message "user.tagline.info" /></p>
</div> </div>
<input class="form-control" type="text" value="${u.user.tagline!""}" id="tagline" <input class="form-control" type="text" value="${u.user.tagline!""}" id="tagline"
name="tagline" maxlength="${config.ore.users.maxTaglineLen}" /> name="tagline" maxlength="${config.getMaxTaglineLen()}" />
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>