quinta-feira, 30 de setembro de 2010

Integrando aplicações Delphi Win32 com páginas WEB

Nesse post mostrarei como integrar aplicações Delphi Win32 com páginas Web. O exemplo será bem simples. Vamos imaginar que temos na web um log de erros unificados para todos os nossos clientes, e todas as instâncias de nosso software que estão sendo executadas. Imaginaremos que cada vez que ocorrer uma exception e um relatório de erro for gerado nós enviaremos esse relatório para a página na web, para que sejam armazenados em um banco de dados e possa ser lido ou analisado depois.

No nosso exemplo usaremos um campo memo para simular as mensagens de erro, assim podemos fazer várias mensagens diferentes.

Não usaremos banco de dados no nosso exemplo para não adicionar uma complexidade desnecessária a esse tutorial.

O que faremos é o seguinte: criaremos uma página php ou asp.net preparada para receber os dados de um formulário com um único campo: mLog. Não faremos uma página web para postar um formulário, faremos apenas a página que RECEBE o formulário.

Ao receber uma mensagem de log a página irá gravar a mensagem em um arquivo de texto, ler todos os registros do arquivo e mostrá-los na página em ordem inversa. Se você entrar na página esta não fará nada, apenas mostrará as mensagens.

Note que neste contexto você deveria mostrar apenas uma amostragem das mensagens, filtrar por cliente ou por sistema e esvaziar o arquivo periodicamente. Como nosso foco é apenas interagir com formulários não iremos nos ater a esses detalhes.

Primeiro: Crie o Formulário




//Versão em Asp.net
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Collections.Generic;

namespace RecebeLog
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

            if (Request["mErro"] != null)
            {
                StreamWriter sw = new StreamWriter(Server.MapPath("/lista.txt"), true, System.Text.Encoding.UTF8, 2048);
                sw.WriteLine("---------------------------------------------------");
                sw.WriteLine(Request["mErro"]);
                sw.Flush();
                sw.Close();

            }

            if (!IsPostBack)
            {
                if (File.Exists(Server.MapPath("/lista.txt")))
                {
                    LerArquivo();
                }
                else
                {
                    File.CreateText(Server.MapPath("/lista.txt"));
                }
            }
        }

        protected void LerArquivo()
        {
            StreamReader sr = new StreamReader(Server.MapPath("/lista.txt"));
            Stack pilha = new Stack();

            while (!sr.EndOfStream)
            {
                string linha = sr.ReadLine();
                pilha.Push(linha);
            }
            sr.Close();

            Response.Write("Quantidade de Registros: " + ((int)(pilha.Count) / 2).ToString() + "

");

            while (pilha.Count > 0)
            {
                Response.Write(pilha.Pop() + "
");
            }
        }
    }
}

//fim



//Versão em PHP
<html>

<body>
<?
//#############################################################
//##                 ###
//## Autor:   Vitor Luiz Rubio                        ###
//## E-mail:   vitorrubio@gmail.com                    ###
//## Site:    http://www.vitorrubio.blogspot.com/     ###
//## Msn:    vitorrubio@gmail.com                    ###
//##                  ###
//##                 ###
//#############################################################

//aqui começa o script
//pega as variaveis por POST
$mErro = $_POST["mErro"];
$count = 0;
$count2 = 0;
$total = 0;
$conteudo = array("");

if($mErro != null)
{
 $fp = fopen("lista.txt", "a+");  // abre o arquivo para leitura e "append"
 fwrite($fp, "-------------------------------------------------------------------------------\r\n");
 fwrite($fp, $mErro."\r\n");   // grava no arquivo. Se o arquivo não existir ele será criado
 fclose($fp);       // fecha o arquivo
}

//ABRE O ARQUIVO TXT
$ponteiro = fopen ("lista.txt", "r");

if($ponteiro != null)
{

 //LÊ O ARQUIVO ATÉ CHEGAR AO FIM 
 while (!feof ($ponteiro)) {
   //LÊ UMA LINHA DO ARQUIVO
   $linha = fgets($ponteiro, 4096);
   
   array_push($conteudo, $linha);
   
   $count++;
 }//FECHA WHILE

 //fecha ponteiro para  o arquivo
 fclose ($ponteiro);

 $total = (int)($count -1) / 2;
 echo "<strong>Total de Ocorrências</strong><br/>".$total."<br/><br/>";

 while ($count2 < $count) {
   //LÊ UMA LINHA DO ARQUIVO
   $linha = array_pop($conteudo);
   //IMPRIME NA TELA O RESULTADO
   echo $linha."<br/>";
   
   $count2++;
 }//FECHA WHILE

}

?>

</body>
</html>
//fim


Segundo: Crie o Programa em Delphi

Usaremos para isso um formulário com um botão, um memo e um idHttp.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdHTTP;

type
  TForm1 = class(TForm)
    IdHTTP1: TIdHTTP;
    Memo1: TMemo;
    Button1: TButton;
    edURL: TEdit;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  Texto: TStringList;
begin
  Texto := TStringList.Create;
  try
      Texto.Add('mErro='+Memo1.Lines.Text);
      //mErro = conteudo do memo1
      //use IdHTTP1.Post([URL], [mErro=[Mensagem]]);  //em um TStrings ou TStringList
      IdHTTP1.Post(edURL.Text, Texto);
  finally
      Texto.Free; //não esquecer do free
  end;  
end;

end.



Com isso, e um pouco de imaginação, você pode criar aplicações em qualquer linguagem que postem informações em páginas Web feitas também em qualquer linguagem. E se você usar o método GET na página WEB para ela retornar um XML, JSON ou YAML você já tem um webservice, rápido e simples de se implementar.

Experimente esse programa rodando em APACHE + PHP ou em IIS + Asp.Net e compile e rode o programa em Delphi. Adicione algumas mensagens e depois acesse a página para ver o que acontece. Veja o arquivo de texto. O diretório onde fica o arquivo de texto, ou pelo menos o próprio arquivo, deve ter permissão para que o PHP ou o Asp.Net escrevam nele.

Baixe o exemplo aqui

Have Fun ;)

terça-feira, 28 de setembro de 2010

Dica rápida sobre cache

Para seu site não ficar "preso" nos caches dos browsers e proxies da vida você deve orienta-los a não armazenar cache.

Há varias diretivas a serem colocadas no cabeçalho (head) que podem te ajudar com isso.

Por exemplo:
<META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE">  <!-- diz que não é para ter cache -->
<META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE"> <!-- faz a mesma coisa que o acima, mas  é um comando mais antigo, como cada browser entende uma coisa -->
<META HTTP-EQUIV="EXPIRES" CONTENT="0"> <!-- diz que a página expira agora, então se tiver cachê ele será reciclado. -->




Para fazer isso do lado do "code behind", se estiver trabalhando com C#, pode usar as linhas de código:

