Last active
April 3, 2020 18:19
-
-
Save CharlesNepote/6b9c2c7ae074e7910378c41d3076cf79 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name OFF mass updater user script | |
// @namespace openfoodfacts.org | |
// @version 0.1 | |
// @description Mass Editor | |
// @match https://*.openfoodfacts.org/* | |
// @exclude https://*.wiki.openfoodfacts.org/* | |
// @icon http://world.openfoodfacts.org/favicon.ico | |
// @require http://code.jquery.com/jquery-latest.min.js | |
// @require http://code.jquery.com/ui/1.12.1/jquery-ui.min.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery-tagsinput/1.3.6/jquery.tagsinput.min.js | |
// @grant GM_setValue | |
// @grant GM_getValue | |
// ==/UserScript== | |
// Code from https://github.com/roiKosmic/OFFMassUpdate/blob/master/js/content_script.js | |
// * Allow mass edit of products | |
// * [UI] the pen icon [🖉] allows to open each product directly in edit mode (without opening "view" mode) | |
/*$("head").append ( // .append is a jQuery function | |
+ '<script type="text/javascript" src="http://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>' | |
);/**/ | |
// Library pre-load. See https://stackoverflow.com/questions/950087/how-do-i-include-a-javascript-file-in-another-javascript-file | |
function loadScript(url, callback) { | |
// Adding the script tag to the head as suggested before | |
var head = document.head; | |
var script = document.createElement('script'); | |
script.type = 'text/javascript'; | |
script.src = url; | |
// Then bind the event to the callback function. | |
// There are several events for cross browser compatibility. | |
script.onreadystatechange = callback; | |
script.onload = callback; | |
// Fire the loading | |
head.appendChild(script); | |
} | |
(function() { | |
'use strict'; | |
var massEditCss = ` | |
.products > li > a { | |
padding-bottom: 0; | |
margin-bottom: 0; | |
height: 172px;/**/ | |
} | |
.pus_edit_link { | |
height: 1rem !important; | |
display: inline !important; | |
margin-left: 20px !important; | |
padding: 0 5px 0 5px !important; | |
} | |
.massButton { | |
background-color: red; | |
border: none; | |
text-align: center; | |
display: inline-block; | |
font-size: 30px; | |
color: white; | |
border-radius: 50%; | |
position:fixed; | |
top:3.5rem; | |
right:20px; | |
width:50px; | |
height:50px; | |
z-index:99; | |
cursor:pointer; | |
background-size: cover; | |
} | |
.massForms{ | |
background-color: blue; | |
opacity: .85; | |
padding: 20px; | |
color: rgba(255,255,255,.9); | |
position: fixed; | |
top:80px; | |
right:20px; | |
} | |
.massFormButton{ | |
background-color: red; | |
border: none; | |
color: white; | |
padding: 2px; | |
text-align: center; | |
text-decoration: none; | |
display: block; | |
font-size: 16px; | |
color: white; | |
margin: 4px 2px; | |
width:25%; | |
cursor:pointer; | |
border-radius: 20px; | |
} | |
.upBar{ | |
margin-top:5px; | |
margin-bottom:10px; | |
} | |
#selectAll{ | |
margin-top:5px; | |
display:inline; | |
float:left; | |
} | |
.counter{ | |
margin-top:15px; | |
} | |
#sNumber{ | |
background-color: green; | |
border: none; | |
color: white; | |
text-align: center; | |
text-decoration: none; | |
display: float; | |
float:right; | |
font-size: 16px; | |
color: white; | |
margin: 4px 2px; | |
border-radius: 50%; | |
width:20px | |
} | |
#eNumber{ | |
background-color: red; | |
border: none; | |
color: white; | |
text-align: center; | |
text-decoration: none; | |
display: float; | |
float:right; | |
font-size: 16px; | |
color: white; | |
margin: 4px 2px; | |
border-radius: 50%; | |
width:20px | |
} | |
#backButton{ | |
background-color: red; | |
border: none; | |
color: white; | |
padding: 2px; | |
text-align: center; | |
text-decoration: none; | |
display: block; | |
font-size: 16px; | |
color: white; | |
margin-top: 10px; | |
width:50%; | |
cursor:pointer; | |
border-radius: 20px; | |
} | |
/* Imorted from https://github.com/roiKosmic/OFFMassUpdate/blob/master/css/external/jquery.tagsinput.css */ | |
div.tagsinput { border:1px solid #CCC; background: #FFF; padding:5px; width:300px; height:100px; overflow-y: auto;} | |
div.tagsinput span.tag { border: 1px solid #a5d24a; -moz-border-radius:2px; -webkit-border-radius:2px; display: block; float: left; padding: 5px; text-decoration:none; background: #cde69c; color: #638421; margin-right: 5px; margin-bottom:5px;font-family: helvetica; font-size:13px;} | |
div.tagsinput span.tag a { font-weight: bold; color: #82ad2b; text-decoration:none; font-size: 11px; } | |
div.tagsinput input { width:80px; margin:0px; font-family: helvetica; font-size: 13px; border:1px solid transparent; padding:5px; background: transparent; color: #000; outline:0px; margin-right:5px; margin-bottom:5px; } | |
div.tagsinput div { display:block; float: left; } | |
.tags_clear { clear: both; width: 100%; height: 0px; } | |
.not_valid {background: #FBD8DB !important; color: #90111A !important;} /**/ | |
}`; | |
/*--- For this to work well, we must also add-in the jQuery-UI CSS. | |
We add the CSS this way so that the embedded, relatively linked images load correctly. | |
(Use //ajax... so that https or http is selected as appropriate to avoid "mixed content".) | |
*/ | |
$("head").append ( // .append is a jQuery function | |
+ '<link ' | |
+ 'href="//ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.min.css" ' | |
+ 'rel="stylesheet" type="text/css">' | |
); | |
// apply custom CSS | |
var s = document.createElement('style'); | |
s.type = 'text/css'; | |
s.innerHTML = massEditCss; | |
document.documentElement.appendChild(s); | |
var form_template = "" | |
+"<div id='form'>" | |
+" <div class='upBar'>" | |
+" <input type='checkbox' id='selectAll'> Select All</input>" | |
+" </div>" | |
+" <div>Field to update</div>" | |
+" <select id='champ'>" | |
+" <option value='add_packaging' field='packaging'>Packaging</option>" | |
+" <option value='add_brands' field='brands'>Brands</option>" | |
+" <option value='add_categories' field='categories'>Categories</option>" | |
+" <option value='add_labels' field='labels'>Label, certifications, rewards</option>" | |
+" <option value='add_origins' field='origins'>Ingredients origins</option>" | |
+" <option value='add_manufacturing_places' field='manufacturing_places'>Manufacturing places</option>" | |
+" <option value='add_purchase_places' field='purchase_places'>Purchasing places</option>" | |
+" <option value='add_stores' field='stores'>Stores</option>" | |
+" <option value='add_countries' field='countries'>Purchasing countries</option>" | |
+" <option value='quantity' field='quantity'>Quantity</option>" | |
+" </select>" | |
+" <div id='tagsHidder'><input name='tags' id='tags' value='' /></div>" | |
+" <input name='quantity' id='quantity' type='text' value='' />" | |
+" <div class='massFormButton update'>Update</div>" | |
+"</div>" | |
+"<div id='spinner'>Editing of <span id='pNumber'>0</span> products in progress" | |
+" <div class='counter'>Success <div id='sNumber'>0</div></div>" | |
+" <div class='counter'>Failures <div id='eNumber'>0</div></div>" | |
+" <div id='backButton'> < Back</div>" | |
+"</div>"; | |
var api_url = "/cgi/product_jqm2.pl?"; | |
var api_autocomplete_url = "/cgi/suggest.pl?"; | |
var sField='packaging'; | |
var lang=''; | |
var productToUpdate=0; | |
$(document).ready(function(){ | |
loadScript("https://cdnjs.cloudflare.com/ajax/libs/jquery-tagsinput/1.3.6/jquery.tagsinput.min.js", massEditorMain); // load library before launching script | |
//massEditorMain(); | |
}); | |
/** | |
* massEditorMain: Main function to launch Mass Editor | |
* @param : none | |
* @return : none | |
*/ | |
function massEditorMain(){ | |
if(isAdmin()){ | |
if($(".products").length){ | |
lang = $("html").attr("lang") | |
addingCheckBox(); | |
addingMassButton(); | |
$('#tags').tagsInput( | |
{ | |
onChange: function(){ | |
console.log("Tags updated"); | |
//browser.storage.local.set({"tags":$('#tags').val()}); // ---------------------------------------------------- | |
localStorage.setItem('tags', $('#tags').val()); | |
//GM_SuperValue.set(storageVar, {"tags":$('#tags').val()}); | |
}, | |
autocomplete_url: function(request, response) { | |
var url = api_autocomplete_url+"lc="+lang+"&tagtype="+sField+"&string="+request.term; | |
$.get(url, function(data){ | |
//data = JSON.parse(data); | |
response(data); | |
}); | |
} | |
} | |
); | |
} | |
} | |
} | |
/** | |
* Description : add a checkbox to each listed product, with product code as "value" | |
* @param : none | |
* @return : none | |
*/ | |
function addingCheckBox(){ | |
console.log("Adding check box"); | |
$(".products > li").append( | |
"<input class='massUpdateCheckbox' type='checkbox' value=''/>"); | |
$('.massUpdateCheckbox').each(function(){ | |
var myAnchor= $(this).parent().find("a"); | |
var myHref = myAnchor.attr("href"); // /product/3263856632710/franprix | |
//var myRe = /\/(\w+)\/(\d+)\/(\w+)/; | |
var myRe = /\/(\w+)\/(\d+)([\/|\w]*)/; | |
var result = myRe.exec(myHref); | |
$(this).attr('value',result[2]); // value="3263856632710" | |
console.log("Value: "+result[2]); | |
$(this).after('<a class="pus_edit_link" href="'+document.location.protocol + "//" + document.location.host + | |
"/cgi/product.pl?type=edit&code=" + result[2] + '" target="_blank">🖉</a>'); | |
}); | |
} | |
/** | |
* Description : add Mass Editor Button | |
* @param : none | |
* @return : none | |
*/ | |
function addingMassButton(){ | |
$("body").append( | |
"<div class='massUpdater'>" + | |
" <div class='massButton'>🖊</div>" + | |
" <div class='massForms'>"+form_template+"</div>" + | |
"</div>"); | |
$('.massForms').hide(); | |
$('#spinner').hide(); | |
// ChN: ?????? | |
//initValue(); | |
$(".massButton").click(function(){ | |
if($(".massForms").is(":hidden")){ | |
$('.massForms').show(); | |
$(".massButton").css("background-color","blue"); | |
//browser.storage.local.set({"visible":true}); // ---------------------------------------------------- | |
localStorage.setItem('visible', true); | |
//GM_SuperValue.set(storageVar, {"visible":true}); | |
}else{ | |
$('.massForms').hide(); | |
$(".massButton").css("background-color","red"); | |
clearAllField(); | |
$("#tagsHidder").show(); | |
$("#quantity").hide(); | |
} | |
}); | |
$("#backButton").click(function(){ | |
$("#backButton").hide(); | |
$("#spinner").hide(); | |
$('#selectAll').prop("checked",false); | |
$("#form").show(); | |
resetCounter(); | |
}); | |
$("#quantity").change(function(){ | |
var q = $(this).val(); | |
//browser.storage.local.set({"quantity":q}); // ---------------------------------------------------- | |
localStorage.setItem('quantity', q); | |
//GM_SuperValue.set(storageVar, {"quantity":q}); | |
}); | |
$(".update").click(function(){ | |
$("#spinner").show(); | |
$("#form").hide(); | |
$("#backButton").hide(); | |
sendMassUpdate(); | |
}); | |
$('#selectAll').change(function(){ | |
if($(this).is(':checked')){ | |
$('.massUpdateCheckbox').prop("checked",true); | |
}else{ | |
$('.massUpdateCheckbox').prop("checked",false); | |
} | |
}); | |
$('#champ').change(function(){ | |
sField = $('#champ').find(':selected').attr("field"); | |
//browser.storage.local.set({"selectedField":sField}); // ---------------------------------------------------- | |
localStorage.setItem('selectedField', sField); | |
//GM_SuperValue.set(storageVar, {"selectedField":sField}); | |
console.log("Setting: "+sField); | |
if(sField==='quantity'){ | |
$("#tagsHidder").hide(); | |
$("#quantity").show(); | |
}else{ | |
$("#tagsHidder").show(); | |
$("#quantity").hide(); | |
} | |
}); | |
} | |
/** | |
* Description : | |
* @param : none | |
* @return : none | |
*/ | |
function initValue(){ | |
// browser.storage.local.get(['selectedField'],function(result){ // ---------------------------------------------------- | |
// GM_SuperValue.get(storageValue) | |
// if(result.selectedField != null){ | |
// $("#champ > option[field='"+result.selectedField+"']").prop("selected",true); | |
// console.log("getting:" + result.selectedField); | |
// sField= result.selectedField; | |
// if(sField==='quantity'){ | |
// $("#tagsHidder").hide(); | |
// $("#quantity").show(); | |
// }else{ | |
// $("#tagsHidder").show(); | |
// $("#quantity").hide(); | |
// } | |
// } | |
// }); | |
browser.storage.local.get(['tags'],function(result){ // ---------------------------------------------------- | |
if(result.tags != null){ | |
$('#tags').importTags(result.tags); | |
} | |
} | |
); | |
browser.storage.local.get(['quantity'],function(result){ // ---------------------------------------------------- | |
if(result.quantity != null){ | |
$('#quantity').val(result.quantity); | |
} | |
} | |
); | |
browser.storage.local.get(['visible'],function(result){ // ---------------------------------------------------- | |
if(result.visible == true){ | |
$('.massForms').show(); | |
$(".massButton").css("background-color","blue"); | |
} else { | |
$('.massForms').hide(); | |
$(".massButton").css("background-color","white"); | |
} | |
}); | |
} | |
/** | |
* Description : update each product via the API | |
* @param : none | |
* @return : none | |
*/ | |
function sendMassUpdate(){ | |
var mySelect = $('#champ'); | |
var selectedField = mySelect.find(':selected').val(); | |
productToUpdate= $('.massUpdateCheckbox:checked').length; | |
$('.massUpdateCheckbox').each(function(){ | |
if($(this).is(':checked')){ | |
var remote_url = api_url+"code="+$(this).attr("value")+"&lc="+lang+"&comment="+encodeURIComponent("Updated via Power User Script")+"&"+selectedField+"="; | |
if(sField==='quantity'){ | |
remote_url += encodeURIComponent($("#quantity").val()); | |
}else{ | |
remote_url += encodeURIComponent($('#tags').val()); | |
} | |
console.log("Sending Get request to "+remote_url+"\n"); | |
$.ajax({ | |
type: "GET", | |
url: remote_url, | |
success: function (result) { | |
incrSuccessCounter(); | |
productToUpdate--; | |
updateProductCounter(); | |
if(productToUpdate <=0) $('#backButton').show(); | |
}, | |
error: function(){ | |
incrFailureCounter(); | |
productToUpdate--; | |
updateProductCounter(); | |
if(productToUpdate <=0) $('#backButton').show(); | |
} | |
}); | |
$(this).prop('checked',false); | |
} | |
}); | |
} | |
/** | |
* Description : | |
* @param : none | |
* @return : none | |
*/ | |
function clearAllField(){ | |
//browser.storage.local.clear(); // ---------------------------------------------------- | |
// => | |
localStorage.clear(); | |
$('#tags').importTags(""); | |
$("#quantity").val(""); | |
$("#champ > option[field='packaging']").prop("selected",true); | |
sField='packaging'; | |
$('.massUpdateCheckbox').prop("checked",false); | |
$('#selectAll').prop("checked",false); | |
} | |
function incrFailureCounter(){ | |
var x = parseInt($("#eNumber").html()) +1; | |
$("#eNumber").html(x); | |
} | |
function incrSuccessCounter(){ | |
var x = parseInt($("#sNumber").html()) +1; | |
$("#sNumber").html(x); | |
} | |
function updateProductCounter(){ | |
$("#pNumber").html(productToUpdate); | |
} | |
function resetCounter(){ | |
$("#eNumber").html("0"); | |
$("#sNumber").html("0"); | |
$("#pNumber").html("0"); | |
} | |
/** | |
* Description : Detect if user is connected or not | |
* @param : none | |
* @return : boolean, state of connection: true|false | |
*/ | |
function isConnected(){ | |
if($("input[name='user_id']").length) return false; | |
return true; | |
} | |
/** | |
* Description : Detect if user is admin or not | |
* @param : none | |
* @return : boolean, state of connection: true|false | |
*/ | |
function isAdmin(){ | |
// Detect producers platform // TODO: duplicated code with Power User Script | |
var regex_pro = RegExp('\.pro\.open'); | |
if(regex_pro.test(document.URL) === true) { | |
return true; | |
} | |
// href="/cgi/user.pl?userid=charlesnepote&type=edit" | |
var editor_id = $(".side-nav > li > a").attr("href"); // /editor/charlesnepote | |
console.log("editor_id: " + editor_id); | |
if(editor_id === undefined) return false; | |
var user_id = (/\/(.*?)\/(.*)/).exec(editor_id)[2]; | |
console.log("user_id: "+user_id); | |
var admins = ["aleene", "charlesnepote", | |
"moon-rabbit", "nutrinet-sante", "sebleouf", | |
"tacinte", "tacite", "tacite-mass-editor", "teolemon", | |
"segundo", "stephane", ]; | |
if(admins.includes(user_id)) return true; | |
return false; | |
} | |
})(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment