[gitec] Saap 2.0

Fabiano Weimar dos Santos xirumacanudo em gmail.com
Quarta Dezembro 8 11:42:21 BRST 2010


Oi Sesóstris,

bem... primeiro precisamos de um nickname para você. To quase apelando
para o ctrl-c + ctrl-v :)

Você não vai encontrar documentação sobre o portal_factory em nenhum
lugar, por isso eu vou fazer um resuminho para você. De certa forma é
bom eu escrever isso aqui pois assim já fica para registro caso alguém
precise disso no futuro.

Primeiro é necessário entender o que é esse tal de "portal_factory".

O portal_factory foi invenção do lalo, um ex-funcionário meu. Ele foi
criado no Plone 2.0 para evitar que, quando um objeto fosse criado e
não fosse salvo, que não ficasse um objeto vazio dentro do Plone. No
Plone 1 era exatamente isso que acontecia e no CMF sempre foi assim
pois não existe a noção de formulário de criação de objetos, apenas de
edição de objetos já instanciados (é como a orientação a objetos
prega, você instancia um objeto e depois chama os setters para
atribuir valores as diversas propriedades daquele objeto). O problema
é que a usabilidade disso era sofrivel e sempre ficava um monte de
noticias, paginas e pastas vazias no site, o que gerava uma verdadeira
bagunça.

O principio de funcionamento do portal_factory é criar objetos
persistentes num contexto não persistente. Uma "temp" folder é
persistente, mas transciente, o que é bem diferente (pois os objetos
ficam persistidos em memória e, depois de um determinados tempo
"transcendem para o além" e somem). Criar uma instancia de uma classe
persistente num lugar não persistente é obra do capeta. Isso é feito
usando o REQUEST. O contexto do REQUEST existe apenas enquanto aquele
REQUEST está sendo processado. O portal_factory abusa dessa estratégia
pq, dentre outras coisas, objetos no Plone tem que ter contexto de
aquisição para funcionar e o REQUEST é o último elemento na cadeia da
aquisição.

O portal_factory foi odiado por anos, pois há muitas partes móveis,
pontas soltas que podem explodir miseravelmente se você não seguir
alguns padrões bem definidos de programação. O Andres Jung, author do
PloneCollectorNG, é um "cara das antigas" e certamente tem as manias
dele. Ele trabalhou na Zope Corporation que, dentre outras coisas,
mantinha seu próprio CMS proprietário e não usava Plone. Percebes
agora pq do código dele ser da forma que é e você não conseguir fazer
o que está querendo?

Desde o Plone 2.5 o portal_factory é bem estável e, salvo situações
onde o Plone Product que você está usando foi mal escrito, o
portal_factory funciona bem.

Dicas:

1) Se você ver algum código com _setObject desconfie. Essa era a forma
como objetos Zope padrão eram pindurados no ZODB. Isso é bem baixo
nível e pode, potencialmente, criar objetos com ids inválidos para um
Plone Site. Se você criar um objeto qualquer, usando essa API
._setObject, podes criar um documento que se chama portal_factory,
portal_skins ou index_html e, por sua vez, explodir com seu Plone
Site. Isso não significa que nada que contenha _setObject deva ser
usado; isso só significa que quem usa isso deve saber das implicações
que isso pode causar e tomar os devidos cuidados.

Encare o ._setObject como uma chamada de uma API private, que não
deveria ser usada (pois... começa com "_").

Essa é uma convenção amplamente aceita.

2) Tudo que é criado no Plone (e também no CMF) utiliza uma API alto
nível (que internamente faz validações e acaba, no final das contas,
chamando o _setObject). O método a que me refiro é o invokeFactory.
Aqui tem alguns exemplos:
http://plone.org/documentation/manual/plone-community-developer-documentation/content/creating

O Plone vai um pouco além e disponibiliza API para geração de IDs
válidos automáticos (não é tão simples ter um ID realmente válido,
acredite).

3) Contexto de Aquisição

Aquele __of__ é o que chamamos de context wrapper. Esse negócio é
SINISTRO. Mantenha distancia.

