terça-feira, 28 de abril de 2015

Plug-in do Eclipse, é possível criar um ?

Sejam bem vindos ao Preciso Estudar Sempre. Hoje falaremos sobre plugins no Eclipse e como é fácil construir um. Para que você entenda o que iremos abordar aqui, é necessário que tenha algum conhecimento sobre Java, OO e Eclipse.

Na sua opinião, antes de você ler o parágrafo acima, você achava que era possível a construção de plugins para o Eclipse ?

Sim ? Não ? 

Você deve estar pensando agora: "Ainnn Joãooowww, essas empresas que fazem esses plugins, possuem super laboratórios com tecnologia super avançada, não dá pra eu fazer o meu plugin para Eclipse com o meu notebookizinho meia boca".

Então eu lhe respondo: "Você está errado meu amigo. É possível sim fazer seu próprio plugin e ainda lhe digo mais, é muito fácil".

Caso você esteja absurdamente ávido para respostas que irão mudar sua vida, clique aqui e baixe o projeto pronto.

O nosso plugin será simples, ele irá procurar por uma palavra específica em arquivos dentro de uma pasta e exibir os resultados em uma view.

Vamos por as mãos na massa ?

1 - Crie uma pasta chamada arquivos, no lugar onde você quiser. Lembre-se que será nesse lugar que o plugin irá realizar as buscas.

2 - Crie uma pasta chamada workspace. Essa pasta será o workspace de desenvolvimento do plugin.

3 - Abra o Eclipse apontando a pasta criada no passo 2 como workspace.

4 - Siga os seguinte passos: File > New > Other > Plug-in Project
Figura 1 - Criação do plugin
5 - Ponha um nome para o seu plugin e clique em Next.
Figura 2 - Nome do plugin
6 - Clique em Next.
Figura 3 - Content
7 - A tela de Templates abrirá. Essa tela é muito importante porque definiremos como nosso plugin será. O Eclipse já provê vários templates prontos para o uso. Como iremos criar simplesmente um plugin de view, clique em Plug-in with a view. Quando você clica no template, é mostrado à direita sua descrição e que extensões são usadas. Clique em Next.
Figura 4 - Templates
8 - Depois da tela de templates vem a tela de configurações principais. Essa tela é muito importante, é nela que definiremos a categoria em que nosso plugin se enquadrará, qual será o nome da classe que representará o plugin e o viewer type. O viewer type é o tipo de visualização dos dados. Como queremos que nosso plugin se pareça com o search do Eclipse, escolheremos o modelo Tree viewer. Clique em Finish.
Figura 5 - Configurações principais
Pronto !!!!!! Nosso plugin está pronto e você deve estar se sentindo assim.


Até agora não programamos nada e já criamos um plugin. Lembra que citei no início no post o quanto era fácil criar um plugin para eclipse ? Então, você vê agora na prática.

Vamos ver funcionando ?

Clique com o botão direito em cima do seu projeto e siga os seguintes passos: Clique com o botão direito > Run As > Eclipse Application.

Figura 6 - Executando o plugin
Quando você executar, vai notar que um segundo Eclipse é aberto. Sim, você leu isso mesmo. Isto não está errado pois, você está desenvolvendo parte da ferramenta então, nada mais justo que uma nova instância da ferramenta, com suas alterações, seja criada.

Após o Eclipse ter sido aberto, seu plugin será executado. Caso a execução automática não esteja funcionando, siga os seguintes passos: Window > Show View > Other > Escolha sua categoria e duplo clique no plugin.

Lembra daquela categoria que eu tinha citado acima ? Então, ela entra agora. Quando a tela de views é aberta, a separação é feita por categorias e o nome que você escolheu estará lá.

O resultado da primeira execução é exibido abaixo.
Figura 7 - Resultado da primeira execução
Você notou que o nosso plugin já veio com algum conteúdo pré-definido. Tudo na view é customizável e é isso que faremos. Crie a classe Localizador na package de views.

