2022-10-22

Login system with routing and data filtering by user

I am trying to make a Web app using Apps Script. At the moment I am trying to implement a login to at least get a username and password, but I can't get it to enter the page and that form of login is not very secure since you can see the credentials in the code

I place several parts of the code so that it can be interpreted as I move between the different options

Before trying to login I entered like this to access the page

and so I select with buttons in the nav the pages through which the user moves through buttons in the menuNav

EDIT POST COMMENST--------->Main.html

    <!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8"> 
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap demo</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">

<style>
      .nav-link {
          cursor:pointer;
      }

        body{
          margin-top:20px;
         // background: #ededed;

          background: rgba(255,255,255,1));
         //background: linear-gradient(325deg, rgba(6,0,0,1) 0%, rgba(255,255,255,1) 30%);
      }

      

      .bg-gradient {
        background-image: var(.bg-dark.bg-gradient);
      }
      

      .bg-success {
        --bs-bg-opacity: 1;
        background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important;
      }

      .update-nag{
        display: inline-block;
        font-size: 14px;
        text-align: left;
        background-color: #fff;
        height: 40px;
        -webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.2);
        box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
        margin-bottom: 10px;
      }

      .update-nag:hover{
          cursor: pointer;
          -webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.4);
        box-shadow: 0 1px 1px 0 rgba(0,0,0,.3);
      }

      .update-nag > .update-split{
        background: #337ab7;
        width: 33px;
        float: left;
        color: #fff!important;
        height: 100%;
        text-align: center;
      }

      .update-nag > .update-split > .glyphicon{
        position:relative;
        top: calc(50% - 9px)!important; /* 50% - 3/4 of icon height */
      }
      .update-nag > .update-split.update-success{
        background: #5cb85c!important;
      }

      .update-nag > .update-split.update-danger{
        background: #d9534f!important;
      }

      .update-nag > .update-split.update-info{
        background: #5bc0de!important;
      }



      .update-nag > .update-text{
        line-height: 19px;
        padding-top: 11px;
        padding-left: 45px;
        padding-right: 20px;
      }

