mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-18 07:36:24 -07:00
Add webm support and update jQuery
This commit is contained in:
parent
5a0d4e1ff8
commit
4fcb43330f
15 changed files with 395 additions and 219 deletions
|
@ -42,15 +42,89 @@ function preparePostPreviews(is_inline) {
|
|||
if($(this).next().attr("class") != "inlinepostprev") {
|
||||
$(".postprev").remove();
|
||||
$(this).after($("div#"+this.innerHTML.replace(">>","")).clone().attr({"class":"inlinepostprev","id":"i"+$(this).parent().attr("id")+"-"+($(this).parent().find("div#i"+$(this).parent().attr("id")).length+1)}));
|
||||
console.debug("honk");
|
||||
preparePostPreviews(true);
|
||||
} else {
|
||||
$(this).next().remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getUploadPostID(upload, container) {
|
||||
// if container, upload is div.upload-container
|
||||
// otherwise it's img or video
|
||||
var jqu = container? $jq(upload) : $jq(upload).parent();
|
||||
if(insideOP(jqu)) return jqu.siblings().eq(4).text();
|
||||
else return jqu.siblings().eq(3).text();
|
||||
}
|
||||
|
||||
function insideOP(elem) {
|
||||
return $jq(elem).parents("div.op-post").length > 0;
|
||||
}
|
||||
|
||||
function prepareThumbnails() {
|
||||
// set thumbnails to expand when clicked
|
||||
$jq("a.upload-container").click(function(e) {
|
||||
var a = $jq(this);
|
||||
var thumb = a.find("img.thumbnail");
|
||||
var video;
|
||||
var thumbURL;
|
||||
if(thumb.attr("alt") == undefined) thumbURL = thumb.attr("src");
|
||||
else thumbURL = thumb.attr("alt");
|
||||
var thumb_width = thumb.attr("width");
|
||||
var thumb_height = thumb.attr("height");
|
||||
var file_info_elem = a.prevAll(".file-info:first");
|
||||
var uploadURL = file_info_elem.children("a:first")[0].href;
|
||||
|
||||
if(thumb.attr("src") == thumbURL) {
|
||||
// Expanding thumbnail
|
||||
if(uploadURL.indexOf(".webm") > 0) {
|
||||
// Upload is a video
|
||||
thumb.hide();
|
||||
video = $jq("<video />")
|
||||
.prop({
|
||||
src: uploadURL,
|
||||
autoplay: true,
|
||||
controls: true,
|
||||
loop: true
|
||||
}).click(function(e) {
|
||||
e.preventDefault();
|
||||
if(this.pause) this.pause();
|
||||
this.removeAttribute("src");
|
||||
this.remove();
|
||||
}).insertAfter(a);
|
||||
|
||||
var close_video_btn = $jq("<a />")
|
||||
.prop("href", "javascript:;")
|
||||
.click(function(e) {
|
||||
video.remove();
|
||||
thumb.show();
|
||||
this.remove();
|
||||
}).css({
|
||||
"padding-left": "8px"
|
||||
})
|
||||
.text("[Close]")
|
||||
.insertAfter(file_info_elem);
|
||||
} else {
|
||||
thumb.attr({
|
||||
src: uploadURL,
|
||||
alt: thumbURL
|
||||
});
|
||||
thumb.removeAttr("width");
|
||||
thumb.removeAttr("height");
|
||||
}
|
||||
} else {
|
||||
// Shrinking back to thumbnail
|
||||
thumb.attr({
|
||||
"src": thumbURL,
|
||||
"width": thumb_width,
|
||||
"height": thumb_height
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
var TopBarButton = function(title,callback_open, callback_close) {
|
||||
this.title = title;
|
||||
$jq("div#topbar").append("<a href=\"javascript:void(0)\" class=\"dropdown-button\" id=\""+title.toLowerCase()+"\">"+title+down_arrow_symbol+"</a>");
|
||||
|
@ -113,7 +187,7 @@ function showMessage(msg) {
|
|||
lightbox_css_added = true;
|
||||
}
|
||||
$jq(document.body).prepend("<div class=\"lightbox-bg\"></div><div class=\"lightbox-msg\">"+msg+"<br /><button class=\"lightbox-msg-ok\" style=\"float: right; margin-top:8px;\">OK</button></div>");
|
||||
console.debug($jq(".lightbox-msg").width());
|
||||
console.log($jq(".lightbox-msg").width());
|
||||
var centeroffset = parseInt($jq(".lightbox-msg").css("transform-origin").replace("px",""),10)+$jq(".lightbox-msg").width()/2
|
||||
|
||||
$jq(".lightbox-msg").css({
|
||||
|
@ -191,19 +265,19 @@ function changeFrontPage(page_name) {
|
|||
|
||||
// heavily based on 4chan's quote() function, with a few tweaks
|
||||
function quote(e) {
|
||||
var msgbox_id = "postmsg";
|
||||
if(qr_enabled) msgbox_id = "postmsg-qr";
|
||||
|
||||
if (document.selection) {
|
||||
document.getElementById(msgbox_id).focus();
|
||||
var t = document.getselection.createRange();
|
||||
t.text = ">>" + e + "\n"
|
||||
} else if (document.getElementById(msgbox_id).selectionStart || "0" == document.getElementById(msgbox_id).selectionStart) {
|
||||
var n = document.getElementById(msgbox_id).selectionStart,
|
||||
o = document.getElementById(msgbox_id).selectionEnd;
|
||||
document.getElementById(msgbox_id).value = document.getElementById(msgbox_id).value.substring(0, n) + ">>" + e + "\n" + document.getElementById(msgbox_id).value.substring(o, document.getElementById(msgbox_id).value.length)
|
||||
} else document.getElementById(msgbox_id).value += ">>" + e + "\n"
|
||||
window.scroll(0,document.getElementById(msgbox_id).offsetTop-48);
|
||||
var msgbox_id = "postmsg";
|
||||
if(qr_enabled) msgbox_id = "postmsg-qr";
|
||||
|
||||
if (document.selection) {
|
||||
document.getElementById(msgbox_id).focus();
|
||||
var t = document.getselection.createRange();
|
||||
t.text = ">>" + e + "\n"
|
||||
} else if (document.getElementById(msgbox_id).selectionStart || "0" == document.getElementById(msgbox_id).selectionStart) {
|
||||
var n = document.getElementById(msgbox_id).selectionStart,
|
||||
o = document.getElementById(msgbox_id).selectionEnd;
|
||||
document.getElementById(msgbox_id).value = document.getElementById(msgbox_id).value.substring(0, n) + ">>" + e + "\n" + document.getElementById(msgbox_id).value.substring(o, document.getElementById(msgbox_id).value.length)
|
||||
} else document.getElementById(msgbox_id).value += ">>" + e + "\n"
|
||||
window.scroll(0,document.getElementById(msgbox_id).offsetTop-48);
|
||||
}
|
||||
|
||||
function deletePost(id) {
|
||||
|
@ -284,49 +358,11 @@ function getCookie(name) {
|
|||
for(var i = 0; i < cookie_arr.length; i++) {
|
||||
pair = cookie_arr[i].split("=");
|
||||
if(pair[0] == name) {
|
||||
//var val = decodeURIComponent(pair[1]);
|
||||
val = pair[1].replace("+", " ");
|
||||
val = val.replace("%2B", "+");
|
||||
val = decodeURIComponent(val);
|
||||
return val;
|
||||
return decodeURIComponent(pair[1].replace("+", " ").replace("%2B", "+"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*function preparePostPreviews(is_inline) {
|
||||
var m_type = "mousemove";
|
||||
if(!movable_postpreviews) m_type = "mouseover";
|
||||
if(expandable_postrefs) $("a.postref").attr("href","javascript:void(0);");
|
||||
var hvr_str = "a.postref";
|
||||
if(is_inline) hvr_str = "div.inlinepostprev "+hvr_str;
|
||||
$(hvr_str).hover(function(){
|
||||
$(document.body).append($("div#"+this.innerHTML.replace(">>","")).clone().attr("class","postprev"))
|
||||
$(document).bind(m_type, function(e){
|
||||
$('.postprev').css({
|
||||
left: e.pageX + 8,
|
||||
top: e.pageY + 8
|
||||
});
|
||||
})
|
||||
},
|
||||
function() {
|
||||
$(".postprev").remove();
|
||||
});
|
||||
|
||||
if(expandable_postrefs) {
|
||||
var clk_str = "a.postref";
|
||||
if(is_inline) clk_str = "div.inlinepostprev "+clk_str;
|
||||
$(clk_str).click(function() {
|
||||
if($(this).next().attr("class") != "inlinepostprev") {
|
||||
$(".postprev").remove();
|
||||
$(this).after($("div#"+this.innerHTML.replace(">>","")).clone().attr({"class":"inlinepostprev","id":"i"+$(this).parent().attr("id")+"-"+($(this).parent().find("div#i"+$(this).parent().attr("id")).length+1)}));
|
||||
preparePostPreviews(true);
|
||||
} else {
|
||||
$(this).next().remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
} */
|
||||
|
||||
function reportPost(id) {
|
||||
var reason = prompt("Reason");
|
||||
}
|
||||
|
@ -359,9 +395,8 @@ $jq(document).ready(function() {
|
|||
addStaffButtons();
|
||||
}
|
||||
|
||||
if(isFrontPage()) {
|
||||
changeFrontPage(getHashVal());
|
||||
}
|
||||
if(isFrontPage()) changeFrontPage(getHashVal());
|
||||
else prepareThumbnails();
|
||||
|
||||
$jq(".plus").click(function() {
|
||||
var block = $jq(this).parent().next();
|
||||
|
@ -397,30 +432,4 @@ $jq(document).ready(function() {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
// set thumbnails to expand when clicked
|
||||
var thumbnails = document.getElementsByClassName("thumbnail");
|
||||
for(var i = 0; i < thumbnails.length; i++) {
|
||||
var is_thumb = true;
|
||||
thumbnails[i].onclick = function(e) {
|
||||
var src = this.parentNode.getAttribute("href");
|
||||
if(this.getAttribute("width") != null) {
|
||||
// change the width and height constraints
|
||||
this.setAttribute("old-width",this.getAttribute("width"));
|
||||
this.removeAttribute("width");
|
||||
this.setAttribute("old-height",this.getAttribute("height"));
|
||||
this.removeAttribute("height");
|
||||
} else {
|
||||
// this is an expanded image, shrink it
|
||||
var src = this.parentNode.getAttribute("href");
|
||||
this.setAttribute("width", this.getAttribute("old-width"));
|
||||
this.removeAttribute("old-width");
|
||||
this.setAttribute("height", this.getAttribute("old-height"));
|
||||
this.removeAttribute("old-height");
|
||||
}
|
||||
this.setAttribute("src", src);
|
||||
//thumbnails[i].setAttribute("onclick", "function() { alert(\"hi!\"); return ")
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
6
html/javascript/jquery-1.10.2.min.js
vendored
6
html/javascript/jquery-1.10.2.min.js
vendored
File diff suppressed because one or more lines are too long
2
html/javascript/jquery-3.3.1.min.js
vendored
Normal file
2
html/javascript/jquery-3.3.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -19,11 +19,6 @@ type ManageFunction struct {
|
|||
Callback func() string //return string of html output
|
||||
}
|
||||
|
||||
var (
|
||||
rebuildfront func() string
|
||||
rebuildboards func() string
|
||||
)
|
||||
|
||||
func callManageFunction(w http.ResponseWriter, r *http.Request, data interface{}) {
|
||||
request = *r
|
||||
writer = w
|
||||
|
@ -53,10 +48,6 @@ func callManageFunction(w http.ResponseWriter, r *http.Request, data interface{}
|
|||
|
||||
if _, ok := manage_functions[action]; ok {
|
||||
if staffRank >= manage_functions[action].Permissions {
|
||||
if action == "rebuildall" || action == "purgeeverything" {
|
||||
rebuildfront = manage_functions["rebuildfront"].Callback
|
||||
rebuildboards = manage_functions["rebuildboards"].Callback
|
||||
}
|
||||
managePageBuffer.Write([]byte(manage_functions[action].Callback()))
|
||||
} else if staffRank == 0 && manage_functions[action].Permissions == 0 {
|
||||
managePageBuffer.Write([]byte(manage_functions[action].Callback()))
|
||||
|
@ -89,7 +80,6 @@ func getCurrentStaff() (string, error) {
|
|||
current_session := new(SessionsTable)
|
||||
|
||||
err := row.Scan(¤t_session.Data)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -264,14 +254,16 @@ var manage_functions = map[string]ManageFunction{
|
|||
return
|
||||
}
|
||||
}
|
||||
_, err = db.Exec("truncate `" + config.DBprefix + "posts`")
|
||||
_, err = db.Exec("TRUNCATE `" + config.DBprefix + "posts`")
|
||||
if err != nil {
|
||||
html += err.Error() + "<br />"
|
||||
return
|
||||
}
|
||||
_, _ = db.Exec("ALTER TABLE `" + config.DBprefix + "posts` AUTO_INCREMENT = 1")
|
||||
html += "<br />Everything purged, rebuilding all<br />"
|
||||
html += rebuildboards() + "<hr />\n"
|
||||
db.Exec("ALTER TABLE `" + config.DBprefix + "posts` AUTO_INCREMENT = 1")
|
||||
html += "<br />Everything purged, rebuilding all<br />" +
|
||||
buildBoards(true, 0) + "<hr />\n" +
|
||||
buildFrontPage()
|
||||
|
||||
return
|
||||
}},
|
||||
"executesql": {
|
||||
|
@ -282,10 +274,12 @@ var manage_functions = map[string]ManageFunction{
|
|||
if statement != "" {
|
||||
html += "<hr />"
|
||||
result, sqlerr := db.Exec(statement)
|
||||
fmt.Println(&result)
|
||||
println(1, &result)
|
||||
|
||||
if sqlerr != nil {
|
||||
html += sqlerr.Error()
|
||||
errortext := sqlerr.Error()
|
||||
html += errortext
|
||||
println(1, errortext)
|
||||
} else {
|
||||
html += "Statement esecuted successfully."
|
||||
}
|
||||
|
@ -366,7 +360,7 @@ var manage_functions = map[string]ManageFunction{
|
|||
"<div class=\"section-title-block\"><b>" + announcement.Subject + "</b> by " + announcement.Poster + " at " + humanReadableTime(announcement.Timestamp) + "</div>\n" +
|
||||
"<div class=\"section-body\">" + announcement.Message + "\n</div></div>\n"
|
||||
}
|
||||
iterations += 1
|
||||
iterations++
|
||||
}
|
||||
|
||||
if iterations == 0 {
|
||||
|
@ -609,28 +603,28 @@ var manage_functions = map[string]ManageFunction{
|
|||
if err != nil {
|
||||
do = ""
|
||||
board_creation_status = err.Error()
|
||||
continue
|
||||
break
|
||||
}
|
||||
|
||||
err = os.Mkdir(path.Join(config.DocumentRoot, board.Dir, "res"), 0777)
|
||||
if err != nil {
|
||||
do = ""
|
||||
board_creation_status = err.Error()
|
||||
continue
|
||||
break
|
||||
}
|
||||
|
||||
err = os.Mkdir(path.Join(config.DocumentRoot, board.Dir, "thumb"), 0777)
|
||||
if err != nil {
|
||||
do = ""
|
||||
board_creation_status = err.Error()
|
||||
continue
|
||||
break
|
||||
}
|
||||
|
||||
err = os.Mkdir(path.Join(config.DocumentRoot, board.Dir, "src"), 0777)
|
||||
if err != nil {
|
||||
do = ""
|
||||
board_creation_status = err.Error()
|
||||
continue
|
||||
break
|
||||
}
|
||||
stmt, err := db.Prepare(
|
||||
"INSERT INTO `" + config.DBprefix + "boards` (`order`,`dir`,`type`,`upload_type`,`title`,`subtitle`," +
|
||||
|
@ -647,7 +641,7 @@ var manage_functions = map[string]ManageFunction{
|
|||
if err != nil {
|
||||
do = ""
|
||||
board_creation_status = err.Error()
|
||||
continue
|
||||
break
|
||||
}
|
||||
|
||||
boardCreationTimestamp := getSpecificSQLDateTime(board.CreatedOn)
|
||||
|
@ -664,13 +658,18 @@ var manage_functions = map[string]ManageFunction{
|
|||
if err != nil {
|
||||
do = ""
|
||||
board_creation_status = err.Error()
|
||||
continue
|
||||
println(1, "Error creating board: "+board_creation_status)
|
||||
break
|
||||
} else {
|
||||
board_creation_status = "Board created successfully"
|
||||
rebuildboards()
|
||||
println(1, board_creation_status)
|
||||
buildBoards(true, 0)
|
||||
resetBoardSectionArrays()
|
||||
println(1, "Boards rebuilt successfully")
|
||||
done = true
|
||||
break
|
||||
}
|
||||
resetBoardSectionArrays()
|
||||
|
||||
case do == "del":
|
||||
// resetBoardSectionArrays()
|
||||
case do == "edit":
|
||||
|
@ -680,6 +679,7 @@ var manage_functions = map[string]ManageFunction{
|
|||
rows, err = db.Query("SELECT `column_name`,`column_default` FROM `information_schema`.`columns` WHERE `table_name` = '" + config.DBprefix + "boards'")
|
||||
if err != nil {
|
||||
html += err.Error()
|
||||
println(1, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -687,8 +687,14 @@ var manage_functions = map[string]ManageFunction{
|
|||
var column_name string
|
||||
var column_default string
|
||||
err = rows.Scan(&column_name, &column_default)
|
||||
if err != nil {
|
||||
html += err.Error()
|
||||
println(1, err.Error())
|
||||
return
|
||||
}
|
||||
column_default_int, _ := strconv.Atoi(column_default)
|
||||
column_default_bool := (column_default_int == 1)
|
||||
println(1, "Got this far...")
|
||||
switch column_name {
|
||||
case "id":
|
||||
board.ID = column_default_int
|
||||
|
@ -739,6 +745,7 @@ var manage_functions = map[string]ManageFunction{
|
|||
case "enable_catalog":
|
||||
board.EnableCatalog = column_default_bool
|
||||
}
|
||||
println(1, "Done with the switch")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -810,15 +817,16 @@ var manage_functions = map[string]ManageFunction{
|
|||
"rebuildfront": {
|
||||
Permissions: 3,
|
||||
Callback: func() (html string) {
|
||||
initTemplates()
|
||||
return buildFrontPage()
|
||||
}},
|
||||
"rebuildall": {
|
||||
Permissions: 3,
|
||||
Callback: func() (html string) {
|
||||
html += rebuildfront() + "<hr />\n"
|
||||
html += buildBoardListJSON() + "<hr />\n"
|
||||
html += rebuildboards() + "<hr />\n"
|
||||
return
|
||||
initTemplates()
|
||||
return buildFrontPage() + "<hr />\n" +
|
||||
buildBoardListJSON() + "<hr />\n" +
|
||||
buildBoards(true, 0) + "<hr />\n"
|
||||
}},
|
||||
"rebuildboards": {
|
||||
Permissions: 3,
|
||||
|
|
260
src/posting.go
260
src/posting.go
|
@ -7,6 +7,7 @@ import (
|
|||
"crypto/md5"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"image"
|
||||
|
@ -15,6 +16,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
@ -792,18 +794,18 @@ func sinceLastPost(post *PostTable) int {
|
|||
return -1
|
||||
}
|
||||
|
||||
func createThumbnail(image_obj image.Image, size string) image.Image {
|
||||
func createImageThumbnail(image_obj image.Image, size string) image.Image {
|
||||
var thumb_width int
|
||||
var thumb_height int
|
||||
|
||||
switch {
|
||||
case size == "op":
|
||||
switch size {
|
||||
case "op":
|
||||
thumb_width = config.ThumbWidth
|
||||
thumb_height = config.ThumbHeight
|
||||
case size == "reply":
|
||||
case "reply":
|
||||
thumb_width = config.ThumbWidth_reply
|
||||
thumb_height = config.ThumbHeight_reply
|
||||
case size == "catalog":
|
||||
case "catalog":
|
||||
thumb_width = config.ThumbWidth_catalog
|
||||
thumb_height = config.ThumbHeight_catalog
|
||||
}
|
||||
|
@ -817,6 +819,40 @@ func createThumbnail(image_obj image.Image, size string) image.Image {
|
|||
return image_obj
|
||||
}
|
||||
|
||||
func createVideoThumbnail(video, thumb string, size int) error {
|
||||
sizeStr := strconv.Itoa(size)
|
||||
outputBytes, err := exec.Command("ffmpeg", "-y", "-itsoffset", "-1", "-i", video, "-vframes", "1", "-filter:v", "scale='min("+sizeStr+"\\, "+sizeStr+"):-1'", thumb).CombinedOutput()
|
||||
if err != nil {
|
||||
outputStringArr := strings.Split(string(outputBytes), "\n")
|
||||
if len(outputStringArr) > 1 {
|
||||
outputString := outputStringArr[len(outputStringArr)-2]
|
||||
err = errors.New(outputString)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func getVideoInfo(path string) (map[string]int, error) {
|
||||
var vidInfo map[string]int
|
||||
outputBytes, err := exec.Command("ffprobe", "-v quiet", "-show_format", "-show_streams", path).CombinedOutput()
|
||||
if err == nil && outputBytes != nil {
|
||||
outputStringArr := strings.Split(string(outputBytes), "\n")
|
||||
for _, line := range outputStringArr {
|
||||
lineArr := strings.Split(line, "=")
|
||||
if len(lineArr) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
if lineArr[0] == "width" || lineArr[0] == "height" || lineArr[0] == "size" {
|
||||
value, _ := strconv.Atoi(lineArr[1])
|
||||
vidInfo[lineArr[0]] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vidInfo, err
|
||||
}
|
||||
|
||||
func getNewFilename() string {
|
||||
now := time.Now().Unix()
|
||||
rand.Seed(now)
|
||||
|
@ -972,7 +1008,7 @@ func makePost(w http.ResponseWriter, r *http.Request, data interface{}) {
|
|||
serveErrorPage(w, "Post body is too long")
|
||||
return
|
||||
}
|
||||
post.MessageHTML = sanitizeHTML(post.MessageText)
|
||||
post.MessageHTML = html.EscapeString(post.MessageText)
|
||||
formatMessage(&post)
|
||||
|
||||
post.Password = md5Sum(request.FormValue("postpassword"))
|
||||
|
@ -1025,9 +1061,11 @@ func makePost(w http.ResponseWriter, r *http.Request, data interface{}) {
|
|||
post.FilenameOriginal = html.EscapeString(handler.Filename)
|
||||
filetype := getFileExtension(post.FilenameOriginal)
|
||||
thumbFiletype := filetype
|
||||
if thumbFiletype == "gif" {
|
||||
|
||||
if thumbFiletype == "gif" || thumbFiletype == "webm" {
|
||||
thumbFiletype = "jpg"
|
||||
}
|
||||
|
||||
post.Filename = getNewFilename() + "." + getFileExtension(post.FilenameOriginal)
|
||||
boardArr, _ := getBoardArr(map[string]interface{}{"id": request.FormValue("boardid")}, "")
|
||||
if len(boardArr) == 0 {
|
||||
|
@ -1036,7 +1074,9 @@ func makePost(w http.ResponseWriter, r *http.Request, data interface{}) {
|
|||
_boardDir, _ := getBoardArr(map[string]interface{}{"id": request.FormValue("boardid")}, "")
|
||||
boardDir := _boardDir[0].Dir
|
||||
filePath := path.Join(config.DocumentRoot, "/"+boardDir+"/src/", post.Filename)
|
||||
|
||||
thumbPath := path.Join(config.DocumentRoot, "/"+boardDir+"/thumb/", strings.Replace(post.Filename, "."+filetype, "t."+thumbFiletype, -1))
|
||||
|
||||
catalogThumbPath := path.Join(config.DocumentRoot, "/"+boardDir+"/thumb/", strings.Replace(post.Filename, "."+filetype, "c."+thumbFiletype, -1))
|
||||
|
||||
err := ioutil.WriteFile(filePath, data, 0777)
|
||||
|
@ -1051,89 +1091,181 @@ func makePost(w http.ResponseWriter, r *http.Request, data interface{}) {
|
|||
// Calculate image checksum
|
||||
post.FileChecksum = fmt.Sprintf("%x", md5.Sum(data))
|
||||
|
||||
// Attempt to load uploaded file with imaging library
|
||||
img, err := imaging.Open(filePath)
|
||||
var allowsVids bool
|
||||
vidStmt, err := db.Prepare("SELECT `embeds_allowed` FROM `" + config.DBprefix + "boards` WHERE `id` = ? LIMIT 1")
|
||||
if err != nil {
|
||||
errorText = "Couldn't open uploaded file \"" + post.Filename + "\"" + err.Error()
|
||||
errortext := err.Error()
|
||||
errorLog.Println(errorText)
|
||||
println(1, errorText)
|
||||
serveErrorPage(w, "Upload filetype not supported")
|
||||
|
||||
serveErrorPage(w, "Couldn't get board info: "+errorText)
|
||||
println(1, errortext)
|
||||
return
|
||||
} else {
|
||||
// Get image filesize
|
||||
stat, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
errorLog.Println("Couldn't get image filesize: " + err.Error())
|
||||
println(1, "Couldn't get image filesize: "+err.Error())
|
||||
serveErrorPage(w, "Couldn't get image filesize: "+err.Error())
|
||||
} else {
|
||||
post.Filesize = int(stat.Size())
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if vidStmt != nil {
|
||||
vidStmt.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
err = vidStmt.QueryRow(post.BoardID).Scan(&allowsVids)
|
||||
if err != nil {
|
||||
errortext := err.Error()
|
||||
errorLog.Println(errorText)
|
||||
serveErrorPage(w, "Couldn't get board info: "+errorText)
|
||||
println(1, errortext)
|
||||
return
|
||||
}
|
||||
|
||||
if filetype == "webm" {
|
||||
if !allowsVids || !config.AllowVideoUploads {
|
||||
serveErrorPage(w, "Video uploading is not currently enabled for this board.")
|
||||
os.Remove(filePath)
|
||||
return
|
||||
}
|
||||
|
||||
// Get image width and height, as well as thumbnail width and height
|
||||
post.ImageW = img.Bounds().Max.X
|
||||
post.ImageH = img.Bounds().Max.Y
|
||||
accessLog.Print("Receiving post with video: " + handler.Filename + " from " + request.RemoteAddr + ", referrer: " + request.Referer())
|
||||
if post.ParentID == 0 {
|
||||
post.ThumbW, post.ThumbH = getThumbnailSize(post.ImageW, post.ImageH, "op")
|
||||
err := createVideoThumbnail(filePath, thumbPath, config.ThumbWidth)
|
||||
if err != nil {
|
||||
serveErrorPage(w, err.Error())
|
||||
printf(1, err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
post.ThumbW, post.ThumbH = getThumbnailSize(post.ImageW, post.ImageH, "reply")
|
||||
err := createVideoThumbnail(filePath, thumbPath, config.ThumbWidth_reply)
|
||||
if err != nil {
|
||||
serveErrorPage(w, err.Error())
|
||||
printf(1, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
accessLog.Print("Receiving post with image: " + handler.Filename + " from " + request.RemoteAddr + ", referrer: " + request.Referer())
|
||||
err := createVideoThumbnail(filePath, catalogThumbPath, config.ThumbWidth_catalog)
|
||||
if err != nil {
|
||||
serveErrorPage(w, err.Error())
|
||||
printf(1, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if request.FormValue("spoiler") == "on" {
|
||||
// If spoiler is enabled, symlink thumbnail to spoiler image
|
||||
_, err := os.Stat(path.Join(config.DocumentRoot, "spoiler.png"))
|
||||
outputBytes, err := exec.Command("ffprobe", "-v", "quiet", "-show_format", "-show_streams", filePath).CombinedOutput()
|
||||
if err != nil {
|
||||
errortext := "Error getting video info: " + err.Error()
|
||||
serveErrorPage(w, errortext)
|
||||
printf(1, errortext)
|
||||
return
|
||||
}
|
||||
if err == nil && outputBytes != nil {
|
||||
outputStringArr := strings.Split(string(outputBytes), "\n")
|
||||
for _, line := range outputStringArr {
|
||||
lineArr := strings.Split(line, "=")
|
||||
if len(lineArr) < 2 {
|
||||
continue
|
||||
}
|
||||
value, _ := strconv.Atoi(lineArr[1])
|
||||
switch lineArr[0] {
|
||||
case "width":
|
||||
post.ImageW = value
|
||||
case "height":
|
||||
post.ImageH = value
|
||||
case "size":
|
||||
post.Filesize = value
|
||||
}
|
||||
}
|
||||
if post.ParentID == 0 {
|
||||
post.ThumbW, post.ThumbH = getThumbnailSize(post.ImageW, post.ImageH, "op")
|
||||
} else {
|
||||
post.ThumbW, post.ThumbH = getThumbnailSize(post.ImageW, post.ImageH, "reply")
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Attempt to load uploaded file with imaging library
|
||||
img, err := imaging.Open(filePath)
|
||||
if err != nil {
|
||||
errorText = "Couldn't open uploaded file \"" + post.Filename + "\"" + err.Error()
|
||||
errorLog.Println(errorText)
|
||||
println(1, errorText)
|
||||
os.Remove(filePath)
|
||||
serveErrorPage(w, "Upload filetype not supported")
|
||||
return
|
||||
} else {
|
||||
// Get image filesize
|
||||
stat, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
serveErrorPage(w, "missing /spoiler.png")
|
||||
errortext := "Couldn't get image filesize: " + err.Error()
|
||||
errorLog.Println(errortext)
|
||||
println(1, errortext)
|
||||
serveErrorPage(w, errortext)
|
||||
return
|
||||
} else {
|
||||
err = syscall.Symlink(path.Join(config.DocumentRoot, "spoiler.png"), thumbPath)
|
||||
post.Filesize = int(stat.Size())
|
||||
}
|
||||
|
||||
// Get image width and height, as well as thumbnail width and height
|
||||
post.ImageW = img.Bounds().Max.X
|
||||
post.ImageH = img.Bounds().Max.Y
|
||||
if post.ParentID == 0 {
|
||||
post.ThumbW, post.ThumbH = getThumbnailSize(post.ImageW, post.ImageH, "op")
|
||||
} else {
|
||||
post.ThumbW, post.ThumbH = getThumbnailSize(post.ImageW, post.ImageH, "reply")
|
||||
}
|
||||
|
||||
accessLog.Print("Receiving post with image: " + handler.Filename + " from " + request.RemoteAddr + ", referrer: " + request.Referer())
|
||||
|
||||
if request.FormValue("spoiler") == "on" {
|
||||
// If spoiler is enabled, symlink thumbnail to spoiler image
|
||||
_, err := os.Stat(path.Join(config.DocumentRoot, "spoiler.png"))
|
||||
if err != nil {
|
||||
serveErrorPage(w, "missing /spoiler.png")
|
||||
return
|
||||
} else {
|
||||
err = syscall.Symlink(path.Join(config.DocumentRoot, "spoiler.png"), thumbPath)
|
||||
if err != nil {
|
||||
serveErrorPage(w, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if config.ThumbWidth >= post.ImageW && config.ThumbHeight >= post.ImageH {
|
||||
// If image fits in thumbnail size, symlink thumbnail to original
|
||||
post.ThumbW = img.Bounds().Max.X
|
||||
post.ThumbH = img.Bounds().Max.Y
|
||||
err := syscall.Symlink(filePath, thumbPath)
|
||||
if err != nil {
|
||||
serveErrorPage(w, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if config.ThumbWidth >= post.ImageW && config.ThumbHeight >= post.ImageH {
|
||||
// If image fits in thumbnail size, symlink thumbnail to original
|
||||
post.ThumbW = img.Bounds().Max.X
|
||||
post.ThumbH = img.Bounds().Max.Y
|
||||
err := syscall.Symlink(filePath, thumbPath)
|
||||
if err != nil {
|
||||
serveErrorPage(w, err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
var thumbnail image.Image
|
||||
var catalogThumbnail image.Image
|
||||
if post.ParentID == 0 {
|
||||
// If this is a new thread, generate thumbnail and catalog thumbnail
|
||||
thumbnail = createThumbnail(img, "op")
|
||||
catalogThumbnail = createThumbnail(img, "catalog")
|
||||
println(1, catalogThumbPath)
|
||||
err = imaging.Save(catalogThumbnail, catalogThumbPath)
|
||||
} else {
|
||||
var thumbnail image.Image
|
||||
var catalogThumbnail image.Image
|
||||
if post.ParentID == 0 {
|
||||
// If this is a new thread, generate thumbnail and catalog thumbnail
|
||||
thumbnail = createImageThumbnail(img, "op")
|
||||
catalogThumbnail = createImageThumbnail(img, "catalog")
|
||||
println(1, catalogThumbPath)
|
||||
err = imaging.Save(catalogThumbnail, catalogThumbPath)
|
||||
if err != nil {
|
||||
errorLog.Println("Couldn't generate catalog thumbnail: " + err.Error())
|
||||
serveErrorPage(w, "Couldn't generate catalog thumbnail: "+err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
thumbnail = createImageThumbnail(img, "reply")
|
||||
}
|
||||
err = imaging.Save(thumbnail, thumbPath)
|
||||
if err != nil {
|
||||
errorLog.Println("Couldn't generate catalog thumbnail: " + err.Error())
|
||||
serveErrorPage(w, "Couldn't generate catalog thumbnail: "+err.Error())
|
||||
errortext := "Couldn't save thumbnail: " + err.Error()
|
||||
println(0, errortext)
|
||||
errorLog.Println(errortext)
|
||||
serveErrorPage(w, errortext)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
thumbnail = createThumbnail(img, "reply")
|
||||
}
|
||||
err = imaging.Save(thumbnail, thumbPath)
|
||||
if err != nil {
|
||||
println(0, "Couldn't save thumbnail: "+err.Error())
|
||||
errorLog.Println("Couldn't save thumbnail: " + err.Error())
|
||||
serveErrorPage(w, "Couldn't save thumbnail: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if post.FilenameOriginal != "" {
|
||||
//post.FilenameOriginal = sanitizeHTML(post.FilenameOriginal)
|
||||
//post.FilenameOriginal = html.EscapeString(post.FilenameOriginal)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(post.MessageText) == "" && post.Filename == "" {
|
||||
|
|
|
@ -32,12 +32,12 @@ func (s GochanServer) AddNamespace(basePath string, namespaceFunction func(http.
|
|||
s.namespaces[basePath] = namespaceFunction
|
||||
}
|
||||
|
||||
func (s GochanServer) getFileData(writer http.ResponseWriter, url string) []byte {
|
||||
func (s GochanServer) getFileData(writer http.ResponseWriter, url string) (fileBytes []byte) {
|
||||
filePath := path.Join(config.DocumentRoot, url)
|
||||
results, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
// the requested path isn't a file or directory, 404
|
||||
return nil
|
||||
fileBytes = nil
|
||||
} else {
|
||||
//the file exists, or there is a folder here
|
||||
if results.IsDir() {
|
||||
|
@ -54,7 +54,7 @@ func (s GochanServer) getFileData(writer http.ResponseWriter, url string) []byte
|
|||
}
|
||||
} else {
|
||||
//the file exists, and is not a folder
|
||||
fileBytes, _ := ioutil.ReadFile(filePath)
|
||||
fileBytes, _ = ioutil.ReadFile(filePath)
|
||||
extension := getFileExtension(url)
|
||||
switch extension {
|
||||
case "png":
|
||||
|
@ -75,24 +75,29 @@ func (s GochanServer) getFileData(writer http.ResponseWriter, url string) []byte
|
|||
case "json":
|
||||
writer.Header().Add("Content-Type", "application/json")
|
||||
writer.Header().Add("Cache-Control", "max-age=5, must-revalidate")
|
||||
case "webm":
|
||||
writer.Header().Add("Content-Type", "video/webm")
|
||||
writer.Header().Add("Cache-Control", "max-age=86400")
|
||||
}
|
||||
if strings.HasPrefix(extension, "htm") {
|
||||
writer.Header().Add("Content-Type", "text/html")
|
||||
writer.Header().Add("Cache-Control", "max-age=5, must-revalidate")
|
||||
}
|
||||
|
||||
accessLog.Print("Success: 200 from " + getRealIP(&request) + " @ " + request.RequestURI)
|
||||
return fileBytes
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func serveNotFound(writer http.ResponseWriter, request *http.Request) {
|
||||
writer.Header().Add("Content-Type", "text/html; charset=utf-8")
|
||||
writer.WriteHeader(404)
|
||||
errorPage, err := ioutil.ReadFile(config.DocumentRoot + "/error/404.html")
|
||||
if err != nil {
|
||||
writer.Write([]byte("Requested page not found, and 404 error page not found"))
|
||||
} else {
|
||||
writer.Write(errorPage)
|
||||
|
||||
}
|
||||
errorLog.Print("Error: 404 Not Found from " + getRealIP(request) + " @ " + request.RequestURI)
|
||||
}
|
||||
|
|
|
@ -142,13 +142,32 @@ var funcMap = template.FuncMap{
|
|||
if name == "" || name == "deleted" {
|
||||
return ""
|
||||
}
|
||||
if name[len(name)-3:] == "gif" || name[len(name)-3:] == "gif" {
|
||||
|
||||
if name[len(name)-3:] == "gif" {
|
||||
name = name[:len(name)-3] + "jpg"
|
||||
} else if name[len(name)-4:] == "webm" {
|
||||
name = name[:len(name)-4] + "jpg"
|
||||
}
|
||||
ext_begin := strings.LastIndex(name, ".")
|
||||
new_name := name[:ext_begin] + "t." + getFileExtension(name)
|
||||
return new_name
|
||||
},
|
||||
"getUploadType": func(name string) string {
|
||||
extension := getFileExtension(name)
|
||||
var uploadType string
|
||||
switch extension {
|
||||
case "":
|
||||
case "deleted":
|
||||
uploadType = ""
|
||||
case "webm":
|
||||
case "jpg":
|
||||
case "gif":
|
||||
uploadType = "jpg"
|
||||
case "png":
|
||||
uploadType = "png"
|
||||
}
|
||||
return uploadType
|
||||
},
|
||||
"formatFilesize": func(size_int int) string {
|
||||
size := float32(size_int)
|
||||
if size < 1000 {
|
||||
|
@ -163,8 +182,8 @@ var funcMap = template.FuncMap{
|
|||
return fmt.Sprintf("%0.2f GB", size/1024/1024/1024)
|
||||
},
|
||||
"imageToThumbnailPath": func(img string) string {
|
||||
filetype := img[strings.LastIndex(img, ".")+1:]
|
||||
if filetype == "gif" || filetype == "GIF" {
|
||||
filetype := strings.ToLower(img[strings.LastIndex(img, ".")+1:])
|
||||
if filetype == "gif" || filetype == "webm" {
|
||||
filetype = "jpg"
|
||||
}
|
||||
index := strings.LastIndex(img, ".")
|
||||
|
|
13
src/types.go
13
src/types.go
|
@ -365,6 +365,7 @@ type GochanConfig struct {
|
|||
DefaultStyle_txt string
|
||||
|
||||
AllowDuplicateImages bool
|
||||
AllowVideoUploads bool
|
||||
NewThreadDelay int
|
||||
ReplyDelay int
|
||||
MaxLineLength int
|
||||
|
@ -568,6 +569,18 @@ func initConfig() {
|
|||
config.DefaultStyle_txt = config.Styles_txt[0]
|
||||
}
|
||||
|
||||
if config.NewThreadDelay == 0 {
|
||||
config.NewThreadDelay = 30
|
||||
}
|
||||
|
||||
if config.ReplyDelay == 0 {
|
||||
config.ReplyDelay = 7
|
||||
}
|
||||
|
||||
if config.MaxLineLength == 0 {
|
||||
config.MaxLineLength = 150
|
||||
}
|
||||
|
||||
//ReservedTrips string //eventually this will be map[string]string
|
||||
|
||||
if config.ThumbWidth == 0 {
|
||||
|
|
13
src/util.go
13
src/util.go
|
@ -418,19 +418,14 @@ func reverse(arr []interface{}) (reversed []interface{}) {
|
|||
return
|
||||
}
|
||||
|
||||
func sanitizeHTML(input string) (output string) {
|
||||
output = html.EscapeString(input)
|
||||
return
|
||||
}
|
||||
|
||||
// sanitize/escape HTML strings in a post. This should be run immediately before
|
||||
// the post is inserted into the database
|
||||
func sanitizePost(post PostTable) PostTable {
|
||||
sanitized := post
|
||||
sanitized.Name = sanitizeHTML(sanitized.Name)
|
||||
sanitized.Email = sanitizeHTML(sanitized.Email)
|
||||
sanitized.Subject = sanitizeHTML(sanitized.Subject)
|
||||
sanitized.Password = sanitizeHTML(sanitized.Password)
|
||||
sanitized.Name = html.EscapeString(sanitized.Name)
|
||||
sanitized.Email = html.EscapeString(sanitized.Email)
|
||||
sanitized.Subject = html.EscapeString(sanitized.Subject)
|
||||
sanitized.Password = html.EscapeString(sanitized.Password)
|
||||
return sanitized
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
var styles = [{{range $i, $style := $config.Styles_img}}{{if gt $i 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
var webroot = "{{$config.SiteWebfolder}}"
|
||||
</script>
|
||||
<script type="text/javascript" src="/javascript/jquery/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="/javascript/jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript" src="/javascript/gochan.js"></script>
|
||||
<script type="text/javascript" src="/javascript/manage.js"></script>
|
||||
</head>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{$config.SiteName}}</title>
|
||||
<script type="text/javascript" src="/javascript/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="/javascript/jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript" src="/javascript/msgpack.js"></script>
|
||||
<script type="text/javascript">
|
||||
var styles = [{{range $ii, $style := $config.Styles_img}}{{if gt $ii 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>/{{$board.Dir}}/ - {{$board.Title}}</title>
|
||||
<script type="text/javascript" src="/javascript/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="/javascript/jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var styles = [{{range $ii, $style := $config.Styles_img}}{{if gt $ii 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
var webroot = "{{$config.SiteWebfolder}}";
|
||||
|
@ -56,8 +56,8 @@
|
|||
<div class="op-post" id="op{{$op.ID}}">
|
||||
{{if stringNeq $op.Filename ""}}
|
||||
{{if stringNeq $op.Filename "deleted"}}
|
||||
<div class="file-info">File: <a href="src/{{$op.Filename}}" target="_blank">{{$op.Filename}}</a> - ({{formatFilesize $op.Filesize}} , {{$op.ImageW}}x{{$op.ImageH}}, {{$op.FilenameOriginal}} )</div>
|
||||
<a href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$op.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $op.Filename}}" width="{{$op.ThumbW}}" height="{{$op.ThumbH}}" class="thumbnail" alt="{{imageToThumbnailPath $op.Filename}}" /></a>
|
||||
<div class="file-info">File: <a href="src/{{$op.Filename}}" target="_blank">{{$op.Filename}}</a> - ({{formatFilesize $op.Filesize}} , {{$op.ImageW}}x{{$op.ImageH}}, {{$op.FilenameOriginal}})</div>
|
||||
<a class="upload-container" href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$op.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $op.Filename}}" width="{{$op.ThumbW}}" height="{{$op.ThumbH}}" class="thumbnail" /></a>
|
||||
{{else}}
|
||||
<div class="file-deleted-box" style="text-align:center;">File removed</div>
|
||||
{{end}}
|
||||
|
@ -72,8 +72,8 @@
|
|||
<div class="reply-container">
|
||||
<div class="reply" id="{{$reply.ID}}">
|
||||
<div><input type="checkbox" id="check{{$reply.ID}}" name="check{{$reply.ID}}" /> <label class="post-info" for="check{{$reply.ID}}"> <span class="subject">{{$reply.Subject}}</span> <span class="postername">{{if stringNeq $reply.Email ""}}<a href="mailto:{{$reply.Email}}">{{end}}{{if stringNeq $reply.Name ""}}{{$reply.Name}}{{else}}{{if stringEq $reply.Tripcode ""}}{{$board.Anonymous}}{{end}}{{end}}{{if stringNeq $reply.Email ""}}</a>{{end}}</span>{{if stringNeq $reply.Tripcode ""}}<span class="tripcode">!{{$reply.Tripcode}}</span>{{end}} {{formatTimestamp $reply.Timestamp}} </label><a href="/{{$board.Dir}}/res/{{$op.ID}}.html#{{$reply.ID}}">No.</a> <a href="javascript:quote({{$reply.ID}})" class="backlink-clink">{{$reply.ID}}</a> <span class="post-links"><span class="thread-ddown">[<a href="javascript:void(0)">▼</a>]</span></span></div>
|
||||
{{if stringNeq $reply.Filename ""}}<span class="file-info">File: <a href="src/{{$reply.Filename}}" target="_blank">{{$reply.Filename}}</a> - ({{formatFilesize $reply.Filesize}} , {{$reply.ImageW}}x{{$reply.ImageH}}, {{$reply.FilenameOriginal}} )</span><br />
|
||||
<a href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$reply.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $reply.Filename}}" width="{{$reply.ThumbW}}" height="{{$reply.ThumbH}}" class="thumbnail" alt="{{imageToThumbnailPath $reply.Filename}}" /></a>{{end}}
|
||||
{{if stringNeq $reply.Filename ""}}<span class="file-info">File: <a href="src/{{$reply.Filename}}" target="_blank">{{$reply.Filename}}</a> - ({{formatFilesize $reply.Filesize}} , {{$reply.ImageW}}x{{$reply.ImageH}}, {{$reply.FilenameOriginal}})</span><br />
|
||||
<a class="upload-container" href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$reply.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $reply.Filename}}" width="{{$reply.ThumbW}}" height="{{$reply.ThumbH}}" class="thumbnail" /></a></div>{{end}}
|
||||
|
||||
{{if stringNeq $reply.MessageHTML ""}}
|
||||
<div class="post-text">{{$reply.MessageHTML}}</div>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<title>/{{$board.Dir}}/ - {{truncateString $op.MessageHTML 20 true}}</title>
|
||||
{{end}}{{end}}
|
||||
<title>/{{$board.Dir}} - {{$board.Title}}</title>
|
||||
<script type="text/javascript" src="/javascript/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="/javascript/jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var styles = [{{range $ii, $style := $config.Styles_img}}{{if gt $ii 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
var webroot = "{{$config.SiteWebfolder}}";
|
||||
|
@ -73,24 +73,23 @@
|
|||
<div class="op-post" id="op{{$op.ID}}">
|
||||
{{if stringNeq $op.Filename ""}}
|
||||
{{if stringNeq $op.Filename "deleted"}}
|
||||
<div class="file-info">File: <a href="src/{{$op.Filename}}" target="_blank">{{$op.Filename}}</a> - ({{formatFilesize $op.Filesize}} , {{$op.ImageW}}x{{$op.ImageH}}, {{$op.FilenameOriginal}} )</div>
|
||||
<a href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$op.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $op.Filename}}" width="{{$op.ThumbW}}" height="{{$op.ThumbH}}" class="thumbnail" alt="{{imageToThumbnailPath $op.Filename}}" /></a>
|
||||
<div class="file-info">File: <a href="../src/{{$op.Filename}}" target="_blank">{{$op.Filename}}</a> - ({{formatFilesize $op.Filesize}} , {{$op.ImageW}}x{{$op.ImageH}}, {{$op.FilenameOriginal}})</div>
|
||||
<a class="upload-container" href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$op.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $op.Filename}}" width="{{$op.ThumbW}}" height="{{$op.ThumbH}}" class="thumbnail" /></a>
|
||||
{{else}}
|
||||
<div class="file-deleted-box" style="text-align:center;">File removed</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<input type="checkbox" id="check{{$op.ID}}" name="check{{$op.ID}}" /><label class="post-info" for="check{{$op.ID}}"> <span class="subject">{{$op.Subject}}</span> <span class="postername">{{if stringNeq $op.Email ""}}<a href="mailto:{{$op.Email}}">{{end}}{{if stringNeq $op.Name ""}}{{$op.Name}}{{else}}{{if stringEq $op.Tripcode ""}}{{$board.Anonymous}}{{end}}{{end}}{{if stringNeq $op.Email ""}}</a>{{end}}</span>{{if stringNeq $op.Tripcode ""}}<span class="tripcode">!{{$op.Tripcode}}</span>{{end}} {{formatTimestamp $op.Timestamp}} </label><a href="/{{$board.Dir}}/res/{{$op.ID}}.html#{{$op.ID}}">No.</a> <a href="javascript:quote({{$op.ID}})" class="backlink-click">{{$op.ID}}</a> <span class="post-links"> <span class="thread-ddown">[<a href="javascript:void(0)">▼</a>]</span></span><br />
|
||||
{{if stringNeq $op.MessageHTML ""}}
|
||||
<div class="post-text">{{$op.MessageHTML}}</div>
|
||||
{{end}}
|
||||
<div class="post-text">{{$op.MessageHTML}}</div>
|
||||
|
||||
</div>
|
||||
{{range $reply_num,$reply := $post_arr}}{{if gt $reply_num 0}}
|
||||
<div class="reply-container" id="replycontainer{{$reply.ID}}">
|
||||
<a class="anchor" id="{{$reply.ID}}"></a>
|
||||
<div class="reply" id="reply{{$reply.ID}}">
|
||||
<input type="checkbox" id="check{{$reply.ID}}" name="check{{$reply.ID}}" /> <label class="post-info" for="check{{$reply.ID}}"> <span class="subject">{{$reply.Subject}}</span> <span class="postername">{{if stringNeq $reply.Email ""}}<a href="mailto:{{$reply.Email}}">{{end}}{{if stringNeq $reply.Name ""}}{{$reply.Name}}{{else}}{{if stringEq $reply.Tripcode ""}}{{$board.Anonymous}}{{end}}{{end}}{{if stringNeq $reply.Email ""}}</a>{{end}}</span>{{if stringNeq $reply.Tripcode ""}}<span class="tripcode">!{{$reply.Tripcode}}</span>{{end}} {{formatTimestamp $reply.Timestamp}} </label><a href="/{{$board.Dir}}/res/{{$op.ID}}.html#{{$reply.ID}}">No.</a> <a href="javascript:quote({{$reply.ID}})" class="backlink-click">{{$reply.ID}}</a> <span class="post-links"><span class="thread-ddown">[<a href="javascript:void(0)">▼</a>]</span></span><br />
|
||||
{{if stringNeq $reply.Filename ""}}<span class="file-info">File: <a href="../src/{{$reply.Filename}}" target="_blank">{{$reply.Filename}}</a> - ({{formatFilesize $reply.Filesize}} , {{$reply.ImageW}}x{{$reply.ImageH}}, {{$reply.FilenameOriginal}} )</span><br />
|
||||
<a href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$reply.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $reply.Filename}}" width="{{$reply.ThumbW}}" height="{{$reply.ThumbH}}" class="thumbnail" alt="{{imageToThumbnailPath $reply.Filename}}" /></a>{{end}}
|
||||
{{if stringNeq $reply.Filename ""}}<span class="file-info">File: <a href="../src/{{$reply.Filename}}" target="_blank">{{$reply.Filename}}</a> - ({{formatFilesize $reply.Filesize}} , {{$reply.ImageW}}x{{$reply.ImageH}}, {{$reply.FilenameOriginal}})</span><br />
|
||||
<a class="upload-container" href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$reply.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $reply.Filename}}" width="{{$reply.ThumbW}}" height="{{$reply.ThumbH}}" class="thumbnail" /></a>{{end}}
|
||||
<div class="post-text">{{$reply.MessageHTML}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
var styles = [{{range $i, $style := .Styles_img}}{{if gt $i 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
var webroot = "{{.SiteWebfolder}}"
|
||||
</script>
|
||||
<script type="text/javascript" src="/javascript/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="/javascript/jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript" src="/javascript/gochan.js"></script>
|
||||
<script type="text/javascript" src="/javascript/manage.js"></script>
|
||||
</head>
|
||||
|
|
|
@ -8,7 +8,7 @@ export GOPATH=/vagrant/lib
|
|||
|
||||
apt-get update
|
||||
apt-get -y upgrade
|
||||
apt-get -y install git subversion mercurial golang nginx redis-server mariadb-server mariadb-client
|
||||
apt-get -y install git subversion mercurial golang nginx redis-server mariadb-server mariadb-client ffmpeg
|
||||
|
||||
# Make sure any imported database is utf8mb4
|
||||
# http://mathiasbynens.be/notes/mysql-utf8mb4
|
||||
|
@ -101,4 +101,4 @@ echo
|
|||
echo "Server set up, please run \"vagrant ssh\" on your host machine, and"
|
||||
echo "\"cd /home/vagrant/gochan && ./gochan\" in the guest. Then browse to http://172.27.0.3/manage"
|
||||
echo "to complete installation (TODO: add further instructions as default initial announcement"
|
||||
echo "or /manage?action=firstrun)"
|
||||
echo "or /manage?action=firstrun)"
|
Loading…
Add table
Add a link
Reference in a new issue