Finalmente alguém para cuidar da sua bagunça

Content Rules: regras de conteúdo no Plone

Content Rules, em português, Regras de Conteúdo é um poderoso recurso de gestão de conteúdo disponível a partir do Plone 3.  Trata-se de uma maneira inteligente de tratar o conteúdo de seu site por meio de gatilhos e ações que automaticamente fazem coisas sempre que um conteúdo é criado, deletado ou modificado. 

Nada melhor do que um exemplo para esclarecer. É comum depois que um site esteja pronto e entregue ao cliente que a arquitetura  da informação planejada seja gradualmente desrespeitada pelos usuários na correria do dia a dia. Depois de alguns meses temos a raiz do site cheia de arquivos de difícil identificação, sem falar de conteúdos duplicados espalhados por toda parte. Pois bem, o Content Rule permite um controle maior da organização sem delegar ao usuário final novas responsabilidades. Podemos facilmente escrever uma regra de modo que toda vez que uma notícia for publicada em qualquer lugar do site ela seja movida para a pasta de notícias. Da mesma forma você pode definir que todo o evento criado seja imediatamente copiado para a pasta 'Agenda' de seu site ou ainda sempre que uma imagem for adicionada no portal ela seja copiada para o seu banco de imagens local. Na prática é como se o site tivesse uma faxineira para organizar a bagunça que é feita como conseqüência da rotina de trabalho e desatenção dos usuários finais.


Por padrão o Plone pode aplicar as regras de conteúdo para os seguintes eventos:

  • Quando um objeto é Adicionado
  • Quando um objeto é modificado
  • Quando um objeto é removido
  • Quando o estado do workflow do objeto é alterado.


A estes gatilhos podemos especificar condições como a seguir:

  • Aplicar a regra para um tipo de conteúdo específico (noticia, evento. imagem, etc...)
  • Aplicar a regra para uma extensão de arquivo em especial (*.doc, *.pdf, *.wmv, etc...)
  • Aplicar a regra para um estado de workflow em particular (privado, esboço, publicado, etc..)
  • Aplicar a regra para grupos específicos de usuário (administradores, usuários autenticados, etc..)
  • Aplicar a regra para usuários que tenham um papel em particular (contribuidores, revisores, membros, etc..)


Por fim, satisfeitas as condições que especificamos podemos aplicar as seguintes ações:

  • Exibir uma mensagem de registro do sistema
  • Notificar usuário
  • Copiar para pasta
  • Mover para pasta
  • Deletar objeto
  • Alterar estado de workflow
  • Mandar um e-mail


Importante ressaltar que tudo isso é feito na interface do próprio Plone, sem a necessidade de escrever códigos complicados ou mesmo de realizar alterações na ZMI ou no filesystem. Vejamos a seguir um exemplo simples de como criar um Conte Rule para organizar as noticias num plonesite.

Criando a sua Regra de Conteúdo

Neste exemplo mostraremos como criar uma regra de conteúdo para que todo item do tipo notícia seja movido para a pasta de Notícias de nosso site. Isso é feito em quatro fáceis etapas:

  1. Adicionar uma nova regra
  2. Configurar as condições
  3. Configurar a ação desejada
  4. Aplicar a regra as pastas do site

1. Adicionar uma nova regra


Logue-se em seu plonesite e vá ate as “Configurações do site” e sem seguida clique em “Regras de Conteúdo”. Você verá uma página como a representada abaixo. Clique no botão “Adicionar  Regra de Conteúdo.”

fig1.gif

 
Você entrará agora na página de inclusão da nova regra. Vamos entrar os dados para nossa regra de notícias como na ilustração a seguir:

fig2.gif

Na janela “Adicionar condição”, escolha a opção “Content type” e clique em adicionar. Na página seguinte, escolha o item “Notícias” e salve, como demonstrado na ilustração.

fig3.gif

2. Configurar as condições


Clique em Organizador de Notícias. Iremos agora configurar as condições e as ações de nossa regra. Você deverá estar vendo a seguinte página:

fig4.gif