Response.CacheControl = "no-cache"; //adiciona o meta cachecontrol = no-cache
Response.AddHeader("Pragma", "No-Cache"); //adiciona o meta pragma = no-cache
Response.Cache.SetCacheability(HttpCacheability.NoCache);  //diz que a politica de cache é NoCache
Response.ExpiresAbsolute = DateTime.Now; //diz para o browser que a data de expiração da página é hoje
Response.Expires = -1; //diz para o browser que a expiração do cache é imediata.


Falando francamente eu não sei a diferença entre todos os vários métodos. Não sei qual funciona para cada browser e qual funciona com servidores de proxy, se é que algum funciona.
O que posso adiantar é que é sempre necessário retirar o cache de páginas de conteúdo dinâmico, conteúdo "logado" ou conteúdo "sensível". O restante pode ser cacheado.


Existe uma diferença sim entre o Expires e o ExpiresAbsolute. Na verdade não é uma diferença de função e sim de conceito. Internamente os dois são idênticos por isso é um desperdício de tempo e até "feio/gambi" usar os dois ao mesmo tempo, pois significam a mesma coisa no final.

A diferença entre as propriedades Response.Expires e a Response.ExpiresAbsolute é que na Expires você diz em quanto tempo a página expira, usando um inteiro. Na ExpiresAbsolute você especifica literalmente "quando" ela expira, na forma DateTime. Omitindo a hora a página expira à meia noite do dia especificado. Omitindo o dia e especificando a hora a página expira todos os dias nesse horário. Não sei o que acontece se não especificar nada. O certo seria expirar todo dia à meia noite, mas aí vai depender de outras regras de cache.

Ou seja, em um você especifica intervalo de tempo/quantidade de minutos. No outro você especifica uma data e hora absolutos.

Por isso não deve usar em conjunto o Response.Expires com o Response.ExpiresAbsolute, pois ambos fazem chamada interna ao método privado Response.Cache.SetExpires e então prevalecerá a hora da última chamada. Isso pode ser visto com a ajuda do Reflector.



Definições da Microsoft



The ExpiresAbsolute property specifies the date and time at which a page cached on a browser expires. If the user returns to the same page before that date and time, the cached version is displayed. If a time is not specified, the page expires at midnight of that day. If a date is not specified, the page expires at the given time on the day that the script runs.
http://msdn.microsoft.com/en-us/library/ms526058(VS.90).aspx


The Expires property specifies the duration of time before a page that is cached on a browser expires. If the user returns to the same page before it expires, the cached version is displayed.
http://msdn.microsoft.com/en-us/library/ms525906(v=vs.90).aspx

Veja aqui: http://www.dotnetspark.com/qa/427-difference-between-responseexpires-and-responseexpiresabsolute.aspx

Além desses comandos de cache temos esses metas que orientam os robôs de indexação e busca o que eles devem fazer: se devem indexar, seguir ou não.

<META NAME="ROBOTS" CONTENT="ALL"> 
<META NAME="ROBOTS" CONTENT="INDEX,NOFOLLOW"> 
<META NAME="ROBOTS" CONTENT="NOINDEX,FOLLOW"> 
<META NAME="ROBOTS" CONTENT="NONE">


E temos este comando especial abaixo para instruir o google bot a não arquivar páginas em seu cache

<META NAME="GOOGLEBOT" CONTENT="NOARCHIVE">


Legal. E como usar tudo isso?
A idéia é que você desenvolva um controle, masterpage ou usercontrol contendo esses controles de cache apenas "APENAS MESMO" para páginas que não podem ser cacheadas de jeito nenhum.
Assim se precisar mudar algo na política geral de cache pode mudar nesses controles e não sair à caça das páginas.

Mas você deve criar um outro controle, usercontrol ou masterpage PARA AS PÁGINAS QUE DEVEM SER CACHEADAS. Assim você pode determinar o tempo e a política de cache para o restante do site.
O site precisa de cache na maioria das páginas, por causa do ganho de velocidade que isso traz.

Páginas com conteúdo "logado" ainda podem ser cacheadas contanto que o conteúdo sensível, como "bom dia nome do usuário" seja atualizado por requisições Ajax assíncronas.

É isso aí, espero que tenha sido útil esse post, depois de tanto tempo sem postar.

Have fun :)

terça-feira, 21 de setembro de 2010

Linha vermelha do inferno: metro SP

Descaso, Desrespeito, Desonestidade ... putaria. É nisso que se resume o transporte coletivo em São Paulo.

Todos os dias, TODO O SANTO DIA, desperdiço e encurto um pouco minha vida passando pelo metrô. Situação extremamente insalubre, qualidade de vida ZERO.

Não basta a superlotação e a má administração, falta de infra-estrutura, agora de tempos em tempos ocorre uma pane geral que para essa maldita cidade.

O metrô para, os ônibus, trens e avenidas superlotam, e a cidade continua entupida até as 13:00. Não precisa ser um gênio pra saber que o metrô é mais do que vital para São Paulo, e menos do que precário.

Vou contar o que aconteceu hoje:


  1. 7:50 chego à estação penha do metrô, aparentemente normal, até que o metrô para. O trem na plataforma não parte, e começam os avisos nos auto-falantes.
  2. 8:00 avisam que o metrô está com maior tempo de parada e velocidade reduzida por causa de usuário (singular) na via na estação sé
  3. 8:10 repetem o aviso e continuam repetindo-o sistematicamente de dois em dois minutos, e avisam que a situação será normalizada em 10 minutos (depois 8, 6) Mas esse aviso é repetido duas vezes.
  4. 8:30 restringem o acesso às catracas (restrição nas linhas de bloqueio)
  5. 8:50 Fecham completamente o acesso nas catracas e interrompem a venda de bilhetes
  6. 9:00 Ainda estamos na estação penha e começamos a imaginar "coitados dos caras que estavam nos trens entre estações, pois estão superlotados, esmagados, com portas fechadas e ar desligado"
  7. 9:05 Paro de pensar nos coitados porque os fudidos começam a vir à estação penha andando pelos trilhos e subindo na plataforma. Começa uma comoção e uma depredação geral dos trens, onde as pessoas presas entre as estações, para poder respirar, quebram janelas para poder sair e voltar para algum lugar andando pela via, afinal estavam no meio do nada.
  8. 9:10 é decretada a evacuação da estação penha.


Ainda bem que não choveu, porque senão seria muito pior.

Esses estavam tentando ir pra a Corinthians-Itaquera, tiveram que evacuar o trem


Uma visão mais geral dos fudidos voltando

Tumulto e caos na evacuação

Superlotado em todos os sentidos

Visão de cima: à esquerda sentido corinthians-itaquera e à direita sentido barra funda

Sentido barra funda

Visão de cima


Tentativa de sair da estação Penha

Pessoas tirando fotos pra blogar ou xingar muito no twitter

