gnetvibes 0.4 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/gnetvibes +12 -12
- data/lib/gnetvibes.rb +5 -0
- data/lib/gnetvibes/config.rb +10 -17
- data/lib/gnetvibes/edit.rb +103 -0
- data/lib/gnetvibes/gfx/feed.gif +0 -0
- data/lib/gnetvibes/gfx/netvibes.png +0 -0
- data/lib/gnetvibes/gfx/todolist.gif +0 -0
- data/lib/gnetvibes/gfx/webnote.gif +0 -0
- data/lib/gnetvibes/icon.rb +166 -0
- data/lib/gnetvibes/login.rb +73 -0
- metadata +21 -8
- data/lib/gnetvibes/api.rb +0 -217
- data/lib/gnetvibes/gui.rb +0 -16
data/bin/gnetvibes
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
|
-
|
3
2
|
$KCODE = "U"
|
4
3
|
require 'rubygems'
|
4
|
+
require 'netvibes'
|
5
|
+
require 'gnetvibes'
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
require 'gnetvibes/config'
|
10
|
-
require 'gnetvibes/gui'
|
7
|
+
api = Netvibes.new
|
8
|
+
icon = GNetvibes::Icon.new(api)
|
11
9
|
|
12
|
-
|
13
|
-
|
10
|
+
if not api.load_credentials
|
11
|
+
login = GNetvibes::LoginDialog.new(api)
|
12
|
+
begin
|
13
|
+
login.run
|
14
|
+
rescue Exception
|
15
|
+
retry
|
16
|
+
end
|
17
|
+
end
|
14
18
|
|
15
|
-
api = GNetvibes::API.new
|
16
|
-
icon = GNetvibes::GUI::Icon.new(api)
|
17
|
-
login = GNetvibes::GUI::LoginDialog.new(api).run
|
18
19
|
icon.refresh
|
19
|
-
|
20
20
|
Gtk.main
|
data/lib/gnetvibes.rb
ADDED
data/lib/gnetvibes/config.rb
CHANGED
@@ -1,28 +1,21 @@
|
|
1
|
+
require 'digest/sha1'
|
1
2
|
module GNetvibes
|
2
3
|
NAME = 'gNetvibes'
|
3
|
-
VERSION = '0.
|
4
|
+
VERSION = '0.5'
|
4
5
|
COPYRIGHT = 'Copyright (C) 2007 Florent Solt'
|
5
6
|
DESC = 'Netvibes integration with the Gnome Desktop'
|
6
7
|
AUTHOR = 'Florent Solt'
|
7
8
|
EMAIL = 'florent@solt.biz'
|
8
|
-
HOMEPAGE = 'http://rubyforge.org
|
9
|
+
HOMEPAGE = 'http://gnetvibes.rubyforge.org'
|
10
|
+
LICENSE = 'BSD'
|
9
11
|
|
10
|
-
if
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
if defined? Gdk
|
13
|
+
# Icons
|
14
|
+
ICON = Gdk::Pixbuf.new(File.join(File.dirname(__FILE__), "gfx", "netvibes.png"))
|
15
|
+
ICON_POSTIT = Gdk::Pixbuf.new(File.join(File.dirname(__FILE__), "gfx", "webnote.gif"))
|
16
|
+
ICON_TODOLIST = Gdk::Pixbuf.new(File.join(File.dirname(__FILE__), "gfx", "todolist.gif"))
|
17
|
+
ICON_FEED = Gdk::Pixbuf.new(File.join(File.dirname(__FILE__), "gfx", "feed.gif"))
|
14
18
|
end
|
15
19
|
|
16
|
-
CACHE = File.join(HOME, "cache")
|
17
|
-
DATA = File.join(HOME, "data")
|
18
|
-
|
19
|
-
EXPIRE = 5 * 60
|
20
|
-
|
21
|
-
MODULES = {
|
22
|
-
:postit => 'PostIt',
|
23
|
-
:todolist => 'TodoList',
|
24
|
-
:feed => 'RssReader'
|
25
|
-
}
|
26
20
|
end
|
27
21
|
|
28
|
-
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module GNetvibes
|
2
|
+
class EditDialog < Gtk::Dialog
|
3
|
+
|
4
|
+
attr_reader :buf
|
5
|
+
|
6
|
+
def initialize(id)
|
7
|
+
super(NAME, nil, nil,
|
8
|
+
[Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT],
|
9
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_REJECT])
|
10
|
+
|
11
|
+
# TODO: implementer une methode @api.is_a_postit?(Hash)
|
12
|
+
if id == :postit
|
13
|
+
build_postit("Webnote", "Type your text here")
|
14
|
+
elsif id.is_a? Hash and id['name'] == Netvibes::MODULES[:postit]
|
15
|
+
build_postit(id['title'], id['data']['text'])
|
16
|
+
@delete = self.add_button(Gtk::Stock::DELETE, Gtk::Dialog::RESPONSE_APPLY)
|
17
|
+
elsif id.is_a? Hash and id['name'] == Netvibes::MODULES[:todolist]
|
18
|
+
build_todolist(id)
|
19
|
+
end
|
20
|
+
|
21
|
+
self.has_separator = false
|
22
|
+
self.set_default_size(400, 300)
|
23
|
+
|
24
|
+
self.border_width = 10
|
25
|
+
self.icon = ICON
|
26
|
+
self.modal = false
|
27
|
+
|
28
|
+
self.show_all
|
29
|
+
end
|
30
|
+
|
31
|
+
def update_tag(*args)
|
32
|
+
@buf.apply_tag(@tag_title, @buf.get_iter_at_line(0), @buf.get_iter_at_line(1))
|
33
|
+
@buf.apply_tag(@tag_normal, @buf.get_iter_at_line(1), @buf.get_iter_at_line(2))
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_postit(title, text)
|
37
|
+
# Text
|
38
|
+
@buf = Gtk::TextBuffer.new
|
39
|
+
@buf.signal_connect("changed", self, &method(:update_tag))
|
40
|
+
@txt = Gtk::TextView.new(@buf)
|
41
|
+
@txt.wrap_mode = Gtk::TextTag::WRAP_WORD
|
42
|
+
@txt.accepts_tab = false
|
43
|
+
@tag_title = @buf.create_tag('title', :font => "Sans Bold 12",
|
44
|
+
:pixels_below_lines => 7, :pixels_above_lines => 10,
|
45
|
+
:left_margin => 5)
|
46
|
+
@tag_normal = @buf.create_tag('normal', :font => "Sans 9",
|
47
|
+
:pixels_below_lines => 2, :pixels_above_lines => 0,
|
48
|
+
:left_margin => 10)
|
49
|
+
|
50
|
+
scroll = Gtk::ScrolledWindow.new
|
51
|
+
scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
|
52
|
+
scroll.add(@txt)
|
53
|
+
scroll.border_width = 1
|
54
|
+
frame = Gtk::Frame.new
|
55
|
+
frame.add(scroll)
|
56
|
+
self.vbox.pack_start(frame, true, true)
|
57
|
+
|
58
|
+
@buf.text = ''
|
59
|
+
iter = @buf.get_iter_at_offset(0)
|
60
|
+
@buf.insert(iter, title.strip + "\n", @tag_title)
|
61
|
+
@buf.insert(iter, text.strip + "\n", @tag_normal)
|
62
|
+
@txt.grab_focus
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_todolist(id)
|
66
|
+
list = []
|
67
|
+
id['data'].select{|k,v| k =~ /^tdval/}.each do |k,v|
|
68
|
+
i = k[/\d+$/].to_i
|
69
|
+
list[i] = { :title => v, :checked => id['data']["tdchk_#{i}"].to_i }
|
70
|
+
end
|
71
|
+
@treeview = Gtk::TreeView.new(Gtk::ListStore.new(Fixnum, String))
|
72
|
+
@treeview.headers_visible = false
|
73
|
+
|
74
|
+
render = Gtk::CellRendererToggle.new
|
75
|
+
render.mode = Gtk::CellRenderer::MODE_ACTIVATABLE
|
76
|
+
@column = Gtk::TreeViewColumn.new('Done?', render)
|
77
|
+
@column.set_cell_data_func(render) do |column, cell, model, iter|
|
78
|
+
cell.active = iter.get_value(0) != 0
|
79
|
+
end
|
80
|
+
render.signal_connect("toggled") do |cell, row|
|
81
|
+
@treeview.model.get_iter(row).set_value(0, cell.active? ? 0 : 1)
|
82
|
+
end
|
83
|
+
|
84
|
+
@treeview.append_column(@column)
|
85
|
+
@treeview.append_column(Gtk::TreeViewColumn.new('Title', Gtk::CellRendererText.new, :text => 1))
|
86
|
+
|
87
|
+
list.each do |item|
|
88
|
+
iter = @treeview.model.append
|
89
|
+
iter[0] = item[:checked]
|
90
|
+
iter[1] = item[:title]
|
91
|
+
end
|
92
|
+
|
93
|
+
scroll = Gtk::ScrolledWindow.new
|
94
|
+
scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
|
95
|
+
scroll.add(@treeview)
|
96
|
+
scroll.border_width = 1
|
97
|
+
frame = Gtk::Frame.new
|
98
|
+
frame.add(scroll)
|
99
|
+
self.vbox.pack_start(frame, true, true)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,166 @@
|
|
1
|
+
module GNetvibes
|
2
|
+
class Icon < Gtk::StatusIcon
|
3
|
+
|
4
|
+
attr_reader :buf
|
5
|
+
|
6
|
+
def initialize(api)
|
7
|
+
@api = api
|
8
|
+
|
9
|
+
super()
|
10
|
+
self.pixbuf = ICON
|
11
|
+
|
12
|
+
self.signal_connect('popup-menu') do |w, button, time|
|
13
|
+
unless @menu.nil?
|
14
|
+
@menu.popup(nil, nil, button, time) do
|
15
|
+
self.position_menu(@menu)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_menu
|
22
|
+
if @menu
|
23
|
+
@menu_items.to_a.each {|i| i.destroy}
|
24
|
+
@menu.destroy
|
25
|
+
end
|
26
|
+
@menu_items = []
|
27
|
+
@menu = Gtk::Menu.new
|
28
|
+
@menu_items << Gtk::SeparatorMenuItem.new
|
29
|
+
@menu.append(@menu_items.last)
|
30
|
+
|
31
|
+
@menu_items << Gtk::ImageMenuItem.new(Gtk::Stock::REFRESH)
|
32
|
+
@menu_items.last.signal_connect("activate", true, &method(:refresh))
|
33
|
+
@menu.append(@menu_items.last)
|
34
|
+
|
35
|
+
@menu_items << Gtk::ImageMenuItem.new(Gtk::Stock::QUIT)
|
36
|
+
@menu_items.last.signal_connect("activate") do
|
37
|
+
Gtk.main_quit
|
38
|
+
end
|
39
|
+
@menu.append(@menu_items.last)
|
40
|
+
@menu.show_all
|
41
|
+
end
|
42
|
+
|
43
|
+
def refresh(*args)
|
44
|
+
self.tooltip = "#{NAME} refreshing...";
|
45
|
+
build_menu
|
46
|
+
@api.refresh
|
47
|
+
|
48
|
+
@api.tabs.each do |id, tab|
|
49
|
+
|
50
|
+
# Title
|
51
|
+
submenutitle = Gtk::ImageMenuItem.new(tab['title'])
|
52
|
+
|
53
|
+
if not tab['icon'].strip.empty?
|
54
|
+
submenutitle.image = Gtk::Image.new(@api.download(tab['icon']))
|
55
|
+
end
|
56
|
+
submenu = Gtk::Menu.new
|
57
|
+
|
58
|
+
# Add item
|
59
|
+
@menu_items << Gtk::ImageMenuItem.new(Gtk::Stock::ADD)
|
60
|
+
@menu_items.last.signal_connect("activate", id, &method(:add))
|
61
|
+
submenu.append(@menu_items.last)
|
62
|
+
|
63
|
+
# Separator
|
64
|
+
if @api.tab_has?(id, :postit)
|
65
|
+
@menu_items << Gtk::SeparatorMenuItem.new
|
66
|
+
submenu.append(@menu_items.last)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Postit
|
70
|
+
@api.each_postit_in_tab(id) do |postit|
|
71
|
+
@menu_items << Gtk::ImageMenuItem.new(postit['title'])
|
72
|
+
@menu_items.last.image = Gtk::Image.new(ICON_POSTIT)
|
73
|
+
@menu_items.last.signal_connect("activate", postit['id'], &method(:edit))
|
74
|
+
submenu.append(@menu_items.last)
|
75
|
+
end
|
76
|
+
|
77
|
+
# TodoLists
|
78
|
+
@api.each_todo_list_in_tab(id) do |todolist|
|
79
|
+
title = todolist['data']['title'] || todolist['title']
|
80
|
+
@menu_items << Gtk::ImageMenuItem.new(title)
|
81
|
+
@menu_items.last.image = Gtk::Image.new(ICON_TODOLIST)
|
82
|
+
@menu_items.last.signal_connect("activate", todolist['id'], &method(:edit))
|
83
|
+
submenu.append(@menu_items.last)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Feeds
|
87
|
+
items = []
|
88
|
+
@api.each_feed_in_tab(id) do |feed|
|
89
|
+
next if feed['unread'].zero?
|
90
|
+
if feed['unread'] > 0
|
91
|
+
items << feed['title'] + " (#{feed['unread']})"
|
92
|
+
else
|
93
|
+
items << feed['title']
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
if not items.empty?
|
98
|
+
# Separator
|
99
|
+
if @api.tab_has?(id, :feed)
|
100
|
+
@menu_items << Gtk::SeparatorMenuItem.new
|
101
|
+
submenu.append(@menu_items.last)
|
102
|
+
end
|
103
|
+
|
104
|
+
items.each do |title|
|
105
|
+
@menu_items << Gtk::ImageMenuItem.new(title)
|
106
|
+
@menu_items.last.image = Gtk::Image.new(ICON_FEED)
|
107
|
+
submenu.append(@menu_items.last)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
submenutitle.submenu = submenu
|
112
|
+
@menu_items << submenu
|
113
|
+
@menu_items << submenutitle
|
114
|
+
@menu.prepend(submenutitle)
|
115
|
+
end
|
116
|
+
|
117
|
+
@menu.show_all
|
118
|
+
self.tooltip = NAME;
|
119
|
+
GC.start
|
120
|
+
end
|
121
|
+
|
122
|
+
def edit(obj, mod)
|
123
|
+
mod = @api.modules[mod.to_i]
|
124
|
+
dlg = EditDialog.new(mod)
|
125
|
+
begin
|
126
|
+
res = dlg.run
|
127
|
+
if res == Gtk::Dialog::RESPONSE_ACCEPT
|
128
|
+
@api.save(mod['id'], :title => dlg.buf.text[/^[^\n]*/],
|
129
|
+
:text => dlg.buf.text[/\n.*$/m].strip)
|
130
|
+
refresh
|
131
|
+
dlg.destroy
|
132
|
+
elsif res == Gtk::Dialog::RESPONSE_APPLY
|
133
|
+
confirm = Gtk::MessageDialog.new(dlg, Gtk::Dialog::MODAL,
|
134
|
+
Gtk::MessageDialog::QUESTION, Gtk::MessageDialog::BUTTONS_YES_NO,
|
135
|
+
"Are you sure ?")
|
136
|
+
if confirm.run == Gtk::Dialog::RESPONSE_YES
|
137
|
+
@api.delete(mod['id'])
|
138
|
+
refresh
|
139
|
+
dlg.destroy
|
140
|
+
confirm.destroy
|
141
|
+
else
|
142
|
+
confirm.destroy
|
143
|
+
raise 'Retry'
|
144
|
+
end
|
145
|
+
else
|
146
|
+
dlg.destroy
|
147
|
+
end
|
148
|
+
rescue RuntimeError
|
149
|
+
retry
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def add(obj, tab)
|
154
|
+
dlg = EditDialog.new(:postit)
|
155
|
+
res = dlg.run
|
156
|
+
if res == Gtk::Dialog::RESPONSE_ACCEPT
|
157
|
+
@api.add(tab, :postit, :title => dlg.buf.text[/^[^\n]*/],
|
158
|
+
:text => dlg.buf.text[/\n.*$/m].strip)
|
159
|
+
refresh
|
160
|
+
end
|
161
|
+
dlg.destroy
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module GNetvibes
|
2
|
+
class LoginDialog < Gtk::Dialog
|
3
|
+
|
4
|
+
attr_reader :login, :password
|
5
|
+
|
6
|
+
def initialize(api)
|
7
|
+
@api = api
|
8
|
+
|
9
|
+
# Login Dialog
|
10
|
+
super("#{NAME} Authentification", nil, Gtk::Dialog::MODAL,
|
11
|
+
[Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT],
|
12
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_REJECT])
|
13
|
+
self.icon = ICON
|
14
|
+
self.has_separator = false
|
15
|
+
self.resizable = false
|
16
|
+
|
17
|
+
@login = Gtk::Entry.new
|
18
|
+
@password = Gtk::Entry.new
|
19
|
+
@remember = Gtk::CheckButton.new('Remember me')
|
20
|
+
|
21
|
+
table = Gtk::Table.new(2, 3)
|
22
|
+
table.border_width = 5
|
23
|
+
image = Gtk::Image.new(Gtk::Stock::DIALOG_AUTHENTICATION, Gtk::IconSize::DIALOG)
|
24
|
+
table.attach(image, 0, 1, 0, 2, nil, nil, 10, 10)
|
25
|
+
|
26
|
+
table.attach_defaults(Gtk::Label.new("Login:"), 1, 2, 0, 1)
|
27
|
+
table.attach_defaults(@login, 2, 3, 0, 1)
|
28
|
+
|
29
|
+
table.attach_defaults(Gtk::Label.new("Password:"), 1, 2, 1, 2)
|
30
|
+
@password.visibility = false
|
31
|
+
table.attach_defaults(@password, 2, 3, 1, 2)
|
32
|
+
self.vbox.pack_start(table)
|
33
|
+
self.signal_connect("key_release_event", &method(:login))
|
34
|
+
|
35
|
+
table.attach_defaults(@remember, 2, 3, 2, 3)
|
36
|
+
|
37
|
+
if not ARGV.empty?
|
38
|
+
@login.editable = false
|
39
|
+
@login.text = ARGV.shift
|
40
|
+
@password.text = ARGV.shift if not ARGV.empty?
|
41
|
+
@password.grab_focus
|
42
|
+
else
|
43
|
+
@login.grab_focus
|
44
|
+
end
|
45
|
+
|
46
|
+
self.signal_connect('response', &method(:login))
|
47
|
+
self.show_all
|
48
|
+
end
|
49
|
+
|
50
|
+
def login(obj, ev)
|
51
|
+
if (ev.is_a? Gdk::EventKey and (ev.keyval == Gdk::Keyval::GDK_KP_Enter or ev.keyval == Gdk::Keyval::GDK_Return)) or
|
52
|
+
(ev.is_a? Fixnum and ev == Gtk::Dialog::RESPONSE_ACCEPT)
|
53
|
+
|
54
|
+
if @api.login(@login.text, @password.text)
|
55
|
+
@api.save_credentials if @remember.active?
|
56
|
+
self.destroy
|
57
|
+
else
|
58
|
+
dlg = Gtk::MessageDialog.new(nil, Gtk::Dialog::MODAL,
|
59
|
+
Gtk::MessageDialog::ERROR, Gtk::MessageDialog::BUTTONS_OK,
|
60
|
+
"\nBad email or password\nLogin failed")
|
61
|
+
dlg.run
|
62
|
+
dlg.destroy
|
63
|
+
raise "Login Failed"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
if (ev.is_a? Fixnum and ev == Gtk::Dialog::RESPONSE_REJECT)
|
68
|
+
exit!
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
metadata
CHANGED
@@ -3,13 +3,13 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: gnetvibes
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: "0.
|
7
|
-
date: 2007-03-
|
6
|
+
version: "0.5"
|
7
|
+
date: 2007-03-23 00:00:00 +01:00
|
8
8
|
summary: Netvibes integration with the Gnome Desktop
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email: florent@solt.biz
|
12
|
-
homepage: http://rubyforge.org
|
12
|
+
homepage: http://gnetvibes.rubyforge.org
|
13
13
|
rubyforge_project:
|
14
14
|
description:
|
15
15
|
autorequire:
|
@@ -29,10 +29,15 @@ post_install_message:
|
|
29
29
|
authors:
|
30
30
|
- Florent Solt
|
31
31
|
files:
|
32
|
-
- lib/gnetvibes/api.rb
|
33
|
-
- lib/gnetvibes/gui.rb
|
34
32
|
- lib/gnetvibes/config.rb
|
35
|
-
- lib/gnetvibes/
|
33
|
+
- lib/gnetvibes/edit.rb
|
34
|
+
- lib/gnetvibes/icon.rb
|
35
|
+
- lib/gnetvibes/login.rb
|
36
|
+
- lib/gnetvibes/gfx/netvibes.png
|
37
|
+
- lib/gnetvibes/gfx/todolist.gif
|
38
|
+
- lib/gnetvibes/gfx/feed.gif
|
39
|
+
- lib/gnetvibes/gfx/webnote.gif
|
40
|
+
- lib/gnetvibes.rb
|
36
41
|
test_files: []
|
37
42
|
|
38
43
|
rdoc_options: []
|
@@ -45,5 +50,13 @@ extensions: []
|
|
45
50
|
|
46
51
|
requirements: []
|
47
52
|
|
48
|
-
dependencies:
|
49
|
-
|
53
|
+
dependencies:
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: netvibes
|
56
|
+
version_requirement:
|
57
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0.5"
|
62
|
+
version:
|
data/lib/gnetvibes/api.rb
DELETED
@@ -1,217 +0,0 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
require 'uri'
|
3
|
-
require 'cgi'
|
4
|
-
require 'digest/sha1'
|
5
|
-
|
6
|
-
module GNetvibes
|
7
|
-
class API
|
8
|
-
|
9
|
-
URL = {
|
10
|
-
:base => URI.parse('http://www.netvibes.com'),
|
11
|
-
:login => URI.parse('http://www.netvibes.com/user/signIn.php'),
|
12
|
-
:data => URI.parse('http://www.netvibes.com/get/userData.php'),
|
13
|
-
:save => URI.parse('http://www.netvibes.com/save/userData.php')
|
14
|
-
}
|
15
|
-
|
16
|
-
attr_reader :tabs, :modules
|
17
|
-
|
18
|
-
def initialize
|
19
|
-
@active_user_id = nil
|
20
|
-
@active_session_id = nil
|
21
|
-
@data = nil
|
22
|
-
@key = nil
|
23
|
-
@email = nil
|
24
|
-
@tabs = nil
|
25
|
-
@modules == nil
|
26
|
-
end
|
27
|
-
|
28
|
-
def login(email, password)
|
29
|
-
email.strip!
|
30
|
-
password.strip!
|
31
|
-
res = Net::HTTP.post_form(URL[:login], {'email'=> email, 'password' => password})
|
32
|
-
if res.body.strip == 'success'
|
33
|
-
@key = Digest::SHA1.new(email + password).hexdigest
|
34
|
-
@email = email
|
35
|
-
cookies = CGI::Cookie::parse(res['set-cookie'])
|
36
|
-
@active_user_id = cookies['activeUserID'].value
|
37
|
-
@active_session_id = cookies['activeSessionID'].value
|
38
|
-
refresh
|
39
|
-
true
|
40
|
-
else
|
41
|
-
false
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def save(id, options)
|
46
|
-
mod = @modules[id.to_i]
|
47
|
-
case mod['name']
|
48
|
-
when /PostIt/i
|
49
|
-
mod['title'] = options[:title]
|
50
|
-
mod['data']['text'] = options[:text]
|
51
|
-
mod['mode'] = 'module'
|
52
|
-
else
|
53
|
-
raise 'Not implemented'
|
54
|
-
end
|
55
|
-
Net::HTTP.start(URL[:data].host, URL[:data].port) do |http|
|
56
|
-
headers = {'Cookie' => "activeUserID=#{@active_user_id}; activeSessionID=#{@active_session_id}"}
|
57
|
-
post = Net::HTTP::Post.new(URL[:save].path, headers)
|
58
|
-
post.set_form_data(module2post(mod))
|
59
|
-
http.request(post)
|
60
|
-
end
|
61
|
-
dump
|
62
|
-
end
|
63
|
-
|
64
|
-
def add(tab_id, type, options)
|
65
|
-
case type
|
66
|
-
when /PostIt/i
|
67
|
-
mod = { 'history' => nil, 'mode' => 'new', 'moduleRndld' => rand,
|
68
|
-
'name' => 'PostIt', 'share' => 0, 'status' => 1, 'tab' => tab_id.to_i,
|
69
|
-
'title' => options[:title], 'data' => { 'color' => 'yellow', 'text' => options[:text]}}
|
70
|
-
else
|
71
|
-
raise 'Not implemented'
|
72
|
-
end
|
73
|
-
Net::HTTP.start(URL[:data].host, URL[:data].port) do |http|
|
74
|
-
headers = {'Cookie' => "activeUserID=#{@active_user_id}; activeSessionID=#{@active_session_id}"}
|
75
|
-
post = Net::HTTP::Post.new(URL[:save].path, headers)
|
76
|
-
post.set_form_data(module2post(mod))
|
77
|
-
http.request(post)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def delete(mod)
|
82
|
-
mod = mod.to_i
|
83
|
-
Net::HTTP.start(URL[:data].host, URL[:data].port) do |http|
|
84
|
-
headers = {'Cookie' => "activeUserID=#{@active_user_id}; activeSessionID=#{@active_session_id}"}
|
85
|
-
post = Net::HTTP::Post.new(URL[:save].path, headers)
|
86
|
-
post.set_form_data({'id' => mod, 'mode' => 'close'})
|
87
|
-
http.request(post)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def refresh(force = false)
|
92
|
-
if force or not load
|
93
|
-
res = Net::HTTP.start(URL[:data].host, URL[:data].port) do |http|
|
94
|
-
headers = {'Cookie' => "activeUserID=#{@active_user_id}; activeSessionID=#{@active_session_id}"}
|
95
|
-
http.request(Net::HTTP::Get.new(URL[:data].path, headers))
|
96
|
-
end
|
97
|
-
@data = json2ruby(unicode(res.body))
|
98
|
-
dump
|
99
|
-
end
|
100
|
-
@tabs = {}
|
101
|
-
@modules = {}
|
102
|
-
@data['tabs'].each { |t| @tabs[t['id'].to_i] = t }
|
103
|
-
@data['modules'].each { |m| @modules[m['id'].to_i] = m }
|
104
|
-
true
|
105
|
-
end
|
106
|
-
|
107
|
-
def tab_has?(tab, kind)
|
108
|
-
raise "Unknown module : #{kind}" if not MODULES.keys.include?(kind)
|
109
|
-
@data['modules'].select{|m| m['tab'].to_i == tab.to_i}.each do |m|
|
110
|
-
return true if MODULES[kind] == m['name']
|
111
|
-
end
|
112
|
-
false
|
113
|
-
end
|
114
|
-
|
115
|
-
def each_modules_in_tab(tab, kind = nil)
|
116
|
-
raise "Unknown module : #{kind}" if not MODULES.keys.include?(kind)
|
117
|
-
@data['modules'].select{|m| m['tab'].to_i == tab.to_i}.each do |m|
|
118
|
-
yield m if kind.nil? or MODULES[kind] == m['name']
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def each_webnote_in_tab(tab)
|
123
|
-
each_modules_in_tab(tab, :postit) { |m| yield m }
|
124
|
-
end
|
125
|
-
|
126
|
-
def each_todo_list_in_tab(tab)
|
127
|
-
each_modules_in_tab(tab, :todolist) { |m| yield m }
|
128
|
-
end
|
129
|
-
|
130
|
-
def each_feed_in_tab(tab)
|
131
|
-
each_modules_in_tab(tab, :feed) do |feed|
|
132
|
-
history = feed['data']['history']
|
133
|
-
feed['unread'] = history.split(':')[1..-1].select { |e| e == "0" }.size rescue feed['data']['nbTitles'].to_i
|
134
|
-
p feed
|
135
|
-
yield feed
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def each_todo_list_item(mod)
|
140
|
-
raise "Module is not a TodoList" if @modules[mod.to_i]['name'] != MODULES[:todolist]
|
141
|
-
list = []
|
142
|
-
data = @modules[mod.to_i]['data']
|
143
|
-
data.select{|k,v| k =~ /^tdval/}.each do |k,v|
|
144
|
-
id = k[/\d+$/].to_i
|
145
|
-
list[id] = { 'title' => v, 'checked' => data["tdchk_#{id}"].to_i == 1 }
|
146
|
-
end
|
147
|
-
list.each do |i|
|
148
|
-
yield i
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def dump
|
153
|
-
File.open(File.join(DATA, @email), 'w') do |fd|
|
154
|
-
fd.write crypt(@data.inspect, @key)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def load
|
159
|
-
file = File.join(DATA, @email)
|
160
|
-
if File.exists?(file) and File.mtime(file) > Time.now - EXPIRE
|
161
|
-
@data = eval(crypt(File.read(file), @key))
|
162
|
-
true
|
163
|
-
else
|
164
|
-
false
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def download(uri)
|
169
|
-
if uri =~ /^\//
|
170
|
-
url = URL[:base] + uri
|
171
|
-
else
|
172
|
-
url = URI.parse(uri)
|
173
|
-
end
|
174
|
-
req = Net::HTTP::Get.new(url.path)
|
175
|
-
res = Net::HTTP.start(url.host, url.port) {|http| http.request(req) }
|
176
|
-
res.body
|
177
|
-
end
|
178
|
-
|
179
|
-
private
|
180
|
-
|
181
|
-
UNICODE_TABLE = {
|
182
|
-
'u00b0' => '°', 'u00e0' => 'à', 'u00e2' => 'â', 'u00e7' => 'ç',
|
183
|
-
'u00e8' => 'è', 'u00e9' => 'é', 'u00ea' => 'ê', 'u00ee' => 'î',
|
184
|
-
'u00f4' => 'ô', 'u2019' => '\''
|
185
|
-
}
|
186
|
-
|
187
|
-
def unicode(s)
|
188
|
-
s.gsub(/u[a-f\d]{4}/) { |c| UNICODE_TABLE[c] }
|
189
|
-
end
|
190
|
-
|
191
|
-
def json2ruby(s)
|
192
|
-
eval(s.gsub(/:null/, ':nil').gsub(/":/, '"=>'))
|
193
|
-
end
|
194
|
-
|
195
|
-
def crypt(buf, key)
|
196
|
-
buf = buf.unpack("c*")
|
197
|
-
key = key.unpack("c*")
|
198
|
-
key *= buf.size / key.size + 1
|
199
|
-
buf.zip(key).collect{|c1,c2| c1^c2}.pack("c*")
|
200
|
-
end
|
201
|
-
|
202
|
-
def module2post(mod)
|
203
|
-
post = {}
|
204
|
-
mod.each do |k,v|
|
205
|
-
if v.is_a? Hash
|
206
|
-
v.each do |subk, subv|
|
207
|
-
post["arr#{k.capitalize}[#{subk}]"] = subv
|
208
|
-
end
|
209
|
-
else
|
210
|
-
post[k] = v
|
211
|
-
end
|
212
|
-
end
|
213
|
-
post
|
214
|
-
end
|
215
|
-
|
216
|
-
end
|
217
|
-
end
|
data/lib/gnetvibes/gui.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'digest/sha1'
|
2
|
-
require 'gnetvibes/api'
|
3
|
-
require 'gnetvibes/gui/icon'
|
4
|
-
require 'gnetvibes/gui/dlg_edit'
|
5
|
-
require 'gnetvibes/gui/dlg_login'
|
6
|
-
|
7
|
-
module GNetvibes
|
8
|
-
module GUI
|
9
|
-
|
10
|
-
# Icons
|
11
|
-
ICON = Gdk::Pixbuf.new(File.join(File.dirname(__FILE__), "gui", "netvibes.png"))
|
12
|
-
ICON_WEBNOTE = Gdk::Pixbuf.new(File.join(File.dirname(__FILE__), "gui", "webnote.gif"))
|
13
|
-
ICON_FEED = Gdk::Pixbuf.new(File.join(File.dirname(__FILE__), "gui", "feed.gif"))
|
14
|
-
|
15
|
-
end
|
16
|
-
end
|