Classe Localizador
 package precisoestudarsempreplugin.views;  

 import java.io.BufferedReader; 
 import java.io.File; 
 import java.io.FileNotFoundException; 
 import java.io.FileReader; 
 import java.io.IOException; 
 import java.util.ArrayList; 
 import java.util.HashMap; 
 import java.util.List; 
 import java.util.Map; 

 public class Localizador { 

      public Map<String,List<String>> localizar(){ 
           Map<String,List<String>> ocorrencias = new HashMap<String, List<String>>(); 
           File folder = new File("C:\\arquivos"); 
           for(File file : folder.listFiles()){ 
                ocorrencias.put(file.getName(), this.analisarArquivo(file)); 
           } 
           return ocorrencias; 
      }

      private List<String> analisarArquivo(File file){ 
           List<String> ocorrencias = new ArrayList<String>(); 
           BufferedReader bufferedReader = null; 
           try{ 
                int lineCount=0; 
                String palavraProcurada = "Preciso Estudar Sempre"; 
                bufferedReader = new BufferedReader(new FileReader(file)); 
                while(bufferedReader.ready()){ 
                     ++lineCount; 
                     String line = bufferedReader.readLine(); 
                     if(line.toLowerCase().contains(palavraProcurada.toLowerCase())){ 
                          ocorrencias.add(lineCount + ": " + line); 
                     } 
                } 
           } catch (FileNotFoundException e) { 
                e.printStackTrace(); 
           } catch (IOException e) { 
                e.printStackTrace(); 
           } finally { 
                if(bufferedReader != null) { 
                     try { 
                          bufferedReader.close(); 
                     } catch (IOException e) { 
                          e.printStackTrace(); 
                     } 
                } 
           } 
           return ocorrencias; 
      } 
 } 