Fila pra entrar, fila pra sair
Senhor governador o senhor viu isso? A não, você só faz as suas propagandas políticas de domingo as 4 da manhã, por isso consegue filmar o metrô vazio e funcionando.

Sabe o que poderia solucionar o problema do metrô? Senhores moradores de São Paulo, Brasileiros, Falta Vandalismo.

Senhores leitores esse ano teremos eleições, então façam-me o favor e joguem pac-man na urna eletrônica, pois ela seria melhor aproveitada.

Mais informações:

http://g1.globo.com/vc-no-g1/noticia/2010/09/internautas-relatam-o-caos-no-metro-de-sao-paulo.html
http://g1.globo.com/sao-paulo/noticia/2010/09/trem-para-e-passageiros-andam-nas-vias-da-linha-vermelha.html

Twitter http://twitter.com/#search?q=%23metrosp








sexta-feira, 17 de setembro de 2010

Obtendo informações do Browser: javascript

Os scripts abaixo permitem obter as mais diversas informações do browser.


Retirado de http://www.csgnetwork.com/directbrowsersnippets.html


AppName
<script language="JavaScript"><!--
document.write("AppName: " + navigator.appName);
//-->
</script>
AppVersion
<script language="JavaScript"><!--
document.write("AppVersion: " + navigator.appVersion);
//-->
</script>
UserAgent
<script language="JavaScript"><!--
document.write("UserAgent: " + navigator.userAgent);
//-->
</script>
Platform
<script language="JavaScript"><!--
if (navigator.platform) {
  document.write("Platform: " + navigator.platform);
  }
//-->
</script>
JavaScriptVersion
<script language="JavaScript"><!--
JavaScriptVersion = 10;
//-->
</script>
<script language="JavaScript1.1"><!--
JavaScriptVersion = 11;
//-->
</script>
<script language="JavaScript1.2"><!--
JavaScriptVersion = 12;
//-->
</script>
<script language="JavaScript1.3"><!--
JavaScriptVersion = 13;
//-->
</script>
<script language="JavaScript1.4"><!--
JavaScriptVersion = 14;
//-->
</script>
<script language="JavaScript"><!--
document.write("JavaScriptVersion: " +
  JavaScriptVersion);
//-->
</script>
Java
<script language="JavaScript"><!--
if (JavaScriptVersion >= 11) {
  if (navigator.javaEnabled() == true) {
      document.write("Java: enabled");
    } else {
      document.write("Java: disabled");
      }
  }
//-->
</script>
ScreenWidth
<script language="JavaScript"><!--
if (JavaScriptVersion >= 12) {
  document.write("ScreenWidth: " + screen.width);
  }
//-->
</script>
ScreenHeight
<script language="JavaScript"><!--
if (JavaScriptVersion >= 12) {
  document.write("ScreenHeight: " + screen.height);
  }
//-->
</script>
InnerWidth
<script language="JavaScript"><!--
if (JavaScriptVersion >= 12) {
  Browser = navigator.appName;
  if (navigator.appName.substring(0,9)
    == "Microsoft") Browser = 'MSIE';
  if (Browser != 'MSIE') {
      document.write("InnerWidth: " + innerWidth);
    } else {
      document.write("InnerWidth: " +
        document.body.clientWidth);
      }
  }
//-->
</script>
InnerHeight
<script language="JavaScript"><!--
if (JavaScriptVersion >= 12) {
  Browser = navigator.appName;
  if (navigator.appName.substring(0,9)
    == "Microsoft") Browser = 'MSIE';
  if (Browser != 'MSIE') {
      document.write("InnerHeight: " + innerHeight);
    } else {
      document.write("InnerHeight: " +
        document.body.clientHeight);
      }
  }
//-->
</script>
Colors
<script language="JavaScript"><!--
if (JavaScriptVersion >= 12) {
  Browser = navigator.appName;
  if (navigator.appName.substring(0,9)
    == "Microsoft") Browser = 'MSIE';
  if (Browser != 'MSIE') {
      document.write("Colors: " + screen.pixelDepth);
    } else {
      document.write("Colors: " + screen.colorDepth);
      }
  }
//-->
</script>
Layers
<script language="JavaScript"><!--
if ((document.all) || (document.layers)) {
    document.write("Layers: yes");
  } else {
    document.write("Layers: no");
    }
//-->
</script>
Plugins
<script language="JavaScript"><!--
if (navigator.plugins) {
  for (i = 0; i < navigator.plugins.length; i++) {
    document.write("Plugin " + i + ": " +
      navigator.plugins[i].name + "<br>");
    }
  }
//-->
</script>
Referrer
<script language="JavaScript"><!--
if (document.referrer) {
  document.write("Referrer: " + document.referrer);
  }
//-->
</script>
Time on your computer
<script language="JavaScript"><!--
document.write("Time on your computer: " + Date());
//-->
</script>
Resultados






























Have fun ;)

Capturar IP do usuario no Browser: local e remoto

Saber qual o tipo de browser de nossos clientes/usuários e o seu comportamento é de grande valia. Você precisará disso quando:

  • Prestar suporte ao usuário e ele não souber dizer informações técnicas sobre o browser
  • Logar informações sobre browsers, capacidades e informações
  • Logar informações de parceiros (referrers), domínios e logins
  • Se você criar um site de conteudo restrito poderá detectar logins simultaneos em máquinas ou browsers diferentes
  • Levantamentos estatísticos utilizando as informações logadas acima

Imagine que o login é pessoal e intransferível (lógico neh?) mas que você criou um mecanismo que detecta logins simultâneos e derruba a seção do usuário. Se um usuário se logar de uma rede corporativa, lan-house ou coisa do gênero o IP do client vai ser sempre o mesmo: o IP do proxy ou gateway de acesso a internet. Como saber se foi um usuário diferente que logou com o mesmo login em outra máquina se os IP's são iguais? Pode ter sido o mesmo usuário que abriu dois browsers na mesma máquina.
Na verdade não tem como saber, mas ajudaria bastante se pudéssemos capturar  o IP interno da rede. 

Isso é possível com a variável de servidor (Server Variable) HTTP_X_FORWARDED_FOR. (nem sempre esta variável estará presente, geralmente só quando o acesso for feito via proxy).

O código abaixo é para uma página aspx que mostra todas as capacidades/configurações do browser e suas versões, e mostra também todas as server variables e parâmetros do servidor.

Fique atento a:

  • UserHostAddress
  • REMOTE_ADDR
  • HTTP_X_FORWARDED_FOR
  • Request.Browser.Browser.ToString()
  • Request.Browser.Type.ToString()
  • Request.Browser.Version.ToString()

São as variáveis mais importantes para você atender a essa necessidade.


<html>