http://www.zope.org/Documentation/Books/ZDG/current/Acquisition.stx

O que você queria, no seu código, era apenas "pindurar" o contexto da
aquisicao no objeto que você acabou de criar para poder invocar
métodos desse objeto que dependem da aquisicao. O "absolute_url" é um
exemplo (assim como praticamente tudo no Zope).

Eu recomendo você "pindurar" contexto de aquisicao usando outra
estratégia, a do getattr

parent.invokeFactory(id=id, type_name='Folder')
obj = getattr(parent, id)
obj.indexObject()

Perceba o indexObject() depois de pegar o contexto da aquisicao para
atualizar o portal_catalog com o objeto que você recem criou.

4) portal_factory

Se você estiver com o seu tipo marcado no portal_factory, a criação de
objetos pela interface do plone funciona automaticamente sem criar
objetos "fantasmas" (objetos vazios, criados a partir de objetos nao
salvos). No entanto, você pode querer manipular explicitamente o ID de
seus objetos. Isso é feito por um hook (um gancho, ou gato mesmo) como
o que o POI utiliza

      def _renameAfterCreation(self, check_auto_id=False):
         parent = self.getTracker()
         maxId = 0
         for id in parent.objectIds():
             try:
                 intId = int(id)
                 maxId = max(maxId, intId)
             except (TypeError, ValueError):
                 pass
         newId = str(maxId + 1)
         # Can't rename without a subtransaction commit when using
         # portal_factory!
         transaction.savepoint(optimistic=True)
         self.setId(newId)

Isso é relativamente bem documentado aqui
http://plone.org/documentation/kb/richdocument/controlling-creation

Você tem que lembrar de adicionar algo assim na sua classe

 _at_rename_after_creation = True

Tem que tomar cuidado também com as documentações conflitantes. Se
você visitar o link acima verá uma API assim:

get_transaction().commit(1)

Só que isso é deprecated desde o Zope 2.8.algumacoisa. O certo é fazer
"import transaction" e usar os métodos desse módulo.

5) Criando objetos

Se você quer apenas criar um objeto de um determinado tipo e ir para a
página para preencher os campos desse objeto, utilize o seguinte
script

/createObject?type_name=Topic

Não sei se falei tudo, mas deve ser o necessário para você rever o seu código.

Por fim, não te assusta: você não precisa implodir todo teu fonte e
reescrever. Faça apenas um grep de todos os locais ondem _setObject é
utilizado e faça as adequações necessárias.

-- Fabiano Weimar