Agora, modifique o conteúdo da classe do plugin, no nosso caso é a classe MeuPlugin.

 package precisoestudarsempreplugin.views;  
 import java.util.ArrayList; 
 import java.util.List; 
 import java.util.Map; 
 import java.util.Set; 
 import org.eclipse.core.runtime.IAdaptable; 
 import org.eclipse.jface.action.Action; 
 import org.eclipse.jface.action.IMenuListener; 
 import org.eclipse.jface.action.IMenuManager; 
 import org.eclipse.jface.action.IToolBarManager; 
 import org.eclipse.jface.action.MenuManager; 
 import org.eclipse.jface.action.Separator; 
 import org.eclipse.jface.viewers.IStructuredContentProvider; 
 import org.eclipse.jface.viewers.ITreeContentProvider; 
 import org.eclipse.jface.viewers.LabelProvider; 
 import org.eclipse.jface.viewers.TreeViewer; 
 import org.eclipse.jface.viewers.Viewer; 
 import org.eclipse.jface.viewers.ViewerSorter; 
 import org.eclipse.swt.SWT; 
 import org.eclipse.swt.graphics.Image; 
 import org.eclipse.swt.widgets.Composite; 
 import org.eclipse.swt.widgets.Menu; 
 import org.eclipse.ui.IActionBars; 
 import org.eclipse.ui.ISharedImages; 
 import org.eclipse.ui.IWorkbenchActionConstants; 
 import org.eclipse.ui.PlatformUI; 
 import org.eclipse.ui.part.DrillDownAdapter; 
 import org.eclipse.ui.part.ViewPart; 

 /** 
  * This sample class demonstrates how to plug-in a new 
  * workbench view. The view shows data obtained from the 
  * model. The sample creates a dummy model on the fly, 
  * but a real implementation would connect to the model 
  * available either in this or another plug-in (e.g. the workspace). 
  * The view is connected to the model using a content provider. 
  * <p> 
  * The view uses a label provider to define how model 
  * objects should be presented in the view. Each 
  * view can present the same model objects using 
  * different labels and icons, if needed. Alternatively, 
  * a single label provider can be shared between views 
  * in order to ensure that objects of the same type are 
  * presented in the same way everywhere. 
  * <p> 
  */ 

 public class MeuPlugin extends ViewPart { 

      /** 
       * The ID of the view as specified by the extension. 
       */ 
      public static final String ID = "precisoestudarsempreplugin.views.MeuPlugin"; 
      private TreeViewer viewer; 
      private DrillDownAdapter drillDownAdapter; 
      private Action action1; 
      private Action action2; 

      /* 
       * The content provider class is responsible for 
       * providing objects to the view. It can wrap 
       * existing objects in adapters or simply return 
       * objects as-is. These objects may be sensitive 
       * to the current input of the view, or ignore 
       * it and always show the same content 
       * (like Task List, for example). 
       */ 

      class TreeObject implements IAdaptable { 

           private String name; 
           private TreeParent parent; 

           public TreeObject(String name) { 
                this.name = name; 
           } 

           public String getName() { 
                return name; 
           } 

           public void setParent(TreeParent parent) { 
                this.parent = parent; 
           } 

           public TreeParent getParent() { 
                return parent; 
           } 

           public String toString() { 
                return getName(); 
           } 

           public Object getAdapter(Class key) { 
                return null; 
           } 
      } 

      class TreeParent extends TreeObject { 

           private ArrayList children; 

           public TreeParent(String name) { 
                super(name); 
                children = new ArrayList(); 
           } 

           public void addChild(TreeObject child) { 
                children.add(child); 
                child.setParent(this); 
           } 

           public void removeChild(TreeObject child) { 
                children.remove(child); 
                child.setParent(null); 
           } 

           public TreeObject [] getChildren() { 
                return (TreeObject [])children.toArray(new TreeObject[children.size()]); 
           } 

           public boolean hasChildren() { 
                return children.size()>0; 
           } 
      } 

      class ViewContentProvider implements IStructuredContentProvider, 
                                                     ITreeContentProvider { 
           private TreeParent invisibleRoot; 

           public void inputChanged(Viewer v, Object oldInput, Object newInput) { 

           } 

           public void dispose() { 

           } 

           public Object[] getElements(Object parent) { 
                if (parent.equals(getViewSite())) { 
                     if (invisibleRoot==null) initialize(); 
                     return getChildren(invisibleRoot); 
                } 
                return getChildren(parent); 
           } 

           public Object getParent(Object child) { 
                if (child instanceof TreeObject) { 
                     return ((TreeObject)child).getParent(); 
                } 
                return null; 
           } 

           public Object [] getChildren(Object parent) { 
                if (parent instanceof TreeParent) { 
                     return ((TreeParent)parent).getChildren(); 
                } 
                return new Object[0]; 
           } 

           public boolean hasChildren(Object parent) { 
                if (parent instanceof TreeParent) 
                     return ((TreeParent)parent).hasChildren(); 
                return false; 
           } 

 /* 
  * We will set up a dummy model to initialize tree heararchy. 
  * In a real code, you will connect to a real model and 
  * expose its hierarchy. 
  */ 
           private void initialize() { 
                invisibleRoot = new TreeParent(""); 
                Localizador localizador = new Localizador(); 
                Map<String,List<String>> ocorrencias = localizador.localizar(); 
                Set<String> keys = ocorrencias.keySet(); 
                for(String key : keys){ 
                     if(!ocorrencias.get(key).isEmpty()){ 
                          TreeParent fileRoot = new TreeParent(key); 
                          for(String ocorrencia : ocorrencias.get(key)){ 
                               TreeObject nodeOcorrencia = new TreeObject(ocorrencia); 
                               fileRoot.addChild(nodeOcorrencia); 
                          } 
                          invisibleRoot.addChild(fileRoot); 
                     } 
                } 
           } 
      } 

      class ViewLabelProvider extends LabelProvider { 

           public String getText(Object obj) { 
                return obj.toString(); 
           } 

           public Image getImage(Object obj) { 
                String imageKey = ISharedImages.IMG_TOOL_FORWARD; 
                if (obj instanceof TreeParent) 
                  imageKey = ISharedImages.IMG_OBJ_FILE; 
                return PlatformUI.getWorkbench().getSharedImages().getImage(imageKey); 
           } 
      } 

      class NameSorter extends ViewerSorter { 

      } 

      /** 
       * The constructor. 
       */ 
      public MeuPlugin() { 

      } 

      /** 
       * This is a callback that will allow us 
       * to create the viewer and initialize it. 
       */ 
      public void createPartControl(Composite parent) { 
           viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); 
           drillDownAdapter = new DrillDownAdapter(viewer); 
           viewer.setContentProvider(new ViewContentProvider()); 
           viewer.setLabelProvider(new ViewLabelProvider()); 
           viewer.setSorter(new NameSorter()); 
           viewer.setInput(getViewSite()); 
           // Create the help context id for the viewer's control 
           PlatformUI.getWorkbench().getHelpSystem().setHelp(viewer.getControl(), "Teste.viewer"); 
           makeActions(); 
           hookContextMenu(); 
           contributeToActionBars(); 
      } 

      private void hookContextMenu() { 
           MenuManager menuMgr = new MenuManager("#PopupMenu"); 
           menuMgr.setRemoveAllWhenShown(true); 
           menuMgr.addMenuListener(new IMenuListener() { 
                public void menuAboutToShow(IMenuManager manager) { 
                     MeuPlugin.this.fillContextMenu(manager); 
                } 
           }); 
           Menu menu = menuMgr.createContextMenu(viewer.getControl()); 
           viewer.getControl().setMenu(menu); 
           getSite().registerContextMenu(menuMgr, viewer); 
      } 

      private void contributeToActionBars() { 
           IActionBars bars = getViewSite().getActionBars(); 
           fillLocalPullDown(bars.getMenuManager()); 
           fillLocalToolBar(bars.getToolBarManager()); 
      } 

      private void fillLocalPullDown(IMenuManager manager) { 
           manager.add(action1); 
           manager.add(new Separator()); 
           manager.add(action2); 
      } 

      private void fillContextMenu(IMenuManager manager) { 
           manager.add(action1);
           manager.add(action2); 
           manager.add(new Separator()); 
           drillDownAdapter.addNavigationActions(manager); 
           // Other plug-ins can contribute there actions here 
           manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); 
      } 

      private void fillLocalToolBar(IToolBarManager manager) { 
           manager.add(action1); 
           manager.add(action2); 
           manager.add(new Separator()); 
           drillDownAdapter.addNavigationActions(manager); 
      } 

      private void makeActions() { 
           action1 = new Action() { 
                public void run() { 
                     viewer.expandAll(); 
                } 
           }; 
           action1.setText("Expand All"); 
           action1.setToolTipText("Expand All"); 
           action1.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages(). 
                getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK)); 
           action2 = new Action() { 
                public void run() { 
                     viewer.collapseAll(); 
                } 
           }; 
           action2.setText("Collapse All"); 
           action2.setToolTipText("Collapse All"); 
           action2.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages(). 
                     getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK)); 
      } 

      /** 
       * Passing the focus request to the viewer's control. 
       */ 
      public void setFocus() { 
           viewer.getControl().setFocus(); 
      } 
 } 