<body>
<script runat=server language=C#>

	private void PegaServerVariables()
	{
			Response.Write("IP: " + Request.UserHostAddress + "<br/>");
			Response.Write("Remote Address: " + Request.ServerVariables["REMOTE_ADDR"] + "<br/>");
			
			
			Response.Write("<br><br><br>");
			Response.Write("Browser Caps:<br><br>");
			
			Response.Write("ActiveXControls: " + Request.Browser.ActiveXControls.ToString() + "<br/>");
			Response.Write("AOL: " + Request.Browser.AOL.ToString() + "<br/>");
			Response.Write("BackgroundSounds: " + Request.Browser.BackgroundSounds.ToString() + "<br/>");
			Response.Write("Beta: " + Request.Browser.Beta.ToString() + "<br/>");
			Response.Write("Browser: " + Request.Browser.Browser.ToString() + "<br/>");
			Response.Write("CDF: " + Request.Browser.CDF.ToString() + "<br/>");
			Response.Write("ClrVersion: " + Request.Browser.ClrVersion.ToString() + "<br/>");
			Response.Write("Cookies: " + Request.Browser.Cookies.ToString() + "<br/>");
			Response.Write("Crawler: " + Request.Browser.Crawler.ToString() + "<br/>");
			Response.Write("EcmaScriptVersion: " + Request.Browser.EcmaScriptVersion.ToString() + "<br/>");
			Response.Write("Frames: " + Request.Browser.Frames.ToString() + "<br/>");
			Response.Write("JavaApplets: " + Request.Browser.JavaApplets.ToString() + "<br/>");
			Response.Write("JavaScript: " + Request.Browser.JavaScript.ToString() + "<br/>");
			Response.Write("MajorVersion: " + Request.Browser.MajorVersion.ToString() + "<br/>");
			Response.Write("MinorVersion: " + Request.Browser.MinorVersion.ToString() + "<br/>");
			Response.Write("MSDomVersion: " + Request.Browser.MSDomVersion.ToString() + "<br/>");
			Response.Write("Platform: " + Request.Browser.Platform.ToString() + "<br/>");
			Response.Write("Tables: " + Request.Browser.Tables.ToString() + "<br/>");
			Response.Write("TagWriter: " + Request.Browser.TagWriter.ToString() + "<br/>");
			Response.Write("Type: " + Request.Browser.Type.ToString() + "<br/>");
			Response.Write("VBScript: " + Request.Browser.VBScript.ToString() + "<br/>");
			Response.Write("Version: " + Request.Browser.Version.ToString() + "<br/>");
			Response.Write("W3CDomVersion: " + Request.Browser.W3CDomVersion.ToString() + "<br/>");
			Response.Write("Win16: " + Request.Browser.Win16.ToString() + "<br/>");
			Response.Write("Win32: " + Request.Browser.Win32.ToString() + "<br/>");
			Response.Write("<br><br><br>");			
			

			Response.Write("<br><br><br>");
			Response.Write("Server Variables:<br><br>");
			for(int i=0; i<Request.ServerVariables.Count; i++)
			{
				string valor = string.Empty;
				valor += Request.ServerVariables.GetKey(i) + " = ";
				for(int j=0; j<Request.ServerVariables.GetValues(i).Length; j++)
				{
					valor += Request.ServerVariables.GetValues(i)[j].ToString()+"         ";
				}
				valor += "<br>";
				Response.Write(valor);
			}

			Response.Write("<br><br><br>");
			Response.Write("Server Params:<br><br>");
			for(int i=0; i<Request.Params.Count; i++)
			{
				string valor = string.Empty;
				valor += Request.Params.GetKey(i) + " = ";
				for(int j=0; j<Request.Params.GetValues(i).Length; j++)
				{
					valor += Request.Params.GetValues(i)[j].ToString()+"         ";
				}
				valor += "<br>";
				Response.Write(valor);
			}

	}

	
	
	private void Page_Load(object sender, System.EventArgs e)
	{
		PegaServerVariables();
	}
</script>
<body>

</html>


Só o método, sem o html, num arquivo .cs (code behind) seria assim:

