gnetvibes 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/gnetvibes +18 -0
- data/lib/gnetvibes/api.rb +117 -0
- data/lib/gnetvibes/callback.rb +72 -0
- data/lib/gnetvibes/config.rb +15 -0
- data/lib/gnetvibes/gui.rb +109 -0
- data/lib/gnetvibes/ico.png +0 -0
- data/lib/gnetvibes/webnote.gif +0 -0
- metadata +51 -0
data/bin/gnetvibes
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$KCODE = "U"
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
require 'fileutils'
|
7
|
+
require 'gtk2'
|
8
|
+
|
9
|
+
require 'gnetvibes/config'
|
10
|
+
require 'gnetvibes/gui'
|
11
|
+
|
12
|
+
FileUtils.mkdir_p(GNetvibes::CACHE) if not File.exists?(GNetvibes::CACHE)
|
13
|
+
|
14
|
+
gui = GNetvibes::GUI.new
|
15
|
+
gui.login_dialog.show_all
|
16
|
+
gui.login_dialog.run
|
17
|
+
|
18
|
+
Gtk.main
|
@@ -0,0 +1,117 @@
|
|
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
|
+
end
|
24
|
+
|
25
|
+
def login(email, password)
|
26
|
+
res = Net::HTTP.post_form(URL[:login], {'email'=> email.strip, 'password' => password.strip})
|
27
|
+
if res.body.strip == 'success'
|
28
|
+
@key = Digest::SHA1.new(email + password).hexdigest
|
29
|
+
cookies = CGI::Cookie::parse(res['set-cookie'])
|
30
|
+
@active_user_id = cookies['activeUserID'].value
|
31
|
+
@active_session_id = cookies['activeSessionID'].value
|
32
|
+
refresh
|
33
|
+
true
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def refresh(force = false)
|
40
|
+
if not force and File.exists?(DATA) and File.mtime(DATA) > Time.now - EXPIRE
|
41
|
+
@data = eval(crypt(File.read(DATA), @key))
|
42
|
+
else
|
43
|
+
res = Net::HTTP.start(URL[:data].host, URL[:data].port) do |http|
|
44
|
+
headers = {'Cookie' => "activeUserID=#{@active_user_id}; activeSessionID=#{@active_session_id}"}
|
45
|
+
http.request(Net::HTTP::Get.new(URL[:data].path, headers))
|
46
|
+
end
|
47
|
+
@data = json2ruby(unicode(res.body))
|
48
|
+
dump
|
49
|
+
end
|
50
|
+
@tabs = {}
|
51
|
+
@modules = {}
|
52
|
+
@data['tabs'].each { |t| @tabs[t['id'].to_i] = t }
|
53
|
+
@data['modules'].each { |m| @modules[m['id'].to_i] = m }
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def each_modules_in_tab(tab)
|
58
|
+
@data['modules'].select{|m| m['tab'].to_i == tab.to_i}.each do |m|
|
59
|
+
yield m
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def each_todo_list_item(mod)
|
64
|
+
raise "Module is not a TodoList" if @modules[mod.to_i]['name'] != 'TodoList'
|
65
|
+
list = []
|
66
|
+
data = @modules[mod.to_i]['data']
|
67
|
+
data.select{|k,v| k =~ /^tdval/}.each do |k,v|
|
68
|
+
id = k[/\d+$/].to_i
|
69
|
+
list[id] = { 'title' => v, 'checked' => data["tdchk_#{id}"].to_i == 1 }
|
70
|
+
end
|
71
|
+
list.each do |i|
|
72
|
+
yield i
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def dump
|
77
|
+
File.open(DATA, 'w') do |fd|
|
78
|
+
fd.write crypt(@data.inspect, @key)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def download(uri)
|
83
|
+
if uri =~ /^\//
|
84
|
+
url = URL[:base] + uri
|
85
|
+
else
|
86
|
+
url = URI.parse(uri)
|
87
|
+
end
|
88
|
+
req = Net::HTTP::Get.new(url.path)
|
89
|
+
res = Net::HTTP.start(url.host, url.port) {|http| http.request(req) }
|
90
|
+
res.body
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
UNICODE_TABLE = {
|
96
|
+
'u00b0' => '°', 'u00e0' => 'à', 'u00e2' => 'â', 'u00e7' => 'ç',
|
97
|
+
'u00e8' => 'è', 'u00e9' => 'é', 'u00ea' => 'ê', 'u00ee' => 'î',
|
98
|
+
'u00f4' => 'ô', 'u2019' => '\''
|
99
|
+
}
|
100
|
+
|
101
|
+
def unicode(s)
|
102
|
+
s.gsub(/u[a-f\d]{4}/) { |c| UNICODE_TABLE[c] }
|
103
|
+
end
|
104
|
+
|
105
|
+
def json2ruby(s)
|
106
|
+
eval(s.gsub(/":/, '"=>'))
|
107
|
+
end
|
108
|
+
|
109
|
+
def crypt(buf, key)
|
110
|
+
buf = buf.unpack("c*")
|
111
|
+
key = key.unpack("c*")
|
112
|
+
key *= buf.size / key.size + 1
|
113
|
+
buf.zip(key).collect{|c1,c2| c1^c2}.pack("c*")
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'gnetvibes/api'
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
module GNetvibes
|
5
|
+
class GUI
|
6
|
+
|
7
|
+
def edit(obj, mod)
|
8
|
+
mod = @api.modules[mod.to_i]
|
9
|
+
@title.text = mod['title']
|
10
|
+
@buf.text = mod['data']['text'].strip
|
11
|
+
@txt.grab_focus
|
12
|
+
@edit_dialog.show_all
|
13
|
+
@edit_dialog.run
|
14
|
+
end
|
15
|
+
|
16
|
+
def save(obj, ev)
|
17
|
+
if ev == Gtk::Dialog::RESPONSE_ACCEPT
|
18
|
+
puts "save"
|
19
|
+
end
|
20
|
+
@edit_dialog.hide_on_delete
|
21
|
+
end
|
22
|
+
|
23
|
+
def login(obj, ev)
|
24
|
+
if (ev.is_a? Gdk::EventKey and (ev.keyval == Gdk::Keyval::GDK_KP_Enter or ev.keyval == Gdk::Keyval::GDK_Return)) or
|
25
|
+
(ev.is_a? Fixnum and ev == Gtk::Dialog::RESPONSE_ACCEPT)
|
26
|
+
if @api.login(@login.text, @password.text)
|
27
|
+
refresh
|
28
|
+
@login_dialog.destroy
|
29
|
+
else
|
30
|
+
raise "Login failed"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if (ev.is_a? Fixnum and ev == Gtk::Dialog::RESPONSE_REJECT)
|
35
|
+
exit!
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def refresh
|
40
|
+
@tray.tooltip = "#{GNetvibes::NAME} refreshing...";
|
41
|
+
|
42
|
+
@api.tabs.each do |id, tab|
|
43
|
+
icon = File.join(CACHE, Digest::SHA1.new(tab['icon']).hexdigest)
|
44
|
+
if not File.exists?(icon)
|
45
|
+
File.open(icon, 'w') do |fd|
|
46
|
+
fd.puts @api.download(tab['icon'])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
submenutitle = Gtk::ImageMenuItem.new(tab['title'])
|
51
|
+
submenutitle.image = Gtk::Image.new(icon)
|
52
|
+
|
53
|
+
submenu = Gtk::Menu.new
|
54
|
+
@api.each_modules_in_tab(id) do |mod|
|
55
|
+
case mod['name']
|
56
|
+
when 'PostIt'
|
57
|
+
item = Gtk::ImageMenuItem.new(mod['title'])
|
58
|
+
item.image = Gtk::Image.new(@icon_webnote)
|
59
|
+
item.signal_connect("activate", mod['id'], &method(:edit))
|
60
|
+
submenu.append(item)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
submenutitle.submenu = submenu
|
64
|
+
@menu.prepend(submenutitle)
|
65
|
+
end
|
66
|
+
|
67
|
+
@menu.show_all
|
68
|
+
@tray.tooltip = GNetvibes::NAME;
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module GNetvibes
|
2
|
+
NAME = 'gNetvibes'
|
3
|
+
VERSION = '0.1'
|
4
|
+
COPYRIGHT = 'Copyright (C) 2007 Florent Solt'
|
5
|
+
DESC = 'Netvibes integration with the Gnome Desktop'
|
6
|
+
AUTHOR = 'Florent Solt'
|
7
|
+
EMAIL = 'florent@solt.biz'
|
8
|
+
HOMEPAGE = 'http://rubyforge.org/projects/gnetvibes/'
|
9
|
+
|
10
|
+
CACHE = File.join(ENV['HOME'], ".#{GNetvibes::NAME.downcase}", "cache")
|
11
|
+
DATA = File.join(ENV['HOME'], ".#{GNetvibes::NAME.downcase}", "data")
|
12
|
+
EXPIRE = 5 * 60
|
13
|
+
end
|
14
|
+
|
15
|
+
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'gnetvibes/callback'
|
2
|
+
|
3
|
+
module GNetvibes
|
4
|
+
class GUI
|
5
|
+
|
6
|
+
attr_reader :login_dialog, :edit_dialog
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
# API
|
10
|
+
@api = GNetvibes::API.new
|
11
|
+
|
12
|
+
# Icons
|
13
|
+
@icon = Gdk::Pixbuf.new(File.join(File.dirname(__FILE__), "ico.png"))
|
14
|
+
@icon_webnote = Gdk::Pixbuf.new(File.join(File.dirname(__FILE__), "webnote.gif"))
|
15
|
+
|
16
|
+
# Login Dialog
|
17
|
+
@login_dialog = Gtk::Dialog.new("#{GNetvibes::NAME} Authentification", nil, Gtk::Dialog::MODAL,
|
18
|
+
[Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT],
|
19
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_REJECT])
|
20
|
+
@login_dialog.icon = @icon
|
21
|
+
@login_dialog.resizable = false
|
22
|
+
|
23
|
+
@login = Gtk::Entry.new
|
24
|
+
@password = Gtk::Entry.new
|
25
|
+
|
26
|
+
table = Gtk::Table.new(2, 3)
|
27
|
+
table.border_width = 5
|
28
|
+
image = Gtk::Image.new(Gtk::Stock::DIALOG_AUTHENTICATION, Gtk::IconSize::DIALOG)
|
29
|
+
table.attach(image, 0, 1, 0, 2, nil, nil, 10, 10)
|
30
|
+
|
31
|
+
table.attach_defaults(Gtk::Label.new("Login:"), 1, 2, 0, 1)
|
32
|
+
table.attach_defaults(@login, 2, 3, 0, 1)
|
33
|
+
|
34
|
+
table.attach_defaults(Gtk::Label.new("Password:"), 1, 2, 1, 2)
|
35
|
+
@password.visibility = false
|
36
|
+
@password.invisible_char = 0
|
37
|
+
table.attach_defaults(@password, 2, 3, 1, 2)
|
38
|
+
@login_dialog.vbox.pack_start(table)
|
39
|
+
@password.signal_connect("key_release_event", &method(:login))
|
40
|
+
|
41
|
+
if not ARGV.empty?
|
42
|
+
@login.editable = false
|
43
|
+
@login.text = ARGV.first
|
44
|
+
@password.grab_focus
|
45
|
+
else
|
46
|
+
@login.grab_focus
|
47
|
+
end
|
48
|
+
|
49
|
+
@login_dialog.signal_connect('response', &method(:login))
|
50
|
+
|
51
|
+
# Status Tray Icon
|
52
|
+
@tray = Gtk::StatusIcon.new
|
53
|
+
@tray.pixbuf = @icon
|
54
|
+
|
55
|
+
@menu = Gtk::Menu.new
|
56
|
+
@menu.append(Gtk::SeparatorMenuItem.new)
|
57
|
+
|
58
|
+
item = Gtk::ImageMenuItem.new(Gtk::Stock::REFRESH)
|
59
|
+
item.signal_connect("activate", &method(:refresh))
|
60
|
+
@menu.append(item)
|
61
|
+
|
62
|
+
item = Gtk::ImageMenuItem.new(Gtk::Stock::QUIT)
|
63
|
+
item.signal_connect("activate") do
|
64
|
+
Gtk.main_quit
|
65
|
+
end
|
66
|
+
@menu.append(item)
|
67
|
+
@menu.show_all
|
68
|
+
|
69
|
+
@tray.signal_connect('popup-menu') do |w, button, time|
|
70
|
+
@menu.popup(nil, nil, button, time) do
|
71
|
+
@tray.position_menu(@menu)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Text
|
76
|
+
@buf = Gtk::TextBuffer.new
|
77
|
+
@txt = Gtk::TextView.new(@buf)
|
78
|
+
|
79
|
+
@title = Gtk::Entry.new
|
80
|
+
|
81
|
+
# Edit Dialog
|
82
|
+
@edit_dialog = Gtk::Dialog.new(GNetvibes::NAME, nil, Gtk::Dialog::MODAL,
|
83
|
+
[Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT],
|
84
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_REJECT])
|
85
|
+
|
86
|
+
scroll = Gtk::ScrolledWindow.new
|
87
|
+
scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
|
88
|
+
scroll.add(@txt)
|
89
|
+
scroll.border_width = 1
|
90
|
+
|
91
|
+
frame = Gtk::Frame.new
|
92
|
+
frame.add(scroll)
|
93
|
+
|
94
|
+
@edit_dialog.set_default_size(300, 300)
|
95
|
+
@edit_dialog.signal_connect("delete-event") do
|
96
|
+
@edit_dialog.hide_on_delete
|
97
|
+
end
|
98
|
+
|
99
|
+
@edit_dialog.title = GNetvibes::NAME
|
100
|
+
@edit_dialog.modal = true
|
101
|
+
@edit_dialog.border_width = 10
|
102
|
+
@edit_dialog.icon = @icon
|
103
|
+
@edit_dialog.vbox.pack_start(@title, false, false)
|
104
|
+
@edit_dialog.vbox.pack_start(frame, true, true, 10)
|
105
|
+
@edit_dialog.signal_connect('response', &method(:save))
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
Binary file
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.2
|
3
|
+
specification_version: 1
|
4
|
+
name: gnetvibes
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.1"
|
7
|
+
date: 2007-03-17 00:00:00 +01:00
|
8
|
+
summary: Netvibes integration with the Gnome Desktop
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: florent@solt.biz
|
12
|
+
homepage: http://rubyforge.org/projects/gnetvibes/
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Florent Solt
|
31
|
+
files:
|
32
|
+
- lib/gnetvibes/callback.rb
|
33
|
+
- lib/gnetvibes/api.rb
|
34
|
+
- lib/gnetvibes/config.rb
|
35
|
+
- lib/gnetvibes/gui.rb
|
36
|
+
- lib/gnetvibes/ico.png
|
37
|
+
- lib/gnetvibes/webnote.gif
|
38
|
+
test_files: []
|
39
|
+
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
executables:
|
45
|
+
- gnetvibes
|
46
|
+
extensions: []
|
47
|
+
|
48
|
+
requirements: []
|
49
|
+
|
50
|
+
dependencies: []
|
51
|
+
|