Agora vamos entender o que está escrito.
  • No método initialize é onde toda a magia acontece, é nele aonde são criados os nós da árvore (lembra o tipo de visualização que escolhemos ?). Cada nó possui o conteúdo que queremos exibir.
  • As classes TreeParent TreeObject são innerclasses, se você não sabe o que é isso, clique aqui e dê uma olhada.
  • No método getImage definimos os ícones para as raízes e folhas da árvore. Os ícones usados no nosso plugin são os mesmos usados no Eclipse. Os ícones são representados através de constantes da classe ISharedImages.
  • Nos métodos fillLocalPullDownfillContextMenu é definido a disposição dos botões do plugin. A diferença entre esses métodos é que o primeiro se refere a seta ao lado do minimizar e o segundo aos botões da barra.
Figura 8 - Nossos botões
  • No método makeActions são definidos os tooltips, nomes, as ações e ícones dos botões. No nosso caso, os botões irão expandir e colapsar todos os resultados.
DESAFIO

Nesse post deixo dois desafios e quem conseguir resolver, bote as soluções nos comentários pois, eu não consegui. O primeiro desafio é por o íconeem um dos botões. O segundo desafio é por o highlight nas palavras encontradas.

Sugestões ?! Críticas ?! Elogios ?! 

Deixe aí nos comentários ou na nossa página do facebook.


Referências:
Tutorial - Como criar um plug-in para o Eclipse: https://www.youtube.com/watch?v=4HubyNCIHg4
Nested Classes: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Leia Mais ››

terça-feira, 14 de abril de 2015

Captcha em PHP

Olá amigos do Preciso Estudar Sempre. Hoje, iremos falar sobre captcha mas, você sabe o que é uma captcha ?

Captcha (Completely Automated Public Turing test to tell Computers and Humans Apart) é um teste de desafio cognitivo para diferenciar homens de máquinas. Isto é usado no combate a bots de sistemas. O teste consiste em uma imagem com letras e números distorcidas em que, o usuário do sistema deve dar seu melhor palpite sobre a imagem. Existem algumas implementações de captcha que disponibilizam a opção de emissão de sons para que, o usuário escreva o que ouviu ou até, a escolha de imagens de acordo com um padrão.

Para que você possa ter seu próprio captcha é bem fácil. O google disponibiliza uma API a qual, é muito simples de usar e o resultado final fica bastante legal.

Se você me perguntasse em qual linguagem eu prefiro implementar o captcha, eu te responderia que prefiro no PHP, sem sobras de dúvidas. Essa escolha acontece pelo fato que é muito mais fácil do que fazer com Java e o PHP possui suporte nativo a JSON o que, torna-o muito atrativo para tal tarefa.

captcha.html

 <!DOCTYPE html>  

 <html> 
  <head> 
   <title>Google recapcha demo - Preciso Estudar Semprelt;/title> 
   <script src='https://www.google.com/recaptcha/api.js'></script> 
  </head> 
  <body> 
   <h1>Google reCAPTHA Demo</h1> 
   <form id="comment_form" action="" method="post"> 
    <input type="email" placeholder="Type your email" size="40"><br><br> 
    <textarea name="comment" rows="8" cols="39"></textarea><br><br> 
    <input type="submit" name="submit" value="Post comment"><br><br> 
    <div class="g-recaptcha" data-sitekey="sua-chave-aqui"></div> 
   </form> 
  </body> 
 </html> 