private void PegaServerVariables()
{
  Response.Write("IP: " + Request.UserHostAddress + "
"); Response.Write("Remote Address: " + Request.ServerVariables["REMOTE_ADDR"] + "
"); Response.Write("


"); Response.Write("Browser Caps:

"); Response.Write("ActiveXControls: " + Request.Browser.ActiveXControls.ToString() + "
"); Response.Write("AOL: " + Request.Browser.AOL.ToString() + "
"); Response.Write("BackgroundSounds: " + Request.Browser.BackgroundSounds.ToString() + "
"); Response.Write("Beta: " + Request.Browser.Beta.ToString() + "
"); Response.Write("Browser: " + Request.Browser.Browser.ToString() + "
"); Response.Write("CDF: " + Request.Browser.CDF.ToString() + "
"); Response.Write("ClrVersion: " + Request.Browser.ClrVersion.ToString() + "
"); Response.Write("Cookies: " + Request.Browser.Cookies.ToString() + "
"); Response.Write("Crawler: " + Request.Browser.Crawler.ToString() + "
"); Response.Write("EcmaScriptVersion: " + Request.Browser.EcmaScriptVersion.ToString() + "
"); Response.Write("Frames: " + Request.Browser.Frames.ToString() + "
"); Response.Write("JavaApplets: " + Request.Browser.JavaApplets.ToString() + "
"); Response.Write("JavaScript: " + Request.Browser.JavaScript.ToString() + "
"); Response.Write("MajorVersion: " + Request.Browser.MajorVersion.ToString() + "
"); Response.Write("MinorVersion: " + Request.Browser.MinorVersion.ToString() + "
"); Response.Write("MSDomVersion: " + Request.Browser.MSDomVersion.ToString() + "
"); Response.Write("Platform: " + Request.Browser.Platform.ToString() + "
"); Response.Write("Tables: " + Request.Browser.Tables.ToString() + "
"); Response.Write("TagWriter: " + Request.Browser.TagWriter.ToString() + "
"); Response.Write("Type: " + Request.Browser.Type.ToString() + "
"); Response.Write("VBScript: " + Request.Browser.VBScript.ToString() + "
"); Response.Write("Version: " + Request.Browser.Version.ToString() + "
"); Response.Write("W3CDomVersion: " + Request.Browser.W3CDomVersion.ToString() + "
"); Response.Write("Win16: " + Request.Browser.Win16.ToString() + "
"); Response.Write("Win32: " + Request.Browser.Win32.ToString() + "
"); Response.Write("


"); Response.Write("


"); Response.Write("Server Variables:
"); for(int i=0; i < Request.ServerVariables.Count; i++) { string valor = string.Empty; valor += Request.ServerVariables.GetKey(i) + " = "; for(int j=0; j < Request.ServerVariables.GetValues(i).Length; j++) { valor += Request.ServerVariables.GetValues(i)[j].ToString()+" "; } valor += "
"; Response.Write(valor); } Response.Write("


"); Response.Write("Server Params:

"); for(int i=0; i<Request.Params.Count; i++) { string valor = string.Empty; valor += Request.Params.GetKey(i) + " = "; for(int j=0; j<Request.Params.GetValues(i).Length; j++) { valor += Request.Params.GetValues(i)[j].ToString()+" "; } valor += "
"; Response.Write(valor); } }

Isso funciona não só em qualquer linguagem .net (VB.net, ironPython etc...) mas tem similares em qualquer linguagem dinâmica Web, seja Java, PHP, Ruby ou Python.

Até + ;)

segunda-feira, 13 de setembro de 2010

Quebras de linha no Delphi 2010

Alguns tem me perguntado sobre quebra de linha no Delphi 2010, o que está dando de errado.

Antes de falar sobre isso, precisamos saber como funciona a quebra de linha.

No sistema ASCII (caracteres de um byte) a quebra de linha nos sistemas gnu linux / unix é feita somente com o caracter #10 (ou \n line feed  - LF para os íntimos). Já para sistemas windows a quebra é feita com #13#10 ou CR\LF.

Há ainda mais um agravante: assim como no GNU linux e unix o #10 sozinho é considerado como quebra de linha, há também alguns sistemas onde o #13 sozinho é considerado como quebra de linha. Então, alguns editores, como o notepad++ interpretam tanto o #10 isolado como o #13 isolado como quebras de linha, bem como a dupla #13#10, para atender a todas as demandas.

O #13 ou \r é chamado de Carriage Return (CR) e o #10 ou \n é chamado de Line Feed (LF). Nas impressoras matriciais e maquinas de escrever elétricas antigas, para se pular uma linha e se escrever na linha de baixo, eram necessarios dois comandos:

O Carriage Return retornava o cabeçote de impressão para a posição mais a esquerda e o Line Feed fazia o papel "rolar" a altura de uma linha.

Por isso os caracteres CR e LF são tratados até hoje como comandos, e também por isso que  em sistemas windows ASCII a quebra de linha é formada pelos caracteres CR e LF juntos.

O que acontece com o Delphi 2010 é que agora os caracteres, unicode, são formados por 2 bytes e não apenas um. Isso significa que o byte da direita deve ser preenchido com alguma coisa. Lembrando que, em sistemas IBM-PC, como a leitura é feita da direita para a esquerda, o byte mais significativo é o da direita e o menos significativo é o da esquerda. é como se escrevêssemos números em que a casa das unidades fica a esquerda e a casa das dezenas e centenas vão sendo postas a direita desta. (para ilustrar somente).

Então no Delphi Unicode a quebra de linha se dá não apenas com #13#10, mas com #13#0#10#0, veja os exemplos.

Para salvar os arquivos no formato correto pergunte-se qual o formato de arquivo que o destino necessita, se é unicode ou ASCII, e qual é a quebra de linha nesse sistema.

É interessante sempre salvar um arquivo como UNICODE e sempre usar a constante LineBreak (PROPRIEDADE DO TSTRINGLIST NA UNIT CLASSES) do Delphi, que fornece a quebra de linha correta de acordo com  o sistema. (CASO VOCÊ ESTEJA USANDO TSTRINGLIST)
Para analizar os arquivos gerados use um editor Hexadecimal. usarei por exemplo o HexPlorer.

Cuidado: quando usar o #0, lembre-se que ele deve ser usado apenas nos Delphis anteriores ao 2009 como o 7, ou qualquer outra linguagem que seja nativamente ASCII. Isso porque embora no Delphi 2010 a quebra de linha tenha mudado para #13#0#10#0 os chars no Delphi 2010 JÁ TEM 2 bytes JÁ VEM com o #0 no final.

Nesse caso, o #13 já é automaticamente #13#0 e o #10 já é #10#0. Na verdade o número dos caracteres especiais continuam sendo 13 e 10, isso não muda. Os zeros a esquerda, assim como são desnecessários, torna-se também desnecessário colocar o "zero à direita" em um sistema IBM-PC litle endian, onde, como já dissemos, o "byte à esquerda" (mais significativo) fica à direita.

Então se você digitar #13#0#10#0 no Delphi 2010 na verdade ele vai virar #13#0#0#0#10#0#0#0 com duas quebras de string desnecessárias e que farão com que sua string seja quebrada no meio e nem chegue ao arquivo.


Faremos 6 exemplos: dois salvando em ASCII e usando as sequencias #13#10 e a constante LineBreak. e os outros dois nesse mesmo esquema só que usando Unicode. Os últimos dois usaremos TFileStream.

procedure TForm1.Button1Click(Sender: TObject);
begin

    with TStringList.Create do
    try
      Text := 'Hello' + #13#10 + 'World';
      SaveToFile(ExtractFilePath(Application.ExeName)+'\teste1.txt', TEncoding.ASCII);
    finally
      Free;
    end;

end;

procedure TForm1.Button2Click(Sender: TObject);
begin
    with TStringList.Create do
    try
      Text := 'Hello' + LineBreak + 'World';
      SaveToFile(ExtractFilePath(Application.ExeName)+'\teste2.txt', TEncoding.ASCII);
    finally
      Free;
    end;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin

    with TStringList.Create do
    try
      Text := 'Hello' + #13#10 + 'World';
      SaveToFile(ExtractFilePath(Application.ExeName)+'\teste3.txt', TEncoding.Unicode);
    finally
      Free;
    end;

end;

procedure TForm1.Button4Click(Sender: TObject);
begin
    with TStringList.Create do
    try
      Text := 'Hello' + LineBreak + 'World';
      SaveToFile(ExtractFilePath(Application.ExeName)+'\teste4.txt', TEncoding.Unicode);
    finally
      Free;
    end;
end;

procedure TForm1.Button5Click(Sender: TObject);
begin
    with TFileStream.Create(ExtractFilePath(Application.ExeName)+'\teste5.txt', fmCreate) do
    try
      Write('Hello' + #13#10 + 'World', 24);
    finally
      Free;
    end;
end;

procedure TForm1.Button6Click(Sender: TObject);
begin
    with TFileStream.Create(ExtractFilePath(Application.ExeName)+'\teste6.txt', fmCreate) do
    try
      Write('Hello' + #13#0#10#0 + 'World', 30);
    finally
      Free;
    end;
end;

Conclusão: para quebrar linha use sempre #13#10 juntos, mas tome cuidado com o encoding e com quem será o receptor. Se o destino for unicode e você utilizar o Delphi 7 (ascii) utilize widestrings, mas se o destino for ASCII e você utilizar Delphi 2010 (unicode), utilize AnsiStrings, Encoding.ASCII e quebra de linha #13#10.

Agora vamos duplicar os exemplos 5 e 6 e vamos executá-los tanto no Delphi 2010 como no Delphi 7.

procedure TForm1.Button1Click(Sender: TObject);
var
  teste: AnsiString;
begin
    with TFileStream.Create(ExtractFilePath(Application.ExeName)+'\teste1.txt', fmCreate) do
    try
      teste := 'Hello' + #13#10 + 'World';
      Write(Pointer(teste)^, Length(teste)*SizeOf(AnsiChar));
    finally
      Free;
    end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  teste: AnsiString;
begin
    with TFileStream.Create(ExtractFilePath(Application.ExeName)+'\teste2.txt', fmCreate) do
    try
      teste := 'Hello' + #13#0#10#0 + 'World';
      Write(Pointer(teste)^, Length(teste)*SizeOf(AnsiChar));
    finally
      Free;
    end;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  teste: WideString;
begin
    with TFileStream.Create(ExtractFilePath(Application.ExeName)+'\teste3.txt', fmCreate) do
    try
      teste := 'Hello' + #13#10 + 'World';
      Write(Pointer(teste)^, Length(teste)*SizeOf(WideChar));
    finally
      Free;
    end;
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  teste: WideString;
begin
    with TFileStream.Create(ExtractFilePath(Application.ExeName)+'\teste4.txt', fmCreate) do
    try
      teste := 'Hello' + #13#0#10#0 + 'World';
      Write(Pointer(teste)^, Length(teste)*SizeOf(WideChar));
    finally
      Free;
    end;
end;

O código acima, executado no Delphi 2010, cria 4 arquivos. Repare que usando AnsiString o #13#10 equivale literalmente a #13#10 e o #13#0#10#0 também é transcrito normalmente. Repare também que usando widestrings cada char terá o dobro do tamanho, sendo o mesmo código que teria em ASCII + 00. O #13#10 sai automaticamente como #13#0#10#0 e colocar #0 nessa string duplicaria o #0.

Não abra com o notepad. Principalmente os arquivos 2 e 4 com #13#0#10#0 forçados, pois eles serão interpretados erroneamente e mostrarão as letras com um espaço entre si, como se estivesse lendo um Unicode mal formatado. Use sempre o Hexplorer.

Arquivo teste1

Arquivo teste2

Arquivo teste3

Arquivo teste4


E é isso aí. Até a próxima ;)

sexta-feira, 10 de setembro de 2010

Abrindo e fechando uma página web com o Delphi

É possível executar um browser com WinExec ou ShellExecute para abrir uma página web, mas o que eu vou mostrar aqui é como usar um objeto OLE para criar uma instância de um IE, abrindo ele na página desejada e depois fechando.

Isso pode ser aplicado, por exemplo, em totens ou lan houses onde o acesso ao browser e suas funções, ou até mesmo o acesso a páginas é restrito.

Para isso vamos usar um vetor de 10 posições de variant, mas você pode usar uma lista ou pilha, fique a vontade para usar a imaginação. Esse vetor será responsável por controlar as instâncias do IE abertas e executar operações sobre elas. Você pode fechar na ordem que foi aberto, fechar pelo índice ou fechar todas varrendo o vetor, quando esgotar o tempo do usuário, por exemplo.

Coloque em um formulário um edit (para digitação da url) e dois botões. O código ficará assim:

unit abreFecha;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Comobj;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Label1: TLabel;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    IEapp : array[0..9] of variant;
    fIndice : integer;
  public

  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if fIndice > 9 then
    exit;
  IEApp[findice] := CreateOLEObject('InternetExplorer.Application');
  IEApp[findice].visible := true;
  IEApp[findice].Top := 0;
  IEApp[findice].Left := 0;
  IEApp[findice].width := screen.width;
  IEApp[findice].height := screen.height;
  IEApp[findice].Navigate(edit1.Text);
  inc(fIndice);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if fIndice = 0 then exit;
  IEApp[findice-1].Quit;
  dec(fIndice);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  fIndice := 0;
end;

end.


Até a próxima dica básica de Delphi ;)

Primeiras impressões com Python

O Python é uma linguagem interpretada, dinâmica e multi-plataforma. Roda em qualquer máquina que contenha o seu runtime. É possível "compilar" um programa em python de modo que ele se transforme numa espécie de bytecodes, de tamanho menor, interpretado mais rapidamente pelo runtime. Nesses aspectos ele é semelhante a linguagens como Java, PHP e Ruby.

Você pode usar a GTK ou a API do windows para gerar programas desktop com o python, onde é possível ter janelas, botões etc. Mas o python é muito forte também ( e me arrisco a dizer : principalmente) na web. Web Servers como o Apache tem módulos que interpretam python, e ele pode ser assim usado como linguagem para páginas dinâmicas. Há frameworks que facilitam essa tarefa, como o Django, que contém uma série de classes para se criar sites dinâmicos e bem elaborados. Há inclusive redes sociais feitas com essa dupla.

Vou começar aqui um pequeno tutorial de Python e falar das minhas primeiras impressões com a linguagem.
Uma coisa muito importante no Python é a identação: ela é levada em conta na hora de encadear if's ou laços, e é interpretada como sendo parte do programa. Ou seja, a correta identação substitui os Begin ... End da vida, ou { e }.

Isso ajuda a criar programas bem legíveis e que não se percam em uma infinidade de if's ou laços aninhados.

Um exemplo:

Salvando um arquivo TXT

import string

class LogFile():
    
    def SalvaLog(self, text):
  arq = open ("c:\\teste\\meuarquivo.txt", 'a+')
  arq.write (text)
  arq.close ()
 
a = LogFile ()
a.SalvaLog ("meu teste de escrever em arquivo\nmuito legal")




Por enquanto é isso.

Botão Add To Any

Em matéria de integração com mídias sociais o botão Add To Any se destaca pela variedade de serviços e pelo aspecto. O botão é realmente bonito.

Para usar no blogspot não há tantas opções de customização, e o código é um pouco diferente do código para colocar num site estático, pois tem aqueles comandos que pegam variáveis do próprio blogspot.

Mas existe um mecanismo no site que adiciona o botão diretamente no seu blog, como um widget.


Obter o Botão

Para obter o widget clique no link widget ao lado do seu cms preferido.


O código é este:
<!-- AddToAny Share/Save BEGIN -->
<br/><a class='a2a_dd' href='http://www.addtoany.com/share_save'><img alt='Share/Bookmark' border='0' src='http://static.addtoany.com/buttons/share_save_171_16.png' width='171' height='16'/></a><p class='a2a_linkname_escape' style='display:none'><data:post.title/></p><script type='text/javascript'>a2a_linkname_escape=1;a2a_linkurl="<data:post.url/>";</script><script src='http://static.addtoany.com/menu/page.js' type='text/javascript'></script>
<!-- AddToAny Share/Save END -->

E o efeito é este:


Share/Bookmark


Espero que tenha sido útil ;)

