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="routes" type="me.minidigger.hangar.util.RouteHelper" --]
[#-- @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="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}")
private long authorPageSize;
@Value("${maxTaglineLen:100}")
private long maxTaglineLen;
@Value("${authUrl:https://hangarauth.minidigger.me}")
private String authUrl;
public boolean isFakeUserEnabled() {
return fakeUserEnabled;
}
@ -84,4 +90,12 @@ public class HangarConfig {
public long getAuthorPageSize() {
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();
if (message.contains("org.springframework.web.servlet.support.RequestContext.getMessage")) {
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.")) {
te.printStackTrace();
} else {

View File

@ -9,6 +9,7 @@ import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.Map;
import me.minidigger.hangar.config.HangarConfig;
import me.minidigger.hangar.service.UserService;
import me.minidigger.hangar.util.RouteHelper;
import me.minidigger.hangar.util.TemplateHelper;
@ -21,6 +22,8 @@ public abstract class HangarController {
private UserService userService;
@Autowired
private TemplateHelper templateHelper;
@Autowired
private HangarConfig hangarConfig;
protected ModelAndView fillModel(ModelAndView mav) {
// helpers
@ -30,6 +33,7 @@ public abstract class HangarController {
builder.setExposeFields(true);
builder.setUseModelCache(true);
mav.addObject("@helper", builder.build().getStaticModels());
mav.addObject("config", hangarConfig);
// alerts
if (mav.getModelMap().getAttribute("alerts") == null) {
@ -39,7 +43,7 @@ public abstract class HangarController {
// user data
mav.addObject("user", userService.getCurrentUser()); // TODO this is wrong
mav.addObject("cu", userService.getCurrentUser());
mav.addObject("modelData", userService.getModelData());
mav.addObject("headerData", userService.getHeaderData());
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.UserDao;
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.UserService;
@ -112,15 +113,13 @@ public class UsersController extends HangarController {
@RequestMapping("/{user}")
public Object showProjects(@PathVariable String user) {
// TODO hacky test shit
UsersTable dbUser = userDao.get().getByName(user);
if (dbUser == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
}
System.out.println(dbUser);
ModelAndView mav = new ModelAndView("users/projects");
mav.addObject("u", dbUser); // TODO proper frontend model
mav.addObject("u", userService.getUserData(dbUser));
mav.addObject("o", null);
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;
}

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.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
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.UserDao;
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.generated.ModelData;
import me.minidigger.hangar.model.generated.Organization;
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.UserData;
import me.minidigger.hangar.security.HangarAuthentication;
@Service
@ -47,10 +52,13 @@ public class UserService {
return null;
}
public ModelData getModelData() {
ModelData modelData = new ModelData();
public HeaderData getHeaderData() {
HeaderData headerData = new HeaderData();
return modelData;
headerData.setCurrentUser(getCurrentUser());
// TODO fill headerdata
return headerData;
}
@CacheEvict(value = CacheConfig.AUTHORS_CACHE, allEntries = true)
@ -106,4 +114,15 @@ public class UserService {
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) {
if (user.getIsLocked()) {
if (user.isLocked()) {
return "error.user.locked";
} else {
return null;

View File

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

View File

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

View File

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