</style>

  </head>
    <body>
      <div class="container-fluid">

    <div class="toast-container position-fixed bottom-0 end-0 p-3">
      <div id="liveToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
      
        <div class="toast-header">
          <img src="xxxxxxx" class="rounded me-2" alt="" width="70" height="35">
          <strong class="me-auto">Panel Instancias</strong>
          <small>1seg ago</small>
          <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
        </div>
        <div class="toast-body text-dark">
          Menu activado.
        </div>
      </div>
    </div>
  <!-- Content here -->

                    <div class="row g-1 ">
                        <div class="col-sm-1 col-md-1 position-relative">
                          <button class="btn btn-outline-dark shadow position-absolute top-50 start-50 translate-middle" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasWithBothOptions" id="liveToastBtn" aria-controls="offcanvasWithBothOptions">Menu</button>
                        </div>

                        <div class="col-1 col-md-1">
                          <a class="navbar-brand">
                            <img src="xxxxxx" alt="" width="270" height="70" class="d-inline-block align-text-middle">  </a>
                        </div>
                        <p class="fw-bold"><h5> </h5></p>
                    </div>

                <div class="offcanvas offcanvas-start shadow show text-bg-dark bg-dark  " data-bs-scroll="true" tabindex="1"  id="offcanvasWithBothOptions" aria-labelledby="offcanvasWithBothOptionsLabel">
                    <div class="offcanvas-header">
                      <h5 class="offcanvas-title" id="offcanvasWithBothOptionsLabel">Menu</h5>
                      <button type="button" class="btn-close btn-close-white" id="liveToastBtn" data-bs-dismiss="offcanvas" aria-label="Close"></button>

                    </div>
                   
                  <div class="offcanvas-body p-3 mb-2 bg-dark pe-auto text-white "> 
                          <div class="dropdown pe-auto">
                            <button class="btn btn-light dropdown-toggle" type="button" class="bi bi-send-plus" data-bs-toggle="dropdown" aria-expanded="false">
                                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-send-plus" viewBox="0 0 16 16">
                                    <path d="M15.964.686a.5.5 0 0 0-.65-.65L.767 5.855a.75.75 0 0 0-.124 1.329l4.995 3.178 1.531 2.406a.5.5 0 0 0 .844-.536L6.637 10.07l7.494-7.494-1.895 4.738a.5.5 0 1 0 .928.372l2.8-7Zm-2.54 1.183L5.93 9.363 1.591 6.602l11.833-4.733Z"/>
                                    <path d="M16 12.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0Zm-3.5-2a.5.5 0 0 0-.5.5v1h-1a.5.5 0 0 0 0 1h1v1a.5.5 0 0 0 1 0v-1h1a.5.5 0 0 0 0-1h-1v-1a.5.5 0 0 0-.5-.5Z"/>
                                  </svg>
                              Crear Instancia Nueva
                            </button>
                            <ul class="dropdown-menu pe-auto" class="pe-auto">
                              <li><a class="dropdown-item" target="_blank" href="https://www.totoprayogo.com/" >Marketing</a></li>
                              <li><a class="dropdown-item">RRHH</a></li>
                              <li><a class="dropdown-item">Operaciones</a></li>
                              <li><a class="dropdown-item">Contabilidad</a></li>
                              <li><a class="dropdown-item">Compras</a></li>
                              <li><a class="dropdown-item">Mantenimiento</a></li>
                                  <li>
                                    <hr class="dropdown-divider">
                                  </li>
                              <div class="alert alert-warning alert-dismissible fade show" role="alert">
                                <strong>Eres Nuevo? no te preocupes</strong> Selecciona a que departamento quieres crear una instancia.
                                <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                              </div>
                            </ul>
                          </div>

                          <div class="btn-group-vertical" role="group" aria-label="Vertical button group">
                            <button type="button" class="btn btn-dark"id="noticias-link">Noticias</button>
                            <button type="button" class="btn btn-dark"id="instancias-link">Buscador</button>
                            <button type="button" class="btn btn-dark"id="addinstancias-link">Añadir Instancias</button>
                            <button type="button" class="btn btn-dark"id="modal2-link">Modal2 test</button>

                            <button type="button" class="btn btn-dark" tabindex="-1" aria-disabled="true">Sueño Humedo</button>

                            <button type="button" class="btn btn-dark invisible"id="editarinstancias-link">Editar Instancias</button>
                          </div>

                          
                              <div class="card text-bg-light mb-3" style="max-width: 18rem;">
                                <div class="card-header">Ayuda</div>
                                <div class="card-body">
                                  <h5 class="card-title">ATENCION!!!</h5>
                                  <p class="card-text">xxxxxxxxxxx.</p>
                                </div>
                              </div>
                              <div class="alert alert-warning" role="alert">
                                Selecciona la opcion que mas te convenga
                              </div>
                           </div>
                    </div>
                </div>

  <div id="app"></div>

    <div id="loading"></div>



       

    <script 

