Commit 1f53a044 authored by Eric Seigne's avatar Eric Seigne
Browse files

version 1.6.0 avec les etapes possibles sur les IK

parent 49f1c853
# 1.6.0
- modification profonde de l'outil de gestion des indemnités kilométriques pour gérer les
déplacements de type "tournée" avec des étapes
# 1.5.0
- grosse modification de présentation générale
......
<?xml version='1.0' encoding='utf-8'?>
<widget id="fr.caprel.doliscan" version="1.5.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<widget id="fr.caprel.doliscan" version="1.6.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>doliscan</name>
<description>DoliSCAN is a tiny app for french people looking for a free and self hosting "notes de frais/expenses report" solution.</description>
<author email="eric.seigne@cap-rel.fr" href="https://cap-rel.fr">Eric Seigne</author>
......@@ -145,5 +145,5 @@
</feature>
</platform>
<plugin name="cordova-plugin-calendar" />
<preference name="OverrideUserAgent" value="Mozilla/5.0 OpenNDF/1.5.0 Android" />
<preference name="OverrideUserAgent" value="Mozilla/5.0 OpenNDF/1.6.0 Android" />
</widget>
......@@ -61,11 +61,15 @@
<div id="interobjets"></div>
<div style="align-items:baseline;">
<ons-button modifier="material large" onclick="ikAjoutEtape('leformIK');">Ajouter une étape</ons-button>
<ons-button modifier="material large" onclick="calculTotal('leformIK');">Parcours terminé</ons-button>
<ons-button id="btnAjoutEtape" modifier="material large" onclick="ikAjoutEtape('leformIK');">Ajouter une étape</ons-button>
<ons-button id="btnCalculTotal" modifier="material large" onclick="calculTotal('leformIK');">Parcours terminé</ons-button>
</div>
<div id="interobjets"></div>
<!-- sera completé par le submit -->
<input type="hidden" id="arrivee" name="arrivee" value="">
<!-- <div id="interobjets"></div>
<div style="display: flex; align-items:baseline;">
<ons-input required modifier="underbar" float name="arrivee" id="arrivee" type="text" placeholder="Ville d'arrivée" onchange="$('#arriveeSlug').val('');"></ons-input>
......@@ -79,12 +83,12 @@
</ons-list> -->
<div style="display: flex; align-items:baseline;">
<ons-input required modifier="underbar" float name="distanceTotale" id="distanceTotale" type="number" placeholder="Distance totale (km)" value=""></ons-input>
<ons-button modifier="material" id="btnCalculDistance" onclick="distanceAutomatique(depart.value, arrivee.value, departSlug.value, arriveeSlug.value, 'calcul');"><i class="zmdi zmdi-cloud-outline zmdib"></i></ons-button>
<ons-input required modifier="underbar" float name="distance" id="distance" type="number" placeholder="Distance totale (km)" value=""></ons-input>
<!-- <ons-button modifier="material" id="btnCalculDistance" onclick="distanceAutomatique(depart.value, arrivee.value, departSlug.value, arriveeSlug.value, 'calcul');"><i class="zmdi zmdi-cloud-outline zmdib"></i></ons-button> -->
</div>
</fieldset>
<div id="interobjets"></div>
<ons-button modifier="material large" onclick="checkForm('leformIK');">Valider</ons-button>
<ons-button id="envoyer" modifier="material large" onclick="checkForm('leformIK');">Valider</ons-button>
</ons-col>
</ons-row>
</form>
......@@ -93,6 +97,8 @@
<script type="text/javascript">
//Au lancement on supprime les éventuelles étapes de la saisie précédente
$('#iketape').empty();
$("#envoyer").hide();
var nbEtape = 1;
function ikAjoutEtape(leform) {
......@@ -114,7 +120,6 @@
html += " </div>";
html += " <ons-list style=\"clear: both;\">";
html += " <input type=\"hidden\" id=\"etape" + nbEtape + "Slug\" name=\"etape" + nbEtape + "Slug\" value=\"\">";
html += " <input type=\"hidden\" id=\"etape" + nbEtape + "KM\" name=\"etape" + nbEtape + "KM\" value=\"\">";
html += " <ons-list-item class=\"listeVille\" id=\"listeEtape" + nbEtape + "\" expandable data-role=\"listview\" data-filter=\"true\" data-filter-reveal=\"true\" data-input=\"#label\">";
html += " Favoris...";
html += " </ons-list-item>";
......@@ -156,11 +161,19 @@
let d = parseInt($("#etape" + i + "Distance").val());
total += d;
}
$("#distanceTotale").val(total);
$("#distance").val(total);
$("#envoyer").show();
document.getElementById('envoyer').scrollIntoView();
}
function checkForm(leform) {
// on complète l'arrivée
if (nbEtape > 0) {
let derniereEtape = $("#etape" + (nbEtape - 1)).val();
$("#arrivee").val(derniereEtape);
myDebug(" checkForm on ajoute l'arrivée : " + $("#arrivee").val());
}
if (genericCheckForm(leform)) {
let objForm = document.getElementById(leform);
......@@ -169,6 +182,27 @@
for (i = 1; i < nbEtape; i++) {
addLocalStorageEntry('cities', objForm.elements['etape' + i].value);
}
// on ameliore le label avec la liste des etapes
myDebug(" checkForm on ameliore le champ label avec les etape");
if (nbEtape > 2) {
let label = $("#label").val();
label += " - Étapes : ";
$("#label").val(label);
}
for (i = 1; i < nbEtape; i++) {
let s = $("#etape" + i).val();
let pos = s.indexOf('(');
let label = $("#label").val();
myDebug(" checkForm on ajoute :" + s.slice(0, pos) + " a " + label);
if (i > 1) {
label += ', ';
}
label += s.slice(0, pos).trim();
$("#label").val(label);
}
myDebug(" checkForm on ameliore le champ label :" + $("#label").val());
sendForm(leform);
}
}
......@@ -177,10 +211,6 @@
$('#listeDepart').show();
});
$('#arrivee').keyup(function () {
$('#listeArrivee').show();
});
function selectDepart(ville) {
$('#listeDepart').hide();
$('#depart').val(ville);
......@@ -190,15 +220,6 @@
$('#listeDepart').show();
}
function selectArrivee(ville) {
$('#listeArrivee').hide();
$('#arrivee').val(ville);
$('#distanceTotale').focus();
$('#arriveeSlug').val('xxx');
document.querySelector('#listeArrivee').hideExpansion();
$('#listeArrivee').show();
}
function selectEtape(nb, ville) {
$('#listeEtape' + nb).hide();
$('#etape' + nb).val(ville);
......
......@@ -2,28 +2,26 @@
* (c) Éric Seigne <eric.seigne@cap-rel.fr> - 2019-2020 - GNU AGPLv3
* full source code in src/ subfolder, this file is minifyed
*/
var globalDevMode=false;var globalMyCamera;var globalpictureSource;var globaldestinationType;var globalNetworkResponse=Date.now();var globalCurrentPage="";var globalCurrentTitle="";var globalDB=null;var globalTenMinuts=60*10*1000;var globalCompatibleWithAPIVersion=4;var globalDoliScanDomain="";var globalLastInsertId="";var globalSyncInProgress=false;var globalPictureFileName="";var globalAppVersion="1.5.0";var dataLDF="";var dataIK="";var globalMyNavigator=undefined;var globalResumable=undefined;var globalProgressBar=undefined;var globalTypeFrais="";var globalSrvKeys=['email','api_token','firstname','name','adresse','cp','ville','pays','send_copy_to','compta_email','compta_ik','compta_peage','compta_hotel','compta_train','compta_carburant','compta_taxi','compta_restauration','compta_divers','compta_compteperso','compta_tvadeductible','official_app_version','official_app_message','mode_simple'];function readFromFile(fileName,formdata,subject){var pathToFile=cordova.file.documentsDirectory+fileName;window.resolveLocalFileSystemURL(fileName,function(fileEntry){fileEntry.file(function(file){var reader=new FileReader();var fileSize=file.size;reader.onloadend=function(e){var fileContent=new Uint8Array(reader.result);formdata.append("afile",fileContent);formdata.append("afilesize",fileSize);var today=new Date();var destFilename=today.getFullYear().toString()+('0'+(today.getMonth()+1)).slice(-2)+('0'+today.getDate()).slice(-2)+"_"
var globalDevMode=false;var globalMyCamera;var globalpictureSource;var globaldestinationType;var globalNetworkResponse=Date.now();var globalCurrentPage="";var globalCurrentTitle="";var globalDB=null;var globalTenMinuts=60*10*1000;var globalCompatibleWithAPIVersion=4;var globalDoliScanDomain="";var globalLastInsertId="";var globalSyncInProgress=false;var globalPictureFileName="";var globalAppVersion="1.6.0";var dataLDF="";var dataIK="";var globalMyNavigator=undefined;var globalResumable=undefined;var globalProgressBar=undefined;var globalTypeFrais="";var globalSrvKeys=['email','api_token','firstname','name','adresse','cp','ville','pays','send_copy_to','compta_email','compta_ik','compta_peage','compta_hotel','compta_train','compta_carburant','compta_taxi','compta_restauration','compta_divers','compta_compteperso','compta_tvadeductible','official_app_version','official_app_message','mode_simple'];function readFromFile(fileName,formdata,subject){var pathToFile=cordova.file.documentsDirectory+fileName;window.resolveLocalFileSystemURL(fileName,function(fileEntry){fileEntry.file(function(file){var reader=new FileReader();var fileSize=file.size;reader.onloadend=function(e){var fileContent=new Uint8Array(reader.result);formdata.append("afile",fileContent);formdata.append("afilesize",fileSize);var today=new Date();var destFilename=today.getFullYear().toString()+('0'+(today.getMonth()+1)).slice(-2)+('0'+today.getDate()).slice(-2)+"_"
+today.getHours().toString()+today.getMinutes().toString()+"-"+subject+"-"+basename(fileName);var newFileUri=cordova.file.documentsDirectory;moveFile(fileName,destFilename);endSendForm(formdata,destFilename);};reader.readAsArrayBuffer(file);},errorHandler.bind(null,fileName));},errorHandler.bind(null,fileName));}
function distanceAutomatique(depart,arrivee,departSlug,arriveeSlug,situation){myDebug("distanceAutomatique : "+situation);let d=depart;let a=arrivee;if(situation=="depart"&&departSlug=="xxx")
return;if(departSlug&&departSlug!="xxx"){d=departSlug;}
if(situation=="arrivee"&&arriveeSlug=="xxx")
return;if(arriveeSlug&&arriveeSlug!="xxx"){a=arriveeSlug;}
if(situation=="depart"&&depart==""&&departSlug==""){ons.notification.alert("Merci de renseigner le point de départ (nom de ville ou code postal) !",{title:"Erreur #depart"});return;}
if(situation=="arrivee"&&arrivee==""&&arriveeSlug==""){ons.notification.alert("Merci de renseigner le point de d'arrivée (nom de ville ou code postal) !",{title:"Erreur #depart"});return;}
if(situation=="calcul"&&(depart==""||arrivee=="")){ons.notification.alert("Merci de renseigner le point de départ et d'arrivée...",{title:"Erreur #1"});return;}
if(depart=="")
d="null";if(arrivee=="")
a="null";showWait();$.ajaxSetup({timeout:5000,});result=undefined;if(ajaxDistance)
ajaxDistance.abort();var ajaxDistance=$.ajax({type:"GET",timeout:5000,url:"https://geo.doliscan.fr/api/geo/"+d+"/"+a,beforeSend:function(xhr){xhr.setRequestHeader('Authorization','Bearer '+localGetData("api_token"));},xhrFields:{withCredentials:true},success:function(result){myDebug('distanceAutomatique ajax ok1 / '+situation+" ::: "+JSON.stringify(result));console.log(result);if(result!=undefined){hideWait();if(situation=="depart"){popupChoixDepart(result);}
else if(situation=="arrivee"){popupChoixArrivee(result);}
else{if(result.distanceKM>0){updateDistanceFromServer(result);}
else{myDebug('distanceAutomatique ajax aucune réponse disponible ');ons.notification.alert("Le calcul automatique de distance n'a rien trouvé.\n\nVeuillez saisir la distance manuellement.",{title:"Erreur #1a"});}}}
else{hideWait();ons.notification.alert("Saisie manuelle nécessaire, cet itinéraire n'est pas connue sur nos serveurs",{title:"Erreur #1"});}},error:function(result){myDebug('distanceAutomatique ajax erreur'+result);hideWait();ons.notification.alert("Saisie manuelle nécessaire, cet itinéraire n'est pas connue sur nos serveurs",{title:"Erreur #2"});}});}
function searchVille(leform,ville,slug){myDebug("searchVille : "+ville);let objForm=document.getElementById(leform);let d=$('#'+ville).val();let a="";showWait();$.ajaxSetup({timeout:5000,});result=undefined;if(ajaxDistance)
ajaxDistance.abort();var ajaxDistance=$.ajax({type:"GET",timeout:5000,url:"https://geo.doliscan.fr/api/geo/"+d+"/"+a,beforeSend:function(xhr){xhr.setRequestHeader('Authorization','Bearer '+localGetData("api_token"));},xhrFields:{withCredentials:true},success:function(result){myDebug('searchVille ajax ok1 ::: '+JSON.stringify(result));console.log(result);if(result!=undefined){hideWait();if(result.departsNb==1){functionChooseVille(encodeHTMLEntities(result.depart)+" ("+result.departCP+")",result.departSlug,ville,slug,true);}
else{popupChoixVille(result,ville,slug);}}
else{hideWait();ons.notification.alert("Saisie manuelle nécessaire, cette ville n'est pas connue de nos serveurs",{title:"Erreur #1"});}},error:function(result){myDebug('searchVille ajax erreur'+result);hideWait();ons.notification.alert("Saisie manuelle nécessaire, cette ville n'est pas connue de nos serveurs",{title:"Erreur #2"});}});}
function popupChoixVille(result,ville,villeSlug){myDebug('popupChoixVille pour '+ville+" -- "+villeSlug);return new Promise((resolve,reject)=>{if(result.departsNb!==undefined){let btn=[];let btnNb=0;for(btnNb=0;btnNb<result.departsNb;btnNb++){btn.push({label:encodeHTMLEntities(result.depart[btnNb])+" ("+result.departCP[btnNb]+")",slug:result.departSlug[btnNb],ville:ville,villeSlug:villeSlug});}
if(globalMyNavigator===undefined)globalMyNavigator=document.getElementById('myNavigator');if(globalMyNavigator!==undefined){resolve(globalMyNavigator.pushPage("choix-liste.html",{data:{title:"Précisez la ville",callFunction:'functionChooseVille',btn:btn,btnNb:btnNb},}));}
else{myDebug('ERROR myNavigator does not exists !');reject(null);}}
else{myDebug('Pas de solution !');}});}
function functionChooseVille(label,slug,ville,villeSlug,single=false){myDebug("functionChooseVille "+label+" -- "+slug+" ["+ville+"]"+"{"+villeSlug+"}")
if(slug!==undefined){$('#'+ville).val(decodeURIComponent(label));$('#'+villeSlug).val(slug);if(globalMyNavigator===undefined)globalMyNavigator=document.getElementById('myNavigator');if(globalMyNavigator!==undefined){if(!single)
globalMyNavigator.popPage();}}
else{myDebug("functionChooseVille: slug is undef !")}}
function distanceAutomatiqueEtape(leform,etape,nb){let objForm=document.getElementById(leform);myDebug("distanceAutomatiqueEtape : "+nb);let a="null";let d="null";if(nb==1){d=objForm.elements['depart'].value;}
else{d=objForm.elements['etape'+(nb-1)].value;}
a=objForm.elements['etape'+nb].value;showWait();$.ajaxSetup({timeout:5000,});result=undefined;if(ajaxDistance)
ajaxDistance.abort();var ajaxDistance=$.ajax({type:"GET",timeout:5000,url:"https://geo.doliscan.fr/api/geo/"+d+"/"+a,beforeSend:function(xhr){xhr.setRequestHeader('Authorization','Bearer '+localGetData("api_token"));},xhrFields:{withCredentials:true},success:function(result){myDebug('distanceAutomatiqueEtape ajax ok1 / '+nb+" ::: "+JSON.stringify(result));console.log(result);if(result!=undefined){hideWait();popupChoixEtape(result,etape,nb);if(result.distanceKM>0){}
ajaxDistance.abort();var ajaxDistance=$.ajax({type:"GET",timeout:5000,url:"https://geo.doliscan.fr/api/geo/"+d+"/"+a,beforeSend:function(xhr){xhr.setRequestHeader('Authorization','Bearer '+localGetData("api_token"));},xhrFields:{withCredentials:true},success:function(result){myDebug('distanceAutomatiqueEtape ajax ok1 / '+nb+" ::: "+JSON.stringify(result));console.log(result);if(result!=undefined){hideWait();if(result.arriveesNb==1){functionChooseVille(encodeHTMLEntities(result.arrivee)+" ("+result.arriveeCP+")",result.arriveeSlug,etape,etape+'Slug',true);if(result.distanceKM>0){updateDistanceEtapeFromServer(result,etape,nb);}
else{myDebug('distanceAutomatiqueEtape ajax aucune réponse disponible ');ons.notification.alert("Le calcul automatique de distance n'a rien trouvé.\n\nVeuillez saisir la distance manuellement.",{title:"Erreur #1a"});}}
else{popupChoixEtape(result,etape,nb);}}
else{hideWait();ons.notification.alert("Saisie manuelle nécessaire, cet itinéraire n'est pas connue sur nos serveurs",{title:"Erreur #1"});}},error:function(result){myDebug('distanceAutomatiqueEtape ajax erreur'+result);hideWait();ons.notification.alert("Saisie manuelle nécessaire, cet itinéraire n'est pas connue sur nos serveurs",{title:"Erreur #2"});}});}
function popupChoixDepart(result){myDebug('popupChoixDepart');return new Promise((resolve,reject)=>{if(result.departsNb!==undefined){if(result.departsNb==1){functionChooseDepart2(encodeHTMLEntities(result.depart)+" ("+result.departCP+")",result.departSlug,true);}
else{let btn=[];let btnNb=0;for(btnNb=0;btnNb<result.departsNb;btnNb++){btn.push({label:encodeHTMLEntities(result.depart[btnNb])+" ("+result.departCP[btnNb]+")",slug:result.departSlug[btnNb]});}
......@@ -34,7 +32,7 @@ function popupChoixArrivee(result){return new Promise((resolve,reject)=>{if(resu
else{let btn=[];let btnNb=0;for(btnNb=0;btnNb<result.arriveesNb;btnNb++){btn.push({label:encodeHTMLEntities(result.arrivee[btnNb])+" ("+result.arriveeCP[btnNb]+")",slug:result.arriveeSlug[btnNb]});}
if(globalMyNavigator===undefined)globalMyNavigator=document.getElementById('myNavigator');if(globalMyNavigator!==undefined){resolve(globalMyNavigator.pushPage("choix-liste.html",{data:{title:"Précisez la ville d'arrivée",callFunction:'functionChooseArrivee2',btn:btn,btnNb:btnNb,},}));}
else{myDebug('ERROR myNavigator does not exists !');reject(null);}}}});}
function popupChoixEtape(result,etape,nb){myDebug('popupChoixEtape');ons.notification.alert("Etape ...",{title:"Etape #1"});}
function popupChoixEtape(result,etape,nb){myDebug('popupChoixEtape');}
function functionChooseArrivee2(label,slug,single=false){if(slug!==undefined){$('#arrivee').val(decodeURIComponent(label));$('#arriveeSlug').val(slug);$('#btnCalculDistance').attr('style',"border-radius: 5px; border:#00FF00 2px solid;");$('#btnCalculDistanceArrivee').attr('style',"");if(globalMyNavigator===undefined)globalMyNavigator=document.getElementById('myNavigator');if(globalMyNavigator!==undefined){if(!single)
globalMyNavigator.popPage();}
blinkElement("#btnCalculDistance",3,800);}
......@@ -44,6 +42,7 @@ globalMyNavigator.popPage();}
blinkElement("#btnCalculDistanceArrivee",3,800);}
else{myDebug("functionChooseDepart2: slug is undef !")}}
function updateDistanceFromServer(result){$('#distance').val(result.distanceKM);$('#btnCalculDistance').attr('style',"");}
function updateDistanceEtapeFromServer(result,etape,nb){$('#etape'+nb+'Distance').val(result.distanceKM);$('#distance').val();$('#btnCalculDistance').attr('style',"");}
function feedVehicules(storageKey){let htmlCode="";let html="";let htmlSingleVehicule="";let hasVehicule=false;let hasMultiVehicules=false;let v=localGetData(storageKey);if(v==null){return;}
vehicules=JSON.parse(v);for(let i=0;i<vehicules.length&&i<4;i++){let vA=vehicules[i]['nom'];let vE=vehicules[i]['energie'];let vP=vehicules[i]['puissance'];let vT=vehicules[i]['type'];let vKM=vehicules[i]['kmbefore'];let option=vA+";"+vE+";"+vP+";"+vT+";"+vKM;if(vA!==null){html+="<div class=\"segment__item\">";html+=" <input type=\"radio\" name=\"vehicule\" id=\"vehicule-"+i+"\" value=\""+option+"\" checked=\"checked\" class=\"segment__input\" />";html+=" <div class=\"segment__button\">"+vA+"</div>";html+="</div>";htmlSingleVehicule=option;if(hasVehicule==true){hasMultiVehicules=true;}
hasVehicule=true;}}
......
......@@ -18,7 +18,7 @@ var globalSyncInProgress = false; // Pour savoir si on est en phase de syn
var globalPictureFileName = ""; // Le nom du fichier sans résolution (depuis ios wkbidule app:// ... on garde dans cette variable le path basique pour resumable & sync)
// **********************************************************************
//Ne pas toucher, mis à jour automatiquement à partir du config.xml
var globalAppVersion = "1.5.0"; // mis à jour automatiquement à partir du fichier config.xml lors du build android
var globalAppVersion = "1.6.0"; // mis à jour automatiquement à partir du fichier config.xml lors du build android
// **********************************************************************
var dataLDF = "";
var dataIK = "";
......
......@@ -444,12 +444,12 @@ function functionChooseDepart2(label, slug, single = false) {
//Actualisation du champ "distance" du formulaire
function updateDistanceFromServer(result) {
$('#distanceTotale').val(result.distanceKM);
$('#distance').val(result.distanceKM);
$('#btnCalculDistance').attr('style', "");
}
function updateDistanceEtapeFromServer(result, etape, nb) {
$('#etape' + nb + 'Distance').val(result.distanceKM);
$('#distanceTotale').val();
$('#distance').val();
$('#btnCalculDistance').attr('style', "");
}
......@@ -338,7 +338,7 @@ function genericCheckForm(leform = null) {
let probleme = "";
// myDebug("genericCheckForm");
//Les deux tableaux doivent etre synchro, en haut le code de l'objet en bas le message à afficher
fieldsToValidate = ['nom', 'arrivee', 'depart', 'distanceTotale', 'energievalue', 'label', 'ladate', 'email', 'password', 'puissancevalue', 'ttc', 'typeFrais', 'typevalue', 'photoFacturette'];
fieldsToValidate = ['nom', 'arrivee', 'depart', 'distance', 'energievalue', 'label', 'ladate', 'email', 'password', 'puissancevalue', 'ttc', 'typeFrais', 'typevalue', 'photoFacturette'];
fieldsTexte = ["Nom", "Arrivée", "Départ", "Distance", "Type de carburant", "Objet", "La date", "Courrier électronique", "Mot de passe", "Puissance du véhicule", "Montant TTC", "Type de frais", "Type", "Photo de la facturette"];
let objForm = document.getElementById(leform);
......
......@@ -351,7 +351,7 @@ function completePageAfterGoto(dataLDF) {
document.getElementById("updateID").value = dataLDF['sid'];
document.getElementById("photoFacturette").value = "UPDATE";
fieldsToValidate = ['nom', 'arrivee', 'depart', 'distanceTotale', 'label', 'ladate', 'ttc', 'typeFrais', 'moyenPaiement', 'photoFacturette', 'tvaVal1', 'tvaVal2', 'tvaVal3', 'tvaVal4'];
fieldsToValidate = ['nom', 'arrivee', 'depart', 'distance', 'label', 'ladate', 'ttc', 'typeFrais', 'moyenPaiement', 'photoFacturette', 'tvaVal1', 'tvaVal2', 'tvaVal3', 'tvaVal4'];
for (let i = 0; i < fieldsToValidate.length; i++) {
field = fieldsToValidate[i];
//cas particuliers
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment