34. Usando starzel.votable_behavior en ploneconf.site

  • Queremos usar el comportamiento de votación, para que nuestros usuarios revisores puedan votar.

  • Para mostrar cómo usar los eventos, vamos a publicar automáticamente los tipos de contenidos talks que han alcanzado una cierta puntuación.

  • No vamos a dejar que todos voten todo.

Primero, debemos agregar nuestro paquete como una dependencia al paquete ploneconf.site.

Hacemos esto en dos lugares. La descripción del paquete egg en el archivo setup.py necesita starzel.votable_behavior como una dependencia. De lo contrario ningún código fuente estará disponible.

La configuración persistente necesita ser instalada cuando instalamos nuestro sitio. Esto se configura en GenericSetup.

Comenzamos con la edición del archivo setup.py, con el siguiente código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
...
zip_safe=False,
install_requires=[
    'setuptools',
    'plone.app.dexterity [relations]',
    'plone.app.relationfield',
    'plone.namedfile [blobs]',
    'starzel.votable_behavior',
    # -*- Extra requirements: -*-
],
...

A continuación modificamos el archivo profiles/default/metadata.xml, con el siguiente código XML:

1
2
3
4
5
6
7
8
<metadata>
  <version>1002</version>
    <dependencies>
      <dependency>profile-plone.app.dexterity:default</dependency>
      <dependency>profile-plone.app.relationfield:default</dependency>
      <dependency>profile-starzel.votable_behavior:default</dependency>
    </dependencies>
</metadata>

Qué nombre tan raro. profile- es un prefijo que siempre necesitarás hoy en día. Luego viene el nombre del paquete egg, y la parte después de los dos puntos es el nombre del perfil. El nombre del perfil se define en configuraciones ZCML. Hasta ahora he tropezado sólo sobre un paquete donde el nombre del directorio de perfil era diferente al nombre del perfil GenericSetup.

Ahora el paquete está allí, pero nada es votable. Esto se debe a que ningún tipo de contenido declara utilizar este comportamiento. Podemos añadir este comportamiento a través del panel de control, exportar la configuración y almacenarla en nuestro paquete egg. Vamos a añadirlo a mano ahora.

Tenemos que agregar el comportamiento al tipo de contenidos talks, haremos esto en el archivo profiles/default/types/talk.xml.

Nota

Administrando las dependencias en el archivo metadata.xml es una buena práctica. No podemos confiar en recordar lo que tendríamos que hacer a mano. Por ejemplo, ¿recuerdas que tuvimos que añadir para seleccionar Tipos predeterminados de Plone basados en Dexterity al crear un nuevo sitio de Plone?

En lugar de eso, debemos agregar <dependency>profile-plone.app.contenttypes:plone-content</dependency> como la documentación de plone.app.contenttypes recomienda.

1
2
3
4
5
<property name="behaviors">
  <element value="plone.app.dexterity.behaviors.metadata.IDublinCore"/>
  <element value="plone.app.content.interfaces.INameFromTitle"/>
  <element value="starzel.votable_behavior.interfaces.IVoting"/>
</property>

Ahora nosotros podemos reinstalarlo en nuestro sitio Plone.

Todo el mundo puede votar ahora en los tipos de contenidos talks. Eso no es lo que queríamos. En realidad, sólo queremos que los revisores voten en las charlas pendientes. Esto significa que, dependiendo del estado del flujo de trabajo, el permiso tiene que cambiar. Por suerte, los flujos de trabajo se pueden configurar para hacer precisamente eso. Los tipos de contenidos talks ya tienen su propio flujo de trabajo. Así que no interferiremos con otros paquetes.

En primer lugar, tenemos que decirle al flujo de trabajo que gestionará más permisos. A continuación, tenemos que configurar para cada estado, que el rol de usuario tiene ahora los dos nuevos permisos.

Esa es una configuración muy detallada, tal vez usted quiere hacerlo en la interfaz web y exportar la configuración. Por otro lado, es fácil cometer un simple error en ambos sentidos. Yo acabo de presentar la forma XML aquí.

