DevSecOps

26 abr, 2007

Inserção e Deleção de pastas no servidor com classes e componentes personalizados

Publicidade

Vamos estudar como manipular pastas no servidor para administração de conteúdo. O desenvolvimento será todo com classes em ActionScript 2.0, e muito provavelmente será meu último artigo em AS2.

Também explicarei basicamente como usar classes nesta versão.

Pré-Requisitos

Este artigo foi desenvolvido para usuários intermediários do Flash, que tem a intenção de migrar para o desenvolvimento com classes e componentes visuais, além de entender o funcionamento da conversa da ActionScript com linguagens de servidor. É necessário que se conheça um pouco da linguagem e sintaxe da ActionScript, e também PHP.

Nível Intermediário.

Arquivo Final

Objetivo

Ingressar no desenvolvimento de classes na ActionScript 2.0 e aperfeiçoar o conhecimento RIA.

Conteúdo

No Flash 8, abra um novo documento em branco com as seguintes configurações:

Primeiro passo: Vamos inserir os objetos visuais no palco, e instancia-los como segue abaixo:

Detalhes:

   folders_txt - Dynamic Text
   nameFolder_txt - Input Text
   folderToClear_txt - Input Text
   msg_txt - MovieClip, internamente possui um texto Dynamic Text com o nome de instância msg_txt também.
   send_btn - MovieClip, internamente possui um texto Dynamic Text com o nome de instância label_txt e um MovieClip bg_mc
   delete_btn - MovieClip, uma cópia da instância acima, apenas o nome de instancia é diferente.

Selecione todos os objetos e converta num único MovieClip de nome folderManager.

Dica: O nome dos símbolos (nomes que aparecem na Library) devem respeitar a convenção de nomenclatura, onde resumindo, todo caractere inicial deve ser uma letra minúscula, um “_” ou um “#8221;, caracteres seguintes podem ser numericos, palavras seguintes devem começar com caixa alta, exemplo: adobeFlash8.

A Library deve ficar assim, com exceção aos tipos de objetos que ainda são MovieClip ao invés de Component para vocês:

Entre na área de edição do movieClip folderManager, selecione todos os objetos e clique com o botão direito sobre um deles, peça a opção Distribute to Layers:

Reparem que os objetos estão agora em Layers individuais com seus devidos nomes:

Visualmente finalizamos aqui por hora.

Agora vamos ao desenvolvimento das classes que manipularão nossos objetos visuais.

Peça um arquivo novo, do tipo ActionScript File.

Antes de iniciar a programação, vamos a um básico help de palavras chaves:

class

Principal palavra chave na construção de uma classe, ela armazena toda a construção do objeto e indica o nome da classe em questão. O nome da classe também segue as mesmas convenções de nomenclatura, porém, dessa vez, o caractere inicial deve ser maiúsulo, exemplo:

class TutorialFlash {
}

A classe também pode (e deve) indicar qual o pacote em que se encontra. Por exemplo, podemos separar as classes em pastas, pensando em categorias. Em uma pasta chamada layout, armazenamos os objetos visuais de layout, em uma pasta chamada data, armazenamos elementos de dados, em uma pasta chamada effects guardamos classes de efeitos visuais… exemplo:

class layout.Botao {
}

class data.XMLToArray {
}

Quando possuimos pastas dentro de pastas, separamos essas com “.”, assim como fazemos com o nome da Classe, imaginem que possuimos uma classe XMLToArray dentro de uma pasta data, que por sua vez está na pasta tutorial:

class tutorial.data.XMLToArray {
}

Ainda nesta linha podemos indicar a herança desta classe. A herança nos permite usar propriedades e métodos da classe pai (super) sem ter a necessidade de reprogamá-las. Classes são objetos, e devemos pensar como se fossem objetos de um mundo real, os homens e mulheres não são iguais, porém os dois tem muito em comum, logo eles poderiam herdar uma classe chamada humanos, que por sua vez herdariam uma classe chamada mamíferos, e uma outra chamada seresVivos.. etc. Pensando assim, já teriamos diversas classes individuais (baleia, gato, cachorro, etc), com muita reutilização de código e muito menos trabalho futuro. Exemplo:

class estudos.Exemplo extends MovieClip{
}

A palavra extends é usada para aplicar a herança.

