1 <?xml version=
"1.0" encoding=
"utf-8"?>
3 xmlns:
mx=
"http://www.adobe.com/2006/mxml"
4 backgroundColor=
"white"
7 height=
"100%" width=
"100%"
8 applicationComplete=
"init()"
9 xmlns:
cv=
"customValidators.*"
10 defaultButton=
"{call}">
14 import mx.events.ValidationResultEvent;
15 import mx.validators.Validator;
19 * This program is free software. It comes without any warranty, to
20 * the extent permitted by applicable law. You can redistribute it
21 * and/or modify it under the terms of the Do What The Fuck You Want
22 * To Public License, Version
2, as published by Sam Hocevar. See
23 * http://sam.zoy.org/wtfpl/COPYING for more details.
25 * @author Jordi Boggiano
<j.boggiano@seld.be
>
28 import mx.controls.Label;
29 import mx.controls.Alert;
30 import mx.messaging.channels.AMFChannel;
31 import com.adobe.serialization.json.JSON;
33 // Import the debugger
34 // import nl.demonsters.debugger.MonsterDebugger;
36 public var api:AMFConnector;
37 protected var methods:Array;
38 protected var introspector:String;
40 public var rooturl:String;
43 public var argumentToolTip:String =
"You can use JSON syntax for method arguments ie. an array is written like this [item1, item2, etc.] objects are written {\"propname\
":value, \"propname2\
":value2, etc}";
45 // Variable to hold the debugger
46 // private var debugger:MonsterDebugger;
49 * restores the last settings if available
51 public function init():void
54 // debugger = new MonsterDebugger(this);
56 // Send a simple trace
57 // MonsterDebugger.trace(this,
"Hello World!");
59 var so:SharedObject = SharedObject.getLocal('AMFTester');
61 token.text = so.data.token;
63 if (so.data.username) {
64 username.text = so.data.username;
65 password.text = so.data.password;
67 if (so.data.mode == 'username'){
68 loginType.selectedIndex =
1;
70 this.rememberpassword.selected = so.data.rememberpassword;
71 this.remembertoken.selected = so.data.remembertoken;
72 this.rooturl = Application.application.parameters.rooturl;
73 this.urllabel1.text = 'Root URL :'+this.rooturl;
74 this.urllabel2.text = 'Root URL :'+this.rooturl;
77 public function doConnectToken():void
79 var url:String = this.rooturl + '/webservice/amf/server.php?'+
80 'wstoken='+this.token.text;
82 // saving settings for next time
83 var so:SharedObject = SharedObject.getLocal('AMFTester');
84 if (this.rememberpassword.selected == true ){
85 so.setProperty('token', this.token.text);
87 so.setProperty('token', null);//delete shared obj prop
89 so.setProperty('remembertoken', this.remembertoken.selected);
90 so.setProperty('mode', 'token');
93 public function doConnectUsername():void
95 var url:String = this.rooturl + '/webservice/amf/simpleserver.php?'+
96 'wsusername=' + this.username.text+
97 '&wspassword=' + this.password.text;
99 // saving settings for next time
100 var so:SharedObject = SharedObject.getLocal('AMFTester');
101 if (this.rememberpassword.selected == true ){
102 so.setProperty('username', this.username.text);
103 so.setProperty('password', this.password.text);
105 so.setProperty('username', null);//delete shared obj prop
106 so.setProperty('password', null);
108 so.setProperty('rememberpassword', this.rememberpassword.selected);
109 so.setProperty('mode', 'username');
114 * initializes the connection
116 private function doConnect(url:String):void
118 api = new AMFConnector(url);
119 api.exec('MethodDescriptor.getMethods');
120 api.addEventListener(Event.COMPLETE, handleConnection);
121 if (!api.hasEventListener(NetStatusEvent.NET_STATUS)) {
122 api.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
123 api.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
124 api.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
126 this.panelDebug.enabled = false;
130 * initializes the debugger dialog with the method list and everything
132 protected function handleConnection(event:Event):void
135 for (var cls:String in api.data) {
136 for (var meth:String in api.data[cls]['methods']) {
137 methods.push({label: cls+'.'+meth, docs: api.data[cls]['methods'][meth]['docs'], args: api.data[cls]['methods'][meth]['params']});
141 this.panelDebug.enabled = true;
142 this.maintabs.selectedIndex =
1;
143 func.dataProvider = methods;
144 api.removeEventListener(Event.COMPLETE, handleConnection);
145 api.addEventListener(Event.COMPLETE, process);
152 * outputs a response from the server
154 protected function process(event:Event):void
157 push(input, time() +
": Exception (faultString: "+api.data.faultString+
", extendedData: "+api.data.extendedData+
", faultDetail: "+api.data.faultDetail+
")\n");
159 push(input, time() +
": "+JSON.encode(api.data)+
"\n");
161 // MonsterDebugger.trace(this, {
"data":api.data,
"error":api.error});
162 // MonsterDebugger.trace(this, api.data);
166 * updates the display of arguments when the selected method changes
168 * it's hardly optimal to do it that way but it was faster to copy paste, I just hope nobody needs more than
7 args
170 protected function reloadArgs():void
173 for (i =
1; i <=
7; i++) {
174 this['arg'+i].visible = false;
175 this['arg'+i].includeInLayout = false;
176 this['larg'+i].visible = false;
177 this['larg'+i].includeInLayout = false;
178 this['JSONV'+i].enabled = false;
181 for (var arg:String in func.selectedItem.args) {
182 (this['arg'+i] as TextInput).visible = true;
183 (this['arg'+i] as TextInput).includeInLayout = true;
184 (this['larg'+i] as Label).visible = true;
185 (this['larg'+i] as Label).includeInLayout = true;
186 this['JSONV'+i].enabled = true;
187 this['JSONV'+i].required = func.selectedItem.args[arg]['required'];
189 (this['larg'+i++] as Label).text = func.selectedItem.args[arg]['name'] + (func.selectedItem.args[arg]['required'] ?
"*":
"");
191 if (func.selectedItem.docs ==
""){
192 (this.methodDescription as TextArea).text =
"";
193 (this.methodDescription as TextArea).visible = false;
194 (this.methodDescription as TextArea).includeInLayout = false;
196 (this.methodDescription as TextArea).text = func.selectedItem.docs.replace(/[\n\r\f]+/g,
"\n");
197 (this.methodDescription as TextArea).visible = true;
198 (this.methodDescription as TextArea).includeInLayout = true;
203 * calls a method on the server
205 protected function execute():void
208 var argumentArray:Array = [];
209 var argumentErrors:Array = Validator.validateAll(argumentValidators);
210 if (argumentErrors.length !=
0){
211 // MonsterDebugger.trace(this, argumentErrors);
214 for(var i:int =
1; i <
8; i++)
216 input = this['arg' +i] as TextInput;
219 if (input.text.indexOf(
"{") ==
0 || input.text.indexOf(
"[") ==
0)
221 argumentArray.push(JSON.decode(input.text));
226 argumentArray.push(input.text as String);
231 api.exec(func.selectedLabel, argumentArray[
0], argumentArray[
1], argumentArray[
2], argumentArray[
3], argumentArray[
4], argumentArray[
5], argumentArray[
6]);
232 // MonsterDebugger.trace(this, [func.selectedLabel, argumentArray[
0], argumentArray[
1], argumentArray[
2], argumentArray[
3], argumentArray[
4], argumentArray[
5], argumentArray[
6]]);
233 push(output, time() +
": Calling "+func.selectedLabel+
" with arguments - "+JSON.encode(argumentArray));
237 * clears debug consoles
239 protected function clear():void
241 input.text = output.text =
"";
245 * refreshes the method list
247 protected function refresh():void
249 api.removeEventListener(Event.COMPLETE, process);
250 api.addEventListener(Event.COMPLETE, handleConnection);
251 api.exec(introspector);
255 * returns timestamp string
257 protected function time():String
259 var d:Date = new Date();
260 var ret:String = d.hours+
":"+d.minutes+
":"+d.seconds+
"."+d.milliseconds;
261 return ret +
"000000000000".substring(ret.length);
265 * handler for specific net events
267 public function netStatusHandler(event:NetStatusEvent):void
269 push(input, time() +
": Error("+event.type+
"): "+event.info.code+
", "+event.info.description+
", "+event.info.details);
273 * handler for security errors
275 public function securityErrorHandler(event:SecurityErrorEvent):void
277 push(input, time() +
": Error("+event.type+
"): "+event.text);
281 * handler for io errors
283 public function ioErrorHandler(event:IOErrorEvent):void
285 push(input, time() +
": Error("+event.type+
"): "+event.text);
289 * pushes text into a console and scrolls it down automatically
291 public function push(target:TextArea, text:String):void
293 target.text += text +
"\n";
294 target.verticalScrollPosition = target.maxVerticalScrollPosition;
299 <mx:Array id=
"argumentValidators">
300 <cv:JSONValidator id=
"JSONV1" required=
"false" source=
"{arg1}" property=
"text" />
301 <cv:JSONValidator id=
"JSONV2" required=
"false" source=
"{arg2}" property=
"text" />
302 <cv:JSONValidator id=
"JSONV3" required=
"false" source=
"{arg3}" property=
"text" />
303 <cv:JSONValidator id=
"JSONV4" required=
"false" source=
"{arg4}" property=
"text" />
304 <cv:JSONValidator id=
"JSONV5" required=
"false" source=
"{arg5}" property=
"text" />
305 <cv:JSONValidator id=
"JSONV6" required=
"false" source=
"{arg6}" property=
"text" />
306 <cv:JSONValidator id=
"JSONV7" required=
"false" source=
"{arg7}" property=
"text" />
311 <mx:TabNavigator id=
"maintabs" height=
"100%" width=
"100%" >
313 <mx:TabNavigator label=
"Connect" id=
"loginType" borderStyle=
"solid" height=
"100%" width=
"100%">
314 <mx:Panel label=
"Use Token" id=
"panelConnectToken">
315 <mx:HBox width=
"100%">
316 <mx:Label text=
"Token"/>
317 <mx:TextInput id=
"token" text=
"" width=
"100%"/>
319 <mx:HBox width=
"100%">
320 <mx:Label text=
"Remember"/>
321 <mx:CheckBox id=
"remembertoken" width=
"100%"/>
323 <mx:Label id=
"urllabel1" text=
"URL :" />
324 <mx:HBox width=
"100%">
325 <mx:Spacer width=
"100%" />
326 <mx:Button label=
"Connect" click=
"doConnectToken()"/>
327 <mx:Spacer width=
"100%" />
330 <mx:Panel label=
"Use Username and Password" id=
"panelConnectUsername">
331 <mx:HBox width=
"100%">
332 <mx:Label text=
"Username"/>
333 <mx:TextInput id=
"username" text=
"" width=
"100%"/>
336 <mx:HBox width=
"100%">
337 <mx:Label text=
"Password"/>
338 <mx:TextInput id=
"password" text=
"" displayAsPassword=
"true" width=
"100%"/>
340 <mx:HBox width=
"100%">
341 <mx:Label text=
"Remember"/>
342 <mx:CheckBox id=
"rememberpassword" width=
"100%"/>
344 <mx:Label id=
"urllabel2" text=
"URL :" />
346 <mx:HBox width=
"100%">
347 <mx:Spacer width=
"100%" />
348 <mx:Button label=
"Connect" click=
"doConnectUsername()"/>
349 <mx:Spacer width=
"100%" />
353 <mx:Panel label=
"Service Browser" width=
"100%" height=
"100%" layout=
"vertical" title=
"Moodle AMF Service Browser" enabled=
"false" id=
"panelDebug">
354 <mx:HBox width=
"100%">
355 <mx:Label text=
"Func "/>
356 <mx:ComboBox id=
"func" change=
"reloadArgs()">
359 <mx:TextArea id=
"methodDescription" text=
"" width=
"100%" height=
"120"/>
360 <mx:HBox width=
"100%">
361 <mx:Label id=
"larg1" text=
"Arg 1"/>
362 <mx:TextInput id=
"arg1" toolTip=
"{argumentToolTip}" width=
"100%"/>
364 <mx:HBox width=
"100%">
365 <mx:Label id=
"larg2" text=
"Arg 2"/>
366 <mx:TextInput id=
"arg2" toolTip=
"{argumentToolTip}" width=
"100%"/>
368 <mx:HBox width=
"100%">
369 <mx:Label id=
"larg3" text=
"Arg 3"/>
370 <mx:TextInput id=
"arg3" toolTip=
"{argumentToolTip}" width=
"100%"/>
372 <mx:HBox width=
"100%">
373 <mx:Label id=
"larg4" text=
"Arg 4"/>
374 <mx:TextInput id=
"arg4" toolTip=
"{argumentToolTip}" width=
"100%"/>
376 <mx:HBox width=
"100%">
377 <mx:Label id=
"larg5" text=
"Arg 5"/>
378 <mx:TextInput id=
"arg5" toolTip=
"{argumentToolTip}" width=
"100%"/>
380 <mx:HBox width=
"100%">
381 <mx:Label id=
"larg6" text=
"Arg 6"/>
382 <mx:TextInput id=
"arg6" toolTip=
"{argumentToolTip}" width=
"100%"/>
384 <mx:HBox width=
"100%">
385 <mx:Label id=
"larg7" text=
"Arg 7"/>
386 <mx:TextInput id=
"arg7" toolTip=
"{argumentToolTip}" width=
"100%"/>
388 <mx:HBox width=
"100%">
389 <mx:Button id=
"call" label=
"Call" click=
"execute()"/>
390 <mx:Button label=
"Clear" click=
"clear()"/>
392 <mx:TextArea id=
"output" width=
"100%" height=
"100"/>
393 <mx:TextArea id=
"input" width=
"100%" height=
"300"/>