Você deve ter notado algumas coisas no código. A importação da biblioteca api.js é necessária visto que, ela é a responsável pela criação da captcha e de todos os seus controles. Note também que eu criei uma div com o atributo class preenchido com o valor g-recaptcha. Isto também é necessário pois, é com o atributo class que a API do google irá localizar a div para inserir a captcha. Por último, você também notou o atributo data-sitekey na div. Esse atributo segue a especificação do HTML 5 de criação dinâmica de atributos, ou seja, eu posso criar qualquer atributo em uma tag de forma dinâmica simplesmente iniciando seu nome com a palavra "data-". A partir daí, eu posso trabalhar como se fosse um atributo comum.

Neste atributo (data-sitekey) é definido a chave pública para o captcha.

Chaaave pública ????

Sim, chave pública. A API do google funciona dessa forma. Você cadastra seu domínio no google developer e recebe uma chave pública e outra secreta. Essas chaves são usadas para você fazer a checagem se o que o usuário digitou condiz com a imagem.

Para cadastrar seu domínio e conseguir suas chaves, clique aqui.

Com suas chaves em mão, preencha o atributo data-sitekey com sua chave pública. É possível também gerar o captcha via codificação javascript mas, não utilizaremos essa abordagem. Caso você queira mais exemplos, clique aqui para ver a documentação da Google. Via codificação é possível customizar certos elementos da captcha como: definir função de callback após o preenchimento, definir função caso a captcha expire, realizar internacionalização, mudanças de temas, definição de tipo da captcha (audio ou vídeo) e etc.

Vamos realizar agora a implementação server-side que precisamos. Caso você não tenha nenhuma experiência com PHP, dê uma olhada nesse post em que realizamos uma iniciação no assunto.

Crie o arquivo Form.php.

 <?php   

      $email; 
      $comment; 
      $captcha; 

      if(isset($_POST['email'])){ 
       $email=$_POST['email']; 
      }if(isset($_POST['comment'])){ 
       $email=$_POST['comment']; 
      }if(isset($_POST['g-recaptcha-response'])){ 
       $captcha=$_POST['g-recaptcha-response']; 
      } 

      if(!$captcha){ 
       echo '<h2>Please check the the captcha form.</h2>'; 
       exit; 
      } 

      $response=json_decode(file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=sua_chave_secreta_aqui&response=".$captcha."&remoteip=".$_SERVER['REMOTE_ADDR']));      
      if($response->success==false){ 
       echo '<h2>Você é um spammer ! Zarpa daqui !!!</h2>'; 
      } else { 
       echo '<h2>Obrigado pelo comentário.</h2>'; 
      }  
 ?> 

Algumas observações sobre o código acima:
  • O parâmetro g-recaptcha-response representa o que foi respondido pelo usuário.
  • Realizamos um acesso à url https://www.google.com/recaptcha/api/siteverify para verificar se o que foi escrito pelo usuário condiz com o apresentado. Passamos como parâmetros a nossa chave secreta, o que foi respondido pelo usuário e o nosso ip. O nosso ip tem de ser enviado para realizar a validação se o acesso está sendo feito via localhost ou pelo domínio. Acessos via localhost são permitidos afim de testes.
  • A função json_decode é utilizada porque o que é retornado pelo acesso à url é um objeto JSON. Este objeto possui dois atributos: success e error-codes. Para mais informações, clique aqui.
Caso o atributo retorne true então a verificação obteve sucesso e o usuário deu o palpite certo sobre o captcha mas, se retornar false pode ser que o usuário tenha errado o palpite ou, seja um spammer.

IMPORTANTE: Testes feitos em ambiente de localhost sempre irão resultar em sucesso pois, não existe como você ser um spammer.

Pronto, nossa implementação de PHP + Captcha está pronta. Agora, é só usar !!

Para baixar o projeto pronto, clique aqui.

Sugestões ?! Críticas ?! Elogios ?! 

Deixe aí nos comentários ou na nossa página do facebook.


Referências:
CAPTCHA - http://pt.wikipedia.org/wiki/CAPTCHA
Google Captcha - https://www.google.com/recaptcha/intro/index.html
Um primeiro contato: PHP - http://precisoestudarsempre.blogspot.com.br/2015/03/um-primeiro-contato-php.html
Leia Mais ››