Em 8 de dezembro de 2010 10:30, Sesostris Vieira
<sesostris em interlegis.gov.br> escreveu:
>
> Fabiano,
>
> O autor do produto PloneCollectorNG optou por não usar o portal_factory, conforme ele mesmo colocou nos comentários do método. Para mudar isso, eu teria que corrigir todo o produto, o que será bem trabalhoso, mas acho que será a solução mais "elegante". Você tem algum material sobre o portal_factory para me enviar?
>
> Grato,
>
> Sesóstris Vieira
> Analista de Informática Legislativa
> SPDT / Interlegis / Senado Federal
> sesostris em interlegis.gov.br
>
>
> Fabiano Weimar dos Santos escreveu:
>
> Olá,
>
> Isso aqui é muito feio.
>
>        issue = PloneIssueNG(id)
>        temp._setObject(id, issue)
>        issue = issue.__of__(temp)
>
> Eu conheço bem pouca gente que realmente sabe como funciona esse __of__
>
> Pergunta: pq você não usa a API de invokeFactory do CMF?
>
> Eu não conheço essa API de objetos temporários, mas criar pastas temporárias
> não faz muito sentido quando você entende que os objetos transcientes somem
> sozinhos depois de um tempo. Se fosse possível criar uma pasta transciente,
> ela sumiria e levaria consigo tudo que ela tem dentro? Equivalente seria
> criar conjuntos de objetos quaisquer agrupados por um atributo em comum.
>
> De qualquer forma, acho que o problema aqui é você entender o portal_factory
> e usa-lo. Na época que o portal_factory foi criado ele era chato. Hoje ele
> ta bem mais estável e esse tipo de hack não se justifica. Você pode utilizar
> a api do CMF pura e delegar para o portal_factory a responsabilidade de
> criar as coisas com o contexto certo.
>
> Esse código apenas faria sentido se você não pudesse usar o portal_factory,
> ok.
>
> -- Fabiano Weimar
>
>
> Em 6 de dezembro de 2010 19:08, Sesostris Vieira <
> sesostris em interlegis.gov.br> escreveu:
>
>
>
> Ainda estou com problemas na parte de processos, que está dando um erro.
> O problema está nesse código abaixo, na classe PloneCollectorNG:
>
>    def redirect_create_object(self, RESPONSE=None):
>        """ Create a new issue as temporary object inside a temporary
> folder to
>            avoid unfilled issues. We do no longer support
> portal_factory because
>            it raises more problems than it solves.
>        """
>
>        from Products.TemporaryFolder.TemporaryFolder import
> constructTemporaryFolder
>        if not hasattr(self, 'temp'):
>            constructTemporaryFolder(self, 'temp')
>        temp = getattr(self, 'temp')
>        id = '%s_%f' % (self.Translate('new_issue', 'NewIssue'),
> time.time() * random.random())
>        issue = PloneIssueNG(id)
>        temp._setObject(id, issue)
>        issue = issue.__of__(temp)
>        # remove pending issues
>        pending = [issue.getId() for issue in
> temp.objectValues('PloneIssueNG') if DateTime() -
> issue.bobobase_modification_time() > 2*3600]
>        temp.manage_delObjects(pending)
>        # if more than 50 issues in memory, remove the first 25
>        if len(temp.objectIds()) > 50:
>            pending = temp.objectValues('PloneIssueNG')
>            pending.sort(lambda x,y: cmp(x.bobobase_modification_time(),
> y.bobobase_modification_time()))
>            temp.manage_delObjects([o.getId() for o in pending[:25]])
>        RESPONSE.redirect(issue.absolute_url() + '/pcng_base_edit')
>
> O problema é que a função constructTemporaryFolder cria um objeto que
> não é container e não possui o método _setObject. Se alguém puder
> ajudar, qualquer contribuição será bem vinda.
>
> Sesóstris Vieira
> Analista de Informática Legislativa
> SPDT / Interlegis / Senado Federal
> sesostris em interlegis.gov.br
>
>
>
> CPD Camara Friburgo escreveu:
>
>
> Pessoal,
> fiquei um tempão sem receber mensagens da lista, hoje chegaram todas de
>
>
> uma vez!
>
>
> O Saap 2.0 já está pronto para uso?
>
> Silvia Zveiter de Albuquerque Rocha
> CPD / Câmara Municipal de Nova Friburgo
> 22 2522-1516 ramal 254
>
>
>
> --
> Site da Comunidade GITEC:
> http://colab.interlegis.gov.br/wiki
>
> Regras de participação:
> http://colab.interlegis.gov.br/wiki/ComoParticiparComunidade
>
> Para pesquisar o histórico da lista visite:
> http://colab.interlegis.gov.br/wiki/PesquisaListas
>
> Para administrar sua conta visite:
> http://listas.interlegis.gov.br/mailman/listinfo/gitec
>
>
>
>
>
> --
> Site da Comunidade GITEC:
> http://colab.interlegis.gov.br/wiki
>
> Regras de participação:
> http://colab.interlegis.gov.br/wiki/ComoParticiparComunidade
>
> Para pesquisar o histórico da lista visite:
> http://colab.interlegis.gov.br/wiki/PesquisaListas
>
> Para administrar sua conta visite:
> http://listas.interlegis.gov.br/mailman/listinfo/gitec



--
Fabiano Weimar dos Santos [Xiru]
http://www.pytown.com
Blog: http://blog.xiru.org
Twitter: xiru
Buzz: xirumacanudo
Skype: xirumacanudo
MSN: xirumacanudo


Mais detalhes sobre a lista de discussão GITEC