cakephp user authentication for adobe air app - apache-flex

I have a web application developed using flex and cakephp. My client need to make a desktop application of that web application using Adobe Air. The conversion of the flex to Air is done successfully. I the flex application the communication of flex and cakephp is handled using a remotes controller.
In air application I have a problem of authenticating the user with cakephp default user authentication. Can anyone help me to find a solution for this?

i suggest you to send your user credentials via POST to your cakephp backend.
A login function in your UsersController would look something like this:
public function login() {
if ($this->Auth->login()) {
$this->serviceResponse(Status::SUCCESS);
} else {
$this->serviceResponse(Status::AUTH_FAILED);
}
}
// this is just an example out of my appcontroller to send json responses
public function serviceResponse($code, $data = array()) {
$response = compact('code', 'data');
$this->response->body(json_encode($response));
$this->response->send();
$this->shutdownProcess();
exit;
}
// I also defined a class for return statuses
class Status {
const SUCCESS = 'success';
const ERROR = 'error';
...
}

Basically, you want to send the validation request as an ajax request. To do that you need to modify headers, capture session ids through cookies and post them to keep the session alive. It expects a JSON object returned from Cake.
I've created a couple Flex classes that achieve just this for a Flex Mobile App and a CakePHP backend. It should work the same for your needs.
It's in two files, the AutoValidationUrlRequest.as extends the HeaderURLRequest.as file. I'm not positive but there may be a couple variables to change, but overall it works very well and probably won't take more than a couple changes to get it working on your app.
To use it, simply create a new AutoValidationUrlRequest object and add an event listener on the headerUrlRequestComplete Event and then run AutoValidationUrlRequest.send(...) to POST. there is also a handy method called convertToPostVars for easy Cake Friendly Post variables.
AutoValidationUrlRequest.as:
package libs
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.HTTPStatusEvent;
import models.LocalDeviceData;
import models.Model;
import mx.collections.ArrayCollection;
import mx.core.FlexGlobals;
import mx.utils.ObjectUtil;
[Event("LoginFailed")]
[Event("MobileUserDoesNotExist")]
public class AutoValidationURLRequest extends HeaderURLRequest
{
public static const LOGIN_FAILED:String = "LoginFailed";
public static const MOBILE_USER_DOES_NOT_EXIST:String = "MobileUserDoesNotExist";
/**
* will automatically be set by this class, if not set will login
*/
public var requestHeaders:Object = new Object();
private var _cookie:Object;
/**
* should be an object with name of the cookie variables parsed in as key/value pairs
*/
protected function set cookie(ck:Object):void{
_cookie = ck;
}
protected function get cookie():Object{
return _cookie;
}
public function AutoValidationURLRequest(){
};
public function send(url:String, postData:Object, generateUrlVars:Boolean = true):void{
if(generateUrlVars){
postData = convertToPostVars(postData);
}
sendRequest("http://yourwebsite.com"+url, postData, requestHeaders);
}
override protected function parseHeaders(e:HTTPStatusEvent):void{
super.parseHeaders(e);
if('Set-Cookie' in headers){
requestHeaders['Cookie'] = parseCookie(headers['Set-Cookie']);
//requestHeaders['User-Agent'] = headers['User-Agent'];
}
}
/**
* returns the cookie key/val string for send requests back to the server
*/
protected function parseCookie(cookieString:String):String{
var cookieObj:Object = new Object();
var cookieBits:Array = cookieString.split("; ");
return cookieBits[0];
/*
for each(var ck:String in cookieBits){
var cb:Array = ck.split("=");
cookieObj[cb[0]] = cb[1];
}
return cookieObj;
*/
}
}
}
HeaderURLRequest.as:
package libs
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.HTTPStatusEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
import mx.core.FlexGlobals;
import mx.rpc.events.ResultEvent;
import mx.utils.ObjectUtil;
[Event("headerUrlRequestComplete")]
public class HeaderURLRequest extends EventDispatcher
{
public static const HEADERURLREQUEST_COMPLETE:String = "headerUrlRequestComplete";
public var headers:Array = [];
public var data:Object = new Object();
public var variables:Object = new Object();
public var invalidFields:Object = new Object();
public var errorMsg:String = "";
/**
* the headers array must contain an object with a 'name' key and a 'value' key eg: cookie: <cookieStr>
*/
public function HeaderURLRequest():void{
}
public function sendRequest(url:String, postData:Object = null, requestHeaders:Object = null):void{
var urlLoader:URLLoader = new URLLoader()
var urlRequest : URLRequest = new URLRequest(url);
//make it an ajax request
urlRequest.requestHeaders.push(new URLRequestHeader('X-Requested-With', 'XMLHttpRequest'));
for(var header:* in requestHeaders){
var authHeader:URLRequestHeader = new URLRequestHeader(header as String, requestHeaders[header]);
urlRequest.requestHeaders.push(authHeader)
}
var urlVariables:URLVariables = new URLVariables();
for (var vars:* in postData){
urlVariables[vars] = postData[vars];
}
urlRequest.method = URLRequestMethod.POST
urlRequest.data = urlVariables;
urlLoader.addEventListener(Event.COMPLETE, getData);
urlLoader.addEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, parseHeaders);
urlLoader.load(urlRequest);
}
public function convertToPostVars(postData:Object, prependKeyName:String = ""):Object{
var params:Object = {};
if(prependKeyName == ""){
params['_method'] = 'POST';
}
for (var item:* in postData){
var objtype:Object = ObjectUtil.getClassInfo(postData[item]);
if(objtype.name == "Object"){
var modelKeyName:String = prependKeyName+"["+item+"]";
var subParams:Object = convertToPostVars(postData[item],modelKeyName);
params = merge(params, subParams);
}else{
params["data"+prependKeyName+"["+item+"]"] = postData[item];
}
}
return params;
}
public function flashErrorMsg():String{
var err:String = errorMsg;
errorMsg = "";
return err;
}
protected function parseHeaders(e:HTTPStatusEvent):void{
var i:Number=0;
headers = [];
for each(var header:URLRequestHeader in e.responseHeaders){
headers[header.name] = header.value;
i++;
}
}
protected function getData(e:Event):void{
//trace('data: ');
if(e.currentTarget.data == ''){
e.currentTarget.data = '{}';
}
data = JSON.parse(e.currentTarget.data);
//trace(ObjectUtil.toString(data));
if(data.hasOwnProperty('variables')){
variables = data.variables;
if (variables != null){
if(variables.hasOwnProperty('invalidFields')){
invalidFields = variables.invalidFields;
for (var error:String in invalidFields){
errorMsg += invalidFields[error] + "\n\n";
}
}
}else{
//no variable data found!!
}
}
dispatchEvent(new Event(HEADERURLREQUEST_COMPLETE));
}
public function isEmpty(obj:Object){
var isEmpty:Boolean = true;
for (var n in obj) { isEmpty = false; break; }
return isEmpty;
}
public function merge( obj0:Object, obj1:Object ):Object
{
var obj:Object = { };
for( var p:String in obj0 )
{
obj[ p ] = ( obj1[ p ] != null ) ? obj1[ p ] : obj0[ p ];
//trace( p, ' : obj0', obj0[ p ], 'obj1', obj1[ p ], '-> new value = ', obj[ p ] );
}
for (var p1:String in obj1){
if(!obj.hasOwnProperty(p1)){
obj[ p1 ] = obj1[ p1 ] ;
}
}
return obj;
}
}
}
Also, using this with my forked version of Jose Gonzalez's CakePHP ajax_controller plugin is really handy. It basically takes any Ajax Request and converts all view variables and outputs them into a JSON object, no rendered view. Otherwise, if you're not using an ajax request, Cake will render the views normally. Good Luck!

