20. Escribiendo Viewlets

Un viewlet para el comportamiento social

Un viewlet no es una vista, pero es un fragmento de HTML y la lógica Python que se puede colocar en varios lugares en el sitio. Estos lugares se llaman ViewletManager.

  • Inspeccione y administre los viewlets existentes yendo a la dirección URL http://localhost:8080/Plone/@@manage-viewlets.

  • Ya hemos personalizado un viewlet (el archivo collophon.pt). Ahora agregamos un nuevo viewlet.

  • Los viewlets no guardan los datos (los portlets si lo hacen).

  • Los viewlets no tienen interfaz de usuario (los portlets lo hacen).

El viewlet social-viewlet

Vamos a añadir un enlace al sitio que utiliza la información que hemos recopilado utilizando el comportamiento social.

Registramos el viewlet en el archivo browser/configure.zcml, agregando el siguiente código ZCML:

1
2
3
4
5
6
7
8
9
<browser:viewlet
  name="social"
  for="ploneconf.site.behaviors.social.ISocial"
  manager="plone.app.layout.viewlets.interfaces.IBelowContentTitle"
  class=".viewlets.SocialViewlet"
  layer="zope.interface.Interface"
  template="templates/social_viewlet.pt"
  permission="zope2.View"
  />

Esto registra un viewlet llamado social. Es visible en todos los contenidos que implementa la interfaz ISocial de nuestro comportamiento. También es una buena práctica de obligar a la BrowserLayer llamada IPloneconfSiteLayer de nuestro complemento para que sólo sea mostrado si nuestro complemento esta instalado en realidad.

La clase viewlet SocialViewlet se espera en un archivo browser/viewlets.py, agregando el siguiente código Python:

1
2
3
4
from plone.app.layout.viewlets import ViewletBase

class SocialViewlet(ViewletBase):
    pass

Esta clase no hace nada excepto hacer que la plantilla asociada (Esa tenemos aún que escribirla)

Nota

Si utilizamos Grok no necesitaríamos registrar los viewlets en el archivo configure.zcml pero si hay que hacerlo en código Python. Nos gustaría añadir un archivo viewlets.py que contiene la clase viewlet, la cual se presenta a continuación:

1
2
3
4
5
6
from five import grok
from plone.app.layout.viewlets import interfaces as viewletIFs
from zope.component import Interface

class SocialViewlet(grok.Viewlet):
    grok.viewletmanager(viewletIFs.IBelowContentTitle)

Esto haría lo mismo que el código anterior utilizando el paradigma de Grok de la convención sobre la configuración. En las browser views la referencia es llamada view, usted tenga en cuenta que en las viewlets Grok se llama viewlets (en ese caso viewlet/lanyrd_link).

Vamos a agregar el archivo de la plantilla restante templates/social_viewlet.pt.

1
2
3
4
5
6
7
8
9
<div id="social-links">
    <a href="#"
       class="lanyrd-link"
       tal:define="link view/lanyrd_link"
       tal:condition="link"
       tal:attributes="href link">
         See this talk on Lanyrd!
    </a>
</div>

Como se puede ver esto no es un documento válido HTML. Eso no es necesario, porque no queremos una view completa aquí, sólo un fragmento de código HTML.

Hay una sentencia tal:define, para la consulta view/lanyrd_link. Al igual en la page templates de la plantilla tiene acceso a su clase.

Entonces, ahora tenemos que ampliar el Viewlet Social para añadir el atributo que le falta, agregando el siguiente código Python:

1
2
3
4
5
6
7
8
from plone.app.layout.viewlets import ViewletBase
from ploneconf.site.behaviors.social import ISocial

class SocialViewlet(ViewletBase):

    def lanyrd_link(self):
        adapted = ISocial(self.context)
        return adapted.lanyrd

Hasta ahora, hemos hecho:

  • Registrar el viewlet al contenido que tiene la interfaz ISocial.

  • Adaptar el objeto a su comportamiento para ser capaz de acceder a los campos del comportamiento.

  • Devolver el enlace.

Ejercicio 1

Registrar una viewlet number_of_talks en el pie de página que es visible sólo para los administradores del sitio (el permiso que usted busca es cmf.ManagePortal). Utilice sólo una plantilla (sin clase) para mostrar el número de charlas ya enviadas. Sugerencia: Utilice Aquisition para obtener al catálogo (Usted sabe, no debe hacer esto, pero hay un montón de código por ahí que lo hace...)

Solución

Registrar el viewlet en el archivo browser/configure.zcml, agregando el siguiente código ZCML:

<browser:viewlet
  name="number_of_talks"
  for="*"
  manager="plone.app.layout.viewlets.interfaces.IPortalFooter"
  layer="zope.interface.Interface"
  template="templates/number_of_talks.pt"
  permission="cmf.ManagePortal"
  />

Para los parámetros for y layer el * es la abreviatura de zope.interface.Interface y el mismo efecto que la omisión de ellos: El viewlet se mostrará para todos los tipos de páginas y para todos los sitios de Plone en su instancia Zope.

Agregar el archivo de la plantilla browser/templates/number_of_talks.pt, agregando el siguiente código HTML:

<div class="number_of_talks"
     tal:define="catalog python:context.portal_catalog;
                 talks python:len(catalog(portal_type='talk'));">
    There are <span tal:replace="talks" /> talks.
</div>

python:context.portal_catalog devolverá el catálogo a través de la adquisición. Tenga cuidado si usted desea usar expresiones de rutas: content/portal_catalog llama el catálogo (y devuelve todos los cerebros). Es necesario para evitar esto usando nocall:content/portal_catalog.

Basándose en Adquisición es una mala idea. Sería mucho mejor utilizar la helper view llamada plone_tools desde el módulo plone/app/layout/globals/tools.py para obtener el catálogo.

<div class="number_of_talks"
     tal:define="catalog context/@@plone_tools/catalog;
                 talks python:len(catalog(portal_type='talk'));">
    There are <span tal:replace="talks" /> talks.
</div>

context/@@plone_tools/catalog atraviesa a la vista plone_tools y llama a su método catalog. En expresiones Python debería verse así:

<div class="number_of_talks"
     tal:define="catalog python:context.restrictedTraverse('plone_tools').catalog();
                 talks python:len(catalog(portal_type='talk'));">
    There are <span tal:replace="talks" /> talks.
</div>

No es una buena práctica consultar el catálogo dentro de una plantilla, ya que incluso la simple lógica como esta debería vivir en Python. Pero es muy poderoso si está depurando o necesita una solución rápida y sucia.

Ejercicio 2

Registrar una viewlet days_to_conference en la cabecera. Utilice una clase y una plantilla para mostrar el número de días hasta la conferencia. Usted consigue muchos puntos de bonificación si se muestra en un formato agradable (pensar “En 2 días” y “Último mes”) utilizando un Javascript existentes o biblioteca Python.