MDL-20808 "Create AMF test client" Some updates to client :
[moodle.git] / webservice / amf / testclient / AMFTester.mxml
CommitLineData
94a9b9e7
JP
1<?xml version="1.0" encoding="utf-8"?>
2<mx:Application
3 xmlns:mx="http://www.adobe.com/2006/mxml"
4 backgroundColor="white"
5 layout="absolute"
6 creationPolicy="all"
7 height="100%" width="100%"
8 applicationComplete="init()"
9 xmlns:cv="customValidators.*"
10 defaultButton="{call}">
11
12 <mx:Script>
13 <![CDATA[
14 import mx.events.ValidationResultEvent;
15 import mx.validators.Validator;
16 /**
17 * Main class/dialog
18 *
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.
24 *
25 * @author Jordi Boggiano <j.boggiano@seld.be>
26 */
27
28 import mx.controls.Label;
29 import mx.controls.Alert;
30 import mx.messaging.channels.AMFChannel;
31 import com.adobe.serialization.json.JSON;
32
c77e75a3
JP
33 // Import the debugger
34// import nl.demonsters.debugger.MonsterDebugger;
35
94a9b9e7
JP
36 public var api:AMFConnector;
37 protected var methods:Array;
38 protected var introspector:String;
39
40 public var rooturl:String;
41
42 [Bindable]
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}";
44
45 // Variable to hold the debugger
46// private var debugger:MonsterDebugger;
47
48 /**
49 * restores the last settings if available
50 */
51 public function init():void
52 {
53 // Init the debugger
54// debugger = new MonsterDebugger(this);
55
56 // Send a simple trace
57// MonsterDebugger.trace(this, "Hello World!");
58
59 var so:SharedObject = SharedObject.getLocal('AMFTester');
60 if (so.data.token) {
61 token.text = so.data.token;
62 }
63 if (so.data.username) {
64 username.text = so.data.username;
65 password.text = so.data.password;
66 }
67 if (so.data.mode == 'username'){
68 loginType.selectedIndex = 1;
69 }
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;
75
76 }
77 public function doConnectToken():void
78 {
79 var url:String = this.rooturl + '/webservice/amf/server.php?'+
80 'wstoken='+this.token.text;
81 this.doConnect(url);
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);
86 } else {
87 so.setProperty('token', null);//delete shared obj prop
88 }
89 so.setProperty('remembertoken', this.remembertoken.selected);
90 so.setProperty('mode', 'token');
91 so.flush();
92 }
93 public function doConnectUsername():void
94 {
95 var url:String = this.rooturl + '/webservice/amf/simpleserver.php?'+
96 'wsusername=' + this.username.text+
97 '&wspassword=' + this.password.text;
98 this.doConnect(url);
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);
104 } else {
105 so.setProperty('username', null);//delete shared obj prop
106 so.setProperty('password', null);
107 }
108 so.setProperty('rememberpassword', this.rememberpassword.selected);
109 so.setProperty('mode', 'username');
110 so.flush();
111 }
112
113 /**
114 * initializes the connection
115 */
116 private function doConnect(url:String):void
117 {
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);
125 }
126 this.panelDebug.enabled = false;
127 }
128
129 /**
130 * initializes the debugger dialog with the method list and everything
131 */
132 protected function handleConnection(event:Event):void
133 {
134 methods = [];
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']});
138 }
139 }
b7d76bfd 140 methods.sortOn('label', Array.CASEINSENSITIVE);
94a9b9e7
JP
141
142 this.panelDebug.enabled = true;
143 this.maintabs.selectedIndex = 1;
144 func.dataProvider = methods;
145 api.removeEventListener(Event.COMPLETE, handleConnection);
146 api.addEventListener(Event.COMPLETE, process);
147 reloadArgs();
148
149 }
150
151
152 /**
153 * outputs a response from the server
154 */
155 protected function process(event:Event):void
156 {
157 if (api.error) {
b7d76bfd
JP
158 var keys:String = '';
159 for (var key:String in api.data){
160 keys += key+' ';
161 }
162 push(output, time() + ": Exception (code: "+api.data.code+", description: "+api.data.description+" (line: "+api.data.line+") detail:\n "+api.data.detail+")\n");
94a9b9e7 163 } else {
b7d76bfd 164 push(output, time() + ": "+JSON.encode(api.data)+"\n");
94a9b9e7 165 }
c77e75a3 166// MonsterDebugger.trace(this, {"data":api.data, "error":api.error});
94a9b9e7
JP
167// MonsterDebugger.trace(this, api.data);
168 }
169
170 /**
171 * updates the display of arguments when the selected method changes
172 *
173 * 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
174 */
175 protected function reloadArgs():void
176 {
177 var i:int;
178 for (i = 1; i <= 7; i++) {
179 this['arg'+i].visible = false;
180 this['arg'+i].includeInLayout = false;
181 this['larg'+i].visible = false;
182 this['larg'+i].includeInLayout = false;
b7d76bfd
JP
183 this['cbarg'+i].visible = false;
184 this['cbarg'+i].includeInLayout = false;
94a9b9e7
JP
185 this['JSONV'+i].enabled = false;
186 }
187 i = 1;
188 for (var arg:String in func.selectedItem.args) {
189 (this['arg'+i] as TextInput).visible = true;
190 (this['arg'+i] as TextInput).includeInLayout = true;
b7d76bfd
JP
191 if (func.selectedItem.args[arg]['required']){
192 (this['arg'+i] as TextInput).enabled = true;
193 this['cbarg'+i].selected = true;
194 }
94a9b9e7
JP
195 (this['larg'+i] as Label).visible = true;
196 (this['larg'+i] as Label).includeInLayout = true;
b7d76bfd
JP
197 this['cbarg'+i].visible = !func.selectedItem.args[arg]['required'];
198 this['cbarg'+i].includeInLayout = !func.selectedItem.args[arg]['required'];
199 this['JSONV'+i].enabled = this['cbarg'+i].selected;
200 this['JSONV'+i].required = true;
94a9b9e7 201
b7d76bfd
JP
202 (this['larg'+i] as Label).text = func.selectedItem.args[arg]['name'] + (func.selectedItem.args[arg]['required'] ? "*":"");
203 i++;
204 }
205 while (i <= 7) {
206 this['cbarg'+i].selected = false;
207 i++;
94a9b9e7
JP
208 }
209 if (func.selectedItem.docs == ""){
210 (this.methodDescription as TextArea).text = "";
211 (this.methodDescription as TextArea).visible = false;
212 (this.methodDescription as TextArea).includeInLayout = false;
213 } else {
214 (this.methodDescription as TextArea).text = func.selectedItem.docs.replace(/[\n\r\f]+/g, "\n");
215 (this.methodDescription as TextArea).visible = true;
216 (this.methodDescription as TextArea).includeInLayout = true;
217 }
218 }
219
b7d76bfd
JP
220 public function toggleCheckBoxes(startAt:uint):void{
221 var i:uint= startAt;
222 if (this['cbarg'+i].selected){
223 i--;
224 while (i >= 1){
225 this['cbarg'+i].selected = true;
226 i--;
227 }
228 } else {
229 i++;
230 while (i <= 7){
231 this['cbarg'+i].selected = false;
232 i++;
233 }
234 }
235 }
236
94a9b9e7
JP
237 /**
238 * calls a method on the server
239 */
240 protected function execute():void
241 {
242 var input:TextInput;
243 var argumentArray:Array = [];
244 var argumentErrors:Array = Validator.validateAll(argumentValidators);
245 if (argumentErrors.length != 0){
246// MonsterDebugger.trace(this, argumentErrors);
247 return;
248 }
b7d76bfd 249 for(var i:int = 1; i <= 7; i++)
94a9b9e7
JP
250 {
251 input = this['arg' +i] as TextInput;
b7d76bfd 252 if(input && input.visible)
94a9b9e7 253 {
b7d76bfd
JP
254 if (!this['cbarg' +i].selected){
255 break;
256 } else if (input.text.indexOf("\"") == 0 || input.text.indexOf("{") == 0 || input.text.indexOf("[") == 0)
94a9b9e7
JP
257 try {
258 argumentArray.push(JSON.decode(input.text));
259 } catch (err:Error){
260 return;
261 }
262 else
263 argumentArray.push(input.text as String);
264 }
265 }
266
267
b7d76bfd 268 api.exec(func.selectedLabel, argumentArray);
94a9b9e7
JP
269// MonsterDebugger.trace(this, [func.selectedLabel, argumentArray[0], argumentArray[1], argumentArray[2], argumentArray[3], argumentArray[4], argumentArray[5], argumentArray[6]]);
270 push(output, time() + ": Calling "+func.selectedLabel+" with arguments - "+JSON.encode(argumentArray));
271 }
272
273 /**
274 * clears debug consoles
275 */
276 protected function clear():void
277 {
b7d76bfd 278 output.text = output.text = "";
94a9b9e7
JP
279 }
280
281 /**
282 * refreshes the method list
283 */
284 protected function refresh():void
285 {
286 api.removeEventListener(Event.COMPLETE, process);
287 api.addEventListener(Event.COMPLETE, handleConnection);
288 api.exec(introspector);
289 }
290
291 /**
292 * returns timestamp string
293 */
294 protected function time():String
295 {
296 var d:Date = new Date();
297 var ret:String = d.hours+":"+d.minutes+":"+d.seconds+"."+d.milliseconds;
298 return ret + "000000000000".substring(ret.length);
299 }
300
301 /**
302 * handler for specific net events
303 */
304 public function netStatusHandler(event:NetStatusEvent):void
305 {
b7d76bfd 306 push(output, time() + ": Error("+event.type+"): "+event.info.code+", "+event.info.description+", "+event.info.details);
94a9b9e7
JP
307 }
308
309 /**
310 * handler for security errors
311 */
312 public function securityErrorHandler(event:SecurityErrorEvent):void
313 {
b7d76bfd 314 push(output, time() + ": Error("+event.type+"): "+event.text);
94a9b9e7
JP
315 }
316
317 /**
318 * handler for io errors
319 */
320 public function ioErrorHandler(event:IOErrorEvent):void
321 {
b7d76bfd 322 push(output, time() + ": Error("+event.type+"): "+event.text);
94a9b9e7
JP
323 }
324
325 /**
326 * pushes text into a console and scrolls it down automatically
327 */
328 public function push(target:TextArea, text:String):void
329 {
330 target.text += text + "\n";
331 target.verticalScrollPosition = target.maxVerticalScrollPosition;
332 }
333
334 ]]>
335 </mx:Script>
336 <mx:Array id="argumentValidators">
b7d76bfd
JP
337 <cv:JSONValidator id="JSONV1" required="true" enabled="{cbarg1.selected}" source="{arg1}" property="text" />
338 <cv:JSONValidator id="JSONV2" required="true" enabled="{cbarg2.selected}" source="{arg2}" property="text" />
339 <cv:JSONValidator id="JSONV3" required="true" enabled="{cbarg3.selected}" source="{arg3}" property="text" />
340 <cv:JSONValidator id="JSONV4" required="true" enabled="{cbarg4.selected}" source="{arg4}" property="text" />
341 <cv:JSONValidator id="JSONV5" required="true" enabled="{cbarg5.selected}" source="{arg5}" property="text" />
342 <cv:JSONValidator id="JSONV6" required="true" enabled="{cbarg6.selected}" source="{arg6}" property="text" />
343 <cv:JSONValidator id="JSONV7" required="true" enabled="{cbarg7.selected}" source="{arg7}" property="text" />
94a9b9e7
JP
344 </mx:Array>
345
346
347
348 <mx:TabNavigator id="maintabs" height="100%" width="100%" >
349
350 <mx:TabNavigator label="Connect" id="loginType" borderStyle="solid" height="100%" width="100%">
351 <mx:Panel label="Use Token" id="panelConnectToken">
352 <mx:HBox width="100%">
353 <mx:Label text="Token"/>
354 <mx:TextInput id="token" text="" width="100%"/>
355 </mx:HBox>
356 <mx:HBox width="100%">
357 <mx:Label text="Remember"/>
358 <mx:CheckBox id="remembertoken" width="100%"/>
359 </mx:HBox>
360 <mx:Label id="urllabel1" text="URL :" />
361 <mx:HBox width="100%">
362 <mx:Spacer width="100%" />
363 <mx:Button label="Connect" click="doConnectToken()"/>
364 <mx:Spacer width="100%" />
365 </mx:HBox>
366 </mx:Panel>
367 <mx:Panel label="Use Username and Password" id="panelConnectUsername">
368 <mx:HBox width="100%">
369 <mx:Label text="Username"/>
370 <mx:TextInput id="username" text="" width="100%"/>
371 </mx:HBox>
372
373 <mx:HBox width="100%">
374 <mx:Label text="Password"/>
375 <mx:TextInput id="password" text="" displayAsPassword="true" width="100%"/>
376 </mx:HBox>
377 <mx:HBox width="100%">
378 <mx:Label text="Remember"/>
379 <mx:CheckBox id="rememberpassword" width="100%"/>
380 </mx:HBox>
381 <mx:Label id="urllabel2" text="URL :" />
382
383 <mx:HBox width="100%">
384 <mx:Spacer width="100%" />
385 <mx:Button label="Connect" click="doConnectUsername()"/>
386 <mx:Spacer width="100%" />
387 </mx:HBox>
388 </mx:Panel>
389 </mx:TabNavigator>
390 <mx:Panel label="Service Browser" width="100%" height="100%" layout="vertical" title="Moodle AMF Service Browser" enabled="false" id="panelDebug">
391 <mx:HBox width="100%">
392 <mx:Label text="Func "/>
393 <mx:ComboBox id="func" change="reloadArgs()">
394 </mx:ComboBox>
395 </mx:HBox>
396 <mx:TextArea id="methodDescription" text="" width="100%" height="120"/>
397 <mx:HBox width="100%">
398 <mx:Label id="larg1" text="Arg 1"/>
b7d76bfd
JP
399 <mx:CheckBox id="cbarg1" click="toggleCheckBoxes(1)"/>
400 <mx:TextInput id="arg1" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg1.selected}"/>
94a9b9e7
JP
401 </mx:HBox>
402 <mx:HBox width="100%">
403 <mx:Label id="larg2" text="Arg 2"/>
b7d76bfd
JP
404 <mx:CheckBox id="cbarg2" click="toggleCheckBoxes(2)"/>
405 <mx:TextInput id="arg2" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg2.selected}"/>
94a9b9e7
JP
406 </mx:HBox>
407 <mx:HBox width="100%">
408 <mx:Label id="larg3" text="Arg 3"/>
b7d76bfd
JP
409 <mx:CheckBox id="cbarg3" click="toggleCheckBoxes(3)"/>
410 <mx:TextInput id="arg3" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg3.selected}"/>
94a9b9e7
JP
411 </mx:HBox>
412 <mx:HBox width="100%">
413 <mx:Label id="larg4" text="Arg 4"/>
b7d76bfd
JP
414 <mx:CheckBox id="cbarg4" click="toggleCheckBoxes(4)"/>
415 <mx:TextInput id="arg4" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg4.selected}"/>
94a9b9e7
JP
416 </mx:HBox>
417 <mx:HBox width="100%">
418 <mx:Label id="larg5" text="Arg 5"/>
b7d76bfd
JP
419 <mx:CheckBox id="cbarg5" click="toggleCheckBoxes(5)"/>
420 <mx:TextInput id="arg5" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg5.selected}"/>
94a9b9e7
JP
421 </mx:HBox>
422 <mx:HBox width="100%">
423 <mx:Label id="larg6" text="Arg 6"/>
b7d76bfd
JP
424 <mx:CheckBox id="cbarg6" click="toggleCheckBoxes(6)"/>
425 <mx:TextInput id="arg6" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg6.selected}"/>
94a9b9e7
JP
426 </mx:HBox>
427 <mx:HBox width="100%">
428 <mx:Label id="larg7" text="Arg 7"/>
b7d76bfd
JP
429 <mx:CheckBox id="cbarg7" click="toggleCheckBoxes(7)"/>
430 <mx:TextInput id="arg7" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg7.selected}"/>
94a9b9e7
JP
431 </mx:HBox>
432 <mx:HBox width="100%">
433 <mx:Button id="call" label="Call" click="execute()"/>
434 <mx:Button label="Clear" click="clear()"/>
435 </mx:HBox>
b7d76bfd 436 <mx:TextArea id="output" width="100%" height="400"/>
94a9b9e7
JP
437 </mx:Panel>
438 </mx:TabNavigator>
439
440</mx:Application>