quinta-feira, 9 de setembro de 2010

Solidão

Quando você se sentir triste... 

Sem amigos...,


E desejar a presença de alguém...


Não perca tempo...
  Faça uma baldeação na Sé!



Mano , na Estação de metrô da SÉ não há solidão que resista ...

Criando um cronômetro no Delphi

Esse é um post básico, destinado a quem está iniciando a programação em Delphi ou Lazarus. Vamos criar um cronômetro.

Um cronômetro deve marcar horas, minutos, segundos e milésimos de segundos. Deve ter um botão para iniciar, um para parar e outro para zerar.

Crie a form preta com um label vermelho e três botões, e deixe-a como a figura acima. Coloque também um TTimer e deixe a propriedade interval em 1 milissegundo e a propriedade enabled como false, para ele começar desligado.

O DFM/LFM ficará assim:

object frmvisor: Tfrmvisor
  Left = 405
  Top = 209
  BorderIcons = [biSystemMenu, biMinimize]
  BorderStyle = bsDialog
  Caption = 'Cron'#244'metro'
  ClientHeight = 102
  ClientWidth = 314
  Color = clBlack
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  SnapBuffer = 1
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object lbTempo: TLabel
    Left = 8
    Top = 8
    Width = 294
    Height = 55
    Caption = '00:00:00.000'
    Font.Charset = ANSI_CHARSET
    Font.Color = clRed
    Font.Height = -48
    Font.Name = 'Microsoft Sans Serif'
    Font.Style = [fsBold]
    ParentFont = False
    Transparent = False
    WordWrap = True
  end
  object btInicio: TButton
    Left = 16
    Top = 72
    Width = 75
    Height = 25
    Caption = '&Iniciar'
    TabOrder = 0
    OnClick = btInicioClick
  end
  object btPara: TButton
    Left = 96
    Top = 72
    Width = 75
    Height = 25
    Caption = '&Parar'
    TabOrder = 1
    OnClick = btParaClick
  end
  object btZera: TButton
    Left = 224
    Top = 72
    Width = 75
    Height = 25
    Caption = '&Zerar'
    TabOrder = 2
    OnClick = btZeraClick
  end
  object crono: TTimer
    Enabled = False
    Interval = 1
    OnTimer = cronoTimer
    Left = 16
    Top = 16
  end
