MDL-20808 "Create AMF test client" Some updates to client :
[moodle.git] / webservice / amf / testclient / AMFTester.mxml
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}">
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                         
33                         // Import the debugger
34 //                      import nl.demonsters.debugger.MonsterDebugger;
35                         
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;
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                                 }
140                                 methods.sortOn('label', Array.CASEINSENSITIVE);
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) {
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");
163                                 } else {
164                                         push(output, time() + ": "+JSON.encode(api.data)+"\n");
165                                 }
166 //                              MonsterDebugger.trace(this, {"data":api.data, "error":api.error});
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;
183                                         this['cbarg'+i].visible = false;
184                                         this['cbarg'+i].includeInLayout = false;
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;
191                                         if (func.selectedItem.args[arg]['required']){
192                                                 (this['arg'+i] as TextInput).enabled = true;
193                                                 this['cbarg'+i].selected = true;
194                                         }
195                                         (this['larg'+i] as Label).visible = true;
196                                         (this['larg'+i] as Label).includeInLayout = true;
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;
201                                         
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++;
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                         
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                         
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                                 }
249                                 for(var i:int = 1; i <= 7; i++)
250                                 {
251                                         input = this['arg' +i] as TextInput;
252                                         if(input && input.visible)
253                                         {
254                                                 if (!this['cbarg' +i].selected){
255                                                         break;
256                                                 } else if (input.text.indexOf("\"") == 0 || input.text.indexOf("{") == 0 || input.text.indexOf("[") == 0)
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                                 
268                                 api.exec(func.selectedLabel, argumentArray);
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                         {
278                                 output.text = output.text = "";
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                         }
301                         /**
302                          * handler for specific net events
303                          */
304                         public function netStatusHandler(event:NetStatusEvent):void 
305                         {
306                                 push(output, time() + ": Error("+event.type+"): "+event.info.code+", "+event.info.description+", "+event.info.details);
307                         }
308                         
309                         /**
310                          * handler for security errors
311                          */
312                         public function securityErrorHandler(event:SecurityErrorEvent):void
313                         {
314                                 push(output, time() + ": Error("+event.type+"): "+event.text);
315                         }
316                 
317                         /**
318                          * handler for io errors
319                          */
320                         public function ioErrorHandler(event:IOErrorEvent):void 
321                         {
322                                 push(output, time() + ": Error("+event.type+"): "+event.text);
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                         }
334                 ]]>
335         </mx:Script>
336         <mx:Array id="argumentValidators">
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" />
344         </mx:Array>
345                 
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"/>
399                                 <mx:CheckBox id="cbarg1" click="toggleCheckBoxes(1)"/>
400                                 <mx:TextInput id="arg1" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg1.selected}"/>
401                         </mx:HBox>
402                         <mx:HBox width="100%">
403                                 <mx:Label id="larg2" text="Arg 2"/>
404                                 <mx:CheckBox id="cbarg2" click="toggleCheckBoxes(2)"/>
405                                 <mx:TextInput id="arg2" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg2.selected}"/>
406                         </mx:HBox>
407                         <mx:HBox width="100%">
408                                 <mx:Label id="larg3" text="Arg 3"/>
409                                 <mx:CheckBox id="cbarg3" click="toggleCheckBoxes(3)"/>
410                                 <mx:TextInput id="arg3" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg3.selected}"/>
411                         </mx:HBox>
412                         <mx:HBox width="100%">
413                                 <mx:Label id="larg4" text="Arg 4"/>
414                                 <mx:CheckBox id="cbarg4" click="toggleCheckBoxes(4)"/>
415                                 <mx:TextInput id="arg4" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg4.selected}"/>
416                         </mx:HBox>
417                         <mx:HBox width="100%">
418                                 <mx:Label id="larg5" text="Arg 5"/>
419                                 <mx:CheckBox id="cbarg5" click="toggleCheckBoxes(5)"/>
420                                 <mx:TextInput id="arg5" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg5.selected}"/>
421                         </mx:HBox>
422                         <mx:HBox width="100%">
423                                 <mx:Label id="larg6" text="Arg 6"/>
424                                 <mx:CheckBox id="cbarg6" click="toggleCheckBoxes(6)"/>
425                                 <mx:TextInput id="arg6" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg6.selected}"/>
426                         </mx:HBox>
427                         <mx:HBox width="100%">
428                                 <mx:Label id="larg7" text="Arg 7"/>
429                                 <mx:CheckBox id="cbarg7" click="toggleCheckBoxes(7)"/>
430                                 <mx:TextInput id="arg7" toolTip="{argumentToolTip}" width="100%" enabled="{cbarg7.selected}"/>
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>
436                         <mx:TextArea id="output"  width="100%" height="400"/>
437                 </mx:Panel>
438     </mx:TabNavigator>
439         
440 </mx:Application>