La configuración del flujo de trabajo está en el archivo profiles/default/workfows/talks_workflow.xml, con el siguiente código XML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0"?>
<dc-workflow workflow_id="talks_workflow" title="Talks Workflow" description=" - Simple workflow that is useful for basic web sites. - Things start out as private, and can either be submitted for review, or published directly. - The creator of a content item can edit the item even after it is published." state_variable="review_state" initial_state="private" manager_bypass="False">
 <permission>Access contents information</permission>
 <permission>Change portal events</permission>
 <permission>Modify portal content</permission>
 <permission>View</permission>
 <permission>starzel.votable_behavior: View Vote</permission>
 <permission>starzel.votable_behavior: Do Vote</permission>
 <state state_id="pending" title="Pending review">
  <description>Waiting to be reviewed, not editable by the owner.</description>
  ...
  <permission-map name="starzel.votable_behavior: View Vote" acquired="False">
   <permission-role>Site Administrator</permission-role>
   <permission-role>Manager</permission-role>
   <permission-role>Reviewer</permission-role>
  </permission-map>
  <permission-map name="starzel.votable_behavior: Do Vote" acquired="False">
   <permission-role>Site Administrator</permission-role>
   <permission-role>Manager</permission-role>
   <permission-role>Reviewer</permission-role>
  </permission-map>
  ...
 </state>
 <state state_id="private" title="Private">
  <description>Can only be seen and edited by the owner.</description>
  ...
  <permission-map name="starzel.votable_behavior: View Vote" acquired="False">
   <permission-role>Site Administrator</permission-role>
   <permission-role>Manager</permission-role>
  </permission-map>
  <permission-map name="starzel.votable_behavior: Do Vote" acquired="False">
   <permission-role>Site Administrator</permission-role>
   <permission-role>Manager</permission-role>
  </permission-map>
  ...
 </state>
 <state state_id="published" title="Published">
  <description>Visible to everyone, editable by the owner.</description>
  ...
  <permission-map name="starzel.votable_behavior: View Vote" acquired="False">
   <permission-role>Site Administrator</permission-role>
   <permission-role>Manager</permission-role>
  </permission-map>
  <permission-map name="starzel.votable_behavior: Do Vote" acquired="False">
  </permission-map>
  ...
 </state>
  ...
</dc-workflow>

Tenemos que volver a instalar nuestro producto de nuevo.

Pero esta vez, esto no es suficiente. Los permisos se actualizan en los cambios de flujo de trabajo. Mientras no ocurra un cambio en el flujo de trabajo, los tipos de contenidos talks tienen los mismos permisos que nunca.

Afortunadamente, para eso hay un botón “Update security settings” en la vista Flujo de trabajo de la ZMI.

Después de hacer clic en este, sólo los usuarios Administradores y los usuarios Revisores pueden ver la funcionalidad de votación.

Por último, añadimos nuestra tonta función para autorizar los tipos de contenidos talks.

Rápidamente terminas escribiendo muchos manejadores de eventos, por lo que ponemos todo en un directorio para los manejadores de eventos.

Para los eventos nosotros necesitamos un directorio events.

Crear el directorio events y agregar un archivo vació events/__init__.py.

Lo próximo, registramos el directorio events en el archivo configure.zcml

1
<include package=".events" />

Entonces, escribimos la configuración ZCML para los eventos dentro del archivo events/configure.zcml, agregando el siguiente código ZCML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<configure
    xmlns="http://namespaces.zope.org/zope">

  <subscriber
    for="starzel.votable_behavior.interfaces.IVotable
         zope.lifecycleevent.IObjectModifiedEvent"
    handler=".votable.votable_update"
    />

</configure>

Esto parece un MultiAdapter. Queremos ser notificados cuando un objeto IVotable se modifica. Nuestro método recibirá el objeto votable, y el evento en sí.

Y finalmente, nuestro manipulador del evento en el archivo events/votable.py, con el siguiente código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from plone.api.content import transition
from plone.api.content import get_state
from starzel.votable_behavior.interfaces import IVoting


def votable_update(votable_object, event):
    votable = IVoting(votable_object)
    if get_state(votable_object) == 'pending':
        if votable.average_vote() > 0.5:
            transition(votable_object, transition='publish')

Estamos usando un montón de sentencias plone api aquí. La Plone API hace el código una brisa fresca. Además, no hay nada realmente interesante. Sólo haremos algo, si el estado de flujo de trabajo está pendiente y el voto promedio es superior a 0,5. Como puede ver, el método transition no quiere el estado de destino, sino la transición para mover el estado al estado de destino.

No hay nada especial en marcha.