mirror of
https://github.com/httpjamesm/AnonymousOverflow.git
synced 2024-12-06 19:16:43 +01:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4fa89879cf | |||
| cc9e549aaf | |||
| 6bfed5a9e3 | |||
| a0df61a92d | |||
| bbf098e7f4 | |||
| 2c4ded0800 | |||
| f0e42287cf | |||
| 9ebb6c90d9 | |||
| 69d96d646d | |||
| c87ce26da8 | |||
| 3dea27b09f | |||
| 4d8632e26c | |||
| eee8ec42c5 | |||
| d7bca93af8 | |||
| 2b608b4725 | |||
| bd48b309ac | |||
| 15b843d46b |
@@ -6,11 +6,11 @@ This project is super lightweight by design. The UI is simple and the frontend i
|
|||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Clearnet Instances
|
## Clearnet Instances
|
||||||
|
|
||||||
@@ -22,6 +22,9 @@ This project is super lightweight by design. The UI is simple and the frontend i
|
|||||||
| [overflow.smnz.de](https://overflow.smnz.de) | Germany | Operated by [smnz.de](https://smnz.de) |
|
| [overflow.smnz.de](https://overflow.smnz.de) | Germany | Operated by [smnz.de](https://smnz.de) |
|
||||||
| [anonymousoverflow.esmailelbob.xyz](https://anonymousoverflow.esmailelbob.xyz/) | Canada | Operated by [Esmail EL BoB](https://esmailelbob.xyz) |
|
| [anonymousoverflow.esmailelbob.xyz](https://anonymousoverflow.esmailelbob.xyz/) | Canada | Operated by [Esmail EL BoB](https://esmailelbob.xyz) |
|
||||||
| [overflow.lunar.icu](https://overflow.lunar.icu) | Germany | Operated by [lunar.icu](https://lunar.icu/) |
|
| [overflow.lunar.icu](https://overflow.lunar.icu) | Germany | Operated by [lunar.icu](https://lunar.icu/) |
|
||||||
|
| [overflow.adminforge.de](https://overflow.adminforge.de/) | Germany | Operated by [adminForge](https://adminforge.de/) |
|
||||||
|
| [ao.foss.wtf](https://ao.foss.wtf) | Germany | Operated by [foss.wtf](https://foss.wtf) |
|
||||||
|
| [overflow.hostux.net](https://overflow.hostux.net/) | France | Operated by [Hostux](https://hostux.net/) |
|
||||||
|
|
||||||
## Other Instances
|
## Other Instances
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
var Version = "1.5"
|
var Version = "1.6"
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ func main() {
|
|||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
|
|
||||||
r.LoadHTMLGlob("templates/*")
|
r.LoadHTMLGlob("templates/*")
|
||||||
r.Static("/static", "./public")
|
|
||||||
|
|
||||||
r.Use(gin.Recovery())
|
r.Use(gin.Recovery())
|
||||||
r.Use(middleware.XssPreventionHeaders())
|
r.Use(middleware.XssPreventionHeaders())
|
||||||
@@ -40,6 +39,8 @@ func main() {
|
|||||||
r.Use(middleware.OptionsMiddleware())
|
r.Use(middleware.OptionsMiddleware())
|
||||||
r.Use(middleware.Ratelimit())
|
r.Use(middleware.Ratelimit())
|
||||||
|
|
||||||
|
r.GET("/static/*filepath", routes.StaticContent)
|
||||||
|
|
||||||
r.GET("/robots.txt", func(c *gin.Context) {
|
r.GET("/robots.txt", func(c *gin.Context) {
|
||||||
c.String(200, "User-agent: *\nDisallow: /")
|
c.String(200, "User-agent: *\nDisallow: /")
|
||||||
})
|
})
|
||||||
@@ -47,10 +48,17 @@ func main() {
|
|||||||
r.GET("/options/:name", routes.ChangeOptions)
|
r.GET("/options/:name", routes.ChangeOptions)
|
||||||
|
|
||||||
r.GET("/", routes.GetHome)
|
r.GET("/", routes.GetHome)
|
||||||
|
|
||||||
r.POST("/", routes.PostHome)
|
r.POST("/", routes.PostHome)
|
||||||
|
|
||||||
|
r.GET("/questions/:id", func(c *gin.Context) {
|
||||||
|
// redirect user to the question with the title
|
||||||
|
c.Redirect(302, fmt.Sprintf("/questions/%s/placeholder", c.Param("id")))
|
||||||
|
})
|
||||||
r.GET("/questions/:id/:title", routes.ViewQuestion)
|
r.GET("/questions/:id/:title", routes.ViewQuestion)
|
||||||
|
r.GET("/questions/:id/:title/:answerId", func(c *gin.Context) {
|
||||||
|
// redirect user to the answer with the title
|
||||||
|
c.Redirect(302, fmt.Sprintf("/questions/%s/%s#%s", c.Param("id"), c.Param("title"), c.Param("answerId")))
|
||||||
|
})
|
||||||
|
|
||||||
r.GET("/proxy", routes.GetImage)
|
r.GET("/proxy", routes.GetImage)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 363 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 01-.659 1.591l-5.432 5.432a2.25 2.25 0 00-.659 1.591v2.927a2.25 2.25 0 01-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 00-.659-1.591L3.659 7.409A2.25 2.25 0 013 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0112 3z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 491 B |
+68
-8
@@ -29,6 +29,20 @@ body {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-tags {
|
||||||
|
display: flex;
|
||||||
|
gap: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-tags .tag {
|
||||||
|
background-color: var(--meta-bg);
|
||||||
|
color: var(--tag-text-color);
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
background-color: var(--code-bg);
|
background-color: var(--code-bg);
|
||||||
padding: 0.15rem;
|
padding: 0.15rem;
|
||||||
@@ -50,22 +64,25 @@ pre {
|
|||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-divider,
|
.post-divider {
|
||||||
.answer-divider {
|
|
||||||
margin-top: 3rem;
|
margin-top: 3rem;
|
||||||
margin-bottom: 3rem;
|
margin-bottom: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.answer-divider {
|
|
||||||
border: 0;
|
|
||||||
height: 1px;
|
|
||||||
background-color: var(--divider-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.answer {
|
||||||
|
padding-top: 2rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
border-bottom: 1px solid var(--muted-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.answer-meta {
|
.answer-meta {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
@@ -73,6 +90,13 @@ img {
|
|||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
background-color: var(--meta-bg);
|
background-color: var(--meta-bg);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-meta p {
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.answer-meta.accepted {
|
.answer-meta.accepted {
|
||||||
@@ -111,6 +135,42 @@ img {
|
|||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.answers-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answers-header .sorting form {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answers-header select {
|
||||||
|
border: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background-color: var(--meta-bg);
|
||||||
|
color: var(--text-color);
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answers-header button {
|
||||||
|
width: 2rem;
|
||||||
|
border: 0;
|
||||||
|
height: 2rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: var(--meta-bg);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 800px) {
|
@media only screen and (max-width: 800px) {
|
||||||
body {
|
body {
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
|
|||||||
+1
-12
@@ -19,12 +19,6 @@ func GetImage(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
url := c.Query("url")
|
|
||||||
if url == "" {
|
|
||||||
c.String(400, "Missing url")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate the auth token
|
// validate the auth token
|
||||||
token, err := jwt.ParseWithClaims(authorization, &types.ImageProxyClaims{}, func(token *jwt.Token) (interface{}, error) {
|
token, err := jwt.ParseWithClaims(authorization, &types.ImageProxyClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
|
||||||
@@ -51,11 +45,6 @@ func GetImage(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if claims.ImageURL != url {
|
|
||||||
c.String(400, "Request & token mismatch")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if claims.Exp < time.Now().Unix() {
|
if claims.Exp < time.Now().Unix() {
|
||||||
c.String(400, "Token expired")
|
c.String(400, "Token expired")
|
||||||
return
|
return
|
||||||
@@ -63,7 +52,7 @@ func GetImage(c *gin.Context) {
|
|||||||
|
|
||||||
// download the image
|
// download the image
|
||||||
client := resty.New()
|
client := resty.New()
|
||||||
resp, err := client.R().Get(url)
|
resp, err := client.R().Get(claims.ImageURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.AbortWithStatus(500)
|
c.AbortWithStatus(500)
|
||||||
return
|
return
|
||||||
|
|||||||
+26
-1
@@ -21,6 +21,13 @@ import (
|
|||||||
var codeBlockRegex = regexp.MustCompile(`(?s)<pre><code>(.+?)<\/code><\/pre>`)
|
var codeBlockRegex = regexp.MustCompile(`(?s)<pre><code>(.+?)<\/code><\/pre>`)
|
||||||
var questionCodeBlockRegex = regexp.MustCompile(`(?s)<pre class=".+"><code( class=".+")?>(.+?)</code></pre>`)
|
var questionCodeBlockRegex = regexp.MustCompile(`(?s)<pre class=".+"><code( class=".+")?>(.+?)</code></pre>`)
|
||||||
|
|
||||||
|
var soSortValues = map[string]string{
|
||||||
|
"votes": "scoredesc",
|
||||||
|
"trending": "trending",
|
||||||
|
"newest": "modifieddesc",
|
||||||
|
"oldest": "createdasc",
|
||||||
|
}
|
||||||
|
|
||||||
func ViewQuestion(c *gin.Context) {
|
func ViewQuestion(c *gin.Context) {
|
||||||
client := resty.New()
|
client := resty.New()
|
||||||
|
|
||||||
@@ -36,7 +43,17 @@ func ViewQuestion(c *gin.Context) {
|
|||||||
|
|
||||||
questionTitle := c.Param("title")
|
questionTitle := c.Param("title")
|
||||||
|
|
||||||
soLink := fmt.Sprintf("https://stackoverflow.com/questions/%s/%s", questionId, questionTitle)
|
sortValue := c.Query("sort_by")
|
||||||
|
if sortValue == "" {
|
||||||
|
sortValue = "votes"
|
||||||
|
}
|
||||||
|
|
||||||
|
soSortValue, ok := soSortValues[sortValue]
|
||||||
|
if !ok {
|
||||||
|
soSortValue = soSortValues["votes"]
|
||||||
|
}
|
||||||
|
|
||||||
|
soLink := fmt.Sprintf("https://stackoverflow.com/questions/%s/%s?answertab=%s", questionId, questionTitle, soSortValue)
|
||||||
|
|
||||||
resp, err := client.R().Get(soLink)
|
resp, err := client.R().Get(soLink)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -81,6 +98,9 @@ func ViewQuestion(c *gin.Context) {
|
|||||||
|
|
||||||
questionPostLayout := doc.Find("div.post-layout").First()
|
questionPostLayout := doc.Find("div.post-layout").First()
|
||||||
|
|
||||||
|
questionTags := utils.GetPostTags(questionPostLayout)
|
||||||
|
newFilteredQuestion.Tags = questionTags
|
||||||
|
|
||||||
questionBodyParent := doc.Find("div.s-prose")
|
questionBodyParent := doc.Find("div.s-prose")
|
||||||
|
|
||||||
questionBodyParentHTML, err := questionBodyParent.Html()
|
questionBodyParentHTML, err := questionBodyParent.Html()
|
||||||
@@ -176,6 +196,7 @@ func ViewQuestion(c *gin.Context) {
|
|||||||
answers := []types.FilteredAnswer{}
|
answers := []types.FilteredAnswer{}
|
||||||
|
|
||||||
doc.Find("div.answer").Each(func(i int, s *goquery.Selection) {
|
doc.Find("div.answer").Each(func(i int, s *goquery.Selection) {
|
||||||
|
|
||||||
newFilteredAnswer := types.FilteredAnswer{}
|
newFilteredAnswer := types.FilteredAnswer{}
|
||||||
|
|
||||||
postLayout := s.Find("div.post-layout")
|
postLayout := s.Find("div.post-layout")
|
||||||
@@ -215,6 +236,9 @@ func ViewQuestion(c *gin.Context) {
|
|||||||
answerTimestamp = html.EscapeString(s.Find("span.relativetime").Text())
|
answerTimestamp = html.EscapeString(s.Find("span.relativetime").Text())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
answerId, _ := s.Attr("data-answerid")
|
||||||
|
newFilteredAnswer.ID = answerId
|
||||||
|
|
||||||
newFilteredAnswer.AuthorName = answerAuthorName
|
newFilteredAnswer.AuthorName = answerAuthorName
|
||||||
newFilteredAnswer.AuthorURL = answerAuthorURL
|
newFilteredAnswer.AuthorURL = answerAuthorURL
|
||||||
newFilteredAnswer.Timestamp = answerTimestamp
|
newFilteredAnswer.Timestamp = answerTimestamp
|
||||||
@@ -251,6 +275,7 @@ func ViewQuestion(c *gin.Context) {
|
|||||||
"imagePolicy": imagePolicy,
|
"imagePolicy": imagePolicy,
|
||||||
"theme": c.MustGet("theme").(string),
|
"theme": c.MustGet("theme").(string),
|
||||||
"currentUrl": fmt.Sprintf("%s/questions/%s/%s", os.Getenv("APP_URL"), questionId, questionTitle),
|
"currentUrl": fmt.Sprintf("%s/questions/%s/%s", os.Getenv("APP_URL"), questionId, questionTitle),
|
||||||
|
"sortValue": sortValue,
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func StaticContent(c *gin.Context) {
|
||||||
|
cleanFilePath := strings.ReplaceAll(c.Param("filepath"), "..", "")
|
||||||
|
|
||||||
|
c.File(fmt.Sprintf("./public/%s", cleanFilePath))
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ package types
|
|||||||
import "html/template"
|
import "html/template"
|
||||||
|
|
||||||
type FilteredAnswer struct {
|
type FilteredAnswer struct {
|
||||||
|
ID string
|
||||||
|
|
||||||
Upvotes string
|
Upvotes string
|
||||||
IsAccepted bool
|
IsAccepted bool
|
||||||
|
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ type FilteredQuestion struct {
|
|||||||
AuthorURL string
|
AuthorURL string
|
||||||
ShortenedBody string
|
ShortenedBody string
|
||||||
Comments []FilteredComment
|
Comments []FilteredComment
|
||||||
|
Tags []string
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ func ReplaceImgTags(inHtml string) string {
|
|||||||
authToken, _ := generateImageProxyAuth(src)
|
authToken, _ := generateImageProxyAuth(src)
|
||||||
|
|
||||||
// replace the img tag with a proxied url
|
// replace the img tag with a proxied url
|
||||||
inHtml = strings.Replace(inHtml, imgTag, fmt.Sprintf(`<img src="%s/proxy?url=%s&auth=%s">`, os.Getenv("APP_URL"), src, authToken), 1)
|
inHtml = strings.Replace(inHtml, imgTag, fmt.Sprintf(`<img src="%s/proxy?auth=%s">`, os.Getenv("APP_URL"), authToken), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return inHtml
|
return inHtml
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPostTags(postLayout *goquery.Selection) []string {
|
||||||
|
var tags []string
|
||||||
|
postLayout.Find("a.post-tag").Each(func(i int, s *goquery.Selection) {
|
||||||
|
tags = append(tags, s.Text())
|
||||||
|
})
|
||||||
|
return tags
|
||||||
|
}
|
||||||
+37
-10
@@ -38,21 +38,50 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">{{ .question.Body }}</div>
|
<div class="card-body">{{ .question.Body }}</div>
|
||||||
{{ if .question.Comments }}
|
<div class="card-tags">
|
||||||
{{ template "comments.html" .question }}
|
{{ range .question.Tags }}
|
||||||
{{end}}
|
<div class="tag">{{ . }}</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ if .question.Comments }} {{ template "comments.html"
|
||||||
|
.question }} {{end}}
|
||||||
</div>
|
</div>
|
||||||
<hr class="post-divider" />
|
<hr class="post-divider" />
|
||||||
<h2>Answers</h2>
|
<div class="answers-header">
|
||||||
|
<h2>Answers</h2>
|
||||||
|
<div class="sorting">
|
||||||
|
<form>
|
||||||
|
<select name="sort_by">
|
||||||
|
<option disabled value="">Sort answers by...</option>
|
||||||
|
<option value="votes"{{ if eq .sortValue "votes" }} selected{{ end }}>Votes</option>
|
||||||
|
<option value="trending"{{ if eq .sortValue "trending" }} selected{{ end }}>Trending</option>
|
||||||
|
<option value="newest"{{ if eq .sortValue "newest" }} selected{{ end }}>Date modified (newest first)</option>
|
||||||
|
<option value="oldest"{{ if eq .sortValue "oldest" }} selected{{ end }}>Date created (oldest first)</option>
|
||||||
|
</select>
|
||||||
|
<button type="submit">
|
||||||
|
<img
|
||||||
|
src="/static/icons/sort.svg"
|
||||||
|
alt="Sieve icon"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{ range $answer := .answers }}
|
{{ range $answer := .answers }}
|
||||||
<div class="answer">
|
<div class="answer" id="{{ $answer.ID }}">
|
||||||
<div
|
<div
|
||||||
class="answer-meta{{ if $answer.IsAccepted }} accepted{{end}}"
|
class="answer-meta{{ if $answer.IsAccepted }} accepted{{end}}"
|
||||||
>
|
>
|
||||||
|
<p>
|
||||||
{{ if $answer.IsAccepted }} Accepted - {{ end }}
|
{{ if $answer.IsAccepted }} Accepted - {{ end }}
|
||||||
{{$answer.Upvotes}} Votes
|
{{$answer.Upvotes}} Votes
|
||||||
</div>
|
</p>
|
||||||
|
<a href="#{{ $answer.ID }}" class="answer-link">
|
||||||
|
<div class="icon">
|
||||||
|
<img src="/static/icons/link.svg" alt="Paperclip icon" />
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
{{ $answer.Body }}
|
{{ $answer.Body }}
|
||||||
<div class="answer-author-parent">
|
<div class="answer-author-parent">
|
||||||
<div class="answer-author">
|
<div class="answer-author">
|
||||||
@@ -65,11 +94,9 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ if $answer.Comments }}
|
{{ if $answer.Comments }} {{ template "comments.html" $answer }}
|
||||||
{{ template "comments.html" $answer }}
|
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<hr class="answer-divider" />
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user