end




O código dessa form, o arquivo.pas segue abaixo:

unit visor;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, dateutils;

type
  Tfrmvisor = class(TForm)
    btInicio: TButton;
    btPara: TButton;
    btZera: TButton;
    crono: TTimer;
    lbTempo: TLabel;
    procedure cronoTimer(Sender: TObject);
    procedure btInicioClick(Sender: TObject);
    procedure btParaClick(Sender: TObject);
    procedure btZeraClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    fTempo: Ttime;
    fMomento: integer;
  end;

var
  frmvisor: Tfrmvisor;

implementation

{$R *.dfm}

procedure Tfrmvisor.cronoTimer(Sender: TObject);
begin
  ftempo := (GetTickCount - fmomento) * OneMillisecond;
  lbTempo.Caption := formatdatetime('hh:nn:ss.zzz', fTempo);
end;

procedure Tfrmvisor.btInicioClick(Sender: TObject);
begin
  fmomento := GetTickCount;
  crono.Enabled := true;
end;

procedure Tfrmvisor.btParaClick(Sender: TObject);
begin
  crono.Enabled := false;
end;

procedure Tfrmvisor.btZeraClick(Sender: TObject);
begin
  fTempo := 0;
  lbTempo.Caption := formatdatetime('hh:nn:ss.zzz', fTempo);
end;

procedure Tfrmvisor.FormCreate(Sender: TObject);
begin
  self.DouBleBuffered := True; //evitar o flick do relógio.
end;

end.


O principio é simples: Temos uma variavel chamada momento, do tipo inteiro, que armazena este exato momento através do método GetTickCount da API do windows, que traz a quantidade de milissegundos passada desde que se iniciou a máquina, e uma variável Tempo do tipo Time que a cada milissegundo do timer armazenará o GetTickCount de agora (a cada milsec) menos o que estava armazenado em Momento tudo isso multiplicado por OneMilliSecond, que é uma constante racional que armazena o valor de um milissegundo em relação a um dia. Isso porque no Delphi e Lazarus um dia é um inteiro e um milissegundo é uma fração de 1/milissegundos_de_um_dia.

O calculo é esse (GetTickCount - fmomento) * OneMillisecond;

Iniciar captura o momento e liga o timer. Parar simplesmente para o timer. Zerar atribui 0 ao Momento.

Esse foi um post básico do Delphi/Lázarus, mas não abordei como manusear as ferramentas para criar esses arquivos. Talvez em uma futura video - aula?

Você pode baixar o Lazarus aqui: http://sourceforge.net/projects/lazarus/files/ e o fonte deste projeto aqui.

Have Fun ;)

Tradutores Online

O André Rubio fez um excelente post em seu blog sobre como  adicionar um plugin/gadget para tradução automática do seu site/blog.

O André explicou sobre os botões Convey This e Translate This. Eu vou falar sobre o google translator e o WordLingo. Você pode blogar e testar em trechos de livros em domínio público para ter uma idéia da precisão da tradução.

Porém já adianto: além das traduções serem bastante imprecisas as traduções acontecem "client-side" e não "server-side", ou seja, as traduções são feitas diretamente no seu navegador e não no servidor. Isso significa que  os robôs das ferramentas de busca (google, bing, yahoo etc) não  poderão achar sua página nas linguas traduzidas. Apenas na lingua em que você postar. Por isso, se quiser que seu blog atinja um público - alvo que fale inglês e espanhol você deve postar em inglês e espanhol, nem que seja em outro blog/cms e linque os dois depois.

Google Translator
Primeiro vamos falar do Google Translator. Como você pode ver, na parte superior esquerda desse blog o google translator já é utilizado. Para utilizar esse recurso vá em http://translate.google.com/translate_tools.
Com ele você tem as opções de selecionar as linguagens participantes, selecionar a aparência do botão e integrar com google analytics.

O código para aplicar é este abaixo:

<!-- inicio Google Translate Element -->
<div id="google_translate_element"></div><script>
function googleTranslateElementInit() {
  new google.translate.TranslateElement({
    pageLanguage: 'pt',   //parametro que indica a linguagem primaria do seu site
    multilanguagePage: true, //parametro que indica se seu site tem conteudo em outras linguagens
    gaTrack: true,    //parametro que define se a barra de tradução gerará conteudo para o analytics
    layout: google.translate.TranslateElement.InlineLayout.SIMPLE //layout do elemento
  }, 'google_translate_element');
}
</script><script src="http://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
<!-- fim Google Translate Element -->



http://www.worldlingo.com/


O worldlingo tem uma série de ferramentas profissionais, porém limitadas, de tradução. Ele tem soluções pagas onde o preço varia de acordo com o total de palavras traduzidas e o número de vezes que é traduzido. Mas a versão free para blogs e sites é bem usável. Uma vantagem é que o texto é traduzido no servidor e uma cópia do texto vem ao seu navegador, com alertas dizendo que essa é apenas uma cópia traduzida, e pode não ser muito precisa. O texto também traz figuras que marcam onde começa e onde termina a tradução. Fora dessas marcas o texto continuará em portugues. Isso faz com que apenas uma parte do texto seja traduzida por vez, e deve-se clicar na figura ao final da tradução para que a área traduzida "ande" para a frente.

