Merge lp:~axlrose112/midori/midori-restful_client-feature into lp:midori
- midori-restful_client-feature
- Merge into trunk
Proposed by
axlrose112
Status: | Needs review |
---|---|
Proposed branch: | lp:~axlrose112/midori/midori-restful_client-feature |
Merge into: | lp:midori |
Diff against target: |
1016 lines (+1012/-0) 1 file modified
extensions/restful-client.vala (+1012/-0) |
To merge this branch: | bzr merge lp:~axlrose112/midori/midori-restful_client-feature |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Cris Dywan | Needs Fixing | ||
Review via email: mp+274045@code.launchpad.net |
Commit message
Description of the change
midori extension resful-client
To post a comment you must log in.
Revision history for this message
axlrose112 (axlrose112) wrote : | # |
great i am going to fix it :)
Unmerged revisions
- 7046. By axlrose112
-
midori restful client feature
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'extensions/restful-client.vala' |
2 | --- extensions/restful-client.vala 1970-01-01 00:00:00 +0000 |
3 | +++ extensions/restful-client.vala 2015-10-10 13:23:24 +0000 |
4 | @@ -0,0 +1,1012 @@ |
5 | +/* |
6 | + Copyright (C) 2015 James Axl <axlrose112@gmail.com> |
7 | + |
8 | + This library is free software; you can redistribute it and/or |
9 | + modify it under the terms of the GNU Lesser General Public |
10 | + License as published by the Free Software Foundation; either |
11 | + version 2.1 of the License, or (at your option) any later version. |
12 | + |
13 | + See the file COPYING for the full license text. |
14 | + |
15 | + 0. Earth and all that is in it, with knowledge we protect it. And the best of mankind knowledge makes him and he makes it. |
16 | +*/ |
17 | + |
18 | +using Soup; |
19 | + |
20 | +namespace RestFul { |
21 | + private class Manager : Midori.Extension { |
22 | + private MainWindow _MainWindow; |
23 | + |
24 | + internal Manager () { |
25 | + GLib.Object (name: _("Restful Client"), |
26 | + description: _("Test your requests with Restful-Server"), |
27 | + version: "0.1" + Midori.VERSION_SUFFIX, |
28 | + authors: "James Axl <axlrose112@gmail.com>"); |
29 | + activate.connect (this.activated); |
30 | + deactivate.connect (this.deactivated); |
31 | + } |
32 | + |
33 | + void tool_menu_populated (Gtk.Menu menu) { |
34 | + var item = new Gtk.MenuItem.with_label("Restful Client"); |
35 | + menu.add(item); |
36 | + item.activate.connect (() => { |
37 | + if (_MainWindow == null ) { |
38 | + _MainWindow = new MainWindow(); |
39 | + _MainWindow.ShowAll(); |
40 | + } else if(_MainWindow.Opened) { |
41 | + _MainWindow.present (); |
42 | + return; |
43 | + } else { |
44 | + _MainWindow = new MainWindow(); |
45 | + _MainWindow.ShowAll(); |
46 | + } |
47 | + }); |
48 | + item.show(); |
49 | + } |
50 | + |
51 | + void browser_added (Midori.Browser browser) { |
52 | + browser.populate_tool_menu.connect(tool_menu_populated); |
53 | + } |
54 | + |
55 | + void activated (Midori.App app) { |
56 | + _MainWindow = null; |
57 | + foreach (var browser in app.get_browsers ()) |
58 | + browser_added (browser); |
59 | + app.add_browser.connect (browser_added); |
60 | + } |
61 | + |
62 | + void deactivated () { |
63 | + var app = get_app (); |
64 | + app.add_browser.disconnect (browser_added); |
65 | + } |
66 | + |
67 | + } |
68 | + |
69 | + public class RequestGui : Gtk.Box { |
70 | + public string method {get; private set;} |
71 | + public string Header {get; private set;} |
72 | + public string UrlText {get; private set;} |
73 | + public string DataText {get; private set;} |
74 | + private Gtk.Entry Url; |
75 | + private Gtk.Entry Data; |
76 | + private Gtk.ComboBox Cbox; |
77 | + private Gtk.ComboBox HeaderBox; |
78 | + private Project project; |
79 | + private RestfulData Db; |
80 | + public MainWindow _MainWindow; |
81 | + |
82 | + public RequestGui(MainWindow mainWindow) { |
83 | + |
84 | + Object (orientation : Gtk.Orientation.VERTICAL, spacing : 5); |
85 | + _MainWindow = mainWindow; |
86 | + var paned = new Gtk.Paned (Gtk.Orientation.HORIZONTAL); |
87 | + var scrolledResultView = new Gtk.ScrolledWindow (null, null); |
88 | + var scrolledProject = new Gtk.ScrolledWindow (null, null); |
89 | + var status = new Gtk.Statusbar(); |
90 | + var notebook = new Gtk.Notebook (); |
91 | + var title = new Gtk.Label ("Projects"); |
92 | + var vbox1 = new Gtk.Box (Gtk.Orientation.VERTICAL, 3); |
93 | + var hbox1 = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 3); |
94 | + var hbox2 = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 3); |
95 | + var hbox3 = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 3); |
96 | + var hbox4 = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 3); |
97 | + var appendRequest = new Gtk.Button.with_label("Append"); |
98 | + var resultView = new Gtk.TextView(); |
99 | + Db = new RestfulData(); |
100 | + var soupRequest = new RestfulSoup(this); |
101 | + project = new Project(this, appendRequest, Db); |
102 | + appendRequest.clicked.connect (() => { |
103 | + project.AddRequest(); |
104 | + }); |
105 | + appendRequest.set_sensitive(false); |
106 | + Url = new Gtk.Entry(); |
107 | + Data = new Gtk.Entry(); |
108 | + Url.set_placeholder_text ("Set URL"); |
109 | + Data.set_placeholder_text ("Set DATA"); |
110 | + Url.changed.connect (() => { |
111 | + UrlText = Url.get_text (); |
112 | + if(UrlText == "" || !project.IsProject ) appendRequest.set_sensitive(false); |
113 | + else if(UrlText != "" && project.IsProject) appendRequest.set_sensitive(true); |
114 | + }); |
115 | + Url.activate.connect (() => { |
116 | + if (UrlText == "") { |
117 | + InfoDialog("Fill the URL field"); |
118 | + return; |
119 | + } |
120 | + if (!soupRequest.SendRequest(UrlText, DataText, method, Header)) return ; |
121 | + resultView.buffer.text = soupRequest.ResfultBody; |
122 | + }); |
123 | + Url.set_icon_from_icon_name (Gtk.EntryIconPosition.SECONDARY, "edit-clear"); |
124 | + Url.icon_press.connect ((pos, event) => { |
125 | + if (pos == Gtk.EntryIconPosition.SECONDARY) { |
126 | + Url.set_text (""); |
127 | + } |
128 | + }); |
129 | + Data.changed.connect (() => { |
130 | + DataText = Data.get_text (); |
131 | + }); |
132 | + Data.activate.connect (() => { |
133 | + soupRequest.SendRequest(UrlText, DataText, method, Header); |
134 | + resultView.buffer.text = soupRequest.ResfultBody; |
135 | + }); |
136 | + Data.set_icon_from_icon_name (Gtk.EntryIconPosition.SECONDARY, "edit-clear"); |
137 | + Data.icon_press.connect ((pos, event) => { |
138 | + if (pos == Gtk.EntryIconPosition.SECONDARY) { |
139 | + Data.set_text (""); |
140 | + } |
141 | + }); |
142 | + |
143 | + resultView.editable = false; |
144 | + var bar = new Gtk.MenuBar (); |
145 | + var itemFile = new Gtk.MenuItem.with_mnemonic ("_File"); |
146 | + var itemHelp = new Gtk.MenuItem.with_mnemonic ("_?"); |
147 | + bar.add (itemFile); |
148 | + bar.add (itemHelp); |
149 | + var fileMenu = new Gtk.Menu (); |
150 | + var helpMenu = new Gtk.Menu (); |
151 | + |
152 | + itemFile.set_submenu (fileMenu); |
153 | + itemHelp.set_submenu (helpMenu); |
154 | + var itemNew = new Gtk.MenuItem.with_mnemonic ("_New project"); |
155 | + var itemExportAll = new Gtk.MenuItem.with_mnemonic ("_Export data"); |
156 | + var itemImportAll = new Gtk.MenuItem.with_mnemonic ("_Import data"); |
157 | + var itemSeeYou = new Gtk.MenuItem.with_mnemonic ("_See you"); |
158 | + var itemAbout = new Gtk.MenuItem.with_mnemonic ("_About"); |
159 | + var itemHelpWeb = new Gtk.MenuItem.with_mnemonic ("_Help"); |
160 | + |
161 | + fileMenu.add (itemNew); |
162 | + fileMenu.add (itemExportAll); |
163 | + fileMenu.add (itemImportAll); |
164 | + fileMenu.add (itemSeeYou); |
165 | + helpMenu.add (itemAbout); |
166 | + helpMenu.add (itemHelpWeb); |
167 | + |
168 | + itemNew.activate.connect (() => { |
169 | + project.ItemNewProject(); |
170 | + }); |
171 | + |
172 | + itemHelpWeb.activate.connect (() => { |
173 | + try { |
174 | + AppInfo.launch_default_for_uri ("https://wiki.xfce.org/restful-client_midori", null); |
175 | + } catch { |
176 | + print("Can not open it\n"); |
177 | + } |
178 | + }); |
179 | + |
180 | + itemAbout.activate.connect (() => { |
181 | + Gtk.MessageDialog msg = new Gtk.MessageDialog (_MainWindow, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, "Welcome to Midori Restful-client"); |
182 | + msg.format_secondary_text("Press Ok if you like it and Cancel if you have issues."); |
183 | + msg.response.connect ((response_id) => { |
184 | + switch (response_id) { |
185 | + case Gtk.ResponseType.OK: |
186 | + try { |
187 | + AppInfo.launch_default_for_uri ("http://midori-browser.org/", null); |
188 | + } catch { |
189 | + print("Can not open it\n"); |
190 | + } |
191 | + break; |
192 | + case Gtk.ResponseType.CANCEL: |
193 | + try { |
194 | + AppInfo.launch_default_for_uri ("https://bugs.launchpad.net/midori/+filebug", null); |
195 | + } catch { |
196 | + print("Can not open it\n"); |
197 | + } |
198 | + break; |
199 | + } |
200 | + msg.destroy(); |
201 | + }); |
202 | + msg.show (); |
203 | + }); |
204 | + |
205 | + itemImportAll.activate.connect (() => { |
206 | + var import = new RestFullExportImportData(); |
207 | + Gtk.FileChooserDialog chooser = new Gtk.FileChooserDialog ( |
208 | + "Select your favorite file", _MainWindow, Gtk.FileChooserAction.OPEN, |
209 | + "_Cancel", |
210 | + Gtk.ResponseType.CANCEL, |
211 | + "_Open", |
212 | + Gtk.ResponseType.ACCEPT); |
213 | + var filter = new Gtk.FileFilter (); |
214 | + chooser.set_filter (filter); |
215 | + filter.add_pattern ("*.midorix"); |
216 | + if (chooser.run () == Gtk.ResponseType.ACCEPT) { |
217 | + string uri = chooser.get_uri(); |
218 | + import.ImportData(uri); |
219 | + } |
220 | + chooser.close (); |
221 | + }); |
222 | + |
223 | + itemExportAll.activate.connect (() => { |
224 | + var export = new RestFullExportImportData(); |
225 | + Gtk.FileChooserDialog chooser = new Gtk.FileChooserDialog ( |
226 | + "Select your favorite file", _MainWindow, Gtk.FileChooserAction.SAVE, |
227 | + "_Cancel", |
228 | + Gtk.ResponseType.CANCEL, |
229 | + "_Save", |
230 | + Gtk.ResponseType.ACCEPT); |
231 | + if (chooser.run () == Gtk.ResponseType.ACCEPT) { |
232 | + string uri = chooser.get_uri(); |
233 | + export.ExportData(uri); |
234 | + } |
235 | + chooser.close (); |
236 | + }); |
237 | + |
238 | + itemSeeYou.activate.connect (() => { |
239 | + _MainWindow.destroy(); |
240 | + }); |
241 | + |
242 | + /* cb method */ |
243 | + var method_store = new Gtk.ListStore (1, typeof (string)); |
244 | + Gtk.TreeIter iter; |
245 | + method = "POST"; |
246 | + method_store.append (out iter); |
247 | + method_store.set (iter, 0, method); |
248 | + method_store.append (out iter); |
249 | + method_store.set (iter, 0, "GET"); |
250 | + method_store.append (out iter); |
251 | + method_store.set (iter, 0, "PUT"); |
252 | + method_store.append (out iter); |
253 | + method_store.set (iter, 0, "DELETE"); |
254 | + |
255 | + /* cb header */ |
256 | + Gtk.ListStore headerlist = new Gtk.ListStore (1, typeof (string)); |
257 | + |
258 | + headerlist.append (out iter); |
259 | + headerlist.set (iter, 0, "application/x-www-form-urlencoded"); |
260 | + headerlist.append (out iter); |
261 | + headerlist.set (iter, 0, "application/json"); |
262 | + |
263 | + /* The Box */ |
264 | + |
265 | + Cbox = new Gtk.ComboBox.with_model (method_store); |
266 | + HeaderBox = new Gtk.ComboBox.with_model (headerlist); |
267 | + |
268 | + var renderer = new Gtk.CellRendererText (); |
269 | + Cbox.pack_start (renderer, false); |
270 | + Cbox.add_attribute (renderer, "text", 0); |
271 | + Cbox.active = 0; |
272 | + |
273 | + var headerrenderer = new Gtk.CellRendererText (); |
274 | + HeaderBox.pack_start (headerrenderer, false); |
275 | + HeaderBox.add_attribute (headerrenderer, "text", 0); |
276 | + HeaderBox.active = 0; |
277 | + |
278 | + Cbox.changed.connect (() => { |
279 | + GLib.Value val1; |
280 | + Cbox.get_active_iter (out iter); |
281 | + method_store.get_value (iter, 0, out val1); |
282 | + method = (string) val1; |
283 | + }); |
284 | + |
285 | + HeaderBox.changed.connect (() => { |
286 | + GLib.Value val1; |
287 | + HeaderBox.get_active_iter (out iter); |
288 | + headerlist.get_value (iter, 0, out val1); |
289 | + Header = (string) val1; |
290 | + }); |
291 | + |
292 | + hbox1.pack_start (Cbox, false, false, 5); |
293 | + hbox1.pack_start (Url, true, true, 5); |
294 | + hbox1.pack_start (appendRequest , false, false, 5); |
295 | + hbox2.pack_start (HeaderBox, false, false, 5); |
296 | + hbox2.pack_start (Data, true, true, 5); |
297 | + scrolledResultView.add(resultView); |
298 | + hbox3.pack_start (scrolledResultView, true, true, 5); |
299 | + scrolledProject.add(project); |
300 | + notebook.append_page (scrolledProject, title); |
301 | + paned.add1(notebook); |
302 | + paned.add2(vbox1); |
303 | + vbox1.pack_start (hbox1, false, false, 5); |
304 | + vbox1.pack_start (hbox2, false, false, 5); |
305 | + vbox1.pack_start (hbox3, true, true, 5); |
306 | + hbox4.pack_start (paned, true, true, 7); |
307 | + this.pack_start (bar, false, false, 1); |
308 | + this.pack_start (hbox4, true, true, 5); |
309 | + this.pack_start (status, false, false, 1); |
310 | + BackToTheZero(); |
311 | + } |
312 | + |
313 | + public void FillWidgets(List<string> dataResquest) { |
314 | + Url.set_text(dataResquest.nth_data (0)); |
315 | + if (dataResquest.nth_data(1) == "empty") |
316 | + dataResquest.nth(1).data = ""; |
317 | + Data.set_text(dataResquest.nth_data(1)); |
318 | + Cbox.set_active(int.parse(dataResquest.nth_data (2))); |
319 | + HeaderBox.set_active(int.parse(dataResquest.nth_data (3))); |
320 | + foreach (string request in dataResquest) |
321 | + dataResquest.remove(request); |
322 | + } |
323 | + |
324 | + public void BackToTheZero() { |
325 | + Url.set_text(""); |
326 | + Data.set_text(""); |
327 | + Cbox.set_active(0); |
328 | + HeaderBox.set_active(0); |
329 | + Header = "application/x-www-form-urlencoded"; |
330 | + method = "POST"; |
331 | + UrlText = ""; |
332 | + DataText = ""; |
333 | + } |
334 | + |
335 | + public void InfoDialog(string msg) { |
336 | + var dialog = new Gtk.MessageDialog(_MainWindow, Gtk.DialogFlags.MODAL,Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msg); |
337 | + dialog.set_title("Midori"); |
338 | + dialog.run(); |
339 | + dialog.destroy(); |
340 | + } |
341 | + } |
342 | + |
343 | + public class MainWindow : Gtk.Window { |
344 | + public bool Opened {get; set;} |
345 | + public MainWindow() { |
346 | + this.title = "Midori Restful-client"; |
347 | + this.set_default_size (800, 600); |
348 | + this.window_position = Gtk.WindowPosition.CENTER; |
349 | + Opened = false; |
350 | + this.destroy.connect(HideMe); |
351 | + } |
352 | + |
353 | + public void ShowAll() { |
354 | + Opened = true; |
355 | + var gui = new RequestGui(this); |
356 | + this.add(gui); |
357 | + this.show_all(); |
358 | + } |
359 | + |
360 | + private void HideMe() { |
361 | + this.destroy(); |
362 | + Opened = false; |
363 | + } |
364 | + } |
365 | + |
366 | + public class RestfulSoup { |
367 | + public string ResfultBody {get; private set;} |
368 | + private RequestGui _RequestGui; |
369 | + |
370 | + public RestfulSoup (RequestGui requestGui) { |
371 | + _RequestGui = requestGui; |
372 | + } |
373 | + |
374 | + public bool SendRequest(string url, string data, string method, string header) { |
375 | + var new_url = url; |
376 | + |
377 | + try { |
378 | + var regex = new Regex("""^(http|https)(:\/\/[a-zA-Z0-9?=#.\/]+)"""); |
379 | + if (regex.match(new_url)) { |
380 | + var session = new Soup.Session (); |
381 | + var message = new Soup.Message (method, new_url); |
382 | + if (data != "") |
383 | + message.set_request(header, MemoryUse.COPY, data.data); |
384 | + session.send_message (message); |
385 | + ResfultBody = (string) message.response_body.data; |
386 | + return true; |
387 | + } else { |
388 | + _RequestGui.InfoDialog("You should put a right url e.g 'http://midori-browser.org'"); |
389 | + } |
390 | + } catch( RegexError re ) { |
391 | + warning ("%s", re.message); |
392 | + } |
393 | + return false; |
394 | + } |
395 | + } |
396 | + |
397 | + public class RestfulData { |
398 | + private Sqlite.Database DB; |
399 | + private int EC; |
400 | + private string ErrMsg; |
401 | + public List<string> ProjectList; |
402 | + public List<string> RequestList; |
403 | + public List<string> RequestDataList; |
404 | + public RestfulData() { |
405 | + // Open/Create a database: I used /tmp just for testing but i am going to use config dir after |
406 | + EC = Sqlite.Database.open ("/tmp/midori_restful.db", out DB); |
407 | + ProjectList = new List<string>(); |
408 | + RequestList = new List<string>(); |
409 | + RequestDataList = new List<string>(); |
410 | + if (EC != Sqlite.OK) { |
411 | + stderr.printf ("Can't open database: %d: %s\n", DB.errcode (), DB.errmsg ()); |
412 | + return; |
413 | + } |
414 | + string query = """ |
415 | + create table if not exists `project` ( |
416 | + `id_project` INTEGER, |
417 | + `name` Text NOT NULL UNIQUE, |
418 | + `date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
419 | + PRIMARY KEY(id_project)); |
420 | + create table if not exists `request` ( |
421 | + `id_request` INTEGER, |
422 | + `name` Text NOT NULL UNIQUE, |
423 | + `url` Text NOT NULL, |
424 | + `data` Text NOT NULL, |
425 | + `method` Text NOT NULL, |
426 | + `header` Text NOT NULL, |
427 | + `date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
428 | + `id_project` integer, |
429 | + PRIMARY KEY(id_request), |
430 | + Foreign key(id_project) references project on delete cascade on update cascade); |
431 | + """; |
432 | + string on_key = "PRAGMA foreign_keys = ON;"; |
433 | + EC = DB.exec (on_key, null, out ErrMsg); |
434 | + |
435 | + // Execute our query: |
436 | + EC = DB.exec (query, null, out ErrMsg); |
437 | + if (EC != Sqlite.OK){ |
438 | + stderr.printf ("Error: %s\n", ErrMsg); |
439 | + return; |
440 | + } |
441 | + } |
442 | + |
443 | + public bool RegisterProject(string name, string date = "empty") { |
444 | + string query; |
445 | + if (date == "empty") |
446 | + query = "INSERT INTO project (name) VALUES ('"+ name +"');"; |
447 | + else |
448 | + query = "INSERT INTO project (name) VALUES ('"+ name +"', '"+ date +"');"; |
449 | + |
450 | + EC = DB.exec (query, null, out ErrMsg); |
451 | + if (EC != Sqlite.OK) { |
452 | + stderr.printf ("Error: %s\n", ErrMsg); |
453 | + return false; |
454 | + } |
455 | + return true; |
456 | + } |
457 | + |
458 | + public bool DeleteProject(string name) { |
459 | + string query = "DELETE from project WHERE name = '"+ name +"';"; |
460 | + EC = DB.exec (query, null, out ErrMsg); |
461 | + if (EC != Sqlite.OK) { |
462 | + stderr.printf ("Error: %s\n", ErrMsg); |
463 | + return false; |
464 | + } |
465 | + return true; |
466 | + } |
467 | + |
468 | + public bool CheckDuplicatProject (string name) { |
469 | + Sqlite.Statement stmt; |
470 | + var query = "select name from project where name = '"+ name +"';"; |
471 | + if ((EC = DB.prepare_v2 (query, -1, out stmt, null)) == Sqlite.ERROR) { |
472 | + printerr ("SQL error: %d, %s\n", EC, DB.errmsg ()); |
473 | + return false; |
474 | + } |
475 | + EC = stmt.step(); |
476 | + if (EC != Sqlite.ROW && EC != Sqlite.DONE)return false; |
477 | + return (EC == Sqlite.ROW); |
478 | + } |
479 | + |
480 | + public bool RetrieveProject() { |
481 | + var query = "select name from project;"; |
482 | + EC = DB.exec (query, exec_callback, out ErrMsg); |
483 | + if (EC != Sqlite.OK) { |
484 | + stderr.printf ("Error: %s\n", ErrMsg); |
485 | + return false; |
486 | + } |
487 | + return true; |
488 | + } |
489 | + |
490 | + public int CountProject() { |
491 | + string[] res; |
492 | + int nrows; |
493 | + int ncols; |
494 | + var query = "select name from project;"; |
495 | + if ((EC = DB.get_table (query, out res, out nrows, out ncols, out ErrMsg)) == Sqlite.ERROR) { |
496 | + printerr ("SQL error: %d, %s\n", EC, ErrMsg); |
497 | + } |
498 | + return nrows; |
499 | + } |
500 | + |
501 | + private int exec_callback (int n_columns, string[] values, string[] column_names) { |
502 | + for (int i = 0; i < n_columns; i++) { |
503 | + this.ProjectList.append(values[i]); |
504 | + } |
505 | + return 0; |
506 | + } |
507 | + |
508 | + public bool RegisterRequest(string p_name, string r_name, string r_url, string method, string r_data, string r_header, string r_date = "empty"){ |
509 | + Sqlite.Statement stmt; |
510 | + string query; |
511 | + int id_project = 0; |
512 | + string p_query = "select id_project from project where name = '"+ p_name +"';"; |
513 | + if ((EC = DB.prepare_v2 (p_query, -1, out stmt, null)) == Sqlite.ERROR) { |
514 | + printerr ("SQL error: %d, %s\n", EC, DB.errmsg ()); |
515 | + return false; |
516 | + } |
517 | + |
518 | + if (stmt.step () == Sqlite.ROW) id_project = int.parse(stmt.column_text (0)); |
519 | + if (r_date == "empty") |
520 | + query = "INSERT INTO request (name, url, data, id_project, method, header) VALUES ('"+ r_name +"', '"+ r_url +"', '"+ r_data +"' , ?, '"+ method +"', '"+ r_header +"');"; |
521 | + else |
522 | + query = "INSERT INTO request (name, url, data, id_project, method, header, date) VALUES ('"+ r_name +"', '"+ r_url +"', '"+ r_data +"' , ?, '"+ method +"', '"+ r_header +"', '"+ r_date +"');"; |
523 | + |
524 | + EC = DB.prepare_v2(query, -1, out stmt); |
525 | + EC = stmt.bind_int(1, id_project); |
526 | + EC = stmt.step(); |
527 | + if (EC != Sqlite.DONE) { |
528 | + stderr.printf ("Error: %s\n", ErrMsg); |
529 | + return false; |
530 | + } |
531 | + |
532 | + return true; |
533 | + } |
534 | + |
535 | + public bool DuplicatRequest(string p_name, string r_name) { |
536 | + Sqlite.Statement stmt; |
537 | + var query = "select project.name from request, project where request.id_project = project.id_project and request.name = '"+r_name+"' and project.name='"+p_name+"';"; |
538 | + if ((EC = DB.prepare_v2 (query, -1, out stmt, null)) == Sqlite.ERROR) { |
539 | + printerr ("SQL error: %d, %s\n", EC, DB.errmsg ()); |
540 | + return false; |
541 | + } |
542 | + EC = stmt.step(); |
543 | + if (EC != Sqlite.ROW && EC != Sqlite.DONE) return false; |
544 | + return (EC == Sqlite.ROW); |
545 | + } |
546 | + |
547 | + public bool DeleteRequest(string name) { |
548 | + |
549 | + string query = "DELETE FROM request WHERE name = '"+ name +"';"; |
550 | + EC = DB.exec (query, null, out ErrMsg); |
551 | + if (EC != Sqlite.OK) { |
552 | + stderr.printf ("Error here: %s\n", ErrMsg); |
553 | + return false; |
554 | + } |
555 | + return true; |
556 | + } |
557 | + |
558 | + public bool RetrieveRequestByProject(string name) { |
559 | + Sqlite.Statement stmt; |
560 | + string query = "select request.name from project, request where request.id_project = project .id_project and project.name = '"+ name +"';"; |
561 | + if ((EC = DB.prepare_v2 (query, -1, out stmt, null)) == Sqlite.ERROR) { |
562 | + printerr ("SQL error: %d, %s\n", EC, DB.errmsg ()); |
563 | + return false; |
564 | + } |
565 | + while (stmt.step () == Sqlite.ROW) { |
566 | + this.RequestList.append(stmt.column_text(0)); |
567 | + } |
568 | + return true; |
569 | + } |
570 | + |
571 | + public bool RetrieveRequestData(string name){ |
572 | + Sqlite.Statement stmt; |
573 | + string query = "select url, data, method, header, date from request where name = '"+ name +"';"; |
574 | + if ((EC = DB.prepare_v2 (query, -1, out stmt, null)) == Sqlite.ERROR) { |
575 | + printerr ("SQL error: %d, %s\n", EC, DB.errmsg ()); |
576 | + return false; |
577 | + } |
578 | + if (stmt.step () == Sqlite.ROW) { |
579 | + this.RequestDataList.append(stmt.column_text(0)); |
580 | + this.RequestDataList.append(stmt.column_text(1)); |
581 | + switch (stmt.column_text(2)) { |
582 | + case "POST": |
583 | + this.RequestDataList.append("0"); |
584 | + break; |
585 | + case "GET": |
586 | + this.RequestDataList.append("1"); |
587 | + break; |
588 | + case "PUT": |
589 | + this.RequestDataList.append("2"); |
590 | + break; |
591 | + case "DELETE": |
592 | + this.RequestDataList.append("3"); |
593 | + break; |
594 | + } |
595 | + switch (stmt.column_text(3)) { |
596 | + case "application/x-www-form-urlencoded": |
597 | + this.RequestDataList.append("0"); |
598 | + break; |
599 | + case "application/json": |
600 | + this.RequestDataList.append("1"); |
601 | + break; |
602 | + } |
603 | + this.RequestDataList.append(stmt.column_text(4)); |
604 | + return true; |
605 | + } |
606 | + return false; |
607 | + } |
608 | + |
609 | + public bool RetrieveRequestDataXml(string name){ |
610 | + Sqlite.Statement stmt; |
611 | + string query = "select url, data, method, header, date from request where name = '"+ name +"';"; |
612 | + if ((EC = DB.prepare_v2 (query, -1, out stmt, null)) == Sqlite.ERROR) { |
613 | + printerr ("SQL error: %d, %s\n", EC, DB.errmsg ()); |
614 | + return false; |
615 | + } |
616 | + if (stmt.step () == Sqlite.ROW) { |
617 | + this.RequestDataList.append(stmt.column_text(0)); |
618 | + this.RequestDataList.append(stmt.column_text(1)); |
619 | + this.RequestDataList.append(stmt.column_text(2)); |
620 | + this.RequestDataList.append(stmt.column_text(3)); |
621 | + this.RequestDataList.append(stmt.column_text(4)); |
622 | + return true; |
623 | + } |
624 | + return false; |
625 | + } |
626 | + |
627 | + } |
628 | + |
629 | + public class Project : Gtk.TreeView { |
630 | + |
631 | + public string SelectedRow {get; private set;} |
632 | + public bool IsProject {get; private set;} |
633 | + private Gtk.TreeStore ProjectStore; |
634 | + private Gtk.TreeIter ProjectParent; |
635 | + private Gtk.TreeIter RequestChild; |
636 | + private Gtk.TreeIter selected_iter; |
637 | + private Gtk.Button AppendRequest; |
638 | + private RestfulData Db; |
639 | + private InputDialog Name; |
640 | + private RequestGui _RequestGui; |
641 | + private Gtk.TreeSelection _Selection; |
642 | + private Gtk.Menu menuSystem; |
643 | + private Gtk.MenuItem delete_project; |
644 | + private Gtk.MenuItem delete_request; |
645 | + |
646 | + public Project (RequestGui requestGui , Gtk.Button appendRequest, RestfulData db) { |
647 | + IsProject = false; |
648 | + AppendRequest = appendRequest; |
649 | + Db = db; |
650 | + _RequestGui = requestGui; |
651 | + var selected = this.get_selection (); |
652 | + selected.changed.connect (this.SeletedRow); |
653 | + this.headers_visible = false; |
654 | + menuSystem = new Gtk.Menu(); |
655 | + delete_project = new Gtk.MenuItem.with_label ("Delete project"); |
656 | + delete_request = new Gtk.MenuItem.with_label ("Delete request"); |
657 | + delete_project.activate.connect (() => { |
658 | + DeleteProjectRequest(); |
659 | + }); |
660 | + delete_request.activate.connect (() => { |
661 | + DeleteProjectRequest(); |
662 | + }); |
663 | + menuSystem.add(delete_project); |
664 | + menuSystem.add(delete_request); |
665 | + menuSystem.show(); |
666 | + |
667 | + this.button_press_event.connect((w, event) => { |
668 | + if (event.button == 3) { |
669 | + return PopUpHandler (menuSystem, event); |
670 | + } |
671 | + return false; |
672 | + }); |
673 | + this.popup_menu.connect((event) => { |
674 | + return PopUpHandler (menuSystem, null); |
675 | + }); |
676 | + |
677 | + ProjectStore = new Gtk.TreeStore (1, typeof (string)); |
678 | + this.set_model (ProjectStore); |
679 | + this.insert_column_with_attributes (-1, "Projects", new Gtk.CellRendererText (), "text", 0, null); |
680 | + if (Db.RetrieveProject()) { |
681 | + foreach (string project in Db.ProjectList) { |
682 | + ProjectStore.append (out ProjectParent, null); |
683 | + ProjectStore.set (ProjectParent, 0, project, -1); |
684 | + if(Db.RetrieveRequestByProject(project)){ |
685 | + foreach (string request in Db.RequestList) { |
686 | + ProjectStore.append (out RequestChild, ProjectParent); |
687 | + ProjectStore.set (RequestChild, 0, request, -1); |
688 | + } |
689 | + foreach (string request in Db.RequestList) |
690 | + Db.RequestList.remove(request); |
691 | + } |
692 | + } |
693 | + foreach (string project in Db.ProjectList) |
694 | + Db.RequestList.remove(project); |
695 | + } |
696 | + } |
697 | + |
698 | + private bool PopUpHandler (Gtk.Menu popup, Gdk.EventButton? eb) { |
699 | + |
700 | + if (Db.CountProject() > 0) { |
701 | + if (eb.button == 3) { |
702 | + Gtk.TreePath path; |
703 | + Gtk.TreeViewColumn column; |
704 | + int mousex = (int) eb.x; |
705 | + int mousey = (int) eb.y; |
706 | + int x; |
707 | + int y; |
708 | + int a; |
709 | + int b; |
710 | + convert_tree_to_bin_window_coords (mousex, mousey, out a, out b); |
711 | + if (get_path_at_pos (a, b, out path, out column, out x, out y)) |
712 | + set_cursor (path, column, true); |
713 | + popup.popup (null, null, null, eb.button, eb.time); |
714 | + return true; |
715 | + } |
716 | + } |
717 | + return false; |
718 | + } |
719 | + |
720 | + public void ItemNewProject() { |
721 | + Name = new InputDialog(_RequestGui._MainWindow); |
722 | + Name.show_all (); |
723 | + |
724 | + Name.input.activate.connect (() => { |
725 | + if (Name.input.get_text() == "") { |
726 | + CheckInputEmptyOrDuplicat(true); |
727 | + return; |
728 | + } else if (AddProject(Name.input.get_text())) { |
729 | + Name.close(); |
730 | + return; |
731 | + } else { |
732 | + CheckInputEmptyOrDuplicat(false); |
733 | + return; |
734 | + } |
735 | + }); |
736 | + } |
737 | + |
738 | + private bool AddProject(string name) { |
739 | + if (Db.CheckDuplicatProject(name)) return false; |
740 | + if(!Db.RegisterProject(name)) return false; |
741 | + ProjectStore.append (out ProjectParent, null); |
742 | + ProjectStore.set (ProjectParent, 0, name, -1); |
743 | + return true; |
744 | + } |
745 | + |
746 | + public void DeleteProjectRequest() { |
747 | + GLib.Value val; |
748 | + Gtk.TreeModel model; |
749 | + Gtk.TreeIter iter; |
750 | + if (_Selection.get_selected (out model, out selected_iter)) { |
751 | + ProjectStore.get_value(selected_iter, 0, out val); |
752 | + if (!model.iter_parent (out iter, selected_iter)) { |
753 | + if(Db.DeleteProject((string) val)) |
754 | + ProjectStore.remove(ref selected_iter); |
755 | + } else { |
756 | + if(Db.DeleteRequest((string) val)) |
757 | + ProjectStore.remove(ref selected_iter); |
758 | + } |
759 | + } |
760 | + } |
761 | + |
762 | + public void SeletedRow(Gtk.TreeSelection selection) { |
763 | + Gtk.TreeModel model; |
764 | + Gtk.TreeIter iter; |
765 | + _Selection = selection; |
766 | + Value val; |
767 | + if (_Selection.get_selected (out model, out selected_iter)) { |
768 | + ProjectStore.get_value( selected_iter, 0, out val); |
769 | + SelectedRow = (string)val; |
770 | + if (model.iter_parent (out iter, selected_iter)) { |
771 | + IsProject = false; |
772 | + delete_project.hide(); |
773 | + delete_request.show(); |
774 | + AppendRequest.set_sensitive(false); |
775 | + if (Db.RetrieveRequestData(SelectedRow)) |
776 | + _RequestGui.FillWidgets(Db.RequestDataList); |
777 | + else |
778 | + _RequestGui.BackToTheZero(); |
779 | + } else { |
780 | + delete_request.hide(); |
781 | + delete_project.show(); |
782 | + IsProject = true; |
783 | + _RequestGui.BackToTheZero(); |
784 | + } |
785 | + } |
786 | + } |
787 | + |
788 | + public void AddRequest() { |
789 | + GLib.Value valProject; |
790 | + ProjectStore.get_value(selected_iter, 0, out valProject); |
791 | + Name = new InputDialog(_RequestGui._MainWindow); |
792 | + Name.show_all(); |
793 | + |
794 | + Name.input.activate.connect (() => { |
795 | + if (Name.input.get_text() == "") { |
796 | + Name.Warnning.set_text("Please fill the field"); |
797 | + CheckInputEmptyOrDuplicat(true); |
798 | + return; |
799 | + } else if (Db.DuplicatRequest((string)valProject, Name.input.get_text())){ |
800 | + CheckInputEmptyOrDuplicat(false); |
801 | + return; |
802 | + } else if (!Db.RegisterRequest( (string)valProject, Name.input.get_text(), _RequestGui.UrlText, _RequestGui.method, _RequestGui.DataText, _RequestGui.Header)) return ; |
803 | + Gtk.TreeIter project_iter; |
804 | + ProjectStore.append (out project_iter, selected_iter); |
805 | + ProjectStore.set (project_iter, 0, Name.input.get_text(), -1); |
806 | + Name.close(); |
807 | + }); |
808 | + } |
809 | + |
810 | + private void CheckInputEmptyOrDuplicat(bool emptyOrDuplicat) { |
811 | + if (emptyOrDuplicat) Name.Warnning.set_text("Please fill the field"); |
812 | + else Name.Warnning.set_text("Already exists"); |
813 | + Gdk.Color RED; |
814 | + Gdk.Color.parse("red", out RED); |
815 | + Name.Warnning.modify_fg(Gtk.StateType.NORMAL, RED); |
816 | + |
817 | + if (Name.VboxWarnning.get_children().length() == 1) |
818 | + Name.VboxWarnning.pack_start (Name.Warnning, false, false, 5); |
819 | + Name.Warnning.show(); |
820 | + } |
821 | + } |
822 | + |
823 | + public class InputDialog : Gtk.Dialog { |
824 | + public Gtk.Entry input; |
825 | + public Gtk.Label Warnning; |
826 | + public Gtk.Box VboxWarnning; |
827 | + |
828 | + public InputDialog(MainWindow mainWindow) { |
829 | + Object(title : "Restful-client", parent : mainWindow); |
830 | + this.set_default_size (50, 30); |
831 | + this.border_width = 5; |
832 | + this.window_position = Gtk.WindowPosition.CENTER; |
833 | + var hbox1 = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 5); |
834 | + VboxWarnning = new Gtk.Box (Gtk.Orientation.VERTICAL, 1); |
835 | + var vbox1 = get_content_area () as Gtk.Box; |
836 | + input = new Gtk.Entry(); |
837 | + Warnning = new Gtk.Label(""); |
838 | + Warnning.hide(); |
839 | + input.set_placeholder_text("Set a name"); |
840 | + VboxWarnning.pack_end (hbox1, true, false, 5); |
841 | + hbox1.pack_start (input, true, true, 5); |
842 | + vbox1.pack_start (VboxWarnning, false, false, 5); |
843 | + mainWindow.destroy.connect(this.Destroy); |
844 | + } |
845 | + |
846 | + private void Destroy() { |
847 | + this.destroy(); |
848 | + } |
849 | + } |
850 | + |
851 | + public class RestFullExportImportData { |
852 | + |
853 | + private Xml.TextWriter Writer; |
854 | + private List<string> XmlDataList; |
855 | + private RestfulData _DB; |
856 | + |
857 | + public RestFullExportImportData() { |
858 | + _DB = new RestfulData(); |
859 | + } |
860 | + |
861 | + public void ExportData(string path) { |
862 | + if (_DB.RetrieveProject()) { |
863 | + Writer = new Xml.TextWriter.filename (path + ".midorix"); |
864 | + Writer.set_indent (true); |
865 | + Writer.set_indent_string ("\t"); |
866 | + Writer.start_document (); |
867 | + Writer.start_element ("Projects"); |
868 | + foreach(var l in _DB.ProjectList) { |
869 | + Writer.start_element ("Project"); |
870 | + Writer.start_attribute ("name"); |
871 | + Writer.write_string (l); |
872 | + Writer.end_attribute (); |
873 | + if (_DB.RetrieveRequestByProject(l)) { |
874 | + foreach(var r in _DB.RequestList) { |
875 | + Writer.start_element ("Request"); |
876 | + Writer.start_attribute ("name"); |
877 | + Writer.write_string (r); |
878 | + if (_DB.RetrieveRequestDataXml(r)) { |
879 | + Writer.write_element ("url", _DB.RequestDataList.nth_data(1)); |
880 | + Writer.write_element ("method", _DB.RequestDataList.nth_data(2)); |
881 | + if (_DB.RequestDataList.nth_data(1) == "") |
882 | + _DB.RequestDataList.nth(1).data = "empty"; |
883 | + Writer.write_element ("data", _DB.RequestDataList.nth_data(1)); |
884 | + Writer.write_element ("header", _DB.RequestDataList.nth_data(3)); |
885 | + Writer.write_element ("date", _DB.RequestDataList.nth_data(4)); |
886 | + } |
887 | + foreach (string request in _DB.RequestDataList) |
888 | + _DB.RequestDataList.remove(request); |
889 | + |
890 | + Writer.end_attribute (); |
891 | + Writer.end_element(); |
892 | + } |
893 | + foreach (string request in _DB.RequestList) |
894 | + _DB.RequestList.remove(request); |
895 | + } |
896 | + Writer.end_element(); |
897 | + } |
898 | + foreach (string request in _DB.ProjectList) |
899 | + _DB.ProjectList.remove(request); |
900 | + Writer.end_element(); |
901 | + Writer.end_document(); |
902 | + Writer.flush(); |
903 | + } |
904 | + } |
905 | + |
906 | + public void ImportData(string path) { |
907 | + |
908 | + Xml.Doc* doc = Xml.Parser.parse_file (path); |
909 | + XmlDataList = new List<string?>(); |
910 | + if (doc == null) { |
911 | + stdout.printf ("File %s not found or permissions missing\n", path); |
912 | + return; |
913 | + } |
914 | + |
915 | + Xml.Node* root = doc->get_root_element (); |
916 | + if (root == null) { |
917 | + stdout.printf ("WANTED! root\n"); |
918 | + delete doc; |
919 | + return; |
920 | + } |
921 | + |
922 | + if (root->name == "Projects") { |
923 | + RegsiterXmlProject (root); |
924 | + } else { |
925 | + stdout.printf ("Unexpected element %s\n", root->name); |
926 | + return; |
927 | + } |
928 | + delete doc; |
929 | + } |
930 | + private void FillXmlList (Xml.Node* node, string node_name) { |
931 | + assert (node->name == node_name); |
932 | + |
933 | + for (Xml.Node* iter = node->children; iter != null; iter = iter->next) { |
934 | + if (iter->type == Xml.ElementType.TEXT_NODE) { |
935 | + XmlDataList.append(iter->get_content ()); |
936 | + } else { |
937 | + stdout.printf ("Unexpected element %s\n", iter->name); |
938 | + return; |
939 | + } |
940 | + } |
941 | + } |
942 | + |
943 | + public void RegsiterXmlItem (Xml.Node* node) { |
944 | + assert (node->name == "Project"); |
945 | + |
946 | + string? id = node->get_prop ("name"); |
947 | + if (id != null) { |
948 | + _DB.RegisterProject(id); |
949 | + } else { |
950 | + stdout.puts ("Expected: <Project name=...\n"); |
951 | + return; |
952 | + } |
953 | + |
954 | + for (Xml.Node* iter = node->children; iter != null; iter = iter->next) { |
955 | + if (iter->type == Xml.ElementType.ELEMENT_NODE) { |
956 | + switch (iter->name) { |
957 | + case "Request": |
958 | + string? id_r = iter->get_prop ("name"); |
959 | + for (Xml.Node* iter_r = iter->children; iter_r != null; iter_r = iter_r->next) { |
960 | + if (iter_r->type == Xml.ElementType.ELEMENT_NODE) { |
961 | + switch (iter_r->name) { |
962 | + case "url": |
963 | + FillXmlList (iter_r, "url"); |
964 | + break; |
965 | + case "method": |
966 | + FillXmlList (iter_r, "method"); |
967 | + break; |
968 | + case "data": |
969 | + FillXmlList (iter_r, "data"); |
970 | + break; |
971 | + case "header": |
972 | + FillXmlList (iter_r, "header"); |
973 | + break; |
974 | + case "date": |
975 | + FillXmlList (iter_r, "date"); |
976 | + break; |
977 | + |
978 | + default: |
979 | + stdout.printf ("Unexpected element %s\n", iter_r->name); |
980 | + break; |
981 | + } |
982 | + } |
983 | + } |
984 | + if (_DB.RegisterRequest(id, id_r, XmlDataList.nth_data(0), XmlDataList.nth_data(1), XmlDataList.nth_data(2), XmlDataList.nth_data(3), XmlDataList.nth_data(4))) |
985 | + foreach (string request in XmlDataList) |
986 | + XmlDataList.remove(request); |
987 | + else |
988 | + return; |
989 | + break; |
990 | + default: |
991 | + stdout.printf ("Unexpected element %s\n", iter->name); |
992 | + break; |
993 | + } |
994 | + } |
995 | + } |
996 | + } |
997 | + |
998 | + public void RegsiterXmlProject (Xml.Node* node) { |
999 | + assert (node->name == "Projects"); |
1000 | + |
1001 | + for (Xml.Node* iter = node->children; iter != null; iter = iter->next) { |
1002 | + if (iter->type == Xml.ElementType.ELEMENT_NODE) { |
1003 | + if (iter->name == "Project") { |
1004 | + RegsiterXmlItem (iter); |
1005 | + } else { |
1006 | + stdout.printf ("Unexpected element %s\n", iter->name); |
1007 | + } |
1008 | + } |
1009 | + } |
1010 | + } |
1011 | + |
1012 | + } |
1013 | +} |
1014 | +public Midori.Extension extension_init () { |
1015 | + return new RestFul.Manager (); |
1016 | +} |
As an observation the coding style diverts a bit and uses Java style VariableNames rather than variableNames, though as always I consider this up to the extension author(s). The code is generally very clean and modular, which is nice.
Suggestions for improvement:
You could use Midori.Database API to simplify error handling and binding of parameters. Compiled statements are more efficient than concatenating queries on demand.
c.f. grep Midori.Database extensions
I didn't figure out how to create projects. I think the UX needs improvement.
A "Submit" button would seem like a nice idea, through trial and error I found out that hitting Enter in the text field makes a request, but it took me a moment.
^^ I think the projects UX is critical here for landing a first version, the other ones are optional.