revolution 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/extconf.rb +7 -0
- data/revolution.c +399 -0
- metadata +39 -0
data/extconf.rb
ADDED
data/revolution.c
ADDED
@@ -0,0 +1,399 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
|
3
|
+
#include "ruby.h"
|
4
|
+
|
5
|
+
#include "glib.h"
|
6
|
+
#include <glib-object.h>
|
7
|
+
|
8
|
+
#include <libebook/e-book.h>
|
9
|
+
#include <libecal/e-cal.h>
|
10
|
+
|
11
|
+
#include <libical/pvl.h>
|
12
|
+
#include <libical/ical.h>
|
13
|
+
|
14
|
+
#include <libical/icalcomponent.h>
|
15
|
+
#include <libical/icalproperty.h>
|
16
|
+
#include <libical/icalvalue.h>
|
17
|
+
#include <libical/icaltime.h>
|
18
|
+
#include <libical/icalenums.h>
|
19
|
+
|
20
|
+
VALUE crRevolution;
|
21
|
+
VALUE module;
|
22
|
+
|
23
|
+
/*
|
24
|
+
* call-seq:
|
25
|
+
* new() -> Revolution
|
26
|
+
*
|
27
|
+
* Creates a new Revolution object
|
28
|
+
*/
|
29
|
+
static VALUE revolution_init(VALUE self) {
|
30
|
+
return Qnil;
|
31
|
+
}
|
32
|
+
|
33
|
+
static gint cal_opened_status = -1;
|
34
|
+
|
35
|
+
static void revolution_calendar_opened_callback(ECal* calendar, gint arg, gpointer user_data) {
|
36
|
+
cal_opened_status = arg;
|
37
|
+
}
|
38
|
+
|
39
|
+
void copy_summary(const VALUE ruby_obj, ECalComponent* ev_obj) {
|
40
|
+
ECalComponentText summary;
|
41
|
+
e_cal_component_get_summary(ev_obj, &summary);
|
42
|
+
rb_iv_set(ruby_obj, "@summary", rb_str_new2(summary.value ? summary.value : ""));
|
43
|
+
}
|
44
|
+
|
45
|
+
void copy_start(const VALUE ruby_obj, ECalComponent* ev_obj) {
|
46
|
+
ECalComponentDateTime start;
|
47
|
+
e_cal_component_get_dtstart(ev_obj, &start);
|
48
|
+
if (start.value) {
|
49
|
+
rb_iv_set(ruby_obj, "@start", rb_funcall( rb_cTime, rb_intern( "at" ), 1, INT2NUM(icaltime_as_timet(*start.value))));
|
50
|
+
e_cal_component_free_datetime(&start);
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
void copy_last_modification(const VALUE ruby_obj, ECalComponent* ev_obj) {
|
55
|
+
struct icaltimetype* last_mod;
|
56
|
+
e_cal_component_get_last_modified(ev_obj, &last_mod);
|
57
|
+
if (last_mod) {
|
58
|
+
rb_iv_set(ruby_obj, "@last_modification", rb_funcall( rb_cTime, rb_intern( "at" ), 1, INT2NUM(icaltime_as_timet(*last_mod))));
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
void copy_uid(const VALUE ruby_obj, ECalComponent* ev_obj) {
|
63
|
+
const char* uid;
|
64
|
+
e_cal_component_get_uid(ev_obj, &uid);
|
65
|
+
if (uid) {
|
66
|
+
rb_iv_set(ruby_obj, "@uid", rb_str_new2(uid));
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
|
71
|
+
///////////////////////////////////////////////////////
|
72
|
+
// Tasks
|
73
|
+
VALUE crEvTask;
|
74
|
+
|
75
|
+
/*
|
76
|
+
* call-seq:
|
77
|
+
* new() -> EvTask
|
78
|
+
*
|
79
|
+
* Creates a new EvTask object
|
80
|
+
*/
|
81
|
+
static VALUE evTask_init(VALUE self) {
|
82
|
+
rb_define_attr(crEvTask, "uid", 1, 0);
|
83
|
+
rb_define_attr(crEvTask, "summary", 1, 0);
|
84
|
+
rb_define_attr(crEvTask, "description", 1, 0);
|
85
|
+
rb_define_attr(crEvTask, "start", 1, 0);
|
86
|
+
rb_define_attr(crEvTask, "due", 1, 0);
|
87
|
+
rb_define_attr(crEvTask, "last_modification", 1, 0);
|
88
|
+
return Qtrue;
|
89
|
+
}
|
90
|
+
|
91
|
+
void copy_into_task(const VALUE ruby_task, ECalComponent* ev_task) {
|
92
|
+
copy_uid(ruby_task, ev_task);
|
93
|
+
copy_summary(ruby_task, ev_task);
|
94
|
+
copy_last_modification(ruby_task, ev_task);
|
95
|
+
copy_start(ruby_task, ev_task);
|
96
|
+
|
97
|
+
// Note that task have only one description, so this singly-linked list
|
98
|
+
// has no more than one element. This is as opposed to a journal object, which
|
99
|
+
// has multiple description entries.
|
100
|
+
GSList* descriptions = NULL;
|
101
|
+
e_cal_component_get_description_list(ev_task, &descriptions);
|
102
|
+
if (descriptions) {
|
103
|
+
ECalComponentText* desc = (ECalComponentText*)descriptions->data;
|
104
|
+
rb_iv_set(ruby_task, "@description", rb_str_new2(desc->value ? desc->value: ""));
|
105
|
+
}
|
106
|
+
e_cal_component_free_text_list(descriptions);
|
107
|
+
|
108
|
+
ECalComponentDateTime due_date;
|
109
|
+
e_cal_component_get_due(ev_task, &due_date);
|
110
|
+
if (due_date.value) {
|
111
|
+
rb_iv_set(ruby_task, "@due", rb_funcall( rb_cTime, rb_intern( "at" ), 1, INT2NUM(icaltime_as_timet(*due_date.value))));
|
112
|
+
e_cal_component_free_datetime(&due_date);
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
/*
|
117
|
+
* call-seq:
|
118
|
+
* get_all_tasks() -> Array
|
119
|
+
*
|
120
|
+
* Fetches all tasks
|
121
|
+
*/
|
122
|
+
static VALUE revolution_get_all_tasks(VALUE self) {
|
123
|
+
VALUE result = rb_ary_new();
|
124
|
+
|
125
|
+
ECal* cal = e_cal_new_system_tasks();
|
126
|
+
g_signal_connect(G_OBJECT(cal), "cal-opened", G_CALLBACK(revolution_calendar_opened_callback), NULL);
|
127
|
+
GError* error = 0;
|
128
|
+
e_cal_open(cal, FALSE, &error);
|
129
|
+
if (error) {
|
130
|
+
g_warning("Unable to open calendar: %s", error->message);
|
131
|
+
g_clear_error(&error);
|
132
|
+
return result;
|
133
|
+
}
|
134
|
+
|
135
|
+
while (cal_opened_status == -1) {
|
136
|
+
g_usleep(G_USEC_PER_SEC*0.01);
|
137
|
+
}
|
138
|
+
if (cal_opened_status != 0) {
|
139
|
+
g_warning("Unable to open calendar; error code %d", cal_opened_status);
|
140
|
+
return result;
|
141
|
+
}
|
142
|
+
|
143
|
+
GList* objects, *l;
|
144
|
+
// #t == true, i.e. return everything
|
145
|
+
e_cal_get_object_list_as_comp(cal, "#t", &objects, &error);
|
146
|
+
if (error) {
|
147
|
+
g_warning("Unable to query calendar: %s", error->message);
|
148
|
+
g_clear_error(&error);
|
149
|
+
return result;
|
150
|
+
}
|
151
|
+
|
152
|
+
for (l = objects; l;l = l->next) {
|
153
|
+
ECalComponent *ev_task = E_CAL_COMPONENT (l->data);
|
154
|
+
VALUE ruby_task = rb_class_new_instance(0, 0, crEvTask);
|
155
|
+
copy_into_task(ruby_task, ev_task);
|
156
|
+
rb_ary_push(result, ruby_task);
|
157
|
+
g_object_unref (ev_task);
|
158
|
+
}
|
159
|
+
|
160
|
+
return result;
|
161
|
+
}
|
162
|
+
|
163
|
+
///////////////////////////////////////////////////////
|
164
|
+
// Calendar
|
165
|
+
VALUE crEvAppointment;
|
166
|
+
|
167
|
+
/*
|
168
|
+
* call-seq:
|
169
|
+
* new() -> EvAppointment
|
170
|
+
*
|
171
|
+
* Creates a new EvAppointment object
|
172
|
+
*/
|
173
|
+
static VALUE evAppointment_init(VALUE self) {
|
174
|
+
rb_define_attr(crEvAppointment, "uid", 1, 0);
|
175
|
+
rb_define_attr(crEvAppointment, "summary", 1, 0);
|
176
|
+
rb_define_attr(crEvAppointment, "location", 1, 0);
|
177
|
+
rb_define_attr(crEvAppointment, "organizer", 1, 0);
|
178
|
+
rb_define_attr(crEvAppointment, "start", 1, 0);
|
179
|
+
rb_define_attr(crEvAppointment, "end", 1, 0);
|
180
|
+
rb_define_attr(crEvAppointment, "last_modification", 1, 0);
|
181
|
+
rb_define_attr(crEvAppointment, "alarm_set", 1, 0);
|
182
|
+
rb_define_attr(crEvAppointment, "busy_status", 1, 0);
|
183
|
+
rb_define_attr(crEvAppointment, "recurring", 1, 0);
|
184
|
+
// INLINE RUBY CODE define an "all_day" method
|
185
|
+
return Qtrue;
|
186
|
+
}
|
187
|
+
|
188
|
+
void copy_into_appt(const VALUE ruby_appt, ECalComponent* ev_appt) {
|
189
|
+
copy_uid(ruby_appt, ev_appt);
|
190
|
+
copy_summary(ruby_appt, ev_appt);
|
191
|
+
copy_start(ruby_appt, ev_appt);
|
192
|
+
copy_last_modification(ruby_appt, ev_appt);
|
193
|
+
|
194
|
+
const char* location;
|
195
|
+
e_cal_component_get_location(ev_appt, &location);
|
196
|
+
if (location) {
|
197
|
+
rb_iv_set(ruby_appt, "@location", rb_str_new2(location));
|
198
|
+
}
|
199
|
+
|
200
|
+
// there's probably a good way to combine this with due_date
|
201
|
+
ECalComponentDateTime end;
|
202
|
+
e_cal_component_get_dtend(ev_appt, &end);
|
203
|
+
if (end.value) {
|
204
|
+
rb_iv_set(ruby_appt, "@end", rb_funcall( rb_cTime, rb_intern( "at" ), 1, INT2NUM(icaltime_as_timet(*end.value))));
|
205
|
+
e_cal_component_free_datetime(&end);
|
206
|
+
}
|
207
|
+
|
208
|
+
ECalComponentOrganizer organizer;
|
209
|
+
e_cal_component_get_organizer(ev_appt, &organizer);
|
210
|
+
if (organizer.value) {
|
211
|
+
if (!g_strncasecmp(organizer.value, "mailto:", 7)) {
|
212
|
+
organizer.value += 7;
|
213
|
+
}
|
214
|
+
rb_iv_set(ruby_appt, "@organizer", rb_str_new2(organizer.value?organizer.value: ""));
|
215
|
+
}
|
216
|
+
|
217
|
+
ECalComponentTransparency transp;
|
218
|
+
e_cal_component_get_transparency(ev_appt, &transp);
|
219
|
+
// This should probably be an int or enum or some such rather than just a true/false
|
220
|
+
rb_iv_set(ruby_appt, "@busy_status", transp == E_CAL_COMPONENT_TRANSP_OPAQUE ? Qtrue : Qfalse);
|
221
|
+
|
222
|
+
rb_iv_set(ruby_appt, "@alarm_set", e_cal_component_has_alarms(ev_appt) ? Qtrue : Qfalse);
|
223
|
+
rb_iv_set(ruby_appt, "@recurring", e_cal_component_has_recurrences(ev_appt) ? Qtrue : Qfalse);
|
224
|
+
}
|
225
|
+
|
226
|
+
/*
|
227
|
+
* call-seq:
|
228
|
+
* get_all_appointments(start, end) -> Array
|
229
|
+
*
|
230
|
+
* Fetches all appointments between the given Time values.
|
231
|
+
*/
|
232
|
+
static VALUE revolution_get_all_appointments(VALUE self, VALUE start, VALUE end) {
|
233
|
+
VALUE result = rb_ary_new();
|
234
|
+
|
235
|
+
// This works, but what about getting an ESourceList and all that?
|
236
|
+
ECal* cal = e_cal_new_system_calendar();
|
237
|
+
g_signal_connect(G_OBJECT(cal), "cal-opened", G_CALLBACK(revolution_calendar_opened_callback), NULL);
|
238
|
+
GError* error = 0;
|
239
|
+
e_cal_open(cal, FALSE, &error);
|
240
|
+
if (error) {
|
241
|
+
g_warning("Unable to open calendar: %s", error->message);
|
242
|
+
g_clear_error(&error);
|
243
|
+
return result;
|
244
|
+
}
|
245
|
+
|
246
|
+
while (cal_opened_status == -1) {
|
247
|
+
g_usleep(G_USEC_PER_SEC*0.01);
|
248
|
+
}
|
249
|
+
if (cal_opened_status != 0) {
|
250
|
+
g_warning("Unable to open calendar; error code %d", cal_opened_status);
|
251
|
+
return result;
|
252
|
+
}
|
253
|
+
|
254
|
+
GList* results, *l;
|
255
|
+
e_cal_get_object_list_as_comp(cal, "#t", &results, &error);
|
256
|
+
if (error) {
|
257
|
+
g_warning("Unable to query calendar: %s", error->message);
|
258
|
+
g_clear_error(&error);
|
259
|
+
return result;
|
260
|
+
}
|
261
|
+
|
262
|
+
for (l = results; l;l = l->next) {
|
263
|
+
ECalComponent *ev_appt = E_CAL_COMPONENT(l->data);
|
264
|
+
VALUE ruby_appt = rb_class_new_instance(0, 0, crEvAppointment);
|
265
|
+
copy_into_appt(ruby_appt, ev_appt);
|
266
|
+
rb_ary_push(result, ruby_appt);
|
267
|
+
g_object_unref (ev_appt);
|
268
|
+
}
|
269
|
+
|
270
|
+
g_list_free(results);
|
271
|
+
|
272
|
+
return result;
|
273
|
+
}
|
274
|
+
|
275
|
+
|
276
|
+
///////////////////////////////////////////////////////
|
277
|
+
// Address Book
|
278
|
+
VALUE crEvContact;
|
279
|
+
|
280
|
+
/*
|
281
|
+
* call-seq:
|
282
|
+
* new() -> EvContact
|
283
|
+
*
|
284
|
+
* Creates a new EvContact object
|
285
|
+
*/
|
286
|
+
static VALUE evContact_init(VALUE self) {
|
287
|
+
rb_define_attr(crEvContact, "uid", 1, 0);
|
288
|
+
rb_define_attr(crEvContact, "first_name", 1, 0);
|
289
|
+
rb_define_attr(crEvContact, "last_name", 1, 0);
|
290
|
+
rb_define_attr(crEvContact, "home_email", 1, 0);
|
291
|
+
rb_define_attr(crEvContact, "work_email", 1, 0);
|
292
|
+
return Qtrue;
|
293
|
+
}
|
294
|
+
|
295
|
+
char* fetch_field_or_empty_str(EContact* ev_contact, const EContactField field) {
|
296
|
+
char* value = e_contact_get(ev_contact, field);
|
297
|
+
return (value) ? value : "";
|
298
|
+
}
|
299
|
+
|
300
|
+
void copy_into_contact(const VALUE ruby_contact, EContact* ev_contact) {
|
301
|
+
// last mod time will work in v2.16+ : e_contact_get (ev_contact, E_CONTACT_REV));
|
302
|
+
rb_iv_set(ruby_contact, "@uid", rb_str_new2(fetch_field_or_empty_str(ev_contact, E_CONTACT_UID)));
|
303
|
+
rb_iv_set(ruby_contact, "@first_name", rb_str_new2(fetch_field_or_empty_str(ev_contact, E_CONTACT_GIVEN_NAME)));
|
304
|
+
rb_iv_set(ruby_contact, "@last_name", rb_str_new2(fetch_field_or_empty_str(ev_contact, E_CONTACT_FAMILY_NAME)));
|
305
|
+
|
306
|
+
gpointer email1 = e_contact_get(ev_contact, E_CONTACT_EMAIL);
|
307
|
+
if (email1) {
|
308
|
+
GList* attrs, *attr;
|
309
|
+
attrs = e_contact_get_attributes(ev_contact, E_CONTACT_EMAIL);
|
310
|
+
for (attr = attrs; attr ; attr = attr->next) {
|
311
|
+
EVCardAttribute* a = attr->data; // g_message("attr name = %s\n", e_vcard_attribute_get_name(a));
|
312
|
+
char* value = e_vcard_attribute_get_value(a);
|
313
|
+
GList* params, *l;
|
314
|
+
params = e_vcard_attribute_get_params(a);
|
315
|
+
for (l = params; l; l = l->next) {
|
316
|
+
EVCardAttributeParam* p = l->data;
|
317
|
+
if (!g_ascii_strcasecmp((char*)e_vcard_attribute_param_get_name(p), "TYPE")) {
|
318
|
+
char* type = e_vcard_attribute_param_get_values(p)->data;
|
319
|
+
if (!g_ascii_strcasecmp(type, "WORK")) {
|
320
|
+
rb_iv_set(ruby_contact, "@work_email", rb_str_new2(value));
|
321
|
+
} else if (!g_ascii_strcasecmp(type, "HOME")) {
|
322
|
+
rb_iv_set(ruby_contact, "@home_email", rb_str_new2(value));
|
323
|
+
}
|
324
|
+
}
|
325
|
+
}
|
326
|
+
}
|
327
|
+
}
|
328
|
+
}
|
329
|
+
|
330
|
+
/*
|
331
|
+
* call-seq:
|
332
|
+
* get_all_contacts() -> Array
|
333
|
+
*
|
334
|
+
* Fetches all contacts
|
335
|
+
*/
|
336
|
+
static VALUE revolution_get_all_contacts(VALUE self) {
|
337
|
+
VALUE result = rb_ary_new();
|
338
|
+
GError* error = 0;
|
339
|
+
EBook* book = e_book_new_default_addressbook(&error);
|
340
|
+
if (error != NULL) {
|
341
|
+
g_warning("Unable to locate the default Evolution address book: %s", error->message);
|
342
|
+
g_clear_error(&error);
|
343
|
+
return result;
|
344
|
+
}
|
345
|
+
|
346
|
+
e_book_open(book, TRUE, &error);
|
347
|
+
if (error) {
|
348
|
+
g_warning("Unable to open the Evolution address book: %s", error->message);
|
349
|
+
g_clear_error(&error);
|
350
|
+
return result;
|
351
|
+
}
|
352
|
+
|
353
|
+
EBookQuery* query = e_book_query_any_field_contains("");
|
354
|
+
|
355
|
+
GList* results, *l = NULL;
|
356
|
+
e_book_get_contacts(book, query, &results, &error);
|
357
|
+
if (error) {
|
358
|
+
g_warning("Unable to query the Evolution address book: %s", error->message);
|
359
|
+
g_clear_error(&error);
|
360
|
+
return result;
|
361
|
+
}
|
362
|
+
for (l = results; l; l = l->next) {
|
363
|
+
EContact* ev_contact = E_CONTACT(l->data);
|
364
|
+
VALUE ruby_contact = rb_class_new_instance(0, 0, crEvContact);
|
365
|
+
copy_into_contact(ruby_contact, ev_contact);
|
366
|
+
rb_ary_push(result, ruby_contact);
|
367
|
+
g_object_unref(ev_contact);
|
368
|
+
}
|
369
|
+
e_book_query_unref(query);
|
370
|
+
g_list_free(results);
|
371
|
+
|
372
|
+
return result;
|
373
|
+
}
|
374
|
+
|
375
|
+
/*
|
376
|
+
* An interface to the Evolution[http://www.gnome.org/projects/evolution/]
|
377
|
+
* calendar, contact, and task information.
|
378
|
+
*/
|
379
|
+
void Init_revolution() {
|
380
|
+
module = rb_define_module("Revolution");
|
381
|
+
|
382
|
+
crEvContact = rb_define_class_under(module, "EvContact", rb_cObject);
|
383
|
+
rb_define_method(crEvContact, "initialize", evContact_init, 0);
|
384
|
+
|
385
|
+
crEvAppointment = rb_define_class_under(module, "EvAppointment", rb_cObject);
|
386
|
+
rb_define_method(crEvAppointment, "initialize", evAppointment_init, 0);
|
387
|
+
|
388
|
+
crEvTask = rb_define_class_under(module, "EvTask", rb_cObject);
|
389
|
+
rb_define_method(crEvTask, "initialize", evTask_init, 0);
|
390
|
+
|
391
|
+
crRevolution = rb_define_class_under(module, "Revolution", rb_cObject);
|
392
|
+
rb_define_method(crRevolution, "initialize", revolution_init, 0);
|
393
|
+
rb_define_method(crRevolution, "get_all_contacts", revolution_get_all_contacts, 0);
|
394
|
+
rb_define_method(crRevolution, "get_all_appointments", revolution_get_all_appointments, 2);
|
395
|
+
rb_define_method(crRevolution, "get_all_tasks", revolution_get_all_tasks, 0);
|
396
|
+
|
397
|
+
g_type_init();
|
398
|
+
}
|
399
|
+
|
metadata
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.8
|
3
|
+
specification_version: 1
|
4
|
+
name: revolution
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.1"
|
7
|
+
date: 2005-03-21
|
8
|
+
summary: Revolution is a binding for the Evolution email client
|
9
|
+
require_paths:
|
10
|
+
- ''
|
11
|
+
email: tom@infoether.com
|
12
|
+
homepage: http://revolution.rubyforge.org/
|
13
|
+
rubyforge_project: revolution
|
14
|
+
description:
|
15
|
+
autorequire: revolution
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
-
|
22
|
+
- ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 1.8.0
|
25
|
+
version:
|
26
|
+
platform: ruby
|
27
|
+
authors:
|
28
|
+
- Tom Copeland
|
29
|
+
files:
|
30
|
+
- revolution.c
|
31
|
+
- extconf.rb
|
32
|
+
test_files: []
|
33
|
+
rdoc_options: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
executables: []
|
36
|
+
extensions:
|
37
|
+
- extconf.rb
|
38
|
+
requirements: []
|
39
|
+
dependencies: []
|