Na janela “Adicionar condição”, escolha a opção “Content type” e clique em adicionar. Na página seguinte, escolha o item “Notícias” e salve, como demonstrado na ilustração.

fig5.gif

3. Configurar a ação desejada

De volta a página de configuração da nossa regra de conteúdo em Adicionar ação, escolha agora a opção “Move to folder” e clique em “adicionar”

fig6.gif

Seleciona a pasta, “Noticias” e salve as alterações:

fig7.gif

4 Aplicar a regra as pastas do site

fig8.gif

Nossa regra de conteúdo esta criada. Precisamos agora apenas aplicá-la ao site. No nosso exemplo aplicaremos esta regra na raiz, de modo que seja herdada por todas as subpastas, mas poderíamos também aplicar regras apenas a subpastas específicas. Para isso vá até a raiz de seu site e clique na aba “Regras”. Selecione então nosso “Organizador de Notícias” e clique em Adicionar:

fig9.gif

Selecione gora nossa regra na lista que vai aparecer e então clique em “aplicar a subpastas”

Nosso “Organizador de Notícias” está pronto e funcinal. Faça um teste agora criando uma notícia em qualquer pasta do site. Se você seguiu corretamente os passos verá que estes itens sempre serão movidos para a página de notícias do site.

Criando novos eventos e ações


Vimos com criar uma regra simples, mas o poder do Content Rule é bem maior do que um mero organizador de conteúdo. É um recurso fácil de aprender que está sendo estendido pela comunidade de desenvolvedores para ficar ainda mais poderoso. Já é possivel por exemplo encontrar produtos como o collective.contentrules.webservices que permite a publicação de anúncios no Twitter e em outros sites semelhantes.

Veremos agora como cria ruma nova condição que verifica as Palavras Chaves de um conteúdo. Embora esse exemplo mostre como criar um novo tipo de conteúdo, o processo de criar uma nova ação é muito similar. Sempre que as diferenças entre estes dois processos ocorrerem elas serão especificadas.

Criando um pacote

Em primeiro lugar é preciso criar um novo pacote onde colocaremos nossa nova condição. Usaremos um pacote egg e instalaremos ele em nosso buildout.

No diretório src; de seu produto:

$ cd src

$ paster create -t plone collective.keywordcondition

Selected and implied templates:

  ZopeSkel#basic_namespace  A project with a namespace package

  ZopeSkel#plone            A Plone project

Variables:

  egg:      collective.keywordcondition

  package:  collectivekeywordcondition

  project:  collective.keywordcondition

Enter namespace_package (Namespace package (like plone)) ['plone']: collective

Enter package (The package contained namespace package (like example)) ['example']: keywordcondition

Enter zope2product (Are you creating a Zope 2 Product?) [False]: False

...

Enter zip_safe (True/False: if the package can be distributed as a .zip file) [False]: False

Atenção a pergunta sobre o namespace e name de seu pacote (package), no nosso exeplo correspondendo ao egg name collective.keywordcondition. Atenção também ao zope2products status que no caso deve ser Falso, pois não precisaremos do Generic Setup ou outro comportamento do Zope2. por fim, zip-safe deve ser falso também, como em todos os pacotes do Zope.

Em seguida instale como um egg em seu buildout de modo que o Zope reconheça seu pacote. faça isso editando o arquivo buildout.cfg:

[buildout]

...

eggs =

  ...

  collective.keywordcondition

developer =

  ...

  src/collective.keywordcondition

...

[instance]

...

zcml =

...

collective.keywordcondition

Rode novamente seu buildout.

Adicionando uma condição


Uma condição do Content Rule consiste em:

1 - Uma interface que descreve os aspectos configuraveis da condição
2 - Um objeto permanente usado para armazenar as configurações da condição
3 - Um adaptador usado para executar a condição quando a regra é executada
4 - Um view e um view de edição que permite que o usuário configure as condições.

Tudo isso será colocado em um arquivo que chamaremos no nosso exemplo de keyword.py dentro do pacote collective.keyword

Em primeiro ligar vamos importar alguns elementos importantes:

