Added new inputs to CreateProject.vue

This commit is contained in:
Jake Potrebic 2020-10-07 01:42:11 -07:00 committed by MiniDigger
parent f67b04c257
commit 6db5a2752b
10 changed files with 293 additions and 147 deletions

View File

@ -17,7 +17,7 @@
<div class="input-group-prepend">
<label for="project-name" class="input-group-text" v-text="$t('project.create.input.name')"></label>
</div>
<input v-model="form.projectName" type="text" id="project-name" name="name" class="form-control" required @input="projectNameInput" />
<input v-model.trim="form.projectName" type="text" id="project-name" name="name" class="form-control" required @input="projectNameInput" />
<div class="input-group-append">
<div v-show="success.projectName" class="input-group-text text-success">
<i class="fas fa-check-circle fa-lg"></i>
@ -33,7 +33,7 @@
<div class="input-group-prepend">
<label for="project-description" class="input-group-text" v-text="$t('project.create.input.description')"></label>
</div>
<input v-model="form.description" type="text" name="description" class="form-control" id="project-description" required />
<input v-model.trim="form.description" type="text" name="description" class="form-control" id="project-description" required />
</div>
</div>
<div class="col-12">
@ -41,13 +41,109 @@
<div class="input-group-prepend">
<label for="category-input" class="input-group-text" v-text="$t('project.create.input.category')"></label>
</div>
<select v-model="form.category" id="category-input" name="category" class="custom-select" required>
<select id="category-input" name="category" class="custom-select" required>
<option v-for="cat in categories" :key="cat.id" :value="cat.name" v-text="cat.name"></option>
</select>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary float-right" :disabled="!success.projectName">Create project</button>
<div class="row collapse" id="additional-settings">
<div class="col-12 text-center"><span class="input-divider">Links</span></div>
<div class="col-12">
<div class="input-group">
<div class="input-group-prepend">
<label for="homepage-input" class="input-group-text"> <i class="fas fa-home"></i> Homepage </label>
</div>
<input id="homepage-input" name="homepageUrl" type="text" class="form-control" />
</div>
</div>
<div class="col-12">
<div class="input-group">
<div class="input-group-prepend">
<label for="issue-tracker-input" class="input-group-text"> <i class="fas fa-bug"></i> Issue Tracker </label>
</div>
<input id="issue-tracker-input" name="issueTrackerUrl" type="text" class="form-control" />
</div>
</div>
<div class="col-12">
<div class="input-group">
<div class="input-group-prepend">
<label for="source-input" class="input-group-text"> <i class="fas fa-code"></i> Source Code </label>
</div>
<input id="source-input" name="sourceUrl" type="text" class="form-control" />
</div>
</div>
<div class="col-12">
<div class="input-group">
<div class="input-group-prepend">
<label for="external-support-input" class="input-group-text"> <i class="fas fa-question"></i> External Support </label>
</div>
<input id="external-support-input" name="externalSupportUrl" type="text" class="form-control" />
</div>
</div>
<div class="col-12 text-center"><span class="input-divider">License</span></div>
<div class="col-12">
<div class="input-group">
<div class="input-group-prepend">
<label for="license-type-input" class="input-group-text">Type</label>
</div>
<select v-model="form.licenseType" name="licenseType" id="license-type-input" class="custom-select">
<option v-text="$t('licenses.mit')"></option>
<option v-text="$t('licenses.apache2.0')"></option>
<option v-text="$t('licenses.gpl')"></option>
<option v-text="$t('licenses.lgpl')"></option>
<option v-text="$t('licenses.custom')"></option>
</select>
</div>
</div>
<div class="col-12">
<div class="input-group">
<div class="input-group-prepend">
<label for="license-url-input" class="input-group-text">
<i class="fas fa-gavel"></i> {{ form.licenseType === $t('licenses.custom') ? 'Name/' : '' }}URL
</label>
</div>
<div v-show="form.licenseType === $t('licenses.custom')" class="input-group-prepend">
<input
v-model.trim="form.customName"
type="text"
id="license-name-input"
name="licenseName"
class="form-control"
placeholder="Custom Name"
aria-label="License Name"
/>
</div>
<input id="license-url-input" name="licenseUrl" class="form-control" type="text" placeholder="URL" />
</div>
</div>
<div class="col-12 text-center"><span class="input-divider">SEO</span></div>
<div class="col-12">
<div class="input-group">
<div class="input-group-prepend">
<label for="keywords-input" class="input-group-text"> <i class="fas fa-key"></i> Keywords </label>
</div>
<input id="keywords-input" name="keywords" type="text" class="form-control" />
</div>
</div>
</div>
<button
type="button"
class="btn btn-info float-left"
data-toggle="collapse"
data-target="#additional-settings"
aria-expanded="false"
aria-controls="additional-settings"
>
Additional Settings (optional)
</button>
<button
type="submit"
class="btn btn-primary float-right"
:disabled="!success.projectName || !form.description || (form.licenseType === $t('licenses.custom') && !form.customName)"
>
Create project
</button>
</HangarForm>
</template>
@ -73,7 +169,8 @@ export default {
createAs: window.CURRENT_USER.id,
projectName: '',
description: '',
category: Category.values[0].name,
licenseType: '',
customName: '',
},
categories: Category.values,
};
@ -107,4 +204,35 @@ export default {
.row > * {
margin-bottom: 10px;
}
label > svg {
margin-right: 5px;
}
#license-name-input {
border-radius: 0;
}
.input-divider {
position: relative;
}
.input-divider::before,
.input-divider::after {
content: '';
position: absolute;
width: 10vw;
top: 0.35rem;
height: 2px;
overflow: hidden;
background-color: #00000082;
}
.input-divider::before {
left: -11vw;
}
.input-divider::after {
right: -11vw;
}
</style>

