Support clipboard upload on home page
Add shortcuts for all the buttons Add abuse reporter page Fix user buckets page Add a-ads advertisement
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
function UploadProgressBar(uploadManager, queueDiv, file){
|
function UploadProgressBar(uploadManager, queueDiv, file) {
|
||||||
this.uploadManager = uploadManager
|
this.uploadManager = uploadManager
|
||||||
this.file = file
|
this.file = file
|
||||||
this.name = file.name
|
this.name = file.name
|
||||||
@@ -14,38 +14,38 @@ function UploadProgressBar(uploadManager, queueDiv, file){
|
|||||||
this.file,
|
this.file,
|
||||||
this.name,
|
this.name,
|
||||||
(progress) => { this.onProgress(progress) },
|
(progress) => { this.onProgress(progress) },
|
||||||
(id) => { this.onFinished(id) },
|
(id) => { this.onFinished(id) },
|
||||||
(val, msg) => { this.onFailure(val, msg) }
|
(val, msg) => { this.onFailure(val, msg) }
|
||||||
)
|
)
|
||||||
|
|
||||||
// Browsers don't render the transition if the opacity is set and
|
// Browsers don't render the transition if the opacity is set and
|
||||||
// updated in the same frame. So we have to wait a frame (or more)
|
// updated in the same frame. So we have to wait a frame (or more)
|
||||||
// before changing the opacity to make sure the transition triggers
|
// before changing the opacity to make sure the transition triggers
|
||||||
window.setTimeout(() => {this.uploadDiv.style.opacity = "1"}, 100)
|
window.setTimeout(() => { this.uploadDiv.style.opacity = "1" }, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
UploadProgressBar.prototype.onProgress = function(progress){
|
UploadProgressBar.prototype.onProgress = function (progress) {
|
||||||
this.uploadDiv.innerText = "Uploading... " + Math.round(progress*1000)/10 + "%\n" + this.name
|
this.uploadDiv.innerText = "Uploading... " + Math.round(progress * 1000) / 10 + "%\n" + this.name
|
||||||
this.uploadDiv.style.background = 'linear-gradient('
|
this.uploadDiv.style.background = 'linear-gradient('
|
||||||
+'to right, '
|
+ 'to right, '
|
||||||
+'var(--layer_3_color) 0%, '
|
+ 'var(--layer_3_color) 0%, '
|
||||||
+'var(--highlight_color) '+ ((progress*100)) +'%, '
|
+ 'var(--highlight_color) ' + ((progress * 100)) + '%, '
|
||||||
+'var(--layer_3_color) '+ ((progress*100)+1) +'%)'
|
+ 'var(--layer_3_color) ' + ((progress * 100) + 1) + '%)'
|
||||||
}
|
}
|
||||||
UploadProgressBar.prototype.onFinished = function(id){
|
UploadProgressBar.prototype.onFinished = function (id) {
|
||||||
console.log("Upload finished: "+this.file.name+" "+id)
|
console.log("Upload finished: " + this.file.name + " " + id)
|
||||||
|
|
||||||
this.uploadDiv.style.background = 'var(--layer_3_color)'
|
this.uploadDiv.style.background = 'var(--layer_3_color)'
|
||||||
this.uploadDiv.href = '/u/'+id
|
this.uploadDiv.href = '/u/' + id
|
||||||
this.uploadDiv.target= "_blank"
|
this.uploadDiv.target = "_blank"
|
||||||
|
|
||||||
let fileImg = document.createElement("img")
|
let fileImg = document.createElement("img")
|
||||||
fileImg.src = apiEndpoint+'/file/'+id+'/thumbnail'
|
fileImg.src = apiEndpoint + '/file/' + id + '/thumbnail'
|
||||||
fileImg.alt = this.file.name
|
fileImg.alt = this.file.name
|
||||||
|
|
||||||
let linkSpan = document.createElement("span")
|
let linkSpan = document.createElement("span")
|
||||||
linkSpan.classList = "file_button_title"
|
linkSpan.classList = "file_button_title"
|
||||||
linkSpan.innerText = domainURL()+"/u/"+id
|
linkSpan.innerText = domainURL() + "/u/" + id
|
||||||
|
|
||||||
this.uploadDiv.innerHTML = "" // Remove uploading progress
|
this.uploadDiv.innerHTML = "" // Remove uploading progress
|
||||||
this.uploadDiv.appendChild(fileImg)
|
this.uploadDiv.appendChild(fileImg)
|
||||||
@@ -53,7 +53,7 @@ UploadProgressBar.prototype.onFinished = function(id){
|
|||||||
this.uploadDiv.appendChild(document.createElement("br"))
|
this.uploadDiv.appendChild(document.createElement("br"))
|
||||||
this.uploadDiv.appendChild(linkSpan)
|
this.uploadDiv.appendChild(linkSpan)
|
||||||
}
|
}
|
||||||
UploadProgressBar.prototype.onFailure = function(val, msg) {
|
UploadProgressBar.prototype.onFailure = function (val, msg) {
|
||||||
if (val === "") {
|
if (val === "") {
|
||||||
val = "Could not connect to server"
|
val = "Could not connect to server"
|
||||||
}
|
}
|
||||||
@@ -62,22 +62,18 @@ UploadProgressBar.prototype.onFailure = function(val, msg) {
|
|||||||
this.uploadDiv.style.background = 'var(--danger_color)'
|
this.uploadDiv.style.background = 'var(--danger_color)'
|
||||||
this.uploadDiv.style.color = 'var(--highlight_text_color)'
|
this.uploadDiv.style.color = 'var(--highlight_text_color)'
|
||||||
this.uploadDiv.appendChild(document.createTextNode("Upload failed: "))
|
this.uploadDiv.appendChild(document.createTextNode("Upload failed: "))
|
||||||
this.uploadDiv.appendChild(document.createTextNode(msg+" ("+val+")"))
|
this.uploadDiv.appendChild(document.createTextNode(msg + " (" + val + ")"))
|
||||||
this.uploadDiv.appendChild(document.createElement("br"))
|
this.uploadDiv.appendChild(document.createElement("br"))
|
||||||
this.uploadDiv.appendChild(document.createTextNode(this.file.name))
|
this.uploadDiv.appendChild(document.createTextNode(this.file.name))
|
||||||
console.log(msg)
|
console.log(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let uploader = null
|
let uploader = new UploadManager(apiEndpoint + "/file", uploadsFinished)
|
||||||
let shareTitle = ""
|
let shareTitle = ""
|
||||||
let shareLink = ""
|
let shareLink = ""
|
||||||
|
|
||||||
function handleUploads(files) {
|
function handleUploads(files) {
|
||||||
if (uploader === null){
|
|
||||||
uploader = new UploadManager(apiEndpoint+"/file", uploadsFinished)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -101,8 +97,8 @@ function uploadsFinished() {
|
|||||||
let uploadLog = uploader.finishedUploads()
|
let uploadLog = uploader.finishedUploads()
|
||||||
|
|
||||||
if (uploadLog.length === 1) {
|
if (uploadLog.length === 1) {
|
||||||
shareTitle = "Download "+uploadLog[0].fileName+" here"
|
shareTitle = "Download " + uploadLog[0].fileName + " here"
|
||||||
shareLink = domainURL()+"/u/"+uploadLog[0].fileID
|
shareLink = domainURL() + "/u/" + uploadLog[0].fileID
|
||||||
|
|
||||||
showShareButtons()
|
showShareButtons()
|
||||||
} else if (uploadLog.length > 1) {
|
} else if (uploadLog.length > 1) {
|
||||||
@@ -111,13 +107,13 @@ function uploadsFinished() {
|
|||||||
createList(
|
createList(
|
||||||
title, true,
|
title, true,
|
||||||
).then(resp => {
|
).then(resp => {
|
||||||
console.log("Automatic list ID "+resp.id)
|
console.log("Automatic list ID " + resp.id)
|
||||||
shareTitle = "View "+title+" here"
|
shareTitle = "View " + title + " here"
|
||||||
shareLink = domainURL()+"/l/"+resp.id
|
shareLink = domainURL() + "/l/" + resp.id
|
||||||
|
|
||||||
showShareButtons()
|
showShareButtons()
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
alert("Failed to generate link. Please check your internet connection and try again.\nError: "+err)
|
alert("Failed to generate link. Please check your internet connection and try again.\nError: " + err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,10 +126,10 @@ function createList(title, anonymous) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return fetch(
|
return fetch(
|
||||||
apiEndpoint+"/list",
|
apiEndpoint + "/list",
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {"Content-Type": "application/json; charset=UTF-8"},
|
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
"title": title,
|
"title": title,
|
||||||
"anonymous": anonymous,
|
"anonymous": anonymous,
|
||||||
@@ -142,7 +138,7 @@ function createList(title, anonymous) {
|
|||||||
}
|
}
|
||||||
).then(resp => {
|
).then(resp => {
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
return Promise.reject("HTTP error: "+resp.status)
|
return Promise.reject("HTTP error: " + resp.status)
|
||||||
}
|
}
|
||||||
return resp.json()
|
return resp.json()
|
||||||
})
|
})
|
||||||
@@ -163,7 +159,7 @@ function showShareButtons() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function copyLink() {
|
function copyLink() {
|
||||||
if(copyText(shareLink)) {
|
if (copyText(shareLink)) {
|
||||||
console.log('Text copied')
|
console.log('Text copied')
|
||||||
document.querySelector("#btn_copy_link>span").textContent = "Copied!"
|
document.querySelector("#btn_copy_link>span").textContent = "Copied!"
|
||||||
document.getElementById("btn_copy_link").classList.add("button_highlight")
|
document.getElementById("btn_copy_link").classList.add("button_highlight")
|
||||||
@@ -177,35 +173,47 @@ function copyLink() {
|
|||||||
* Upload Handlers
|
* Upload Handlers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Relay click event to hidden file field
|
// Relay click event to hidden file field
|
||||||
document.getElementById("upload_file_button").onclick = function() {
|
document.getElementById("upload_file_button").onclick = () => {
|
||||||
document.getElementById("file_input_field").click()
|
document.getElementById("file_input_field").click()
|
||||||
}
|
}
|
||||||
document.getElementById("file_input_field").onchange = function(evt){
|
document.getElementById("file_input_field").onchange = e => {
|
||||||
// Start uploading the files async
|
// Start uploading the files async
|
||||||
window.setTimeout(handleUploads(evt.target.files), 1)
|
window.setTimeout(handleUploads(e.target.files), 1)
|
||||||
|
|
||||||
// This resets the file input field
|
// This resets the file input field
|
||||||
document.getElementById("file_input_field").nodeValue = ""
|
document.getElementById("file_input_field").nodeValue = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById("upload_text_button").onclick = function() {
|
document.getElementById("upload_text_button").onclick = () => {
|
||||||
window.location.href = '/t/'
|
window.location.href = '/t'
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Drag 'n Drop upload handlers
|
* Drag 'n Drop upload handlers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
document.ondragover = function(e) {
|
document.addEventListener("dragover", e => {
|
||||||
|
document.getElementById("file_drop_highlight").style.display = ""
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}
|
})
|
||||||
document.ondragenter = function(e) {
|
document.addEventListener("dragenter", e => {
|
||||||
|
document.getElementById("file_drop_highlight").style.display = ""
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}
|
})
|
||||||
document.addEventListener('drop', function(e){
|
document.addEventListener("dragleave", e => {
|
||||||
|
document.getElementById("file_drop_highlight").style.display = "none"
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
})
|
||||||
|
document.addEventListener("drop", e => {
|
||||||
|
document.getElementById("file_drop_highlight").style.display = "none"
|
||||||
|
|
||||||
if (e.dataTransfer && e.dataTransfer.files.length > 0) {
|
if (e.dataTransfer && e.dataTransfer.files.length > 0) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@@ -214,37 +222,41 @@ document.addEventListener('drop', function(e){
|
|||||||
window.setTimeout(handleUploads(e.dataTransfer.files), 1)
|
window.setTimeout(handleUploads(e.dataTransfer.files), 1)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
document.addEventListener("paste", e => {
|
||||||
|
if (e.clipboardData.files[0]) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
window.setTimeout(handleUploads(e.clipboardData.files), 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Share buttons
|
* Share buttons
|
||||||
*/
|
*/
|
||||||
|
|
||||||
document.getElementById("btn_social_share").addEventListener("click", function() {
|
document.getElementById("btn_social_share").addEventListener("click", () => {
|
||||||
window.navigator.share({
|
window.navigator.share({ title: "Pixeldrain", text: shareTitle, url: shareLink })
|
||||||
title: "Pixeldrain",
|
|
||||||
text: shareTitle,
|
|
||||||
url: shareLink
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
document.getElementById("btn_copy_link").addEventListener("click", function() {
|
document.getElementById("btn_copy_link").addEventListener("click", () => {
|
||||||
copyLink()
|
copyLink()
|
||||||
})
|
})
|
||||||
document.getElementById("btn_open_link").addEventListener("click", function() {
|
document.getElementById("btn_open_link").addEventListener("click", () => {
|
||||||
window.open(shareLink, '_blank')
|
window.open(shareLink, '_blank')
|
||||||
})
|
})
|
||||||
document.getElementById("btn_social_email").addEventListener("click", function() {
|
document.getElementById("btn_social_email").addEventListener("click", () => {
|
||||||
window.open('mailto:please@set.address?subject=File%20on%20pixeldrain&body=' + shareLink)
|
window.open('mailto:please@set.address?subject=File%20on%20pixeldrain&body=' + shareLink)
|
||||||
})
|
})
|
||||||
document.getElementById("btn_social_twitter").addEventListener("click", function() {
|
document.getElementById("btn_social_twitter").addEventListener("click", () => {
|
||||||
window.open('https://twitter.com/share?url=' + shareLink)
|
window.open('https://twitter.com/share?url=' + shareLink)
|
||||||
})
|
})
|
||||||
document.getElementById("btn_social_facebook").addEventListener("click", function() {
|
document.getElementById("btn_social_facebook").addEventListener("click", () => {
|
||||||
window.open('http://www.facebook.com/sharer.php?u=' + shareLink)
|
window.open('http://www.facebook.com/sharer.php?u=' + shareLink)
|
||||||
})
|
})
|
||||||
document.getElementById("btn_social_reddit").addEventListener("click", function() {
|
document.getElementById("btn_social_reddit").addEventListener("click", () => {
|
||||||
window.open('https://www.reddit.com/submit?url=' + shareLink)
|
window.open('https://www.reddit.com/submit?url=' + shareLink)
|
||||||
})
|
})
|
||||||
document.getElementById("btc_social_tumblr").addEventListener("click", function() {
|
document.getElementById("btn_social_tumblr").addEventListener("click", () => {
|
||||||
window.open('http://www.tumblr.com/share/link?url=' + shareLink)
|
window.open('http://www.tumblr.com/share/link?url=' + shareLink)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -253,18 +265,18 @@ document.getElementById("btc_social_tumblr").addEventListener("click", function(
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function renderListButton(apiURL, id, title, subtitle) {
|
function renderListButton(apiURL, id, title, subtitle) {
|
||||||
let btn = document.createElement("a")
|
let btn = document.createElement("a")
|
||||||
btn.classList = "file_button"
|
btn.classList = "file_button"
|
||||||
btn.href = "/l/"+id
|
btn.href = "/l/" + id
|
||||||
btn.target = "_blank"
|
btn.target = "_blank"
|
||||||
let thumbnail = document.createElement("img")
|
let thumbnail = document.createElement("img")
|
||||||
thumbnail.src = apiURL+"/list/"+id+"/thumbnail?width=80&height=80"
|
thumbnail.src = apiURL + "/list/" + id + "/thumbnail?width=80&height=80"
|
||||||
thumbnail.alt = title
|
thumbnail.alt = title
|
||||||
let titleSpan = document.createElement("span")
|
let titleSpan = document.createElement("span")
|
||||||
titleSpan.classList = "file_button_title"
|
titleSpan.classList = "file_button_title"
|
||||||
titleSpan.innerText = title
|
titleSpan.innerText = title
|
||||||
let br = document.createElement("br")
|
let br = document.createElement("br")
|
||||||
let subtitleSpan = document.createElement("span")
|
let subtitleSpan = document.createElement("span")
|
||||||
subtitleSpan.classList = "file_button_subtitle"
|
subtitleSpan.classList = "file_button_subtitle"
|
||||||
subtitleSpan.innerText = subtitle
|
subtitleSpan.innerText = subtitle
|
||||||
|
|
||||||
@@ -276,12 +288,12 @@ function renderListButton(apiURL, id, title, subtitle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create list button
|
// Create list button
|
||||||
document.getElementById("btn_create_list").addEventListener("click", function(evt) {
|
document.getElementById("btn_create_list").addEventListener("click", function (evt) {
|
||||||
let title = prompt(
|
let title = prompt(
|
||||||
"You are creating a list containing " + uploader.finishedUploads().length + " files.\n"
|
"You are creating a list containing " + uploader.finishedUploads().length + " files.\n"
|
||||||
+ "What do you want to call it?", "My New Album"
|
+ "What do you want to call it?", "My New Album"
|
||||||
)
|
)
|
||||||
if(title === null){
|
if (title === null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
createList(title, false).then(resp => {
|
createList(title, false).then(resp => {
|
||||||
@@ -289,11 +301,11 @@ document.getElementById("btn_create_list").addEventListener("click", function(ev
|
|||||||
renderListButton(
|
renderListButton(
|
||||||
apiEndpoint,
|
apiEndpoint,
|
||||||
resp.id,
|
resp.id,
|
||||||
domainURL()+'/l/'+resp.id,
|
domainURL() + '/l/' + resp.id,
|
||||||
"List creation finished!",
|
"List creation finished!",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
window.open('/l/'+resp.id, '_blank')
|
window.open('/l/' + resp.id, '_blank')
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
let div = document.createElement("div")
|
let div = document.createElement("div")
|
||||||
div.className = "file_button"
|
div.className = "file_button"
|
||||||
@@ -305,55 +317,55 @@ document.getElementById("btn_create_list").addEventListener("click", function(ev
|
|||||||
})
|
})
|
||||||
|
|
||||||
let btnCopyLinks = document.getElementById("btn_copy_links")
|
let btnCopyLinks = document.getElementById("btn_copy_links")
|
||||||
btnCopyLinks.addEventListener("click", function(){
|
btnCopyLinks.addEventListener("click", function () {
|
||||||
let text = ""
|
let text = ""
|
||||||
let uploads = uploader.finishedUploads()
|
let uploads = uploader.finishedUploads()
|
||||||
|
|
||||||
// Add the text to the textarea
|
// Add the text to the textarea
|
||||||
for (let i = 0; i < uploads.length; i++) {
|
for (let i = 0; i < uploads.length; i++) {
|
||||||
// Example: https://pixeldrain.com/u/abcd1234: Some_file.png
|
// Example: https://pixeldrain.com/u/abcd1234: Some_file.png
|
||||||
text += domainURL()+"/u/"+uploads[i].fileID+" "+uploads[i].fileName+"\n"
|
text += domainURL() + "/u/" + uploads[i].fileID + " " + uploads[i].fileName + "\n"
|
||||||
}
|
}
|
||||||
if (shareLink.includes("/l/")) {
|
if (shareLink.includes("/l/")) {
|
||||||
text += "\n"+shareLink+" All "+uploads.length+" files\n"
|
text += "\n" + shareLink + " All " + uploads.length + " files\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the selected text
|
// Copy the selected text
|
||||||
if(copyText(text)){
|
if (copyText(text)) {
|
||||||
btnCopyLinks.classList.add("button_highlight")
|
btnCopyLinks.classList.add("button_highlight")
|
||||||
btnCopyLinks.innerHTML = "Links copied to clipboard!"
|
btnCopyLinks.innerHTML = "Links copied to clipboard!"
|
||||||
}else{
|
} else {
|
||||||
btnCopyLinks.classList.add("button_red")
|
btnCopyLinks.classList.add("button_red")
|
||||||
btnCopyLinks.innerHTML = "Copying links failed"
|
btnCopyLinks.innerHTML = "Copying links failed"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let btnCopyBBCode = document.getElementById("btn_copy_bbcode")
|
let btnCopyBBCode = document.getElementById("btn_copy_bbcode")
|
||||||
btnCopyBBCode.addEventListener("click", function(){
|
btnCopyBBCode.addEventListener("click", function () {
|
||||||
let text = ""
|
let text = ""
|
||||||
let uploads = uploader.finishedUploads()
|
let uploads = uploader.finishedUploads()
|
||||||
|
|
||||||
// Add the text to the textarea
|
// Add the text to the textarea
|
||||||
for (let i = 0; i < uploads.length; i++) {
|
for (let i = 0; i < uploads.length; i++) {
|
||||||
// Example: [url=https://pixeldrain.com/u/abcd1234]Some_file.png[/url]
|
// Example: [url=https://pixeldrain.com/u/abcd1234]Some_file.png[/url]
|
||||||
text += "[url="+domainURL()+"/u/"+uploads[i].fileID+"]"+uploads[i].fileName+"[/url]\n"
|
text += "[url=" + domainURL() + "/u/" + uploads[i].fileID + "]" + uploads[i].fileName + "[/url]\n"
|
||||||
}
|
}
|
||||||
if (shareLink.includes("/l/")) {
|
if (shareLink.includes("/l/")) {
|
||||||
text += "\n[url="+shareLink+"]All "+uploads.length+" files[/url]\n"
|
text += "\n[url=" + shareLink + "]All " + uploads.length + " files[/url]\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the selected text
|
// Copy the selected text
|
||||||
if(copyText(text)){
|
if (copyText(text)) {
|
||||||
btnCopyBBCode.classList.add("button_highlight")
|
btnCopyBBCode.classList.add("button_highlight")
|
||||||
btnCopyBBCode.innerHTML = "BBCode copied to clipboard!"
|
btnCopyBBCode.innerHTML = "BBCode copied to clipboard!"
|
||||||
}else{
|
} else {
|
||||||
btnCopyBBCode.classList.add("button_red")
|
btnCopyBBCode.classList.add("button_red")
|
||||||
btnCopyBBCode.innerHTML = "Copying links failed"
|
btnCopyBBCode.innerHTML = "Copying links failed"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let btnCopyMarkdown = document.getElementById("btn_copy_markdown")
|
let btnCopyMarkdown = document.getElementById("btn_copy_markdown")
|
||||||
btnCopyMarkdown.addEventListener("click", function(){
|
btnCopyMarkdown.addEventListener("click", function () {
|
||||||
let text = ""
|
let text = ""
|
||||||
let uploads = uploader.finishedUploads()
|
let uploads = uploader.finishedUploads()
|
||||||
|
|
||||||
@@ -362,17 +374,17 @@ btnCopyMarkdown.addEventListener("click", function(){
|
|||||||
// Example: * [Some_file.png](https://pixeldrain.com/u/abcd1234)
|
// Example: * [Some_file.png](https://pixeldrain.com/u/abcd1234)
|
||||||
|
|
||||||
if (uploads.length > 1) { text += " * " }
|
if (uploads.length > 1) { text += " * " }
|
||||||
text += "["+uploads[i].fileName+"]("+domainURL()+"/u/"+uploads[i].fileID+")\n"
|
text += "[" + uploads[i].fileName + "](" + domainURL() + "/u/" + uploads[i].fileID + ")\n"
|
||||||
}
|
}
|
||||||
if (shareLink.includes("/l/")) {
|
if (shareLink.includes("/l/")) {
|
||||||
text += " * [All "+uploads.length+" files]("+shareLink+")\n"
|
text += " * [All " + uploads.length + " files](" + shareLink + ")\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the selected text
|
// Copy the selected text
|
||||||
if(copyText(text)){
|
if (copyText(text)) {
|
||||||
btnCopyMarkdown.classList.add("button_highlight")
|
btnCopyMarkdown.classList.add("button_highlight")
|
||||||
btnCopyMarkdown.innerHTML = "Markdown copied to clipboard!"
|
btnCopyMarkdown.innerHTML = "Markdown copied to clipboard!"
|
||||||
}else{
|
} else {
|
||||||
btnCopyMarkdown.classList.add("button_red")
|
btnCopyMarkdown.classList.add("button_red")
|
||||||
btnCopyMarkdown.innerHTML = "Copying links failed"
|
btnCopyMarkdown.innerHTML = "Copying links failed"
|
||||||
}
|
}
|
||||||
@@ -382,19 +394,35 @@ btnCopyMarkdown.addEventListener("click", function(){
|
|||||||
/*
|
/*
|
||||||
* Keyboard shortcuts
|
* Keyboard shortcuts
|
||||||
*/
|
*/
|
||||||
document.addEventListener("keydown", function(event){
|
document.addEventListener("keydown", e => {
|
||||||
if (event.ctrlKey || event.altKey || event.metaKey) {
|
if (e.ctrlKey || e.altKey || e.metaKey) {
|
||||||
return // prevent custom shortcuts from interfering with system shortcuts
|
return // prevent custom shortcuts from interfering with system shortcuts
|
||||||
}
|
}
|
||||||
if (event.keyCode === 67) { // c
|
if (e.key === "c") {
|
||||||
// Copy links to clipboard
|
|
||||||
document.getElementById("btn_copy_link").click()
|
document.getElementById("btn_copy_link").click()
|
||||||
} else if (event.keyCode === 85) { // u
|
} else if (e.key === "u") {
|
||||||
// Click the upload button
|
|
||||||
document.getElementById("file_input_field").click()
|
document.getElementById("file_input_field").click()
|
||||||
} else if (event.keyCode === 84) { // t
|
} else if (e.key === "t") {
|
||||||
// Click the text button
|
|
||||||
document.getElementById("upload_text_button").click()
|
document.getElementById("upload_text_button").click()
|
||||||
|
} else if (e.key === "o") {
|
||||||
|
document.getElementById("btn_open_link").click()
|
||||||
|
} else if (e.key === "l") {
|
||||||
|
document.getElementById("btn_create_list").click()
|
||||||
|
} else if (e.key === "e") {
|
||||||
|
document.getElementById("btn_social_email").click()
|
||||||
|
} else if (e.key === "w") {
|
||||||
|
document.getElementById("btn_social_twitter").click()
|
||||||
|
} else if (e.key === "f") {
|
||||||
|
document.getElementById("btn_social_facebook").click()
|
||||||
|
} else if (e.key === "r") {
|
||||||
|
document.getElementById("btn_social_reddit").click()
|
||||||
|
} else if (e.key === "m") {
|
||||||
|
document.getElementById("btn_social_tumblr").click()
|
||||||
|
} else if (e.key === "a") {
|
||||||
|
document.getElementById("btn_copy_links").click()
|
||||||
|
} else if (e.key === "d") {
|
||||||
|
document.getElementById("btn_copy_markdown").click()
|
||||||
|
} else if (e.key === "b") {
|
||||||
|
document.getElementById("btn_copy_bbcode").click()
|
||||||
}
|
}
|
||||||
console.log(event.keyCode)
|
|
||||||
})
|
})
|
||||||
|
@@ -10,8 +10,23 @@
|
|||||||
<div class="page_content">
|
<div class="page_content">
|
||||||
{{if and .Authenticated .User.IsAdmin}}
|
{{if and .Authenticated .User.IsAdmin}}
|
||||||
<div class="limit_width">
|
<div class="limit_width">
|
||||||
<a class="button" href="/admin/globals">Update global settings</a>
|
<h3>Actions</h3>
|
||||||
<a class="button" href="/admin/abuse">Block files</a>
|
</div>
|
||||||
|
<br/>
|
||||||
|
<a class="button" href="/admin/abuse">
|
||||||
|
<i class="icon">block</i>
|
||||||
|
Block files
|
||||||
|
</a>
|
||||||
|
<a class="button" href="/admin/abuse_reporters">
|
||||||
|
<i class="icon">report</i>
|
||||||
|
Manage abuse reporters
|
||||||
|
</a>
|
||||||
|
<a class="button" href="/admin/globals">
|
||||||
|
<i class="icon">edit</i>
|
||||||
|
Update global settings
|
||||||
|
</a>
|
||||||
|
<br/>
|
||||||
|
<div class="limit_width">
|
||||||
<h3>Bandwidth and views</h3>
|
<h3>Bandwidth and views</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="highlight_dark">
|
<div class="highlight_dark">
|
||||||
|
19
res/template/admin_abuse_reporters.html
Normal file
19
res/template/admin_abuse_reporters.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{{define "admin_abuse_reporters"}}<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
{{template "meta_tags" "Abuse reporters"}}
|
||||||
|
{{template "user_style" .}}
|
||||||
|
<script>window.api_endpoint = '{{.APIEndpoint}}';</script>
|
||||||
|
<link rel='stylesheet' href='/res/svelte/admin_abuse_reporters.css'>
|
||||||
|
<script defer src='/res/svelte/admin_abuse_reporters.js'></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{template "page_top" .}}
|
||||||
|
<h1>Abuse reporters</h1>
|
||||||
|
<div id="page_content" class="page_content"></div>
|
||||||
|
|
||||||
|
{{template "page_bottom" .}}
|
||||||
|
{{template "analytics"}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
@@ -186,7 +186,15 @@
|
|||||||
<a href="/click/7wy9gg2J?target=%2F%23pro" class="button button_highlight">Pixeldrain Pro: Only €2 per month</a>
|
<a href="/click/7wy9gg2J?target=%2F%23pro" class="button button_highlight">Pixeldrain Pro: Only €2 per month</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{ else }}
|
||||||
|
<!-- scrolling="no" is not allowed by the W3C, but overflow: hidden doesn't work in chrome, so I have no choice -->
|
||||||
|
<iframe class="sponsors_banner"
|
||||||
|
data-aa="73974"
|
||||||
|
src="//ad.a-ads.com/73974?size=728x90&background_color={{.Style.Layer1Color.RGB}}&text_color={{.Style.TextColor.RGB}}&title_color={{.Style.HighlightColor.RGB}}&title_hover_color={{.Style.HighlightColor.RGB}}&link_color={{.Style.HighlightColor.RGB}}&link_hover_color={{.Style.HighlightColor.RGB}}"
|
||||||
|
style="width:728px; height:90px; border:none; padding:0; overflow:hidden;"
|
||||||
|
scrolling="no">
|
||||||
|
</iframe>
|
||||||
|
{{ end }}
|
||||||
{{ else if not .Other.UserAdsEnabled }}
|
{{ else if not .Other.UserAdsEnabled }}
|
||||||
<div style="text-align: center; line-height: 1.3em; font-size: 13px;">
|
<div style="text-align: center; line-height: 1.3em; font-size: 13px;">
|
||||||
Thank you for supporting pixeldrain!
|
Thank you for supporting pixeldrain!
|
||||||
@@ -295,7 +303,7 @@
|
|||||||
{{ if eq .Other.AdType 5 }}
|
{{ if eq .Other.AdType 5 }}
|
||||||
<!-- AdMaven -->
|
<!-- AdMaven -->
|
||||||
<script data-cfasync="false" async src="//d227cncaprzd7y.cloudfront.net/?acncd=905608"></script>
|
<script data-cfasync="false" async src="//d227cncaprzd7y.cloudfront.net/?acncd=905608"></script>
|
||||||
{{ else if eq .Other.AdType 7 }}
|
{{ else if or (eq .Other.AdType 7) (eq .Other.AdType 8) (eq .Other.AdType 9) (eq .Other.AdType 10) (eq .Other.AdType 11) }}
|
||||||
<!-- PropellerAds -->
|
<!-- PropellerAds -->
|
||||||
<script>
|
<script>
|
||||||
// Load fires when the page is completely finished loading,
|
// Load fires when the page is completely finished loading,
|
||||||
|
@@ -125,63 +125,37 @@
|
|||||||
<img class="header_image" src="/res/img/header_orbitron_wide.png" alt="Header image">
|
<img class="header_image" src="/res/img/header_orbitron_wide.png" alt="Header image">
|
||||||
</picture>
|
</picture>
|
||||||
<br/>
|
<br/>
|
||||||
<div class="highlight_blue">
|
|
||||||
<div class="limit_width">
|
|
||||||
<p>
|
|
||||||
Hello! Due to
|
|
||||||
<a href="https://nypost.com/2021/02/14/hundreds-of-thousands-without-power-after-oregon-ice-storm/" target="_blank">an ice storm in Oregon</a>
|
|
||||||
the datacenter where pixeldrain is hosted in North America
|
|
||||||
was cut off from the internet and has lost power. This
|
|
||||||
caused an outage of a few hours last saturday (it happened
|
|
||||||
when I was out ice skating, so I didn't notice it until I
|
|
||||||
got back).
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
I am now redirecting all American (north and south) traffic
|
|
||||||
to the UK, the server there has more than enough capacity to
|
|
||||||
handle it all. Due to the long distance the website might
|
|
||||||
feel a bit slower.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Files which were uploaded to the server in Oregon shortly
|
|
||||||
before the outage will not be available until the server is
|
|
||||||
back up. Normally all files are backed up in Finland, but
|
|
||||||
this process takes a while. Due to the abruptness of the
|
|
||||||
outage a lot of files could not make it over. When the
|
|
||||||
server comes back online your files will be downloadable
|
|
||||||
again.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
I will post updates <a href="https://twitter.com/Fornax96"
|
|
||||||
target="_blank">on Twitter</a> when the situation changes.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="page_content" style="padding-top: 0; margin-bottom: 100px;">
|
<div class="page_content" style="padding-top: 0; margin-bottom: 100px;">
|
||||||
<div id="instruction_1" class="instruction_highlight" style="margin-top: 0;"><div class="limit_width">
|
<div id="instruction_1" class="instruction_highlight" style="margin-top: 0;"><div class="limit_width">
|
||||||
<span class="big_number">1</span>
|
<span class="big_number">1</span>
|
||||||
<span class="instruction_text">Select files to upload</span>
|
<span class="instruction_text">Select files to upload</span>
|
||||||
<br/>
|
<br/> You can also drop files on this page from your file
|
||||||
You can also drop files anywhere on this page from your file
|
manager or paste an image from your clipboard
|
||||||
manager
|
|
||||||
</div></div>
|
</div></div>
|
||||||
<input id="file_input_field" type="file" name="file" multiple="multiple"/>
|
<input id="file_input_field" type="file" name="file" multiple="multiple"/>
|
||||||
<button id="upload_file_button" class="big_button button_highlight">
|
<button id="upload_file_button" class="big_button button_highlight">
|
||||||
<i class="icon small">cloud_upload</i>
|
<i class="icon small">cloud_upload</i>
|
||||||
<u>U</u>pload Files</button>
|
<u>U</u>pload Files
|
||||||
|
</button>
|
||||||
<button id="upload_text_button" class="big_button button_highlight">
|
<button id="upload_text_button" class="big_button button_highlight">
|
||||||
<i class="icon small">text_fields</i>
|
<i class="icon small">text_fields</i>
|
||||||
Upload <u>T</u>ext</button>
|
Upload <u>T</u>ext
|
||||||
|
</button>
|
||||||
<br/>
|
<br/>
|
||||||
<p>
|
<p>
|
||||||
By uploading files to pixeldrain you acknowledge and accept our
|
By uploading files to pixeldrain you acknowledge and accept our
|
||||||
<a href="/about#content-policy">content policy</a>.
|
<a href="/about#content-policy">content policy</a>.
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
<div id="instruction_2" class="instruction_highlight">
|
<div id="instruction_2" class="instruction_highlight">
|
||||||
<div class="limit_width">
|
<div class="limit_width">
|
||||||
<span class="big_number">2</span><span class="instruction_text">Wait for the files to finish uploading</span>
|
<span class="big_number">2</span><span class="instruction_text">Wait for the files to finish uploading</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="file_drop_highlight" class="highlight_green" style="display: none;">
|
||||||
|
Gimme gimme gimme!<br/>
|
||||||
|
Drop your files to upload them
|
||||||
|
</div>
|
||||||
<div id="uploads_queue"></div>
|
<div id="uploads_queue"></div>
|
||||||
|
|
||||||
<div id="instruction_3" class="instruction_highlight">
|
<div id="instruction_3" class="instruction_highlight">
|
||||||
@@ -197,30 +171,30 @@
|
|||||||
{{template `copy.svg` .}}<br/><span><u>C</u>opy link</span>
|
{{template `copy.svg` .}}<br/><span><u>C</u>opy link</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="btn_open_link" class="social_button" style="display: inline-block">
|
<button id="btn_open_link" class="social_button" style="display: inline-block">
|
||||||
{{template `open_in_new.svg` .}}<br/><span>Open link</span>
|
{{template `open_in_new.svg` .}}<br/><span><u>O</u>pen link</span>
|
||||||
</button>
|
</button>
|
||||||
<div id="social_buttons" style="display: inline-block">
|
<div id="social_buttons" style="display: inline-block">
|
||||||
<button id="btn_social_email" class="social_button">
|
<button id="btn_social_email" class="social_button">
|
||||||
{{template `email.svg` .}}<br/>E-Mail
|
{{template `email.svg` .}}<br/><u>E</u>-Mail
|
||||||
</button>
|
</button>
|
||||||
<button id="btn_social_twitter" class="social_button">
|
<button id="btn_social_twitter" class="social_button">
|
||||||
{{template `twitter.svg` .}}<br/>Twitter
|
{{template `twitter.svg` .}}<br/>T<u>w</u>itter
|
||||||
</button>
|
</button>
|
||||||
<button id="btn_social_facebook" class="social_button">
|
<button id="btn_social_facebook" class="social_button">
|
||||||
{{template `facebook.svg` .}}<br/>Facebook
|
{{template `facebook.svg` .}}<br/><u>F</u>acebook
|
||||||
</button>
|
</button>
|
||||||
<button id="btn_social_reddit" class="social_button">
|
<button id="btn_social_reddit" class="social_button">
|
||||||
{{template `reddit.svg` .}}<br/>Reddit
|
{{template `reddit.svg` .}}<br/><u>R</u>eddit
|
||||||
</button>
|
</button>
|
||||||
<button id="btc_social_tumblr" class="social_button">
|
<button id="btn_social_tumblr" class="social_button">
|
||||||
{{template `tumblr.svg` .}}<br/>Tumblr
|
{{template `tumblr.svg` .}}<br/>Tu<u>m</u>blr
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
<button id="btn_create_list"><i class="icon">list</i> Create list with uploaded files</button>
|
<button id="btn_create_list"><i class="icon">list</i> Create <u>l</u>ist with uploaded files</button>
|
||||||
<button id="btn_copy_links"><i class="icon">content_copy</i> Copy all links to clipboard</button>
|
<button id="btn_copy_links"><i class="icon">content_copy</i> Copy <u>a</u>ll links to clipboard</button>
|
||||||
<button id="btn_copy_markdown"><i class="icon">content_copy</i> Copy markdown to clipboard</button>
|
<button id="btn_copy_markdown"><i class="icon">content_copy</i> Copy mark<u>d</u>own to clipboard</button>
|
||||||
<button id="btn_copy_bbcode"><i class="icon">content_copy</i> Copy BBCode to clipboard</button>
|
<button id="btn_copy_bbcode"><i class="icon">content_copy</i> Copy <u>B</u>BCode to clipboard</button>
|
||||||
<br/>
|
<br/>
|
||||||
<div id="created_lists"></div>
|
<div id="created_lists"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -32,6 +32,7 @@ export default [
|
|||||||
"filesystem",
|
"filesystem",
|
||||||
"modal",
|
"modal",
|
||||||
"user_buckets",
|
"user_buckets",
|
||||||
|
"admin_abuse_reporters",
|
||||||
].map((name, index) => ({
|
].map((name, index) => ({
|
||||||
input: `src/${name}.js`,
|
input: `src/${name}.js`,
|
||||||
output: {
|
output: {
|
||||||
|
8
svelte/src/admin_abuse_reporters.js
Normal file
8
svelte/src/admin_abuse_reporters.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import App from './admin_abuse_reporters/AbuseReporters.svelte';
|
||||||
|
|
||||||
|
const app = new App({
|
||||||
|
target: document.getElementById("page_content"),
|
||||||
|
props: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app;
|
182
svelte/src/admin_abuse_reporters/AbuseReporters.svelte
Normal file
182
svelte/src/admin_abuse_reporters/AbuseReporters.svelte
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { formatDate, formatDuration } from "../util/Formatting.svelte";
|
||||||
|
import Spinner from "../util/Spinner.svelte";
|
||||||
|
|
||||||
|
let loading = true
|
||||||
|
let reporters = []
|
||||||
|
|
||||||
|
let creating = false
|
||||||
|
let new_reporter_email
|
||||||
|
let new_reporter_name
|
||||||
|
let new_reporter_type = "individual"
|
||||||
|
|
||||||
|
const get_reporters = async () => {
|
||||||
|
loading = true;
|
||||||
|
try {
|
||||||
|
const resp = await fetch(window.api_endpoint+"/admin/abuse_reporter");
|
||||||
|
if(resp.status >= 400) {
|
||||||
|
throw new Error(resp.text());
|
||||||
|
}
|
||||||
|
reporters = await resp.json();
|
||||||
|
} catch (err) {
|
||||||
|
alert(err);
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const create_reporter = async () => {
|
||||||
|
if (!new_reporter_email.value) {
|
||||||
|
alert("Please enter an e-mail address!")
|
||||||
|
return
|
||||||
|
} else if (!new_reporter_name.value) {
|
||||||
|
alert("Please enter a name!")
|
||||||
|
return
|
||||||
|
} else if (!new_reporter_type) {
|
||||||
|
alert("Please enter a type!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const form = new FormData()
|
||||||
|
form.append("email", new_reporter_email.value)
|
||||||
|
form.append("name", new_reporter_name.value)
|
||||||
|
form.append("type", new_reporter_type)
|
||||||
|
|
||||||
|
const resp = await fetch(
|
||||||
|
window.api_endpoint+"/admin/abuse_reporter",
|
||||||
|
{ method: "POST", body: form }
|
||||||
|
);
|
||||||
|
if(resp.status >= 400) {
|
||||||
|
throw new Error(await resp.text());
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
alert("Failed to add abuse reporter! "+err)
|
||||||
|
}
|
||||||
|
|
||||||
|
creating = false
|
||||||
|
get_reporters();
|
||||||
|
}
|
||||||
|
|
||||||
|
const delete_reporter = async (email) => {
|
||||||
|
if (!confirm("Delete this reporter address?\n\n"+email)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch(
|
||||||
|
window.api_endpoint+"/admin/abuse_reporter/"+encodeURI(email),
|
||||||
|
{ method: "DELETE" }
|
||||||
|
);
|
||||||
|
if(resp.status >= 400) {
|
||||||
|
throw new Error(await resp.text());
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
alert("Failed to delete abuse reporter! "+err)
|
||||||
|
}
|
||||||
|
|
||||||
|
get_reporters();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(get_reporters);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{#if loading}
|
||||||
|
<div class="spinner_container">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="limit_width">
|
||||||
|
<div class="toolbar" style="text-align: left;">
|
||||||
|
<a class="button" href="/admin">
|
||||||
|
<i class="icon">arrow_back</i> Return to admin panel
|
||||||
|
</a>
|
||||||
|
<div class="toolbar_spacer"></div>
|
||||||
|
<button class:button_highlight={creating} on:click={() => {creating = !creating}}>
|
||||||
|
<i class="icon">create</i> Add abuse reporter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{#if creating}
|
||||||
|
<div class="highlight_light">
|
||||||
|
<form on:submit|preventDefault={create_reporter}>
|
||||||
|
<table class="form">
|
||||||
|
<tr>
|
||||||
|
<td>E-mail address</td>
|
||||||
|
<td><input type="text" bind:this={new_reporter_email}/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Name</td>
|
||||||
|
<td><input type="text" bind:this={new_reporter_name} value="Anonymous tip"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Type</td>
|
||||||
|
<td>
|
||||||
|
<input id="reporter_type_individual" name="reporter_type" type="radio" bind:group={new_reporter_type} value="individual" />
|
||||||
|
<label for="reporter_type_individual">Individual</label>
|
||||||
|
<br/>
|
||||||
|
<input id="reporter_type_org" name="reporter_type" type="radio" bind:group={new_reporter_type} value="org" />
|
||||||
|
<label for="reporter_type_org">Organisation</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<button class="button_highlight" type="submit" style="float: right;">
|
||||||
|
<i class="icon">save</i> Save
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<table style="text-align: left;">
|
||||||
|
<tr>
|
||||||
|
<td>E-mail</td>
|
||||||
|
<td>Name</td>
|
||||||
|
<td>Blocked</td>
|
||||||
|
<td>Type</td>
|
||||||
|
<td>Last used</td>
|
||||||
|
<td>Created</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
{#each reporters as reporter}
|
||||||
|
<tr>
|
||||||
|
<td>{reporter.email}</td>
|
||||||
|
<td>{reporter.name}</td>
|
||||||
|
<td>{reporter.files_blocked}</td>
|
||||||
|
<td>{reporter.type}</td>
|
||||||
|
<td>{formatDate(reporter.last_used, true, true, false)}</td>
|
||||||
|
<td>{formatDate(reporter.created, false, false, false)}</td>
|
||||||
|
<td>
|
||||||
|
<button on:click|preventDefault={() => {delete_reporter(reporter.email)}} class="button button_red">
|
||||||
|
<i class="icon">delete</i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.spinner_container {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
height: 100px;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.toolbar > * { flex: 0 0 auto; }
|
||||||
|
.toolbar_spacer { flex: 1 1 auto; }
|
||||||
|
</style>
|
@@ -1,178 +1,181 @@
|
|||||||
<script>
|
<script>
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
let dispatch = createEventDispatcher();
|
let dispatch = createEventDispatcher();
|
||||||
|
|
||||||
export let bucket_id;
|
export let bucket_id;
|
||||||
export let target_dir;
|
export let target_dir;
|
||||||
|
|
||||||
let upload_jobs = [];
|
let upload_jobs = [];
|
||||||
let upload_threads = 0;
|
let upload_threads = 0;
|
||||||
let max_upload_threads = 3;
|
let max_upload_threads = 3;
|
||||||
|
|
||||||
// Adds files to the upload queue. The file_list parameter needs to be of type
|
// Adds files to the upload queue. The file_list parameter needs to be of type
|
||||||
// FileList. Upload will also create the necessary directories to place nested
|
// FileList. Upload will also create the necessary directories to place nested
|
||||||
// files in.
|
// files in.
|
||||||
export const upload = (file_list) => {
|
export const upload = (file_list) => {
|
||||||
for (let i = 0; i < file_list.length; i++) {
|
console.log(file_list)
|
||||||
upload_jobs.push({
|
|
||||||
file: file_list[i],
|
for (let i = 0; i < file_list.length; i++) {
|
||||||
progress: 0,
|
upload_jobs.push({
|
||||||
target_dir: target_dir.valueOf(),
|
file: file_list[i],
|
||||||
uploading: false,
|
progress: 0,
|
||||||
finished: false,
|
target_dir: target_dir.valueOf(),
|
||||||
tries: 0,
|
uploading: false,
|
||||||
});
|
finished: false,
|
||||||
|
tries: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This updates the UI
|
||||||
|
upload_jobs = upload_jobs;
|
||||||
|
|
||||||
|
while (upload_threads < max_upload_threads) {
|
||||||
|
upload_threads++;
|
||||||
|
setTimeout(upload_file, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploads_finished = () => {
|
||||||
|
dispatch("finished");
|
||||||
|
};
|
||||||
|
|
||||||
|
const upload_file = () => {
|
||||||
|
let job = null;
|
||||||
|
for (let i = 0; i < upload_jobs.length; i++) {
|
||||||
|
// If a file is done we remove it from the array
|
||||||
|
if (upload_jobs[i].progress >= 1) {
|
||||||
|
upload_jobs.splice(i, 1);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This updates the UI
|
if (
|
||||||
upload_jobs = upload_jobs;
|
upload_jobs[i].uploading === false &&
|
||||||
|
upload_jobs[i].finished === false
|
||||||
while (upload_threads < max_upload_threads) {
|
) {
|
||||||
upload_threads++;
|
job = upload_jobs[i];
|
||||||
setTimeout(upload_file, 1);
|
job.uploading = true;
|
||||||
|
upload_jobs = upload_jobs;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
if (job === null) {
|
||||||
|
upload_threads--;
|
||||||
|
|
||||||
const uploads_finished = () => {
|
if (upload_threads === 0) {
|
||||||
dispatch("finished");
|
uploads_finished();
|
||||||
};
|
|
||||||
|
|
||||||
const upload_file = () => {
|
|
||||||
let job = null;
|
|
||||||
for (let i = 0; i < upload_jobs.length; i++) {
|
|
||||||
// If a file is done we remove it from the array
|
|
||||||
if (upload_jobs[i].progress >= 1) {
|
|
||||||
upload_jobs.splice(i, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
upload_jobs[i].uploading === false &&
|
|
||||||
upload_jobs[i].finished === false
|
|
||||||
) {
|
|
||||||
job = upload_jobs[i];
|
|
||||||
job.uploading = true;
|
|
||||||
upload_jobs = upload_jobs;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (job === null) {
|
return;
|
||||||
upload_threads--;
|
}
|
||||||
|
|
||||||
if (upload_threads === 0) {
|
console.log(job);
|
||||||
uploads_finished();
|
|
||||||
}
|
let form = new FormData();
|
||||||
|
form.append("type", "file");
|
||||||
|
form.append("file", job.file);
|
||||||
|
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
xhr.open(
|
||||||
|
"POST",
|
||||||
|
"/api/filesystem/" +
|
||||||
|
bucket_id +
|
||||||
|
encodeURIComponent(job.target_dir + "/" + job.file.name),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
xhr.timeout = 21600000; // 6 hours, to account for slow connections
|
||||||
|
|
||||||
|
// Report progress updates back to the caller
|
||||||
|
xhr.upload.addEventListener("progress", (evt) => {
|
||||||
|
if (evt.lengthComputable) {
|
||||||
|
job.progress = evt.loaded / evt.total;
|
||||||
|
upload_jobs = upload_jobs;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
xhr.onreadystatechange = () => {
|
||||||
|
// readystate 4 means the upload is done
|
||||||
|
if (xhr.readyState !== 4) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(job);
|
if (xhr.status >= 100 && xhr.status < 400) {
|
||||||
|
// Request is a success
|
||||||
|
|
||||||
let form = new FormData();
|
// Finish the upload job
|
||||||
form.append("type", "file");
|
job.uploading = false;
|
||||||
form.append("file", job.file);
|
job.finished = true;
|
||||||
|
upload_file();
|
||||||
let xhr = new XMLHttpRequest();
|
} else if (xhr.status >= 400) {
|
||||||
xhr.open(
|
// Request failed
|
||||||
"POST",
|
console.log(
|
||||||
"/api/filesystem/" +
|
"Upload error. status: " +
|
||||||
bucket_id +
|
xhr.status +
|
||||||
encodeURIComponent(job.target_dir + "/" + job.file.name),
|
" response: " +
|
||||||
true
|
xhr.response
|
||||||
);
|
);
|
||||||
xhr.timeout = 21600000; // 6 hours, to account for slow connections
|
let resp = JSON.parse(xhr.response);
|
||||||
|
if (job.tries === 3) {
|
||||||
// Report progress updates back to the caller
|
// Upload failed
|
||||||
xhr.upload.addEventListener("progress", (evt) => {
|
|
||||||
if (evt.lengthComputable) {
|
|
||||||
job.progress = evt.loaded / evt.total;
|
|
||||||
upload_jobs = upload_jobs;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
xhr.onreadystatechange = () => {
|
|
||||||
// readystate 4 means the upload is done
|
|
||||||
if (xhr.readyState !== 4) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (xhr.status >= 100 && xhr.status < 400) {
|
|
||||||
// Request is a success
|
|
||||||
|
|
||||||
// Finish the upload job
|
|
||||||
job.uploading = false;
|
|
||||||
job.finished = true;
|
|
||||||
upload_file();
|
|
||||||
} else if (xhr.status >= 400) {
|
|
||||||
// Request failed
|
|
||||||
console.log(
|
|
||||||
"Upload error. status: " +
|
|
||||||
xhr.status +
|
|
||||||
" response: " +
|
|
||||||
xhr.response
|
|
||||||
);
|
|
||||||
let resp = JSON.parse(xhr.response);
|
|
||||||
if (job.tries === 3) {
|
|
||||||
// Upload failed
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// Try again
|
|
||||||
job.tries++;
|
|
||||||
job.uploading = false;
|
|
||||||
job.finished = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sleep the upload thread for 5 seconds
|
|
||||||
setTimeout(upload_file, 5000);
|
|
||||||
} else {
|
} else {
|
||||||
// Request did not arrive
|
// Try again
|
||||||
if (job.tries === 3) {
|
job.tries++;
|
||||||
// Upload failed
|
job.uploading = false;
|
||||||
alert("upload failed " + xhr.responseText);
|
job.finished = false;
|
||||||
job.uploading = false;
|
|
||||||
job.finished = false;
|
|
||||||
} else {
|
|
||||||
// Try again
|
|
||||||
job.tries++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sleep the upload thread for 5 seconds
|
|
||||||
setTimeout(upload_file, 5000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
upload_jobs = upload_jobs;
|
// Sleep the upload thread for 5 seconds
|
||||||
};
|
setTimeout(upload_file, 5000);
|
||||||
xhr.send(form);
|
} else {
|
||||||
};
|
// Request did not arrive
|
||||||
|
if (job.tries === 3) {
|
||||||
|
// Upload failed
|
||||||
|
alert("upload failed " + xhr.responseText);
|
||||||
|
job.uploading = false;
|
||||||
|
job.finished = false;
|
||||||
|
} else {
|
||||||
|
// Try again
|
||||||
|
job.tries++;
|
||||||
|
}
|
||||||
|
|
||||||
// File input dialog handling
|
// Sleep the upload thread for 5 seconds
|
||||||
let file_input;
|
setTimeout(upload_file, 5000);
|
||||||
export const picker = () => {
|
|
||||||
file_input.click();
|
|
||||||
};
|
|
||||||
const file_input_change = (e) => {
|
|
||||||
upload(e.target.files);
|
|
||||||
file_input.nodeValue = "";
|
|
||||||
};
|
|
||||||
|
|
||||||
// Drag and drop upload
|
|
||||||
let hidden = true;
|
|
||||||
const dragover = (e) => {
|
|
||||||
hidden = false;
|
|
||||||
};
|
|
||||||
const dragleave = (e) => {
|
|
||||||
hidden = true;
|
|
||||||
};
|
|
||||||
const drop = (e) => {
|
|
||||||
hidden = true;
|
|
||||||
upload(e.dataTransfer.files);
|
|
||||||
};
|
|
||||||
const paste = (e) => {
|
|
||||||
if (e.clipboardData.files[0]) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
console.log(e.clipboardData.files[0].getAsFile());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upload_jobs = upload_jobs;
|
||||||
};
|
};
|
||||||
|
xhr.send(form);
|
||||||
|
};
|
||||||
|
|
||||||
|
// File input dialog handling
|
||||||
|
let file_input;
|
||||||
|
export const picker = () => {
|
||||||
|
file_input.click();
|
||||||
|
};
|
||||||
|
const file_input_change = (e) => {
|
||||||
|
upload(e.target.files);
|
||||||
|
file_input.nodeValue = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Drag and drop upload
|
||||||
|
let hidden = true;
|
||||||
|
const dragover = (e) => {
|
||||||
|
hidden = false;
|
||||||
|
};
|
||||||
|
const dragleave = (e) => {
|
||||||
|
hidden = true;
|
||||||
|
};
|
||||||
|
const drop = (e) => {
|
||||||
|
hidden = true;
|
||||||
|
upload(e.dataTransfer.files);
|
||||||
|
};
|
||||||
|
const paste = (e) => {
|
||||||
|
if (e.clipboardData.files[0]) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
console.log(e.clipboardData.files[0]);
|
||||||
|
upload(e.clipboardData.files)
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@@ -1,42 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { fs_create_bucket } from "../filesystem/FilesystemAPI.svelte";
|
|
||||||
|
|
||||||
let name
|
|
||||||
|
|
||||||
const submit = async () => {
|
|
||||||
if (!name.value) {
|
|
||||||
alert("Please enter a name!")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let bucket = await fs_create_bucket(name.value)
|
|
||||||
console.log(bucket)
|
|
||||||
} catch (err) {
|
|
||||||
alert("Failed to create bucket! "+err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="highlight_light">
|
|
||||||
<form on:submit|preventDefault={submit}>
|
|
||||||
<table class="form">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
Name
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" bind:this={name}/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">
|
|
||||||
<button class="button_highlight" type="submit" style="float: right;">
|
|
||||||
<i class="icon">save</i> Save
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@@ -1,5 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { fs_delete_bucket } from "../filesystem/FilesystemAPI.svelte";
|
import { fs_delete_bucket } from "../filesystem/FilesystemAPI.svelte";
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
let dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let bucket
|
export let bucket
|
||||||
let details_hidden = true
|
let details_hidden = true
|
||||||
@@ -24,6 +26,8 @@ const delete_bucket = async () => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert("Failed to delete bucket! "+err)
|
alert("Failed to delete bucket! "+err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispatch("refresh");
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
@@ -1,17 +1,17 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import Bucket from "./Bucket.svelte";
|
import UserBucket from "./UserBucket.svelte";
|
||||||
import Spinner from "../util/Spinner.svelte";
|
import Spinner from "../util/Spinner.svelte";
|
||||||
import { fs_get_buckets } from "../filesystem/FilesystemAPI.svelte";
|
import { fs_get_buckets, fs_create_bucket } from "../filesystem/FilesystemAPI.svelte";
|
||||||
import NewBucket from "./NewBucket.svelte";
|
|
||||||
|
|
||||||
let loading = true;
|
let loading = true
|
||||||
let buckets = [];
|
let buckets = []
|
||||||
|
|
||||||
let new_bucket;
|
let creating_bucket = false
|
||||||
let creating_bucket = false;
|
let new_bucket_name
|
||||||
|
|
||||||
const get_buckets = async () => {
|
const get_buckets = async () => {
|
||||||
|
loading = true;
|
||||||
try {
|
try {
|
||||||
let resp = await fs_get_buckets();
|
let resp = await fs_get_buckets();
|
||||||
buckets = resp.buckets;
|
buckets = resp.buckets;
|
||||||
@@ -22,6 +22,23 @@ const get_buckets = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const create_bucket = async () => {
|
||||||
|
if (!new_bucket_name.value) {
|
||||||
|
alert("Please enter a name!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let bucket = await fs_create_bucket(new_bucket_name.value)
|
||||||
|
console.log(bucket)
|
||||||
|
} catch (err) {
|
||||||
|
alert("Failed to create bucket! "+err)
|
||||||
|
}
|
||||||
|
|
||||||
|
creating_bucket = false
|
||||||
|
get_buckets();
|
||||||
|
}
|
||||||
|
|
||||||
onMount(get_buckets);
|
onMount(get_buckets);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -42,29 +59,40 @@ onMount(get_buckets);
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{#if creating_bucket}
|
{#if creating_bucket}
|
||||||
<NewBucket bind:this={new_bucket}></NewBucket>
|
<div class="highlight_light">
|
||||||
|
<form on:submit|preventDefault={create_bucket}>
|
||||||
|
<table class="form">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Name
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" bind:this={new_bucket_name}/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<button class="button_highlight" type="submit" style="float: right;">
|
||||||
|
<i class="icon">save</i> Save
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<h2>Persistent buckets</h2>
|
|
||||||
<p>
|
|
||||||
These buckets don't expire, but have limited storage space and
|
|
||||||
bandwidth. Their limits can be raised by buying a subscription.
|
|
||||||
</p>
|
|
||||||
{#each buckets as bucket}
|
{#each buckets as bucket}
|
||||||
<Bucket bucket={bucket}></Bucket>
|
<UserBucket bucket={bucket} on:refresh={get_buckets}></UserBucket>
|
||||||
{/each}
|
{/each}
|
||||||
<br/>
|
|
||||||
<h2>Temporary buckets</h2>
|
|
||||||
<p>
|
|
||||||
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.spinner_container {
|
.spinner_container {
|
||||||
display: inline-block;
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
@@ -50,15 +50,15 @@ func adType() int {
|
|||||||
switch i := rand.Intn(20); i {
|
switch i := rand.Intn(20); i {
|
||||||
case 0, 1: // 10%
|
case 0, 1: // 10%
|
||||||
return amarulaSolutions
|
return amarulaSolutions
|
||||||
case 2: // 5%
|
case 2: // 5%, also shows propellerads
|
||||||
return pdpro1
|
return pdpro1
|
||||||
case 3: // 5%
|
case 3: // 5%, also shows propellerads
|
||||||
return pdpro2
|
return pdpro2
|
||||||
case 4: // 5%
|
case 4: // 5%, also shows propellerads
|
||||||
return pdpro3
|
return pdpro3
|
||||||
case 5: // 5%
|
case 5: // 5%, also shows propellerads
|
||||||
return pdpro4
|
return pdpro4
|
||||||
default: // 70%
|
default: // 70%, also shows a-ads
|
||||||
return propellerAds
|
return propellerAds
|
||||||
// default:
|
// default:
|
||||||
// panic(fmt.Errorf("random number generator returned unrecognised number: %d", i))
|
// panic(fmt.Errorf("random number generator returned unrecognised number: %d", i))
|
||||||
|
@@ -161,11 +161,12 @@ func New(
|
|||||||
{PST, "knoxfs_activate" /* */, wc.serveForm(wc.knoxfsLinkForm, true)},
|
{PST, "knoxfs_activate" /* */, wc.serveForm(wc.knoxfsLinkForm, true)},
|
||||||
|
|
||||||
// Admin settings
|
// Admin settings
|
||||||
{GET, "admin" /* */, wc.serveTemplate("admin_panel", true)},
|
{GET, "admin" /* */, wc.serveTemplate("admin_panel", true)},
|
||||||
{GET, "admin/globals" /**/, wc.serveForm(wc.adminGlobalsForm, true)},
|
{GET, "admin/globals" /* */, wc.serveForm(wc.adminGlobalsForm, true)},
|
||||||
{PST, "admin/globals" /**/, wc.serveForm(wc.adminGlobalsForm, true)},
|
{PST, "admin/globals" /* */, wc.serveForm(wc.adminGlobalsForm, true)},
|
||||||
{GET, "admin/abuse" /* */, wc.serveForm(wc.adminAbuseForm, true)},
|
{GET, "admin/abuse" /* */, wc.serveForm(wc.adminAbuseForm, true)},
|
||||||
{PST, "admin/abuse" /* */, wc.serveForm(wc.adminAbuseForm, true)},
|
{PST, "admin/abuse" /* */, wc.serveForm(wc.adminAbuseForm, true)},
|
||||||
|
{GET, "admin/abuse_reporters" /**/, wc.serveTemplate("admin_abuse_reporters", true)},
|
||||||
|
|
||||||
// Advertising related
|
// Advertising related
|
||||||
{GET, "click/:id" /* */, wc.serveAdClick},
|
{GET, "click/:id" /* */, wc.serveAdClick},
|
||||||
|
Reference in New Issue
Block a user