Related

dispatch event not fired in action script 2.0

I have created a class named CheckBoxSelectAll in which I am triggering an event as below.
import mx.events.EventDispatcher;
import flash.filters.GlowFilter;
class CheckBoxSelectAll
{
public function dispatchEvent() {};
public function addEventListener() {};
public function removeEventListener() {};
private var checkbox_mc:MovieClip;
private var parent_mc:MovieClip;
function CheckBoxSelectAll()
{
mx.events.EventDispatcher.initialize(this);
}
function CreateCheckBox(c_mc:MovieClip)
{
var labelGlow:GlowFilter = new GlowFilter(0xFFFFFF, .30, 4, 4, 3, 3);
var labelFilters:Array = [labelGlow];
this.parent_mc = c_mc;
checkbox_mc = parent_mc.createEmptyMovieClip("",this.checkbox_mc.getNextHighestDepth() );
checkbox_mc._x =450;// boxX;
checkbox_mc._y =143;// boxY;
checkbox_mc.lineStyle(1, 0);
checkbox_mc.beginFill(currentFill, currentAlpha);
checkbox_mc.moveTo(0, triSize);
checkbox_mc.lineTo(triSize, triSize);
checkbox_mc.lineTo(triSize, 0);
checkbox_mc.lineTo(0, 0);
checkbox_mc.lineTo(0, triSize);
checkbox_mc.endFill();
checkbox_mc._visible = true;
checkbox_mc.onPress = function() {
var eventObject:Object = {target:this, type:'onDataReady'};
dispatchEvent(eventObject);
trace("OnPress refresh...");
}
}
}
In Parent movie clip, used following code
var select_all_listener:Object = new Object();
select_all_listener.onDataReady = triggerDisksLoad;
var select_all_box:CheckBoxSelectAll;
select_all_box = new CheckBoxSelectAll();
select_all_box.addEventListener("onDataReady", select_all_listener);
select_all_box.CreateCheckBox(this);
function triggerDisksLoad(evtObj) { trace("triggerDisksLoad called...!!!"); }
Here function triggerDisksLoad is not called.
The problem of your code is the scope where the checkbox_mc.onPress handler is executed, to avoid that, you can use the Delegate class, like this :
import mx.events.EventDispatcher;
import mx.utils.Delegate;
class CheckBoxSelectAll extends MovieClip
{
// ...
function CreateCheckBox(c_mc:MovieClip)
{
// ...
checkbox_mc.onPress = Delegate.create(this, _onPress);
}
private function _onPress():Void {
var event:Object = {target: this, type: 'onDataReady'};
dispatchEvent(event);
}
}
Also for the new MovieClips creation, when we use getNextHighestDepth(), it's usually used with the parent of the new MovieClip, so you can write :
checkbox_mc = parent_mc.createEmptyMovieClip('mc_name', parent_mc.getNextHighestDepth());
Hope that can help.