A classe também possui um método construtor, que é a principal função da classe. Ela deve possuir o mesmo nome da classe. Quando não a inserimos o Flash faz isso automaticamente:

class estudos.Exemplo extends MovieClip{
 	function Exemplo(){
   }
}
private

Diferentemente do código anexado ao frame ou objetos, todas as variáveis devem ser declaradas e tipificadas. A palavra chave private indica que a variavel ou funcão pode ser acessada somente pela própria classe ou por subclasses. Isso faz com que em um grande projeto, usuários de sua classe não alterem propriedades e/ou métodos indevidos:

 class estudos.Exemplo extends MovieClip{
 private var teste:String;
 function Exemplo(){
 teste = "Testando nossa classe!";
 trace(teste);
 }
 private function interno(){
 }
 }
public

A palavra public indica que a variável ou função (propriedade e função são os nomes usados quando estão dentro de uma classe) podem ser acessados por qualquer objeto.

class estudos.Exemplo extends MovieClip{
 public var teste:String;
 public function Exemplo(){
 teste = "Testando nossa classe!";
 trace(teste);
 }
 }

 //////////
 

class Alpha {
private var privateProperty = "visible only within class and subclasses";
public var publicProperty = "visible everywhere";
}  

class Beta extends Alpha {
function Beta() {
trace("privateProperty is " + privateProperty);
}
}

var myBeta:Beta = new Beta(); // Output: privateProperty is visible only within class and subclasses 
trace(myBeta.privateProperty); // Error
import

Utilizado para anexar classes de outros pacotes:

import mx.utils.Delegate;

class Teste extends MovieClip {
private var button_mc:MovieClip;

public function Teste() {
button_mc.onRelease = Delegate.create(this, showMessage);
}
private function showMessage(Void):Void {
trace("message");
}
}

A classe Delegate é utilizada para manter o escopo do local onde é chamada. Apenas para teste, isso pode ser executado a partir de um frame:

Crie um movieClip no stage com o nome button_mc;

import mx.utils.Delegate;
var button_mc:MovieClip;
function show(str:String):String {
	if (arguments.length) {
		trace(str);
	} else {
		str = "Sem parametros";
		trace(str);
	}
	return str;
}
button_mc.onRelease = function(Void):Void  {
	exec = Delegate.create(this, show);
	exec("msg");
};
button_mc.onPress = Delegate.create(this, show);

Por hoje a teoria acaba aqui, vamos a primeira classe, a classe SimpleButton, nossos botões de gravar e apagar pastas no servidor:

Não se esqueça de olhar onde a classe se encontra, para que esteja salva na pasta correta teremos 3 classes no total.

//CLASSE SIMPLE_BUTTON
//Pacote
class com.tutorial.leandroamano.imasters.layout.SimpleButton extends MovieClip {
	//Propriedades
	private var label_txt:TextField;
	private var bg_mc:MovieClip;
	//Constantes, propriedades que nunca trocam de valor, devem ser nomeadas em caixa alta, separadas por "_" quando compostas
	private var DIFFERENCE:Number = 10;
	//Indica parametro configurável a partir da barra de propriedades em parameters
	[Inspectable (name="label")]
	 public var _label:String;
	 //Contrutor
	public function SimpleButton() {
		this.onLoad = function(Void):Void  {
			this.init();
		};
	}
	//Métodos
	private function init(Void):Void {
		this.label_txt.text = this._label;
		this.label_txt.autoSize = true;
		this.bg_mc._width = this.label_txt._width+this.DIFFERENCE;
	}
	
	private function onRelease(Void):Void {
		this.onClick();
	}
	//Getters e Setters
	public function set label(_label:String):Void {
		this._label = _label;
		this.init();
	}
	
	public function get label():String {
		return this._label;
	}
	//Eventos
	public function onClick() {
	}
}

Classe Alert que retorna os erros no campo de texto:

