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.
Files changed (3) hide show
  1. data/extconf.rb +7 -0
  2. data/revolution.c +399 -0
  3. metadata +39 -0
data/extconf.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+
3
+ $CPPFLAGS += " -Wall " + `pkg-config --cflags libebook-1.0 libecal-1.0 glib-2.0 evolution-data-server-1.0`
4
+ $LOCAL_LIBS += `pkg-config --libs libebook-1.0 libecal-1.0`
5
+
6
+ create_makefile("revolution")
7
+
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: []