src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8" crossorigin="anonymous"></script>
         <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js" integrity="sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3" crossorigin="anonymous"></script>
       <!-- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.min.js" integrity="sha384-7VPbUDkoPSGFnVtYi0QogXtr74QeVeeIs99Qfg5YCF+TidwNdjvaKZX19NZ/e6oz" crossorigin="anonymous"></script>-->

  <script>
      var data;

    const toastTrigger = document.getElementById('liveToastBtn')
    const toastLiveExample = document.getElementById('liveToast')
    if (toastTrigger) {
      toastTrigger.addEventListener('click', () => {
        const toast = new bootstrap.Toast(toastLiveExample)

        toast.show()
      })
    }

  function loadView(options){
      var id = typeof options.id === "undefined" ? "app" : options.id;
      var cb = typeof options.callback === "undefined" ? function(){} : options.callback;

      google.script.run.withSuccessHandler(function(html){
          document.getElementById(id).innerHTML = html;
          typeof options.params === "undefined" ?  cb() : cb(options.params);
      })[options.func]();
  }
  
  function setDataForSearch(){
    google.script.run.withSuccessHandler(function(dataReturned){
      data = dataReturned.slice();
    }).getDataForSearch();
  }

  function search(){

    var searchInput = document.getElementById("searchInput").value.toString().toLowerCase().trim();
    var searchWords = searchInput.split(/\s+/);
    var searchColumns = [1,2,3,4,5,6,7,8,9];
    // and or 

    var resultsArray = searchInput === "" ? [] : data.filter(function(r){

      return searchWords.every(function(word){
        return searchColumns.some(function(colIndex){
          return r[colIndex].toString().toLowerCase().indexOf(word) !== -1
        });
      });
    });
    var searchResultsBox = document.getElementById("searchResults");
    var templateBox = document.getElementById("rowTemplate");
    var template = templateBox.content;

    searchResultsBox.innerHTML = "";

    resultsArray.forEach(function(r){

      var tr = template.cloneNode(true);
      var custIDColumn = tr.querySelector(".custID");
      var idInstColumn = tr.querySelector(".idInst");
      var localColumn = tr.querySelector(".local");
      var contactoColumn = tr.querySelector(".contacto");
      var departamentoColumn = tr.querySelector(".departamento");
      var tipoColumn = tr.querySelector(".tipo");
      var deleteButton = tr.querySelector(".delete-button");
      var editButton = tr.querySelector(".edit-button");
      var procesoButton = tr.querySelector(".proceso-button");
      var terminadaButton = tr.querySelector(".terminada-button");



      custIDColumn.textContent = r[0];
      deleteButton.dataset.customerId = r[0];
      editButton.dataset.customerId = r[0];
      procesoButton.dataset.customerId = r[0];
      terminadaButton.dataset.customerId = r[0];
      localColumn.textContent = r[1];
      contactoColumn.textContent = r[2];
      departamentoColumn.textContent = r[3];
      tipoColumn.textContent = r[4];
      idInstColumn.textContent = r[11];
      
      searchResultsBox.appendChild(tr);
    });

  }
        /*
      var tipoColumn = tr.querySelector(".tipo");
      var espcifico1Column = tr.querySelector(".espcifico1");        
      var especifico2Column = tr.querySelector(".especifico2");
      var refColumn = tr.querySelector(".ref");
      var timestampColumn = tr.querySelector(".timestamp");
           */     

           /* 
      tipoColumn.textContent = r[6];
      espcifico1Column.textContent = r[7];     
      espcifico2Column.textContent = r[8];
      refColumn.textContent = r[9];
      timestampColumn.textContent = r[10];
           */  

  function displayConfirmationDelete(e){
     if(e.target.dataset.buttonState === "delete"){
      e.target.previousElementSibling.classList.remove("d-none");
      e.target.textContent = "Cancelar";
      e.target.dataset.buttonState = "cancel";
    } else {
      e.target.previousElementSibling.classList.add("d-none");
      e.target.textContent = "Delete";
      e.target.dataset.buttonState = "delete";
    }
  }

    function displayConfirmationEstado(e){
     if(e.target.dataset.buttonState === "estado"){
      e.target.previousElementSibling.classList.remove("d-none");
      e.target.nextElementSibling.classList.remove("d-none");
      e.target.textContent = "Cancelar";
      e.target.dataset.buttonState = "cancel";
    } else {
      e.target.previousElementSibling.classList.add("d-none");
      e.target.nextElementSibling.classList.add("d-none");
      e.target.textContent = "Estado";
      e.target.dataset.buttonState = "estado";
    }
  }

  function deleteCustomer(e){
    var custID = e.target.dataset.customerId;
    google.script.run.withSuccessHandler(function(){
      e.target.closest(".result-box").remove();
      var ids = data.map(function(r){return r[0].toString().toLowerCase() });
      var index = ids.indexOf(custID.toString().toLowerCase());
      data.splice(index,1);
     }).deleteById(custID);
    
  }

  function afterEditViewLoads(params){
    //{custID: 32}
    document.getElementById("customer-id").value  = params.custID;
    google.script.run.withSuccessHandler(function(customerInfo){
      document.getElementById("local").value = customerInfo.local;
      document.getElementById("contacto").value = customerInfo.contacto;
      document.getElementById("departamento").value = customerInfo.departamento;
    }).getCustomerById(params.custID);
  }

  function editCustomer(){
      var customerInfo = {};
      customerInfo.local = document.getElementById("local").value;
      customerInfo.contacto = document.getElementById("contacto").value;
      customerInfo.departamento = document.getElementById("departamento").value;

      var id = document.getElementById("customer-id").value;

      google.script.run.withSuccessHandler(function(res){

        document.getElementById("editar-message").classList.remove("invisible");
        setTimeout(function(){
        document.getElementById("editar-message").classList.add("invisible");
        },2000);

      }).editCustomerById(id,customerInfo);

  }
      function addCustomer(){

          var customerInfo = {};
          customerInfo.local = document.getElementById("local").value;
          customerInfo.contacto = document.getElementById("contacto").value;
          customerInfo.departamento = document.getElementById("departamento").value;

          google.script.run.withSuccessHandler(function(){

            document.getElementById("local").value = "" ;
            document.getElementById("contacto").value = "" ;
            document.getElementById("departamento").value = "" ;

        document.getElementById("add-message").classList.remove("invisible");
        setTimeout(function(){
        document.getElementById("add-message").classList.add("invisible");
        },2000);

        }).addCustomer(customerInfo);
  }

  function loadInstanciasView(){
    loadView({func: "loadInstanciasView", callback: setDataForSearch });
  }

  function loadAddInstanciasView(){
    loadView({func: "loadAddInstanciasView"});
  }

  function loadEditarInstanciasView(){
    loadView({func: "loadEditarInstanciasView"});
  }

  function loadNoticiasView(){
    loadView({func: "loadNoticiasView"});
  }

    function loadModal2View(){
    loadView({func: "loadModal2View"});
  }
  //    function loadmainView(){
  //  loadView({func: "loadmainView"});
  //}



  document.getElementById("instancias-link").addEventListener("click",loadInstanciasView);
  document.getElementById("addinstancias-link").addEventListener("click",loadAddInstanciasView);
  document.getElementById("editarinstancias-link").addEventListener("click",loadEditarInstanciasView);
  document.getElementById("noticias-link").addEventListener("click",loadNoticiasView);
  document.getElementById("modal2-link").addEventListener("click",loadModal2View);
  //document.getElementById("main-link").addEventListener("click",loadmainView);



  

  function inputEventHandler(e){
    if(e.target.matches("#searchInput")){
      search();
    }
  }
  function clickEventHandler(e){
    if(e.target.matches(".delete-button")){
      deleteCustomer(e);
    }
    if(e.target.matches(".before-delete-button")){
      displayConfirmationDelete(e);
    }
    if(e.target.matches(".terminada-button")){
      terminadaCustomer(e);
    }
    if(e.target.matches(".proceso-button")){
      procesoCustomer(e);
    }
    if(e.target.matches(".before-estado-button")){
      displayConfirmationEstado(e);
    }
    if(e.target.matches(".edit-button")){
     loadView({func: "loadEditarInstanciasView", callback: afterEditViewLoads, params: {custID: e.target.dataset.customerId} });
    }
    if(e.target.matches("#save-changes")){
      editCustomer();
    }
    if(e.target.matches("#cancel-changes")){
      loadInstanciasView();
    }
    if(e.target.matches("#add-customer-button")){
      addCustomer();
    }



  }
  document.getElementById("app").addEventListener("input",inputEventHandler);
  document.getElementById("app").addEventListener("click",clickEventHandler);

  </script>
