Pular para o conteúdo. Ir para a navegação
Ações do site
Opções do usuário

TcheZope.org

Você está aqui: Página Inicial Documentação Manuais O Livro do Zope Variáveis e DTML Avançada
Ações do documento

9. Variáveis e DTML Avançada

Um nível acima
Este capítulo estuda mais de perto a DTML. Mostra a segurança com DTML e a complicada edição de como as variáveis são procuradas na DTML. Também mostra usos avançados das tags básicas mostradas no Capítulo 3 e as tags com objetivos especiais. Este capítulo apresentará a você um programa DTML.

DTML é uma espécie de linguagem que "você faz o que pensa." Isto é bom, quando ele faz o que você realmente quer que ele faça, mas quando ele faz algo que você não queria que ele fizesse, isto é ruim. Este capítulo mostra a você como fazer com que o DTML faça o que você realmente quer.

Não é mentira que a DTML tem reputação por ser complexa. E é verdade, a DTML é realmente simples se o que você quer fazer é um simples layout, como você viu até agora. De qualquer forma, se você quer usar DTML para tarefas mais avançadas, você tem que entender de onde vem as variáveis DTML.

Aqui está um erro muito comum que quase todos os usuários principiantes cometem. Imagine que você possui um DTML Document chamado zooName. Este documento contém um formulário HTML como este:

<dtml-var standard_html_header>
<dtml-if zooName>
<p><dtml-var zooName></p>
<dtml-else>
<form action="<dtml-var URL>" method="GET">
<input name="zooName">
<input type="submit" value="What is zooName?">
</form>
</dtml-if>
<dtml-var standard_html_footer>

Este exemplo parece simples o suficiente, a idéia é, esta é uma página HTML que chama ela mesma. Isto é porque a ação do HTML é da variável URL, que vai se tornar a URL do DTML Document.

