revolution 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|