How to executes a .bat file on local machine from adobe air application?

I m using flex4.6 and i wanted to run a .bat file from ma air application, is there any help ?
You can use NativeProcess class.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/desktop/NativeProcess.html
Try this
This class might helpful for you my_command.bat echo "Hello Native Process"
Before run applications (Projectname-app.xml) check supportedProfiles tag
desktop extendedDesktop mobileDevice exendedMobileDevice
to
extendedDesktop
package
{
import flash.desktop.NativeProcess;
import flash.desktop.NativeProcessStartupInfo;
import flash.events.ProgressEvent;
import flash.filesystem.File;
import mx.controls.Alert;
public class PlatformPackaging
{
private var nativeProcess:NativeProcess;
private var isErrorOccured:Boolean;
private var batFile:File;
private var cmdFile:File;
private function get isWindows():Boolean
{
return true;
}
public function PlatformPackaging()
{
}
public function start(targetPlatform:String):void
{
if(isWindows)
{
batFile = File.applicationDirectory.resolvePath("publish/my_command.bat");
cmdFile = new File("c:\\Windows\\System32\\cmd.exe");
}
proceedToPackaging();
}
private function proceedToPackaging():void {
var processArgs:Vector.<String> = new Vector.<String>;
processArgs.push("/c");
processArgs.push(batFile.nativePath);
var workingDirectory:File = File.applicationDirectory.resolvePath("publish/");
var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
nativeProcessStartupInfo.arguments = processArgs;
nativeProcessStartupInfo.executable = cmdFile;
nativeProcessStartupInfo.workingDirectory = workingDirectory;
nativeProcess = new NativeProcess();
nativeProcess.addEventListener(ProgressEvent.STANDARD_ERROR_DATA,onStandardErrorOutputDataHandler);
nativeProcess.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA,onStandardOutputDataHandler);
nativeProcess.start(nativeProcessStartupInfo);
}
private function onStandardErrorOutputDataHandler(event:ProgressEvent) : void
{
var certResponse:String = nativeProcess.standardError.readUTFBytes(nativeProcess.standardError.bytesAvailable);
trace(certResponse);
nativeProcess.removeEventListener(ProgressEvent.STANDARD_ERROR_DATA,onStandardOutputDataHandler);
nativeProcess.exit();
}
Alert.show(certResponse,'Error');
}
protected function onStandardOutputDataHandler(event:ProgressEvent):void
{
var status:String = nativeProcess.standardOutput.readUTFBytes(nativeProcess.standardOutput.bytesAvailable);
Alert.show(status)
}
}
}
Please let me know any clarification.

