Ir al contenido

Archivos y correo ​

Traducción automática

Esta página se tradujo automáticamente del inglés y todavía no la revisó un hablante nativo; las correcciones son bienvenidas en GitHub. Si algo no coincide, vale el original en inglés.

Un formulario puede recibir un archivo, una página puede devolver uno, y una acción puede enviar correo. projects/helpdesk usa las tres cosas: tickets con un adjunto, un correo al equipo de soporte y otro a quien hizo el pedido.

Recibir un archivo ​

Declara el archivo como un parámetro de la acción. accept y maxsize son reglas como minlength: un archivo que no las cumple nunca llega a la acción, y el formulario muestra el motivo junto al campo.

xml
<q:action name="open" method="POST">
  <q:param name="title" required="true" minlength="5" />
  <q:param name="attachment" type="file" maxsize="5MB" accept=".png,.jpg,.pdf" />

  <q:if condition="attachment">
    <q:file action="upload" file="{attachment}" result="saved" />
    <!-- saved.filename, saved.original_filename, saved.size, saved.mimetype -->
  </q:if>
  <q:redirect url="/" flash="Ticket opened: {title}" />
</q:action>

<ui:form on-submit="open" submit="Open ticket">
  <ui:input bind="title" />
  <ui:input bind="attachment" />
</ui:form>

El formulario sabe que la acción recibe un archivo: envía multipart/form-data, y attachment es un campo de archivo que ofrece .png,.jpg,.pdf. No escribes ninguna de las dos cosas.

El archivo se guarda en paths.uploads (./uploads salvo que la configuración diga otra cosa), con un nombre seguro; destination="invoices" lo pone en una carpeta dentro de ella. Guarda saved.filename — es la forma de volver a encontrar el archivo.

q:file action es upload, delete o send (más abajo); cualquier otra cosa no pasa el análisis:

xml
<q:file action="copy" file="{attachment}" />

Error: <q:file action="copy">: use upload, delete or send

Un campo de varias líneas es <ui:input bind="description" rows="6" />.

Devolver un archivo ​

Los archivos subidos no se sirven como archivos estáticos: cualquiera con la URL podría leerlos. Una página envía uno, y decide quién puede tenerlo:

xml
<!-- components/attachment/[id].q -->
<q:component name="Attachment">
  <q:query name="ticket" datasource="db">
    SELECT attachment, attachment_name FROM tickets WHERE id = :id AND attachment IS NOT NULL
    <q:param name="id" value="{id}" type="integer" />
  </q:query>
  <q:if condition="ticket_result.recordCount == 1">
    <q:file action="send" file="{ticket[0].attachment}" name="{ticket[0].attachment_name}" />
  </q:if>
  <p>There is no attachment for ticket #{id}.</p>
</q:component>

q:file action="send" termina la página con el archivo como descarga. El nombre guardado viene de la base de datos, nunca de la URL; una ruta fuera de paths.uploads se rechaza, y un archivo que no existe responde 404. Pon una guarda en la página (require_auth, o un q:if con q:redirect) y solo lo recibe quien corresponde.

Enviar correo ​

Una acción envía un mensaje con q:mail; el servidor es la sección mail: de quantum.config.yaml. De la receta Correo en desarrollo, probada tal como se muestra:

xml
<q:component name="Contact">
  <q:action name="send" method="POST">
    <q:param name="email" type="email" required="true" />
    <q:param name="message" required="true" minlength="5" />
    <q:mail to="support@example.com" reply_to="{email}" subject="Contact from {email}" type="text">{message}</q:mail>
    <q:redirect url="/" flash="Thanks! We will answer {email}." />
  </q:action>

  <h1>Contact us</h1>
  <q:if condition="flash"><p class="flash">{flash}</p></q:if>
  <form method="POST" action="/?action=send">
    <input name="email" type="email" />
    <textarea name="message"></textarea>
    <button>Send</button>
  </form>
</q:component>
yaml
# Recipe: mail in development. `host: log` writes each message to the log
# instead of sending it; set SMTP_HOST in production and the same page sends.
paths:
  components: ./components

mail:
  host: ${SMTP_HOST:-log}
  from: support@example.com

host: log escribe cada mensaje en el log en lugar de enviarlo — úsalo en desarrollo. Sin una sección mail:, q:mail es un error que lo dice; nunca finge que envía:

xml
<q:mail to="ana@example.com" subject="Welcome">Hello, Ana!</q:mail>

Error: q:mail needs a mail server

Cuando el servidor dice que no ​

Un mensaje que el servidor no acepta detiene la acción con el motivo del servidor. Cuando el resto de la acción debe ocurrir igual, pon onerror="continue" y verifica <name>_result.success — de la receta Cuando el servidor de correo dice que no, donde el pedido se guarda aunque no se pueda enviar su confirmación:

xml
<q:component name="Order">
  <q:action name="order" method="POST">
    <q:param name="email" type="email" required="true" />
    <q:param name="item" required="true" />
    <q:query name="saved" datasource="db">
      INSERT INTO orders (email, item) VALUES (:email, :item)
      <q:param name="email" value="{email}" type="string" />
      <q:param name="item" value="{item}" type="string" />
    </q:query>

    <!-- onerror="continue": a refused message does not undo the order -->
    <q:mail name="confirmation" to="{email}" subject="Your order: {item}"
            type="text" onerror="continue">We received your order for {item}.</q:mail>

    <q:if condition="confirmation_result.success">
      <q:redirect url="/" flash="Ordered {item}. A confirmation is on its way." />
    </q:if>
    <q:redirect url="/" flash="Ordered {item}. We could not send the confirmation e-mail." />
  </q:action>

  <h1>Order</h1>
  <q:if condition="flash"><p class="flash">{flash}</p></q:if>
  <form method="POST" action="/?action=order">
    <input name="email" type="email" />
    <input name="item" />
    <button>Order</button>
  </form>
</q:component>

El helpdesk (projects/helpdesk) hace lo mismo: guarda primero el ticket y dice, en su mensaje, cuándo no se pudo enviar un correo.

Reglas ​

FILE-1, FILE-2, MAIL-1, MAIL-2 y UI-14 en la SPEC.

Licencia MIT · Hecho con VitePress