mirror of
https://github.com/HangarMC/Hangar.git
synced 2024-12-27 07:03:26 +08:00
Project settings (#49)
This commit is contained in:
parent
c5342bbb98
commit
6546b1a13a
@ -6,5 +6,4 @@
|
||||
[#-- @ftlvariable name="templateHelper" type="me.minidigger.hangar.util.TemplateHelper" --]
|
||||
[#-- @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" --]
|
||||
|
@ -542,7 +542,6 @@ public class HangarConfig {
|
||||
}
|
||||
|
||||
// Added to make freemarker realize they are here
|
||||
|
||||
public FakeUserConfig getFakeUser() {
|
||||
return fakeUser;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package me.minidigger.hangar.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import freemarker.template.TemplateException;
|
||||
import me.minidigger.hangar.model.converters.CategoryConverter;
|
||||
import me.minidigger.hangar.util.RouteHelper;
|
||||
import no.api.freemarker.java8.Java8ObjectWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -134,5 +135,6 @@ public class MvcConfig implements WebMvcConfigurer {
|
||||
};
|
||||
}
|
||||
});
|
||||
registry.addConverter(new CategoryConverter());
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
package me.minidigger.hangar.controller;
|
||||
|
||||
import me.minidigger.hangar.util.AlertUtil;
|
||||
import me.minidigger.hangar.util.AlertUtil.AlertType;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
@ -12,13 +15,24 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import me.minidigger.hangar.controller.HangarController;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
import springfox.documentation.schema.Enums;
|
||||
|
||||
@Controller
|
||||
public class ApplicationController extends HangarController {
|
||||
|
||||
@RequestMapping("/")
|
||||
public ModelAndView showHome() {
|
||||
return fillModel( new ModelAndView("home"));
|
||||
public ModelAndView showHome(@ModelAttribute("alertType") String alertType, @ModelAttribute("alertMsg") String alertMsg) {
|
||||
ModelAndView mav = new ModelAndView("home");
|
||||
AlertType type;
|
||||
try {
|
||||
type = AlertType.valueOf(alertType);
|
||||
} catch (IllegalArgumentException e) {
|
||||
type = null;
|
||||
}
|
||||
if (type != null && alertMsg != null)
|
||||
AlertUtil.showAlert(mav, type, alertMsg);
|
||||
return fillModel(mav);
|
||||
}
|
||||
|
||||
@Secured("ROLE_USER")
|
||||
|
@ -1,8 +1,28 @@
|
||||
package me.minidigger.hangar.controller;
|
||||
|
||||
|
||||
import me.minidigger.hangar.config.HangarConfig;
|
||||
import me.minidigger.hangar.db.dao.HangarDao;
|
||||
import me.minidigger.hangar.db.dao.ProjectDao;
|
||||
import me.minidigger.hangar.db.dao.UserDao;
|
||||
import me.minidigger.hangar.db.model.ProjectsTable;
|
||||
import me.minidigger.hangar.db.model.UsersTable;
|
||||
import me.minidigger.hangar.model.Category;
|
||||
import me.minidigger.hangar.model.Permission;
|
||||
import me.minidigger.hangar.model.Role;
|
||||
import me.minidigger.hangar.model.viewhelpers.ProjectData;
|
||||
import me.minidigger.hangar.model.viewhelpers.ProjectPage;
|
||||
import me.minidigger.hangar.model.viewhelpers.ScopedProjectData;
|
||||
import me.minidigger.hangar.service.OrgService;
|
||||
import me.minidigger.hangar.service.UserService;
|
||||
import me.minidigger.hangar.service.project.PagesSerivce;
|
||||
import me.minidigger.hangar.service.project.ProjectFactory;
|
||||
import me.minidigger.hangar.service.project.ProjectService;
|
||||
import me.minidigger.hangar.util.AlertUtil;
|
||||
import me.minidigger.hangar.util.AlertUtil.AlertType;
|
||||
import me.minidigger.hangar.util.HangarException;
|
||||
import me.minidigger.hangar.util.RouteHelper;
|
||||
import me.minidigger.hangar.util.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
@ -13,25 +33,12 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import me.minidigger.hangar.db.dao.HangarDao;
|
||||
import me.minidigger.hangar.db.dao.UserDao;
|
||||
import me.minidigger.hangar.db.model.ProjectsTable;
|
||||
import me.minidigger.hangar.db.model.UsersTable;
|
||||
import me.minidigger.hangar.model.Category;
|
||||
import me.minidigger.hangar.model.Permission;
|
||||
import me.minidigger.hangar.model.viewhelpers.ProjectData;
|
||||
import me.minidigger.hangar.model.viewhelpers.ScopedProjectData;
|
||||
import me.minidigger.hangar.service.OrgService;
|
||||
import me.minidigger.hangar.service.UserService;
|
||||
import me.minidigger.hangar.service.project.ProjectFactory;
|
||||
import me.minidigger.hangar.service.project.ProjectService;
|
||||
import me.minidigger.hangar.util.AlertUtil;
|
||||
import me.minidigger.hangar.util.HangarException;
|
||||
import me.minidigger.hangar.util.RouteHelper;
|
||||
|
||||
@Controller
|
||||
public class ProjectsController extends HangarController {
|
||||
|
||||
@ -45,9 +52,10 @@ public class ProjectsController extends HangarController {
|
||||
private final HangarDao<UserDao> userDao;
|
||||
private final ProjectService projectService;
|
||||
private final PagesSerivce pagesSerivce;
|
||||
private final HangarDao<ProjectDao> projectDao;
|
||||
|
||||
@Autowired
|
||||
public ProjectsController(HangarConfig hangarConfig, UserService userService, OrgService orgService, RouteHelper routeHelper, ProjectFactory projectFactory, HangarDao<UserDao> userDao, ProjectService projectService, PagesSerivce pagesSerivce) {
|
||||
public ProjectsController(HangarConfig hangarConfig, UserService userService, OrgService orgService, RouteHelper routeHelper, ProjectFactory projectFactory, HangarDao<UserDao> userDao, ProjectService projectService, HangarDao<ProjectDao> projectDao, PagesSerivce pagesSerivce) {
|
||||
this.hangarConfig = hangarConfig;
|
||||
this.userService = userService;
|
||||
this.orgService = orgService;
|
||||
@ -56,6 +64,7 @@ public class ProjectsController extends HangarController {
|
||||
this.userDao = userDao;
|
||||
this.projectService = projectService;
|
||||
this.pagesSerivce = pagesSerivce;
|
||||
this.projectDao = projectDao;
|
||||
}
|
||||
|
||||
@PostMapping(value = "/new", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
@ -69,19 +78,19 @@ public class ProjectsController extends HangarController {
|
||||
String uploadError = projectFactory.getUploadError(currentUser);
|
||||
if (uploadError != null) {
|
||||
ModelAndView mav = showCreator();
|
||||
AlertUtil.showAlert(mav, "error", uploadError);
|
||||
AlertUtil.showAlert(mav, AlertType.ERROR, uploadError);
|
||||
return fillModel(mav);
|
||||
}
|
||||
// validate input
|
||||
Category category = Category.fromTitle(cat);
|
||||
if (category == null) {
|
||||
ModelAndView mav = showCreator();
|
||||
AlertUtil.showAlert(mav, "error", "error.project.categoryNotFound");
|
||||
AlertUtil.showAlert(mav, AlertType.ERROR, "error.project.categoryNotFound");
|
||||
return fillModel(mav);
|
||||
}
|
||||
if (!ID_PATTERN.matcher(pluginId).matches()) {
|
||||
ModelAndView mav = showCreator();
|
||||
AlertUtil.showAlert(mav, "error", "error.project.invalidPluginId");
|
||||
AlertUtil.showAlert(mav, AlertType.ERROR, "error.project.invalidPluginId");
|
||||
return fillModel(mav);
|
||||
}
|
||||
// TODO check that currentUser can upload to owner
|
||||
@ -89,7 +98,7 @@ public class ProjectsController extends HangarController {
|
||||
UsersTable ownerUser = userDao.get().getById(owner);
|
||||
if (ownerUser == null) {
|
||||
ModelAndView mav = showCreator();
|
||||
AlertUtil.showAlert(mav, "error", "error.project.ownerNotFound");
|
||||
AlertUtil.showAlert(mav, AlertType.ERROR, "error.project.ownerNotFound");
|
||||
return fillModel(mav);
|
||||
}
|
||||
|
||||
@ -99,7 +108,7 @@ public class ProjectsController extends HangarController {
|
||||
project = projectFactory.createProject(ownerUser, name, pluginId, category, description);
|
||||
} catch (HangarException ex) {
|
||||
ModelAndView mav = showCreator();
|
||||
AlertUtil.showAlert(mav, "error", ex.getMessageKey());
|
||||
AlertUtil.showAlert(mav, AlertType.ERROR, ex.getMessageKey());
|
||||
return fillModel(mav);
|
||||
}
|
||||
|
||||
@ -137,8 +146,7 @@ public class ProjectsController extends HangarController {
|
||||
ModelAndView mav = new ModelAndView("projects/pages/view");
|
||||
ProjectData projectData = projectService.getProjectData(author, slug);
|
||||
mav.addObject("p", projectData);
|
||||
ScopedProjectData sp = new ScopedProjectData();
|
||||
sp.setPermissions(Permission.IsProjectOwner.add(Permission.EditPage));
|
||||
ScopedProjectData sp = projectService.getScopedProjectData(projectData.getProject().getId(), projectData.getProjectOwner().getId());
|
||||
mav.addObject("sp", sp);
|
||||
mav.addObject("page", ProjectPage.of(pagesSerivce.getPage(projectData.getProject().getId(), hangarConfig.pages.home.getName())));
|
||||
mav.addObject("parentPage");
|
||||
@ -195,20 +203,37 @@ public class ProjectsController extends HangarController {
|
||||
|
||||
@Secured("ROLE_USER")
|
||||
@RequestMapping("/{author}/{slug}/manage")
|
||||
public Object showSettings(@PathVariable Object author, @PathVariable Object slug) {
|
||||
return null; // TODO implement showSettings request controller
|
||||
public ModelAndView showSettings(@PathVariable String author, @PathVariable String slug) {
|
||||
ModelAndView mav = new ModelAndView("projects/settings");
|
||||
ProjectData projectData = projectService.getProjectData(author, slug);
|
||||
mav.addObject("p", projectData);
|
||||
ScopedProjectData scopedProjectData = projectService.getScopedProjectData(projectData.getProject().getId(), projectData.getProjectOwner().getId());
|
||||
mav.addObject("sp", scopedProjectData);
|
||||
// TODO add deploymentKey and iconUrl
|
||||
return fillModel(mav);
|
||||
}
|
||||
|
||||
@Secured("ROLE_USER")
|
||||
@RequestMapping("/{author}/{slug}/manage/delete")
|
||||
public Object softDelete(@PathVariable Object author, @PathVariable Object slug) {
|
||||
return null; // TODO implement softDelete request controller
|
||||
@PostMapping(value = "/{author}/{slug}/manage/delete", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public RedirectView softDelete(@PathVariable String author, @PathVariable String slug, @RequestParam(required = false) String comment, RedirectAttributes ra) {
|
||||
ProjectData projectData = projectService.getProjectData(author, slug);
|
||||
projectFactory.softDeleteProject(projectData, comment);
|
||||
|
||||
// TODO user action log
|
||||
ra.addFlashAttribute("alertType", AlertType.SUCCESS);
|
||||
ra.addFlashAttribute("alertMsg", "project.deleted");// TODO add old project name as msg arg
|
||||
return new RedirectView(routeHelper.getRouteUrl("showHome"));
|
||||
}
|
||||
|
||||
@Secured("ROLE_USER")
|
||||
@RequestMapping("/{author}/{slug}/manage/hardDelete")
|
||||
public Object delete(@PathVariable Object author, @PathVariable Object slug) {
|
||||
return null; // TODO implement delete request controller
|
||||
public RedirectView delete(@PathVariable String author, @PathVariable String slug, RedirectAttributes ra) {
|
||||
ProjectData projectData = projectService.getProjectData(author, slug);
|
||||
projectFactory.hardDeleteProject(projectData);
|
||||
// TODO UAL
|
||||
ra.addFlashAttribute("alertType", AlertType.SUCCESS);
|
||||
ra.addFlashAttribute("alertMsg", "project.deleted");// TODO add old project name as msg arg
|
||||
return new RedirectView(routeHelper.getRouteUrl("showHome"));
|
||||
}
|
||||
|
||||
@Secured("ROLE_USER")
|
||||
@ -218,15 +243,54 @@ public class ProjectsController extends HangarController {
|
||||
}
|
||||
|
||||
@Secured("ROLE_USER")
|
||||
@RequestMapping("/{author}/{slug}/manage/rename")
|
||||
public Object rename(@PathVariable Object author, @PathVariable Object slug) {
|
||||
return null; // TODO implement rename request controller
|
||||
@PostMapping(value = "/{author}/{slug}/manage/rename", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public Object rename(@PathVariable String author, @PathVariable String slug, @RequestParam("name") String newName) {
|
||||
ProjectData projectData = projectService.getProjectData(author, slug);
|
||||
|
||||
try {
|
||||
projectFactory.checkProjectAvailability(projectData.getProjectOwner(), newName);
|
||||
} catch (HangarException e) {
|
||||
ModelAndView mav = showSettings(author, slug);
|
||||
AlertUtil.showAlert(mav, AlertType.ERROR, "error.nameUnavailable");
|
||||
AlertUtil.showAlert(mav, AlertType.ERROR, e.getMessageKey());
|
||||
return mav;
|
||||
}
|
||||
|
||||
projectData.getProject().setName(newName);
|
||||
projectData.getProject().setSlug(StringUtils.slugify(newName));
|
||||
projectDao.get().update(projectData.getProject());
|
||||
// TODO User action log
|
||||
return new RedirectView(routeHelper.getRouteUrl("projects.show", author, newName));
|
||||
}
|
||||
|
||||
@Secured("ROLE_USER")
|
||||
@RequestMapping("/{author}/{slug}/manage/save")
|
||||
public Object save(@PathVariable Object author, @PathVariable Object slug) {
|
||||
return null; // TODO implement save request controller
|
||||
@PostMapping(value = "/{author}/{slug}/manage/save", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public RedirectView save(@PathVariable String author,
|
||||
@PathVariable String slug,
|
||||
@RequestParam Category category,
|
||||
@RequestParam(required = false) String keywords,
|
||||
@RequestParam(required = false) String issues,
|
||||
@RequestParam(required = false) String source,
|
||||
@RequestParam(value = "license-name", required = false) String licenseName,
|
||||
@RequestParam(value = "license-url", required = false) String licenseUrl,
|
||||
@RequestParam("forum-sync") boolean forumSync,
|
||||
@RequestParam String description,
|
||||
@RequestParam("update-icon") boolean updateIcon) {
|
||||
ProjectsTable projectsTable = projectService.getProjectData(author, slug).getProject();
|
||||
projectsTable.setCategory(category);
|
||||
Set<String> keywordSet = keywords != null ? Set.of(keywords.split(" ")) : Set.of();
|
||||
projectsTable.setKeywords(keywordSet);
|
||||
projectsTable.setIssues(issues);
|
||||
projectsTable.setSource(source);
|
||||
projectsTable.setLicenseName(licenseName);
|
||||
projectsTable.setLicenseUrl(licenseUrl);
|
||||
projectsTable.setForumSync(forumSync);
|
||||
projectsTable.setDescription(description);
|
||||
projectDao.get().update(projectsTable);
|
||||
// TODO update icon handling
|
||||
// TODO user action log
|
||||
|
||||
return new RedirectView(routeHelper.getRouteUrl("projects.show", author, slug)); // TODO implement save request controller
|
||||
}
|
||||
|
||||
@Secured("ROLE_USER")
|
||||
|
@ -1,6 +1,7 @@
|
||||
package me.minidigger.hangar.controller;
|
||||
|
||||
import me.minidigger.hangar.util.AlertUtil;
|
||||
import me.minidigger.hangar.util.AlertUtil.AlertType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
@ -70,7 +71,7 @@ public class UsersController extends HangarController {
|
||||
if (success) {
|
||||
return new ModelAndView("redirect:" + returnUrl);
|
||||
} else {
|
||||
return applicationController.showHome(); // on a scale of banana to kneny, how bad is it to call another controller?
|
||||
return new ModelAndView("redirect:" + routeHelper.getRouteUrl("showHome"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -148,7 +149,7 @@ public class UsersController extends HangarController {
|
||||
public ModelAndView saveTagline(@PathVariable String user, @RequestParam("tagline") String tagline) {
|
||||
if (tagline.length() > hangarConfig.user.getMaxTaglineLen()) {
|
||||
ModelAndView mav = showProjects(user);
|
||||
AlertUtil.showAlert(mav, AlertUtil.ERROR, "error.tagline.tooLong"); // TODO pass length param to key
|
||||
AlertUtil.showAlert(mav, AlertType.ERROR, "error.tagline.tooLong"); // TODO pass length param to key
|
||||
return new ModelAndView("redirect:" + routeHelper.getRouteUrl("users.showProjects", user));
|
||||
}
|
||||
// TODO user action log
|
||||
|
@ -1,7 +1,11 @@
|
||||
package me.minidigger.hangar.db.dao;
|
||||
|
||||
import me.minidigger.hangar.db.model.ProjectsTable;
|
||||
import me.minidigger.hangar.db.model.UsersTable;
|
||||
import me.minidigger.hangar.model.Permission;
|
||||
import me.minidigger.hangar.model.generated.ProjectStatsAll;
|
||||
import me.minidigger.hangar.model.viewhelpers.ProjectMember;
|
||||
import me.minidigger.hangar.model.viewhelpers.ScopedProjectData;
|
||||
import me.minidigger.hangar.service.project.ProjectFactory.InvalidProjectReason;
|
||||
import org.jdbi.v3.sqlobject.config.RegisterBeanMapper;
|
||||
import org.jdbi.v3.sqlobject.customizer.BindBean;
|
||||
@ -12,6 +16,7 @@ import org.jdbi.v3.sqlobject.statement.SqlUpdate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Repository
|
||||
@RegisterBeanMapper(ProjectsTable.class)
|
||||
@ -23,6 +28,14 @@ public interface ProjectDao {
|
||||
@GetGeneratedKeys
|
||||
ProjectsTable insert(@BindBean ProjectsTable project);
|
||||
|
||||
// TODO expand as needed
|
||||
@SqlUpdate("UPDATE projects SET name = :name, slug = :slug, category = :category, keywords = :keywords, issues = :issues, source = :source, " +
|
||||
"license_name = :licenseName, license_url = :licenseUrl, forum_sync = :forumSync, description = :description WHERE id = :id")
|
||||
void update(@BindBean ProjectsTable project);
|
||||
|
||||
@SqlUpdate("DELETE FROM projects WHERE id = :id")
|
||||
void delete(@BindBean ProjectsTable project);
|
||||
|
||||
@SqlQuery("SELECT CASE " +
|
||||
"WHEN owner_id = :ownerId AND name = :name THEN 'OWNER_NAME' " +
|
||||
"WHEN owner_id = :ownerId AND slug = :slug THEN 'OWNER_SLUG' " +
|
||||
@ -32,6 +45,13 @@ public interface ProjectDao {
|
||||
)
|
||||
InvalidProjectReason checkValidProject(long ownerId, String pluginId, String name, String slug);
|
||||
|
||||
@SqlQuery("SELECT CASE " +
|
||||
"WHEN owner_id = :ownerId AND name = :name THEN 'OWNER_NAME' " +
|
||||
"WHEN owner_id = :ownerId AND slug = :slug THEN 'OWNER_SLUG' " +
|
||||
"END " +
|
||||
"FROM projects")
|
||||
InvalidProjectReason checkNamespace(long ownerId, String name, String slug);
|
||||
|
||||
@SqlQuery("select * from projects where lower(owner_name) = lower(:author) AND lower(slug) = lower(:slug)")
|
||||
ProjectsTable getBySlug(String author, String slug);
|
||||
|
||||
@ -52,4 +72,32 @@ public interface ProjectDao {
|
||||
"(SELECT COUNT(*) as downloads FROM project_versions_downloads_individual pvdi WHERE pvdi.project_id = :id) as d"
|
||||
)
|
||||
ProjectStatsAll getProjectStats(long id);
|
||||
|
||||
@RegisterBeanMapper(value = ScopedProjectData.class)
|
||||
@RegisterBeanMapper(value = Permission.class, prefix = "perm")
|
||||
@SqlQuery("SELECT watching, starred, uproject_flags, perm_value::bigint FROM" +
|
||||
"(SELECT exists(SELECT 1 FROM project_watchers WHERE project_id = :projectId AND user_id = :userId) as watching) as is_watching," +
|
||||
"(SELECT exists(SELECT 1 FROM project_stars WHERE project_id = :projectId AND user_id = :userId) as starred) as is_starred," +
|
||||
"(SELECT exists(SELECT 1 FROM project_flags WHERE project_id = :projectId AND user_id = :userId AND is_resolved IS FALSE) as uproject_flags) as is_flagged," +
|
||||
"(SELECT permission perm_value FROM project_trust WHERE project_id = :projectId AND user_id = :userId) as perm_table")
|
||||
ScopedProjectData getScopedProjectData(long projectId, long userId);
|
||||
|
||||
|
||||
@RegisterBeanMapper(value = UsersTable.class, prefix = "usr")
|
||||
@RegisterBeanMapper(value = ProjectMember.class, prefix = "pm")
|
||||
@SqlQuery("SELECT " +
|
||||
"u.id usr_id," +
|
||||
"u.created_at usr_created_at, " +
|
||||
"u.full_name usr_full_name," +
|
||||
"u.name usr_name," +
|
||||
"u.email usr_email," +
|
||||
"u.tagline usr_tagline," +
|
||||
"u.join_date usr_join_date," +
|
||||
"u.read_prompts usr_read_prompts," +
|
||||
"u.is_locked usr_is_locked," +
|
||||
"u.language usr_language," +
|
||||
"upr.role_type pm_role," +
|
||||
"upr.is_accepted pm_is_accepted " +
|
||||
"FROM user_project_roles upr JOIN users u on upr.user_id = u.id WHERE upr.project_id = :projectId")
|
||||
Map<ProjectMember, UsersTable> getProjectMembers(long projectId);
|
||||
}
|
||||
|
31
src/main/java/me/minidigger/hangar/db/dao/VisibilityDao.java
Normal file
31
src/main/java/me/minidigger/hangar/db/dao/VisibilityDao.java
Normal file
@ -0,0 +1,31 @@
|
||||
package me.minidigger.hangar.db.dao;
|
||||
|
||||
import me.minidigger.hangar.db.model.ProjectVersionVisibilityChangesTable;
|
||||
import me.minidigger.hangar.db.model.ProjectVisibilityChangesTable;
|
||||
import org.jdbi.v3.sqlobject.config.RegisterBeanMapper;
|
||||
import org.jdbi.v3.sqlobject.customizer.BindBean;
|
||||
import org.jdbi.v3.sqlobject.customizer.Timestamped;
|
||||
import org.jdbi.v3.sqlobject.statement.GetGeneratedKeys;
|
||||
import org.jdbi.v3.sqlobject.statement.SqlQuery;
|
||||
import org.jdbi.v3.sqlobject.statement.SqlUpdate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@RegisterBeanMapper(ProjectVisibilityChangesTable.class)
|
||||
@RegisterBeanMapper(ProjectVersionVisibilityChangesTable.class)
|
||||
public interface VisibilityDao {
|
||||
|
||||
@GetGeneratedKeys
|
||||
@Timestamped
|
||||
@SqlUpdate("INSERT INTO project_visibility_changes (created_at, created_by, project_id, comment, resolved_at, resolved_by, visibility) " +
|
||||
"VALUES (:now, :createdBy, :projectId, :comment, :resolvedAt, :resolvedBy, :visibility)")
|
||||
ProjectVisibilityChangesTable insert(@BindBean ProjectVisibilityChangesTable projectVisibilityChangesTable);
|
||||
|
||||
@SqlUpdate("UPDATE project_visibility_changes SET resolved_at = :resolvedAt, resolved_by = :resolvedBy") // I think these are the only two things that change after the fact
|
||||
void update(@BindBean ProjectVisibilityChangesTable projectVisibilityChangesTable);
|
||||
|
||||
@SqlQuery("SELECT * FROM project_visibility_changes WHERE project_id = :projectId AND resolved_at IS NULL ORDER BY created_at LIMIT 1")
|
||||
ProjectVisibilityChangesTable getLatestProjectVisibilityChange(long projectId);
|
||||
|
||||
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package me.minidigger.hangar.db.model;
|
||||
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class ProjectVisibilityChangesTable {
|
||||
@ -10,10 +12,24 @@ public class ProjectVisibilityChangesTable {
|
||||
private long createdBy;
|
||||
private long projectId;
|
||||
private String comment;
|
||||
@Nullable
|
||||
private OffsetDateTime resolvedAt;
|
||||
private long resolvedBy;
|
||||
@Nullable
|
||||
private Long resolvedBy;
|
||||
private long visibility;
|
||||
|
||||
public ProjectVisibilityChangesTable(long id, OffsetDateTime createdAt, long createdBy, long projectId, String comment, @Nullable OffsetDateTime resolvedAt, @Nullable Long resolvedBy, long visibility) {
|
||||
this.id = id;
|
||||
this.createdAt = createdAt;
|
||||
this.createdBy = createdBy;
|
||||
this.projectId = projectId;
|
||||
this.comment = comment;
|
||||
this.resolvedAt = resolvedAt;
|
||||
this.resolvedBy = resolvedBy;
|
||||
this.visibility = visibility;
|
||||
}
|
||||
|
||||
public ProjectVisibilityChangesTable() { }
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
@ -60,16 +76,18 @@ public class ProjectVisibilityChangesTable {
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public OffsetDateTime getResolvedAt() {
|
||||
return resolvedAt;
|
||||
}
|
||||
|
||||
public void setResolvedAt(OffsetDateTime resolvedAt) {
|
||||
public void setResolvedAt(@Nullable OffsetDateTime resolvedAt) {
|
||||
this.resolvedAt = resolvedAt;
|
||||
}
|
||||
|
||||
|
||||
public long getResolvedBy() {
|
||||
@Nullable
|
||||
public Long getResolvedBy() {
|
||||
return resolvedBy;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import org.jdbi.v3.core.enums.EnumByOrdinal;
|
||||
import org.jdbi.v3.core.mapper.reflect.ColumnName;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Collection;
|
||||
|
||||
public class ProjectsTable {
|
||||
|
||||
@ -24,7 +25,7 @@ public class ProjectsTable {
|
||||
private String description;
|
||||
private Visibility visibility;
|
||||
private Object notes; // TODO jsonb
|
||||
private String keywords;
|
||||
private Collection<String> keywords;
|
||||
private String homepage;
|
||||
private String issues;
|
||||
private String source;
|
||||
@ -177,11 +178,11 @@ public class ProjectsTable {
|
||||
}
|
||||
|
||||
|
||||
public String getKeywords() {
|
||||
public Collection<String> getKeywords() {
|
||||
return keywords;
|
||||
}
|
||||
|
||||
public void setKeywords(String keywords) {
|
||||
public void setKeywords(Collection<String> keywords) {
|
||||
this.keywords = keywords;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package me.minidigger.hangar.db.model;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.jdbi.v3.core.annotation.Unmappable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.OffsetDateTime;
|
||||
@ -37,6 +38,7 @@ public class UsersTable implements Serializable {
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Unmappable
|
||||
public UsersTable getUser() {
|
||||
return this; // TODO dummy to fix frontend
|
||||
}
|
||||
|
@ -3,6 +3,10 @@ package me.minidigger.hangar.model;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public enum Category {
|
||||
ADMIN_TOOLS(0, "Admin Tools", "fa-server", "admin_tools"),
|
||||
CHAT(1, "Chat", "fa-comment", "chat"),
|
||||
@ -78,4 +82,8 @@ public enum Category {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Set<Category> visible() {
|
||||
return Arrays.stream(Category.values()).filter(Category::isVisible).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,11 @@
|
||||
package me.minidigger.hangar.model;
|
||||
|
||||
import org.jdbi.v3.core.mapper.reflect.ColumnName;
|
||||
import org.jdbi.v3.core.mapper.reflect.JdbiConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
@ -53,12 +59,18 @@ public class Permission implements Comparable<Permission> {
|
||||
public static final Permission HardDeleteVersion = new Permission(1L << 42);
|
||||
public static final Permission EditAllUserSettings = new Permission(1L << 43);
|
||||
|
||||
private final long value;
|
||||
private long value;
|
||||
|
||||
private Permission(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Permission() { }
|
||||
|
||||
public void setValue(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Permission add(Permission other) {
|
||||
return new Permission(value | other.value);
|
||||
}
|
||||
@ -95,4 +107,8 @@ public class Permission implements Comparable<Permission> {
|
||||
public int compareTo(Permission o) {
|
||||
return (int) (value - o.value);
|
||||
}
|
||||
|
||||
public static Permission fromLong(long value) {
|
||||
return new Permission(value);
|
||||
}
|
||||
}
|
||||
|
@ -32,10 +32,10 @@ public enum Role {
|
||||
GOLD_DONOR("Gold_Donor",17, GLOBAL, None, "Gold Donor", GOLD),
|
||||
DIAMOND_DONOR("Diamond_Donor",18, GLOBAL, None, "Diamond Donor", LIGHTBLUE),
|
||||
|
||||
PROJECT_OWNER("Project_Owner", 19, PROJECT, IsProjectOwner.add(EditApiKeys).add(DeleteProject).add(DeleteVersion), "Owner", TRANSPARENT, false),
|
||||
PROJECT_SUPPORT("Project_Support", 22, PROJECT, IsProjectMember, "Support", TRANSPARENT),
|
||||
PROJECT_EDITOR("Project_Editor", 21, PROJECT, EditPage.add(PROJECT_SUPPORT.getPermissions()), "Editor", TRANSPARENT),
|
||||
PROJECT_DEVELOPER("Project_Developer", 20, PROJECT, CreateVersion.add(EditVersion).add(PROJECT_EDITOR.getPermissions()), "Developer", TRANSPARENT),
|
||||
PROJECT_OWNER("Project_Owner", 19, PROJECT, IsProjectOwner.add(EditApiKeys).add(DeleteProject).add(DeleteVersion).add(PROJECT_DEVELOPER.getPermissions()), "Owner", TRANSPARENT, false),
|
||||
|
||||
ORGANIZATION_SUPPORT("Organization_Support", 28, RoleCategory.ORGANIZATION, PostAsOrganization.add(IsOrganizationMember), "Support", TRANSPARENT),
|
||||
ORGANIZATION_EDITOR("Organization_Editor", 27, RoleCategory.ORGANIZATION, PROJECT_EDITOR.permissions.add(ORGANIZATION_SUPPORT.permissions), "Editor", TRANSPARENT),
|
||||
|
@ -0,0 +1,11 @@
|
||||
package me.minidigger.hangar.model.converters;
|
||||
|
||||
import me.minidigger.hangar.model.Category;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class CategoryConverter implements Converter<String, Category> {
|
||||
@Override
|
||||
public Category convert(String s) {
|
||||
return Category.fromTitle(s);
|
||||
}
|
||||
}
|
@ -1,22 +1,25 @@
|
||||
package me.minidigger.hangar.model.viewhelpers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import me.minidigger.hangar.db.customtypes.RoleCategory;
|
||||
import me.minidigger.hangar.db.model.ProjectVersionsTable;
|
||||
import me.minidigger.hangar.db.model.ProjectVisibilityChangesTable;
|
||||
import me.minidigger.hangar.db.model.ProjectsTable;
|
||||
import me.minidigger.hangar.db.model.UserProjectRolesTable;
|
||||
import me.minidigger.hangar.db.model.UsersTable;
|
||||
import me.minidigger.hangar.model.Permission;
|
||||
import me.minidigger.hangar.model.Visibility;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ProjectData {
|
||||
|
||||
private ProjectsTable joinable;
|
||||
private UsersTable projectOwner;
|
||||
private int publicVersions;
|
||||
private Map<UsersTable, UserProjectRolesTable> members;
|
||||
private Map<ProjectMember, UsersTable> members;
|
||||
private List<Object> flags; // TODO flags, flag, user.name, resolvedBy
|
||||
private int noteCount;
|
||||
private ProjectVisibilityChangesTable lastVisibilityChange;
|
||||
@ -25,12 +28,14 @@ public class ProjectData {
|
||||
private String iconUrl;
|
||||
private long starCount;
|
||||
private long watcherCount;
|
||||
private String namespace;
|
||||
private ProjectViewSettings settings;
|
||||
|
||||
public ProjectData() {
|
||||
//
|
||||
}
|
||||
|
||||
public ProjectData(ProjectsTable joinable, UsersTable projectOwner, int publicVersions, Map<UsersTable, UserProjectRolesTable> members, List<Object> flags, int noteCount, ProjectVisibilityChangesTable lastVisibilityChange, String lastVisibilityChangeUser, ProjectVersionsTable recommendedVersion, String iconUrl, long starCount, long watcherCount) {
|
||||
public ProjectData(ProjectsTable joinable, UsersTable projectOwner, int publicVersions, Map<ProjectMember, UsersTable> members, List<Object> flags, int noteCount, ProjectVisibilityChangesTable lastVisibilityChange, String lastVisibilityChangeUser, ProjectVersionsTable recommendedVersion, String iconUrl, long starCount, long watcherCount, ProjectViewSettings settings) {
|
||||
this.joinable = joinable;
|
||||
this.projectOwner = projectOwner;
|
||||
this.publicVersions = publicVersions;
|
||||
@ -43,6 +48,8 @@ public class ProjectData {
|
||||
this.iconUrl = iconUrl;
|
||||
this.starCount = starCount;
|
||||
this.watcherCount = watcherCount;
|
||||
namespace = projectOwner.getName() + "/" + joinable.getSlug();
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public int getFlagCount() {
|
||||
@ -81,7 +88,7 @@ public class ProjectData {
|
||||
return publicVersions;
|
||||
}
|
||||
|
||||
public Map<UsersTable, UserProjectRolesTable> getMembers() {
|
||||
public Map<ProjectMember, UsersTable> getMembers() {
|
||||
return members;
|
||||
}
|
||||
|
||||
@ -116,4 +123,19 @@ public class ProjectData {
|
||||
public long getWatcherCount() {
|
||||
return watcherCount;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public ProjectViewSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
public Map<ProjectMember, UsersTable> filteredMembers(HeaderData headerData) {
|
||||
boolean hasEditMembers = headerData.globalPerm(Permission.ManageSubjectMembers);
|
||||
boolean userIsOwner = headerData.isAuthenticated() ? headerData.getCurrentUser().getId() == projectOwner.getId() : false;
|
||||
if (hasEditMembers || userIsOwner) return members;
|
||||
else return members.entrySet().stream().filter(member -> member.getKey().getIsAccepted()).collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
package me.minidigger.hangar.model.viewhelpers;
|
||||
|
||||
import me.minidigger.hangar.db.model.UsersTable;
|
||||
import me.minidigger.hangar.model.Role;
|
||||
import org.jdbi.v3.core.mapper.Nested;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ProjectMember {
|
||||
|
||||
@Nested("usr")
|
||||
private UsersTable user;
|
||||
private Role role;
|
||||
private boolean isAccepted = false;
|
||||
|
||||
public ProjectMember(@NotNull UsersTable user, Role role, boolean isAccepted) {
|
||||
this.user = user;
|
||||
this.role = role;
|
||||
this.isAccepted = isAccepted;
|
||||
}
|
||||
|
||||
public ProjectMember() { }
|
||||
|
||||
@Nested("usr")
|
||||
public UsersTable getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Nested("usr")
|
||||
public void setUser(UsersTable user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Role getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(Role role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public boolean getIsAccepted() {
|
||||
return isAccepted;
|
||||
}
|
||||
|
||||
public void setIsAccepted(boolean accepted) {
|
||||
isAccepted = accepted;
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package me.minidigger.hangar.model.viewhelpers;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ProjectViewSettings {
|
||||
private Collection<String> keywords;
|
||||
private String homepage;
|
||||
private String issues;
|
||||
private String source;
|
||||
private String support;
|
||||
private String licenseName;
|
||||
private String licenseUrl;
|
||||
private boolean forumSync = true;
|
||||
|
||||
public ProjectViewSettings(Collection<String> keywords, String homepage, String issues, String source, String support, String licenseName, String licenseUrl, boolean forumSync) {
|
||||
this.keywords = keywords;
|
||||
this.homepage = homepage;
|
||||
this.issues = issues;
|
||||
this.source = source;
|
||||
this.support = support;
|
||||
this.licenseName = licenseName;
|
||||
this.licenseUrl = licenseUrl;
|
||||
this.forumSync = forumSync;
|
||||
}
|
||||
|
||||
public ProjectViewSettings() { }
|
||||
|
||||
public Collection<String> getKeywords() {
|
||||
return keywords;
|
||||
}
|
||||
|
||||
public void setKeywords(List<String> keywords) {
|
||||
this.keywords = keywords;
|
||||
}
|
||||
|
||||
public String getHomepage() {
|
||||
return homepage;
|
||||
}
|
||||
|
||||
public void setHomepage(String homepage) {
|
||||
this.homepage = homepage;
|
||||
}
|
||||
|
||||
public String getIssues() {
|
||||
return issues;
|
||||
}
|
||||
|
||||
public void setIssues(String issues) {
|
||||
this.issues = issues;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public String getSupport() {
|
||||
return support;
|
||||
}
|
||||
|
||||
public void setSupport(String support) {
|
||||
this.support = support;
|
||||
}
|
||||
|
||||
public String getLicenseName() {
|
||||
return licenseName;
|
||||
}
|
||||
|
||||
public void setLicenseName(String licenseName) {
|
||||
this.licenseName = licenseName;
|
||||
}
|
||||
|
||||
public String getLicenseUrl() {
|
||||
return licenseUrl;
|
||||
}
|
||||
|
||||
public void setLicenseUrl(String licenseUrl) {
|
||||
this.licenseUrl = licenseUrl;
|
||||
}
|
||||
|
||||
public boolean isForumSync() {
|
||||
return forumSync;
|
||||
}
|
||||
|
||||
public void setForumSync(boolean forumSync) {
|
||||
this.forumSync = forumSync;
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package me.minidigger.hangar.model.viewhelpers;
|
||||
|
||||
import me.minidigger.hangar.model.Permission;
|
||||
import org.jdbi.v3.core.annotation.Unmappable;
|
||||
import org.jdbi.v3.core.mapper.Nested;
|
||||
|
||||
public class ScopedProjectData {
|
||||
|
||||
@ -14,10 +16,12 @@ public class ScopedProjectData {
|
||||
return permissions.has(perm);
|
||||
}
|
||||
|
||||
@Unmappable
|
||||
public boolean isCanPostAsOwnerOrga() {
|
||||
return canPostAsOwnerOrga;
|
||||
}
|
||||
|
||||
@Unmappable
|
||||
public void setCanPostAsOwnerOrga(boolean canPostAsOwnerOrga) {
|
||||
this.canPostAsOwnerOrga = canPostAsOwnerOrga;
|
||||
}
|
||||
@ -50,7 +54,9 @@ public class ScopedProjectData {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Nested("perm")
|
||||
public void setPermissions(Permission permissions) {
|
||||
this.permissions = permissions;
|
||||
this.canPostAsOwnerOrga = permissions.has(Permission.PostAsOrganization);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package me.minidigger.hangar.security;
|
||||
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
@ -63,6 +63,7 @@ public class UserService {
|
||||
|
||||
public HeaderData getHeaderData() {
|
||||
HeaderData headerData = new HeaderData();
|
||||
headerData.setGlobalPermission(headerData.getGlobalPermission().add(Permission.HardDeleteProject)); // TODO remove
|
||||
|
||||
headerData.setCurrentUser(getCurrentUser());
|
||||
// TODO fill headerdata
|
||||
|
@ -5,20 +5,29 @@ import me.minidigger.hangar.db.model.ProjectPagesTable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
import me.minidigger.hangar.config.HangarConfig;
|
||||
import me.minidigger.hangar.db.dao.HangarDao;
|
||||
import me.minidigger.hangar.db.dao.ProjectChannelDao;
|
||||
import me.minidigger.hangar.db.dao.ProjectDao;
|
||||
import me.minidigger.hangar.db.dao.VisibilityDao;
|
||||
import me.minidigger.hangar.db.model.ProjectChannelsTable;
|
||||
import me.minidigger.hangar.db.model.ProjectVisibilityChangesTable;
|
||||
import me.minidigger.hangar.db.model.ProjectsTable;
|
||||
import me.minidigger.hangar.db.model.UsersTable;
|
||||
import me.minidigger.hangar.model.Category;
|
||||
import me.minidigger.hangar.model.Role;
|
||||
import me.minidigger.hangar.model.Visibility;
|
||||
import me.minidigger.hangar.model.viewhelpers.ProjectData;
|
||||
import me.minidigger.hangar.service.RoleService;
|
||||
import me.minidigger.hangar.service.UserService;
|
||||
import me.minidigger.hangar.util.HangarException;
|
||||
import me.minidigger.hangar.util.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@ -29,15 +38,17 @@ public class ProjectFactory {
|
||||
private final HangarDao<ProjectChannelDao> projectChannelDao;
|
||||
private final HangarDao<ProjectDao> projectDao;
|
||||
private final HangarDao<ProjectPageDao> projectPagesDao;
|
||||
private final HangarDao<VisibilityDao> visibilityDao;
|
||||
private final RoleService roleService;
|
||||
private final UserService userService;
|
||||
private final PagesFactory pagesFactory;
|
||||
|
||||
@Autowired
|
||||
public ProjectFactory(HangarConfig hangarConfig, HangarDao<ProjectChannelDao> projectChannelDao, HangarDao<ProjectDao> projectDao, HangarDao<ProjectPageDao> projectPagesDao, RoleService roleService, UserService userService, PagesFactory pagesFactory) {
|
||||
public ProjectFactory(HangarConfig hangarConfig, HangarDao<ProjectChannelDao> projectChannelDao, HangarDao<ProjectDao> projectDao, HangarDao<ProjectPageDao> projectPagesDao, HangarDao<VisibilityDao> visibilityDao, RoleService roleService, UserService userService, PagesFactory pagesFactory) {
|
||||
this.hangarConfig = hangarConfig;
|
||||
this.projectChannelDao = projectChannelDao;
|
||||
this.projectDao = projectDao;
|
||||
this.visibilityDao = visibilityDao;
|
||||
this.roleService = roleService;
|
||||
this.userService = userService;
|
||||
this.pagesFactory = pagesFactory;
|
||||
@ -52,7 +63,7 @@ public class ProjectFactory {
|
||||
}
|
||||
}
|
||||
|
||||
public ProjectsTable createProject(UsersTable ownerUser, String name, String pluginId, Category category, String description) {
|
||||
public ProjectsTable createProject(UsersTable ownerUser, String name, String pluginId, Category category, String description) throws HangarException {
|
||||
String slug = StringUtils.slugify(name);
|
||||
ProjectsTable projectsTable = new ProjectsTable(pluginId, name, slug, ownerUser.getName(), ownerUser.getId(), category, description, Visibility.NEW);
|
||||
|
||||
@ -61,15 +72,7 @@ public class ProjectFactory {
|
||||
String content = "# " + name + "\n\n" + hangarConfig.pages.home.getMessage();
|
||||
ProjectPagesTable pagesTable = new ProjectPagesTable(-1, OffsetDateTime.now(), -1, hangarConfig.pages.home.getName(), StringUtils.slugify(hangarConfig.pages.home.getName()), content, false, null);
|
||||
|
||||
InvalidProjectReason invalidProjectReason;
|
||||
if (!hangarConfig.isValidProjectName(name)) {
|
||||
invalidProjectReason = InvalidProjectReason.INVALID_NAME;
|
||||
} else {
|
||||
invalidProjectReason = projectDao.get().checkValidProject(ownerUser.getId(), pluginId, name, slug);
|
||||
}
|
||||
if (invalidProjectReason != null) {
|
||||
throw new HangarException(invalidProjectReason.key);
|
||||
}
|
||||
checkProjectAvailability(ownerUser, name, pluginId);
|
||||
|
||||
projectsTable = projectDao.get().insert(projectsTable);
|
||||
channelsTable.setProjectId(projectsTable.getId());
|
||||
@ -85,6 +88,42 @@ public class ProjectFactory {
|
||||
return projectsTable;
|
||||
}
|
||||
|
||||
public void softDeleteProject(ProjectData projectData, String comment) {
|
||||
ProjectsTable project = projectData.getProject();
|
||||
// if (project.getVisibility() == Visibility.NEW) {
|
||||
// hardDeleteProject(projectData);
|
||||
// return;
|
||||
// }
|
||||
|
||||
ProjectVisibilityChangesTable latestChange = visibilityDao.get().getLatestProjectVisibilityChange(project.getId());
|
||||
if (latestChange != null) { // resolve last unresolved change
|
||||
latestChange.setResolvedAt(OffsetDateTime.now());
|
||||
latestChange.setResolvedBy(project.getOwnerId());
|
||||
visibilityDao.get().update(latestChange);
|
||||
}
|
||||
visibilityDao.get().insert(new ProjectVisibilityChangesTable(-1, OffsetDateTime.now(), project.getOwnerId(), project.getId(), comment, null, null, Visibility.SOFTDELETE.getValue()));
|
||||
|
||||
project.setVisibility(Visibility.SOFTDELETE);
|
||||
projectDao.get().update(project);
|
||||
}
|
||||
|
||||
public void hardDeleteProject(ProjectData projectData) {
|
||||
// TODO UAC
|
||||
projectDao.get().delete(projectData.getProject());
|
||||
}
|
||||
|
||||
public void checkProjectAvailability(UsersTable author, String page) throws HangarException {
|
||||
checkProjectAvailability(author, page, null);
|
||||
}
|
||||
|
||||
public void checkProjectAvailability(UsersTable author, String page, @Nullable String pluginId) throws HangarException {
|
||||
InvalidProjectReason invalidProjectReason;
|
||||
if (!hangarConfig.isValidProjectName(page)) invalidProjectReason = InvalidProjectReason.INVALID_NAME;
|
||||
else if (pluginId != null) invalidProjectReason = projectDao.get().checkValidProject(author.getId(), pluginId, page, StringUtils.slugify(page));
|
||||
else invalidProjectReason = projectDao.get().checkNamespace(author.getId(), page, StringUtils.slugify(page));
|
||||
if (invalidProjectReason != null) throw new HangarException(invalidProjectReason.key);
|
||||
}
|
||||
|
||||
public enum InvalidProjectReason {
|
||||
PLUGIN_ID("error.project.invalidPluginId"),
|
||||
OWNER_NAME("error.project.nameExists"),
|
||||
|
@ -7,13 +7,15 @@ import me.minidigger.hangar.db.model.ProjectPagesTable;
|
||||
import me.minidigger.hangar.db.model.ProjectVersionsTable;
|
||||
import me.minidigger.hangar.db.model.ProjectVisibilityChangesTable;
|
||||
import me.minidigger.hangar.db.model.ProjectsTable;
|
||||
import me.minidigger.hangar.db.model.UserProjectRolesTable;
|
||||
import me.minidigger.hangar.db.model.UsersTable;
|
||||
import me.minidigger.hangar.model.generated.Project;
|
||||
import me.minidigger.hangar.model.generated.ProjectNamespace;
|
||||
import me.minidigger.hangar.model.generated.ProjectSettings;
|
||||
import me.minidigger.hangar.model.generated.UserActions;
|
||||
import me.minidigger.hangar.model.viewhelpers.ProjectData;
|
||||
import me.minidigger.hangar.model.viewhelpers.ProjectMember;
|
||||
import me.minidigger.hangar.model.viewhelpers.ProjectViewSettings;
|
||||
import me.minidigger.hangar.model.viewhelpers.ScopedProjectData;
|
||||
import me.minidigger.hangar.util.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@ -22,7 +24,6 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@ -57,7 +58,9 @@ public class ProjectService {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
int publicVersions = 0;
|
||||
Map<UsersTable, UserProjectRolesTable> members = new HashMap<>();
|
||||
Map<ProjectMember, UsersTable> projectMembers = projectDao.get().getProjectMembers(projectsTable.getId());
|
||||
projectMembers.forEach(ProjectMember::setUser); // I don't know why the SQL query isn't doing this automatically...
|
||||
// System.out.println(projectMembers.keySet().stream().findFirst().get().getRole().getPermissions().toNamed()); TODO REMOVE
|
||||
List<Object> flags = new ArrayList<>();
|
||||
int noteCount = 0;
|
||||
ProjectVisibilityChangesTable lastVisibilityChange = null;
|
||||
@ -66,7 +69,34 @@ public class ProjectService {
|
||||
String iconUrl = "";
|
||||
long starCount = 0;
|
||||
long watcherCount = 0;
|
||||
return new ProjectData(projectsTable, projectOwner, publicVersions, members, flags, noteCount, lastVisibilityChange, lastVisibilityChangeUser, recommendedVersion, iconUrl, starCount, watcherCount);
|
||||
ProjectViewSettings settings = new ProjectViewSettings(
|
||||
projectsTable.getKeywords(),
|
||||
projectsTable.getHomepage(),
|
||||
projectsTable.getIssues(),
|
||||
projectsTable.getSource(),
|
||||
projectsTable.getSupport(),
|
||||
projectsTable.getLicenseName(),
|
||||
projectsTable.getLicenseUrl(),
|
||||
projectsTable.getForumSync()
|
||||
);
|
||||
return new ProjectData(projectsTable,
|
||||
projectOwner,
|
||||
publicVersions,
|
||||
projectMembers,
|
||||
flags,
|
||||
noteCount,
|
||||
lastVisibilityChange,
|
||||
lastVisibilityChangeUser,
|
||||
recommendedVersion,
|
||||
iconUrl,
|
||||
starCount,
|
||||
watcherCount,
|
||||
settings
|
||||
);
|
||||
}
|
||||
|
||||
public ScopedProjectData getScopedProjectData(long projectId, long userId) {
|
||||
return projectDao.get().getScopedProjectData(projectId, userId);
|
||||
}
|
||||
|
||||
public ProjectPagesTable getPage(long projectId, String slug) {
|
||||
|
@ -7,14 +7,19 @@ import java.util.Map;
|
||||
|
||||
public class AlertUtil {
|
||||
|
||||
public static final String ERROR = "error";
|
||||
public enum AlertType {
|
||||
ERROR,
|
||||
SUCCESS,
|
||||
INFO,
|
||||
WARNING
|
||||
}
|
||||
|
||||
public static ModelAndView showAlert(ModelAndView mav, String alertType, String alertMessage) {
|
||||
public static ModelAndView showAlert(ModelAndView mav, AlertType alertType, String alertMessage) {
|
||||
Map<String, String> alerts = (Map<String, String>) mav.getModelMap().getAttribute("alerts");
|
||||
if (alerts == null) {
|
||||
alerts = new HashMap<>();
|
||||
}
|
||||
alerts.put(alertType, alertMessage);
|
||||
alerts.put(alertType.name().toLowerCase(), alertMessage);
|
||||
mav.addObject("alerts", alerts);
|
||||
return mav;
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
<#import "/spring.ftl" as spring />
|
||||
<#import "*/utils/hangar.ftlh" as hangar />
|
||||
|
||||
@import ore.data.project.ProjectNamespace
|
||||
@import ore.models.project.Visibility
|
||||
|
||||
<#assign Visibility=@helper["me.minidigger.hangar.model.Visibility"] />
|
||||
<#macro btnHide namespace projectVisibility>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-alert btn-hide-dropdown dropdown-toggle" type="button" id="visibility-actions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-project="${namespace}" style="color: black">
|
||||
@ -11,7 +9,7 @@
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="visibility-actions">
|
||||
<#list Visibility.values.sortBy(_.value) as visibility>
|
||||
<#list Visibility.values as visibility>
|
||||
<li>
|
||||
<a href="#" class="btn-visibility-change" data-project="${namespace}" data-level="${visibility.value}" <#if visibility.showModal> data-modal="true" </#if>>
|
||||
<@spring.message "visibility.name." + visibility.nameKey /> <#if projectVisibility == visibility> <i class="fa fa-check" style="color: black" aria-hidden="true"></i> </#if>
|
||||
|
@ -1,139 +1,128 @@
|
||||
<#import "/spring.ftl" as spring />
|
||||
<#import "*/utils/hangar.ftlh" as hangar />
|
||||
|
||||
@import ore.data.project.Category
|
||||
@(form: String,
|
||||
homepage: Option[String] = None,
|
||||
issues: Option[String] = None,
|
||||
source: Option[String] = None,
|
||||
support: Option[String] = None,
|
||||
licenseName: Option[String] = None,
|
||||
licenseUrl: Option[String] = None,
|
||||
selected: Option[Category] = None,
|
||||
forumSync: Boolean = true,
|
||||
keywords: List[String] = Nil)(implicit messages: Messages)
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Category</h4>
|
||||
<p>
|
||||
Categorize your project into one of @Category.visible.size categories. Appropriately categorizing your
|
||||
project makes it easier for people to find.
|
||||
</p>
|
||||
</div>
|
||||
<div class="setting-content">
|
||||
<select class="form-control" id="category" name="category" form="${form}">
|
||||
<#list Category.values as category>
|
||||
<#if category.isVisible>
|
||||
<option <#if selected?? && selected.equals(category)> selected </#if> >
|
||||
${category.title}
|
||||
</option>
|
||||
</#if>
|
||||
</#list>
|
||||
</select>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Keywords <i>(optional)</i></h4>
|
||||
<p>
|
||||
These are special words that will return your project when people add them to their searches. Max 5.
|
||||
</p>
|
||||
</div>
|
||||
<input <#if keywords.nonEmpty> value="${keywords.mkString(" ")}" </#if> form="@form" type="text" class="form-control" id="keywords"
|
||||
name="keywords" placeholder="sponge server plugins mods" />
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Homepage <i>(optional)</i></h4>
|
||||
<p>
|
||||
Having a custom homepage for your project helps you look more proper, official, and gives you another place
|
||||
to gather information about your project.
|
||||
</p>
|
||||
</div>
|
||||
<input <#if homepage??> value="${homepage}" </#if> form="${form}" type="url" class="form-control" id="homepage"
|
||||
name="homepage" placeholder="https://papermc.io" />
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Issue tracker <i>(optional)</i></h4>
|
||||
<p>
|
||||
Providing an issue tracker helps your users get support more easily and provides you with an easy way to
|
||||
track bugs.
|
||||
</p>
|
||||
</div>
|
||||
<input <#if issues??> value="${issues}" </#if> form="${form}" type="url" class="form-control" id="issues"
|
||||
name="issues" placeholder="https://github.com/MiniDigger/Hangar/issues" />
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Source code <i>(optional)</i></h4>
|
||||
<p>Support the community of developers by making your project open source!</p>
|
||||
</div>
|
||||
<input <#if source??> value="${source}" </#if> form="${form}" type="url" class="form-control" id="source"
|
||||
name="source" placeholder="https://github.com/MiniDigger/Hangar" />
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>External support <i>(optional)</i></h4>
|
||||
<p>
|
||||
An external place where you can offer support to your users. Could be a forum, a Discord server, or
|
||||
somewhere else.
|
||||
</p>
|
||||
</div>
|
||||
<input <#if support??> value="${support}" </#if> form="${form}" type="url" class="form-control" id="support"
|
||||
name="support" placeholder="https://discord.gg/papermc" />
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4><@spring.message "project.settings.license") <i>(<@spring.message "general.optional" /> /></i></h4>
|
||||
<p><@spring.message "project.settings.license.info" /></p>
|
||||
</div>
|
||||
<div class="input-group pull-left">
|
||||
<div class="input-group-btn">
|
||||
<button type="button" class="btn btn-default btn-license dropdown-toggle" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
<span class="license">${licenseName!messages("licenses.mit")}</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<input type="text" class="form-control" style="display: none;" name="license-name" form="${form}"
|
||||
value="${licenseName!messages("licenses.mit")}" />
|
||||
<ul class="dropdown-menu dropdown-license">
|
||||
<li><a><@spring.message "licenses.mit" /></a></li>
|
||||
<li><a><@spring.message "licenses.apache2.0" /></a></li>
|
||||
<li><a><@spring.message "licenses.gpl" /></a></li>
|
||||
<li><a><@spring.message "licenses.lgpl" /></a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a class="license-custom"><@spring.message "licenses.custom" />…</a></li>
|
||||
</ul>
|
||||
<#macro inputSettings form homepage="" issues="" source="" support="" licenseName="" licenseUrl="" selected=@helper["me.minidigger.hangar.model.Category"].UNDEFINED forumSync=true keywords=[]>
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Category</h4>
|
||||
<p>
|
||||
Categorize your project into one of ${@helper["me.minidigger.hangar.model.Category"].visible()?size} categories. Appropriately categorizing your
|
||||
project makes it easier for people to find.
|
||||
</p>
|
||||
</div>
|
||||
<input type="text" name="license-url" class="form-control" form="${form}"
|
||||
placeholder="https://github.com/MiniDigger/Hangar/LICENSE.txt" value="${licenseUrl}">
|
||||
<div class="setting-content">
|
||||
<select class="form-control" id="category" name="category" form="${form}">
|
||||
<#-- @ftlvariable name="category" type="me.minidigger.hangar.model.Category" -->
|
||||
<#list @helper["me.minidigger.hangar.model.Category"].values() as category>
|
||||
<#if category.isVisible()>
|
||||
<option <#if selected?? && selected.equals(category)> selected </#if> >
|
||||
${category.title}
|
||||
</option>
|
||||
</#if>
|
||||
</#list>
|
||||
</select>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4><@spring.message "project.settings.forumSync" /></h4>
|
||||
<p><@spring.message "project.settings.forumSync.info" /></p>
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Keywords <i>(optional)</i></h4>
|
||||
<p>
|
||||
These are special words that will return your project when people add them to their searches. Max 5.
|
||||
</p>
|
||||
</div>
|
||||
<input <#if keywords?size gt 0> value="${keywords?join(" ")}" </#if> form="${form}" type="text" class="form-control" id="keywords"
|
||||
name="keywords" placeholder="sponge server plugins mods" />
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="setting-content">
|
||||
<label>
|
||||
<input <#if forumSync> checked </#if> value="true" form="${form}" type="checkbox" id="forum-sync" name="forum-sync">
|
||||
Make forum posts
|
||||
</label>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Homepage <i>(optional)</i></h4>
|
||||
<p>
|
||||
Having a custom homepage for your project helps you look more proper, official, and gives you another place
|
||||
to gather information about your project.
|
||||
</p>
|
||||
</div>
|
||||
<input <#if homepage?has_content> value="${homepage}" </#if> form="${form}" type="url" class="form-control" id="homepage" name="homepage" placeholder="https://papermc.io" />
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Issue tracker <i>(optional)</i></h4>
|
||||
<p>
|
||||
Providing an issue tracker helps your users get support more easily and provides you with an easy way to
|
||||
track bugs.
|
||||
</p>
|
||||
</div>
|
||||
<input <#if issues?has_content> value="${issues}" </#if> form="${form}" type="url" class="form-control" id="issues"
|
||||
name="issues" placeholder="https://github.com/MiniDigger/Hangar/issues" />
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Source code <i>(optional)</i></h4>
|
||||
<p>Support the community of developers by making your project open source!</p>
|
||||
</div>
|
||||
<input <#if source?has_content> value="${source}" </#if> form="${form}" type="url" class="form-control" id="source" name="source" placeholder="https://github.com/MiniDigger/Hangar" />
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>External support <i>(optional)</i></h4>
|
||||
<p>
|
||||
An external place where you can offer support to your users. Could be a forum, a Discord server, or
|
||||
somewhere else.
|
||||
</p>
|
||||
</div>
|
||||
<input <#if support?has_content> value="${support}" </#if> form="${form}" type="url" class="form-control" id="support" name="support" placeholder="https://discord.gg/papermc" />
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4><@spring.message "project.settings.license" /> <i>(<@spring.message "general.optional" />)</i></h4>
|
||||
<p><@spring.message "project.settings.license.info" /></p>
|
||||
</div>
|
||||
<div class="input-group pull-left">
|
||||
<div class="input-group-btn">
|
||||
<button type="button" class="btn btn-default btn-license dropdown-toggle" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
<span class="license"><#if licenseName?has_content>${licenseName}<#else><@spring.message "licenses.mit" /></#if></span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<input type="text" class="form-control" style="display: none;" name="license-name" form="${form}"
|
||||
value="<#if licenseName?has_content>${licenseName}<#else><@spring.message "licenses.mit" /></#if>" />
|
||||
<ul class="dropdown-menu dropdown-license">
|
||||
<li><a><@spring.message "licenses.mit" /></a></li>
|
||||
<li><a><@spring.message "licenses.apache2.0" /></a></li>
|
||||
<li><a><@spring.message "licenses.gpl" /></a></li>
|
||||
<li><a><@spring.message "licenses.lgpl" /></a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a class="license-custom"><@spring.message "licenses.custom" />…</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<input type="text" name="license-url" class="form-control" form="${form}"
|
||||
placeholder="https://github.com/MiniDigger/Hangar/LICENSE.txt" value="${licenseUrl}">
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4><@spring.message "project.settings.forumSync" /></h4>
|
||||
<p><@spring.message "project.settings.forumSync.info" /></p>
|
||||
</div>
|
||||
<div class="setting-content">
|
||||
<label>
|
||||
<input <#if forumSync> checked </#if> value="true" form="${form}" type="checkbox" id="forum-sync" name="forum-sync">
|
||||
Make forum posts
|
||||
</label>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</#macro>
|
||||
|
||||
|
@ -58,7 +58,7 @@ Documentation page within Project overview.
|
||||
<p><span id="star-count"></span> <a href="${routes.getRouteUrl("projects.showStargazers", p.project.ownerName, p.project.slug, "")}">stars</a></p>
|
||||
<p><span id="watcher-count"></span> <a href="${routes.getRouteUrl("projects.showWatchers", p.project.ownerName, p.project.slug, "")}">watchers</a></p>
|
||||
<p><span id="download-count"></span> total downloads</p>
|
||||
<#if p.project.licenseName??>
|
||||
<#if p.project.licenseName?has_content && p.project.licenseUrl?has_content>
|
||||
<p>
|
||||
<@spring.message "project.license.link" />
|
||||
<a target="_blank" rel="noopener" href="${p.project.licenseUrl}">${p.project.licenseName}</a>
|
||||
|
@ -3,8 +3,12 @@
|
||||
<#import "*/utils/form.ftlh" as form>
|
||||
<#import "*/utils/csrf.ftlh" as csrf>
|
||||
<#import "*/projects/view.ftlh" as projects />
|
||||
<#import "*/projects/helper/btnHide.ftlh" as btnHide />
|
||||
<#import "*/projects/helper/inputSettings.ftlh" as inputSettings />
|
||||
<#import "*/utils/userAvatar.ftlh" as userAvatar />
|
||||
<#import "*/users/memberList.ftlh" as memberList />
|
||||
|
||||
@import controllers.sugar.Requests.OreRequest
|
||||
<#--@import controllers.sugar.Requests.OreRequest
|
||||
@import models.viewhelper.{ProjectData, ScopedProjectData}
|
||||
@import ore.OreConfig
|
||||
@import ore.db.Model
|
||||
@ -16,7 +20,7 @@
|
||||
@import views.html.utils
|
||||
|
||||
@(p: ProjectData, sp: ScopedProjectData, deploymentKey: Option[Model[ProjectApiKey]], iconUrl: String)(implicit messages: Messages, flash: Flash,
|
||||
request: OreRequest[_], config: OreConfig, renderer: MarkdownRenderer, assetsFinder: AssetsFinder)
|
||||
request: OreRequest[_], config: OreConfig, renderer: MarkdownRenderer, assetsFinder: AssetsFinder)-->
|
||||
|
||||
<#assign scriptsVar>
|
||||
<script type="text/javascript" src="<@hangar.url "javascripts/projectManage.js" />"></script>
|
||||
@ -25,7 +29,7 @@
|
||||
<script type="text/javascript" src="<@hangar.url "javascripts/keyGen.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 @CSPNonce.attr>
|
||||
<script <#--@CSPNonce.attr -->>
|
||||
projectName = "${p.project.name}";
|
||||
PROJECT_OWNER = "${p.project.ownerName}";
|
||||
PROJECT_SLUG = "${p.project.slug}";
|
||||
@ -35,7 +39,10 @@
|
||||
</script>
|
||||
</#assign>
|
||||
|
||||
<@projects.view p=p sp=sp active="#settings" =additionalScripts=scriptsVar>
|
||||
<#-- @ftlvariable name="p" type="me.minidigger.hangar.model.viewhelpers.ProjectData" -->
|
||||
<#-- @ftlvariable name="sp" type="me.minidigger.hangar.model.viewhelpers.ScopedProjectData" -->
|
||||
<#assign Permission=@helper["me.minidigger.hangar.model.Permission"]>
|
||||
<@projects.view p=p sp=sp active="#settings" additionalScripts=scriptsVar>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
@ -44,8 +51,8 @@
|
||||
<div class="panel panel-default panel-settings">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title pull-left"><@spring.message "project.settings" /></h3>
|
||||
<#if request.headerData.globalPerm(Permission.SeeHidden)>
|
||||
@projects.helper.btnHide(p.project.namespace, p.project.visibility)
|
||||
<#if headerData.globalPerm(Permission.SeeHidden)>
|
||||
<@btnHide.btnHide p.namespace, p.visibility />
|
||||
|
||||
<div class="modal fade" id="modal-visibility-comment" tabindex="-1" role="dialog" aria-labelledby="modal-visibility-comment">
|
||||
<div class="modal-dialog" role="document">
|
||||
@ -70,50 +77,48 @@
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
@projects.helper.inputSettings(
|
||||
form = "save",
|
||||
homepage = p.project.settings.homepage,
|
||||
issues = p.project.settings.issues,
|
||||
source = p.project.settings.source,
|
||||
support = p.project.settings.support,
|
||||
licenseName = p.project.settings.licenseName,
|
||||
licenseUrl = p.project.settings.licenseUrl,
|
||||
selected = Some(p.project.category),
|
||||
forumSync = p.project.settings.forumSync,
|
||||
keywords = p.project.settings.keywords
|
||||
)
|
||||
|
||||
<@inputSettings.inputSettings
|
||||
"save"
|
||||
p.settings.homepage
|
||||
p.settings.issues
|
||||
p.settings.source
|
||||
p.settings.support
|
||||
p.settings.licenseName
|
||||
p.settings.licenseUrl
|
||||
p.project.category
|
||||
p.settings.forumSync
|
||||
p.settings.keywords
|
||||
/>
|
||||
<!-- Description -->
|
||||
@defining(config.ore.projects.maxDescLen) { maxLength =>
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Description</h4>
|
||||
<p>A short description of your project (max @maxLength).</p>
|
||||
</div>
|
||||
<input form="save" class="form-control" type="text" id="description"
|
||||
name="description" maxlength="@maxLength"
|
||||
@p.project.description.map { description =>
|
||||
value="@description"
|
||||
}.getOrElse {
|
||||
placeholder="<@spring.message "version.create.noDescription" />"
|
||||
}
|
||||
/>
|
||||
<div class="clearfix"></div>
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4>Description</h4>
|
||||
<p>A short description of your project (max ${config.projects.maxDescLen})</p>
|
||||
</div>
|
||||
}
|
||||
<input
|
||||
type="text"
|
||||
form="save"
|
||||
class="form-control"
|
||||
id="description"
|
||||
name="description"
|
||||
maxlength="${config.projects.maxDescLen}"
|
||||
<#if p.project.description?has_content>
|
||||
value="${p.project.description}"
|
||||
<#else>
|
||||
placeholder="<@spring.message "version.create.noDescription" />"
|
||||
</#if>
|
||||
>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<!-- Project icon -->
|
||||
<div class="setting setting-icon">
|
||||
<form id="form-icon" enctype="multipart/form-data">
|
||||
@CSRF.formField
|
||||
<@csrf.formField />
|
||||
<div class="setting-description">
|
||||
<h4>Icon</h4>
|
||||
|
||||
@utils.userAvatar(
|
||||
Some(p.projectOwner.name),
|
||||
p.projectOwner.avatarUrl,
|
||||
imgSrc = iconUrl,
|
||||
clazz = "user-avatar-md")
|
||||
<#-- @ftlvariable name="iconUrl" type="java.lang.String" -->
|
||||
<@userAvatar.userAvatar userName=p.projectOwner.name avatarUrl=p.projectOwner.avatarUrl imgSrc=iconUrl clazz="user-avatar-md" />
|
||||
|
||||
<input class="form-control-static" type="file" id="icon" name="icon" />
|
||||
</div>
|
||||
@ -132,7 +137,7 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<#if sp.perms(Permission.EditApiKeys)>
|
||||
<#if sp.perms(@helper["me.minidigger.hangar.model.Permission"].EditApiKeys)>
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4><@spring.message "project.settings.deployKey" /></h4>
|
||||
@ -140,6 +145,7 @@
|
||||
<@spring.message "project.settings.deployKey.info" />
|
||||
<a href="#"><i class="fas fa-question-circle"></i></a>
|
||||
</p>
|
||||
<#-- TODO project api keys -->
|
||||
@deploymentKey.map { key =>
|
||||
<input class="form-control input-key" type="text" value="@key.value" readonly />
|
||||
}.getOrElse {
|
||||
@ -171,18 +177,17 @@
|
||||
</div>
|
||||
<div class="setting-content">
|
||||
<input form="rename" id="name" name="name" class="form-control" type="text"
|
||||
value="@p.project.name"
|
||||
value="${p.project.name}"
|
||||
maxlength="@config.ore.projects.maxNameLen">
|
||||
<button id="btn-rename" data-toggle="modal" data-target="#modal-rename"
|
||||
class="btn btn-warning" disabled>
|
||||
<@spring.message "project.rename" />
|
||||
<button id="btn-rename" data-toggle="modal" data-target="#modal-rename" class="btn btn-warning" disabled>
|
||||
<@spring.message "project.rename" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<!-- Delete -->
|
||||
<#if sp.perms(Permission.DeleteProject)>
|
||||
<#if sp.perms(@helper["me.minidigger.hangar.model.Permission"].DeleteProject)>
|
||||
<div class="setting">
|
||||
<div class="setting-description">
|
||||
<h4 class="danger">Delete</h4>
|
||||
@ -198,14 +203,14 @@
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
<#if request.headerData.globalPerm(Permission.HardDeleteProject)>
|
||||
<#if headerData.globalPerm(@helper["me.minidigger.hangar.model.Permission"].HardDeleteProject)>
|
||||
<div class="setting striped">
|
||||
<div class="setting-description">
|
||||
<h4 class="danger">Hard Delete</h4>
|
||||
<p>Once you delete a project, it cannot be recovered.</p>
|
||||
</div>
|
||||
<div class="setting-content">
|
||||
<button class="btn btn-delete btn-danger btn-visibility-change" data-project="@p.project.ownerName/@p.project.slug" data-level="-99" data-modal="true">
|
||||
<button class="btn btn-delete btn-danger btn-visibility-change" data-project="${p.project.ownerName}/${p.project.slug}" data-level="-99" data-modal="true">
|
||||
<strong>Hard Delete</strong>
|
||||
</button>
|
||||
</div>
|
||||
@ -220,18 +225,24 @@
|
||||
<i class="fas fa-check"></i> Save changes
|
||||
</button>
|
||||
</@form.form>
|
||||
|
||||
<script>
|
||||
// Basically, hides the form value if its empty. Makes the controller simpler
|
||||
$("#save").submit(function() {
|
||||
$(":input[form=save]").filter(function () {
|
||||
return !this.value;
|
||||
}).attr("disabled", true);
|
||||
|
||||
return true;
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Side panel -->
|
||||
<div class="col-md-4">
|
||||
@users.memberList(p,
|
||||
editable = true,
|
||||
perms = sp.permissions,
|
||||
removeCall = routes.getRouteUrl("projects.removeMember", p.project.ownerName, p.project.slug),
|
||||
settingsCall = routes.getRouteUrl("projects.showSettings", p.project.ownerName, p.project.slug)
|
||||
)
|
||||
<@memberList.memberList j=p perms=sp.permissions editable=true removeCall=routes.getRouteUrl("projects.removeMember", p.project.ownerName, p.project.slug) settingsCall=routes.getRouteUrl("projects.showSettings", p.project.ownerName, p.project.slug) />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -21,6 +21,8 @@
|
||||
-->
|
||||
|
||||
<!-- TODO: Pagination -->
|
||||
<#-- @ftlvariable name="j" type="java.util.Set<me.minidigger.hangar.model.viewhelpers.ProjectMember>" -->
|
||||
<#-- @ftlvariable name="perms" type="me.minidigger.hangar.model.Permission" -->
|
||||
<#assign Permission=@helper["me.minidigger.hangar.model.Permission"]>
|
||||
<#macro memberList j perms editable=false removeCall="" settingsCall="" saveCall="">
|
||||
<#if j.members?size != 0>
|
||||
@ -98,33 +100,34 @@
|
||||
|
||||
<ul class="list-members list-group">
|
||||
<!-- Member list -->
|
||||
@j.filteredMembers.map { case (role, user) =>
|
||||
<li class="list-group-item">
|
||||
<@userAvatar.userAvatar userName=user.name avatarUrl=user.avatarUrl clazz="user-avatar-xs"></@userAvatar.userAvatar>
|
||||
<a class="username" href="${routes.getRouteUrl("users.showProjects", user.name)}">
|
||||
${user.name}
|
||||
</a>
|
||||
<p style="display: none;" class="role-id">${role.id}</p>
|
||||
<#if editable && perms.has(Permission.ManageSubjectMembers) && !role.role.permissions.has(Permission.IsOrganizationOwner)>
|
||||
<a href="#">
|
||||
<i style="padding-left:5px" class="fas fa-trash" data-toggle="modal" data-target="#modal-user-delete"></i>
|
||||
<#-- @ftlvariable name="projectMember" type="me.minidigger.hangar.model.viewhelpers.ProjectMember" -->
|
||||
<#-- @ftlvariable name="user" type="me.minidigger.hangar.db.model.UsersTable" -->
|
||||
<#list j.filteredMembers(headerData) as projectMember, user>
|
||||
<li class="list-group-item">
|
||||
<@userAvatar.userAvatar userName=user.name avatarUrl=user.avatarUrl clazz="user-avatar-xs"></@userAvatar.userAvatar>
|
||||
<a class="username" href="${routes.getRouteUrl("users.showProjects", user.name)}">
|
||||
${user.name}
|
||||
</a>
|
||||
<a href="#"><i style="padding-left:5px" class="fas fa-edit"></i></a>
|
||||
</#if>
|
||||
<p style="display: none;" class="role-id">${projectMember.role.roleId}</p>
|
||||
<#if editable && perms.has(Permission.ManageSubjectMembers) && !projectMember.role.permissions.has(Permission.IsOrganizationOwner)>
|
||||
<a href="#">
|
||||
<i style="padding-left:5px" class="fas fa-trash" data-toggle="modal" data-target="#modal-user-delete"></i>
|
||||
</a>
|
||||
<a href="#"><i style="padding-left:5px" class="fas fa-edit"></i></a>
|
||||
</#if>
|
||||
|
||||
<span class="minor pull-right">
|
||||
<#if !role.isAccepted>
|
||||
<span class="minor">(Invited as ${role.role.title})</span>
|
||||
<#else>
|
||||
${role.role.title}
|
||||
</#if>
|
||||
</span>
|
||||
|
||||
</li>
|
||||
}
|
||||
<span class="minor pull-right">
|
||||
<#if !projectMember.isAccepted>
|
||||
<span class="minor">(Invited as ${projectMember.role.title})</span>
|
||||
<#else>
|
||||
${projectMember.role.title}
|
||||
</#if>
|
||||
</span>
|
||||
</li>
|
||||
</#list>
|
||||
|
||||
<!-- User search -->
|
||||
<#if perms.has(Permission.ManageSubjectMembers) && editable>
|
||||
<#if perms.has(@helper["me.minidigger.hangar.model.Permission"].ManageSubjectMembers) && editable>
|
||||
<li class="list-group-item">
|
||||
<@userSearch.userSearch />
|
||||
</li>
|
||||
|
Loading…
Reference in New Issue
Block a user