Por ser uma tradução server-side podem ser feitos links, redirecionamentos e encurtamentos de url direto para a tradução.

se pegarmos uma url de documento traduzido, podemos dissecar seus parâmetros:


http://www.worldlingo.com/wl/services/S1790.5/translation?wl_srclang=PT&wl_trglang=EN&wl_rurl=http://vitorrubio.blogspot.com/&wl_url=http://vitorrubio.blogspot.com/&wl_offset=525#wl_tstart

  • wl_srclang=PT --> linguagem de origem: português
  • wl_trglang=EN --> destino: inglês
  • wl_rurl=http://vitorrubio.blogspot.com/ --> referer da tradução (de onde veio)
  • wl_url=http://vitorrubio.blogspot.com/ --> url que está sendo traduzida
  • wl_offset=525#wl_tstart --> offset (a quantas palavras do início ou do último marco, não aparece se for a primeira parte)

O código para colocar ele no seu site/blog é este:
<!-- inicio do worldlingo -->
<center><img src="http://img176.imageshack.us/img176/6091/84056383at1.png"/> </center> 
<form id="translateForm" 
action="http://www.worldlingo.com/S1790.5/translation" 
target="_blank"> 
<input value="PT" name="wl_srclang" type="hidden"/> 
<select style="font-size: 90%; width: 160px;" 
name="wl_trglang"> 
<!-- linguagens de destino -->
<option value="EN"/>Português - Inglês 
<option value="ES"/>Português - Espanhol 
</select> 
<input value="seu site aqui" name="wl_url" 
type="hidden"/> 
<input style="font-size: 90%; width: 60px;" value="Traduza" 
type="submit"/> 
</form>
<!-- inicio do worldlingo -->

E o resultado:









Como já mencionado pelo André ainda há o Translate This e o Convey This

Translate This

<!-- Begin TranslateThis Button -->

<div id="translate-this"><a href="http://translateth.is/" class="translate-this-button">Translate</a></div>

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript" src="http://x.translateth.is/translate-this.js"></script>
<script type="text/javascript">
TranslateThis();
</script>

<!-- End TranslateThis Button -->









Convey This

<!-- Begin ConveyThis Button -->
<script type="text/javascript">
 var conveythis_src = 'pt';
</script>
<div class="conveythis">
 <a class="conveythis_drop" title="Translate" href="http://www.todayter.com/"><span class="conveythis_left_image">online dating</span></a>
 <a class="conveythis_drop" title="Translate" href="http://www.conveythis.com">Translate</a><span class="conveythis_seperator"> | </span>
 <a class="conveythis_no_drop" title="English" href="http://www.translation-services-usa.com/english.php" ><span class="conveythis_image_english">English</span></a>
 <a class="conveythis_no_drop" title="Spanish" href="http://www.translation-services-usa.com/spanish.php" ><span class="conveythis_image_spanish">Spanish</span></a> 
 <a class="conveythis_no_drop" title="German" href="http://www.translation-services-usa.com/german.php" ><span class="conveythis_image_german">German</span></a> 
 <a class="conveythis_no_drop" title="Chinese" href="http://www.translation-services-usa.com/chinese.php" ><span class="conveythis_image_chinese">Chinese</span></a> 
</div>
<script type="text/javascript" src="http://no-stats10.conveythis.com/kern_e2/_v_2_3/javascript/e2_3.js"></script>
<!-- End ConveyThis Button -->

O Convey This usa a API do Google, então dá no mesmo usar o Convey This ou usar o Google Translator

Bom, é isso, espero que seja útil a todos.

Have Fun ;)

quarta-feira, 8 de setembro de 2010

Atualize seu Navegador

Amigos blogueiros, developers, escritores e interneteiros: o Firefox está na versão 3.6.9, com a versão 4 beta saindo do forno, e o Chrome está firme e forte na versão 6.

Não existe motivo para você arriscar ser invadido ou roubado por causa de um navegador desatualizado. E para você blogueiro, Web Developer ou Web Master, está na  hora de fazer sites compliantes com os padrões internacionais do W3C e tentar forçar o usuário a abandonar os navegadores velhos. Para isso apresento-lhes a barra e selo da campanha "Atualize Seu Navegador".

O código se divide entre um selinho e um script que gera uma barrinha de alerta, como mostrado a seguir:

Barrinha

<!-- barrinha de campanha atualize seu navegador -->
<script type="text/javascript" src="http://updateyourbrowser.net/asn.js"> </script>
<!-- fim barrinha --> 

Selinho

<!-- banner de campanha atualize seu navegador -->
<a href="http://updateyourbrowser.net/" title="Atualize seu Navegador"><img src="http://updateyourbrowser.net/asn.jpg" border="0" alt="Atualize seu Navegador" /></a>
<!-- fim do banner -->

O selinho ficará assim:

Atualize seu Navegador


E pode ser colocado em qualquer lugar, por exemplo num widget na sua barra lateral (sidebar) do blogger.
Já o script da barrinha necessariamente tem que ir para a tag script do modelo xml do seu template.

Para isso clique em Design --> Editar HTML e no modelo inclua o script da barrinha na tag head, logo acima do fechamento da tag /head.

O local é este.
Se tudo correr bem, ao usar um browser antigo o usuário receberá uma simpática mensagenzinha, como a da figura abaixo onde eu usei o Mozilla Firefox 1.5 (desatualizado, mas pelo menos suporta scripts).

Mensagenzinha circulada em vermelho: 
Você está usando Firefox 1.5, um navegador antigo e com falhas de segurança. Por favor atualize seu navegador. [x]

É isso. Agora se quiser pode testar meu blog mesmo em um browser antigo. Mas faça um favor ao meu google analytics: não teste muitas vezes com o (maldito) IE6.

Have fun ;)

Postagens populares

Marcadores

delphi (60) C# (31) poo (21) Lazarus (19) Site aos Pedaços (15) sql (13) Reflexões (10) .Net (9) Humor (9) javascript (9) ASp.Net (8) api (8) Básico (6) Programação (6) ms sql server (5) Web (4) banco de dados (4) HTML (3) PHP (3) Python (3) design patterns (3) jQuery (3) livros (3) metaprogramação (3) Ajax (2) Debug (2) Dicas Básicas Windows (2) Pascal (2) games (2) linguagem (2) música (2) singleton (2) tecnologia (2) Anime (1) Api do Windows (1) Assembly (1) Eventos (1) Experts (1) GNU (1) Inglês (1) JSON (1) SO (1) datas (1) developers (1) dicas (1) easter egg (1) firebird (1) interfaces (1) introspecção (1) memo (1) oracle (1) reflexão (1)