First working version. No user limits or usage tracking.
This commit is contained in:
parent
070ea5cc20
commit
dc56b1f5d0
|
@ -40,10 +40,6 @@ func TestStreams(t *testing.T) {
|
||||||
AssertRegexMatch(t, BuildStreams(), RegexStreams, nil)
|
AssertRegexMatch(t, BuildStreams(), RegexStreams, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSiteMap(t *testing.T) {
|
|
||||||
AssertRegexMatch(t, BuildSiteMap(), RegexSiteMap, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWhenIsIt(t *testing.T) {
|
func TestWhenIsIt(t *testing.T) {
|
||||||
AssertRegexMatch(t, BuildWhenIsIt(), RegexWhenIsIt, nil)
|
AssertRegexMatch(t, BuildWhenIsIt(), RegexWhenIsIt, nil)
|
||||||
}
|
}
|
||||||
|
@ -288,6 +284,11 @@ func TestEpisodeGuide(t *testing.T) {
|
||||||
AssertSubdomain(t, BuildCineraIndex("hero", "code"), "hero")
|
AssertSubdomain(t, BuildCineraIndex("hero", "code"), "hero")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAssetUpload(t *testing.T) {
|
||||||
|
AssertRegexMatch(t, BuildAssetUpload("hero"), RegexAssetUpload, nil)
|
||||||
|
AssertSubdomain(t, BuildAssetUpload("hero"), "hero")
|
||||||
|
}
|
||||||
|
|
||||||
func TestProjectCSS(t *testing.T) {
|
func TestProjectCSS(t *testing.T) {
|
||||||
AssertRegexMatch(t, BuildProjectCSS("000000"), RegexProjectCSS, nil)
|
AssertRegexMatch(t, BuildProjectCSS("000000"), RegexProjectCSS, nil)
|
||||||
}
|
}
|
||||||
|
@ -308,7 +309,8 @@ func TestPublic(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestForumMarkRead(t *testing.T) {
|
func TestForumMarkRead(t *testing.T) {
|
||||||
AssertRegexMatch(t, BuildForumMarkRead(c.CurrentProject.Slug, 5), RegexForumMarkRead, map[string]string{"sfid": "5"})
|
AssertRegexMatch(t, BuildForumMarkRead("hero", 5), RegexForumMarkRead, map[string]string{"sfid": "5"})
|
||||||
|
AssertSubdomain(t, BuildForumMarkRead("hero", 5), "hero")
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssertSubdomain(t *testing.T, fullUrl string, expectedSubdomain string) {
|
func AssertSubdomain(t *testing.T, fullUrl string, expectedSubdomain string) {
|
||||||
|
|
|
@ -609,6 +609,17 @@ func BuildDiscordShowcaseBacklog() string {
|
||||||
return Url("/discord_showcase_backlog", nil)
|
return Url("/discord_showcase_backlog", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* User assets
|
||||||
|
*/
|
||||||
|
|
||||||
|
var RegexAssetUpload = regexp.MustCompile("^/upload_asset$")
|
||||||
|
|
||||||
|
// NOTE(asaf): Providing the projectSlug avoids any CORS problems.
|
||||||
|
func BuildAssetUpload(projectSlug string) string {
|
||||||
|
return ProjectUrl("/upload_asset", nil, projectSlug)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assets
|
* Assets
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,6 +10,10 @@
|
||||||
#editor {
|
#editor {
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#editor.drop {
|
||||||
|
box-shadow: inset 0px 0px 5px yellow;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
@ -104,6 +108,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
const maxFileSize = {{ .MaxFileSize }};
|
||||||
|
const uploadUrl = {{ .UploadUrl }};
|
||||||
|
|
||||||
const form = document.querySelector('#form');
|
const form = document.querySelector('#form');
|
||||||
const titleField = document.querySelector('#title'); // may be undefined, be careful!
|
const titleField = document.querySelector('#title'); // may be undefined, be careful!
|
||||||
const textField = document.querySelector('#editor');
|
const textField = document.querySelector('#editor');
|
||||||
|
@ -177,5 +184,155 @@
|
||||||
form.addEventListener('submit', e => {
|
form.addEventListener('submit', e => {
|
||||||
window.localStorage.removeItem(storageKey);
|
window.localStorage.removeItem(storageKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let fileCounter = 0;
|
||||||
|
|
||||||
|
textField.addEventListener("dragover", function(ev) {
|
||||||
|
let effect = "none";
|
||||||
|
for (let i = 0; i < ev.dataTransfer.items.length; ++i) {
|
||||||
|
if (ev.dataTransfer.items[i].kind.toLowerCase() == "file") {
|
||||||
|
effect = "copy";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ev.dataTransfer.dropEffect = effect;
|
||||||
|
ev.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
let enterCounter = 0;
|
||||||
|
|
||||||
|
textField.addEventListener("dragenter", function(ev) {
|
||||||
|
enterCounter++;
|
||||||
|
let droppable = false;
|
||||||
|
for (let i = 0; i < ev.dataTransfer.items.length; ++i) {
|
||||||
|
if (ev.dataTransfer.items[i].kind.toLowerCase() == "file") {
|
||||||
|
droppable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (droppable) {
|
||||||
|
textField.classList.add("drop");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
textField.addEventListener("dragleave", function(ev) {
|
||||||
|
enterCounter--;
|
||||||
|
if (enterCounter == 0) {
|
||||||
|
textField.classList.remove("drop");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeUploadString(uploadNumber, filename) {
|
||||||
|
return `Uploading file #${uploadNumber}: \`${filename}\`...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
textField.addEventListener("drop", function(ev) {
|
||||||
|
enterCounter = 0;
|
||||||
|
textField.classList.remove("drop");
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
for (let i = 0; i < ev.dataTransfer.files.length; ++i) {
|
||||||
|
let f = ev.dataTransfer.files[i];
|
||||||
|
if (f.size < maxFileSize) {
|
||||||
|
items.push({ file: f, error: null });
|
||||||
|
} else {
|
||||||
|
items.push({ file: null, error: `\`${f.name}\` is too big! Max size is ${maxFileSize} but the file is ${f.size}.` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cursorStart = textField.selectionStart;
|
||||||
|
let cursorEnd = textField.selectionEnd;
|
||||||
|
|
||||||
|
let toInsert = "\n";
|
||||||
|
for (let i = 0; i < items.length; ++i) {
|
||||||
|
if (items[i].file) {
|
||||||
|
fileCounter++;
|
||||||
|
toInsert += makeUploadString(fileCounter, items[i].file.name) + "\n";
|
||||||
|
startUpload(fileCounter, items[i].file);
|
||||||
|
} else {
|
||||||
|
toInsert += `${items[i].error}\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
textField.value = textField.value.substring(0, cursorStart) + toInsert + textField.value.substring(cursorEnd, textField.value.length);
|
||||||
|
doMarkdown();
|
||||||
|
|
||||||
|
ev.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
function replaceUploadString(upload, newString) {
|
||||||
|
let cursorStart = textField.selectionStart;
|
||||||
|
let cursorEnd = textField.selectionEnd;
|
||||||
|
let uploadString = makeUploadString(upload.uploadNumber, upload.file.name);
|
||||||
|
let insertIndex = textField.value.indexOf(uploadString)
|
||||||
|
textField.value = textField.value.replace(uploadString, newString);
|
||||||
|
if (cursorStart <= insertIndex + uploadString.length) {
|
||||||
|
textField.selectionStart = cursorStart;
|
||||||
|
} else {
|
||||||
|
textField.selectionStart = cursorStart - uploadString.length + newString.length;
|
||||||
|
}
|
||||||
|
if (cursorEnd <= insertIndex + uploadString.length) {
|
||||||
|
textField.selectionEnd = cursorEnd;
|
||||||
|
} else {
|
||||||
|
textField.selectionEnd = cursorEnd - uploadString.length + newString.length;
|
||||||
|
}
|
||||||
|
doMarkdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
let uploadQueue = [];
|
||||||
|
let currentUpload = null;
|
||||||
|
let currentXhr = null;
|
||||||
|
|
||||||
|
function startUpload(uploadNumber, file) {
|
||||||
|
uploadQueue.push({
|
||||||
|
uploadNumber: uploadNumber,
|
||||||
|
file: file
|
||||||
|
});
|
||||||
|
uploadNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadDone(ev) {
|
||||||
|
try {
|
||||||
|
if (currentXhr.status == 200 && currentXhr.response) {
|
||||||
|
if (currentXhr.response.url) {
|
||||||
|
let url = currentXhr.response.url;
|
||||||
|
let newString = `[${currentUpload.file.name}](${url})`;
|
||||||
|
if (currentXhr.response.mime.startsWith("image")) {
|
||||||
|
newString = "!" + newString;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceUploadString(currentUpload, newString);
|
||||||
|
} else if (currentXhr.response.error) {
|
||||||
|
replaceUploadString(currentUpload, `Upload failed for \`${currentUpload.file.name}\`: ${currentXhr.response.error}.`);
|
||||||
|
} else {
|
||||||
|
replaceUploadString(currentUpload, `There was a problem uploading your file \`${currentUpload.file.name}\`.`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
replaceUploadString(currentUpload, `There was a problem uploading your file \`${currentUpload.file.name}\`.`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
replaceUploadString(currentUpload, `There was a problem uploading your file \`${currentUpload.file.name}\`.`);
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
currentUpload = null;
|
||||||
|
currentXhr = null;
|
||||||
|
uploadNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadNext() {
|
||||||
|
if (currentUpload == null) {
|
||||||
|
next = uploadQueue.shift();
|
||||||
|
if (next) {
|
||||||
|
// NOTE(asaf): We use XHR because fetch can't do upload progress reports. Womp womp. https://youtu.be/Pubd-spHN-0?t=2
|
||||||
|
currentXhr = new XMLHttpRequest();
|
||||||
|
currentXhr.open("POST", uploadUrl, true);
|
||||||
|
currentXhr.setRequestHeader("Hmn-Upload-Filename", next.file.name);
|
||||||
|
currentXhr.responseType = "json";
|
||||||
|
currentXhr.addEventListener("loadend", uploadDone);
|
||||||
|
currentXhr.send(next.file);
|
||||||
|
currentUpload = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
package website
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.handmade.network/hmn/hmn/src/assets"
|
||||||
|
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||||
|
"git.handmade.network/hmn/hmn/src/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AssetUploadResult struct {
|
||||||
|
Url string `json:"url,omitempty"`
|
||||||
|
Mime string `json:"mime,omitempty"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const assetMaxSize = 10 * 1024 * 1024
|
||||||
|
const assetMaxSizeAdmin = 10 * 1024 * 1024 * 1024
|
||||||
|
|
||||||
|
func AssetMaxSize(user *models.User) int {
|
||||||
|
if user.IsStaff {
|
||||||
|
return assetMaxSizeAdmin
|
||||||
|
} else {
|
||||||
|
return assetMaxSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssetUpload(c *RequestContext) ResponseData {
|
||||||
|
maxFilesize := AssetMaxSize(c.CurrentUser)
|
||||||
|
|
||||||
|
contentLength, hasLength := c.Req.Header["Content-Length"]
|
||||||
|
if hasLength {
|
||||||
|
filesize, err := strconv.Atoi(contentLength[0])
|
||||||
|
if err == nil && filesize > maxFilesize {
|
||||||
|
res := ResponseData{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
}
|
||||||
|
jsonString, _ := json.Marshal(AssetUploadResult{
|
||||||
|
Error: fmt.Sprintf("Filesize too big. Maximum size is %d.", maxFilesize),
|
||||||
|
})
|
||||||
|
res.Write(jsonString)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filenameHeader, hasFilename := c.Req.Header["Hmn-Upload-Filename"]
|
||||||
|
originalFilename := ""
|
||||||
|
if hasFilename {
|
||||||
|
originalFilename = strings.ReplaceAll(filenameHeader[0], " ", "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyReader := http.MaxBytesReader(c.Res, c.Req.Body, int64(maxFilesize))
|
||||||
|
data, err := io.ReadAll(bodyReader)
|
||||||
|
if err != nil {
|
||||||
|
res := ResponseData{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Errors: []error{err},
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
mimeType := http.DetectContentType(data)
|
||||||
|
width := 0
|
||||||
|
height := 0
|
||||||
|
|
||||||
|
if strings.HasPrefix(mimeType, "image") {
|
||||||
|
config, _, err := image.DecodeConfig(bytes.NewReader(data))
|
||||||
|
if err == nil {
|
||||||
|
width = config.Width
|
||||||
|
height = config.Height
|
||||||
|
} else {
|
||||||
|
// NOTE(asaf): Not image
|
||||||
|
mimeType = "application/octet-stream"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asset, err := assets.Create(c.Context(), c.Conn, assets.CreateInput{
|
||||||
|
Content: data,
|
||||||
|
Filename: originalFilename,
|
||||||
|
ContentType: mimeType,
|
||||||
|
UploaderID: &c.CurrentUser.ID,
|
||||||
|
Width: width,
|
||||||
|
Height: height,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
res := ResponseData{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Errors: []error{err},
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
res := ResponseData{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
}
|
||||||
|
jsonString, err := json.Marshal(AssetUploadResult{
|
||||||
|
Url: hmnurl.BuildS3Asset(asset.S3Key),
|
||||||
|
Mime: asset.MimeType,
|
||||||
|
})
|
||||||
|
res.Write(jsonString)
|
||||||
|
return res
|
||||||
|
}
|
|
@ -236,7 +236,7 @@ func BlogNewThread(c *RequestContext) ResponseData {
|
||||||
[]templates.Breadcrumb{BlogBreadcrumb(c.CurrentProject.Slug)},
|
[]templates.Breadcrumb{BlogBreadcrumb(c.CurrentProject.Slug)},
|
||||||
)
|
)
|
||||||
|
|
||||||
editData := getEditorDataForNew(baseData, nil)
|
editData := getEditorDataForNew(c.CurrentUser, baseData, nil)
|
||||||
editData.SubmitUrl = hmnurl.BuildBlogNewThread(c.CurrentProject.Slug)
|
editData.SubmitUrl = hmnurl.BuildBlogNewThread(c.CurrentProject.Slug)
|
||||||
editData.SubmitLabel = "Create Post"
|
editData.SubmitLabel = "Create Post"
|
||||||
|
|
||||||
|
@ -319,7 +319,7 @@ func BlogPostEdit(c *RequestContext) ResponseData {
|
||||||
BlogThreadBreadcrumbs(c.CurrentProject.Slug, &postData.Thread),
|
BlogThreadBreadcrumbs(c.CurrentProject.Slug, &postData.Thread),
|
||||||
)
|
)
|
||||||
|
|
||||||
editData := getEditorDataForEdit(baseData, postData)
|
editData := getEditorDataForEdit(c.CurrentUser, baseData, postData)
|
||||||
editData.SubmitUrl = hmnurl.BuildBlogPostEdit(c.CurrentProject.Slug, cd.ThreadID, cd.PostID)
|
editData.SubmitUrl = hmnurl.BuildBlogPostEdit(c.CurrentProject.Slug, cd.ThreadID, cd.PostID)
|
||||||
editData.SubmitLabel = "Submit Edited Post"
|
editData.SubmitLabel = "Submit Edited Post"
|
||||||
if postData.Thread.FirstID != postData.Post.ID {
|
if postData.Thread.FirstID != postData.Post.ID {
|
||||||
|
@ -388,7 +388,7 @@ func BlogPostReply(c *RequestContext) ResponseData {
|
||||||
replyPost := templates.PostToTemplate(&postData.Post, postData.Author, c.Theme)
|
replyPost := templates.PostToTemplate(&postData.Post, postData.Author, c.Theme)
|
||||||
replyPost.AddContentVersion(postData.CurrentVersion, postData.Editor)
|
replyPost.AddContentVersion(postData.CurrentVersion, postData.Editor)
|
||||||
|
|
||||||
editData := getEditorDataForNew(baseData, &replyPost)
|
editData := getEditorDataForNew(c.CurrentUser, baseData, &replyPost)
|
||||||
editData.SubmitUrl = hmnurl.BuildBlogPostReply(c.CurrentProject.Slug, cd.ThreadID, cd.PostID)
|
editData.SubmitUrl = hmnurl.BuildBlogPostReply(c.CurrentProject.Slug, cd.ThreadID, cd.PostID)
|
||||||
editData.SubmitLabel = "Submit Reply"
|
editData.SubmitLabel = "Submit Reply"
|
||||||
|
|
||||||
|
|
|
@ -49,13 +49,18 @@ type editorData struct {
|
||||||
IsEditing bool
|
IsEditing bool
|
||||||
EditInitialContents string
|
EditInitialContents string
|
||||||
PostReplyingTo *templates.Post
|
PostReplyingTo *templates.Post
|
||||||
|
|
||||||
|
MaxFileSize int
|
||||||
|
UploadUrl string
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEditorDataForNew(baseData templates.BaseData, replyPost *templates.Post) editorData {
|
func getEditorDataForNew(currentUser *models.User, baseData templates.BaseData, replyPost *templates.Post) editorData {
|
||||||
result := editorData{
|
result := editorData{
|
||||||
BaseData: baseData,
|
BaseData: baseData,
|
||||||
CanEditTitle: replyPost == nil,
|
CanEditTitle: replyPost == nil,
|
||||||
PostReplyingTo: replyPost,
|
PostReplyingTo: replyPost,
|
||||||
|
MaxFileSize: AssetMaxSize(currentUser),
|
||||||
|
UploadUrl: hmnurl.BuildAssetUpload(baseData.Project.Subdomain),
|
||||||
}
|
}
|
||||||
|
|
||||||
if replyPost != nil {
|
if replyPost != nil {
|
||||||
|
@ -65,13 +70,15 @@ func getEditorDataForNew(baseData templates.BaseData, replyPost *templates.Post)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEditorDataForEdit(baseData templates.BaseData, p postAndRelatedModels) editorData {
|
func getEditorDataForEdit(currentUser *models.User, baseData templates.BaseData, p postAndRelatedModels) editorData {
|
||||||
return editorData{
|
return editorData{
|
||||||
BaseData: baseData,
|
BaseData: baseData,
|
||||||
Title: p.Thread.Title,
|
Title: p.Thread.Title,
|
||||||
CanEditTitle: p.Thread.FirstID == p.Post.ID,
|
CanEditTitle: p.Thread.FirstID == p.Post.ID,
|
||||||
IsEditing: true,
|
IsEditing: true,
|
||||||
EditInitialContents: p.CurrentVersion.TextRaw,
|
EditInitialContents: p.CurrentVersion.TextRaw,
|
||||||
|
MaxFileSize: AssetMaxSize(currentUser),
|
||||||
|
UploadUrl: hmnurl.BuildAssetUpload(baseData.Project.Subdomain),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,7 +596,7 @@ func ForumNewThread(c *RequestContext) ResponseData {
|
||||||
}
|
}
|
||||||
|
|
||||||
baseData := getBaseData(c, "Create New Thread", SubforumBreadcrumbs(cd.LineageBuilder, c.CurrentProject, cd.SubforumID))
|
baseData := getBaseData(c, "Create New Thread", SubforumBreadcrumbs(cd.LineageBuilder, c.CurrentProject, cd.SubforumID))
|
||||||
editData := getEditorDataForNew(baseData, nil)
|
editData := getEditorDataForNew(c.CurrentUser, baseData, nil)
|
||||||
editData.SubmitUrl = hmnurl.BuildForumNewThread(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), true)
|
editData.SubmitUrl = hmnurl.BuildForumNewThread(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), true)
|
||||||
editData.SubmitLabel = "Post New Thread"
|
editData.SubmitLabel = "Post New Thread"
|
||||||
|
|
||||||
|
@ -676,7 +683,7 @@ func ForumPostReply(c *RequestContext) ResponseData {
|
||||||
replyPost := templates.PostToTemplate(&postData.Post, postData.Author, c.Theme)
|
replyPost := templates.PostToTemplate(&postData.Post, postData.Author, c.Theme)
|
||||||
replyPost.AddContentVersion(postData.CurrentVersion, postData.Editor)
|
replyPost.AddContentVersion(postData.CurrentVersion, postData.Editor)
|
||||||
|
|
||||||
editData := getEditorDataForNew(baseData, &replyPost)
|
editData := getEditorDataForNew(c.CurrentUser, baseData, &replyPost)
|
||||||
editData.SubmitUrl = hmnurl.BuildForumPostReply(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID)
|
editData.SubmitUrl = hmnurl.BuildForumPostReply(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID)
|
||||||
editData.SubmitLabel = "Submit Reply"
|
editData.SubmitLabel = "Submit Reply"
|
||||||
|
|
||||||
|
@ -745,7 +752,7 @@ func ForumPostEdit(c *RequestContext) ResponseData {
|
||||||
}
|
}
|
||||||
baseData := getBaseData(c, title, ForumThreadBreadcrumbs(cd.LineageBuilder, c.CurrentProject, &postData.Thread))
|
baseData := getBaseData(c, title, ForumThreadBreadcrumbs(cd.LineageBuilder, c.CurrentProject, &postData.Thread))
|
||||||
|
|
||||||
editData := getEditorDataForEdit(baseData, postData)
|
editData := getEditorDataForEdit(c.CurrentUser, baseData, postData)
|
||||||
editData.SubmitUrl = hmnurl.BuildForumPostEdit(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID)
|
editData.SubmitUrl = hmnurl.BuildForumPostEdit(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID)
|
||||||
editData.SubmitLabel = "Submit Edited Post"
|
editData.SubmitLabel = "Submit Edited Post"
|
||||||
|
|
||||||
|
|
|
@ -231,6 +231,8 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe
|
||||||
), http.StatusMovedPermanently)
|
), http.StatusMovedPermanently)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mainRoutes.POST(hmnurl.RegexAssetUpload, AssetUpload)
|
||||||
|
|
||||||
mainRoutes.GET(hmnurl.RegexPodcast, PodcastIndex)
|
mainRoutes.GET(hmnurl.RegexPodcast, PodcastIndex)
|
||||||
mainRoutes.GET(hmnurl.RegexPodcastEdit, PodcastEdit)
|
mainRoutes.GET(hmnurl.RegexPodcastEdit, PodcastEdit)
|
||||||
mainRoutes.POST(hmnurl.RegexPodcastEdit, PodcastEditSubmit)
|
mainRoutes.POST(hmnurl.RegexPodcastEdit, PodcastEditSubmit)
|
||||||
|
|
Loading…
Reference in New Issue