Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

Mass edit mod api JS (done) (#868)

* Mass Edit MOD api JS (WIP)

In continuity with the mass edit mod api, this is the javascript use of
it.
##What does it do?
* Delete of multiple torrents on index/search
* Category change of multiple torrents
* Change of owner of multiple torrents
* Lock & delete of multiple torrents

##How?
* New toolbar only visible for mods
* Checkboxes added only for mods
* Selection and click on the button in toolbar
* Nothing is submitted, you have to review the changes in a modal window
listing them.
* Then the ajax queries are initialized one at a time with a progression
bar
* You can always at any moment delete entries from the queuing list

* Improved progress bar

* Deleting part almost done

Improved modal design
All dom interactions should be done
Prepared Query for only one callback
Improved Modal to keep a link to the active modal

* Finished =D

Added some translation string

* Forgot the refreshing of the page

Just an option that can be disabled by making refreshTimeout to 0
Cette révision appartient à :
akuma06 2017-06-02 04:51:44 +02:00 révisé par ewhal
Parent c4511f7238
révision e62ebb05ba
8 fichiers modifiés avec 832 ajouts et 9 suppressions

Voir le fichier

@ -600,6 +600,12 @@ div.profile-content.box > nav > ul > li {
height: 13px;
width: 13px;
}
.trash-icon {
width: 16px;
height: 16px;
display: inline-block;
background-image: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjE2cHgiIGhlaWdodD0iMTZweCI+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTQ2NS40MjMsNDguMjQxaC0xMzcuNjFWMjMuOTU1QzMyNy44MTMsMTAuNzQ2LDMxNy4wODIsMCwzMDMuODkzLDBoLTk1Ljc4NWMtMTMuMTksMC0yMy45MiwxMC43NDYtMjMuOTIsMjMuOTU1VjQ4LjI0ICAgIEg0Ni41NzdjLTYuNjU1LDAtMTIuMDQ5LDUuMzk0LTEyLjA0OSwxMi4wNDljMCw2LjY1NSw1LjM5NCwxMi4wNDksMTIuMDQ5LDEyLjA0OWgyMi4zMzJsMTUuMjI4LDM5Ni4zOTYgICAgQzg1LjA2OSw0OTIuOTk1LDEwNC44MTgsNTEyLDEyOS4wOTksNTEyaDI1My44MDRjMjQuMjgxLDAsNDQuMDMtMTkuMDA2LDQ0Ljk2LTQzLjI2N2wxNS4yMjgtMzk2LjM5NmgyMi4zMzIgICAgYzYuNjUzLDAsMTIuMDQ5LTUuMzk0LDEyLjA0OS0xMi4wNDlDNDc3LjQ3Miw1My42MzUsNDcyLjA3OCw0OC4yNDEsNDY1LjQyMyw0OC4yNDF6IE0yMDguMjg1LDI0LjA5N2g5NS40M3YyNC4xNDNoLTk1LjQzVjI0LjA5N3ogICAgIE00MDMuNzg0LDQ2Ny44MDljLTAuNDMzLDExLjI2OC05LjYwNSwyMC4wOTQtMjAuODgyLDIwLjA5NEgxMjkuMDk5Yy0xMS4yNzYsMC0yMC40NDgtOC44MjctMjAuODgyLTIwLjA5NUw5My4wMjUsNzIuMzM4aDMyNS45NTIgICAgTDQwMy43ODQsNDY3LjgwOXoiIGZpbGw9IiMwMDAwMDAiLz4KCTwvZz4KPC9nPgo8Zz4KCTxnPgoJCTxwYXRoIGQ9Ik0xODIuNjMsMTgxLjU3MWMtMC4xMjctNi41NzUtNS40OTQtMTEuODE3LTEyLjA0Mi0xMS44MTdjLTAuMDc4LDAtMC4xNTgsMC0wLjIzNiwwLjAwMiAgICBjLTYuNjUyLDAuMTI4LTExLjk0Myw1LjYyNi0xMS44MTUsMTIuMjc4bDMuNzgxLDE5Ni42MzRjMC4xMjYsNi41NzUsNS40OTUsMTEuODE3LDEyLjA0MiwxMS44MTdjMC4wNzgsMCwwLjE1OCwwLDAuMjM2LTAuMDAyICAgIGM2LjY1My0wLjEyOCwxMS45NDMtNS42MjQsMTEuODE1LTEyLjI3OEwxODIuNjMsMTgxLjU3MXoiIGZpbGw9IiMwMDAwMDAiLz4KCTwvZz4KPC9nPgo8Zz4KCTxnPgoJCTxwYXRoIGQ9Ik0yNTUuOTk4LDE2OS43NTNjLTYuNjU0LDAtMTIuMDQ5LDUuMzk0LTEyLjA0OSwxMi4wNDl2MTk2LjYzNGMwLDYuNjU0LDUuMzk0LDEyLjA0OSwxMi4wNDksMTIuMDQ5ICAgIGM2LjY1NSwwLDEyLjA0OS01LjM5NCwxMi4wNDktMTIuMDQ5VjE4MS44MDJDMjY4LjA0NywxNzUuMTQ4LDI2Mi42NTMsMTY5Ljc1MywyNTUuOTk4LDE2OS43NTN6IiBmaWxsPSIjMDAwMDAwIi8+Cgk8L2c+CjwvZz4KPGc+Cgk8Zz4KCQk8cGF0aCBkPSJNMzQxLjY0NSwxNjkuNzU2Yy02LjYyOC0wLjE0Ny0xMi4xNTEsNS4xNjItMTIuMjc4LDExLjgxNWwtMy43ODEsMTk2LjYzNGMtMC4xMjksNi42NTMsNS4xNjIsMTIuMTUsMTEuODE1LDEyLjI3OCAgICBjMC4wNzgsMC4wMDEsMC4xNTgsMC4wMDIsMC4yMzYsMC4wMDJjNi41NDYsMCwxMS45MTYtNS4yNDQsMTIuMDQyLTExLjgxN2wzLjc4MS0xOTYuNjM0ICAgIEMzNTMuNTg4LDE3NS4zOCwzNDguMjk5LDE2OS44ODMsMzQxLjY0NSwxNjkuNzU2eiIgZmlsbD0iIzAwMDAwMCIvPgoJPC9nPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=)
}
/* Filelist */
.filelist-control {
cursor: pointer;
@ -712,6 +718,210 @@ input.filelist-checkbox:checked + table.table-filelist {
.editor-preview-side {
top: 110px;
}
/* Mod Tools */
.modtools {
position: fixed;
top:60px;
padding: 1px 10px;
max-width: 1060px;
width:100%;
background: #111;
border: 1px solid #222;
height: 25px;
}
.tr-cb {
width: 20px;
text-align: left;
display: none;
}
.modtools .actions {
display: none;
}
.modtools span.btn-group {
margin-left: 20px;
}
.modtools .cb_action {
height: 100%;
}
/* Modal box */
/* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 4; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content/Box */
/* Modal Header */
.modal-header {
padding: 2px 16px;
background: #111; /* Old browsers */
background: -moz-linear-gradient(bottom, #222 0%, #111 100%);
background: -webkit-linear-gradient(bottom, #222 0%, #111 100%);
background: linear-gradient(to top, #222 0%, #111 100%);
color: white;
}
/* Modal Body */
.modal-body {padding: 2px 16px;}
/* Modal Footer */
.modal-footer {
padding: 2px 16px;
background: #222; /* Old browsers */
background: -moz-linear-gradient(bottom, #111 0%, #222 100%);
background: -webkit-linear-gradient(bottom, #111 0%, #222 100%);
background: linear-gradient(to top, #111 0%, #222 100%);
color: white;
}
.modal-footer span {
float: right;
margin-right: 20px;
}
.modal-footer span button {
margin-right: 5px;
padding: 1em 2em;
background: none;
border: 1px solid white;
border-radius: 3px;
color: white;
font-weight: bold;
}
.modal-footer span .close {
font-size: 1em;
float: none;
background: #E84C4C;
}
.modal-footer span #confirm_changes {
font-size: 1em;
background: #98D9A8;
}
/* Modal Content */
.modal-content {
position: relative;
background-color: #fefefe;
margin: auto;
padding: 0;
border: 1px solid #888;
width: 80%;
margin-top: 20%;
max-width: 1000px;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
-webkit-animation-name: animatetop;
-webkit-animation-duration: 0.4s;
animation-name: animatetop;
animation-duration: 0.4s
}
.modal .close {
float: right;
font-size: 2em;
cursor: pointer;
}
.modal a.icon {
width: 16px;
height: 16px;
}
.modal .edit_list {
background: #98D9A8;
padding: 0.3em;
margin-bottom: 3px;
}
.modal .delete_list {
background: #E84C4C;
padding: 0.3em;
margin-bottom: 3px;
}
.modal .delete_item {
font-weight: bold;
padding: 3px;
}
.modal .edit_item {
font-weight: bold;
padding: 3px;
}
.delete_item a, .delete_item span.infos, .edit_item a, .edit_item span.infos {
float: right;
}
.delete_list span.infos, .edit_list span.infos {
float: right;
margin-top: 1em;
}
.modal-body div .title h3 {
cursor: pointer;
}
.modal .list {
border: 1px solid #222;
margin-bottom: 0.5em;
display: none;
}
.modal .list .delete_item:nth-child(even), .modal .list .edit_item:nth-child(even) {background: #CCC}
.modal .list .delete_item:nth-child(odd), .modal .list .edit_item:nth-child(odd) {background: #FFF}
.modal-footer button {
cursor: pointer;
}
.modal .close:hover, .modal-footer span button:hover, .modal a.icon:hover {
opacity: 0.7;
}
/* Add Animation */
@-webkit-keyframes animatetop {
from {top: -300px; opacity: 0}
to {top: 0; opacity: 1}
}
@keyframes animatetop {
from {top: -300px; opacity: 0}
to {top: 0; opacity: 1}
}
.progress-bar {
height:1.5em;
width: 100%;
background: #333;
padding: 0;
}
.progress-green {
height: 100%;
margin: 0;
background: #22A243;
text-align: right;
padding-right: 2px;
color: white;
padding-top: 0.25em;
}
.logs_mess {
background: #ddd;
height: 100px;
overflow: auto;
padding: 3px;
font-style: italic;
}
.logs_mess div.success {
color: #22A243;
}
.logs_mess div.error {
color: #893636;
.error-text, .success-text {
text-align: center;
}

84
public/js/modal.js Fichier normal
Voir le fichier

@ -0,0 +1,84 @@
// Get the modal
var Modal = {
active: 0,
Init: function (params) {
var elements = params.elements
var button = (params.button != undefined) ? params.button : false
if (elements.innerHTML != undefined) {
} else {
var nbEl = elements.length
for (var i=0; i < nbEl; i++) {
var modal = elements[i];
this.addModal(modal, button, i, params.before, params.after, params.close)
}
}
},
addModal: function(modal, btn, i, before_callback, after_callback, close_callback) {
var isBtnArray = false;
// Get the button that opens the modal
if (!btn) {
btn = document.getElementById("modal_btn_"+modal.id)
} else if (btn.match(/^#/)) {
btn = document.getElementById(btn.substr(1));
} else if (btn.match(/^\./)) {
btn = document.getElementsByClassName(btn.substr(1));
isBtnArray = true;
} else {
console.error("Couldn't find the button")
return
}
if ((isBtnArray) && (i > 0) && (btn.length > 0) && (btn.length > i)) {
btn[i].addEventListener("click", function(e) {
if (before_callback != undefined) before_callback()
modal.style.display = "block";
Modal.active = modal;
if (after_callback != undefined) after_callback()
e.preventDefault();
});
} else {
btn = (isBtnArray) ? btn[0] : btn;
// When the user clicks on the button, open the modal
btn.addEventListener("click", function(e) {
if (before_callback != undefined) before_callback()
modal.style.display = "block";
Modal.active = modal;
if (after_callback != undefined) after_callback()
e.preventDefault();
});
}
// Get the <span> element that closes the modal
var span = document.querySelectorAll("#"+modal.id+" .close")[0]
// When the user clicks on <span> (x), close the modal
span.addEventListener("click", function(e) {
modal.style.display = "none";
Modal.active = 0;
if (close_callback != undefined) close_callback()
e.preventDefault();
});
// When the user clicks anywhere outside of the modal, close it
window.addEventListener("click", function(event) {
if (event.target == modal) {
modal.style.display = "none";
Modal.active = 0;
if (close_callback != undefined) close_callback()
}
});
},
CloseActive: function() {
if (this.active != 0) {
this.active.style.display= "none";
this.active = 0;
}
},
GetActive: function() {
return this.active;
},
Open: function(q) {
var modal = document.querySelector(q);
if (modal != undefined) {
modal.style.display= "none";
this.active = modal;
}
}
};

Voir le fichier

@ -1,6 +1,6 @@
var Query = {
Failed:0,
MaxConsecutingFailing:-1,
MaxFail: 10,
Get: function(url, renderer, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
@ -9,15 +9,34 @@ var Query = {
if (this.status == 200) {
Query.Failed = 0;
renderer(this.response);
callback(this.response);
if (callback != undefined) callback(this.response);
} else {
console.log("Error when refresh")
Query.Failed++;
console.log("Attempt to refresh "+Query.Failed+"...");
if ((Query.MaxConsecutingFailing == -1) || (Query.Failed < Query.MaxConsecutingFailing)) Query.Get(url, renderer, callback);
if ((Query.MaxFail == -1) || (Query.Failed < Query.MaxFail)) Query.Get(url, renderer, callback);
else console.error("Too many attempts, stopping...")
}
};
xhr.send();
},
Post: function(url, postArgs, callback) {
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.responseType = 'json';
xhr.onload = function(e) {
if (this.status == 200) {
Query.Failed = 0;
if (callback != undefined) callback(this.response);
} else {
console.log("Error when refresh")
Query.Failed++;
console.log("Attempt to refresh "+Query.Failed+"...");
if ((Query.MaxFail == -1) || (Query.Failed < Query.MaxFail)) Query.Post(url, postArgs, callback);
else console.error("Too many attempts, stopping...")
}
};
xhr.send(postArgs);
}
}
};

364
public/js/torrentsMod.js Fichier normal
Voir le fichier

@ -0,0 +1,364 @@
var TorrentsMod = {
// Variables that can be modified to change the dom interactions
show_hide_button: "show_actions",
btn_class_action: "cb_action",
btn_class_submit: "cb_submit",
progress_bar_id: "progress_modtool",
status_input_name: "status_id",
owner_input_name: "owner_id",
category_input_name: "category_id",
delete_btn: "delete",
lock_delete_btn: "lock_delete",
edit_btn: "edit",
refreshTimeout: 3000,
// Internal variables used for processing the request
selected: [],
queued: [],
unique_id:1,
error_count:0,
progress_count: 0,
progress_max: 0,
pause: false,
// Init method
Create: function() {
var sh_btn = document.getElementById(TorrentsMod.show_hide_button);
var btn_actions = document.getElementsByClassName(this.btn_class_action)
var btn_submit = document.getElementsByClassName(this.btn_class_submit)
btn_submit[0].disabled = true;
for (var i=0; i < btn_actions.length; i++) {
btn_actions[i].disabled = true;
switch (btn_actions[i].id) {
case this.delete_btn:
btn_actions[i].addEventListener("click", this.Delete)
break;
case this.lock_delete_btn:
btn_actions[i].addEventListener("click", this.LockDelete)
break;
case this.edit_btn:
btn_actions[i].addEventListener("click", this.Edit)
break;
default:
break;
}
}
for (var i=0; i < this.checkboxes.length; i++) {
checkbox = this.checkboxes[i];
checkbox.addEventListener("change", this.checkboxEventHandler)
}
sh_btn.addEventListener("click", function(e) {
var display = "inline"
var divActions = this.nextElementSibling;
console.log(divActions)
if (divActions.style.display == "inline") {
display = "none";
}
divActions.style.display = display;
var td_cbs = document.getElementsByClassName("tr-cb")
for (var i=0; i < td_cbs.length; i++) {
td_cb = td_cbs[i];
td_cb.style.display = (display == "inline") ? "table-cell" : "none";
}
var toggleText = this.dataset.toggleText;
this.dataset.toggleText = this.innerText;
this.innerText = toggleText;
});
},
// generate a unique id for a query
getId: function(){
return this.unique_id++;
},
// UI Methods
selectAll: function(bool) {
var l = TorrentsMod.checkboxes.length;
for (var i = 0; i < l; i++) {
TorrentsMod.checkboxes[i].checked = bool;
TorrentsMod.checkboxEventHandlerFunc(TorrentsMod.checkboxes[i]);
}
},
disableBtnActions: function() {
var btn_actions = document.getElementsByClassName(this.btn_class_action)
for (var i=0; i < btn_actions.length; i++) {
btn_actions[i].disabled = true;
}
},
enableBtnActions: function() {
var btn_actions = document.getElementsByClassName(this.btn_class_action)
for (var i=0; i < btn_actions.length; i++) {
btn_actions[i].disabled = false;
}
},
enableBtnSubmit: function() {
var btn_submit = document.getElementsByClassName(this.btn_class_submit)
btn_submit[0].disabled = false;
},
enableApplyChangesBtn: function() {
var btn_apply_changes = document.getElementById("confirm_changes");
btn_apply_changes.disabled=false;
},
disableBtnSubmit: function() {
var btn_submit = document.getElementsByClassName(this.btn_class_submit)
btn_submit[0].disabled = true;
},
disableApplyChangesBtn: function() {
var btn_apply_changes = document.getElementById("confirm_changes");
btn_apply_changes.disabled=true;
},
removeDivFromList: function(i) {
var queueAction = this.queued[i];
var parentDiv = document.getElementById(queueAction.unique_id).parentNode;
parentDiv.removeChild(document.getElementById(queueAction.unique_id));
},
removeFromParent: function(el) {
var parentDiv = el.parentNode;
parentDiv.removeChild(el);
},
generatingModal: function() {
listLength = this.queued.length;
var div = {"edit": "", "delete": ""};
for (var i=0; i < listLength; i++) {
var listHTML = "";
for(key in this.queued[i].selection) {
var selection = this.queued[i].selection[key];
selection.key = i;
listHTML += Templates.Render("torrents."+this.queued[i].action+".item", selection);
}
this.queued[i].list = listHTML;
this.queued[i].key = i;
div[this.queued[i].action] += Templates.Render("torrents."+this.queued[i].action+".block", this.queued[i]);
}
this.progress_count = 0;
this.progress_max = listLength;
document.querySelector(".modal .edit_changes").innerHTML = div["edit"];
document.querySelector(".modal .delete_changes").innerHTML = div["delete"];
},
toggleList: function(el) {
el.parentNode.nextSibling.style.display = (el.parentNode.nextSibling.style.display != "block") ? "block" : "none"
},
addToLog: function(type, msg) {
var logDiv = document.querySelector(".modal .logs_mess");
if (logDiv.style.display == "none") logDiv.style.display = "block"
logDiv.innerHTML += Templates.Render("torrents.logs."+type, msg);
},
updateProgressBar: function() {
document.querySelector("#"+this.progress_bar_id).style.display = "block";
var progress_green = document.querySelector("#"+this.progress_bar_id+" .progress-green");
var perc = this.progress_count/this.progress_max*100;
progress_green.style.width=perc+"%";
progress_green.innerText = this.progress_count+"/"+this.progress_max;
},
resetModal: function() {
var logDiv = document.querySelector(".modal .logs_mess");
logDiv.style.display = "none";
document.querySelector("#"+this.progress_bar_id).style.display = "none";
logDiv.innerHTML = "";
document.querySelector(".modal .edit_changes").innerHTML = "";
document.querySelector(".modal .delete_changes").innerHTML = "";
this.enableApplyChangesBtn();
},
statusToClassName: function(status) {
var className = ["", "normal", "remake", "trusted", "aplus", ""]
return className[status];
},
// Selection Management Methods
addToSelection: function (torrent) {
this.selected[torrent.id] = torrent;
},
removeFromSelection: function(torrent) {
delete this.selected[torrent.id];
for (t in this.selected) {
return
}
this.selected = [];
},
// Query Queue Management Methods
AddToQueue: function(QueueAction) {
QueueAction.unique_id = this.getId(); // used for DOM interaction
this.queued.push(QueueAction);
this.enableBtnSubmit()
},
RemoveFromQueueAfterItems: function(i) {
for (t in this.queued[i].selection) {
this.RemoveItemFromQueue(i, t);
}
},
RemoveFromQueue: function(i) {
this.progress_max = (this.progress_max - 1 >= 0) ? this.progress_max-1 : 0;
this.RemoveFromQueueAction(i)
return false;
},
RemoveFromQueueAction: function(i) {
this.removeFromParent(document.getElementById("list_"+this.queued[i].unique_id));
this.queued.splice(i, 1);
if (this.queued.length == 0) {
this.disableBtnSubmit();
if (this.progress_max>0) {
this.disableApplyChangesBtn();
} else {
Modal.CloseActive();
}
}
},
formatSelectionToQuery: function(selection) {
var format = "";
for (s in selection) {
format += "&torrent_id="+selection[s].id
}
return (format != "") ? format.substr(1) : ""
},
RemoveItemFromQueue: function(i, id) {
this.removeFromParent(document.getElementById("list_item_"+id));
delete this.queued[i].selection[id];
document.getElementById("torrent_cb_"+id).checked=false;
document.getElementById("torrent_"+id).style.display="";
var test = 0;
for (t in this.queued[i].selection) {
test++
break;
}
if (test == 0) this.RemoveFromQueue(i);
return false;
},
newQueryAttempt: function(queryUrl, queryPost, callback) {
Query.Post(queryUrl, queryPost, function (response) {
if ((response.length == 0)||(!response.ok)) { // Query has failed
var errorMsg = response.errors.join("<br>");
TorrentsMod.addToLog("error", errorMsg);
TorrentsMod.error_count++;
if (TorrentsMod.error_count < 2) {
TorrentsMod.addToLog("success", "Trying a new attempt...");
TorrentsMod.newQueryAttempt(queryUrl, queryPost, callback)
} else {
TorrentsMod.addToLog("error", "The query ("+queryUrl+"?"+queryPost+") seems broken!");
if (callback != undefined) {
TorrentsMod.addToLog("error", "Passing to the next query...");
callback(response) // So we can query only one item
}
}
} else {
var succesMsg = (response.infos != null) ? response.infos.join("<br>") : "Query executed with success!";
TorrentsMod.addToLog("success", succesMsg)
if (callback != undefined) callback(response) // So we can query only one item
}
});
},
QueryQueue: function(i, callback) {
if (this.queued.length > 0) {
var QueueAction = this.queued[i]; // we clone it so we can delete it safely
this.RemoveFromQueueAction(i);
var queryPost = "";
var queryUrl = "/mod/api/torrents";
QueueAction.queryPost = TorrentsMod.formatSelectionToQuery(QueueAction.selection)
if (QueueAction.action == "delete") {
queryPost="action="+QueueAction.action;
queryPost+="&withreport="+QueueAction.withReport;
queryPost += "&status="+((QueueAction.status != undefined) ? QueueAction.status : "");
queryPost += "&"+QueueAction.queryPost; // we add torrent id
} else if (QueueAction.action == "edit") {
queryPost="action=multiple";
queryPost += "&status="+QueueAction.status;
queryPost += "&owner="+QueueAction.owner;
queryPost += "&category="+QueueAction.category;
queryPost += "&"+QueueAction.queryPost; // we add torrent id
}
TorrentsMod.newQueryAttempt(queryUrl, queryPost, callback)
} else {
TorrentsMod.addToLog("success", "All operations are done!")
if (TorrentsMod.refreshTimeout > 0) {
TorrentsMod.addToLog("success", "Refreshing the page in 3 seconds...")
setTimeout(function(){
window.location.reload()
}, TorrentsMod.refreshTimeout);
}
}
},
QueryLoop: function() {
if (TorrentsMod.progress_count <= TorrentsMod.progress_max) {
TorrentsMod.updateProgressBar()
TorrentsMod.progress_count++;
if (TorrentsMod.progress_count > TorrentsMod.progress_max) TorrentsMod.progress_count = TorrentsMod.progress_max;
TorrentsMod.QueryQueue(0, TorrentsMod.QueryLoop);
}
},
// Event Handlers
checkboxEventHandler: function(e) {
var el = e.target;
TorrentsMod.checkboxEventHandlerFunc(el);
},
checkboxEventHandlerFunc: function(el) {
var name = el.dataset.name;
var id = el.value;
if (el.checked) TorrentsMod.addToSelection({name:name, id:id});
else TorrentsMod.removeFromSelection({name:name, id:id});
if (TorrentsMod.selected.length > 0) TorrentsMod.enableBtnActions();
else TorrentsMod.disableBtnActions();
},
// Action Methods
DeleteHandler: function(locked) {
var withReport = confirm("Do you want to delete the reports along the selected torrents?")
var selection = TorrentsMod.selected;
if (locked)
TorrentsMod.AddToQueue({ action: "delete",
withReport: withReport,
selection: selection,
queryPost: "",
infos: "with lock"+ ((withReport) ? " and reports" : ""),
status: "5" });
else TorrentsMod.AddToQueue({
action: "delete",
withReport: withReport,
selection: selection,
infos: (withReport) ? "with reports" : "",
queryPost: ""});
for (i in selection) document.getElementById("torrent_"+i).style.display="none";
TorrentsMod.selected = []
TorrentsMod.disableBtnActions();
},
Delete: function(e) {
TorrentsMod.DeleteHandler(false);
e.preventDefault();
},
LockDelete: function(e) {
TorrentsMod.DeleteHandler(true);
e.preventDefault();
},
Edit: function(e) {
var selection = TorrentsMod.selected;
var status = document.querySelector(".modtools *[name='"+TorrentsMod.status_input_name+"']").value;
var owner_id = document.querySelector(".modtools *[name='"+TorrentsMod.owner_input_name+"']").value;
var category = document.querySelector(".modtools *[name='"+TorrentsMod.category_input_name+"']").value;
var infos = "";
infos += (status != "") ? "status: "+status : "";
infos += (owner_id != "") ? " owner_id: "+owner_id : "";
infos += (category != "") ? " category: "+category : "";
TorrentsMod.AddToQueue({
action: "edit",
selection: selection,
queryPost: "", // We don't format now, we wait until the query is sent
infos: (infos != "" ) ? "with "+infos : "No changes",
status: status,
category: category,
owner: owner_id });
if (status != "") {
for (i in selection) document.getElementById("torrent_"+i).className="torrent-info "+TorrentsMod.statusToClassName(status);
}
TorrentsMod.selected = []
TorrentsMod.disableBtnActions();
e.preventDefault();
},
ApplyChanges: function() {
this.pause = false;
this.QueryLoop();
}
};
// Load torrentMods when DOM is ready
document.addEventListener("DOMContentLoaded", function() {
TorrentsMod.checkboxes = document.querySelectorAll("input[name='torrent_id']");
TorrentsMod.Create();
});

Voir le fichier

@ -609,6 +609,11 @@ func torrentManyAction(r *http.Request) {
/* Changes are done, we save */
db.ORM.Unscoped().Model(&torrent).UpdateColumn(&torrent)
} else if action == "delete" {
if status == model.TorrentStatusBlocked { // Then we should lock torrents before deleting them
torrent.Status = status
messages.AddInfoTf("infos", "torrent_moved", torrent.Name)
db.ORM.Unscoped().Model(&torrent).UpdateColumn(&torrent) // We must save it here and soft delete it after
}
_, err = torrentService.DeleteTorrent(torrentID)
if err != nil {
messages.ImportFromError("errors", err)

Voir le fichier

@ -26,7 +26,6 @@
<label for="Status">{{call $.T "torrent_status"}}</label>
<select name="status" class="form-control input-sm">
<option value="5" {{if eq .Status 5}}selected{{end}}>{{ call $.T "torrent_status_blocked" }}</option>
<option value="0" {{if eq .Status 0}}selected{{end}}>{{call $.T "torrent_status_hidden"}}</option>
<option value="1" {{if eq .Status 1}}selected{{end}}>{{call $.T "torrent_status_normal"}}</option>
<option value="2" {{if eq .Status 2}}selected{{end}}>{{call $.T "torrent_status_remake"}}</option>
<option value="3" {{if eq .Status 3}}selected{{end}}>{{call $.T "trusted"}}</option>

Voir le fichier

@ -17,6 +17,9 @@ Your browser does not support the audio element.
<table>
<thead class="torrent-info">
<tr>
{{ if HasAdmin $.User }}
<th class="tr-cb"><input type="checkbox" name="select_all" onchange="TorrentsMod.selectAll(this.checked)"></th>
{{end}}
<th class="tr-cat">{{call $.T "category"}}</th>
<th class="tr-name">
<a href="{{ genSearchWithOrdering .URL "1" }}">{{call $.T "name"}}<span class="sort-arrows">{{ genSortArrows .URL "1" }}</span></a>
@ -39,10 +42,15 @@ Your browser does not support the audio element.
</thead>
<tbody id="torrentListResults">
{{ range .Models}}
<tr class="torrent-info
<tr id="torrent_{{ .ID }}" class="torrent-info
{{if eq .Status 2}}remake{{end}}
{{if eq .Status 3}}trusted{{end}}
{{if eq .Status 4}}aplus{{end}}">
{{if eq .Status 4}}aplus{{end}}" id="torrent{{ .ID }}">
{{ if HasAdmin $.User }}
<td class="tr-cb">
<input data-name="{{ .Name }}" type="checkbox" id="torrent_cb_{{ .ID }}" name="torrent_id" value="{{ .ID }}">
</td>
{{ end }}
<td class="tr-cat home-td">
<a href="{{$.URL.Parse (printf "/search?c=%s_%s" .Category .SubCategory) }}">
{{ if Sukebei }}
@ -88,11 +96,113 @@ Your browser does not support the audio element.
</tbody>
</table>
</div>
{{ if HasAdmin $.User }}
<div class="modtools">
<button id="show_actions" data-toggle-text="{{call $.T "hide_mod_tools"}}">{{call $.T "show_mod_tools"}}</button>
<span class="actions">
<span class="btn-group">
<select class="cb_action" name="category_id">
<option value="">{{call $.T "category"}}</option>
{{ range $name_cat, $id_cat := (GetCategories true) }}
<option value="{{ $id_cat }}">{{call $.T $name_cat }}</option>
{{ end }}
</select>
<input class="cb_action" type="text" name="owner_id" placeholder="Owner ID (eg. Renchon = 0)">
<select class="cb_action" name="status_id">
<option value="">{{call $.T "torrent_status"}}</option>
<option value="5">{{ call $.T "torrent_status_blocked" }}</option>
<option value="1">{{call $.T "torrent_status_normal"}}</option>
<option value="2" >{{call $.T "torrent_status_remake"}}</option>
<option value="3">{{call $.T "trusted"}}</option>
<option value="4">A+</option>
</select>
<button class="cb_action" id="edit">{{ call $.T "edit" }}</button>
</span>
<span class="btn-group">
<button class="cb_action" id="lock_delete">{{ call $.T "lock_delete" }}</button>
<button class="cb_action" id="delete">{{ call $.T "delete" }}</button>
</span>
<span class="btn-group">
<button class="cb_submit" id="modal_active">{{ call $.T "save_changes" }}</button>
</span>
</span>
</div>
<!-- Modal -->
<div id="modal_mod_tools" class="modal">
<!-- Modal content -->
<div class="modal-content">
<div class="modal-header">
<span class="close">&times;</span>
<h2>{{ call $.T "following_changes_applied" }}</h2>
</div>
<div class="modal-body">
<h3>{{ call $.T "changes_in_following_order" }}</h3>
<div class="progress-bar" id="progress_modtool" style="display: none;"><div class="progress-green"></div></div>
<hr>
<div class="logs_mess" style="display: none;"></div>
<h2>{{ call $.T "edit_changes" }}</h2>
<div class="edit_changes"></div>
<h2>{{ call $.T "delete_changes" }}</h2>
<div class="delete_changes"></div>
</div>
<div class="modal-footer">
<span><button id="confirm_changes" onclick="TorrentsMod.ApplyChanges();">{{ call $.T "yes"}}</button>
<button class="close" onclick="Modal.CloseActive();">{{ call $.T "no"}}</button></span>
<h3>{{ call $.T "are_you_sure" }} </h3>
</div>
</div>
</div>
{{end}}
{{end}}
{{ define "footer_js"}}
<script type="text/javascript" src="{{ $.URL.Parse "/js/template.js" }}"></script>
<script type="text/javascript" src="{{ $.URL.Parse "/js/query.js" }}"></script>
<script type="text/javascript" src="{{ $.URL.Parse "/js/modal.js" }}"></script>
<script type="text/javascript" src="{{ $.URL.Parse "/js/torrents.js" }}"></script>
{{ if HasAdmin $.User }}
<script type="text/javascript" src="{{ $.URL.Parse "/js/torrentsMod.js" }}"></script>
<script type="text/javascript">
Modal.Init({elements: document.getElementsByClassName("modal"),
button: "#modal_active",
before: function() {
TorrentsMod.generatingModal();
},
close: function() {
TorrentsMod.resetModal();
}
});
Templates.Add("torrents.delete.item", function(torrent) {
return '<div class="delete_item" id="list_item_'+torrent.id+'"><span>'+Templates.EncodeEntities(torrent.name)+'</span>'+
'<a href="#" onclick="return TorrentsMod.RemoveItemFromQueue('+torrent.key+', '+torrent.id+')"><i class="trash-icon"></i></a></div>'
});
Templates.Add("torrents.delete.block", function(torrentQuery){
return '<div class="delete_list" id="list_'+torrentQuery.unique_id+'"><div class="title">'+
'<h3 style="display:inline-block;" onclick="TorrentsMod.toggleList(this);">Query #'+torrentQuery.unique_id+
'</h3>'+
'<span class="infos">'+torrentQuery.infos+'<a href="#" class="icon" onclick="return TorrentsMod.RemoveFromQueue('+torrentQuery.key+')"><div class="trash-icon"></div></a>'+
'</span></div>'+
'<div class="list">'+torrentQuery.list+'</div></div>';
});
Templates.Add("torrents.edit.item", function(torrent) {
return '<div class="edit_item" id="list_item_'+torrent.id+'"><span>'+Templates.EncodeEntities(torrent.name)+'</span>'+
'<a href="#" onclick="return TorrentsMod.RemoveItemFromQueue('+torrent.key+', '+torrent.id+')"><i class="trash-icon"></i></a></div>'
});
Templates.Add("torrents.edit.block", function(torrentQuery){
return '<div class="edit_list" id="list_'+torrentQuery.unique_id+'"><div class="title">'+
'<h3 style="display:inline-block;" onclick="TorrentsMod.toggleList(this);">Query #'+torrentQuery.unique_id+
'</h3>'+
'<span class="infos">'+torrentQuery.infos+'<a href="#" class="icon" onclick="return TorrentsMod.RemoveFromQueue('+torrentQuery.key+')"><div class="trash-icon"></div></a>'+
'</span></div>'+
'<div class="list">'+torrentQuery.list+'</div></div>';
});
Templates.Add("torrents.logs.error", function(msg) {
return '<div class="error">'+msg+'</div>';
});
Templates.Add("torrents.logs.success", function(msg) {
return '<div class="success">'+msg+'</div>';
});
</script>
{{end}}
<!-- JS Template -->
<script type="text/javascript">
Templates.Add("torrents.item", function(torrent) {
@ -119,7 +229,7 @@ Your browser does not support the audio element.
"<td class=\"tr-date home-td date-short hide-xs\">"+torrent.date+"</td>"+
"</tr>";
});
Torrents.LastID = {{ lastID .URL .Models }};
if (Torrents.LastID > 0) Torrents.CanRefresh = true;
Torrents.LastID = {{ lastID .URL .Models }};
if (Torrents.LastID > 0) Torrents.CanRefresh = true;
</script>
{{end}}

Voir le fichier

@ -655,6 +655,10 @@
"id": "torrent_status_remake",
"translation": "Remake"
},
{
"id": "torrent_status_blocked",
"translation": "Locked"
},
{
"id": "profile_edit_page",
"translation": "Edit %s's profile"
@ -935,6 +939,10 @@
"id": "edit",
"translation": "Edit"
},
{
"id": "lock_delete",
"translation": "Lock & Delete"
},
{
"id": "delete_definitely_torrent_warning",
"translation": "You will not be able to recover the file, neither stop someone to reupload it!"
@ -1006,5 +1014,29 @@
{
"id": "hide",
"translation": "Hide"
},
{
"id":"show_mod_tools",
"translation": "Show Mod Tools"
},
{
"id":"hide_mod_tools",
"translation": "Hide Mod Tools"
},
{
"id": "following_changes_applied",
"translation": "Following changes will be applied"
},
{
"id": "changes_in_following_order",
"translation": "Changes will be made in the following order:"
},
{
"id": "edit_changes",
"translation": "Edit Changes"
},
{
"id": "delete_changes",
"translation": "Delete Changes"
}
]