Commit | Line | Data |
---|---|---|
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> |