from persistent import Persistent

from OFS.SimpleItem import SimpleItem

from zope.interface import implements, Interface

from zope.component import adapts

from zope.formlib import form

from zope import schema

from zope.app.component.hooks import getSite

from zope.component.interfaces import IObjectEvent

from plone.contentrules.rule.interfaces import IExecutable, IRuleElementData

from plone.app.contentrules.browser.formhelper import AddForm, EditForm

from Acquisition import aq_inner

from collective.keywordcondition import MessageFactory as _

Perceba que usamos o MessageFactory para fins de tradução. Ele é definido em __initi__.py no nosso pacote collective.keywordcondition:

from zope.i18nmessageid import MessageFactory

MessageFactory = MessageFactory('collective.keywordcondition')

Agora, definimos a interface usado zope.schema, que descreve os aspectos configuráveis de nosso condição. No nosso caso, uma lista de palavras chave. O título, descrição e outros metadados serão usados por zope.formlib nos formulários de edição e adição. Veja o pacote zope.schema para mais informações sobre como cada campo é usado.

class IKeywordCondition(Interface):

    """Interface for the configurable aspects of a keyword condition.   

    This is also used to create add and edit forms, below.

    """   

    keywords = schema.Tuple(title=_(u"Keywords"),

                            description=_(u"The keywords to check for."),

                            required=True,

                            value_type=schema.TextLine(title=_(u"Keyword")))

Agora, criaremos a implementação de armazenamento da condição:

class KeywordCondition(SimpleItem):

    """The actual persistent implementation of the keyword condition element.   

    Note that we must mix in SimpleItem to keep Zope 2 security happy.

    """

    implements(IKeywordCondition, IRuleElementData)   

    keywords = []

    element = "collective.keywordcondition.Keyword"   

    @property

    def summary(self):

        return _(u"Keywords contains: ${names}", mapping=dict(names=", ".join(self.keywords)))