//CLASSE ALERT
//Pacote
class com.tutorial.leandroamano.imasters.messages.Alert {
	//Propriedades
	private var msg_txt:TextField;
	private var intervalID:Number;
	//Construtor
	public function Alert() {
	}
	//Métodos
	public function displayMessage(msg:String, timeToDie:Number):Void {
		if (!arguments.length) {
			throw new Error("Número de argumentos incorreto");
		}
		try {
			if (arguments.length == 1) {
				timeToDie = 5;
			}
			clearInterval(this.intervalID);
			var thisObj:Alert = this;
			thisObj.msg_txt.text = msg;
			this.intervalID = setInterval(function (Void):Void {
				thisObj.msg_txt.text = "";
				clearInterval(thisObj.intervalID);
			}, timeToDie*1000);
		} catch (error:Error) {
			trace(error.message);
		}
	}
}
//CLASSE MANAGER
//Importando classes a serem utilizadas
   import mx.utils.Delegate;
   import com.tutorial.leandroamano.imasters.layout.SimpleButton;
   import com.tutorial.leandroamano.imasters.messages.Alert;
   //Pacote
   class com.tutorial.leandroamano.imasters.folder.Manager extends MovieClip {
  //Propriedades
   private var folders_txt:TextField;
   private var nameFolder_txt:TextField;
   private var folderToClear_txt:TextField;
   private var msg_txt;
   private var send_btn;
   private var delete_btn;
   //Principal variável neste tutorial em questão, ela é quem enviará e retornará os dados do servidor, utilizei aqui por uma questão de viabilidade do tutorial, mas também podemos fazer isso em qualquer outra linguagem, a unica questão é enviar as variáveis corretas para a linguagem de servidor
   private var phpManager:LoadVars;
   private var onEnterFrameBeacon:MovieClip;
   private var nextDepth:Number;
   private var begin:Number;
   private var end:Number;
   //Construtor
   public function Manager() {
   Selection.setFocus(nameFolder_txt);
   Mouse.addListener(this);
   
   nameFolder_txt.maxChars = 20;
   nameFolder_txt.restrict = "a-z 1-9";
   
   folderToClear_txt.maxChars = 20;
   folderToClear_txt.restrict = "a-z 1-9";
   
   nameFolder_txt.tabIndex = 1;
   send_btn.tabIndex = 2;
   folderToClear_txt.tabIndex = 3;
   delete_btn.tabIndex = 4;
   
   nextDepth = this.getNextHighestDepth();
   onEnterFrameBeacon = this.createEmptyMovieClip("temp_mc", nextDepth);
   //Chama a função para inserir os dados
   send_btn.onClick = Delegate.create(this, insert);
   //Chama a função para apagar os dados
   delete_btn.onClick = Delegate.create(this, clear);
   
   folderToClear_txt.onSetFocus = function (Void):Void {
   this.text = "";
   }

 this.show();
   }
   //Métodos
   private function show(Void):Void {
   phpManager = new LoadVars();
  [c]//Lemos o arquivo do servidor (download no final da página) para que assim retorne as pastas já existentes
   phpManager.load("http://localhost/leandro_amano/flash8/folder/show_folder.php");[/c]
   //Ao ler, faça
   phpManager.onLoad = Delegate.create(this, onShow);
   }
   
   private function onShow(success:Boolean):Void {
   if(success){
   var tempText = unescape(String(this.phpManager));
   var stringToClear = tempText.indexOf("=&onLoad");
   var folders = tempText.substr(0, stringToClear);
   if (folders.indexOf("onLoad") == 0) {
   folders_txt.htmlText = "";
   return;
   }
   folders_txt.htmlText = folders;
   } else {
   folders_txt.htmlText = "Erro na tentativa de leitura..."
   }
   }
   
   private function insert(Void):Void {
   //Verificamos se existe algo digitado para que a pasta possua um nome
   if (!nameFolder_txt.length) {
   msg_txt.displayMessage("Digite o nome da pasta a ser criada!", 5);
   Selection.setFocus(nameFolder_txt);
   } else {
   msg_txt.displayMessage("");
   phpManager = new LoadVars();
   //Passamos a variavel folder, para que seja lido no php
   phpManager.folder = nameFolder_txt.text;
   phpManager.sendAndLoad("http://localhost/leandro_amano/flash8/folder/insert_folder.php",
   phpManager, "POST");
   phpManager.onData = Delegate.create(this, onInsert);
   }
   }
   
   private function onInsert(msg:String):Void {
  //Ao inserir ele atualiza a exibição das pastas atuais no servidor
   this.show();
  //Mensagens de acordo com o evento ocorrido
   if (msg.indexOf("criada") == 0) {
   msg_txt.displayMessage("Pasta criada com sucesso!", 5);
   nameFolder_txt.text = "";
   } else if(msg == undefined) {
   msg_txt.displayMessage("Erro na tentativa de inserção!", 5);
   } else {
   msg_txt.displayMessage("Pasta já existe!", 5);
   }
   }
   
   private function clear(Void):Void {
   phpManager = new LoadVars();
   //Indica ao php qual pasta deve ser apagada (o php de exemplo exclui qualquer coisa que esteja na pasta num loop varrendo todas subpastas)
   phpManager.folder = folderToClear_txt.text;
   phpManager.sendAndLoad("http://localhost/leandro_amano/flash8/folder/delete_folder.php", 
   phpManager, "POST");
   phpManager.onData = Delegate.create(this, show);
   }
   //Daqui até o fim criei algumas funções que exibem no campo de deleção o que foi selecionado no campo que exibe todas as pastas, lembrando que o php exclui uma pasta principal por vez
   private function detectSelection(Void):Void {
   folderToClear_txt.text = "";
   onEnterFrameBeacon.onEnterFrame = Delegate.create(this, _onEnterFrame);
   }
   
   private function _onEnterFrame(Void):Void {
   begin = Selection.getBeginIndex();
   end = Selection.getEndIndex();
   folderToClear_txt.text = folders_txt.text.substring(begin, end);
   }
   
   private function killEnterFrame(Void):Void {
   delete onEnterFrameBeacon.onEnterFrame;
   }
   //Eventos
   private function onMouseMove(Void):Void {
   folders_txt.onSetFocus = Delegate.create(this, detectSelection);
   folders_txt.onKillFocus = Delegate.create(this, killEnterFrame);
   }
   }

