Began work on the new theme, Cosora.
Added the ReplyStore and the ProfileReplyStore. Added more allowed file extensions for attachments. The tif, webp, and apng extensions are now recognised as images. Added the Delete method to the Reply struct. Added the Like method to the Reply struct. Refactored the topic list avatars to make things easier on Cosora. The attachment cap should now work properly on topics. You can now attach files to replies. The Markdown parser now ignores URLs rather than mangling them. Fixed a bug where themes weren't able to register custom resources. Added the ability to embed images. Added the ability to embed videos. Made the requirements for URLs looser. Misc improvements to the themes and templates.
|
@ -39,6 +39,9 @@ func gloinit() error {
|
|||
return err
|
||||
}
|
||||
|
||||
rstore = NewSQLReplyStore()
|
||||
prstore = NewSQLProfileReplyStore()
|
||||
|
||||
dbProd = db
|
||||
//db_test, err = sql.Open("testdb","")
|
||||
//if err != nil {
|
||||
|
|
After Width: | Height: | Size: 55 KiB |
15
main.go
|
@ -44,12 +44,16 @@ type StringList []string
|
|||
// ? - Should we allow users to upload .php or .go files? It could cause security issues. We could store them with a mangled extension to render them inert
|
||||
// TODO: Let admins manage this from the Control Panel
|
||||
var allowedFileExts = StringList{
|
||||
"png", "jpg", "jpeg", "svg", "bmp", "gif",
|
||||
"txt", "xml", "json", "yaml", "js", "py", "rb",
|
||||
"mp3", "mp4", "avi", "wmv",
|
||||
"png", "jpg", "jpeg", "svg", "bmp", "gif", "tif", "webp", "apng", // images
|
||||
|
||||
"txt", "xml", "json", "yaml", "toml", "ini", "md", "html", "rtf", "js", "py", "rb", "css", "scss", "less", "java", "ts", "cs", "c", "cc", "cpp", "cxx", "C", "c++", "h", "hh", "hpp", "hxx", "h++", "rs", "rlib", "htaccess", "gitignore", // text
|
||||
|
||||
"mp3", "mp4", "avi", "wmv", "webm", // video
|
||||
|
||||
"otf", "woff2", "woff", "ttf", "eot", // fonts
|
||||
}
|
||||
var imageFileExts = StringList{
|
||||
"png", "jpg", "jpeg", "svg", "bmp", "gif",
|
||||
"png", "jpg", "jpeg", "svg", "bmp", "gif", "tif", "webp", "apng",
|
||||
}
|
||||
|
||||
// TODO: Write a test for this
|
||||
|
@ -100,6 +104,9 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
rstore = NewSQLReplyStore()
|
||||
prstore = NewSQLProfileReplyStore()
|
||||
|
||||
initTemplates()
|
||||
|
||||
err = initPhrases()
|
||||
|
|
162
member_routes.go
|
@ -170,17 +170,18 @@ func routeTopicCreateSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
}
|
||||
|
||||
// Handle the file attachments
|
||||
// TODO: Stop duplicating this code
|
||||
if user.Perms.UploadFiles {
|
||||
var mpartFiles = r.MultipartForm.File
|
||||
if len(mpartFiles) > 5 {
|
||||
LocalError("You can't attach more than five files", w, r, user)
|
||||
return
|
||||
}
|
||||
files, ok := r.MultipartForm.File["upload_files"]
|
||||
if ok {
|
||||
if len(files) > 5 {
|
||||
LocalError("You can't attach more than five files", w, r, user)
|
||||
return
|
||||
}
|
||||
|
||||
for _, fheaders := range r.MultipartForm.File {
|
||||
for _, hdr := range fheaders {
|
||||
log.Print("hdr.Filename ", hdr.Filename)
|
||||
extarr := strings.Split(hdr.Filename, ".")
|
||||
for _, file := range files {
|
||||
log.Print("file.Filename ", file.Filename)
|
||||
extarr := strings.Split(file.Filename, ".")
|
||||
if len(extarr) < 2 {
|
||||
LocalError("Bad file", w, r, user)
|
||||
return
|
||||
|
@ -195,11 +196,11 @@ func routeTopicCreateSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
}
|
||||
ext = strings.ToLower(reg.ReplaceAllString(ext, ""))
|
||||
if !allowedFileExts.Contains(ext) {
|
||||
LocalError("You're not allowed this upload files with this extension", w, r, user)
|
||||
LocalError("You're not allowed to upload files with this extension", w, r, user)
|
||||
return
|
||||
}
|
||||
|
||||
infile, err := hdr.Open()
|
||||
infile, err := file.Open()
|
||||
if err != nil {
|
||||
LocalError("Upload failed", w, r, user)
|
||||
return
|
||||
|
@ -223,7 +224,7 @@ func routeTopicCreateSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
}
|
||||
defer outfile.Close()
|
||||
|
||||
infile, err = hdr.Open()
|
||||
infile, err = file.Open()
|
||||
if err != nil {
|
||||
LocalError("Upload failed", w, r, user)
|
||||
return
|
||||
|
@ -249,11 +250,20 @@ func routeTopicCreateSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
}
|
||||
|
||||
func routeCreateReply(w http.ResponseWriter, r *http.Request, user User) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
PreError("Bad Form", w, r)
|
||||
// TODO: Reduce this to 1MB for attachments for each file?
|
||||
if r.ContentLength > int64(config.MaxRequestSize) {
|
||||
size, unit := convertByteUnit(float64(config.MaxRequestSize))
|
||||
CustomError("Your attachments are too big. Your files need to be smaller than "+strconv.Itoa(int(size))+unit+".", http.StatusExpectationFailed, "Error", w, r, user)
|
||||
return
|
||||
}
|
||||
r.Body = http.MaxBytesReader(w, r.Body, int64(config.MaxRequestSize))
|
||||
|
||||
err := r.ParseMultipartForm(int64(megabyte))
|
||||
if err != nil {
|
||||
LocalError("Unable to parse the form", w, r, user)
|
||||
return
|
||||
}
|
||||
|
||||
tid, err := strconv.Atoi(r.PostFormValue("tid"))
|
||||
if err != nil {
|
||||
PreError("Failed to convert the Topic ID", w, r)
|
||||
|
@ -279,6 +289,83 @@ func routeCreateReply(w http.ResponseWriter, r *http.Request, user User) {
|
|||
return
|
||||
}
|
||||
|
||||
// Handle the file attachments
|
||||
// TODO: Stop duplicating this code
|
||||
if user.Perms.UploadFiles {
|
||||
files, ok := r.MultipartForm.File["upload_files"]
|
||||
if ok {
|
||||
if len(files) > 5 {
|
||||
LocalError("You can't attach more than five files", w, r, user)
|
||||
return
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
log.Print("file.Filename ", file.Filename)
|
||||
extarr := strings.Split(file.Filename, ".")
|
||||
if len(extarr) < 2 {
|
||||
LocalError("Bad file", w, r, user)
|
||||
return
|
||||
}
|
||||
ext := extarr[len(extarr)-1]
|
||||
|
||||
// TODO: Can we do this without a regex?
|
||||
reg, err := regexp.Compile("[^A-Za-z0-9]+")
|
||||
if err != nil {
|
||||
LocalError("Bad file extension", w, r, user)
|
||||
return
|
||||
}
|
||||
ext = strings.ToLower(reg.ReplaceAllString(ext, ""))
|
||||
if !allowedFileExts.Contains(ext) {
|
||||
LocalError("You're not allowed to upload files with this extension", w, r, user)
|
||||
return
|
||||
}
|
||||
|
||||
infile, err := file.Open()
|
||||
if err != nil {
|
||||
LocalError("Upload failed", w, r, user)
|
||||
return
|
||||
}
|
||||
defer infile.Close()
|
||||
|
||||
hasher := sha256.New()
|
||||
_, err = io.Copy(hasher, infile)
|
||||
if err != nil {
|
||||
LocalError("Upload failed [Hashing Failed]", w, r, user)
|
||||
return
|
||||
}
|
||||
infile.Close()
|
||||
|
||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||
filename := checksum + "." + ext
|
||||
outfile, err := os.Create("." + "/attachs/" + filename)
|
||||
if err != nil {
|
||||
LocalError("Upload failed [File Creation Failed]", w, r, user)
|
||||
return
|
||||
}
|
||||
defer outfile.Close()
|
||||
|
||||
infile, err = file.Open()
|
||||
if err != nil {
|
||||
LocalError("Upload failed", w, r, user)
|
||||
return
|
||||
}
|
||||
defer infile.Close()
|
||||
|
||||
_, err = io.Copy(outfile, infile)
|
||||
if err != nil {
|
||||
LocalError("Upload failed [Copy Failed]", w, r, user)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = addAttachmentStmt.Exec(topic.ParentID, "forums", tid, "replies", user.ID, filename)
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content := preparseMessage(html.EscapeString(r.PostFormValue("reply-content")))
|
||||
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
|
@ -286,25 +373,12 @@ func routeCreateReply(w http.ResponseWriter, r *http.Request, user User) {
|
|||
return
|
||||
}
|
||||
|
||||
wcount := wordCount(content)
|
||||
_, err = createReplyStmt.Exec(tid, content, parseMessage(content, topic.ParentID, "forums"), ipaddress, wcount, user.ID)
|
||||
_, err = rstore.Create(tid, content, ipaddress, topic.ParentID, user.ID)
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = addRepliesToTopicStmt.Exec(1, user.ID, tid)
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
return
|
||||
}
|
||||
|
||||
// Flush the topic out of the cache
|
||||
tcache, ok := topics.(TopicCache)
|
||||
if ok {
|
||||
tcache.CacheRemove(tid)
|
||||
}
|
||||
|
||||
err = fstore.UpdateLastTopic(tid, user.ID, topic.ParentID)
|
||||
if err != nil && err != ErrNoRows {
|
||||
InternalError(err, w)
|
||||
|
@ -334,6 +408,8 @@ func routeCreateReply(w http.ResponseWriter, r *http.Request, user User) {
|
|||
}
|
||||
|
||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
||||
|
||||
wcount := wordCount(content)
|
||||
err = user.increasePostStats(wcount, false)
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
|
@ -341,6 +417,7 @@ func routeCreateReply(w http.ResponseWriter, r *http.Request, user User) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Refactor this
|
||||
func routeLikeTopic(w http.ResponseWriter, r *http.Request, user User) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
|
@ -450,7 +527,7 @@ func routeReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
return
|
||||
}
|
||||
|
||||
reply, err := getReply(rid)
|
||||
reply, err := rstore.Get(rid)
|
||||
if err == ErrNoRows {
|
||||
PreError("You can't like something which doesn't exist!", w, r)
|
||||
return
|
||||
|
@ -484,15 +561,6 @@ func routeReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
return
|
||||
}
|
||||
|
||||
err = hasLikedReplyStmt.QueryRow(user.ID, rid).Scan(&rid)
|
||||
if err != nil && err != ErrNoRows {
|
||||
InternalError(err, w)
|
||||
return
|
||||
} else if err != ErrNoRows {
|
||||
LocalError("You already liked this!", w, r, user)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = users.Get(reply.CreatedBy)
|
||||
if err != nil && err != ErrNoRows {
|
||||
LocalError("The target user doesn't exist", w, r, user)
|
||||
|
@ -502,15 +570,11 @@ func routeReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
return
|
||||
}
|
||||
|
||||
score := 1
|
||||
_, err = createLikeStmt.Exec(score, rid, "replies", user.ID)
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
err = reply.Like(user.ID)
|
||||
if err == ErrAlreadyLiked {
|
||||
LocalError("You've already liked this!", w, r, user)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = addLikesToReplyStmt.Exec(1, rid)
|
||||
if err != nil {
|
||||
} else if err != nil {
|
||||
InternalError(err, w)
|
||||
return
|
||||
}
|
||||
|
@ -612,7 +676,7 @@ func routeReportSubmit(w http.ResponseWriter, r *http.Request, user User, sitemI
|
|||
var fid = 1
|
||||
var title, content string
|
||||
if itemType == "reply" {
|
||||
reply, err := getReply(itemID)
|
||||
reply, err := rstore.Get(itemID)
|
||||
if err == ErrNoRows {
|
||||
LocalError("We were unable to find the reported post", w, r, user)
|
||||
return
|
||||
|
@ -633,7 +697,7 @@ func routeReportSubmit(w http.ResponseWriter, r *http.Request, user User, sitemI
|
|||
title = "Reply: " + topic.Title
|
||||
content = reply.Content + "\n\nOriginal Post: #rid-" + strconv.Itoa(itemID)
|
||||
} else if itemType == "user-reply" {
|
||||
userReply, err := getUserReply(itemID)
|
||||
userReply, err := prstore.Get(itemID)
|
||||
if err == ErrNoRows {
|
||||
LocalError("We weren't able to find the reported post", w, r, user)
|
||||
return
|
||||
|
|
52
misc_test.go
|
@ -377,6 +377,58 @@ func TestGroupStore(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestReplyStore(t *testing.T) {
|
||||
if !gloinited {
|
||||
gloinit()
|
||||
}
|
||||
if !pluginsInited {
|
||||
initPlugins()
|
||||
}
|
||||
|
||||
reply, err := rstore.Get(-1)
|
||||
if err == nil {
|
||||
t.Error("RID #-1 shouldn't exist")
|
||||
}
|
||||
|
||||
reply, err = rstore.Get(0)
|
||||
if err == nil {
|
||||
t.Error("RID #0 shouldn't exist")
|
||||
}
|
||||
|
||||
reply, err = rstore.Get(1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if reply.ID != 1 {
|
||||
t.Error("RID #1 has the wrong ID. It should be 1 not " + strconv.Itoa(reply.ID))
|
||||
}
|
||||
if reply.ParentID != 1 {
|
||||
t.Error("The parent topic of RID #1 should be 1 not " + strconv.Itoa(reply.ParentID))
|
||||
}
|
||||
if reply.CreatedBy != 1 {
|
||||
t.Error("The creator of RID #1 should be 1 not " + strconv.Itoa(reply.CreatedBy))
|
||||
}
|
||||
}
|
||||
|
||||
func TestProfileReplyStore(t *testing.T) {
|
||||
if !gloinited {
|
||||
gloinit()
|
||||
}
|
||||
if !pluginsInited {
|
||||
initPlugins()
|
||||
}
|
||||
|
||||
_, err := prstore.Get(-1)
|
||||
if err == nil {
|
||||
t.Error("RID #-1 shouldn't exist")
|
||||
}
|
||||
|
||||
_, err = prstore.Get(0)
|
||||
if err == nil {
|
||||
t.Error("RID #0 shouldn't exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlugs(t *testing.T) {
|
||||
var res string
|
||||
var msgList []MEPair
|
||||
|
|
|
@ -387,6 +387,7 @@ func routeReplyEditSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Refactor this
|
||||
// TODO: Disable stat updates in posts handled by plugin_socialgroups
|
||||
func routeReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
||||
err := r.ParseForm()
|
||||
|
@ -402,7 +403,7 @@ func routeReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
return
|
||||
}
|
||||
|
||||
reply, err := getReply(rid)
|
||||
reply, err := rstore.Get(rid)
|
||||
if err == ErrNoRows {
|
||||
PreErrorJSQ("The reply you tried to delete doesn't exist.", w, r, isJs)
|
||||
return
|
||||
|
@ -431,11 +432,12 @@ func routeReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
return
|
||||
}
|
||||
|
||||
_, err = deleteReplyStmt.Exec(rid)
|
||||
err = reply.Delete()
|
||||
if err != nil {
|
||||
InternalErrorJSQ(err, w, r, isJs)
|
||||
return
|
||||
}
|
||||
|
||||
//log.Print("Reply #" + strconv.Itoa(rid) + " was deleted by User #" + strconv.Itoa(user.ID))
|
||||
if !isJs {
|
||||
//http.Redirect(w,r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
|
||||
|
@ -455,24 +457,15 @@ func routeReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
InternalErrorJSQ(err, w, r, isJs)
|
||||
return
|
||||
}
|
||||
_, err = removeRepliesFromTopicStmt.Exec(1, reply.ParentID)
|
||||
if err != nil {
|
||||
InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
LocalError("Bad IP", w, r, user)
|
||||
LocalErrorJSQ("Bad IP", w, r, user, isJs)
|
||||
return
|
||||
}
|
||||
err = addModLog("delete", reply.ParentID, "reply", ipaddress, user.ID)
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
return
|
||||
}
|
||||
tcache, ok := topics.(TopicCache)
|
||||
if ok {
|
||||
tcache.CacheRemove(reply.ParentID)
|
||||
InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -570,7 +563,7 @@ func routeIps(w http.ResponseWriter, r *http.Request, user User) {
|
|||
return
|
||||
}
|
||||
|
||||
ip := r.FormValue("ip")
|
||||
var ip = r.FormValue("ip")
|
||||
var uid int
|
||||
var reqUserList = make(map[int]bool)
|
||||
|
||||
|
|
112
pages.go
|
@ -611,13 +611,56 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
|||
continue
|
||||
}
|
||||
|
||||
//log.Print("Normal URL")
|
||||
outbytes = append(outbytes, msgbytes[lastItem:i]...)
|
||||
urlLen := partialURLBytesLen(msgbytes[i:])
|
||||
if msgbytes[i+urlLen] > 32 { // space and invisibles
|
||||
//log.Print("INVALID URL")
|
||||
//log.Print("msgbytes[i+urlLen]", msgbytes[i+urlLen])
|
||||
//log.Print("string(msgbytes[i+urlLen])", string(msgbytes[i+urlLen]))
|
||||
//log.Print("msgbytes[i:i+urlLen]", msgbytes[i:i+urlLen])
|
||||
//log.Print("string(msgbytes[i:i+urlLen])", string(msgbytes[i:i+urlLen]))
|
||||
outbytes = append(outbytes, invalidURL...)
|
||||
i += urlLen
|
||||
continue
|
||||
}
|
||||
|
||||
media, ok := parseMediaBytes(msgbytes[i : i+urlLen])
|
||||
if !ok {
|
||||
outbytes = append(outbytes, invalidURL...)
|
||||
i += urlLen
|
||||
continue
|
||||
}
|
||||
|
||||
if media.Type == "attach" {
|
||||
outbytes = append(outbytes, imageOpen...)
|
||||
outbytes = append(outbytes, []byte(media.URL+"?sectionID="+strconv.Itoa(sectionID)+"§ionType="+sectionType)...)
|
||||
outbytes = append(outbytes, imageOpen2...)
|
||||
outbytes = append(outbytes, []byte(media.URL+"?sectionID="+strconv.Itoa(sectionID)+"§ionType="+sectionType)...)
|
||||
outbytes = append(outbytes, imageClose...)
|
||||
i += urlLen
|
||||
lastItem = i
|
||||
continue
|
||||
} else if media.Type == "image" {
|
||||
outbytes = append(outbytes, imageOpen...)
|
||||
outbytes = append(outbytes, []byte(media.URL)...)
|
||||
outbytes = append(outbytes, imageOpen2...)
|
||||
outbytes = append(outbytes, []byte(media.URL)...)
|
||||
outbytes = append(outbytes, imageClose...)
|
||||
i += urlLen
|
||||
lastItem = i
|
||||
continue
|
||||
} else if media.Type == "raw" {
|
||||
outbytes = append(outbytes, []byte(media.Body)...)
|
||||
i += urlLen
|
||||
lastItem = i
|
||||
continue
|
||||
} else if media.Type != "" {
|
||||
outbytes = append(outbytes, unknownMedia...)
|
||||
i += urlLen
|
||||
continue
|
||||
}
|
||||
|
||||
outbytes = append(outbytes, urlOpen...)
|
||||
outbytes = append(outbytes, msgbytes[i:i+urlLen]...)
|
||||
outbytes = append(outbytes, urlOpen2...)
|
||||
|
@ -649,7 +692,7 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
|||
continue
|
||||
}
|
||||
|
||||
if media.Type == "image" {
|
||||
if media.Type == "attach" {
|
||||
outbytes = append(outbytes, imageOpen...)
|
||||
outbytes = append(outbytes, []byte(media.URL+"?sectionID="+strconv.Itoa(sectionID)+"§ionType="+sectionType)...)
|
||||
outbytes = append(outbytes, imageOpen2...)
|
||||
|
@ -658,6 +701,20 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
|||
i += urlLen
|
||||
lastItem = i
|
||||
continue
|
||||
} else if media.Type == "image" {
|
||||
outbytes = append(outbytes, imageOpen...)
|
||||
outbytes = append(outbytes, []byte(media.URL)...)
|
||||
outbytes = append(outbytes, imageOpen2...)
|
||||
outbytes = append(outbytes, []byte(media.URL)...)
|
||||
outbytes = append(outbytes, imageClose...)
|
||||
i += urlLen
|
||||
lastItem = i
|
||||
continue
|
||||
} else if media.Type == "raw" {
|
||||
outbytes = append(outbytes, []byte(media.Body)...)
|
||||
i += urlLen
|
||||
lastItem = i
|
||||
continue
|
||||
} else if media.Type != "" {
|
||||
outbytes = append(outbytes, unknownMedia...)
|
||||
i += urlLen
|
||||
|
@ -730,9 +787,9 @@ func validateURLBytes(data []byte) bool {
|
|||
i = 2
|
||||
}
|
||||
|
||||
// ? - There should only be one : and that's only if the URL is on a non-standard port
|
||||
// ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s.
|
||||
for ; datalen > i; i++ {
|
||||
if data[i] != '\\' && data[i] != '_' && data[i] != ':' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
|
||||
if data[i] != '\\' && data[i] != '_' && data[i] != ':' && data[i] != '?' && data[i] != '&' && data[i] != '=' && data[i] != ';' && data[i] != '@' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -756,9 +813,9 @@ func validatedURLBytes(data []byte) (url []byte) {
|
|||
i = 2
|
||||
}
|
||||
|
||||
// ? - There should only be one : and that's only if the URL is on a non-standard port
|
||||
// ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s.
|
||||
for ; datalen > i; i++ {
|
||||
if data[i] != '\\' && data[i] != '_' && data[i] != ':' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
|
||||
if data[i] != '\\' && data[i] != '_' && data[i] != ':' && data[i] != '?' && data[i] != '&' && data[i] != '=' && data[i] != ';' && data[i] != '@' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
|
||||
return invalidURL
|
||||
}
|
||||
}
|
||||
|
@ -785,9 +842,9 @@ func partialURLBytes(data []byte) (url []byte) {
|
|||
i = 2
|
||||
}
|
||||
|
||||
// ? - There should only be one : and that's only if the URL is on a non-standard port
|
||||
// ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s.
|
||||
for ; end >= i; i++ {
|
||||
if data[i] != '\\' && data[i] != '_' && data[i] != ':' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
|
||||
if data[i] != '\\' && data[i] != '_' && data[i] != ':' && data[i] != '?' && data[i] != '&' && data[i] != '=' && data[i] != ';' && data[i] != '@' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
|
||||
end = i
|
||||
}
|
||||
}
|
||||
|
@ -814,9 +871,9 @@ func partialURLBytesLen(data []byte) int {
|
|||
i = 2
|
||||
}
|
||||
|
||||
// ? - There should only be one : and that's only if the URL is on a non-standard port
|
||||
// ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s.
|
||||
for ; datalen > i; i++ {
|
||||
if data[i] != '\\' && data[i] != '_' && data[i] != ':' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
|
||||
if data[i] != '\\' && data[i] != '_' && data[i] != ':' && data[i] != '?' && data[i] != '&' && data[i] != '=' && data[i] != ';' && data[i] != '@' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
|
||||
//log.Print("Bad Character: ", data[i])
|
||||
return i
|
||||
}
|
||||
|
@ -828,6 +885,7 @@ func partialURLBytesLen(data []byte) int {
|
|||
type MediaEmbed struct {
|
||||
Type string //image
|
||||
URL string
|
||||
Body string
|
||||
}
|
||||
|
||||
// TODO: Write a test for this
|
||||
|
@ -846,6 +904,8 @@ func parseMediaBytes(data []byte) (media MediaEmbed, ok bool) {
|
|||
port := url.Port()
|
||||
//log.Print("hostname ", hostname)
|
||||
//log.Print("scheme ", scheme)
|
||||
query := url.Query()
|
||||
//log.Printf("query %+v\n", query)
|
||||
|
||||
var samesite = hostname == "localhost" || hostname == site.URL
|
||||
if samesite {
|
||||
|
@ -870,15 +930,47 @@ func parseMediaBytes(data []byte) (media MediaEmbed, ok bool) {
|
|||
if len(pathFrags) >= 2 {
|
||||
if samesite && pathFrags[1] == "attachs" && (scheme == "http" || scheme == "https") {
|
||||
//log.Print("Attachment")
|
||||
media.Type = "image"
|
||||
media.Type = "attach"
|
||||
var sport string
|
||||
// ? - Assumes the sysadmin hasn't mixed up the two standard ports
|
||||
if port != "443" && port != "80" {
|
||||
sport = ":" + port
|
||||
}
|
||||
media.URL = scheme + "://" + hostname + sport + path
|
||||
return media, true
|
||||
}
|
||||
}
|
||||
|
||||
// ? - I don't think this hostname will hit every YT domain
|
||||
// TODO: Make this a more customisable handler rather than hard-coding it in here
|
||||
if hostname == "www.youtube.com" && path == "/watch" {
|
||||
video, ok := query["v"]
|
||||
if ok && len(video) >= 1 && video[0] != "" {
|
||||
media.Type = "raw"
|
||||
// TODO: Filter the URL to make sure no nasties end up in there
|
||||
media.Body = "<iframe class='postIframe' src='https://www.youtube-nocookie.com/embed/" + video[0] + "' frameborder='0' allowfullscreen></iframe>"
|
||||
return media, true
|
||||
}
|
||||
}
|
||||
|
||||
lastFrag := pathFrags[len(pathFrags)-1]
|
||||
if lastFrag != "" {
|
||||
// TODO: Write a function for getting the file extension of a string
|
||||
extarr := strings.Split(lastFrag, ".")
|
||||
if len(extarr) >= 2 {
|
||||
ext := extarr[len(extarr)-1]
|
||||
if imageFileExts.Contains(ext) {
|
||||
media.Type = "image"
|
||||
var sport string
|
||||
if port != "443" && port != "80" {
|
||||
sport = ":" + port
|
||||
}
|
||||
media.URL = scheme + "://" + hostname + sport + path
|
||||
return media, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return media, true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package main
|
||||
|
||||
//import "fmt"
|
||||
import "regexp"
|
||||
import "strings"
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var markdownMaxDepth = 25 // How deep the parser will go when parsing Markdown strings
|
||||
var markdownUnclosedElement []byte
|
||||
|
@ -87,6 +89,15 @@ func _markdownParse(msg string, n int) string {
|
|||
//log.Print(" ")*/
|
||||
|
||||
switch msg[index] {
|
||||
// TODO: Do something slightly less hacky for skipping URLs
|
||||
case '/':
|
||||
if len(msg) > (index+2) && msg[index+1] == '/' {
|
||||
for ; index < len(msg) && msg[index] != ' '; index++ {
|
||||
|
||||
}
|
||||
index--
|
||||
continue
|
||||
}
|
||||
case '_':
|
||||
var startIndex = index
|
||||
if (index + 1) >= len(msg) {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// EQCSS / Tommy Hodgins, Maxime Euzière / MIT license
|
||||
// version 1.7.0
|
||||
(function(root,factory){if(typeof define==="function"&&define.amd)define([],factory);else if(typeof module==="object"&&module.exports)module.exports=factory();else root.EQCSS=factory()})(this,function(){var EQCSS={data:[]};EQCSS.load=function(){var styles=document.getElementsByTagName("style");for(var i=0;i<styles.length;i++)if(styles[i].getAttribute("data-eqcss-read")===null){styles[i].setAttribute("data-eqcss-read","true");EQCSS.process(styles[i].innerHTML)}var script=document.getElementsByTagName("script");
|
||||
for(var i=0;i<script.length;i++)if(script[i].getAttribute("data-eqcss-read")===null&&script[i].type==="text/eqcss"){if(script[i].src)(function(){var xhr=new XMLHttpRequest;xhr.open("GET",script[i].src,true);xhr.send(null);xhr.onreadystatechange=function(){EQCSS.process(xhr.responseText)}})();else EQCSS.process(script[i].innerHTML);script[i].setAttribute("data-eqcss-read","true")}var link=document.getElementsByTagName("link");for(i=0;i<link.length;i++)if(link[i].getAttribute("data-eqcss-read")===null&&
|
||||
link[i].rel==="stylesheet"){if(link[i].href)(function(){var xhr=new XMLHttpRequest;xhr.open("GET",link[i].href,true);xhr.send(null);xhr.onreadystatechange=function(){EQCSS.process(xhr.responseText)}})();link[i].setAttribute("data-eqcss-read","true")}};EQCSS.parse=function(code){var parsed_queries=new Array;code=code.replace(/\s+/g," ");code=code.replace(/\/\*[\w\W]*?\*\//g,"");code=code.replace(/@element/g,"\n@element");code=code.replace(/(@element.*?\{([^}]*?\{[^}]*?\}[^}]*?)*\}).*/g,"$1");code.replace(/(@element.*(?!@element))/g,
|
||||
function(string,query){var dataEntry={};query.replace(/(@element)\s*(".*?"|'.*?'|.*?)\s*(and\s*\(|{)/g,function(string,atrule,selector,extra){selector=selector.replace(/^\s?['](.*)[']/,"$1");selector=selector.replace(/^\s?["](.*)["]/,"$1");dataEntry.selector=selector});dataEntry.conditions=[];query.replace(/and ?\( ?([^:]*) ?: ?([^)]*) ?\)/g,function(string,measure,value){var unit=null;unit=value.replace(/^(\d*\.?\d+)(\D+)$/,"$2");if(unit===value)unit=null;value=value.replace(/^(\d*\.?\d+)\D+$/,"$1");
|
||||
dataEntry.conditions.push({measure:measure,value:value,unit:unit})});query.replace(/{(.*)}/g,function(string,style){dataEntry.style=style});parsed_queries.push(dataEntry)});return parsed_queries};EQCSS.register=function(queries){if(Object.prototype.toString.call(queries)==="[object Object]"){EQCSS.data.push(queries);EQCSS.apply()}if(Object.prototype.toString.call(queries)==="[object Array]"){for(var i=0;i<queries.length;i++)EQCSS.data.push(queries[i]);EQCSS.apply()}};EQCSS.process=function(code){var queries=
|
||||
EQCSS.parse(code);return EQCSS.register(queries)};EQCSS.apply=function(){var i,j,k;var elements;var element_guid;var css_block;var element_guid_parent;var element_guid_prev;var element_guid_next;var css_code;var element_width,parent_width;var element_height,parent_height;var element_line_height;var test;var computed_style;var parent_computed_style;for(i=0;i<EQCSS.data.length;i++){elements=document.querySelectorAll(EQCSS.data[i].selector);for(j=0;j<elements.length;j++){element_guid="data-eqcss-"+i+
|
||||
"-"+j;elements[j].setAttribute(element_guid,"");element_guid_parent="data-eqcss-"+i+"-"+j+"-parent";if(elements[j]!=document.documentElement)elements[j].parentNode.setAttribute(element_guid_parent,"");element_guid_prev="data-eqcss-"+i+"-"+j+"-prev";var prev_sibling=function(el){while(el=el.previousSibling)if(el.nodeType===1)return el}(elements[j]);if(prev_sibling)prev_sibling.setAttribute(element_guid_prev,"");element_guid_next="data-eqcss-"+i+"-"+j+"-next";var next_sibling=function(el){while(el=
|
||||
el.nextSibling)if(el.nodeType===1)return el}(elements[j]);if(next_sibling)next_sibling.setAttribute(element_guid_next,"");css_block=document.querySelector("#"+element_guid);if(!css_block){css_block=document.createElement("style");css_block.id=element_guid;css_block.setAttribute("data-eqcss-read","true");document.querySelector("head").appendChild(css_block)}css_block=document.querySelector("#"+element_guid);test=true;test_conditions:for(k=0;k<EQCSS.data[i].conditions.length;k++){computed_style=window.getComputedStyle(elements[j],
|
||||
null);parent_computed_style=null;if(elements[j]!=document.documentElement)parent_computed_style=window.getComputedStyle(elements[j].parentNode,null);var recomputed=false;if(EQCSS.data[i].conditions[k].unit==="vw"){recomputed=true;var value=parseInt(EQCSS.data[i].conditions[k].value);EQCSS.data[i].conditions[k].recomputed_value=value*window.innerWidth/100}else if(EQCSS.data[i].conditions[k].unit==="vh"){recomputed=true;var value=parseInt(EQCSS.data[i].conditions[k].value);EQCSS.data[i].conditions[k].recomputed_value=
|
||||
value*window.innerHeight/100}else if(EQCSS.data[i].conditions[k].unit==="vmin"){recomputed=true;var value=parseInt(EQCSS.data[i].conditions[k].value);EQCSS.data[i].conditions[k].recomputed_value=value*Math.min(window.innerWidth,window.innerHeight)/100}else if(EQCSS.data[i].conditions[k].unit==="vmax"){recomputed=true;var value=parseInt(EQCSS.data[i].conditions[k].value);EQCSS.data[i].conditions[k].recomputed_value=value*Math.max(window.innerWidth,window.innerHeight)/100}else if(EQCSS.data[i].conditions[k].unit!=
|
||||
null&&EQCSS.data[i].conditions[k].unit!="px"&&EQCSS.data[i].conditions[k].unit!="%"){var div=document.createElement("div");div.style.visibility="hidden";div.style.border="1px solid red";div.style.width=EQCSS.data[i].conditions[k].value+EQCSS.data[i].conditions[k].unit;var position=elements[j];if(elements[j]!=document.documentElement)position=elements[j].parentNode;position.appendChild(div);EQCSS.data[i].conditions[k].value=parseInt(window.getComputedStyle(div,null).getPropertyValue("width"));EQCSS.data[i].conditions[k].unit=
|
||||
"px";position.removeChild(div)}var final_value=recomputed?EQCSS.data[i].conditions[k].recomputed_value:parseInt(EQCSS.data[i].conditions[k].value);switch(EQCSS.data[i].conditions[k].measure){case "min-width":if(recomputed===true||EQCSS.data[i].conditions[k].unit==="px"){element_width=parseInt(computed_style.getPropertyValue("width"));if(!(element_width>=final_value)){test=false;break test_conditions}}if(EQCSS.data[i].conditions[k].unit==="%"){element_width=parseInt(computed_style.getPropertyValue("width"));
|
||||
parent_width=parseInt(parent_computed_style.getPropertyValue("width"));if(!(parent_width/element_width<=100/final_value)){test=false;break test_conditions}}break;case "max-width":if(recomputed===true||EQCSS.data[i].conditions[k].unit==="px"){element_width=parseInt(computed_style.getPropertyValue("width"));if(!(element_width<=final_value)){test=false;break test_conditions}}if(EQCSS.data[i].conditions[k].unit==="%"){element_width=parseInt(computed_style.getPropertyValue("width"));parent_width=parseInt(parent_computed_style.getPropertyValue("width"));
|
||||
if(!(parent_width/element_width>=100/final_value)){test=false;break test_conditions}}break;case "min-height":if(recomputed===true||EQCSS.data[i].conditions[k].unit==="px"){element_height=parseInt(computed_style.getPropertyValue("height"));if(!(element_height>=final_value)){test=false;break test_conditions}}if(EQCSS.data[i].conditions[k].unit==="%"){element_height=parseInt(computed_style.getPropertyValue("height"));parent_height=parseInt(parent_computed_style.getPropertyValue("height"));if(!(parent_height/
|
||||
element_height<=100/final_value)){test=false;break test_conditions}}break;case "max-height":if(recomputed===true||EQCSS.data[i].conditions[k].unit==="px"){element_height=parseInt(computed_style.getPropertyValue("height"));if(!(element_height<=final_value)){test=false;break test_conditions}}if(EQCSS.data[i].conditions[k].unit==="%"){element_height=parseInt(computed_style.getPropertyValue("height"));parent_height=parseInt(parent_computed_style.getPropertyValue("height"));if(!(parent_height/element_height>=
|
||||
100/final_value)){test=false;break test_conditions}}break;case "min-scroll-x":var element=elements[j];var element_scroll=element.scrollLeft;if(!element.hasScrollListener)if(element===document.documentElement||element===document.body)window.addEventListener("scroll",function(){EQCSS.throttle();element.hasScrollListener=true});else element.addEventListener("scroll",function(){EQCSS.throttle();element.hasScrollListener=true});if(recomputed===true||EQCSS.data[i].conditions[k].unit==="px"){if(!(element_scroll>=
|
||||
final_value)){test=false;break test_conditions}}else if(EQCSS.data[i].conditions[k].unit==="%"){var element_scroll_size=elements[j].scrollWidth;var element_size;if(elements[j]===document.documentElement||elements[j]===document.body)element_size=window.innerWidth;else element_size=parseInt(computed_style.getPropertyValue("width"));if(!(element_scroll/(element_scroll_size-element_size)*100>=final_value)){test=false;break test_conditions}}break;case "min-scroll-y":var element=elements[j];element_scroll=
|
||||
elements[j].scrollTop;if(!element.hasScrollListener)if(element===document.documentElement||element===document.body)window.addEventListener("scroll",function(){EQCSS.throttle();element.hasScrollListener=true});else element.addEventListener("scroll",function(){EQCSS.throttle();element.hasScrollListener=true});if(recomputed===true||EQCSS.data[i].conditions[k].unit==="px"){if(!(element_scroll>=final_value)){test=false;break test_conditions}}else if(EQCSS.data[i].conditions[k].unit==="%"){var element_scroll_size=
|
||||
elements[j].scrollHeight;var element_size;if(elements[j]===document.documentElement||elements[j]===document.body)element_size=window.innerHeight;else element_size=parseInt(computed_style.getPropertyValue("height"));if(!(element_scroll/(element_scroll_size-element_size)*100>=final_value)){test=false;break test_conditions}}break;case "max-scroll-x":var element=elements[j];element_scroll=elements[j].scrollLeft;if(!element.hasScrollListener)if(element===document.documentElement||element===document.body)window.addEventListener("scroll",
|
||||
function(){EQCSS.throttle();element.hasScrollListener=true});else element.addEventListener("scroll",function(){EQCSS.throttle();element.hasScrollListener=true});if(recomputed===true||EQCSS.data[i].conditions[k].unit==="px"){if(!(element_scroll<=final_value)){test=false;break test_conditions}}else if(EQCSS.data[i].conditions[k].unit==="%"){var element_scroll_size=elements[j].scrollWidth;var element_size;if(elements[j]===document.documentElement||elements[j]===document.body)element_size=window.innerWidth;
|
||||
else element_size=parseInt(computed_style.getPropertyValue("width"));if(!(element_scroll/(element_scroll_size-element_size)*100<=final_value)){test=false;break test_conditions}}break;case "max-scroll-y":var element=elements[j];element_scroll=elements[j].scrollTop;if(!element.hasScrollListener)if(element===document.documentElement||element===document.body)window.addEventListener("scroll",function(){EQCSS.throttle();element.hasScrollListener=true});else element.addEventListener("scroll",function(){EQCSS.throttle();
|
||||
element.hasScrollListener=true});if(recomputed===true||EQCSS.data[i].conditions[k].unit==="px"){if(!(element_scroll<=final_value)){test=false;break test_conditions}}else if(EQCSS.data[i].conditions[k].unit==="%"){var element_scroll_size=elements[j].scrollHeight;var element_size;if(elements[j]===document.documentElement||elements[j]===document.body)element_size=window.innerHeight;else element_size=parseInt(computed_style.getPropertyValue("height"));if(!(element_scroll/(element_scroll_size-element_size)*
|
||||
100<=final_value)){test=false;break test_conditions}}break;case "min-characters":if(elements[j].value){if(!(elements[j].value.length>=final_value)){test=false;break test_conditions}}else if(!(elements[j].textContent.length>=final_value)){test=false;break test_conditions}break;case "max-characters":if(elements[j].value){if(!(elements[j].value.length<=final_value)){test=false;break test_conditions}}else if(!(elements[j].textContent.length<=final_value)){test=false;break test_conditions}break;case "min-children":if(!(elements[j].children.length>=
|
||||
final_value)){test=false;break test_conditions}break;case "max-children":if(!(elements[j].children.length<=final_value)){test=false;break test_conditions}break;case "min-lines":element_height=parseInt(computed_style.getPropertyValue("height"))-parseInt(computed_style.getPropertyValue("border-top-width"))-parseInt(computed_style.getPropertyValue("border-bottom-width"))-parseInt(computed_style.getPropertyValue("padding-top"))-parseInt(computed_style.getPropertyValue("padding-bottom"));element_line_height=
|
||||
computed_style.getPropertyValue("line-height");if(element_line_height==="normal"){var element_font_size=parseInt(computed_style.getPropertyValue("font-size"));element_line_height=element_font_size*1.125}else element_line_height=parseInt(element_line_height);if(!(element_height/element_line_height>=final_value)){test=false;break test_conditions}break;case "max-lines":element_height=parseInt(computed_style.getPropertyValue("height"))-parseInt(computed_style.getPropertyValue("border-top-width"))-parseInt(computed_style.getPropertyValue("border-bottom-width"))-
|
||||
parseInt(computed_style.getPropertyValue("padding-top"))-parseInt(computed_style.getPropertyValue("padding-bottom"));element_line_height=computed_style.getPropertyValue("line-height");if(element_line_height==="normal"){var element_font_size=parseInt(computed_style.getPropertyValue("font-size"));element_line_height=element_font_size*1.125}else element_line_height=parseInt(element_line_height);if(!(element_height/element_line_height+1<=final_value)){test=false;break test_conditions}break;case "orientation":if(EQCSS.data[i].conditions[k].value===
|
||||
"square")if(!(elements[j].offsetWidth===elements[j].offsetHeight)){test=false;break test_conditions}if(EQCSS.data[i].conditions[k].value==="portrait")if(!(elements[j].offsetWidth<elements[j].offsetHeight)){test=false;break test_conditions}if(EQCSS.data[i].conditions[k].value==="landscape")if(!(elements[j].offsetHeight<elements[j].offsetWidth)){test=false;break test_conditions}break;case "min-aspect-ratio":var el_width=EQCSS.data[i].conditions[k].value.split("/")[0];var el_height=EQCSS.data[i].conditions[k].value.split("/")[1];
|
||||
if(!(el_width/el_height<=elements[j].offsetWidth/elements[j].offsetHeight)){test=false;break test_conditions}break;case "max-aspect-ratio":var el_width=EQCSS.data[i].conditions[k].value.split("/")[0];var el_height=EQCSS.data[i].conditions[k].value.split("/")[1];if(!(elements[j].offsetWidth/elements[j].offsetHeight<=el_width/el_height)){test=false;break test_conditions}break}}if(test===true){css_code=EQCSS.data[i].style;css_code=css_code.replace(/eval\( *((".*?")|('.*?')) *\)/g,function(string,match){return EQCSS.tryWithEval(elements[j],
|
||||
match)});css_code=css_code.replace(/(\$|eq_)this/gi,"["+element_guid+"]");css_code=css_code.replace(/(\$|eq_)parent/gi,"["+element_guid_parent+"]");css_code=css_code.replace(/(\$|eq_)prev/gi,"["+element_guid_prev+"]");css_code=css_code.replace(/(\$|eq_)next/gi,"["+element_guid_next+"]");css_code=css_code.replace(/(\$|eq_)root/gi,"html");css_code=css_code.replace(/(\d*\.?\d+)(?:\s*)(ew|eh|emin|emax)/gi,function(match,$1,$2){switch($2){case "ew":return elements[j].offsetWidth/100*$1+"px";break;case "eh":return elements[j].offsetHeight/
|
||||
100*$1+"px";break;case "emin":return Math.min(elements[j].offsetWidth,elements[j].offsetHeight)/100*$1+"px";break;case "emax":return Math.max(elements[j].offsetWidth,elements[j].offsetHeight)/100*$1+"px";break}});try{css_block.innerHTML=css_code}catch(e){css_block.styleSheet.cssText=css_code}}else try{css_block.innerHTML=""}catch(e$0){css_block.styleSheet.cssText=""}}}};EQCSS.tryWithEval=function(element,string){var $it=element;var ret="";try{with($it)ret=eval(string.slice(1,-1))}catch(e){ret=""}return ret};
|
||||
EQCSS.reset=function(){EQCSS.data=[];var style_tag=document.querySelectorAll('head style[id^="data-eqcss-"]');for(var i=0;i<style_tag.length;i++)style_tag[i].parentNode.removeChild(style_tag[i]);var tag=document.querySelectorAll("*");for(var j=0;j<tag.length;j++)for(var k=0;k<tag[j].attributes.length;k++)if(tag[j].attributes[k].name.indexOf("data-eqcss-")===0)tag[j].removeAttribute(tag[j].attributes[k].name)};EQCSS.domReady=function(fn){var done=false;var top=true;var doc=window.document;var root=
|
||||
doc.documentElement;var modern=!~navigator.userAgent.indexOf("MSIE 8");var add=modern?"addEventListener":"attachEvent";var rem=modern?"removeEventListener":"detachEvent";var pre=modern?"":"on";var init=function(e){if(e.type==="readystatechange"&&doc.readyState!=="complete")return;(e.type==="load"?window:doc)[rem](pre+e.type,init,false);if(!done&&(done=true))fn.call(window,e.type||e)},poll=function(){try{root.doScroll("left")}catch(e){setTimeout(poll,50);return}init("poll")};if(doc.readyState==="complete")fn.call(window,
|
||||
"lazy");else{if(!modern&&root.doScroll){try{top=!window.frameElement}catch(e){}if(top)poll()}doc[add](pre+"DOMContentLoaded",init,false);doc[add](pre+"readystatechange",init,false);window[add](pre+"load",init,false)}};var EQCSS_throttle_available=true;var EQCSS_throttle_queued=false;var EQCSS_mouse_down=false;var EQCSS_timeout=200;EQCSS.throttle=function(){if(EQCSS_throttle_available){EQCSS.apply();EQCSS_throttle_available=false;setTimeout(function(){EQCSS_throttle_available=true;if(EQCSS_throttle_queued){EQCSS_throttle_queued=
|
||||
false;EQCSS.apply()}},EQCSS_timeout)}else EQCSS_throttle_queued=true};EQCSS.domReady(function(){EQCSS.load();EQCSS.throttle()});window.addEventListener("resize",EQCSS.throttle);window.addEventListener("input",EQCSS.throttle);window.addEventListener("click",EQCSS.throttle);window.addEventListener("mousedown",function(e){if(e.which===1)EQCSS_mouse_down=true});window.addEventListener("mousemove",function(){if(EQCSS_mouse_down)EQCSS.throttle()});window.addEventListener("mouseup",function(){EQCSS_mouse_down=
|
||||
false;EQCSS.throttle()});function l(a){console.log(a)}return EQCSS});
|
|
@ -448,7 +448,7 @@ $(document).ready(function(){
|
|||
return hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('')
|
||||
}).then(function(hash) {
|
||||
console.log("hash",hash);
|
||||
let content = document.getElementById("topic_content")
|
||||
let content = document.getElementById("input_content")
|
||||
console.log("content.value",content.value);
|
||||
|
||||
if(content.value == "") content.value = content.value + "//" + siteURL + "/attachs/" + hash + "." + ext;
|
||||
|
@ -462,7 +462,7 @@ $(document).ready(function(){
|
|||
}
|
||||
}
|
||||
|
||||
var uploadFiles = document.getElementById("quick_topic_upload_files");
|
||||
var uploadFiles = document.getElementById("upload_files");
|
||||
if(uploadFiles != null) {
|
||||
uploadFiles.addEventListener("change", uploadFileHandler, false);
|
||||
}
|
||||
|
|
90
reply.go
|
@ -6,7 +6,11 @@
|
|||
*/
|
||||
package main
|
||||
|
||||
import "errors"
|
||||
|
||||
// ? - Should we add a reply store to centralise all the reply logic? Would this cover profile replies too or would that be seperate?
|
||||
var rstore ReplyStore
|
||||
var prstore ProfileReplyStore
|
||||
|
||||
type ReplyUser struct {
|
||||
ID int
|
||||
|
@ -50,18 +54,100 @@ type Reply struct {
|
|||
LikeCount int
|
||||
}
|
||||
|
||||
var ErrAlreadyLiked = errors.New("You already liked this!")
|
||||
|
||||
// TODO: Write tests for this
|
||||
// TODO: Wrap these queries in a transaction to make sure the state is consistent
|
||||
func (reply *Reply) Like(uid int) (err error) {
|
||||
var rid int // unused, just here to avoid mutating reply.ID
|
||||
err = hasLikedReplyStmt.QueryRow(uid, reply.ID).Scan(&rid)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return err
|
||||
} else if err != ErrNoRows {
|
||||
return ErrAlreadyLiked
|
||||
}
|
||||
|
||||
score := 1
|
||||
_, err = createLikeStmt.Exec(score, reply.ID, "replies", uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = addLikesToReplyStmt.Exec(1, reply.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Write tests for this
|
||||
func (reply *Reply) Delete() error {
|
||||
_, err := deleteReplyStmt.Exec(reply.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = removeRepliesFromTopicStmt.Exec(1, reply.ParentID)
|
||||
tcache, ok := topics.(TopicCache)
|
||||
if ok {
|
||||
tcache.CacheRemove(reply.ParentID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy gives you a non-pointer concurrency safe copy of the reply
|
||||
func (reply *Reply) Copy() Reply {
|
||||
return *reply
|
||||
}
|
||||
|
||||
func getReply(id int) (*Reply, error) {
|
||||
type ReplyStore interface {
|
||||
Get(id int) (*Reply, error)
|
||||
Create(tid int, content string, ipaddress string, fid int, uid int) (id int, err error)
|
||||
}
|
||||
|
||||
type SQLReplyStore struct {
|
||||
}
|
||||
|
||||
func NewSQLReplyStore() *SQLReplyStore {
|
||||
return &SQLReplyStore{}
|
||||
}
|
||||
|
||||
func (store *SQLReplyStore) Get(id int) (*Reply, error) {
|
||||
reply := Reply{ID: id}
|
||||
err := getReplyStmt.QueryRow(id).Scan(&reply.ParentID, &reply.Content, &reply.CreatedBy, &reply.CreatedAt, &reply.LastEdit, &reply.LastEditBy, &reply.IPAddress, &reply.LikeCount)
|
||||
return &reply, err
|
||||
}
|
||||
|
||||
func getUserReply(id int) (*Reply, error) {
|
||||
// TODO: Write a test for this
|
||||
func (store *SQLReplyStore) Create(tid int, content string, ipaddress string, fid int, uid int) (id int, err error) {
|
||||
wcount := wordCount(content)
|
||||
res, err := createReplyStmt.Exec(tid, content, parseMessage(content, fid, "forums"), ipaddress, wcount, uid)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
_, err = addRepliesToTopicStmt.Exec(1, uid, tid)
|
||||
if err != nil {
|
||||
return int(lastID), err
|
||||
}
|
||||
tcache, ok := topics.(TopicCache)
|
||||
if ok {
|
||||
tcache.CacheRemove(tid)
|
||||
}
|
||||
return int(lastID), err
|
||||
}
|
||||
|
||||
type ProfileReplyStore interface {
|
||||
Get(id int) (*Reply, error)
|
||||
}
|
||||
|
||||
type SQLProfileReplyStore struct {
|
||||
}
|
||||
|
||||
func NewSQLProfileReplyStore() *SQLProfileReplyStore {
|
||||
return &SQLProfileReplyStore{}
|
||||
}
|
||||
|
||||
func (store *SQLProfileReplyStore) Get(id int) (*Reply, error) {
|
||||
reply := Reply{ID: id}
|
||||
err := getUserReplyStmt.QueryRow(id).Scan(&reply.ParentID, &reply.Content, &reply.CreatedBy, &reply.CreatedAt, &reply.LastEdit, &reply.LastEditBy, &reply.IPAddress)
|
||||
return &reply, err
|
||||
|
|
|
@ -169,17 +169,15 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerV
|
|||
}
|
||||
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets, headerVars.ThemeName+"/panel.css")
|
||||
if len(themes[headerVars.ThemeName].Resources) != 0 {
|
||||
if len(themes[headerVars.ThemeName].Resources) > 0 {
|
||||
rlist := themes[headerVars.ThemeName].Resources
|
||||
for _, resource := range rlist {
|
||||
if resource.Location == "global" || resource.Location == "panel" {
|
||||
halves := strings.Split(resource.Name, ".")
|
||||
if len(halves) != 2 {
|
||||
continue
|
||||
}
|
||||
if halves[1] == "css" {
|
||||
extarr := strings.Split(resource.Name, ".")
|
||||
ext := extarr[len(extarr)-1]
|
||||
if ext == "css" {
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets, resource.Name)
|
||||
} else if halves[1] == "js" {
|
||||
} else if ext == "js" {
|
||||
headerVars.Scripts = append(headerVars.Scripts, resource.Name)
|
||||
}
|
||||
}
|
||||
|
@ -268,17 +266,15 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *
|
|||
headerVars.NoticeList = append(headerVars.NoticeList, "Your account has been suspended. Some of your permissions may have been revoked.")
|
||||
}
|
||||
|
||||
if len(themes[headerVars.ThemeName].Resources) != 0 {
|
||||
if len(themes[headerVars.ThemeName].Resources) > 0 {
|
||||
rlist := themes[headerVars.ThemeName].Resources
|
||||
for _, resource := range rlist {
|
||||
if resource.Location == "global" || resource.Location == "frontend" {
|
||||
halves := strings.Split(resource.Name, ".")
|
||||
if len(halves) != 2 {
|
||||
continue
|
||||
}
|
||||
if halves[1] == "css" {
|
||||
extarr := strings.Split(resource.Name, ".")
|
||||
ext := extarr[len(extarr)-1]
|
||||
if ext == "css" {
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets, resource.Name)
|
||||
} else if halves[1] == "js" {
|
||||
} else if ext == "js" {
|
||||
headerVars.Scripts = append(headerVars.Scripts, resource.Name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,12 +56,13 @@ if tmpl_forum_vars.CurrentUser.Loggedin {
|
|||
w.Write(menu_3)
|
||||
w.Write([]byte(tmpl_forum_vars.CurrentUser.Link))
|
||||
w.Write(menu_4)
|
||||
w.Write([]byte(tmpl_forum_vars.CurrentUser.Session))
|
||||
w.Write(menu_5)
|
||||
} else {
|
||||
w.Write([]byte(tmpl_forum_vars.CurrentUser.Session))
|
||||
w.Write(menu_6)
|
||||
}
|
||||
} else {
|
||||
w.Write(menu_7)
|
||||
}
|
||||
w.Write(menu_8)
|
||||
w.Write(header_14)
|
||||
if tmpl_forum_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_15)
|
||||
|
@ -137,48 +138,54 @@ w.Write([]byte(item.Creator.Avatar))
|
|||
w.Write(forum_27)
|
||||
}
|
||||
w.Write(forum_28)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(forum_29)
|
||||
w.Write([]byte(item.LastReplyAt))
|
||||
w.Write(forum_30)
|
||||
w.Write([]byte(item.Link))
|
||||
w.Write(forum_31)
|
||||
w.Write(forum_29)
|
||||
w.Write([]byte(item.Title))
|
||||
w.Write(forum_32)
|
||||
w.Write(forum_30)
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
w.Write(forum_33)
|
||||
w.Write(forum_31)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(forum_34)
|
||||
w.Write(forum_32)
|
||||
if item.IsClosed {
|
||||
w.Write(forum_35)
|
||||
w.Write(forum_33)
|
||||
}
|
||||
if item.Sticky {
|
||||
w.Write(forum_34)
|
||||
}
|
||||
w.Write(forum_35)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(forum_36)
|
||||
}
|
||||
if item.Sticky {
|
||||
w.Write(forum_37)
|
||||
if item.LastUser.Avatar != "" {
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(forum_38)
|
||||
w.Write([]byte(item.LastUser.Avatar))
|
||||
w.Write(forum_39)
|
||||
}
|
||||
}
|
||||
w.Write(forum_39)
|
||||
if item.LastUser.Avatar != "" {
|
||||
w.Write(forum_40)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write([]byte(item.LastUser.Avatar))
|
||||
w.Write(forum_41)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
}
|
||||
w.Write(forum_42)
|
||||
w.Write([]byte(item.LastReplyAt))
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(forum_43)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(forum_44)
|
||||
w.Write([]byte(item.LastReplyAt))
|
||||
w.Write(forum_45)
|
||||
}
|
||||
} else {
|
||||
w.Write(forum_44)
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_45)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_46)
|
||||
}
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_47)
|
||||
}
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_48)
|
||||
}
|
||||
w.Write(forum_49)
|
||||
}
|
||||
w.Write(forum_50)
|
||||
w.Write(footer_0)
|
||||
if len(tmpl_forum_vars.Header.Themes) != 0 {
|
||||
for _, item := range tmpl_forum_vars.Header.Themes {
|
||||
|
|
|
@ -55,12 +55,13 @@ if tmpl_forums_vars.CurrentUser.Loggedin {
|
|||
w.Write(menu_3)
|
||||
w.Write([]byte(tmpl_forums_vars.CurrentUser.Link))
|
||||
w.Write(menu_4)
|
||||
w.Write([]byte(tmpl_forums_vars.CurrentUser.Session))
|
||||
w.Write(menu_5)
|
||||
} else {
|
||||
w.Write([]byte(tmpl_forums_vars.CurrentUser.Session))
|
||||
w.Write(menu_6)
|
||||
}
|
||||
} else {
|
||||
w.Write(menu_7)
|
||||
}
|
||||
w.Write(menu_8)
|
||||
w.Write(header_14)
|
||||
if tmpl_forums_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_15)
|
||||
|
|
234
template_list.go
|
@ -56,16 +56,17 @@ var menu_2 = []byte(`</a></li>
|
|||
var menu_3 = []byte(`
|
||||
<li class="menu_left menu_account"><a href="/user/edit/critical/">Account</a></li>
|
||||
<li class="menu_left menu_profile"><a href="`)
|
||||
var menu_4 = []byte(`">Profile</a></li>
|
||||
<li class="menu_left menu_account supermod_only"><a href="/panel/">Panel</a></li>
|
||||
var menu_4 = []byte(`">Profile</a></li>`)
|
||||
var menu_5 = []byte(`
|
||||
<li class="menu_left menu_panel menu_account supermod_only"><a href="/panel/">Panel</a></li>
|
||||
<li class="menu_left menu_logout"><a href="/accounts/logout/?session=`)
|
||||
var menu_5 = []byte(`">Logout</a></li>
|
||||
var menu_6 = []byte(`">Logout</a></li>
|
||||
`)
|
||||
var menu_6 = []byte(`
|
||||
var menu_7 = []byte(`
|
||||
<li class="menu_left menu_register"><a href="/accounts/create/">Register</a></li>
|
||||
<li class="menu_left menu_login"><a href="/accounts/login/">Login</a></li>
|
||||
`)
|
||||
var menu_7 = []byte(`
|
||||
var menu_8 = []byte(`
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -233,20 +234,29 @@ var topic_93 = []byte(`</div>
|
|||
|
||||
`)
|
||||
var topic_94 = []byte(`
|
||||
<div class="rowblock topic_reply_form">
|
||||
<form action="/reply/create/" method="post">
|
||||
<input name="tid" value='`)
|
||||
<div class="rowblock topic_reply_form quick_create_form">
|
||||
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
|
||||
<input form="reply_form" name="tid" value='`)
|
||||
var topic_95 = []byte(`' type="hidden" />
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem"><textarea name="reply-content" placeholder="Insert reply here" required></textarea></div>
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem">
|
||||
<textarea id="input_content" form="reply_form" name="reply-content" placeholder="Insert reply here" required></textarea>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="reply-button" class="formbutton">Create Reply</button></div>
|
||||
</div>
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<button form="reply_form" name="reply-button" class="formbutton">Create Reply</button>
|
||||
`)
|
||||
var topic_96 = []byte(`
|
||||
<input name="upload_files" form="reply_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>`)
|
||||
var topic_97 = []byte(`
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var topic_96 = []byte(`
|
||||
var topic_98 = []byte(`
|
||||
|
||||
</main>
|
||||
|
||||
|
@ -428,20 +438,29 @@ var topic_alt_86 = []byte(`
|
|||
var topic_alt_87 = []byte(`</div>
|
||||
`)
|
||||
var topic_alt_88 = []byte(`
|
||||
<div class="rowblock topic_reply_form">
|
||||
<form action="/reply/create/" method="post">
|
||||
<input name="tid" value='`)
|
||||
<div class="rowblock topic_reply_form quick_create_form">
|
||||
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
|
||||
<input form="reply_form" name="tid" value='`)
|
||||
var topic_alt_89 = []byte(`' type="hidden" />
|
||||
<div class="formrow">
|
||||
<div class="formitem"><textarea name="reply-content" placeholder="Insert reply here" required></textarea></div>
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem">
|
||||
<textarea id="input_content" form="reply_form" name="reply-content" placeholder="Insert reply here" required></textarea>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="reply-button" class="formbutton">Create Reply</button></div>
|
||||
</div>
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<button form="reply_form" name="reply-button" class="formbutton">Create Reply</button>
|
||||
`)
|
||||
var topic_alt_90 = []byte(`
|
||||
<input name="upload_files" form="reply_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>`)
|
||||
var topic_alt_91 = []byte(`
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var topic_alt_90 = []byte(`
|
||||
var topic_alt_92 = []byte(`
|
||||
|
||||
</main>
|
||||
|
||||
|
@ -675,7 +694,7 @@ var topics_6 = []byte(`
|
|||
</div>
|
||||
`)
|
||||
var topics_7 = []byte(`
|
||||
<div class="rowblock topic_create_form" style="display: none;">
|
||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;">
|
||||
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
||||
<div class="formrow topic_board_row real_first_child">
|
||||
<div class="formitem"><select form="topic_create_form_form" id="topic_board_input" name="topic-board">
|
||||
|
@ -695,18 +714,18 @@ var topics_13 = []byte(`
|
|||
</div>
|
||||
<div class="formrow topic_content_row">
|
||||
<div class="formitem">
|
||||
<textarea form="topic_create_form_form" id="topic_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow topic_button_row">
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<button form="topic_create_form_form" class="formbutton">Create Topic</button>
|
||||
`)
|
||||
var topics_14 = []byte(`
|
||||
<input name="quick_topic_upload_files" form="topic_create_form_form" id="quick_topic_upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="quick_topic_upload_files" class="formbutton add_file_button">Add File</label>`)
|
||||
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>`)
|
||||
var topics_15 = []byte(`
|
||||
<div id="upload_file_dock"></div>
|
||||
<button class="formbutton close_form">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -715,56 +734,58 @@ var topics_15 = []byte(`
|
|||
var topics_16 = []byte(`
|
||||
<div id="topic_list" class="rowblock topic_list" aria-label="A list containing topics from every forum">
|
||||
`)
|
||||
var topics_17 = []byte(`<div class="rowitem topic_left passive datarow `)
|
||||
var topics_17 = []byte(`<div class="topic_row">
|
||||
<div class="rowitem topic_left passive datarow `)
|
||||
var topics_18 = []byte(`topic_sticky`)
|
||||
var topics_19 = []byte(`topic_closed`)
|
||||
var topics_20 = []byte(`" style="`)
|
||||
var topics_21 = []byte(`background-image: url(`)
|
||||
var topics_22 = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
||||
var topics_23 = []byte(`">
|
||||
var topics_20 = []byte(`">
|
||||
`)
|
||||
var topics_21 = []byte(`<img src="`)
|
||||
var topics_22 = []byte(`" height="64" />`)
|
||||
var topics_23 = []byte(`
|
||||
<span class="topic_inner_left">
|
||||
<a class="rowtopic" href="`)
|
||||
var topics_24 = []byte(`">`)
|
||||
var topics_25 = []byte(`</a> `)
|
||||
var topics_26 = []byte(`<a class="rowsmall parent_forum" href="`)
|
||||
var topics_27 = []byte(`">`)
|
||||
var topics_28 = []byte(`</a>`)
|
||||
var topics_29 = []byte(`
|
||||
<br /><a class="rowsmall starter" href="`)
|
||||
var topics_30 = []byte(`">`)
|
||||
var topics_31 = []byte(`</a>
|
||||
`)
|
||||
var topics_32 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var topics_33 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var topics_34 = []byte(`
|
||||
</span>
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">`)
|
||||
var topics_24 = []byte(` replies</span><br />
|
||||
<span class="lastReplyAt">`)
|
||||
var topics_25 = []byte(`</span>
|
||||
</span>
|
||||
<span>
|
||||
<a class="rowtopic" href="`)
|
||||
var topics_26 = []byte(`">`)
|
||||
var topics_27 = []byte(`</a> `)
|
||||
var topics_28 = []byte(`<a class="rowsmall" href="`)
|
||||
var topics_29 = []byte(`">`)
|
||||
var topics_30 = []byte(`</a>`)
|
||||
var topics_31 = []byte(`
|
||||
<br /><a class="rowsmall" href="`)
|
||||
var topics_32 = []byte(`">Starter: `)
|
||||
var topics_33 = []byte(`</a>
|
||||
`)
|
||||
var topics_34 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var topics_35 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var topics_36 = []byte(`
|
||||
var topics_35 = []byte(` replies</span><br />
|
||||
<span class="topicCount">x topics</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="rowitem topic_right passive datarow `)
|
||||
var topics_37 = []byte(`topic_sticky`)
|
||||
var topics_38 = []byte(`topic_closed`)
|
||||
var topics_39 = []byte(`" style="`)
|
||||
var topics_40 = []byte(`background-image: url(`)
|
||||
var topics_41 = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
||||
var topics_42 = []byte(`">
|
||||
var topics_36 = []byte(`topic_sticky`)
|
||||
var topics_37 = []byte(`topic_closed`)
|
||||
var topics_38 = []byte(`">
|
||||
`)
|
||||
var topics_39 = []byte(`<img src="`)
|
||||
var topics_40 = []byte(`" height="64" />`)
|
||||
var topics_41 = []byte(`
|
||||
<span>
|
||||
<a href="`)
|
||||
var topics_43 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var topics_44 = []byte(`</a><br>
|
||||
var topics_42 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var topics_43 = []byte(`</a><br>
|
||||
<span class="rowsmall lastReplyAt">Last: `)
|
||||
var topics_45 = []byte(`</span>
|
||||
var topics_44 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
`)
|
||||
var topics_46 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
|
||||
var topics_47 = []byte(` <a href="/topics/create/">Start one?</a>`)
|
||||
var topics_48 = []byte(`</div>`)
|
||||
var topics_49 = []byte(`
|
||||
</div>`)
|
||||
var topics_45 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
|
||||
var topics_46 = []byte(` <a href="/topics/create/">Start one?</a>`)
|
||||
var topics_47 = []byte(`</div>`)
|
||||
var topics_48 = []byte(`
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
@ -801,7 +822,7 @@ var forum_16 = []byte(`
|
|||
</div>
|
||||
`)
|
||||
var forum_17 = []byte(`
|
||||
<div class="rowblock topic_create_form" style="display: none;">
|
||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;">
|
||||
<form id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
||||
<input form="topic_create_form_form" id="topic_board_input" name="topic-board" value="`)
|
||||
var forum_18 = []byte(`" type="hidden">
|
||||
|
@ -812,18 +833,18 @@ var forum_18 = []byte(`" type="hidden">
|
|||
</div>
|
||||
<div class="formrow topic_content_row">
|
||||
<div class="formitem">
|
||||
<textarea form="topic_create_form_form" id="topic_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow topic_button_row">
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<button form="topic_create_form_form" name="topic-button" class="formbutton">Create Topic</button>
|
||||
`)
|
||||
var forum_19 = []byte(`
|
||||
<input name="quick_topic_upload_files" form="topic_create_form_form" id="quick_topic_upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="quick_topic_upload_files" class="formbutton add_file_button">Add File</label>`)
|
||||
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>`)
|
||||
var forum_20 = []byte(`
|
||||
<div id="upload_file_dock"></div>
|
||||
<button class="formbutton close_form">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -832,50 +853,55 @@ var forum_20 = []byte(`
|
|||
var forum_21 = []byte(`
|
||||
<div id="forum_topic_list" class="rowblock topic_list">
|
||||
`)
|
||||
var forum_22 = []byte(`<div class="rowitem topic_left passive datarow `)
|
||||
var forum_22 = []byte(`<div class="topic_row">
|
||||
<div class="rowitem topic_left passive datarow `)
|
||||
var forum_23 = []byte(`topic_sticky`)
|
||||
var forum_24 = []byte(`topic_closed`)
|
||||
var forum_25 = []byte(`" style="`)
|
||||
var forum_26 = []byte(`background-image: url(`)
|
||||
var forum_27 = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
||||
var forum_28 = []byte(`">
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">`)
|
||||
var forum_29 = []byte(` replies</span><br />
|
||||
<span class="lastReplyAt">`)
|
||||
var forum_30 = []byte(`</span>
|
||||
</span>
|
||||
<span>
|
||||
var forum_25 = []byte(`">
|
||||
`)
|
||||
var forum_26 = []byte(`<img src="`)
|
||||
var forum_27 = []byte(`" height="64" />`)
|
||||
var forum_28 = []byte(`
|
||||
<span class="topic_inner_left">
|
||||
<a class="rowtopic" href="`)
|
||||
var forum_29 = []byte(`">`)
|
||||
var forum_30 = []byte(`</a>
|
||||
<br /><a class="rowsmall starter" href="`)
|
||||
var forum_31 = []byte(`">`)
|
||||
var forum_32 = []byte(`</a>
|
||||
<br /><a class="rowsmall" href="`)
|
||||
var forum_33 = []byte(`">Starter: `)
|
||||
var forum_34 = []byte(`</a>
|
||||
`)
|
||||
var forum_35 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var forum_36 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var forum_37 = []byte(`
|
||||
var forum_33 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var forum_34 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var forum_35 = []byte(`
|
||||
</span>
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">`)
|
||||
var forum_36 = []byte(` replies</span><br />
|
||||
<span class="topicCount">x topics</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="rowitem topic_right passive datarow" style="`)
|
||||
var forum_38 = []byte(`background-image: url(`)
|
||||
var forum_39 = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
||||
var forum_40 = []byte(`">
|
||||
<div class="rowitem topic_right passive datarow `)
|
||||
var forum_37 = []byte(`topic_sticky`)
|
||||
var forum_38 = []byte(`topic_closed`)
|
||||
var forum_39 = []byte(`">
|
||||
`)
|
||||
var forum_40 = []byte(`<img src="`)
|
||||
var forum_41 = []byte(`" height="64" />`)
|
||||
var forum_42 = []byte(`
|
||||
<span>
|
||||
<a href="`)
|
||||
var forum_41 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var forum_42 = []byte(`</a><br>
|
||||
var forum_43 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var forum_44 = []byte(`</a><br>
|
||||
<span class="rowsmall lastReplyAt">Last: `)
|
||||
var forum_43 = []byte(`</span>
|
||||
var forum_45 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
`)
|
||||
var forum_44 = []byte(`<div class="rowitem passive">There aren't any topics in this forum yet.`)
|
||||
var forum_45 = []byte(` <a href="/topics/create/`)
|
||||
var forum_46 = []byte(`">Start one?</a>`)
|
||||
var forum_47 = []byte(`</div>`)
|
||||
var forum_48 = []byte(`
|
||||
</div>`)
|
||||
var forum_46 = []byte(`<div class="rowitem passive">There aren't any topics in this forum yet.`)
|
||||
var forum_47 = []byte(` <a href="/topics/create/`)
|
||||
var forum_48 = []byte(`">Start one?</a>`)
|
||||
var forum_49 = []byte(`</div>`)
|
||||
var forum_50 = []byte(`
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
|
|
@ -56,12 +56,13 @@ if tmpl_profile_vars.CurrentUser.Loggedin {
|
|||
w.Write(menu_3)
|
||||
w.Write([]byte(tmpl_profile_vars.CurrentUser.Link))
|
||||
w.Write(menu_4)
|
||||
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
|
||||
w.Write(menu_5)
|
||||
} else {
|
||||
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
|
||||
w.Write(menu_6)
|
||||
}
|
||||
} else {
|
||||
w.Write(menu_7)
|
||||
}
|
||||
w.Write(menu_8)
|
||||
w.Write(header_14)
|
||||
if tmpl_profile_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_15)
|
||||
|
|
|
@ -56,12 +56,13 @@ if tmpl_topic_vars.CurrentUser.Loggedin {
|
|||
w.Write(menu_3)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Link))
|
||||
w.Write(menu_4)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(menu_5)
|
||||
} else {
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(menu_6)
|
||||
}
|
||||
} else {
|
||||
w.Write(menu_7)
|
||||
}
|
||||
w.Write(menu_8)
|
||||
w.Write(header_14)
|
||||
if tmpl_topic_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_15)
|
||||
|
@ -286,8 +287,12 @@ if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
|
|||
w.Write(topic_94)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_95)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.UploadFiles {
|
||||
w.Write(topic_96)
|
||||
}
|
||||
w.Write(topic_97)
|
||||
}
|
||||
w.Write(topic_98)
|
||||
w.Write(footer_0)
|
||||
if len(tmpl_topic_vars.Header.Themes) != 0 {
|
||||
for _, item := range tmpl_topic_vars.Header.Themes {
|
||||
|
|
|
@ -56,12 +56,13 @@ if tmpl_topic_alt_vars.CurrentUser.Loggedin {
|
|||
w.Write(menu_3)
|
||||
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Link))
|
||||
w.Write(menu_4)
|
||||
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
|
||||
w.Write(menu_5)
|
||||
} else {
|
||||
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
|
||||
w.Write(menu_6)
|
||||
}
|
||||
} else {
|
||||
w.Write(menu_7)
|
||||
}
|
||||
w.Write(menu_8)
|
||||
w.Write(header_14)
|
||||
if tmpl_topic_alt_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_15)
|
||||
|
@ -274,8 +275,12 @@ if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply {
|
|||
w.Write(topic_alt_88)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
|
||||
w.Write(topic_alt_89)
|
||||
}
|
||||
if tmpl_topic_alt_vars.CurrentUser.Perms.UploadFiles {
|
||||
w.Write(topic_alt_90)
|
||||
}
|
||||
w.Write(topic_alt_91)
|
||||
}
|
||||
w.Write(topic_alt_92)
|
||||
w.Write(footer_0)
|
||||
if len(tmpl_topic_alt_vars.Header.Themes) != 0 {
|
||||
for _, item := range tmpl_topic_alt_vars.Header.Themes {
|
||||
|
|
|
@ -56,12 +56,13 @@ if tmpl_topics_vars.CurrentUser.Loggedin {
|
|||
w.Write(menu_3)
|
||||
w.Write([]byte(tmpl_topics_vars.CurrentUser.Link))
|
||||
w.Write(menu_4)
|
||||
w.Write([]byte(tmpl_topics_vars.CurrentUser.Session))
|
||||
w.Write(menu_5)
|
||||
} else {
|
||||
w.Write([]byte(tmpl_topics_vars.CurrentUser.Session))
|
||||
w.Write(menu_6)
|
||||
}
|
||||
} else {
|
||||
w.Write(menu_7)
|
||||
}
|
||||
w.Write(menu_8)
|
||||
w.Write(header_14)
|
||||
if tmpl_topics_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_15)
|
||||
|
@ -129,62 +130,60 @@ w.Write([]byte(item.Creator.Avatar))
|
|||
w.Write(topics_22)
|
||||
}
|
||||
w.Write(topics_23)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(topics_24)
|
||||
w.Write([]byte(item.LastReplyAt))
|
||||
w.Write(topics_25)
|
||||
w.Write([]byte(item.Link))
|
||||
w.Write(topics_26)
|
||||
w.Write(topics_24)
|
||||
w.Write([]byte(item.Title))
|
||||
w.Write(topics_27)
|
||||
w.Write(topics_25)
|
||||
if item.ForumName != "" {
|
||||
w.Write(topics_28)
|
||||
w.Write(topics_26)
|
||||
w.Write([]byte(item.ForumLink))
|
||||
w.Write(topics_29)
|
||||
w.Write(topics_27)
|
||||
w.Write([]byte(item.ForumName))
|
||||
w.Write(topics_30)
|
||||
w.Write(topics_28)
|
||||
}
|
||||
w.Write(topics_31)
|
||||
w.Write(topics_29)
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
w.Write(topics_32)
|
||||
w.Write(topics_30)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(topics_31)
|
||||
if item.IsClosed {
|
||||
w.Write(topics_32)
|
||||
}
|
||||
if item.Sticky {
|
||||
w.Write(topics_33)
|
||||
if item.IsClosed {
|
||||
}
|
||||
w.Write(topics_34)
|
||||
}
|
||||
if item.Sticky {
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(topics_35)
|
||||
}
|
||||
w.Write(topics_36)
|
||||
if item.Sticky {
|
||||
w.Write(topics_37)
|
||||
w.Write(topics_36)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(topics_37)
|
||||
}
|
||||
}
|
||||
w.Write(topics_38)
|
||||
}
|
||||
}
|
||||
w.Write(topics_39)
|
||||
if item.LastUser.Avatar != "" {
|
||||
w.Write(topics_40)
|
||||
w.Write(topics_39)
|
||||
w.Write([]byte(item.LastUser.Avatar))
|
||||
w.Write(topics_41)
|
||||
w.Write(topics_40)
|
||||
}
|
||||
w.Write(topics_42)
|
||||
w.Write(topics_41)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(topics_43)
|
||||
w.Write(topics_42)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(topics_44)
|
||||
w.Write(topics_43)
|
||||
w.Write([]byte(item.LastReplyAt))
|
||||
w.Write(topics_45)
|
||||
w.Write(topics_44)
|
||||
}
|
||||
} else {
|
||||
w.Write(topics_46)
|
||||
w.Write(topics_45)
|
||||
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(topics_46)
|
||||
}
|
||||
w.Write(topics_47)
|
||||
}
|
||||
w.Write(topics_48)
|
||||
}
|
||||
w.Write(topics_49)
|
||||
w.Write(footer_0)
|
||||
if len(tmpl_topics_vars.Header.Themes) != 0 {
|
||||
for _, item := range tmpl_topics_vars.Header.Themes {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
{{end}}
|
||||
</div>
|
||||
{{if .CurrentUser.Perms.CreateTopic}}
|
||||
<div class="rowblock topic_create_form" style="display: none;">
|
||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;">
|
||||
<form id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
||||
<input form="topic_create_form_form" id="topic_board_input" name="topic-board" value="{{.Forum.ID}}" type="hidden">
|
||||
<div class="formrow topic_name_row real_first_child">
|
||||
|
@ -27,42 +27,45 @@
|
|||
</div>
|
||||
<div class="formrow topic_content_row">
|
||||
<div class="formitem">
|
||||
<textarea form="topic_create_form_form" id="topic_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow topic_button_row">
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<button form="topic_create_form_form" name="topic-button" class="formbutton">Create Topic</button>
|
||||
{{if .CurrentUser.Perms.UploadFiles}}
|
||||
<input name="quick_topic_upload_files" form="topic_create_form_form" id="quick_topic_upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="quick_topic_upload_files" class="formbutton add_file_button">Add File</label>{{end}}
|
||||
<div id="upload_file_dock"></div>
|
||||
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>{{end}}
|
||||
<button class="formbutton close_form">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div id="forum_topic_list" class="rowblock topic_list">
|
||||
{{range .ItemList}}<div class="rowitem topic_left passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}" style="{{if .Creator.Avatar}}background-image: url({{.Creator.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">{{.PostCount}} replies</span><br />
|
||||
<span class="lastReplyAt">{{.LastReplyAt}}</span>
|
||||
</span>
|
||||
<span>
|
||||
{{range .ItemList}}<div class="topic_row">
|
||||
<div class="rowitem topic_left passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
||||
{{if .Creator.Avatar}}<img src="{{.Creator.Avatar}}" height="64" />{{end}}
|
||||
<span class="topic_inner_left">
|
||||
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a>
|
||||
<br /><a class="rowsmall" href="{{.Creator.Link}}">Starter: {{.Creator.Name}}</a>
|
||||
<br /><a class="rowsmall starter" href="{{.Creator.Link}}">{{.Creator.Name}}</a>
|
||||
{{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
|
||||
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
||||
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
||||
</span>
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">{{.PostCount}} replies</span><br />
|
||||
<span class="topicCount">x topics</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="rowitem topic_right passive datarow" style="{{if .LastUser.Avatar}}background-image: url({{.LastUser.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
||||
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
||||
{{if .LastUser.Avatar}}<img src="{{.LastUser.Avatar}}" height="64" />{{end}}
|
||||
<span>
|
||||
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br>
|
||||
<span class="rowsmall lastReplyAt">Last: {{.LastReplyAt}}</span>
|
||||
</span>
|
||||
</div>
|
||||
{{else}}<div class="rowitem passive">There aren't any topics in this forum yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">Start one?</a>{{end}}</div>{{end}}
|
||||
</div>{{else}}<div class="rowitem passive">There aren't any topics in this forum yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">Start one?</a>{{end}}</div>{{end}}
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
</li>
|
||||
{{if .CurrentUser.Loggedin}}
|
||||
<li class="menu_left menu_account"><a href="/user/edit/critical/">Account</a></li>
|
||||
<li class="menu_left menu_profile"><a href="{{.CurrentUser.Link}}">Profile</a></li>
|
||||
<li class="menu_left menu_account supermod_only"><a href="/panel/">Panel</a></li>
|
||||
<li class="menu_left menu_profile"><a href="{{.CurrentUser.Link}}">Profile</a></li>{{/** TODO: Remove the menu_account class once we confirm this won't break any theme **/}}
|
||||
<li class="menu_left menu_panel menu_account supermod_only"><a href="/panel/">Panel</a></li>
|
||||
<li class="menu_left menu_logout"><a href="/accounts/logout/?session={{.CurrentUser.Session}}">Logout</a></li>
|
||||
{{else}}
|
||||
<li class="menu_left menu_register"><a href="/accounts/create/">Register</a></li>
|
||||
|
|
|
@ -79,16 +79,23 @@
|
|||
{{end}}{{end}}</div>
|
||||
|
||||
{{if .CurrentUser.Perms.CreateReply}}
|
||||
<div class="rowblock topic_reply_form">
|
||||
<form action="/reply/create/" method="post">
|
||||
<input name="tid" value='{{.Topic.ID}}' type="hidden" />
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem"><textarea name="reply-content" placeholder="Insert reply here" required></textarea></div>
|
||||
<div class="rowblock topic_reply_form quick_create_form">
|
||||
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
|
||||
<input form="reply_form" name="tid" value='{{.Topic.ID}}' type="hidden" />
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem">
|
||||
<textarea id="input_content" form="reply_form" name="reply-content" placeholder="Insert reply here" required></textarea>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="reply-button" class="formbutton">Create Reply</button></div>
|
||||
</div>
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<button form="reply_form" name="reply-button" class="formbutton">Create Reply</button>
|
||||
{{if .CurrentUser.Perms.UploadFiles}}
|
||||
<input name="upload_files" form="reply_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>{{end}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
|
|
@ -77,16 +77,23 @@
|
|||
</article>
|
||||
{{end}}</div>
|
||||
{{if .CurrentUser.Perms.CreateReply}}
|
||||
<div class="rowblock topic_reply_form">
|
||||
<form action="/reply/create/" method="post">
|
||||
<input name="tid" value='{{.Topic.ID}}' type="hidden" />
|
||||
<div class="formrow">
|
||||
<div class="formitem"><textarea name="reply-content" placeholder="Insert reply here" required></textarea></div>
|
||||
<div class="rowblock topic_reply_form quick_create_form">
|
||||
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
|
||||
<input form="reply_form" name="tid" value='{{.Topic.ID}}' type="hidden" />
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem">
|
||||
<textarea id="input_content" form="reply_form" name="reply-content" placeholder="Insert reply here" required></textarea>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="reply-button" class="formbutton">Create Reply</button></div>
|
||||
</div>
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<button form="reply_form" name="reply-button" class="formbutton">Create Reply</button>
|
||||
{{if .CurrentUser.Perms.UploadFiles}}
|
||||
<input name="upload_files" form="reply_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>{{end}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</div>
|
||||
{{if ne .CurrentUser.ID 0}}
|
||||
{{if .ForumList}}
|
||||
<div class="rowblock topic_create_form" style="display: none;">
|
||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;">
|
||||
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
||||
<div class="formrow topic_board_row real_first_child">
|
||||
<div class="formitem"><select form="topic_create_form_form" id="topic_board_input" name="topic-board">
|
||||
|
@ -26,16 +26,16 @@
|
|||
</div>
|
||||
<div class="formrow topic_content_row">
|
||||
<div class="formitem">
|
||||
<textarea form="topic_create_form_form" id="topic_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow topic_button_row">
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<button form="topic_create_form_form" class="formbutton">Create Topic</button>
|
||||
{{if .CurrentUser.Perms.UploadFiles}}
|
||||
<input name="quick_topic_upload_files" form="topic_create_form_form" id="quick_topic_upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="quick_topic_upload_files" class="formbutton add_file_button">Add File</label>{{end}}
|
||||
<div id="upload_file_dock"></div>
|
||||
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>{{end}}
|
||||
<button class="formbutton close_form">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -43,26 +43,29 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
<div id="topic_list" class="rowblock topic_list" aria-label="A list containing topics from every forum">
|
||||
{{range .TopicList}}<div class="rowitem topic_left passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}" style="{{if .Creator.Avatar}}background-image: url({{.Creator.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">{{.PostCount}} replies</span><br />
|
||||
<span class="lastReplyAt">{{.LastReplyAt}}</span>
|
||||
</span>
|
||||
<span>
|
||||
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a> {{if .ForumName}}<a class="rowsmall" href="{{.ForumLink}}">{{.ForumName}}</a>{{end}}
|
||||
<br /><a class="rowsmall" href="{{.Creator.Link}}">Starter: {{.Creator.Name}}</a>
|
||||
{{range .TopicList}}<div class="topic_row">
|
||||
<div class="rowitem topic_left passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
||||
{{if .Creator.Avatar}}<img src="{{.Creator.Avatar}}" height="64" />{{end}}
|
||||
<span class="topic_inner_left">
|
||||
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a> {{if .ForumName}}<a class="rowsmall parent_forum" href="{{.ForumLink}}">{{.ForumName}}</a>{{end}}
|
||||
<br /><a class="rowsmall starter" href="{{.Creator.Link}}">{{.Creator.Name}}</a>
|
||||
{{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
|
||||
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
||||
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
||||
</span>
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">{{.PostCount}} replies</span><br />
|
||||
<span class="topicCount">x topics</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}" style="{{if .LastUser.Avatar}}background-image: url({{.LastUser.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
||||
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
||||
{{if .LastUser.Avatar}}<img src="{{.LastUser.Avatar}}" height="64" />{{end}}
|
||||
<span>
|
||||
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br>
|
||||
<span class="rowsmall lastReplyAt">Last: {{.LastReplyAt}}</span>
|
||||
</span>
|
||||
</div>
|
||||
{{else}}<div class="rowitem passive">There aren't any topics yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/">Start one?</a>{{end}}</div>{{end}}
|
||||
</div>{{else}}<div class="rowitem passive">There aren't any topics yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/">Start one?</a>{{end}}</div>{{end}}
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
|
Before Width: | Height: | Size: 39 KiB |
|
@ -1 +0,0 @@
|
|||
/* Sample CSS file. Doesn't do anything. */
|
Before Width: | Height: | Size: 136 KiB |
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"Name": "cosmo-classic",
|
||||
"FriendlyName": "Cosmo Classic",
|
||||
"Version": "Coming Soon",
|
||||
"Creator": "Azareal",
|
||||
"Disabled": true,
|
||||
"HideFromThemes": true,
|
||||
"Tag": "🏗️",
|
||||
"URL": "github.com/Azareal/Gosora",
|
||||
"Templates": [
|
||||
{
|
||||
"Name": "topic",
|
||||
"Source": "topic_alt"
|
||||
}
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 388 KiB |
Before Width: | Height: | Size: 39 KiB |
|
@ -1,71 +0,0 @@
|
|||
/* Control Panel */
|
||||
|
||||
.tag-mini {
|
||||
margin-left: 0px;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
color: black;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.panel_tag {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: #202020;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.panel_floater {
|
||||
float: right;
|
||||
}
|
||||
#panel_groups > .rowitem > .panel_floater {
|
||||
float: none;
|
||||
}
|
||||
#panel_groups > .rowitem > .panel_floater > .panel_right_button {
|
||||
float: right;
|
||||
}
|
||||
#panel_forums > .rowitem > .panel_floater {
|
||||
float: none;
|
||||
}
|
||||
#panel_forums > .rowitem > .panel_floater > .panel_buttons {
|
||||
float: right;
|
||||
}
|
||||
#panel_forums > .rowitem > span > .forum_name {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.panel_rank_tag, .forum_preset, .forum_active {
|
||||
float: none;
|
||||
color: #202020 !important;
|
||||
font-size: 11px;
|
||||
}
|
||||
.panel_rank_tag_admin:before { content: "Admins"; }
|
||||
.panel_rank_tag_mod:before { content: "Mods"; }
|
||||
.panel_rank_tag_banned:before { content: "Banned"; }
|
||||
.panel_rank_tag_guest:before { content: "Guests"; }
|
||||
.panel_rank_tag_member:before { content: "Members"; }
|
||||
|
||||
.forum_preset_announce:after { content: "Announcements"; }
|
||||
.forum_preset_members:after { content: "Member Only"; }
|
||||
.forum_preset_staff:after { content: "Staff Only"; }
|
||||
.forum_preset_admins:after { content: "Admin Only"; }
|
||||
.forum_preset_archive:after { content: "Archive"; }
|
||||
.forum_preset_all:after { content: "Public"; }
|
||||
.forum_preset_custom, .forum_preset_ { display: none !important; }
|
||||
.forum_active_Hide:before { content: "Hidden"; }
|
||||
.forum_active_Hide + .forum_preset:before { content: " | "; }
|
||||
.forum_active_Show { display: none !important; }
|
||||
.forum_active_name { color: #707070; }
|
||||
.builtin_forum_divider { border-bottom-style: solid; }
|
||||
|
||||
.perm_preset_no_access:before { content: "No Access"; color: maroon; }
|
||||
.perm_preset_read_only:before { content: "Read Only"; color: green; }
|
||||
.perm_preset_can_post:before { content: "Can Post"; color: green; }
|
||||
.perm_preset_can_moderate:before { content: "Can Moderate"; color: darkblue; }
|
||||
.perm_preset_custom:before { content: "Custom"; color: black; }
|
||||
.perm_preset_default:before { content: "Default"; }
|
Before Width: | Height: | Size: 136 KiB |
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"Name": "cosmo-conflux",
|
||||
"FriendlyName": "Cosmo Conflux",
|
||||
"Version": "Coming Soon",
|
||||
"Creator": "Azareal",
|
||||
"FullImage": "cosmo-conflux.png",
|
||||
"ForkOf": "cosmo",
|
||||
"MobileFriendly": true,
|
||||
"HideFromThemes": true,
|
||||
"Tag": "🏗️",
|
||||
"URL": "github.com/Azareal/Gosora",
|
||||
"Templates": [
|
||||
{
|
||||
"Name": "topic",
|
||||
"Source": "topic_alt"
|
||||
}
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 408 KiB |
Before Width: | Height: | Size: 39 KiB |
|
@ -1,71 +0,0 @@
|
|||
/* Control Panel */
|
||||
|
||||
.tag-mini {
|
||||
margin-left: 0px;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
color: black;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.panel_tag {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: #202020;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.panel_floater {
|
||||
float: right;
|
||||
}
|
||||
#panel_groups > .rowitem > .panel_floater {
|
||||
float: none;
|
||||
}
|
||||
#panel_groups > .rowitem > .panel_floater > .panel_right_button {
|
||||
float: right;
|
||||
}
|
||||
#panel_forums > .rowitem > .panel_floater {
|
||||
float: none;
|
||||
}
|
||||
#panel_forums > .rowitem > .panel_floater > .panel_buttons {
|
||||
float: right;
|
||||
}
|
||||
#panel_forums > .rowitem > span > .forum_name {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.panel_rank_tag, .forum_preset, .forum_active {
|
||||
float: none;
|
||||
color: #202020 !important;
|
||||
font-size: 11px;
|
||||
}
|
||||
.panel_rank_tag_admin:before { content: "Admins"; }
|
||||
.panel_rank_tag_mod:before { content: "Mods"; }
|
||||
.panel_rank_tag_banned:before { content: "Banned"; }
|
||||
.panel_rank_tag_guest:before { content: "Guests"; }
|
||||
.panel_rank_tag_member:before { content: "Members"; }
|
||||
|
||||
.forum_preset_announce:after { content: "Announcements"; }
|
||||
.forum_preset_members:after { content: "Member Only"; }
|
||||
.forum_preset_staff:after { content: "Staff Only"; }
|
||||
.forum_preset_admins:after { content: "Admin Only"; }
|
||||
.forum_preset_archive:after { content: "Archive"; }
|
||||
.forum_preset_all:after { content: "Public"; }
|
||||
.forum_preset_custom, .forum_preset_ { display: none !important; }
|
||||
.forum_active_Hide:before { content: "Hidden"; }
|
||||
.forum_active_Hide + .forum_preset:before { content: " | "; }
|
||||
.forum_active_Show { display: none !important; }
|
||||
.forum_active_name { color: #707070; }
|
||||
.builtin_forum_divider { border-bottom-style: solid; }
|
||||
|
||||
.perm_preset_no_access:before { content: "No Access"; color: maroon; }
|
||||
.perm_preset_read_only:before { content: "Read Only"; color: green; }
|
||||
.perm_preset_can_post:before { content: "Can Post"; color: green; }
|
||||
.perm_preset_can_moderate:before { content: "Can Moderate"; color: darkblue; }
|
||||
.perm_preset_custom:before { content: "Custom"; color: black; }
|
||||
.perm_preset_default:before { content: "Default"; }
|
Before Width: | Height: | Size: 136 KiB |
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"Name": "cosmo",
|
||||
"FriendlyName": "AtomBB Cosmo",
|
||||
"Version": "Coming Soon",
|
||||
"Creator": "Azareal",
|
||||
"FullImage": "cosmo.png",
|
||||
"MobileFriendly": true,
|
||||
"HideFromThemes": true,
|
||||
"Tag": "🏗️",
|
||||
"URL": "github.com/Azareal/Gosora",
|
||||
"Templates": [
|
||||
{
|
||||
"Name": "topic",
|
||||
"Source": "topic_alt"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
:root {
|
||||
--header-border-color: hsl(0,0%,85%);
|
||||
--element-border-color: hsl(0,0%,90%);
|
||||
--element-background-color: white;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 16px;
|
||||
font-family: arial;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: hsl(0,0%,40%);
|
||||
}
|
||||
|
||||
#back {
|
||||
padding: 8px;
|
||||
padding-top: 14px;
|
||||
display: flex;
|
||||
/*background-color: hsl(0,0%,97%);*/
|
||||
}
|
||||
|
||||
#main {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 200px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav {
|
||||
border-bottom: 1px solid var(--header-border-color);
|
||||
}
|
||||
|
||||
li {
|
||||
margin-right: 12px;
|
||||
}
|
||||
.menu_left a:after {
|
||||
content: "|";
|
||||
margin-left: 12px;
|
||||
color: var(--header-border-color);
|
||||
}
|
||||
|
||||
.menu_overview {
|
||||
font-size: 22px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.menu_overview a:after {
|
||||
margin-left: 14px;
|
||||
margin-right: 4px;
|
||||
position: relative;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.menu_forums a:before {
|
||||
content: "\f03a";
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.menu_topics a:before {
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
margin-right: 4px;
|
||||
content: "\f27b";
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
.menu_alerts {
|
||||
display: none;
|
||||
}
|
||||
.menu_account a:before {
|
||||
content: "\f2c3";
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.menu_profile a:before {
|
||||
content: "\f2c0";
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
margin-right: 5px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.menu_panel a:before {
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
margin-right: 6px;
|
||||
content: "\f108";
|
||||
}
|
||||
.menu_logout a:before {
|
||||
content: "\f08b";
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
margin-left: 14px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.alertList {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rowblock {
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid var(--header-border-color);
|
||||
border-bottom: 2px solid var(--header-border-color);
|
||||
background-color: var(--element-background-color);
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.rowhead {
|
||||
padding: 13px;
|
||||
padding-top: 14px;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
.rowhead h1 {
|
||||
font-size: 20px;
|
||||
font-weight: normal;
|
||||
color: hsl(0,0%,10%);
|
||||
-webkit-margin-before: 0;
|
||||
-webkit-margin-after: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.topic_list {
|
||||
border: none;
|
||||
}
|
||||
.topic_list .topic_row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.topic_left, .topic_right {
|
||||
margin-bottom: 8px;
|
||||
padding: 4px;
|
||||
display: flex;
|
||||
border: 1px solid var(--element-border-color);
|
||||
border-bottom: 2px solid var(--element-border-color);
|
||||
}
|
||||
|
||||
.topic_list .rowtopic {
|
||||
font-size: 18px;
|
||||
color: hsl(0,0%,30%);
|
||||
margin-right: 2px;
|
||||
max-width: 230px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.topic_list .rowsmall.starter:before {
|
||||
content: "\f007";
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
margin-right: 5px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.topic_list .rowsmall.starter:before {
|
||||
content: "\f007";
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
margin-right: 5px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.topic_list .topic_status_e {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.topic_left {
|
||||
flex: 1 1 calc(100% - 400px);
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.topic_inner_right {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
font-size: 17px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.topic_right {
|
||||
flex: 1 1 150px;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.topic_left img {
|
||||
border-radius: 30px;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
margin-top: 8px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.topic_right img {
|
||||
border-radius: 30px;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.topic_left .topic_inner_left {
|
||||
margin-top: 12px;
|
||||
margin-left: 8px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.topic_right > span {
|
||||
margin-top: 12px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.topic_sticky {
|
||||
border-bottom: 2px solid hsl(51, 60%, 50%);
|
||||
}
|
||||
|
||||
/*@element .topic_left .rowtopic and (min-characters: 10) {
|
||||
}*/
|
||||
|
||||
@media(max-width: 670px) {
|
||||
.topic_inner_right {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,16 @@
|
|||
"Version": "0.0.1",
|
||||
"Creator": "Azareal",
|
||||
"URL": "github.com/Azareal/Gosora",
|
||||
"HideFromThemes":true,
|
||||
"Tag": "WIP"
|
||||
"Tag": "WIP",
|
||||
"Sidebars":"right",
|
||||
"Resources": [
|
||||
{
|
||||
"Name":"font-awesome-4.7.0/css/font-awesome.css",
|
||||
"Location":"global"
|
||||
},
|
||||
{
|
||||
"Name":"EQCSS.min.js",
|
||||
"Location":"global"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -483,19 +483,16 @@ input, select, textarea {
|
|||
width: calc(100% - 14px);
|
||||
}
|
||||
|
||||
.topic_button_row .formitem {
|
||||
.quick_button_row .formitem, .quick_create_form .upload_file_dock {
|
||||
display: flex;
|
||||
}
|
||||
.topic_create_form .add_file_button {
|
||||
.quick_create_form .add_file_button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.topic_create_form .close_form {
|
||||
.quick_create_form .close_form {
|
||||
margin-left: auto;
|
||||
}
|
||||
.topic_create_form .upload_file_dock {
|
||||
display: flex;
|
||||
}
|
||||
.topic_create_form .uploadItem {
|
||||
.quick_create_form .uploadItem {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
background-size: 25px 30px;
|
||||
|
@ -564,6 +561,9 @@ input, select, textarea {
|
|||
content: "Locked";
|
||||
}
|
||||
|
||||
.topic_list .topic_row {
|
||||
display: flex;
|
||||
}
|
||||
/* Temporary hack, so that I don't break the topic lists of the other themes */
|
||||
.topic_list .topic_inner_right {
|
||||
display: none;
|
||||
|
@ -573,19 +573,35 @@ input, select, textarea {
|
|||
overflow: hidden;
|
||||
}
|
||||
.topic_list .topic_left {
|
||||
width: calc(100% - 284px);
|
||||
width: 100%;
|
||||
height: 59px;
|
||||
display: flex;
|
||||
padding: 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.topic_list .topic_right {
|
||||
height: 35px;
|
||||
height: 59px;
|
||||
margin-left: 8px;
|
||||
width: 108px;
|
||||
display: flex;
|
||||
width: 284px;
|
||||
padding: 0px;
|
||||
}
|
||||
.topic_list .rowitem:last-child {
|
||||
.topic_list .topic_left img, .topic_list .topic_right img {
|
||||
width: 64px;
|
||||
}
|
||||
.topic_list .topic_inner_left, .topic_right > span {
|
||||
margin-left: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.topic_list .topic_row:last-child {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.topic_list .lastReplyAt {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.topic_list .starter:before {
|
||||
content: "Starter: ";
|
||||
}
|
||||
|
||||
.topic_name_input {
|
||||
width: 100%;
|
||||
|
@ -606,6 +622,10 @@ input, select, textarea {
|
|||
top: -5px;
|
||||
}
|
||||
|
||||
.prev_link, .next_link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.postImage {
|
||||
max-width: 100%;
|
||||
max-height: 200px;/*300px;*/
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
$(document).ready(function(){
|
||||
// TODO: Run this when the image is loaded rather than when the document is ready?
|
||||
$(".topic_list img").each(function(){
|
||||
let aspectRatio = this.naturalHeight / this.naturalWidth;
|
||||
console.log("aspectRatio ",aspectRatio);
|
||||
console.log("this.height ",this.naturalHeight);
|
||||
console.log("this.width ",this.naturalWidth);
|
||||
|
||||
$(this).css({
|
||||
height: aspectRatio * this.width
|
||||
});
|
||||
});
|
||||
});
|
|
@ -4,5 +4,11 @@
|
|||
"Version": "0.0.1",
|
||||
"Creator": "Azareal",
|
||||
"FullImage": "shadow.png",
|
||||
"URL": "github.com/Azareal/Gosora"
|
||||
"URL": "github.com/Azareal/Gosora",
|
||||
"Resources": [
|
||||
{
|
||||
"Name":"shadow/misc.js",
|
||||
"Location":"global"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -395,7 +395,7 @@ li a {
|
|||
|
||||
/* Topics */
|
||||
|
||||
.topic_list {
|
||||
.topic_list .topic_row {
|
||||
display: grid;
|
||||
grid-template-columns: calc(100% - 204px) 204px;
|
||||
}
|
||||
|
@ -405,6 +405,9 @@ li a {
|
|||
.topic_list .lastReplyAt {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.topic_list .starter:before {
|
||||
content: "Starter: ";
|
||||
}
|
||||
|
||||
@supports not (display: grid) {
|
||||
.topic_list .rowitem {
|
||||
|
@ -419,6 +422,20 @@ li a {
|
|||
}
|
||||
}
|
||||
|
||||
.topic_left, .topic_right {
|
||||
display: flex;
|
||||
padding: 0px;
|
||||
height: 58px;
|
||||
}
|
||||
.topic_left img, .topic_right img {
|
||||
width: 64px;
|
||||
height: auto;
|
||||
}
|
||||
.topic_left .topic_inner_left, .topic_right > span {
|
||||
margin-top: 10px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.postImage {
|
||||
max-width: 100%;
|
||||
max-height: 200px;
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
$(document).ready(function(){
|
||||
// TODO: Run this when the image is loaded rather than when the document is ready?
|
||||
$(".topic_list img").each(function(){
|
||||
let aspectRatio = this.naturalHeight / this.naturalWidth;
|
||||
console.log("aspectRatio ",aspectRatio);
|
||||
console.log("this.height ",this.naturalHeight);
|
||||
console.log("this.width ",this.naturalWidth);
|
||||
|
||||
$(this).css({
|
||||
height: aspectRatio * this.width
|
||||
});
|
||||
});
|
||||
});
|
|
@ -12,5 +12,11 @@
|
|||
"Name": "topic",
|
||||
"Source": "topic_alt"
|
||||
}
|
||||
],
|
||||
"Resources": [
|
||||
{
|
||||
"Name":"tempra-conflux/misc.js",
|
||||
"Location":"global"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -228,10 +228,22 @@ li a {
|
|||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.stat_green { background-color: lightgreen; border-color: lightgreen; }
|
||||
.stat_orange { background-color: #ffe4b3; border-color: #ffe4b3; }
|
||||
.stat_red { background-color: #ffb2b2; border-color: #ffb2b2; }
|
||||
.stat_disabled { background-color: lightgray; border-color: lightgray; }
|
||||
.stat_green {
|
||||
background-color: lightgreen;
|
||||
border-color: lightgreen;
|
||||
}
|
||||
.stat_orange {
|
||||
background-color: #ffe4b3;
|
||||
border-color: #ffe4b3;
|
||||
}
|
||||
.stat_red {
|
||||
background-color: #ffb2b2;
|
||||
border-color: #ffb2b2;
|
||||
}
|
||||
.stat_disabled {
|
||||
background-color: lightgray;
|
||||
border-color: lightgray;
|
||||
}
|
||||
|
||||
.rowitem {
|
||||
width: 100%;
|
||||
|
@ -314,6 +326,10 @@ button {
|
|||
|
||||
/* Topics */
|
||||
|
||||
.topic_list .starter:before {
|
||||
content: "Starter: ";
|
||||
}
|
||||
|
||||
.topic_sticky {
|
||||
background-color: rgb(255,255,234);
|
||||
}
|
||||
|
|
|
@ -231,11 +231,25 @@ li a {
|
|||
font-size: 16px;
|
||||
}
|
||||
|
||||
.grid_istat { margin-bottom: 5px; }
|
||||
.stat_green { background-color: lightgreen; border-color: lightgreen; }
|
||||
.stat_orange { background-color: #ffe4b3; border-color: #ffe4b3; }
|
||||
.stat_red { background-color: #ffb2b2; border-color: #ffb2b2; }
|
||||
.stat_disabled { background-color: lightgray; border-color: lightgray; }
|
||||
.grid_istat {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.stat_green {
|
||||
background-color: lightgreen;
|
||||
border-color: lightgreen;
|
||||
}
|
||||
.stat_orange {
|
||||
background-color: #ffe4b3;
|
||||
border-color: #ffe4b3;
|
||||
}
|
||||
.stat_red {
|
||||
background-color: #ffb2b2;
|
||||
border-color: #ffb2b2;
|
||||
}
|
||||
.stat_disabled {
|
||||
background-color: lightgray;
|
||||
border-color: lightgray;
|
||||
}
|
||||
|
||||
.rowhead .rowitem, .colstack_head .rowitem {
|
||||
background-color: rgb(252,252,252);
|
||||
|
@ -259,15 +273,24 @@ li a {
|
|||
padding-right: 10px;
|
||||
background-color: white;
|
||||
}
|
||||
/*.rowitem:not(.passive) { font-size: 17px; }*/
|
||||
.rowitem:not(:last-child) { border-bottom: 1px dotted hsl(0, 0%, 80%); }
|
||||
.rowitem:not(:last-child) {
|
||||
border-bottom: 1px solid hsl(0,0%,85%);
|
||||
}
|
||||
|
||||
.rowitem a {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.rowitem a:hover { color: silver; }
|
||||
.top_post { margin-bottom: 12px; }
|
||||
.opthead { display: none; }
|
||||
.rowitem a:hover {
|
||||
color: silver;
|
||||
}
|
||||
|
||||
.top_post {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.opthead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rowitem.has_opt {
|
||||
float: left;
|
||||
|
@ -372,16 +395,22 @@ li a {
|
|||
|
||||
/* Topics */
|
||||
|
||||
.topic_list {
|
||||
.topic_list .topic_row {
|
||||
display: grid;
|
||||
grid-template-columns: calc(100% - 204px) 204px;
|
||||
}
|
||||
.topic_list .rowitem {
|
||||
border-bottom: 1px solid hsl(0,0%,85%);
|
||||
}
|
||||
.topic_list .topic_inner_right {
|
||||
display: none;
|
||||
}
|
||||
.topic_list .lastReplyAt {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.topic_list .starter:before {
|
||||
content: "Starter: ";
|
||||
}
|
||||
|
||||
@supports not (display: grid) {
|
||||
.topic_list .rowitem {
|
||||
|
@ -396,6 +425,21 @@ li a {
|
|||
}
|
||||
}
|
||||
|
||||
.topic_left, .topic_right {
|
||||
display: flex;
|
||||
padding: 0px;
|
||||
height: 58px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.topic_left img, .topic_right img {
|
||||
width: 64px;
|
||||
height: auto;
|
||||
}
|
||||
.topic_left .topic_inner_left, .topic_right > span {
|
||||
margin-top: 10px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.postImage {
|
||||
max-width: 100%;
|
||||
max-height: 200px;
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
$(document).ready(function(){
|
||||
// TODO: Run this when the image is loaded rather than when the document is ready?
|
||||
$(".topic_list img").each(function(){
|
||||
let aspectRatio = this.naturalHeight / this.naturalWidth;
|
||||
console.log("aspectRatio ",aspectRatio);
|
||||
console.log("this.height ",this.naturalHeight);
|
||||
console.log("this.width ",this.naturalWidth);
|
||||
|
||||
$(this).css({
|
||||
height: aspectRatio * this.width
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,8 +9,8 @@
|
|||
"Sidebars":"right",
|
||||
"Resources": [
|
||||
{
|
||||
"Name": "sample.css",
|
||||
"Location": "none"
|
||||
"Name":"tempra-simple/misc.js",
|
||||
"Location":"global"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|