Upload de archivos al servidor - Página index.html

El primer archivo que analizaremos es el index.html, que tiene por objetivo desplegar el calendario de comidas, permitir agregar, borrar y visualizar fotos de comidas.

index.html
<!DOCTYPE html>
<html lang="es">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Calendario de Eventos</title>

  <link href="bootstrap-4.3.1/css/bootstrap.min.css" rel="stylesheet">
  <link href="fullcalendar-4.3.1/packages/core/main.css" rel="stylesheet">
  <link href="fullcalendar-4.3.1/packages/daygrid/main.css" rel="stylesheet">
  <link href="fullcalendar-4.3.1/packages/timegrid/main.css" rel="stylesheet">
  <link href="fullcalendar-4.3.1/packages/bootstrap/main.css" rel="stylesheet">


  <script src="js/jquery-3.4.1.js"></script>
  <script src="js/popper.min.js"></script>
  <script src="bootstrap-4.3.1/js/bootstrap.min.js"></script>
  <script src='js/moment-with-locales.js'></script>
  <script src='fullcalendar-4.3.1/packages/core/main.js'></script>
  <script src='fullcalendar-4.3.1/packages/daygrid/main.js'></script>
  <script src='fullcalendar-4.3.1/packages/timegrid/main.js'></script>
  <script src='fullcalendar-4.3.1/packages/interaction/main.js'></script>
  <script src='fullcalendar-4.3.1/packages/core/locales/es.js'></script>
  <script src='fullcalendar-4.3.1/packages/bootstrap/main.js'></script>
</head>

<body>
  <div class="container-fluid">
    <section class="content-header">
      <h1>
        Fotos de Comidas
      </h1>
    </section>

    <div class="row">

      <div class="col-12">
        <div id="Calendario1" style="border: 1px solid #000;padding:2px"></div>
      </div>

    </div>
  </div>


  <!-- FormularioEventos -->

  <div class="modal fade" id="FormularioEventos" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-label="Close">
            <span aria-hidden="true">×</span>
          </button>
        </div>
        <div class="modal-body">
          <input type="hidden" id="Codigo">
          <div class="form-row">
            <div class="form-group col-md-12">
              <label>Título del evento:</label>
              <input type="text" id="Titulo" class="form-control" placeholder="">
            </div>
          </div>
          <div class="form-row">
            <div class="form-group col-md-12">
              <label>Foto:</label>
              <input type="file" id="Archivo" class="form-control" placeholder="" name="Archivo">
            </div>
          </div>
          <div class="form-row">
            <div class="form-group col-md-12">
              <label>Fecha:</label>

              <div class="input-group" data-autoclose="true">
                <input type="date" id="FechaInicio" value="" class="form-control" readonly />
              </div>
            </div>
            <div class="form-group col-md-12">
              <label>Hora:</label>

              <div class="input-group" data-autoclose="true">
                <input type="text" id="HoraInicio" value="" class="form-control" readonly />
              </div>
            </div>
          </div>
        </div>
        <div class="modal-footer">

          <button type="button" id="BotonAgregar" class="btn btn-success">Agregar</button>
          <button type="button" class="btn btn-success" data-dismiss="modal">Cancelar</button>

        </div>
      </div>
    </div>
  </div>



  <!-- FormularioImagen -->
  <div class="modal fade" id="FormularioImagen" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-label="Close">
            <span aria-hidden="true">×</span>
          </button>
        </div>
        <div class="modal-body">
          <input type="hidden" id="CodigoFoto">
          <div class="form-row">
            <div class="form-group col-md-12">
              <label>Comida:</label>
              <input type="text" id="Comida" class="form-control" placeholder="" readonly>
            </div>
          </div>
          <div class="form-row">
            <div class="form-group col-md-12">
              <img id="Foto" src="#" style="width:100%">
            </div>
          </div>
        </div>
        <div class="modal-footer">
          <button type="button" id="BotonBorrar" class="btn btn-success">Borrar</button>
          <button type="button" class="btn btn-success" data-dismiss="modal">Cancelar</button>
        </div>
      </div>
    </div>
  </div>


  <script>
    document.addEventListener("DOMContentLoaded", function() {

      let calendario1 = new FullCalendar.Calendar(document.getElementById('Calendario1'), {
        plugins: ['dayGrid', 'timeGrid', 'interaction'],
        droppable: true,
        locale: 'es',
        showNonCurrentDates: false,
        header: {
          left: 'today,prev,next',
          center: 'title',
          right: ''
        },
        editable: false,
        events: 'datoseventos.php?accion=listar',
        height: "parent",
        dateClick: function(info) {
          limpiarFormulario();
          let fechaHora = info.dateStr.split("T");
          $('#FechaInicio').val(fechaHora[0]);
          $('#HoraInicio').val(moment(new Date()).format("HH:mm"));
          $("#FormularioEventos").modal();
        },
        eventClick: function(info) {
          $('#Comida').val(info.event.title + '  -  ' + moment(info.event.start).format("HH:mm - DD/MM/YYYY") + '');
          $("#Foto").attr("src", "fotos/" + info.event.id + '.jpg');
          $('#CodigoFoto').val(info.event.id);
          $("#FormularioImagen").modal();
        },
        eventRender: function(info) {
          info.el.children[0].innerHTML += '<br><span class="fc-content">' + '<img style="width:100%;border-radius:20%" src="fotos/' + info.event.id + '.jpg"></span>';
          return true;
        }
      });

      calendario1.render();

      //Eventos de botones de la aplicación
      $('#BotonAgregar').click(function() {
        agregarRegistro();
        $("#FormularioEventos").modal('hide');
      });

      $('#BotonBorrar').click(function() {
        borrarRegistro();
        $("#FormularioImagen").modal('hide');
      });


      // funciones para comunicarse con el servidor via ajax
      function agregarRegistro() {
        let archivo1 = $('#Archivo').prop('files')[0];
        let datos = new FormData();
        datos.append('Archivo', archivo1);
        datos.append('Titulo', $('#Titulo').val());
        datos.append('FechaInicio', $('#FechaInicio').val());
        datos.append('HoraInicio', $('#HoraInicio').val());
        $.ajax({
          type: 'POST',
          url: 'datoseventos.php?accion=agregar',
          data: datos,
          dataType: 'text',
          cache: false,
          contentType: false,
          processData: false,
          success: function(msg) {
            if (msg == "error")
              alert("No se puede subir ese formato de archivo");
            calendario1.refetchEvents();
          },
          error: function(error) {
            alert("Hay un problema:" + error);
          }
        });
      }

      function borrarRegistro(registro) {
        $.ajax({
          type: 'GET',
          url: 'datoseventos.php?accion=borrar&codigo=' + $('#CodigoFoto').val(),
          success: function(msg) {
            calendario1.refetchEvents();
          },
          error: function(error) {
            alert("Hay un problema:" + error);
          }
        });
      }

      function limpiarFormulario() {
        $('#Codigo').val('');
        $('#Titulo').val('');
        $('#Archivo').val('');
      }

    });
  </script>