</div>
  </body>
</html>

Instancias.html----->

<p class="fw-bold"><h1>Buscador</h1></p>

<form>
  <div class="mb-3">
    <input type="text" class="form-control border-success border" list="datalistOptions" id="searchInput" placeholder="Escribe lo que necesites....">
      <datalist id="datalistOptions">
        <option value="RRHH">
        <option value="Mkt">
        <option value="Operaciones">
        <option value="Mantenimiento">
        <option value="Informatica">
      </datalist>

    <div id="AyudaBusqueda" class="form-text">formulario de busqueda.</div>
  </div>

<table class="table table-hover align-middle table-striped table-responsive-xxl overflow-scroll shadow-lg rounded  ">
  <thead class="table-dark align-middle text-center shadow-lg ">
    <tr>
      <th scope="col">CustID</th>
      <th scope="col">IdInst</th>
      <th scope="col">Local</th>
      <th scope="col">Contacto</th>
      <th scope="col">Departamento</th>
      <th scope="col">Tipo</th>



      <th scope="col"></th>
      <th scope="col"></th>
      <th scope="col"></th>
      <th scope="col"></th>
    </tr>
  </thead>
  <tbody id="searchResults">

  </tbody>
</table>

<template id="rowTemplate">
    <tr class="result-box shadow-sm table-responsive-xxl rounded align-middle" >
      <th class="custID text-center" scope="row"></th>
      <td class="idInst"></td>
      <td class="local text-center"></td>
      <td class="contacto text-center"></td>
      <td class="departamento text-center"></td>
      <td class="tipo text-center"></td>

      <td>
        <div class="btn-group" role="group">

          <button type="button" class="btn btn-outline-success shadow-sm terminada-button d-none">Terminada</button>
          <button type="button" class="btn btn-outline-dark shadow-sm before-estado-button" data-button-state="estado">Estado</button>
          <button type="button" class="btn btn-outline-warning shadow-sm proceso-button d-none">Proceso</button>
          
        </div>
      </td>
      <td>
        <button type="button" class="btn btn-outline-dark shadow-sm edit-button">Editar</button></td>
      <td>
        <div class="btn-group" role="group">
          <button type="button" class="btn btn-danger shadow-sm delete-button d-none">Confirmar</button>
          <button type="button" class="btn btn-outline-dark shadow-sm before-delete-button" data-button-state="delete">Borrar</button>
        </div>
      </td>

    </tr>