View File

@ -29,6 +29,7 @@ import {
faEyeSlash,
faFlag,
faGamepad,
faGavel,
faGem as fasGem,
faGlobe,
faGraduationCap,
@ -174,7 +175,8 @@ library.add(
faExclamationTriangle,
faCheckSquare,
faQuestion,
faTag
faTag,
faGavel
);
dom.watch();

View File

@ -1,80 +0,0 @@
import $ from 'jquery';
import { toggleSpinner } from '@/utils';
//=====> HELPER FUNCTIONS
function tooltip(selector, title) {
$(selector).tooltip({
placement: 'right',
title: title,
});
}
function success(selector, then) {
// Simulate loading :P
setTimeout(function () {
toggleSpinner($(selector));
$(selector).addClass('fa-check-circle');
then();
}, 500);
}
function failed(selector, message) {
toggleSpinner($(selector));
$(selector).addClass('fa-times-circle');
tooltip(selector, message);
}
// eslint-disable-next-line no-unused-vars
function checkId(name, owner, slug) {
$.ajax({
url: '/api/v1/projects/' + owner + '/' + slug,
statusCode: {
404: function () {
success('.id-status', function () {
checkName(name, true, owner, slug);
});
},
200: function () {
failed('.id-status', 'That plugin ID is not available!');
checkName(name, false, owner, slug);
},
},
});
}
function checkName(name, idSuccess, owner, slug) {
if (name.length > 25) {
failed('.name-status', 'This name is too long. Please rename your project to something under 25 characters.');
updateContinueButton(idSuccess, false);
return;
}
$.ajax({
url: '/' + owner + '/' + slug,
statusCode: {
404: function () {
success('.name-status', function () {
updateContinueButton(idSuccess, true);
});
},
200: function () {
failed('.name-status', 'You already have a project of this name!');
updateContinueButton(idSuccess, false);
},
},
});
}
function updateContinueButton(idSuccess, nameSuccess) {
var btn = $('.continue-btn').hide().removeClass('btn-default');
var icon = toggleSpinner(btn.find('[data-fa-i2svg]'));
if (idSuccess && nameSuccess) {
btn.addClass('btn-primary').prop('disabled', false);
icon.addClass('fa-arrow-right');
} else {
btn.addClass('btn-danger');
icon.addClass('fa-times');
}
btn.fadeIn();
}

View File

@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.papermc.hangar.config.hangar.HangarConfig;
import io.papermc.hangar.controller.forms.NewProjectForm;
import io.papermc.hangar.controller.forms.ProjectNameValidate;
import io.papermc.hangar.db.customtypes.LoggedActionType;
import io.papermc.hangar.db.customtypes.LoggedActionType.ProjectContext;
@ -143,10 +144,7 @@ public class ProjectsController extends HangarController {
@UserLock
@Secured("ROLE_USER")
@PostMapping(value = "/new", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public Object createProject(@RequestParam("name") String name,
@RequestParam("category") String cat,
@RequestParam("description") String description,
@RequestParam("owner") long owner) {
public Object createProject(NewProjectForm newProjectForm) {
// check if creation should be prevented
UsersTable curUser = getCurrentUser();
String uploadError = projectFactory.getUploadError(curUser);
@ -156,14 +154,14 @@ public class ProjectsController extends HangarController {
return fillModel(mav);
}
// validate input
Category category = Category.fromTitle(cat);
Category category = Category.fromTitle(newProjectForm.getCategory());
if (category == null) {
ModelAndView mav = showCreator();
AlertUtil.showAlert(mav, AlertUtil.AlertType.ERROR, "error.project.categoryNotFound");
return fillModel(mav);
}
ProjectOwner ownerUser = getProjectOwner(owner);
ProjectOwner ownerUser = getProjectOwner(newProjectForm.getOwner());
if (ownerUser == null) {
return fillModel(AlertUtil.showAlert(Routes.PROJECTS_SHOW_CREATOR.getRedirect(), AlertType.ERROR, "error.project.ownerNotFound"));
}
@ -171,7 +169,7 @@ public class ProjectsController extends HangarController {
// create project
ProjectsTable project;
try {
project = projectFactory.createProject(ownerUser, name, category, description);
project = projectFactory.createProject(ownerUser, category, newProjectForm);
} catch (HangarException ex) {
ModelAndView mav = showCreator();
AlertUtil.showAlert(mav, AlertUtil.AlertType.ERROR, ex.getMessageKey());

View File

@ -0,0 +1,114 @@
package io.papermc.hangar.controller.forms;
public class NewProjectForm {
private String name;
private String category;
private String description;
private long owner;
private String homepageUrl;
private String issueTrackerUrl;
private String sourceUrl;
private String externalSupportUrl;
private String keywords;
private String licenseType;
private String licenseName;
private String licenseUrl;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public long getOwner() {
return owner;
}
public void setOwner(long owner) {
this.owner = owner;
}
public String getHomepageUrl() {
return homepageUrl;
}
public void setHomepageUrl(String homepageUrl) {
this.homepageUrl = homepageUrl;
}
public String getIssueTrackerUrl() {
return issueTrackerUrl;
}
public void setIssueTrackerUrl(String issueTrackerUrl) {
this.issueTrackerUrl = issueTrackerUrl;
}
public String getSourceUrl() {
return sourceUrl;
}
public void setSourceUrl(String sourceUrl) {
this.sourceUrl = sourceUrl;
}
public String getExternalSupportUrl() {
return externalSupportUrl;
}
public void setExternalSupportUrl(String externalSupportUrl) {
this.externalSupportUrl = externalSupportUrl;
}
public String getKeywords() {
return keywords;
}
public void setKeywords(String keywords) {
this.keywords = keywords;
}
public String getLicenseType() {
return licenseType;
}
public void setLicenseType(String licenseType) {
this.licenseType = licenseType;
}
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;
}
}

View File

@ -27,8 +27,8 @@ import java.util.Map;
@RegisterBeanMapper(ProjectsTable.class)
public interface ProjectDao {
@SqlUpdate("insert into projects (created_at, name, slug, owner_name, owner_id, category, description, visibility) " +
"values (:now, :name, :slug, :ownerName,:ownerId, :category, :description, :visibility)")
@SqlUpdate("insert into projects (created_at, name, slug, owner_name, owner_id, category, description, visibility, homepage, issues, source, support, license_name, license_url) " +
"values (:now, :name, :slug, :ownerName,:ownerId, :category, :description, :visibility, :homepage, :issues, :source, :support, :licenseName, :licenseUrl)")
@Timestamped
@GetGeneratedKeys
ProjectsTable insert(@BindBean ProjectsTable project);

View File

@ -1,14 +1,17 @@
package io.papermc.hangar.db.model;
import io.papermc.hangar.controller.forms.NewProjectForm;
import io.papermc.hangar.db.customtypes.JSONB;
import io.papermc.hangar.model.Category;
import io.papermc.hangar.model.Visibility;
import io.papermc.hangar.util.StringUtils;
import org.jdbi.v3.core.annotation.Unmappable;
import org.jdbi.v3.core.enums.EnumByOrdinal;
import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.Set;
public class ProjectsTable implements Visitable {
@ -34,18 +37,24 @@ public class ProjectsTable implements Visitable {
private String licenseUrl;
private boolean forumSync;
public ProjectsTable() {
//
}
public ProjectsTable() { }
public ProjectsTable(String name, String slug, String ownerName, long ownerId, Category category, String description, Visibility visibility) {
this.name = name;
this.slug = slug;
this.ownerName = ownerName;
this.ownerId = ownerId;
public ProjectsTable(ProjectOwner projectOwner, Category category, NewProjectForm newProjectForm) {
this.name = newProjectForm.getName();
this.slug = StringUtils.slugify(newProjectForm.getName());
this.ownerName = projectOwner.getName();
this.ownerId = projectOwner.getUserId();
this.category = category;
this.description = description;
this.visibility = visibility;
this.description = newProjectForm.getDescription();
this.visibility = Visibility.NEW;
this.homepage = StringUtils.stringOrNull(newProjectForm.getHomepageUrl());
this.issues = StringUtils.stringOrNull(newProjectForm.getIssueTrackerUrl());
this.source = StringUtils.stringOrNull(newProjectForm.getSourceUrl());
this.support = StringUtils.stringOrNull(newProjectForm.getExternalSupportUrl());
this.keywords = newProjectForm.getKeywords() != null ? Set.of(newProjectForm.getKeywords().split("\\s")) : Set.of();
this.licenseName = StringUtils.stringOrNull(newProjectForm.getLicenseType().equalsIgnoreCase("custom") ? newProjectForm.getLicenseName() : newProjectForm.getLicenseType());
this.licenseUrl = StringUtils.stringOrNull(newProjectForm.getLicenseUrl());
}
public long getId() {

View File

@ -2,6 +2,7 @@ package io.papermc.hangar.service.project;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.papermc.hangar.config.hangar.HangarConfig;
import io.papermc.hangar.controller.forms.NewProjectForm;
import io.papermc.hangar.db.customtypes.JSONB;
import io.papermc.hangar.db.customtypes.LoggedActionType;
import io.papermc.hangar.db.customtypes.LoggedActionType.ProjectContext;
@ -87,16 +88,15 @@ public class ProjectFactory {
}
}
public ProjectsTable createProject(ProjectOwner ownerUser, String name, Category category, String description) {
String slug = StringUtils.slugify(name);
ProjectsTable projectsTable = new ProjectsTable(name, slug, ownerUser.getName(), ownerUser.getUserId(), category, description, Visibility.NEW);
public ProjectsTable createProject(ProjectOwner ownerUser, Category category, NewProjectForm newProjectForm) {
ProjectsTable projectsTable = new ProjectsTable(ownerUser, category, newProjectForm);
ProjectChannelsTable channelsTable = new ProjectChannelsTable(hangarConfig.channels.getNameDefault(), hangarConfig.channels.getColorDefault(), -1, false);
String content = "# " + name + "\n\n" + hangarConfig.pages.home.getMessage();
String content = "# " + projectsTable.getName() + "\n\n" + hangarConfig.pages.home.getMessage();
ProjectPagesTable pagesTable = new ProjectPage(-1, hangarConfig.pages.home.getName(), StringUtils.slugify(hangarConfig.pages.home.getName()), content, false, null);
checkProjectAvailability(ownerUser, name);
checkProjectAvailability(ownerUser, projectsTable.getName());
projectsTable = projectDao.get().insert(projectsTable);
channelsTable.setProjectId(projectsTable.getId());

View File

@ -1,5 +1,7 @@
package io.papermc.hangar.util;
import org.springframework.lang.Nullable;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
@ -39,6 +41,16 @@ public class StringUtils {
return Arrays.stream(str.split("\\.")).map(Integer::parseInt).collect(Collectors.toList());
}
/**
* Takes a nullable input and returns itself or if blank, null
* @param input input string
* @return itself or null
*/
public static String stringOrNull(@Nullable String input) {
if (input == null || input.isBlank()) return null;
return input;
}
private static final Pattern LAST_WHOLE_VERSION = Pattern.compile("((?<=,\\s)|^)[0-9.]{2,}(?=-\\d+$)");
private static final Pattern PREV_HAS_HYPHEN = Pattern.compile("(?<=\\d-)\\d+$");
private static final Pattern PREV_HAS_COMMA_OR_FIRST = Pattern.compile("((?<=,\\s)|^)[0-9.]+$");

View File

@ -11,7 +11,6 @@ Page used for uploading and creating new projects.
window.ORGANIZAITONS = ${mapper.valueToTree(createProjectOrgas)};
</script>
<script type="text/javascript" src="<@hangar.url "js/create-project.js" />"></script>
<#--<script type="text/javascript" src="<@hangar.url "js/projectCreateValidate.js" />"></script>-->
</#assign>
<#assign styleVar>
<link rel="stylesheet" href="<@hangar.url "css/create-project.css" />">
@ -37,42 +36,6 @@ Page used for uploading and creating new projects.
<div>
<div id="create-project"></div>
<#--<#import "*/utils/form.ftlh" as form>
<@form.form action=Routes.PROJECTS_CREATE_PROJECT.getRouteUrl() method="POST">
<#import "*/utils/csrf.ftlh" as csrf>
<@csrf.formField />
<div class="form-group">
<label for="projectName"><@spring.message "project.create.input.name" /></label>
<input type="text" id="projectName" name="name" class="form-control">
</div>
<div class="form-group">
<label for="projectCategory"><@spring.message "project.create.input.category" /></label>
<select id="projectCategory" name="category" class="form-control">
<#assign Category=@helper["io.papermc.hangar.model.Category"]>
<#list Category.values() as cat>
<option>${cat.title}</option>
</#list>
</select>
</div>
<div class="form-group">
<label for="projectDescription"><@spring.message "project.create.input.description" /></label>
<input type="text" id="projectDescription" name="description" class="form-control">
</div>
<div class="form-group">
<label for="projectCategory"><@spring.message "project.owner" /></label>
<select id="projectCategory" name="owner" class="form-control">
<option value="${cu.id}">${cu.name}</option>
<#list createProjectOrgas as orga>
<option value="${orga.id}">${orga.name}</option>
</#list>
</select>
</div>
<button type="submit" class="btn btn-primary">Create project</button>
</@form.form>-->
</div>
</div>
</div>