How to read binary content of a file in flex

I am very new to Flex. I want to retrieve file's binary content using flex but failed. Here is the code so far:
// ActionScript file
package source.fileApi{
import flash.display.Sprite;
import flash.external.ExternalInterface;
import flash.system.Security;
import flash.utils.setTimeout;
import flash.net.FileReference;
import flash.net.FileFilter;
import flash.events.IOErrorEvent;
import flash.events.Event;
import flash.utils.ByteArray;
import mx.utils.URLUtil;
public class FileAPIMain {
private var debug:Boolean = false;
private var dataReaded:String;
private var fr:FileReference;
public function FileAPIMain():void{
ExternalInterface.addCallback("setDebug", setDebug);
ExternalInterface.addCallback("onLoadFileClick", onLoadFileClick);
ExternalInterface.call("FileReader.__onFlashInitialized");
}
public function log(message:String):void {
if (debug) {
ExternalInterface.call("FileReader.__log", encodeURIComponent("[FileReader] " + message));
}
}
public function setDebug(val:Boolean):void {
debug = val;
if (val) {
log("debug enabled");
}
}
public function onLoadFileClick():void{
//create the FileReference instance
fr = new FileReference();
//listen for when they select a file
fr.addEventListener(Event.SELECT, onFileSelect);
//listen for when then cancel out of the browse dialog
fr.addEventListener(Event.CANCEL,onCancel);
//open a native browse dialog that filters for text files
fr.browse();
}
private function onFileSelect(e:Event):void{
fr.removeEventListener(Event.SELECT, onFileSelect);
//listen for when the file has loaded
fr.addEventListener(Event.COMPLETE, onLoadComplete);
//listen for any errors reading the file
fr.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
//load the content of the file
fr.load();
}
private function onCancel(e:Event):void{
log("File Browse Canceled");
fr = null;
}
private function onLoadComplete(e:Event):void{
fr.removeEventListener(Event.COMPLETE, onLoadComplete);
//get the data from the file as a ByteArray
var data:ByteArray = fr.data as ByteArray;
dataReaded = data.readUTFBytes(data.bytesAvailable);
ExternalInterface.call("FileReader.__getSize", fr.size);
ExternalInterface.call("FileReader.__getName", fr.name);
ExternalInterface.call("FileReader.__getData", dataReaded);
ExternalInterface.call("FileReader.__takeAction");
//clean up the FileReference instance
fr = null;
}
private function onLoadError(e:IOErrorEvent):void{
log("Error loading file : " + e.text);
}
}
}
But this did not give me the binary content of the file.. Could any one please tell me how to retrieve the full binary content of the given file using Flex (I am using FP 10.0).....
If you user FLashPlayer 10+ then make use of FileReference.
See the pure AS3 sample below
var buttonShape:Shape = new Shape();
buttonShape.graphics.beginFill(0x336699);
buttonShape.graphics.drawCircle(50, 50, 25);
var button = new SimpleButton(buttonShape, buttonShape, buttonShape, buttonShape);
addChild(button);
var fileRef:FileReference= new FileReference();
button.addEventListener(MouseEvent.CLICK, onButtonClick);
function onButtonClick(e:MouseEvent):void
{
fileRef.browse([new FileFilter("Images", "*.jpg;*.gif;*.png")]);
fileRef.addEventListener(Event.SELECT, onFileSelected);
}
function onFileSelected(e:Event):void
{
fileRef.addEventListener(Event.COMPLETE, onFileLoaded);
fileRef.load();
}
function onFileLoaded(e:Event):void
{
// here we get the data
var theData:Object = e.target.data;
// next you can manipulate the data as you like! ;) depending on the type
}