As variáveis da palavra chave, definem o valor padrão do campo na interface IKeywordCondition já mencionada. O elemento especificada apenas um nome para esse elemento da regra (condições e ações costumam ser chamados de elementos da regra (element rule). Por convenção este nome possui o nome do pacote separado por um ponto de modo a garantir que seja único. O nome do elemento será referenciado novamente mais tarde no arquivo configure.zcml


O objeto de armazenagem deve também implementar uma propriedade summmary. Ela é usada para no Painel de Controle  termos um resumo do que a condição faz.

O Executor


Depois de definir a condição de armazenamento, definimos o executor:


class KeywordConditionExecutor(object):

    """The executor for this condition.   

    This is registered as an adapter in configure.zcml

    """

    implements(IExecutable)

    adapts(Interface, IKeywordCondition, IObjectEvent)        

    def __init__(self, context, element, event):

        self.context = context

        self.element = element

        self.event = event

    def __call__(self):

        context = aq_inner(self.event.object)

        keywords = frozenset()

        try:

            keywords = frozenset(context.Subject())

        except (AttributeError, TypeError,):

            # The object doesn't have a Subject method

            return False

        return (len(keywords.intersection(self.element.keywords)) > 0)

É simples adaptar o tipo de objeto de conteúdo por contexto (nesse caso, não nos interessa que tipo de objeto será usado, assim especificamos Interface, que é a interface mais genérica de todas), o tipo de condição (nossa interface recém definido IKeywordCondition) e o tipo de evento ( no nosso caso IObjectevent, uma interface genérica que descreve eventos sobre objetos.

ATENÇÃO: É possível sobrescrever implementações do executor para diferentes contextos ou eventos através do registro de novos adaptadores.

O executor tem ainda um método importante, o __call__9) que é chamado durante a execução da regra, Ele deve retornar verdadeiro ou falso. Em caso de True, a execução continua, par a a próxima condição ou ação, caso contrário o processo é abortado.

Se você está criando um action seu adaptador é quem farpa o verdadeiro trabalho da ação. Ele também deve retornar true ou false para indicar se o processo pode continuar.

Perceba como o executor é escrito de uma maneira defensiva, afinal as regras podem ser invocada para uma variedade de eventos e circunstancias.

Formulários


Finalmente devemos definir os formulários de inclusão e edição que usaremos para criar ou modificar nossa nova
condição. Usaremos a biblioteca zope,formlib e nossa interface IkeywordCondition da seguinte maneira:


class KeywordAddForm(AddForm):

    """An add form for portal type conditions.

    """

    form_fields = form.FormFields(IKeywordCondition)

    label = _(u"Add Keyword Condition")

    description = _(u"A keyword condition makes the rule apply only to content with certain keywords.")

    form_name = _(u"Configure element")   

    def create(self, data):

        c = KeywordCondition()

        form.applyChanges(c, self.form_fields, data)

        return c

class KeywordEditForm(EditForm):

    """An edit form for portal type conditions

    """

    form_fields = form.FormFields(IKeywordCondition)

    label = _(u"Edit Keyword Condition")

    description = _(u"A keyword condition makes the rule apply only to content with certain keywords.")

    form_name = _(u"Configure element")


As classes AddForm e EditForm prestam suporte ao plone.app.contenrules com os boões e validadores padrão.

Configurando o componente


Com o código no lugar devemos agora registrar nosso componente no configure.zcml

<configure

    xmlns="http://namespaces.zope.org/zope"

    xmlns:browser="http://namespaces.zope.org/browser"

    xmlns:plone="http://namespaces.plone.org/plone"

    xmlns:five="http://namespaces.zope.org/five"

    i18n_domain="collective.keywordcondition">

    <include package="plone.contentrules" />

    <include package="plone.contentrules" file="meta.zcml" />   

    <!-- the keyword condition -->

    <adapter factory=".keyword.KeywordConditionExecutor" />

    <browser:page

      for="plone.app.contentrules.browser.interfaces.IRuleConditionAdding"

      name="collective.keywordcondition.Keyword"

      class=".keyword.KeywordAddForm"

      permission="cmf.ManagePortal"

      />

    <browser:page

      for="collective.keywordcondition.keyword.IKeywordCondition"

      name="edit"

      class=".keyword.KeywordEditForm"

      permission="cmf.ManagePortal"

      />

    <plone:ruleCondition

        name="collective.keywordcondition.Keyword"

        title="Keyword"

        description="Apply only when the current content object has one of the given keywords"

        for="*"

        event="zope.component.interfaces.IObjectEvent"

        addview="collective.keywordcondition.Keyword"

        editview="edit"

        />

</configure>


Aqui em primeiro lugar importamos os elementos de configuração necessários de plone.app.contentrules e registramos nosso adaptador, e os views de inclusão e edição. O nome do view de inclusão deve corresponder ao nome do elemento (collective.keywordcondition.Keyword  no nosso caso) conforme fora definido na classe. O nome da edição deve ser sempre edit

Finalmente registraremos o tipo de condição:

Atenção: Quando criando uma ação use a diretiva <plone:ruleAction />. Ela usa os mesmos atributos de <plone:ruleCondition/>

Novamente, o nome deve corresponder ao definido na classe. O título e descrição são usamos na interface do usuário.

Os atributos for e event são usados para controlar quando o elemento está disponível. Nesse caso, ele é disponível em todos os contextos de eventos e objeto.

Muitos dos eventos registrados no Content Rules são herdados de IObjectEvent. A interface simplesmente garante que haverá um atributo de objeto no evento que contem o objeto para qual o evento foi colocado. Se você quer restringir a condição para um evento ou tipo mais específico você pode definir uma interface diferente aqui. Isso geralmente só é necessário se você precisa de informações adicionais para executar a condição.

Atenção: Deve haver pelo menos um adaptador aplicável ao evento na qual a condição ou ação for registrada.


Fontes:
http://plone.org/documentation/tutorial/creating-content-rule-conditions-and-actions/tutorial-all-pages

http://www.slideshare.net/wooda/kamon-ayeva-let-the-machine-work-for-you-using-plone-content-rules-for-more-productivity/