Agora que as classes estão criadas, voltemos ao layout do Flash para converter os objetos MovieClip em Component: Abra a Library, clique com o botão direito sobre o item alert e selecione Linkage.

Mantenha o identifier como está, e em AS 2.0 class insira:

com.tutorial.leandroamano.imasters.messages.Alert

Confirme em OK.

Novamente va até a Library, selecione o mesmo item com o botão direito e selecione Component Definition, No campo AS 2.0 class insira:

com.tutorial.leandroamano.imasters.messages.Alert

Ao pressionar OK, o icone na biblioteca que era MovieClip será convertido em Component. Caso haja algum erro de código, o painel Output exibirá os erros. Caso isso aconteça, reveja o código e tente novamente.

Todos os passos acima devem ser feitos também com os MovieClips SimpleButton e FolderManager.

Ao finalizar, os PHPs são:

//show_folder.php
<?php
foreach (glob("*",GLOB_ONLYDIR) as $dir) {
  echo "$dir<br>";
} 
?>

//insert_folder.php
<?php
$filename = '$folder';
if (!file_exists($filename)) {
    $folder = $_POST["folder"];
    mkdir("$folder", 0777, TRUE);
	echo "criada";
} else {
    echo "existe";
}
?>

//delete_folder.php
<?php
function deleteDirectory($dirname,$only_empty=false) {
    if (!is_dir($dirname))
        return false;
    $dscan = array(realpath($dirname));
    $darr = array();
    while (!empty($dscan)) {
        $dcur = array_pop($dscan);
        $darr[] = $dcur;
        if ($d=opendir($dcur)) {
            while ($f=readdir($d)) {
                if ($f=='.' || $f=='..')
                    continue;
                $f=$dcur.'/'.$f;
                if (is_dir($f))
                    $dscan[] = $f;
                else
                    unlink($f);
            }
            closedir($d);
        }
    }
    $i_until = ($only_empty)? 1 : 0;
    for ($i=count($darr)-1; $i>=$i_until; $i--) {
        echo "\nDeleting '".$darr[$i]."' ... ";
        if (rmdir($darr[$i]))
            echo "ok";
        else
            echo "FAIL";
    }
    return (($only_empty)? (count(scandir)<=2) : (!is_dir($dirname)));
}
$folder = utf8_decode(@$_POST['folder']);
deleteDirectory($folder,false)
?>

Podem testar diretamente no Flash, lembrando que, por segurança, o funcionamento da comunicação com o servidor funciona dentro do Flash e no próprio servidor.

Abraços e até o CS3!

Download dos arquivos