Access of Undefined Property as3

I know there are a number of posts dealing with this issue. But, I'm still not understanding it.
I keep getting a "1120: Access of undefined property CSSloader." in the following script:
package as3.comp{
import flash.events.Event;
import flash.events.ProgressEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.MovieClip;
import flash.text.StyleSheet;
public class cont extends MovieClip {
public var contentWidth:Number;
public var contentHeight:Number;
private var myXML:XML;
private var myLoader:URLLoader;
public function cont():void {
//Start
}
public function start():void {
myTooltip.start();
myTooltip.followMouse(false);
myTooltip.hide();
myWaiting.start();
myWaiting.hide();
myXML = new XML();
myXML.ignoreComments=true;
myXML.ignoreWhitespace=true;
contentWidth=100;
contentHeight=100;
contentSpace.width=contentWidth;
contentSpace.height=contentHeight;
//myWaiting.hide(); //hide clock
}
public function loadMc(contentFile:String):void {
var pictLdr:Loader = new Loader();
var pictURL:String=contentFile;
var pictURLReq:URLRequest=new URLRequest(pictURL);
pictLdr.contentLoaderInfo.addEventListener(Event.COMPLETE, imgLoadedF);
pictLdr.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, preloader);
pictLdr.load(pictURLReq);
contentLoadMC.addChild(pictLdr);
}
private function imgLoadedF(event:Event):void {
myTooltip.hide();
MovieClip(parent.parent).addContentMovie(this);
MovieClip(parent.parent).refresh();
}
private function preloader(event:ProgressEvent):void {
var pcent:Number=event.bytesLoaded/event.bytesTotal*100;
myTooltip.show(""+int(pcent));
}
public function loadCSS(event:Event):void {
var CSSloader:URLLoader = new URLLoader();
var req_css:URLRequest = new URLRequest("example.css");
CSSloader.load(req_css);
CSSloader.addEventListener("complete", loadXML);
}
public function loadXML(contentFile:String) {
var XML_URL:String=contentFile;
var myXMLURL:URLRequest=new URLRequest(XML_URL);
myLoader=new URLLoader(myXMLURL);
myLoader.addEventListener("complete", xmlLoaded);
}
private function xmlLoaded(event:Event):void {
myXML=XML(myLoader.data);
myXML.ignoreWhite = true;
var sheet:StyleSheet = new StyleSheet();
sheet.parseCSS(CSSloader.data);
myHtmlText.styleSheet = sheet;
myHtmlText.htmlText="HERE" + myXML.pageTop;
myHtmlText.width=contentWidth-10;
myHtmlText.height=myHtmlText.textHeight+30;
myHtmlText.x=10;
myHtmlText.y=10;
contentSpace.width=contentWidth;
contentSpace.height=myHtmlText.height+4;
myWaiting.hide();
MovieClip(parent.parent).addContentMovie(this);
MovieClip(parent.parent).refresh();
myLoader.removeEventListener("complete", xmlLoaded);
}
public function loadContent(contentFile:String) {
if (contentFile.substr(-3)=="xml") {
myWaiting.show();//show clock
loadXML(contentFile);
} else {
myTooltip.show("0");
loadMc(contentFile);
}
}
}
}
I've tried adding the code within loadCSS to both loadXML and xmlLoaded, but get the same error.
Any help would be GREATLY appreciated. Thanks.
You declared CSSloader in the function loadCSS, but you get the error in the function xmlloaded, which is a different scope. If you want CSSloader to be scoped for all functions, you'll have to declare it as a variable outside these functions - by convention at the top. (Unfortunately you have omitted the context of this code, but I assume this is inside a class?).