</body>

</html>

Para desplegar el calendario debemos crear un objeto que nos provee la librería FullCalendar:

      let calendario1 = new FullCalendar.Calendar(document.getElementById('Calendario1'), {

Como hemos visto en ejemplos anteriores inicializamos una serie de propiedades que definen las características del calendario.

La propiedad events especifica el archivo JSON que retorna el servidor con los datos almacenanados actualmente en el calendario:

        events: 'datoseventos.php?accion=listar',

Cuando se hace clic sobre una fecha determinada se ejecuta la función anónima que hemos configurado en la propiedad 'dateClic':

        dateClick: function(info) {
          limpiarFormulario();
          let fechaHora = info.dateStr.split("T");
          $('#FechaInicio').val(fechaHora[0]);
          $('#HoraInicio').val(moment(new Date()).format("HH:mm"));
          $("#FormularioEventos").modal();
        },

Procedemos a borrar los datos del formulario de carga de comidas y lo hacemos visible llamando al método 'modal':

upload php

Cuando presionamos el botón 'Agregar' del formulario se ejecuta el método 'click' asociado al botón 'BotonAgregar', llamamos a la función 'agregarRegistro':

      $('#BotonAgregar').click(function() {
        agregarRegistro();
        $("#FormularioEventos").modal('hide');
      });

La función 'agregarRegistro' procede a crear un objeto de la clase FormData, recuperar los datos del formulario cargando los mismos en el objeto creado y proceder mediante AJAX a enviar los datos al servidor (el objeto que enviamos contiene los datos del archivo adjuntado):

      function agregarRegistro() {
        let archivo1 = $('#Archivo').prop('files')[0];
        let datos = new FormData();
        datos.append('Archivo', archivo1);
        datos.append('Titulo', $('#Titulo').val());
        datos.append('FechaInicio', $('#FechaInicio').val());
        datos.append('HoraInicio', $('#HoraInicio').val());
        $.ajax({
          type: 'POST',
          url: 'datoseventos.php?accion=agregar',
          data: datos,
          dataType: 'text',
          cache: false,
          contentType: false,
          processData: false,
          success: function(msg) {
            if (msg == "error")
              alert("No se puede subir ese formato de archivo");
            calendario1.refetchEvents();
          },
          error: function(error) {
            alert("Hay un problema:" + error);
          }
        });
      }

La propiedad 'eventClick' del calendario tiene por objetivo mostrar un diálogo con la foto de la comida en un tamaño mayor al que aparece en el calendario:

        eventClick: function(info) {
          $('#Comida').val(info.event.title + '  -  ' + moment(info.event.start).format("HH:mm - DD/MM/YYYY") + '');
          $("#Foto").attr("src", "fotos/" + info.event.id + '.jpg');
          $('#CodigoFoto').val(info.event.id);
          $("#FormularioImagen").modal();
        },

El resultado en pantalla es:

upload php

Para que se muestren las imágenes de las comidas dentro del calendario debemos especificar la propiedad 'eventRender' de FullCalendar:

        eventRender: function(info) {
          info.el.children[0].innerHTML += '<br><span class="fc-content">' + '<img style="width:100%;border-radius:20%" src="fotos/' + info.event.id + '.jpg"></span>';
          return true;
        }
      });

Como veremos luego se almacenan las fotos con un nombre que coincide con el 'id' del evento del calendario y la extensión del archivo siempre es 'jpg':

upload php

Por último podemos eliminar una 'comida' registrada en el calendario al presionar el botón de 'Borrar':

      $('#BotonBorrar').click(function() {
        borrarRegistro();
        $("#FormularioImagen").modal('hide');
      });

La función 'borrarRegistro' mediante AJAX procede a solicitar al servidor que se de de baja el código de evento respectivo:

      function borrarRegistro(registro) {
        $.ajax({
          type: 'GET',
          url: 'datoseventos.php?accion=borrar&codigo=' + $('#CodigoFoto').val(),
          success: function(msg) {
            calendario1.refetchEvents();
          },
          error: function(error) {
            alert("Hay un problema:" + error);
          }
        });
      }

En el próximo concepto veremos las tres conexiones AJAX que se efectúan con el servidor desde ésta página.