</template>

<!--
      <th scope="col">tipo</th>
      <th scope="col">especifico1</th>    
      <th scope="col">especifico2</th>
      <th scope="col">ref</th>
      <th scope="col">timestamp</th>
       
 -->

  <!--
      <td class="tipo"></td>
      <td class="especifico1"></td>      
      <td class="especifico2"></td>
      <td class="ref"></td>
      <td class="timestamp"></td>
      
 -->

Loadpartials.html----->

function loadPartialHTML_(partial) {
 const htmlServ = HtmlService.createTemplateFromFile(partial);
 return htmlServ.evaluate().getContent();
}
function loadInstanciasView(){

  return loadPartialHTML_("instancias");

}
function loadAddInstanciasView(){

  return loadPartialHTML_("addinstancias");

}
function loadEditarInstanciasView(){

  return loadPartialHTML_("editarinstancias");

}
function loadNoticiasView(){

  return loadPartialHTML_("noticiasview");

}

ServerSideFuncs.gs----->

function getDataForSearch() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const ws = ss.getSheetByName("1");
  return ws.getRange(2, 1, ws.getLastRow()-1, 12).getDisplayValues();
}

function deleteById(id){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const ws = ss.getSheetByName("1");
  const custIds = ws.getRange(2, 1, ws.getLastRow()-1, 1).getValues().map(r => r[0].toString().toLowerCase());
  const posIndex = custIds.indexOf(id.toString().toLowerCase());
  const rowNumber = posIndex === -1 ? 0 : posIndex + 2;
  ws.deleteRow(rowNumber);
}