Se exister uma variável zooName, então a página será impressa, se não existir, rlr mostra um formulário que pede por ele. Quando você clica `no submit, os dados que você digitou farão com que o comando "if" seja avaliado, e este código deveria imprimir o que foi digitado no formulário.

Mas infelizmente, esta é uma dessas instâncias na qual a DTML não fará o que você quer, porque o nome do DTML Document que contém este DTML também é chamado zooName, e ele não usa a variável fora do requerimento (request), ele usa para si mesmo, que faz com que ele chame ele mesmo, e novamente, infinitamente, até que você consiga um erro "Excessive recursion" (recursão excessiva). Assim ao invés de fazer o que você realmente quer, você obtém um erro. Isto é o que confunde os iniciantes. Nas próximas seções, mostraremos como você concerta este exemplo para fazer o que você quer.

Como as Variáveis são Encontradas

Existem atualmente duas maneiras de concertar o erro do DTML no documento zooName. A primeira é que você pode renomear o documento para algo como zopeNameFormOrReply e sempre lembrar esta excessão especial e nunca fazê-la; nunca sabendo porque ela acontece. A segunda é entender como os nomes são procurados, e saber de onde você quer que o nome venha de um namespace.

O namespace da DTML é uma coleção de objetos organizados em uma pilha. Uma pilha é uma lista de objetos que podem ser manipulados pelos objetos pushing e popping dentro e fora da pilha.

Quando o DTML Document ou Method é executado, o Zope cria um namespace da DTML para decidir os nomes das variáveis DTML. Isto é importante para entender o funcionamento do namespace da DTML assim que você pode exatamente predizer como o Zope localizará as variáveis. Alguns dos problemas enganosos você executará com a DTML provavelmente pode ser resolvido entendendo o DTML namespace.

Quando o Zope procura por nomes na pilha do namespace da DTML ele primeiro procura no objeto bem do topo da pilha. Se o nome não é encontrado ali, então o próximo item abaixo é vasculhado. Zope vai procurar para baixo na pilha, examinando cada objeto dentro dela até ele achar o nome que está procurando.

Se o Zope chegar ao final da pilha e nãp encontrou o que estava procurando, então um erro é gerado. Por exemplo, tente procurar por um nome não existente, unicorn:

  <dtml-var unicorn>

Contanto que não haja uma variável chamada unicorn este DTML retornará um erro, como mostra a Figura 7.1:

7-1.png

Figura 7.1 - Mensagem de erro do DTML indicando que ele não pode achar a variável

Mas a pilha do DTML não é muito para nomes porque a DTML não inicia com uma pilha vazia, antes mesmo que você inicie executando a DTML no Zope já existem objetos na pilha do namespace.

DTML Namespace

Namespaces da DTML são construídas dinamicamente para cada requisição no Zope. Quando você chama um DTML Method ou DTML Document através da web, o namespace da DTML inicia com os mesmos dois primeiros elementos da pilha o objeto cliente e a requisição como mostra a Figura 7.2.

7-2.png

Figura 7.2 - stack DTML namespace inicial.

O objeto cliente é o primeiro objeto no topo da pilha do namespace da DTML. O objeto cliente depende se você está ou não executando um DTML Method ou Document. Em nosso exemplo acima, isto significa que o objeto cliente é chamado de zooName. Motivo pelo qual ele quebra. A entrada do formulário que realmente queremos vem do request da web, mas mas o cliente é procurado primeiro.

O namespce da requisição está sempre no final da pilha do namespace do DTML, e é dessa forma o último namespace a ser procurado por nomes. Isto significa que devemos ser explícitos em nosso exemplo sobre que namespace queremos. Podemos fazer isto com a tag with da DTML.

  <dtml-var standard_html_header>


<dtml-with REQUEST only>
<dtml-if zooName>
<p><dtml-var zooName></p>
<dtml-else>
<form action="<dtml-var URL>" method="GET">
<input name="zooName">
<input type="submit" value="What is zooName?">
</form>
</dtml-if>
</dtml-with>








<dtml-var standard_html_footer>




Aqui a tag with diz para olhar no namespace REQUEST, e somente o namespace REQUEST, para o nome "zooName".

Objeto Cliente do DTML

O objeto cliente na DTML depende se você está ou não executando um DTML Method ou Document. No caso de um Document, o objeto cliente é sempre o próprio documento, ou em outras palavras, um documento DTML um DTML Documento é seu próprio objeto cliente.

Um DTML Method de qualquer forma pode ter diferentes tipos de objetos cliente dependendo de como ele é chamado. Por exemplo, se você possuia um DTML Method mostraca todos os conteúdos de uma pasta do objeto cliente querendo esta pasta então o objeto cliente seria a pasta que está sendo mostrada. Este objeto cliente pode mudar dependendo de qual pasta o método em questão é mostrado. Por exemplo, considere o seguinte DTML Method chamado list no folder raiz.

  <dtml-var standard_html_header>


<ul>
<dtml-in objectValues>
<li><dtml-var title_or_id></li>
</dtml-in>
</ul>








<dtml-var standard_html_footer>




Agora, o que este método mostra depende de como ele é usado. Se você aplicar este método para a pasta Reptiles com a URL http://localhost:8080/Reptiles/list, então você conseguirá algo como na Figura 7.3.

7-3.png

Figura 7.3 - Aplicando o método list para a pasta Reptiles.

Mas se você adotar o método para a pasta Birds com a URL http://localhost:8080/Birds/list então você obteria algo diferente, somente dois itens na lista, Parrot e Raptors.

Mesmo DTML Method, diferentes resultados. No primeiro exemplo, o objeto cliente do método list foi a pasta Reptiles. No segundo exemplo, o objeto cliente foi a pasta Birds. Quando o Zope procurou a variável objectValues, no primeiro caso ele chamou o método objectValues da pasta Reptiles, no segundo caso ele chamou o método objectValues da pasta Birds.

Em outras palavras, o objeto cliente é onde as variáveis assim como os métodos, e propriedades são procuradas por primeiro.

Como você viu Capítulo 4, "Conteúdo Dinâmico com DTML", se o Zope não pode achar uma variável no objeto cliente, ele procura através do repositório do objeto. O Zope usa aquisições para automaticamente [ herdar variáveis do repositório do objeto cliente. Assim quando o Zope passar sobre a hierarquia do objeto procurando as variáveis ele sempre iniciará no objeto cliente, e fucionará bem a partir daí.

Objeto Request do DTML

O objeto request é o objeto mais inferior na pilha do namespace da DTML. A requisição possui todas as informações especificadas para a requisição da web atual.

Da mesma forma o objeto cliente usa aquisição para procurar em vários lugares pelas variáveis, assim também o request procura as variáveis em muitos lugares. Quando o request procura uma variável ele consulta estes fontes em ordem:

  1. O ambiente CGI. O Common Gateway Interface, ou interface CGI define um grupo padrão de variáveis do ambiente para ser usado por scripts web dinâmicos. Estas vaiáveis são oferecidas pelo Zope no namespace do REQUEST.
  2. Dados do formulário. Se a requisição corrente é uma ação do formulário, então qualquer dado de entrada do formulário que foi submetido com a requisição pode ser encontrada no REQUEST do objeto.
  3. Cookies. Se o cliente da requisição corrente tem cookies este pode ser encontrado no objeto REQUEST corrente.
  4. Variáveis adicionais. O namespace REQUEST oferece a você muitas outras informações úteis, como a URL do objeto corrente e tudo sobre seus pais.

O namespace request é muito útil no Zope desde que ele seja o primeiro modo no qual os clientes(neste caso, browsers) se comuniquem com o Zope provendo dados do formulário, cookies e outras informações sobre eles mesmos. Para maiores informações sobre o objeto request, veja o Apêndice B.

Um exemplo muito simples e esclarecedor para simplesmente imprimir o REQUEST em uma página HTML:

<dtml-var standard_html_header>


<dtml-var REQUEST>




<dtml-var standard_html_footer>



Teste você mesmo, você deveria obter algo como na Figura 7.4.

7-4.png

Figura 7.4 - Mostrando o request.

Considerando que o request vem depois do objeto cliente, se houver nomes que existam em ambos request e ojetos cliente, o DTML sempre os achará primeiro no objeto cliente. Isto pode se um problema. Depois, vamos observar algumas maneiras de contornar este problema controlando mais diretamente como o DTML procura as variáveis.que modo procurou variáveis.

Renderizando Variáveis

Quando você inserir uma variável usando a tag var, o Zope primeiro procura a variável usando o namespace da DTML, então ele renderiza-a e insere os resultados. Renderizar significa transformar um objeto ou valor em uma string adequada para inserir na saída. O Zope renderiza simples variáveis usando o método padrão do Python para transformar objetos para strings. Para objetos complexos como DTML Methods e SQL Methods, o Zope chamará o objeto ao invés de apenas tentar transformar ele em uma string. Isto lhe permite inserir DTML Methods en outros DTML Methods.

Em geral o Zope renderiza variáveis do jeito que você quer. Você torna-se conhecedor do processo de renderização quando você começa a fazer coisas mais avançadas. Mais adiante neste capítulo veremos alguns exemplos de como controlar renderização usando a função do DTML getitem.

Modificando o DTML Namespace

Agora que você sabe que o namespace da DTML é uma pilha, você deve estar maravilhado em como, ou até mesmo porque, os novos objetos são inseridos nele.

Algumas tags DTML modificam o namespace da DTML enquanto elas estão executando. Uma tag pode inserir algum objeto no namespace da pilha durante o curso da execução. Estas tags incluem a tag in, a tag with, e a tag let.

Modificações no Namespace da Tag In

Quando a tag in interage sobre uma seqüência ele insere o item corrente na seqüência para o topo do namespace da pilha:

<dtml-var getId> <!-- This is the id of the client object -->



<dtml-in objectValues>




<dtml-var getId> <!-- this is the id of the current item in the
objectValues sequence -->



</dtml-in>

Você viu isto muitas vezes nos exemplos deste livro. Enquanto a tag in está interagindo sobre uma seqüência cada item é inserido para a pilha do namespace para a duração dos conteúdos do bloco da tag in. Quando o bloco acaba de excecutar, o item corrente na seqüência é mandado para fora da pilha do namespace da DTML e o próximo item da seqüência é inserido.

A Tag With

A tag with insere um objeto que você especificou para o topo da pilha do namespace para o resto do bloco with. Isto permite você especificar onde as variáveis poderiam ser procuradas por primeiro. Quando o bloco with fecha, o objeto é mandado para a pilha do namespace.

Considere uma pasta que contém alguns métodos e propriedades que lhe interessam. Você poderia acessar aqueles nomes com expressões Python como esta:

  <dtml-var standard_html_header>

<dtml-var expr="Reptiles.getReptileInfo()">


<dtml-var expr="Reptiles.reptileHouseMaintainer"




 <dtml-in expr="Reptiles.getReptiles()">


<dtml-var species>
</dtml-in>




<dtml-var standard_html_footer>



Observe que algumas coisas complexas foram adicionadas ao código apenas obter coisas fora do folder Reptiles. Usando a tag with você pode tornar este exemplo mais fácil de entender:

  <dtml-var standard_html_header>



<dtml-with Reptiles>




<dtml-var getReptileInfo>
<dtml-var reptileHouseMaintainer>




<dtml-in getReptiles>
<dtml-var species>
</dtml-in>




</dtml-with>




<dtml-var standard_html_footer>




Outra razão para você querer usar a tag with é colocar o request, ou qualquer parte do request no topo da pilha do namespace. Por exemplo suponha que você tenha um formulário que inclui uma entrada chamada id. Se você tentar processar este formulário procurando a variável id como:

  <dtml-var id>

Você não obterá a variável id do formulário, mas o id do cliente objeto. Uma solução é colocar o formulário do request da web no topo da pilha do namespace do DTML usando a tag with:

  <dtml-with expr="REQUEST.form">
<dtml-var id>
</dtml-with>

Isto assegurará que você obtenha o id do formulário primeiro. Veja o Apêndice B para analisar a documentação completa do API do objeto request.

Se você submeteu seu formulário sem fornecer um valor para a entrada id, o formulário no topo da pilha do namespace não funcionará bem, pois o formulário não contém uma variável id. Você ainda obterá o id do objeto cliente id visto que a DTML procurará o objeto cliente depois da falha para achar a variável id no formulário. A tag with tem um atributo que permite você preparar o namespace da DTML para incluir somente o objeto que você especificou:

  <dtml-with expr="REQUEST.form" only>
<dtml-if id>
<dtml-var id>
<dtml-else>
<p>The form didn't contain an "id" variable.</p>
</dtml-if>
</dtml-with>

Usando o atributo only você sabe onde suas variáveis começam a ser procuradas.

A Tag Let

A tag let permite você inserir um novo namespace para a pilha do namespace. Este namespace é definida pelos atributos da tag para a tag let:

  <dtml-let person="'Bob'" relation="'uncle'">
<p><dtml-var person>'s your <dtml-var relation&gt.</p>
</dtml-let>

Este exemplo deveria mostrar:

<p>Bob's your uncle.>/p<

A tag let engloba muitos dos mesmos objetivos da tag with. A principal vantagem da tag let é que você pode usá-la para definir múltiplas variáveis para serem usadas em um bloco. A Tag let cria uma ou mais variáveis novas e seus valores e insere um objeto namespace contendo aquelas variáveis e seus valores no topo da pilha do namespace da DTML. Em geral a tag with é mais útil para inserir objetos existentes para a pilha do namespace, enquanto a tag let é mais adequada para definir novas variáveis para um bloco.

Quando você estiver escrevendo um DTML complexo que requer coisas como novas variáveis, há uma boa chance de você fazer a mesma coisa melhor com o Python ou Perl. Scripts avançados são mostrados no Capítulo 10, "Avançados Scripts em Zope".

O namespace da DTML é complexo, e esta complexidade envolve muito tempo. Entretanto ela ajuda a entender de onde os nomes vem, é muito mais útil sempre especificar onde você está procurando um nome. As tags with e let controlam o namespace para procurar exatamente no lugar certo o nome que você procura.

Funções Úteis do DTML Namespace

Como todas as coisas no Zope, o namespace da DTML é um objeto, e ele pode ser acessado diretamente no DTML com o objeto (underscore). O namespace é freqüentemente referenciado como "the under namespace".

O under namespace oferece a você muitos métodos úteis para certas tarefas de programação. Vejamos alguns deles.

Digamos que você queira imprimir seu nome 3 vezes. Isto pode ser feito com a tag in, mas como você diz explicitamente para a tag in fazer um loop 3 vezes? Apenas passe para ela uma seqüência com três itens:

<dtml-var standard_html_header>
<ul>


<dtml-in expr="_.range(3)">
<li>&ltdtml-var sequence-item>: My name is Bob.</li>
</dtml-in>
</ul>
<dtml-var standard_html_footer>



A expressão Python _.range(3) retornará uma seqüência dos primeiros três inteiros, 0, 1 e 2. A função range é um padrão embutido no Python e muitas das funções embutidas do Python podem ser acessadas através do namespace _, incluindo:

range([start,], stop, [step])

Retorna uma lista de inteiros do start ao stop contando os inteiros step da vez. O padrão de start é 0 e o padrão do stepé 1. Por exemplo.

  _.range(3,9,2) -- gives [3,5,7,9].


len(sequence)

len returns the size of sequence as an integer (retorna o tamanha da sequence como um inteiro)



Muitos desses nomes vem da linguagem Python, que contém um conjunto de funções especiais chamadas built-in. A filosofia do Python é possuir um pequeno conjunto de números de nomes built-in. A filosofia do Zope pode ser imaginada como tendo um grande e complexo array de nomes embutidos.

O namespace under pode também se usado para controlar explicitamente a procura de variáveis. Existe um uso muito comum desta sintaxe. Você viu que a tag in define variáveis especiais, como sequence-item e sequence-key que você pode usar dentro de um loop para ajudar você a mostrá-la e controlá-la. O que acontece se você quiser usar uma destas variáveis dentro de uma expressão Python?:

  <dtml-var standard_html_header>




<h1> O ajuste para as primeiras 3 inteiras:</h1>
<ul>
<dtml-in expr="_.range(3)">
<li>The square of <dtml-var sequence-item> is:
<dtml-var expr="sequence-item * sequence-item">
</li>
</dtml-in>
</ul>



<dtml-var standard_html_footer>

Tente teste isto, ele funciona? Não! Por que não? O problema está na tag var:

<dtml-var expr="sequence-item * sequence-item">

Lembre-se, tudo dentro de um atributo da expressão do Python deve ser uma expressão válida do Python. No DTML, sequence-item é o nome de uma variável, mas em Python significa "The object sequence minus the object item" (A seqüência do objeto menos o item do objeto). Não é o que voccê quer.

O que você realmente quer é procurar a variável sequence-item. Uma maneira de resolver este problema é usar o atributo prefix da tag in. Por exemplo:

  <dtml-var standard_html_header>



<h1> O quadro dos três primeiros inteiros:</h1>
<ul>
<dtml-in prefix="loop" expr="_.range(3)">
<li>The square of <dtml-var loop_item> is:
<dtml-var expr="loop_item * loop_item">
</li>
</dtml-in>
</ul>




<dtml-var standard_html_footer>



O atributo prefix faz com que as variáveis da tag in sejam renomeadas usando o prefix especifico e underscores, preferivelmente usando "sequence" e dashes. Assim neste exemplo, "sequence-item" transformam-se em "loop-item". Veja o Apêndice A para mais informção sobre o atributo prefix.

Outra alternativa para procurar a variável sequence-item em uma expressão DTML é usar dentro da expressão DTML é usar a função utilitária getitem para explicitamente procurar uma variável:

  The square of <dtml-var sequence-item> is:
<tml-var expr="_.getitem(sequence-item) *
_.getitem(sequence-item)">

A função getitem pega o nome para procurar no primeiro argumento. Agora, o DTML Method mostrará corretamente a soma dos três primeiros inteiros. O método getitem recebe um segundo argumento opcional que especifica se renderiza ou não a variável. Lembre que renderizar uma variável DTML significa transformá-la em uma string. Por default a função getitem não renderiza uma variável.

Aqui está como inserir uma variável renderizada chamada my Doc:

  <dtml-var expr="_.getitem(myDoc, 1)">

Este exemplo é de alguma maneira sem propósito, este é o equivalente:

  <dtml-var myDoc>

De qualquer forma, suponha que você tinha um formulário no qual um usuário consegue selecionar em qual documento ele quer ver de uma lista de opções. Suponha que o formulário possuia uma entrada chamada selectedDoc que continha o nome do documento. Você poderia então mostrar o documento renderizado assom:

  <dtml-var expr="_.getitem(selectedDoc, 1)">

Observe no exemplo acima que selectedDoc não está entre aspas. Nós não queremos inserir a variável chamada selectedDoc nós queremos inserir a variável chamada pelo selectedDocs. Por exemplo, o valor do selectedDoc poderia ser ghapterOne. Usando inserções indiretas de variáveis você pode inserir a variável chapterOne. Assim você pode inserir uma variável cujo nome você não sabe quando está autorizando o DTML.

Se você programa em Python e você começa usando os mais complexos aspectos da DTML, considere feito muito do seu trabalho em scripts Python que você chama pela DTML. Isto é melhor explicado no Capítulo 10, "Avançado Script em Zope". Usar Python faz com que você não utilize muito a DTML.

Segurança do DTML

O Zope pode ser usado por diferentes tipos de usuários. Por exemplo, no site do Zope, Zope.org, possui no momento mais de 11.000 membros da comunidade. Cada membro pode logar no Zope, adicionar objetos e novos itens e gerenciar suas contas.

Porque a DTML é uma linguagem script, ela é muito flexível no trabalho com objetos e suas propriedades. Se não existir um sistema de segurança que compele a DTML então um usuário poderia potencialmente criar maldiciosamente ou invadir a privacidade do código DTML.

DTML é restrito pelo padrão das configurações de segurança do Zope. Assim se você não tem permissão para acessar um objeto pela sua URL você também não terá permissão para acessá-la pela DTML. Você não pode usar DTML para burlar o sistema de segurança do Zope.

Por exemplo, suponha que você possui um DTML Document chamado Diary que é privado. Usuários anônimos não poderão acessar seu diário pela web. Se um usuário anônimo visualizar DTML que tenta acessar seu diário o acesso será negado:

  <dtml-var Diary>

DTML verifica que o usuário corrente está autorizado para acessar todas as variáveis DTML. Se o usuário não tem autorização, então o sistema de segurança vai causar um erro Unauthorized e será pedido ao usuário apresentar mais credenciais de autorização privilegiada.

No Capítulo 7, "Usuários e segurança", você leu sobre regras de segurança para conteúdo executável. Existem maneiras de juntar as funções de um DTML Document ou Method para permití-lo acessar variáveis restritas sem levar em consideração as funções do usuário.

Limites de Segurança nos Scripts

DTML não deixará você consumir memória ou executar infinitos loops e recursões onde restrições nos looping e memória são curtas, fazendo da DTML uma linguagem para programação complexa e despendiosa. Por exemplo, você não pode criar grandes listas com a função de utilidade _.range. Você também não tem como acessar o sistema de arquivos diretamente no DTML.

Lembre-se de qualquer forma que os limites de segurança são simples e pode ser mais inteligente para um determinado usuário. Ele geralmente não é uma boa idéia para permitir que qualquer pessoa não confiável escreva código DTML em seu site.

Tags Avançadas do DTML

No restante deste capítulo nós veremos as avançadas tags DTML. Estas tags estão resumidas no Apêndice A. A DTML possui várias tags embutidas, como documentado neste livro, que podem ser levadas em consideração para serem apresentadas em todas as instalações do Zope e execução dos tipos mais comuns de objetos. De qualquer forma, é também possível adicionar novas tags para uma instalação do Zope. Instruções para fazer isto são oferecidas no web site do Zope.org, juntamente com um conjunto interessante de tags DTML.

Esta seção mostra o que poderia ser referenciado como tags diferentes do Zope. Estas tags realmente não se enquadram em nenhuma categira ampla exceto em um grupo de tags, as tags DTML de exception handling (tratamento de excessão) que são discutidos no final deste capítulo.

A Tag Call

A tag var pode chamar métodos, mas ela também insere um valor de retorno. Usando a tag call você pode chamar métodos sem inserir seus valores de retorno na saída. Isto é útil se você estiver mais interessado no efeito da chamada de um método especialmente seu valor de retorno.

Por exemplo, quando você quiser mudar o valor de uma propriedade, animalName, você está mais interessado nos efeitos da chamada do método manage_changeProperties do que no valor de retorno que o método lhe oferece. Aqui está um exemplo:

  <dtml-if expr="REQUEST.has_key(animalName)">
<dtml-call expr="manage_changeProperties(animalName=REQUEST['animalName'])">
<li>The property animalName has changed</h1>
<dtml-else>
<h1>No properties were changed</h1>
</dtml-if>

Neste exemplo, a página mudará uma propriedade dependendo se um certo nome existe. O resultado do método manage_changeProperties não é importante e não precisa ser mostrado para o usuário.

Outro uso comum da tag call é chamar métodos que afetam o comportamento do cliente, como o método RESPONSE.redirect. Neste exemplo, você cria o cliente redirecionado a uma página diferente, para mudar a página que foi redirecionada, mude o valor da variável "target" definida na tag let:

<dtml-var standard_html_header>



<dtml-let target="'http://example.com/new_location.html'">
&lth1>This page has moved, you will now be redirected to the
correct location. If your browser does not redirect, click <a
href="<dtml-var target>"><dtml-var target></a>.</h1>






&ltdtml-call expr="RESPONSE.redirect(target)">






</dtml-let>




<dtml-var standard_html_footer>



Em resumo, a tag call funciona exatamente como a tag var com exceção do fato dela não inserir os resultados chamando a variável.

A Tag Comment

O DTML pode ser documentado com comentários usando a tag comment:

<dtml-var standard_html_header>
<dtml-comment>
This is a DTML comment and will be removed from the DTML code
before it is returned to the client. This is useful for
documenting DTML code. Unlike HTML comments, DTML comments
are NEVER sent to the client.
</dtml-comment>

<!--
This is an HTML comment, this is NOT DTML and will be treated
as HTML and like any other HTML code will get sent to the
client. Although it is customary for an HTML browser to hide
these comments from the end user, they still get sent to the
client and can be easily seen by Viewing the Source of a
document.
-->
<dtml-var standard_html_footer>

O bloco comment é removido da saída do DTML.

Além disso para documentar o DTML você pode usar a tag comment para temporariamente comentar outras tags DTML. Mais tarde você pode remover as tags comment para reabilitar o DTML.

A Tag Tree

A tag tree permite que você construa facilmente árvores dinâmicas em HTML para mostrar dados hierarquicamente. A tree é uma representação gráfica de dados que começa com o objeto "root" que tem objetos abaixo dele geralmente referenciados como "branches" (ramificações) Ramificações podem ter suas próprias ramificações, exatamente como em uma árvore real. Este conceito deveria ser familiar para qualquer um que usa um programa de gerenciamento de arquivo como o Microsoft Windows Explorer para navegar nos sismtemas de arquivos. E, de fato, a visualização de navegação do frame a esquerda da interface de gerenciamento do Zope é criada usando a tag tree.

Por exemplo aqui temos uma árvore que representa uma coleção de pastas e sub-pastas.

7-5.png

Figura 7.5 Árvore HTML gerada pela tag tree.

Aqui está o DTML que gerou esta árvore:

  <dtml-var standard_html_header>
<dtml-tree>
<dtml-var getId>
</dtml-tree>
<dtml-var standard_html_footer>


A tag tree busca objetos para achar seus sub-objetos e ter a responsabilidade de mostrar os resultados como em uma árvore. O bloco da tag tree funciona a como um modelo para mostrar ligações da árvore.

Agora, mesmo o protocolo básico da web, HTTP, é sem origem, você precisa de alguma forma lembrar em que situação a árvore está cada vez que você olhar uma página. Para isto, o Zope armazena o estado da árvore em um cookie. Pelo fato do estado desta árvore estar armazenado em um cookie, somente uma árvore pode aparece em uma página web de cada vez, se não elas vão usar o mesmo cookie confusamente.

Você pode juntar o comportamento da tag tree inteiramente com os atributos da tag tree e com as variáveis especiais. Aqui está uma amostra dos atributos da tag tree.

branches

O nome do método usado para procurar por sub-objetos. Seu padrão é tpValues, que é um método definido pela quantidade de objetos padrão do Zope.

leaves

O nome de um método usado para mostrar objetos que não tem ramificações de sub-objetos.

nowrap

Tanto 0 como 1. Se 0, então o texto ramificado passará para a próxima linha, se não o texto pode ser truncado. O valor default é 0.

sort

Forma ramos antes da inserção de texto ser executado. O valor do atributo é o nome do atributo cujos itens deveria ser classificados.

assume_children

Tanto 0 ou 1. Se 1, então todos os objetos tem sub-objetos, e terão sempre um sinal de mais na frente deles quando eles estão poder então sempre um sinal de adição em frente de os quando eles não estiverem expandidos. Somente quando um item está expandido os sub-objetos podem ser procurados. esta poderia ser uma boa opção quando o retorno dos sub-objetos for um processo custoso. O valor default é 0.

single

Tanto 0 ou 1. Se 1, então somente uma ramificação pode ser expandida. Qualquer ramificação expandida não será expandida quando uma nova ramificação for expandida. O valor default é 0.

skip_unauthorized

Tanto 0 ou 1. Se 1, então não haverá erro ao tentar mostrar os sub-objetos cujo usuário não tem acesso sufuciente. Os sub-objetos protegidos não são mostrados. O valor default é 0.

Suponha que você quer usar a tag tree para criar um mapa dinâmico do site. Você não quer que cada páginaseja mostrada no mapa do site. Digamos que você colocou uma propriedade nos folders e documentos que você quer mostrar no mapar do site.

Vamos primeiro definir um Script com o id publicObjects que retorna objetos públicos:

  ## Script (Python) "publicObjects"
##
"""
Retorna sub-objetos e documentos DTML que possuem uma propriedade true ‘siteMap’.
"""
results=[]
for object in context.objectValues([Folder, 'DTML Document']):
if object.hasProperty(siteMap) and object.siteMap:
results.append(object)
return results

Agora podemos criar um DTML Method que usa a tag tree e nossos scripts desenhar o mapa do site:

 <dtml-var standard_html_header>
<h1>Site Map</h1>
<p><a href="&dtml-URL0;?expand_all=1">Expand All</a> |
<a href="&dtml-URL0;?collapse_all=1">Collapse All</a>
</p>
<dtml-tree branches="publicObjects" skip_unauthorized="1">
<a href="&dtml-absolute_url;"><dtml-var title_or_id></a>
</dtml-tree>
<dtml-var standard_html_footer>

Este DTML Method especiica um link para todos recursos públicos e mostra-os em uma árvore. Aqui está o resultado do mapa do site:

7-6.png

Figura 7.6 - Mapa do site dinâmico usando a tag tree.

Para um resumo dos argumentos da tag tree e variáveis especiais vveja o Apêndice A.

A Tag Return

Em geral DTML cria saídas textuais. Você pode de qualquer forma, fazer com que a DTML retorne outros valores além de texto. Usando a tag return, você pode fazer com que o DTML Method retorne um calor arbitrário exatamente como scripts baseados em Python ou Perl.

Aqui temos um exemplo:

 <p>Este texto é ignorado.</p>

<dtml-return expr="42">

Este DTML Method retorna o número 42.

Outro resultado no uso da tag return é que a execução do DTML parará depois da tag return.

Se você estiver usando a tag return, você certamente deveria estar usando um script no lugar. A tag return foi desenvolvida antes dos scripts e não é muito usada agora que você pode escrever scripts facilmente em Python e Perl.

A Tag Sendmail

A tag sendmail formata e envia mensagens de e-mail. Você pode usar a tag sendmail para conectar-se a um Host Mail existente, ou você pode especificar manualmente seu host SMTP.

Aqui temos um exemplo de como enviar uma mensagem de e-mail usando a tag sendmail:

  <dtml-sendmail>
To: <dtml-var recipient>
Subject: Make Money Fast!!!!

Take advantage of our exciting offer now! Using our exclusive method
you can build unimaginable wealth very quickly. Act now!
</dtml-sendmail>

Note que existe uma linha branca extra separando o cabeçalho do email do corpo da mensagem.

Um uso comum da tag sendmail é enviar mensagens de e-mail geradas por um formulário de feedback. A tag sendmail pode conter qualquer tag DTML que você quiser, assim é fácil juntar sua mensagem com os dados do formukário.

A Tag Mime

A tag mime lhe permite formatar dados usando MIME (Multipurpose Internet Mail Extensions). MIME é um padrão da Internet para codificar dados em uma mensagem de e-mail. Usando a tag mime você pode usar o Zope para enviar emails com anexos.

Suponha que você queira atualizar seu sumário no Zope e tem que mandar um email com este arquivo para o Zope para uma lista de potenciais dos empregados.

Aqui temos o formulário de atualização:

 <dtml-var standard_html_header>
<p>Send you resume to potential employers</p>
<form method=post action="sendresume" ENCTYPE="multipart/form-data">
<p>Resume file: <input type="file" name="resume_file"></p>
<p>Send to:</p>
<p>
<input type="checkbox" name="send_to:list" value="jobs@yahoo.com">
Yahoo<br>

<input type="checkbox" name="send_to:list" value="jobs@microsoft.com">
Microsoft<br>

<input type="checkbox" name="send_to:list" value="jobs@mcdonalds.com">
McDonalds</p>

<input type=submit value="Send Resume">
</form>

<dtml-var standard_html_footer>

Crie outro DTML Method chamado sendresume para processar o formulário e enviar o arquivo sumário:

<dtml-var standard_html_header>
<dtml-if send_to><dtml-in send_to>
<dtml-sendmail smtphost="my.mailserver.com">
To: <dtml-var sequence-item>
Subject: Resume
<dtml-mime type=text/plain encode=7bit>Hi, please take a look at my resume.
<dtml-boundary type=application/octet-stream disposition=attachment
encode=base64><dtml-var expr="resume_file.read()"></dtml-mime>
</dtml-sendmail></dtml-in><p>Your resume was sent.</p>
<dtml-else><p>You didn't select any recipients.</p>
</dtml-if>
<dtml-var standard_html_footer>

Este método interage sobre a variável sendto e envia um e-mail para cada item.

Note que não existe uma linha branca entre o cabeçalho do To: e o inicio da tag mime. Se uma linha branca for inserida entre eles então a mensagem não será interpretada como uma mensagem multipar pelo leitor que receberá o email.

Note também que não há uma nova linha entre a tag boundary e a tag var, ou o fim da tag var e o fechamento da tag mime. Isto é importante, se você quebra as tags com novas linhas então elas serão codificadas e incluídas na parte do MIME, o que provavelmente você não quer.

Conforme a especificação do MIME spec, tags mime podem ser colocadas arbitrariamente dentro das tags mime.

A Tag Unless

A tag unless executa um bloco de códigos a menos que a condição dada seja verdadeira. A tag unless é o oposto da tag if. O código DTML:

  <dtml-if expr="not butter">
I can't believe it's not butter.
</dtml-if>

é equivalente a:

  <dtml-unless expr="butter">
I can't believe it's not butter.
</dtml-unless>

Qual é o objetivo da tag unless? Esta é simplesmente uma tag de conveniência. A tag unless é mais limitada que a tag if, sendo assim ela não pode conter uma tag else ou elif.

Como a tag if, chamando a tag unless pelo nome temos:

  <dtml-unless the_easter_bunny>
The Easter Bunny does not exist or is not true.
</dtml-unless>

Checa a existência da the_easter-bunny bem como se é verdadeira. Enquanto este exemplo somente checa pela verdade do the_easter_bunny:

  <dtml-unless expr="the_easter_bunny">
The Easter Bunny is not true.
</dtml-unless>

Este exemplo causará uma exceção se a the_easter_bunny não existir.

Qualquer coisa que possa ser feita pela tag unless pode ser feita pela tag if. Desta forma, seu uso é totalmente opcional e uma forma de estilo.

Processamento em Lote com a Tag In

Freqüentemente você precisa mostrar uma lista grande de informações mas somente mostrar uma tela de cada vez para o usuário. Por exemplo, se um usuário fizesse uma busca em seu banco de dados e obtesse 120 resultados, você provavelmente vai querer mostrá-los ao usuário somente um pequeno lote, digamos que 10 ou 20 resultados por página. Dividir uma grande lista em partes é chamado de batching (paginação). A paginação possui vários benefícios.

  1. O usuário precisa somente fazer um download de um documento com um tamanho razoável especialmente de um documento grande. Isto faz com que páginas sejam rapidamente baixadas sendo elas menores.
  2. Porque lotes menores de resultados são usados, frequentemente para que menos memória seja consumida pelo Zope.
  3. As interfaces de navegação Next e Previous analisam grandes lotes facilmente.

A tag in oferece várias variáveis para facilitar o processamento de lotes. Olhe este exemplo completo que mostra como exibir 100 itens em grupos de 10 de cada vez:

  <dtml-var standard_html_header>
<dtml-in expr="_.range(100)" size=10 start=query_start>
<dtml-if sequence-start>
<dtml-if previous-sequence>
<a href="<dtml-var URL><dtml-var sequence-query>
query_start=<dtml-var previous-sequence-start-number>"Egt
(Previous <dtml-var previous-sequence-size> results)
</a>
</dtml-if>
<h1>These words are displayed at the top of a batch:</h1>
<ul>
</dtml-if>
<li>Iteration number: <dtml-var sequence-item></li>
<dtml-if sequence-end>
</ul>
<h4>These words are displayed at the bottom of a batch.</h4>
<dtml-if next-sequence>
<a href="<dtml-var URL><dtml-var sequence-query>
query_start=<dtml-var
next-sequence-start-number>">
(Next <dtml-var next-sequence-size> results)
</a>
</dtml-if>
</dtml-if>
</dtml-in>
<dtml-var standard_html_footer>

Vamos dar uma olhada no DTML para ter uma idéia de como ele funciona. Primeiro temos uma tag in que interage sobre os 100 números que são gerados por uma função de utilidade range. O atributo size diz para a tag in mostrar somente 10 itens cada vez. O atributo start diz para a tag in quais números dos itens exibir primeiro.

Dentro da tag in existem duas tags if essenciais. A primeira testa a variável especial sequence-start. Esta variável somente é verdadeira na primeira passagem pelo bloco. Assim os conteúdos desta tag if somente serão executados uma vez no início do loop. A Segunda tag if testa a variavel especial sequence-end. Esta variável somente é verdadeira na última passagem pela tag in. Desta forma o segundo bloco if somente será executado uma vez no final. O parágrafo entre as tags if é executado cada vez que passar pelo loop.

Dentro de cada tag if existe outra tag if que busca pelas vaiáveis especiais previous-sequence e next-sequence. As variáveis são verdadeiras quando o grupo atual tem lotes anteriores ou posterioes respectivamente. Em outras palavras previous-sequence é verdadeira para todos os lotes exceto para o primeiro, e next-sequence é verdadeira para todos os lotes exceto para o último. Assim o DTML testa para ver se há lotes adicionais disponíveis, e se assim ele desenha links de navegação.

A navegação do lote consiste de links que voltam ao documento com um conjunto de variáveis query_start que indicam onde a tag in deveria iniciar quando estiver exibindo o lote. Para perceber melhor como isto funciona, clique nos links previous e next algumas vezes e observe como as URLs para os links de navegação mudam.

Enfim algumas estatísticas sobre os lotes previous e next são mostrados usando variáveis especiais next-sequence-size e previous-sequence-size. Tudo isso termina gerando o seguinte código HTML:

 <html><head><title>Zope</title></head><body bgcolor="#FFFFFF">
<h1>Estas Palavras são mostradas no topo de um grupo:</h1>
<ul>
<li>Iteration number: 0</li>
<li>Iteration number: 1</li>
<li>Iteration number: 2</li>
<li>Iteration number: 3</li>
<li>Iteration number: 4</li>
<li>Iteration number: 5</li>
<li>Iteration number: 6</li>
<li>Iteration number: 7</li>
<li>Iteration number: 8</li>
<li>Iteration number: 9</li>
</ul>
<h4>Estas palavras são mostradas no fundo de um Grupo.</h4>
<a href="http://pdx:8090/batch?query_start=11">
(Next 10 results)
</a>
</body></html>

Processamento em lote pode ser complexo. Uma boa maneira de trabalhar com lotes é usar o objeto Searchable Interface para criar um relatório de busca em grupos (itens, quantidades) para você. Você pode então modificar o DTML para adequar as suas necessidades. Isto é melhor explicado no Capítulo 11, "Buscando e Categorizando Conteúdo".

Tags de Tratamento de Excessão

O Zope possui várias facilidades para o tratamento de excessões. Você pode ter acesso a essas facilidades com as tags raise e try. Para maiores informações sobre excessões e como elas são causadas e tratadas veja um livro sobre Python ou você pode ler o Turorial do Python online.

A Tag Raise

Você pode tratar excessões com a tag raise. Uma razão para tratar excessões é para indicar um erro. Por exemplo você poderia buscar um problema com uma tag if, e no caso de haver algo errado você poderia informar o erro com a tag raise.

A tag raise possui um atributo type para especificar um tipo de erro. O tipo de erro é um pequeno nome descritivo para o erro. Além disso, existem alguns tipos de erros padrão, como Unauthorized e Redirect que são retornados como erros HTTP. Erros Unauthorized mostram a tela de log in no browser do usuário. Você pode tratar erros HTTP para fazer com que o Zope envie um erro HTTP. Por exemplo:

  <dtml-raise type="404"&gtNot Found</dtml-raise>

Isto trata um erro HTTP 404 (not Found). O Zope responde enviando o erro HTTP 404 de volta para o browser do usuário.

A tag raise é uma tag de bloco. O bloco fechado pela tag raise é renderizado para criar uma mensagem de erro. Se o texto renderizado contém qualquer marcação HTML, então o Zope pode exibir o texto com uma mensagem de erro no browser, se não uma mensagem de erro genérica é exibida.

Aqui está um exemplo da tag raise:

<dtml-if expr="balance>= debit_amount">
<dtml-call expr="debitAccount(account, debit_amount)">
<p><dtml-var debit_amount> has been deducted from your
account <dtml-var account>.</p>
<dtml-else>
<dtml-raise type="Insufficient funds">
<p>There is not enough money in account <dtml-account>
to cover the requested debit amount.</p>
</dtml-raise>
</dtml-if>

Há o lado importante do efeito do tratamento de uma excessão, excessões fazem com que a transação corrente seja desfeita. Isto significa que qualquer mudança feita por um request na web será ignorada. Assim além disso para informar erros, excessões permitem a você desfazer as mudanças se um problema surgir.

A Tag Try

Se uma excessão é tratada tanto manualmente com a tag raise, quanto com os resultados de alguns erros que o Zope encontra, você pode capturá-los com a tag try.

Excessões são erros inesperados que o Zope encontra durante a execução de um DTML Document ou Method. Quando uma excessão é detectada, a execução normal da DTML para. Considere o seguinte exemplo:

  Cost per unit: <dtml-var
expr="_.float(total_cost/total_units)"
fmt=dollars-and-cents>

Este DTML trabalha muito bem se total_units não for zero. Entretanto, se total_units for zero, uma excessão ZeroDivisionError (erro de divisão por zero) é causado indicando uma operação ilegal. Portanto quando o DTML for renderizado, uma mensagem de erro será retornada.

Você pode usar a tag try para tratar este tipo de problema. Com a tag try você mesmo pode antecipar e tratar os erros, especialmente obtendo uma mensagem de erro no Zope sempre que ocorrer uma excessão.

A tag try possui duas funções. Primeira, se uma excessão é causada, a tag try adquire o controle da execução e trata a excessão apropriadamente, e deste modo evita o retorno de uma mensagem de erro do Zope. Segunda, a tag try permite a renderização de qualquer DTML subsequente contimue.

Dentro da tag try existem uma ou mais tags except que identificam e tratam diferentes excessões. Quando uma excessão é causada, cada tag except é verificada uma após a outra para ver se ela está compatível com os tipos de excessões. A primeira tag except para verifica trata a excessão. Se excessões não são dadas em uma tag except então a tag except irá comparar todas as excessões.

Aqui está como usar a tag try para prevenir erros que poderiam ocorrer no último exemplo:

<dtml-try>
Cost per unit: <dtml-var expr="_.float(total_cost/total_units)" fmt="dollars-and-cents">
<dtml-except ZeroDivisionError>
Cost per unit: N/A
</dtml-try>

Se um ZeroDivisionError for causado, o controle passa para a tag except e "Cost per unit: N/A" é mostrado. Quando o bloco da tag except termina, a execução do DTML contimua depois do bloco try.

A tag except da DTML funciona com excessões de classes baseadas em Python. Além disso para compara excessões por nome, a tag except verificará qualquer sub classe da excessão chamada. Por exemplo, se ArithmeticError for nomeado na tag except, a tag pode tratar de todas as subclasses ArithmeticError incluindo, ZeroDivisionError. Veja uma referência ao Python como a Referência a Biblioteca do Python online uma lista de excessões do Python e suas subclasses. Uma tag except pode capturar múltiplas excessões listando-as todas na mesma tag.

Dentro do corpo de uma tag except você pode acessar informações sobre a excessão tratada através de muitas variáveis especiais.

  error_type
O tipo de excessão tratada.
error_value
O valor da excessão tratada.
error_tb
O traceback da excessão tratada.

Você pode usar estas variáveis para oferecer mensagens de erro para os usuários ou para adotar diferentes ações como enviar e-mails para o webmaster ou documentar erros dependendo do tipo de erro.

O Bloco Opcional Else da Tag Try

A tag try possui um bloco opcional else que é renderizado se uma excessão não ocorreu. Aqui temos um exemplo de como usar a tag else dentro da tag try.

<dtml-try>
<dtml-call feedAlligators>
<dtml-except NotEnoughFood WrongKindOfFood>
<p>Make sure you have enough alligator food first.</p>
<dtml-except NotHungry>
<p>The alligators aren't hungry yet.</p>
<dtml-except>
<p>There was some problem trying to feed the alligators.<p>
<p>Error type: <dtml-var error_type></p>
<p>Error value: <dtml-var error_value></p>
<dtml-else>
<p>The alligator were successfully fed.</p>
</dtml-try>

O primeiro bloco except verifica que tipo de erro causado é renderizado. Se um bloco except não possuir nome, então ele verifica todos os erros causados. O bloco opcional else é renderizado quando não ocorre excessões no bloco try. Excessões no bloco else não são tratados pelo bloco except precedente.

O Bloco Opcional Finally da Tag Try

Você também pode usar a tag try de um modo um pouco diferente. Ao invés de tratar excessões, a tag try pode ser usada não para pegar excessões, mas para concertá-las.

A tag finally dentro da tag try especifica um bloco limpo para ser renderizado até mesmo quando uma excessão ocorre.

O bloco finally somente é útil se você precisa limpar algo que não pode ser limpo pelo código para aobrtar da transação. O bloco finally sempre será chamado, se houver uma excessão ou não e se uma tag return for usada ou não. Se você usa uma tag return no bloco try, qualquer saída do bloco finally é descartada. Aqui temos um exemplo de como você poderia usar a tag finally:

<dtml-call acquireLock>
<dtml-try>
<dtml-call useLockedResource>
<dtml-finally>
<!-- this always gets done even if an exception is raised -->
<dtml-call releaseLock>
</dtml-try>

Neste exemplo primeiro você adquire um lock em um recurso, então tenta executar algumas ações em um recurso bloqueado (locked). Se uma excessão é causada, você não trata-a, mas você deve lembrar de liberar o lock antes de passar o controle a um tratamento de excessão. Se tudo der certo e nenhuma excessão for causada, você ainda libera o lock no final do bloco try executando o bloco finally.

A forma try/finally da tag try é raramente usada no Zope. Este tipo de controle complexo da programação é freqüentemente feito melhor em Python ou Perl.

Conclusão

O DTML oferece algumas funcionalidades muito poderosas para projetar aplicações para a web. Neste capítulo, nós vimos as tags DTML mais avançadas e algumas de suas opções. Uma referência mais completa pode ser encontrada no Apêndice A.

O próximo capítulo ensina a você como transformar um ajudante de Modelo de Página. Enquanto o DTML é uma poderosa ferramenta, Modelos de Páginas oferecem uma solução mais elegante para geração do HTNL.

por leonardo miranda Última modificação 09/10/2008 11:28 Creative Commons
Navegação
Enquete
Como você efetiva sua participação comunitária?








Mais »