Host and load KML File in Flex project (IGN)

I'm looking at a tutorial to display placemarks using a KML file on a flex application. I'm using IGN API (openscales) in flex project.
The example works perfectly (http://openscales.org/userguide/examples/srcview/source/KMLExample.mxml.
<os:KML url="http://www.parisavelo.net/velib.kml"
proxy="http://openscales.org/proxy.php?url="
numZoomLevels="20"
style="{Style.getDefaultCircleStyle()}"/>
But when I'm hosting the same kml file on my server like that :
<os:KML url="http://www.cycom.org/velib.kml"
proxy="http://openscales.org/proxy.php?url="
numZoomLevels="20"
style="{Style.getDefaultCircleStyle()}"/>
Placemarks don't show up on the map. I tried to host the kml file on different hosts but that doesn't change. Do you have a clue ?
Thank you.
You need to add a crossdomain.xml to the server side (in root folder) to allow the application to access your KML file. For example, if you want to allow access to your server to all IPs and domain names just use a simple wild card as below in your crossdomain.xml file:
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>
For more information about using crossdomain file see: Transfering Data Accross Domains Using crossdomain.xml
If you don't have access to the server or if this solution doesn't work for you, you have to modify the KML.as class in openscales (found in org.openscales.core.layer package).
Change line 19
private var _request: XMLRequest = null;
to
private var _request :URLLoader = null;
Here is the entire modified KML class:
package org.openscales.core.layer
{
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import org.openscales.core.Trace;
import org.openscales.core.feature.Feature;
import org.openscales.core.format.KMLFormat;
import org.openscales.core.request.XMLRequest;
import org.openscales.geometry.basetypes.Bounds;
public class KML extends FeatureLayer
{
private var _url :String = "";
private var _request :URLLoader = null;
private var _kmlFormat :KMLFormat = null;
private var _xml :XML = null;
public function KML ( name :String,
url :String,
bounds :Bounds = null )
{
this._url = url;
this.maxExtent = bounds;
super( name );
this._kmlFormat = new KMLFormat();
}
override public function destroy () :void
{
if ( this._request )
this._request = null;
this.loading = false;
super.destroy();
}
override public function redraw ( fullRedraw :Boolean = true ) :void
{
if ( !displayed )
{
this.clear();
return;
}
if ( !this._request )
{
this.loading = true;
this._request = new URLLoader();
this._request.addEventListener( Event.COMPLETE, onSuccess );
this._request.addEventListener( IOErrorEvent.IO_ERROR, onFailure );
this._request.load( new URLRequest( url ));
}
else
{
this.clear();
this.draw();
}
}
public function onSuccess ( event :Event ) :void
{
this.loading = false;
var loader :URLLoader = event.target as URLLoader;
// To avoid errors if the server is dead
try
{
this._xml = new XML( loader.data );
if ( this.map.baseLayer.projection != null && this.projection != null && this.projection.srsCode != this.map.baseLayer.projection.srsCode )
{
this._kmlFormat.externalProj = this.projection;
this._kmlFormat.internalProj = this.map.baseLayer.projection;
}
this._kmlFormat.proxy = this.proxy;
var features :Vector.<Feature> = this._kmlFormat.read( this._xml ) as Vector.<Feature>;
this.addFeatures( features );
this.clear();
this.draw();
}
catch ( error :Error )
{
Trace.error( error.message );
}
}
protected function onFailure ( event :Event ) :void
{
this.loading = false;
Trace.error( "Error when loading kml " + this._url );
}
public function get url () :String
{
return this._url;
}
public function set url ( value :String ) :void
{
this._url = value;
}
override public function getURL ( bounds :Bounds ) :String
{
return this._url;
}
}
}

Resources