function getCustomerById(id){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const ws = ss.getSheetByName("1");
  const custIds = ws.getRange(2, 1, ws.getLastRow()-1, 1).getDisplayValues().map(r => r[0].toString().toLowerCase());
  const posIndex = custIds.indexOf(id.toString().toLowerCase());
  const rowNumber = posIndex === -1 ? 0 : posIndex + 2;
  const customerInfo = ws.getRange(rowNumber, 1, 1, 4).getDisplayValues()[0];
  //[[45,"Joe"]]
  return { custID: customerInfo[0], local: customerInfo[1], contacto: customerInfo[2], departamento: customerInfo[3] };
}

function editCustomerById(id,customerInfo){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const ws = ss.getSheetByName("1");
  const custIds = ws.getRange(2, 1, ws.getLastRow()-1, 1).getDisplayValues().map(r => r[0].toString().toLowerCase());
  const posIndex = custIds.indexOf(id.toString().toLowerCase());
  const rowNumber = posIndex === -1 ? 0 : posIndex + 2;
  ws.getRange(rowNumber, 2,1,3).setValues([[
                                            customerInfo.local,
                                            customerInfo.contacto,
                                            customerInfo.departamento
                                          ]]);
  return true;                                                        
}

function addCustomer(customerInfo){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const ws = ss.getSheetByName("1");
  const uniqueIDs = ws.getRange(2, 1, ws.getLastRow()-1, 1).getValues();
  var maxNum = 0;
  uniqueIDs.forEach(r => {
    maxNum = r[0] > maxNum ? r[0] : maxNum
  });
  
  var newID = maxNum +1;

  ws.appendRow([
                newID,
                customerInfo.local,
                customerInfo.contacto,
                customerInfo.departamento
               ]);
}

loadforms.gs----->

function loadMainForm() {
  
 const htmlServ = HtmlService.createTemplateFromFile("main");
 const html = htmlServ.evaluate();
 html.setHeight(1200).setWidth (2000);
 const ui = SpreadsheetApp.getUi();
 ui.showModalDialog(html, "xxxxxxxxxx");
}

function loadForm2() {
  
 const htmlForSidebar = HtmlService.createTemplateFromFile("uform2");
 const htmlOutput = htmlForSidebar.evaluate();

 const ui = SpreadsheetApp.getUi();
 ui.showSidebar(htmlOutput);
}

function createMenu_(){

  const ui = SpreadsheetApp.getUi();
  const menu = ui.createMenu("InstanciasPrueba");
  menu.addItem("Instancia", "loadMainForm");
  menu.addItem("Instancia2", "loadForm2");
  menu.addItem("URL", "testNew");
  menu.addToUi();

}

function onOpen(){
 
  createMenu_();

}

//function doGet() {
  //let layout = HtmlService.createTemplateFromFile("main"); 
  //return layout.evaluate();
//}

function doGet(request) {
  return HtmlService.createTemplateFromFile('main').evaluate()
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

    function testNew(){
showAnchor('Stackoverflow','http://stackoverflow.com/questions/tagged/google-apps-script');
    }
    function showAnchor(name,url) {
      var html = '<html><body><a href="'+url+'" target="blank" onclick="google.script.host.close()">'+name+'</a></body></html>';
      var ui = HtmlService.createHtmlOutput(html)
      SpreadsheetApp.getUi().showModelessDialog(ui,"demo");
    }

As I have mentioned before, I am starting from scratch to program and in a self-taught way, I know that I have mistakes and I know that I have errors, but that is why I am here, to learn to do things better



No comments:

Post a Comment