Merge lp:~axlrose112/midori/midori-restful_client-feature into lp:midori

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
Reviewer Review Type Date Requested Status
Cris Dywan Needs Fixing
Review via email: mp+274045@code.launchpad.net

Description of the change

midori extension resful-client

To post a comment you must log in.
Revision history for this message
Cris Dywan (kalikiana) wrote :

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.

review: Needs Fixing
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+}

Subscribers

People subscribed via source and target branches

to all changes: