Merge lp:~jsjgruber/lernid/lernid-proposed into lp:lernid
- lernid-proposed
- Merge into trunk
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 229 | ||||||||||||
Proposed branch: | lp:~jsjgruber/lernid/lernid-proposed | ||||||||||||
Merge into: | lp:lernid | ||||||||||||
Diff against target: |
1340 lines (+525/-153) 21 files modified
bin/lernid (+26/-11) data/ui/ChatWidget.ui (+15/-21) data/ui/LernidWindow.ui (+10/-18) data/ui/PreferencesLernidDialog.ui (+17/-1) debian/changelog (+105/-0) etc/lernid-classrooms.d/ubuntu-charlas (+6/-0) etc/lernid-classrooms.d/ubuntu-classroom (+5/-0) lernid/CouchDBPreferences.py (+65/-12) lernid/Event.py (+14/-1) lernid/EventManager.py (+63/-30) lernid/IrcBackend.py (+16/-8) lernid/PreferencesLernidDialog.py (+3/-0) lernid/Sessions.py (+19/-7) lernid/lernidconfig.py (+1/-1) lernid/widgets/Browser.py (+25/-5) lernid/widgets/Classroom.py (+5/-3) lernid/widgets/IrcWidget.py (+3/-1) lernid/widgets/NativeChatroom.py (+16/-14) lernid/widgets/Schedule.py (+35/-4) lernid/widgets/Slide.py (+75/-15) setup.py (+1/-1) |
||||||||||||
To merge this branch: | bzr merge lp:~jsjgruber/lernid/lernid-proposed | ||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
John S. Gruber | Pending | ||
Review via email: mp+73198@code.launchpad.net |
Commit message
Description of the change
Proposed as Release 0.8.2 (0.8.2.1)
Thanks to enli, coalwater, and diogonese for their contributions.
- 229. By John S. Gruber
-
* Make schedule a bit easier to read by making font used for instructors one size smaller
* Add 'avoid-desktopcouch' debugging option.
* Remove #ubuntu-classroom- es. Assign correct calendar to #ubuntu-charlas
* Document the following merges in debian/changelog:
* Add instructor names to schedule.
* Add option -d (--debug) for message received and members changed events as
well as for all other verbose messages. Makes --verbose less verbose.
Fixes LP: #816080.
* Couch desktopcouch in try: clause; ignore desktopcouch database if
unavailable. Fixes LP: #795138 and LP: #516619 by this circumvention.
* Use a config file as backup for desktopcouch. Window sizes and pane settings
will not be restored. The config file is recreated each time lernid
is run, whether or not desktopcouch is available.
* Get the events from /etc/lernid-classrooms. d/* rather than from the
Internet. Remove all but the last weeks events and scroll down
to the current time. LP: #528870.
* Add /etc/lernid-classrooms. d/ubuntu- classroom and
/etc/lernid- classrooms. d/ubuntu- classroom- es
* Add ubuntu-charlas chatroom choice. These implement the
lernid-config- file-improvemen ts blueprint
basic requirements. Also fix LP: #533279 and LP: #793033.
* Add button to open the slide file in the user's (external) browser.
Add tooltip text to slide window giving the slide url.
Resolves LP: #830856 and LP: #530817.
* Add new [slidefile url] and [slidefile url 3] instructor commands to
load a slide file during a session, and to load the slide file and then
switch to the page.
* Load slide file asynchronously using gio (rather than using threads).
Again fixes LP: #530119. Fixes LP: #795347. Report downloading progress.
Restablishes these two functions originally created by Peeyoosh
Sangolekar.
* Remove bold effect from the room names in the classroom and chatroom label.
Include the '#' in the name, if someone had abbreviated it.
Fixes LP: #806797.
* Add a ZERO WIDTH SPACE and SIX-PER-EM SPACE following "QUESTION: " to help
classbot pick out questions without regard to the word "QUESTION" as its
language might not match the one lernid is using. Addresses LP: 808570.
* Look for classbot messages in a case-insensitive manner. Mark its messages
by making them italic. Fixes LP: #794126
* Retrieve the value of "QUESTION:" from the session or the event. Look first
for a question_token value in the session, then for a locale for the
session, then for a question_token value for the event, then for a locale
value for the event.* If locale is used and begins with es, translate QUESTION to spanish, otherwise
leave as english.* Added a server variable to events for later use.
* Replace light style of QUESTION: label with normal style so it doesn't appear
to be insensitive. Tooltip text applies to both label and the check box
itself. Clicking on the tooltext label toggles the button. Fixes
LP: #806800, LP: #806793, LP: #793029.
* Make Question: upper-case in the notification reminder and in the mouse-over
text. Thanks to Mohammad AbuShady. Fixes LP: #808569 .
* Handle the response to the user dismissing the Open event window in two
stages so that the window can be hidden before the remaining processing
is started. Fixes LP: #806804.
* Add preference to open URL's as a new tab in the user's default browser
rather than in Lernid. The initial classroom web page and the
Event->Open URL... dialog are excluded.
Thanks to Peeyoosh Sangolekar for providing this feature. See
https://blueprints. launchpad. net/lernid/ +spec/open- in-default- browser .
* Make the test for messages from instructors and helpers case-insensitive.
Thanks to Mohammad AbuShady. Fixes LP: #812427 .
* Change the last user-agent product-id from Safari to lernid. Thanks go to
Mohammad AbuShady. Fixes LP: #793805 .
* Prepend URL's input into the Open URL... dialog box with http:// if
necessary. Thanks, Diogenese The Cynical. Fixes LP: #793793.
* Set new version number
Preview Diff
1 | === modified file 'bin/lernid' |
2 | --- bin/lernid 2011-07-15 19:31:10 +0000 |
3 | +++ bin/lernid 2011-08-29 02:52:17 +0000 |
4 | @@ -208,15 +208,18 @@ |
5 | self._eventman.connect('event-disconnect', lambda em, e: menu_tweet.set_sensitive(False)) |
6 | |
7 | self._eventman.connect('event-interrupted', event_interrupted) |
8 | - def event_connected(self, ignore): |
9 | - glib.timeout_add_seconds( 12, event_message) |
10 | + def event_connected(self, event): |
11 | + glib.timeout_add_seconds( 12, event_message, self) |
12 | return False |
13 | |
14 | - def event_message(): |
15 | + def event_message(event_man): |
16 | + schedule = event_man.get_widget_by_name('schedule') |
17 | + qt = schedule.get_question_token() |
18 | message1 = _('You can interact with classes held in the classroom') |
19 | +#TRANSLATORS: %s will be a token from the locale of the target classroom session rather than the student's locale |
20 | message2 = _( |
21 | - 'Add "Question:" to the beginning of your query ' |
22 | - 'to direct it to the classroom instructor.') |
23 | + 'Add "%s" to the beginning of your query ' |
24 | + 'to direct it to the classroom instructor.' % qt) |
25 | notification = pynotify.Notification(message1, message2, 'lernid') |
26 | try: |
27 | notification.show() |
28 | @@ -295,12 +298,19 @@ |
29 | chatroom_label = self.builder.get_object(prefix+'chatroom_label') |
30 | def set_room_label(em, event): |
31 | classroom_name = Options.get('classroom', event.classroom) |
32 | + if classroom_name[0] not in '#&!+': |
33 | + classroom_name = '#' + classroom_name |
34 | chatroom_name = Options.get('chatroom', event.chat) |
35 | - classroom_label.set_label(_('Classroom')+' - '+ classroom_name) |
36 | - chatroom_label.set_label(_('_Chatroom')+' - '+ chatroom_name) |
37 | + if chatroom_name[0] not in '#&!+': |
38 | + chatroom_name = '#' + chatroom_name |
39 | + classroom_label.set_use_markup(True) |
40 | + chatroom_label.set_use_markup(True) |
41 | + classroom_label.set_markup_with_mnemonic('<b>' + _('Classroom')+' </b>'+ classroom_name) |
42 | +#TRANSLATORS: The underscore marks this as a mnemonic accelerator |
43 | + chatroom_label.set_markup_with_mnemonic('<b>'+_('_Chatroom')+' </b>'+ chatroom_name) |
44 | def clear_room_label(em, event): |
45 | - classroom_label.set_label(_('Classroom')) |
46 | - chatroom_label.set_label(_('_Chatroom')) |
47 | + classroom_label.set_markup_with_mnemonic('<b>' + _('Classroom')+'</b>') |
48 | + chatroom_label.set_markup_with_mnemonic('<b>' + _('_Chatroom')+'</b>') |
49 | self._eventman.connect('event-connect' , set_room_label) |
50 | self._eventman.connect('event-disconnect', clear_room_label) |
51 | |
52 | @@ -394,7 +404,7 @@ |
53 | self._url_entry.grab_focus() |
54 | response = self._open_url_dialog.run() |
55 | if response == gtk.RESPONSE_OK: |
56 | - self._browser.set_location(self._url_entry.get_text()) |
57 | + self._browser.set_location(self._url_entry.get_text(), False) |
58 | self._open_url_dialog.hide() |
59 | |
60 | def tweet_session(self, widget): |
61 | @@ -446,6 +456,7 @@ |
62 | self._eventman.disconnect() |
63 | while gtk.events_pending(): |
64 | gtk.main_iteration() |
65 | + Preferences.flush() |
66 | gtk.main_quit() |
67 | |
68 | def toggle_fullscreen(self, menuitem): |
69 | @@ -490,6 +501,7 @@ |
70 | # Support for command line options |
71 | import logging, optparse |
72 | Options.add_option('-v', '--verbose', action='store_true', dest='verbose', help=_('Show debug messages')) |
73 | + Options.add_option('-d', '--debug', action='store_true', dest='more_verbose', help=_('Show debug and classroom management messages')) |
74 | Options.add_option('--classroom', action='store', dest='classroom', |
75 | help=_('Override classroom channel')) |
76 | Options.add_option('--chatroom', action='store', dest='chatroom', |
77 | @@ -500,10 +512,13 @@ |
78 | help=_('Use web chat widget instead of the native one')) |
79 | Options.add_option('--unsafe-override', action='store_true', default=False, |
80 | help=_('Unsafe testing option')) |
81 | + Options.add_option('--avoid-desktopcouch', action='store_true', default=False, |
82 | + help=_('Debugging option to avoid desktopcouch'), |
83 | + dest = 'avoid_desktopcouch') |
84 | Options.parse_args() |
85 | |
86 | # Set the logging level to show debug messages |
87 | - if Options.get('verbose'): |
88 | + if Options.get('verbose') or Options.get('more_verbose'): |
89 | logging.basicConfig(level=logging.DEBUG) |
90 | logging.debug('logging enabled') |
91 | |
92 | |
93 | === modified file 'data/ui/ChatWidget.ui' |
94 | --- data/ui/ChatWidget.ui 2011-06-24 01:33:12 +0000 |
95 | +++ data/ui/ChatWidget.ui 2011-08-29 02:52:17 +0000 |
96 | @@ -43,14 +43,22 @@ |
97 | <property name="can_focus">False</property> |
98 | <property name="spacing">4</property> |
99 | <child> |
100 | - <object class="GtkCheckButton" id="questioncheckbox"> |
101 | + <object class="GtkEventBox" id="eventbox1"> |
102 | <property name="visible">True</property> |
103 | <property name="can_focus">False</property> |
104 | - <property name="receives_default">False</property> |
105 | - <property name="use_action_appearance">False</property> |
106 | - <property name="focus_on_click">False</property> |
107 | - <property name="xalign">0</property> |
108 | - <property name="draw_indicator">True</property> |
109 | + <child> |
110 | + <object class="GtkCheckButton" id="questioncheckbox"> |
111 | + <property name="label" translatable="yes">QUESTION:</property> |
112 | + <property name="visible">True</property> |
113 | + <property name="can_focus">True</property> |
114 | + <property name="receives_default">False</property> |
115 | + <property name="tooltip_text" translatable="yes">"QUESTION:" directs your question to the classroom for an answer from the instructor</property> |
116 | + <property name="use_action_appearance">False</property> |
117 | + <property name="focus_on_click">False</property> |
118 | + <property name="xalign">0</property> |
119 | + <property name="draw_indicator">True</property> |
120 | + </object> |
121 | + </child> |
122 | </object> |
123 | <packing> |
124 | <property name="expand">False</property> |
125 | @@ -59,20 +67,6 @@ |
126 | </packing> |
127 | </child> |
128 | <child> |
129 | - <object class="GtkLabel" id="questionlabel"> |
130 | - <property name="visible">True</property> |
131 | - <property name="can_focus">False</property> |
132 | - <property name="tooltip_text" translatable="yes">"Question:" directs your question to the classroom for an answer from the instructor</property> |
133 | - <property name="label" translatable="yes">Question:</property> |
134 | - <property name="selectable">True</property> |
135 | - </object> |
136 | - <packing> |
137 | - <property name="expand">False</property> |
138 | - <property name="fill">False</property> |
139 | - <property name="position">1</property> |
140 | - </packing> |
141 | - </child> |
142 | - <child> |
143 | <object class="GtkEntry" id="input"> |
144 | <property name="visible">True</property> |
145 | <property name="sensitive">False</property> |
146 | @@ -87,7 +81,7 @@ |
147 | <packing> |
148 | <property name="expand">True</property> |
149 | <property name="fill">True</property> |
150 | - <property name="position">2</property> |
151 | + <property name="position">1</property> |
152 | </packing> |
153 | </child> |
154 | </object> |
155 | |
156 | === modified file 'data/ui/LernidWindow.ui' |
157 | --- data/ui/LernidWindow.ui 2011-06-27 16:28:18 +0000 |
158 | +++ data/ui/LernidWindow.ui 2011-08-29 02:52:17 +0000 |
159 | @@ -64,11 +64,9 @@ |
160 | <property name="xalign">0</property> |
161 | <property name="xpad">6</property> |
162 | <property name="ypad">3</property> |
163 | - <property name="label" translatable="yes">Classroom</property> |
164 | + <property name="label" translatable="yes"><b>Classroom</b></property> |
165 | + <property name="use_markup">True</property> |
166 | <property name="justify">fill</property> |
167 | - <attributes> |
168 | - <attribute name="weight" value="bold"/> |
169 | - </attributes> |
170 | </object> |
171 | <packing> |
172 | <property name="expand">False</property> |
173 | @@ -101,12 +99,10 @@ |
174 | <property name="xalign">0</property> |
175 | <property name="xpad">6</property> |
176 | <property name="ypad">3</property> |
177 | - <property name="label" translatable="yes">_Chatroom</property> |
178 | + <property name="label" translatable="yes"><b>_Chatroom</b></property> |
179 | + <property name="use_markup">True</property> |
180 | <property name="use_underline">True</property> |
181 | <property name="justify">fill</property> |
182 | - <attributes> |
183 | - <attribute name="weight" value="bold"/> |
184 | - </attributes> |
185 | </object> |
186 | <packing> |
187 | <property name="expand">False</property> |
188 | @@ -155,7 +151,7 @@ |
189 | <property name="width_request">800</property> |
190 | <property name="height_request">550</property> |
191 | <property name="can_focus">False</property> |
192 | - <property name="title" translatable="no">Lernid</property> |
193 | + <property name="title">Lernid</property> |
194 | <property name="icon_name">lernid</property> |
195 | <signal name="destroy" handler="quit" swapped="no"/> |
196 | <child> |
197 | @@ -207,7 +203,7 @@ |
198 | <property name="sensitive">False</property> |
199 | <property name="can_focus">False</property> |
200 | <property name="use_action_appearance">False</property> |
201 | - <property name="label" translatable="yes">Open _URL...</property> |
202 | + <property name="label" translatable="yes">Open _URL in Lernid...</property> |
203 | <property name="use_underline">True</property> |
204 | <signal name="activate" handler="open_url" swapped="no"/> |
205 | </object> |
206 | @@ -511,11 +507,9 @@ |
207 | <property name="xalign">0</property> |
208 | <property name="xpad">6</property> |
209 | <property name="ypad">3</property> |
210 | - <property name="label" translatable="yes">Classroom</property> |
211 | + <property name="label" translatable="yes"><b>Classroom</b></property> |
212 | + <property name="use_markup">True</property> |
213 | <property name="justify">fill</property> |
214 | - <attributes> |
215 | - <attribute name="weight" value="bold"/> |
216 | - </attributes> |
217 | </object> |
218 | <packing> |
219 | <property name="expand">False</property> |
220 | @@ -548,12 +542,10 @@ |
221 | <property name="xalign">0</property> |
222 | <property name="xpad">6</property> |
223 | <property name="ypad">3</property> |
224 | - <property name="label" translatable="yes">_Chatroom</property> |
225 | + <property name="label" translatable="yes"><b>_Chatroom</b></property> |
226 | + <property name="use_markup">True</property> |
227 | <property name="use_underline">True</property> |
228 | <property name="justify">fill</property> |
229 | - <attributes> |
230 | - <attribute name="weight" value="bold"/> |
231 | - </attributes> |
232 | </object> |
233 | <packing> |
234 | <property name="expand">False</property> |
235 | |
236 | === modified file 'data/ui/PreferencesLernidDialog.ui' |
237 | --- data/ui/PreferencesLernidDialog.ui 2010-01-31 22:27:11 +0000 |
238 | +++ data/ui/PreferencesLernidDialog.ui 2011-08-29 02:52:17 +0000 |
239 | @@ -60,6 +60,19 @@ |
240 | </packing> |
241 | </child> |
242 | <child> |
243 | + <object class="GtkCheckButton" id="links_in_default_browser"> |
244 | + <property name="label" translatable="yes">Open presentation links in default browser</property> |
245 | + <property name="visible">True</property> |
246 | + <property name="can_focus">True</property> |
247 | + <property name="receives_default">False</property> |
248 | + <property name="draw_indicator">True</property> |
249 | + </object> |
250 | + <packing> |
251 | + <property name="expand">False</property> |
252 | + <property name="position">3</property> |
253 | + </packing> |
254 | + </child> |
255 | + <child> |
256 | <object class="GtkLabel" id="restart_required_message"> |
257 | <property name="xalign">0</property> |
258 | <property name="yalign">1</property> |
259 | @@ -67,9 +80,12 @@ |
260 | <property name="use_markup">True</property> |
261 | </object> |
262 | <packing> |
263 | - <property name="position">3</property> |
264 | + <property name="position">4</property> |
265 | </packing> |
266 | </child> |
267 | + <child> |
268 | + <placeholder/> |
269 | + </child> |
270 | </object> |
271 | <packing> |
272 | <property name="position">1</property> |
273 | |
274 | === modified file 'debian/changelog' |
275 | --- debian/changelog 2011-07-21 05:14:50 +0000 |
276 | +++ debian/changelog 2011-08-29 02:52:17 +0000 |
277 | @@ -1,3 +1,108 @@ |
278 | +lernid (0.8.2.1) natty; urgency=low |
279 | + |
280 | + * Make schedule a bit easier to read by making font used for instructors one size smaller |
281 | + |
282 | + -- John S Gruber <JohnSGruber@gmail.com> Sun, 28 Aug 2011 10:02:15 -0400 |
283 | + |
284 | +lernid (0.8.2) natty; urgency=low |
285 | + |
286 | + * Add 'avoid-desktopcouch' debugging option. |
287 | + |
288 | + * Remove #ubuntu-classroom-es. Assign correct calendar to #ubuntu-charlas |
289 | + |
290 | + |
291 | + * Document the following merges in debian/changelog: |
292 | + |
293 | + * Add instructor names to schedule. |
294 | + |
295 | + * Add option -d (--debug) for message received and members changed events as |
296 | + well as for all other verbose messages. Makes --verbose less verbose. |
297 | + Fixes LP: #816080. |
298 | + |
299 | + * Couch desktopcouch in try: clause; ignore desktopcouch database if |
300 | + unavailable. Fixes LP: #795138 and LP: #516619 by this circumvention. |
301 | + |
302 | + * Use a config file as backup for desktopcouch. Window sizes and pane settings |
303 | + will not be restored. The config file is recreated each time lernid |
304 | + is run, whether or not desktopcouch is available. |
305 | + |
306 | + * Get the events from /etc/lernid-classrooms.d/* rather than from the |
307 | + Internet. Remove all but the last weeks events and scroll down |
308 | + to the current time. LP: #528870. |
309 | + |
310 | + * Add /etc/lernid-classrooms.d/ubuntu-classroom and |
311 | + /etc/lernid-classrooms.d/ubuntu-classroom-es |
312 | + |
313 | + * Add ubuntu-charlas chatroom choice. These implement the |
314 | + lernid-config-file-improvements blueprint |
315 | + basic requirements. Also fix LP: #533279 and LP: #793033. |
316 | + |
317 | + * Add button to open the slide file in the user's (external) browser. |
318 | + Add tooltip text to slide window giving the slide url. |
319 | + Resolves LP: #830856 and LP: #530817. |
320 | + |
321 | + * Add new [slidefile url] and [slidefile url 3] instructor commands to |
322 | + load a slide file during a session, and to load the slide file and then |
323 | + switch to the page. |
324 | + |
325 | + * Load slide file asynchronously using gio (rather than using threads). |
326 | + Again fixes LP: #530119. Fixes LP: #795347. Report downloading progress. |
327 | + Restablishes these two functions originally created by Peeyoosh |
328 | + Sangolekar. |
329 | + |
330 | + * Remove bold effect from the room names in the classroom and chatroom label. |
331 | + Include the '#' in the name, if someone had abbreviated it. |
332 | + Fixes LP: #806797. |
333 | + |
334 | + * Add a ZERO WIDTH SPACE and SIX-PER-EM SPACE following "QUESTION: " to help |
335 | + classbot pick out questions without regard to the word "QUESTION" as its |
336 | + language might not match the one lernid is using. Addresses LP: 808570. |
337 | + |
338 | + * Look for classbot messages in a case-insensitive manner. Mark its messages |
339 | + by making them italic. Fixes LP: #794126 |
340 | + |
341 | + * Retrieve the value of "QUESTION:" from the session or the event. Look first |
342 | + for a question_token value in the session, then for a locale for the |
343 | + session, then for a question_token value for the event, then for a locale |
344 | + value for the event. |
345 | + |
346 | + * If locale is used and begins with es, translate QUESTION to spanish, otherwise |
347 | + leave as english. |
348 | + |
349 | + * Added a server variable to events for later use. |
350 | + |
351 | + * Replace light style of QUESTION: label with normal style so it doesn't appear |
352 | + to be insensitive. Tooltip text applies to both label and the check box |
353 | + itself. Clicking on the tooltext label toggles the button. Fixes |
354 | + LP: #806800, LP: #806793, LP: #793029. |
355 | + |
356 | + * Make Question: upper-case in the notification reminder and in the mouse-over |
357 | + text. Thanks to Mohammad AbuShady. Fixes LP: #808569 . |
358 | + |
359 | + * Handle the response to the user dismissing the Open event window in two |
360 | + stages so that the window can be hidden before the remaining processing |
361 | + is started. Fixes LP: #806804. |
362 | + |
363 | + * Add preference to open URL's as a new tab in the user's default browser |
364 | + rather than in Lernid. The initial classroom web page and the |
365 | + Event->Open URL... dialog are excluded. |
366 | + |
367 | + Thanks to Peeyoosh Sangolekar for providing this feature. See |
368 | + https://blueprints.launchpad.net/lernid/+spec/open-in-default-browser . |
369 | + |
370 | + * Make the test for messages from instructors and helpers case-insensitive. |
371 | + Thanks to Mohammad AbuShady. Fixes LP: #812427 . |
372 | + |
373 | + * Change the last user-agent product-id from Safari to lernid. Thanks go to |
374 | + Mohammad AbuShady. Fixes LP: #793805 . |
375 | + |
376 | + * Prepend URL's input into the Open URL... dialog box with http:// if |
377 | + necessary. Thanks, Diogenese The Cynical. Fixes LP: #793793. |
378 | + |
379 | + * Set new version number |
380 | + |
381 | + -- John S Gruber <JohnSGruber@gmail.com> Sat, 27 Aug 2011 16:02:28 -0400 |
382 | + |
383 | lernid (0.8.1.5) natty; urgency=low |
384 | |
385 | * Note that LP: #808982 is fixed |
386 | |
387 | === added directory 'etc' |
388 | === added directory 'etc/lernid-classrooms.d' |
389 | === added file 'etc/lernid-classrooms.d/ubuntu-charlas' |
390 | --- etc/lernid-classrooms.d/ubuntu-charlas 1970-01-01 00:00:00 +0000 |
391 | +++ etc/lernid-classrooms.d/ubuntu-charlas 2011-08-29 02:52:17 +0000 |
392 | @@ -0,0 +1,6 @@ |
393 | +[#ubuntu-charlas -- En Español] |
394 | +homepage: https://wiki.ubuntu.com/Classroom |
395 | +classroom: ubuntu-charlas |
396 | +Chat:ubuntu-charlas-chat |
397 | +locale: es.ES |
398 | +icalurl: http://www.google.com/calendar/ical/q16aejpuv5kgmoa7b6ridenkpk%40group.calendar.google.com/public/basic.ics |
399 | |
400 | === added file 'etc/lernid-classrooms.d/ubuntu-classroom' |
401 | --- etc/lernid-classrooms.d/ubuntu-classroom 1970-01-01 00:00:00 +0000 |
402 | +++ etc/lernid-classrooms.d/ubuntu-classroom 2011-08-29 02:52:17 +0000 |
403 | @@ -0,0 +1,5 @@ |
404 | +[#ubuntu-classroom -- In English] |
405 | +homepage: https://wiki.ubuntu.com/Classroom |
406 | +classroom: ubuntu-classroom |
407 | +chat: ubuntu-classroom-chat |
408 | +icalurl: http://www.google.com/calendar/ical/canonical.com_qg6t4s8i7mg8d4lgfu9f93qid4@group.calendar.google.com/public/basic.ics |
409 | |
410 | === modified file 'lernid/CouchDBPreferences.py' |
411 | --- lernid/CouchDBPreferences.py 2010-02-08 16:49:55 +0000 |
412 | +++ lernid/CouchDBPreferences.py 2011-08-29 02:52:17 +0000 |
413 | @@ -20,6 +20,10 @@ |
414 | |
415 | from desktopcouch.records.server import CouchDatabase |
416 | from desktopcouch.records.record import Record |
417 | +from ConfigParser import RawConfigParser |
418 | +from lernid.lernidconfig import save_cache_path |
419 | +from lernid.LernidOptions import Options |
420 | +import logging |
421 | |
422 | |
423 | class Preferences(object): |
424 | @@ -28,7 +32,16 @@ |
425 | |
426 | def __init__(self): |
427 | self._db_name = 'lernid' |
428 | - self._database = CouchDatabase(self._db_name, create=True) |
429 | + self._parser = RawConfigParser() |
430 | + self._parser.add_section(self._db_name) |
431 | + try: |
432 | + self._database = CouchDatabase(self._db_name, create=True) |
433 | + except: |
434 | + self._database = False |
435 | + logging.debug('desktopcouch not available') |
436 | + if Options.get('avoid_desktopcouch'): |
437 | + logging.info('avoiding desktopcouch') |
438 | + self._database = False |
439 | self._record_type = "http://wiki.ubuntu.com/Quickly/RecordTypes/Lernid/Preferences" |
440 | self._key = None |
441 | self._load_preferences() |
442 | @@ -41,24 +54,59 @@ |
443 | 'main_window_maximize': False, |
444 | 'statusbar': True, |
445 | 'nick': '', |
446 | - 'show_irc_time': False |
447 | + 'show_irc_time': False, |
448 | + 'links_in_default_browser': False |
449 | + |
450 | } |
451 | - results = self._database.get_records(record_type=self._record_type, create_view=True) |
452 | - if len(results.rows) == 0: |
453 | - #no preferences have ever been saved |
454 | - #save them before returning |
455 | - self._key = self._database.put_record(Record(self._preferences)) |
456 | + if self._database: |
457 | + results = self._database.get_records(record_type=self._record_type, create_view=True) |
458 | + if len(results.rows) == 0: |
459 | + #no preferences have ever been saved |
460 | + #save them before returning |
461 | + self._key = self._database.put_record(Record(self._preferences)) |
462 | + else: |
463 | + prefs = results.rows[0].value |
464 | + self._preferences.update(prefs) |
465 | + self._key = prefs["_id"] |
466 | + self._database.update_fields(self._key, self._preferences) |
467 | else: |
468 | - prefs = results.rows[0].value |
469 | - self._preferences.update(prefs) |
470 | - self._key = prefs["_id"] |
471 | - self._database.update_fields(self._key, self._preferences) |
472 | + file_name = save_cache_path('lernid')+'/config' |
473 | + try: |
474 | + config_file = open(file_name, 'r') |
475 | + except: |
476 | + return |
477 | + self._parser.readfp(config_file) |
478 | + config_file.close() |
479 | + for item in self._parser.items(self._db_name): |
480 | + if item[1].startswith('{'): |
481 | + continue |
482 | + elif item[1] == "True": |
483 | + self._preferences[item[0]] = True |
484 | + elif item[1] == "False": |
485 | + self._preferences[item[0]] = False |
486 | + else: |
487 | + self._preferences[item[0]] = item[1] |
488 | |
489 | def _update(self, prefs): |
490 | if '_id' in prefs: del prefs['_id'] |
491 | if '_rev' in prefs: del prefs['_rev'] |
492 | self._preferences.update(prefs) |
493 | - self._database.update_fields(self._key, prefs) |
494 | + if self._database: |
495 | + self._database.update_fields(self._key, prefs) |
496 | + |
497 | + def _flush(self): |
498 | + for item in self._preferences: |
499 | + if item == '_id': |
500 | + continue |
501 | + if item == '_rev': |
502 | + continue |
503 | + if item == 'record_type': |
504 | + continue |
505 | + self._parser.set(self._db_name, item, self._preferences[item]) |
506 | + |
507 | + config_file = open(save_cache_path('lernid')+'/config', 'w') |
508 | + self._parser.write(config_file) |
509 | + config_file.close() |
510 | |
511 | @classmethod |
512 | def get_instance(cls): |
513 | @@ -96,6 +144,11 @@ |
514 | 'set(dict) or set(key=val, key=val, ...)') |
515 | klass._update(d) |
516 | |
517 | + @classmethod |
518 | + def flush(cls): |
519 | + klass = cls.get_instance() |
520 | + klass._flush() |
521 | + |
522 | if __name__ == '__main__': |
523 | print Preferences.get('width') |
524 | Preferences.set('width', 100) |
525 | |
526 | === modified file 'lernid/Event.py' |
527 | --- lernid/Event.py 2010-02-08 16:49:55 +0000 |
528 | +++ lernid/Event.py 2011-08-29 02:52:17 +0000 |
529 | @@ -23,7 +23,8 @@ |
530 | def __init__(self, **kwargs): |
531 | for k, v in kwargs.iteritems(): |
532 | if k in ('name', 'nick', 'homepage', 'icalurl', 'classroom', |
533 | - 'chat', 'eventstart', 'eventend'): |
534 | + 'chat', 'eventstart', 'eventend', 'locale', |
535 | + 'question_token', 'server'): |
536 | setattr(self, '_'+k, v) |
537 | |
538 | @property |
539 | @@ -51,6 +52,18 @@ |
540 | return self._chat |
541 | |
542 | @property |
543 | + def locale(self): |
544 | + return self._locale |
545 | + |
546 | + @property |
547 | + def server(self): |
548 | + return self._server |
549 | + |
550 | + @property |
551 | + def question_token(self): |
552 | + return self._question_token |
553 | + |
554 | + @property |
555 | def eventstart(self): |
556 | return self._eventstart |
557 | |
558 | |
559 | === modified file 'lernid/EventManager.py' |
560 | --- lernid/EventManager.py 2011-06-27 23:02:17 +0000 |
561 | +++ lernid/EventManager.py 2011-08-29 02:52:17 +0000 |
562 | @@ -20,6 +20,7 @@ |
563 | |
564 | import os |
565 | import gtk |
566 | +import glib |
567 | import gobject |
568 | import ConfigParser |
569 | import urllib2 |
570 | @@ -29,6 +30,7 @@ |
571 | from lernid import ConnectDialog |
572 | from lernid.PasswordDialog import PasswordDialog |
573 | from lernid.lernidconfig import save_cache_path |
574 | +from lernid.lernidconfig import __lernid_data_directory__ |
575 | from lernid.CouchDBPreferences import Preferences |
576 | from lernid.Event import Event |
577 | from lernid.LernidOptions import Options |
578 | @@ -74,15 +76,26 @@ |
579 | homepage: https://wiki.ubuntu.com/Classroom |
580 | classroom: ubuntu-classroom-es |
581 | Chat: ubuntu-classroom-chat-es |
582 | +locale: es.ES |
583 | icalurl: http://www.google.com/calendar/ical/canonical.com_qg6t4s8i7mg8d4lgfu9f93qid4@group.calendar.google.com/public/basic.ics |
584 | """ |
585 | + |
586 | +# Make sure that these names are lower case |
587 | self.classbotnames = ['classbot', 'classroombot'] |
588 | - try: |
589 | - self._configtext = self._read_config() |
590 | - except IOError: |
591 | - Statusbar.push_message(_('Cannot retrieve configuration')) |
592 | - self._configtext = self._defaultconfig |
593 | - self._config.readfp(io.StringIO(unicode(self._configtext))) |
594 | + if Options.get('config'): |
595 | + try: |
596 | + self._configtext = self._read_config() |
597 | + except IOError: |
598 | + Statusbar.push_message(_('Cannot retrieve configuration')) |
599 | + self._configtext = self._defaultconfig |
600 | + self._config.readfp(io.StringIO(unicode(self._configtext))) |
601 | + else: |
602 | + if __lernid_data_directory__.startswith('/'): |
603 | + path = '/etc/lernid-classrooms.d' |
604 | + else: |
605 | + path = os.path.join(os.path.dirname(__file__),'../etc/lernid-classrooms.d') |
606 | + path = os.path.abspath(path) |
607 | + self._config.read([path+'/'+i for i in os.listdir(path)]) |
608 | |
609 | @classmethod |
610 | def get_instance(cls): |
611 | @@ -92,18 +105,15 @@ |
612 | return _instance_object |
613 | |
614 | def _read_config(self): |
615 | - if Options.get('config'): |
616 | - uri = Options.get('config') |
617 | - if uri.startswith('http://') or uri.startswith('https://'): |
618 | - return urllib2.urlopen(uri,None,30).read() |
619 | - else: |
620 | - try: |
621 | - return open(uri).read() |
622 | - except IOError: |
623 | - logging.critical("Unable to read configuration file "+uri) |
624 | - raise |
625 | + uri = Options.get('config') |
626 | + if uri.startswith('http://') or uri.startswith('https://'): |
627 | + return urllib2.urlopen(uri,None,30).read() |
628 | else: |
629 | - return urllib2.urlopen('http://ubuntu-owl.org/lernid/ubuntu.lernid',None,30).read() |
630 | + try: |
631 | + return open(uri).read() |
632 | + except IOError: |
633 | + logging.critical("Unable to read configuration file "+uri) |
634 | + raise |
635 | |
636 | def _connect_event(self, event, nick): |
637 | if self.is_connected(): |
638 | @@ -126,12 +136,28 @@ |
639 | icalurl = "http://www.google.com/calendar/ical/canonical.com_qg6t4s8i7mg8d4lgfu9f93qid4@group.calendar.google.com/public/basic.ics" |
640 | try: |
641 | classroom = self._config.get(event, 'classroom') |
642 | + if classroom[0] not in '#&!+': |
643 | + classroom = '#' + classroom |
644 | except: |
645 | - classroom = "ubuntu-classroom" |
646 | + classroom = "#ubuntu-classroom" |
647 | try: |
648 | chat = self._config.get(event, 'chat') |
649 | - except: |
650 | - chat = "ubuntu-classroom-chat" |
651 | + if chat[0] not in '#&!+': |
652 | + chat = '#' + chat |
653 | + except: |
654 | + chat = "#ubuntu-classroom-chat" |
655 | + try: |
656 | + locale = self._config.get(event, 'locale') |
657 | + except: |
658 | + locale = None |
659 | + try: |
660 | + server = self._config.get(event, 'server') |
661 | + except: |
662 | + server = None |
663 | + try: |
664 | + question_token = self._config.get(event, 'question_token') |
665 | + except: |
666 | + question_token = None |
667 | |
668 | self._event = Event( |
669 | name = event, |
670 | @@ -141,7 +167,10 @@ |
671 | classroom = classroom, |
672 | chat = chat, |
673 | eventstart=eventstart, |
674 | - eventend=eventend |
675 | + eventend=eventend, |
676 | + locale = locale, |
677 | + question_token = question_token, |
678 | + server = server |
679 | ) |
680 | logging.debug('connecting to event {0}'.format(event)) |
681 | Statusbar.push_message(_('Connecting to event'), duration=10) |
682 | @@ -168,20 +197,24 @@ |
683 | if not self._connectdialog: |
684 | self._connectdialog = ConnectDialog.NewConnectDialog() |
685 | self._connectdialog.set_transient_for(parent) |
686 | - self._connectdialog.connect('response', self._connect_dialog_reponse) |
687 | + self._connectdialog.connect('response', self._connect_dialog_first_response) |
688 | self._connectdialog.set_nick(Preferences.get('nick')) |
689 | self._connectdialog.set_transient_for(widget) |
690 | self._connectdialog.show() |
691 | |
692 | - def _connect_dialog_reponse(self, dialog, response): |
693 | + def _connect_dialog_first_response(self, dialog, response): |
694 | if response == gtk.RESPONSE_OK: |
695 | - event = self._connectdialog.get_event() |
696 | - nick = self._connectdialog.get_nick() |
697 | - Preferences.set('nick', nick) |
698 | - self._connect_event(event, nick) |
699 | - password = self._connectdialog.get_password() |
700 | - if password: |
701 | - self._identify_nick(nick, password) |
702 | + self._connectdialog.hide() |
703 | + glib.idle_add(self._connect_dialog_response, dialog, response) |
704 | + |
705 | + def _connect_dialog_response(self, dialog, response): |
706 | + event = self._connectdialog.get_event() |
707 | + nick = self._connectdialog.get_nick() |
708 | + Preferences.set('nick', nick) |
709 | + self._connect_event(event, nick) |
710 | + password = self._connectdialog.get_password() |
711 | + if password: |
712 | + self._identify_nick(nick, password) |
713 | |
714 | def disconnect(self): |
715 | if not self.is_connected(): |
716 | |
717 | === modified file 'lernid/IrcBackend.py' |
718 | --- lernid/IrcBackend.py 2011-06-27 23:02:17 +0000 |
719 | +++ lernid/IrcBackend.py 2011-08-29 02:52:17 +0000 |
720 | @@ -30,6 +30,7 @@ |
721 | from telepathy.constants import * |
722 | |
723 | import logging |
724 | +from lernid.LernidOptions import Options |
725 | |
726 | DBUS_PROPERTIES = dbus.PROPERTIES_IFACE |
727 | |
728 | @@ -167,11 +168,10 @@ |
729 | return |
730 | def setup_channel(yours, object_path, properties): |
731 | channel.do_connect(self._conn, object_path, properties) |
732 | - ircchan = '#' + channel.name if not channel.name[0] in '#&!+' else channel.name |
733 | self._conn[CONNECTION_INTERFACE_REQUESTS].EnsureChannel({ |
734 | CHANNEL + ".ChannelType": CHANNEL_TYPE_TEXT, |
735 | CHANNEL + ".TargetHandleType": HANDLE_TYPE_ROOM, |
736 | - CHANNEL + ".TargetID": ircchan, |
737 | + CHANNEL + ".TargetID": channel.name, |
738 | }, |
739 | reply_handler = setup_channel, |
740 | error_handler = self.error) |
741 | @@ -218,8 +218,12 @@ |
742 | def disconnect(self): |
743 | if not self._conn: |
744 | return |
745 | - self._conn[CONNECTION].Disconnect(reply_handler=ignore, |
746 | - error_handler=ignore) |
747 | + def disconnect_cb(): |
748 | + logging.debug("Server Disconnect Done") |
749 | + def disconnect_error_cb(error): |
750 | + pass |
751 | + self._conn[CONNECTION].Disconnect(reply_handler=disconnect_cb, |
752 | + error_handler=disconnect_error_cb) |
753 | self._nickidentifier = None |
754 | |
755 | def error(self, e): |
756 | @@ -306,7 +310,8 @@ |
757 | def _update_members(self, handles): |
758 | def update_cb(ids): |
759 | self._members.update(zip(handles, ids)) |
760 | - logging.debug('members updated') |
761 | + if Options.get('more_verbose'): |
762 | + logging.debug('members updated') |
763 | self.emit('members-changed', self.get_members()) |
764 | self._conn[CONNECTION_INTERFACE_ALIASING].RequestAliases( |
765 | handles, |
766 | @@ -315,14 +320,16 @@ |
767 | |
768 | def _members_changed(self, message, added, removed, |
769 | local_pending, remote_pending, actor, reason): |
770 | - logging.debug('members changed in %s' % self.name) |
771 | + if Options.get('more_verbose'): |
772 | + logging.debug('members changed in %s' % self.name) |
773 | for h in removed: |
774 | if h in self._members: |
775 | del self._members[h] |
776 | self._update_members(added) |
777 | |
778 | def _received_message(self, id, timestamp, sender_handle, msgtype, flags, text): |
779 | - logging.debug('message received in %s' % self.name) |
780 | + if Options.get('more_verbose'): |
781 | + logging.debug('message received in %s' % self.name) |
782 | if msgtype == CHANNEL_TEXT_MESSAGE_TYPE_ACTION: |
783 | msgtype = self.TYPE_ACTION |
784 | else: |
785 | @@ -390,7 +397,8 @@ |
786 | self.emit('joined') |
787 | |
788 | def _received_message(self, id, timestamp, sender_handle, msgtype, flags, text): |
789 | - logging.debug('private message received by %s' % self.target) |
790 | + if Options.get('more_verbose'): |
791 | + logging.debug('private message received by %s' % self.target) |
792 | self.emit('message-received', self.target, text) |
793 | |
794 | |
795 | |
796 | === modified file 'lernid/PreferencesLernidDialog.py' |
797 | --- lernid/PreferencesLernidDialog.py 2010-03-01 21:37:24 +0000 |
798 | +++ lernid/PreferencesLernidDialog.py 2011-08-29 02:52:17 +0000 |
799 | @@ -49,6 +49,7 @@ |
800 | self.show_appindicator = builder.get_object('show_appindicator') |
801 | self.use_vertical_layout = builder.get_object('vertical_layout') |
802 | self.show_irc_time = builder.get_object('show_irc_time') |
803 | + self.open_links_in_default_browser = builder.get_object('links_in_default_browser') |
804 | |
805 | self._load_preferences() |
806 | self.builder.connect_signals(self) |
807 | @@ -57,6 +58,7 @@ |
808 | self.show_appindicator.set_active(self._prefs.get('show_appindicator')) |
809 | self.use_vertical_layout.set_active(self._prefs.get('vertical')) |
810 | self.show_irc_time.set_active(self._prefs.get('show_irc_time')) |
811 | + self.open_links_in_default_browser.set_active(self._prefs.get('links_in_default_browser')) |
812 | |
813 | def ok(self, widget, data=None): |
814 | """ok - The user has elected to save the changes. |
815 | @@ -66,6 +68,7 @@ |
816 | self._prefs['show_appindicator'] = self.show_appindicator.get_active() |
817 | self._prefs['vertical'] = self.use_vertical_layout.get_active() |
818 | self._prefs['show_irc_time'] = self.show_irc_time.get_active() |
819 | + self._prefs['links_in_default_browser'] = self.open_links_in_default_browser.get_active() |
820 | |
821 | Preferences.set(self._prefs) |
822 | |
823 | |
824 | === modified file 'lernid/Sessions.py' |
825 | --- lernid/Sessions.py 2011-06-28 03:08:16 +0000 |
826 | +++ lernid/Sessions.py 2011-08-29 02:52:17 +0000 |
827 | @@ -25,6 +25,9 @@ |
828 | |
829 | import lernid.DateTime as dt |
830 | import io |
831 | +from datetime import timedelta |
832 | + |
833 | +one_week = timedelta(days=7) |
834 | |
835 | class Session(object): |
836 | |
837 | @@ -32,7 +35,7 @@ |
838 | |
839 | def __init__(self, **kwargs): |
840 | for k, v in kwargs.iteritems(): |
841 | - if k in ('title', 'description', 'instructors', 'helpers', 'local_start', 'local_end', 'slides', 'event'): |
842 | + if k in ('title', 'description', 'instructors', 'helpers', 'local_start', 'local_end', 'slides', 'event', 'locale', 'question_token'): |
843 | setattr(self, '_'+k, v) |
844 | |
845 | @property |
846 | @@ -52,6 +55,14 @@ |
847 | return getattr(self, '_helpers', []) |
848 | |
849 | @property |
850 | + def locale(self): |
851 | + return getattr(self, '_locale', None) |
852 | + |
853 | + @property |
854 | + def question_token(self): |
855 | + return getattr(self, '_question_token', None) |
856 | + |
857 | + @property |
858 | def start_local_date(self): |
859 | # Translators: Local date representation |
860 | return self._local_start.strftime(_('%d %B %Y')) |
861 | @@ -148,11 +159,12 @@ |
862 | else: |
863 | summary = _('Missing Session Name') |
864 | if eventstart_local <= local_start <= eventend_local: |
865 | - sessions.append(Session( |
866 | - title = summary, |
867 | - local_start = local_start, |
868 | - local_end = local_end, |
869 | - **session_data)) |
870 | + if local_start > dt.now_local() - one_week: |
871 | + sessions.append(Session( |
872 | + title = summary, |
873 | + local_start = local_start, |
874 | + local_end = local_end, |
875 | + **session_data)) |
876 | |
877 | # reverse the list to get the events in chronological order |
878 | sessions.sort(lambda x,y: cmp(x.start_datetime, y.start_datetime)) |
879 | @@ -166,7 +178,7 @@ |
880 | continue |
881 | key, value = items |
882 | key = key[:-1].lower() |
883 | - if key in ('description', 'slides', 'event'): |
884 | + if key in ('description', 'slides', 'event', 'locale', 'question_token'): |
885 | session[str(key)] = value |
886 | elif key == 'instructor' or key == 'instructors': |
887 | instructors = [s.strip() for s in value.split(',')] |
888 | |
889 | === modified file 'lernid/lernidconfig.py' |
890 | --- lernid/lernidconfig.py 2011-07-10 03:04:14 +0000 |
891 | +++ lernid/lernidconfig.py 2011-08-29 02:52:17 +0000 |
892 | @@ -31,7 +31,7 @@ |
893 | |
894 | def _(message): return message |
895 | |
896 | -VERSION = '0.8.1.5' |
897 | +VERSION = '0.8.2' |
898 | DESCRIPTION = _('Connect to a world of online tutorials quickly and easily.') |
899 | WEBSITE = 'http://wiki.ubuntu.com/Lernid' |
900 | CONTRIBUTORS = [ |
901 | |
902 | === modified file 'lernid/widgets/Browser.py' |
903 | --- lernid/widgets/Browser.py 2011-07-21 05:09:37 +0000 |
904 | +++ lernid/widgets/Browser.py 2011-08-29 02:52:17 +0000 |
905 | @@ -22,10 +22,13 @@ |
906 | import webkit |
907 | import logging |
908 | import re |
909 | +import webbrowser |
910 | |
911 | from lernid.widgets.Widget import Widget |
912 | from lernid.LernidOptions import Options |
913 | -from lernid.lernidconfig import get_data_path |
914 | +from urlparse import urlparse |
915 | +from lernid.lernidconfig import get_data_path, VERSION |
916 | +from lernid.CouchDBPreferences import Preferences |
917 | |
918 | class Browser(Widget): |
919 | |
920 | @@ -49,6 +52,14 @@ |
921 | builder = self.builder_with_file('BrowserWidget.ui') |
922 | self.add(builder.get_object('browser_vbox')) |
923 | self._browser = webkit.WebView() |
924 | + |
925 | + browser_settings=webkit.WebSettings() |
926 | + useragent=browser_settings.get_property('user-agent') |
927 | + parts=useragent.split(' ') |
928 | + parts[-1]='lernid/'+VERSION |
929 | + browser_settings.set_property('user-agent', ' '.join(parts)) |
930 | + self._browser.set_settings(browser_settings) |
931 | + |
932 | scroll = builder.get_object('browser_scroll') |
933 | scroll.add(self._browser) |
934 | |
935 | @@ -93,7 +104,8 @@ |
936 | self._browser.show() |
937 | # FIXME: why does the button not follow sensitive state of the toolbar? |
938 | self._pause_button.set_sensitive(True) |
939 | - self.set_location(event.homepage) |
940 | + # Ideally the homepage should be opened inside Lernid itself |
941 | + self.set_location(event.homepage, False) |
942 | schedule = eventman.get_widget_by_name('schedule') |
943 | self._on_faculty = schedule.on_faculty # retrieve method |
944 | |
945 | @@ -114,10 +126,17 @@ |
946 | def get_location(self): |
947 | return self._browser.get_property('uri') |
948 | |
949 | - def set_location(self, url): |
950 | + def set_location(self, url, in_default_browser = True): |
951 | + if urlparse(url).scheme == '': |
952 | + url = 'http://' + url |
953 | logging.debug('opening url: '+url) |
954 | - self._browser.open(url) |
955 | - self.emit('page-changed', url) |
956 | + # check if prefernce is set and the link was inside chat |
957 | + if Preferences.get('links_in_default_browser') and in_default_browser: |
958 | + webbrowser.open_new_tab(url) |
959 | + logging.debug('opening url in tab in default browser: '+url) |
960 | + else: |
961 | + self._browser.open(url) |
962 | + self.emit('page-changed', url) |
963 | |
964 | def _back(self, button): |
965 | self._browser.go_back() |
966 | @@ -209,6 +228,7 @@ |
967 | if any([url.endswith(c) for c in '.;:]']): |
968 | url = url[:-1] |
969 | return url |
970 | + if re.search("slidefile", message, re.IGNORECASE): return (load, ignore) |
971 | urls = re.findall("(\[?https?://[^\s\)]*)[\s\)\.\]]?", message, re.IGNORECASE) |
972 | for url in urls: |
973 | if (url.startswith('[') and url.endswith(']')) or load: |
974 | |
975 | === modified file 'lernid/widgets/Classroom.py' |
976 | --- lernid/widgets/Classroom.py 2011-06-27 23:02:17 +0000 |
977 | +++ lernid/widgets/Classroom.py 2011-08-29 02:52:17 +0000 |
978 | @@ -65,6 +65,7 @@ |
979 | text_color = self._textview.get_style().text[gtk.STATE_INSENSITIVE] |
980 | self._buffer.create_tag('gray', foreground_gdk=text_color) |
981 | self._buffer.create_tag('highlight', weight=700) |
982 | + self._buffer.create_tag('italicize', style=pango.STYLE_ITALIC) |
983 | self._init_hyperlinks() |
984 | |
985 | self.show_all() |
986 | @@ -73,7 +74,10 @@ |
987 | IrcWidget.do_event_connect(self, event_man, event) |
988 | self._server = IrcBackend.Server.get_server('irc.freenode.net', event.nick, event_man) |
989 | self._nick = event.nick |
990 | - classchan = self._server.get_channel(Options.get('classroom', event.classroom)) |
991 | + channelname = Options.get('classroom', event.classroom) |
992 | + if channelname[0] not in '#&!+': |
993 | + channelname = '#' + channelname |
994 | + classchan = self._server.get_channel(channelname) |
995 | def joined(server): |
996 | Statusbar.push_message(_('Joined classroom')) |
997 | self._textview.set_sensitive(True) |
998 | @@ -85,8 +89,6 @@ |
999 | self._textview.set_sensitive(False) |
1000 | istart, iend = self._buffer.get_bounds() |
1001 | self._buffer.delete(istart, iend) |
1002 | - classchan = self._server.get_channel( |
1003 | - Options.get('classroom', event.classroom)) |
1004 | self._server.disconnect() |
1005 | self.event_disconnect_signals() |
1006 | |
1007 | |
1008 | === modified file 'lernid/widgets/IrcWidget.py' |
1009 | --- lernid/widgets/IrcWidget.py 2011-06-27 16:39:33 +0000 |
1010 | +++ lernid/widgets/IrcWidget.py 2011-08-29 02:52:17 +0000 |
1011 | @@ -80,8 +80,10 @@ |
1012 | start_pos = iend.get_offset() |
1013 | if self._nick in text and sender != self._nick: |
1014 | self._buffer.insert_with_tags_by_name(iend, text, 'highlight') |
1015 | - elif self._on_faculty(sender) or sender in self._classbotnames: |
1016 | + elif self._on_faculty(sender): |
1017 | self._buffer.insert_with_tags_by_name(iend, text, 'highlight') |
1018 | + elif sender.lower() in self._classbotnames: |
1019 | + self._buffer.insert_with_tags_by_name(iend, text, 'italicize') |
1020 | else: |
1021 | self._buffer.insert(iend, text) |
1022 | |
1023 | |
1024 | === modified file 'lernid/widgets/NativeChatroom.py' |
1025 | --- lernid/widgets/NativeChatroom.py 2011-06-27 23:02:17 +0000 |
1026 | +++ lernid/widgets/NativeChatroom.py 2011-08-29 02:52:17 +0000 |
1027 | @@ -61,10 +61,9 @@ |
1028 | text_color = self._textview.get_style().text[gtk.STATE_INSENSITIVE] |
1029 | self._buffer.create_tag('gray', foreground_gdk=text_color) |
1030 | self._buffer.create_tag('highlight', weight=700) |
1031 | + self._buffer.create_tag('italicize', style=pango.STYLE_ITALIC) |
1032 | self._bold = pango.FontDescription() |
1033 | self._bold.set_weight(700) |
1034 | - self._light = pango.FontDescription() |
1035 | - self._light.set_weight(300) |
1036 | |
1037 | self._input = builder.get_object('input') |
1038 | self._input.connect('icon-press', self._send_text) |
1039 | @@ -73,9 +72,6 @@ |
1040 | self._input.modify_font(pango.FontDescription('Monospace')) |
1041 | |
1042 | self._questioncheckbox = builder.get_object('questioncheckbox') |
1043 | - self._questioncheckbox.connect('toggled', self._questioncheckbox_toggled) |
1044 | - self._questionlabel = builder.get_object('questionlabel') |
1045 | - self._questionlabel.modify_font(self._light) |
1046 | |
1047 | self._init_hyperlinks() |
1048 | |
1049 | @@ -83,7 +79,20 @@ |
1050 | IrcWidget.do_event_connect(self, eventman, event) |
1051 | self._server = IrcBackend.Server.get_server('irc.freenode.net', event.nick, eventman) |
1052 | self._nick = event.nick |
1053 | - self._chatchan = self._server.get_channel(Options.get('chatroom', event.chat)) |
1054 | + channelname = Options.get('chatroom', event.chat) |
1055 | + if channelname[0] not in '#&!+': |
1056 | + channelname = '#' + channelname |
1057 | + self._chatchan = self._server.get_channel(channelname) |
1058 | + schedule = eventman.get_widget_by_name('schedule') |
1059 | + self._get_question_token = schedule.get_question_token |
1060 | +#TRANSLATORS: Future versions will need translations of this word as plugins to match the locale of the target classroom session. The word will prefix questions asked in the chatroom of the instructor |
1061 | + qt = _('QUESTION:') |
1062 | + qt = self._get_question_token() |
1063 | + self._questioncheckbox.set_label(qt) |
1064 | +#TRANSLATORS: %s will be a token from the locale of the target classroom session rather than the student's locale |
1065 | + tooltip = _('"%s" directs your question to the classroom for an answer from the instructor') % qt |
1066 | + self._questioncheckbox.set_tooltip_text(tooltip) |
1067 | + |
1068 | def joined(server): |
1069 | logging.debug('connected to chatroom') |
1070 | self._input.set_sensitive(True) |
1071 | @@ -172,7 +181,7 @@ |
1072 | self._append_to_buffer(_('IRC commands are not yet supported.')) |
1073 | elif self._questioncheckbox.get_active(): |
1074 | self._questioncheckbox.set_active(False) |
1075 | - self._chatchan.send_message(_('QUESTION:') + ' ' + text) |
1076 | + self._chatchan.send_message(self._get_question_token() + u' ' + u'\u200b\u2006' + unicode(text)) |
1077 | else: |
1078 | self._chatchan.send_message(text) |
1079 | self._input.set_text('') |
1080 | @@ -189,10 +198,3 @@ |
1081 | self._at_bottom = True |
1082 | if self._at_bottom: |
1083 | self._adjust.value = self._adjust.upper - self._adjust.page_size |
1084 | - |
1085 | - def _questioncheckbox_toggled(self, *args): |
1086 | - self._input.grab_focus() |
1087 | - if self._questioncheckbox.get_active(): |
1088 | - self._questionlabel.modify_font(self._bold) |
1089 | - else: |
1090 | - self._questionlabel.modify_font(self._light) |
1091 | |
1092 | === modified file 'lernid/widgets/Schedule.py' |
1093 | --- lernid/widgets/Schedule.py 2011-07-15 19:31:10 +0000 |
1094 | +++ lernid/widgets/Schedule.py 2011-08-29 02:52:17 +0000 |
1095 | @@ -120,6 +120,7 @@ |
1096 | self.show_all() |
1097 | |
1098 | def do_event_connect(self, event_man, event): |
1099 | + self._event = event_man.current_event() |
1100 | def quote(text): |
1101 | quote_table = { |
1102 | '<' : '<', |
1103 | @@ -135,21 +136,29 @@ |
1104 | |
1105 | self._schedule = parse_ical(event) |
1106 | self._model.clear() |
1107 | + self._scroll_to = None |
1108 | for session in self._schedule: |
1109 | sessionrow = [] |
1110 | sessionrow.append('') |
1111 | sessionrow.append(session.start_local_date) |
1112 | sessionrow.append(session.start_local_time) |
1113 | sessionrow.append(session.end_local_time) |
1114 | + instructors = reduce(lambda x, y: x + y + ', ', session.instructors, ' ')[:-2] |
1115 | title = quote(session.title) |
1116 | if session.event: |
1117 | - sessionrow.append(title +' <i>' + quote(session.event) +'</i>') |
1118 | + sessionrow.append(title + '<small>' + instructors + '</small> <i>' + quote(session.event) +'</i>') |
1119 | else: |
1120 | - sessionrow.append(title) |
1121 | + sessionrow.append(title + '<small>' + instructors + '</small>') |
1122 | sessionrow.append(session) |
1123 | - self._model.append(sessionrow) |
1124 | + current_row = self._model.append(sessionrow) |
1125 | + if not self._scroll_to and session.state in (session.FUTURE, session.NOW): |
1126 | + self._scroll_to = self._model.get_path(current_row) |
1127 | |
1128 | self._update() |
1129 | + |
1130 | + if self._scroll_to: |
1131 | + self._treeview.scroll_to_cell(self._scroll_to, use_align=True, row_align=.2) |
1132 | + |
1133 | def set_timeout(): |
1134 | self._update_handle = glib.timeout_add_seconds(60, self._update) |
1135 | # Wait at least one minute before updating again, |
1136 | @@ -207,7 +216,29 @@ |
1137 | return self._current_session |
1138 | |
1139 | def on_faculty(self,name): |
1140 | - return self._current_session and name in self._current_session.instructors + self._current_session.helpers |
1141 | + return self._current_session and name.lower() in \ |
1142 | + [i.lower() for i in self._current_session.instructors + self._current_session.helpers] |
1143 | + |
1144 | + def get_question_token(self): |
1145 | + if self._current_session: |
1146 | + if self._current_session.question_token: |
1147 | + return self._current_session.question_token |
1148 | + if self._current_session.locale: |
1149 | + if self._current_session.locale.startswith('es'): |
1150 | + return 'PREGUNTA:' |
1151 | + else: |
1152 | + return 'QUESTIION:' |
1153 | + try: |
1154 | + event = self._event |
1155 | + except: |
1156 | + event = None # Called when no event is opened |
1157 | + if event and event.question_token: |
1158 | + return event.question_token |
1159 | + if event and event.locale: |
1160 | + if event.locale.startswith('es'): |
1161 | + return 'PREGUNTA:' |
1162 | + return 'QUESTION:' |
1163 | + return 'QUESTION:' |
1164 | |
1165 | def show_notification(self, summary, body): |
1166 | n = pynotify.Notification(summary, body, 'lernid') |
1167 | |
1168 | === modified file 'lernid/widgets/Slide.py' |
1169 | --- lernid/widgets/Slide.py 2011-07-21 05:09:37 +0000 |
1170 | +++ lernid/widgets/Slide.py 2011-08-29 02:52:17 +0000 |
1171 | @@ -24,7 +24,8 @@ |
1172 | import poppler |
1173 | import re |
1174 | import logging |
1175 | -import urllib |
1176 | +import gio |
1177 | +import webbrowser |
1178 | |
1179 | from lernid.widgets.Widget import Widget |
1180 | from lernid.lernidconfig import save_cache_path |
1181 | @@ -49,6 +50,7 @@ |
1182 | def __init__(self, vertical=False): |
1183 | Widget.__init__(self, 'slide') |
1184 | self._vertical = vertical |
1185 | + self._slidebutton_cb = None |
1186 | align = gtk.Alignment(0.5, 0.5, 1.0, 1.0) |
1187 | if vertical: |
1188 | align.set_padding(0, 5, 3, 3) |
1189 | @@ -58,6 +60,9 @@ |
1190 | frame = gtk.Frame() |
1191 | frame.set_shadow_type(gtk.SHADOW_IN) |
1192 | align.add(frame) |
1193 | + self._vbox = gtk.VBox() |
1194 | + frame.add(self._vbox) |
1195 | + self._top = gtk.EventBox() |
1196 | self._scroll = gtk.ScrolledWindow() |
1197 | if vertical: |
1198 | self._scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_NEVER) |
1199 | @@ -65,7 +70,19 @@ |
1200 | self._scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) |
1201 | self._image = gtk.Image() |
1202 | self._scroll.add_with_viewport(self._image) |
1203 | - frame.add(self._scroll) |
1204 | + self._top.add(self._scroll) |
1205 | + self._vbox.add(self._top) |
1206 | + self._eventbox = gtk.EventBox() |
1207 | + self._slidebutton = gtk.Button() |
1208 | + self._slidelabel = gtk.Label() |
1209 | + self._slidelabel.set_line_wrap(True) |
1210 | + self._slidelabel.set_justify(gtk.JUSTIFY_CENTER) |
1211 | + self._slidelabel.set_text(_('Click to Open in External Browser')) |
1212 | + self._slidebutton.add(self._slidelabel) |
1213 | + self._eventbox.add(self._slidebutton) |
1214 | + self._eventbox.set_tooltip_text(_('Click to open the slides in ' |
1215 | + 'your browser. From there you can browse, bookmark, or save them.')) |
1216 | + self._vbox.pack_start(self._eventbox, expand=False) |
1217 | viewport = self._scroll.get_child() |
1218 | viewport.set_shadow_type(gtk.SHADOW_NONE) |
1219 | self._master_slide = None |
1220 | @@ -87,14 +104,28 @@ |
1221 | def do_event_disconnect(self, eventman, event): |
1222 | self._image.clear() |
1223 | self.hide() |
1224 | + if self._slidebutton_cb: |
1225 | + self._slidebutton.disconnect(self._slidebutton_cb) |
1226 | + self._slidebutton_cb = None |
1227 | Statusbar.pop_message('slidesession') |
1228 | self.event_disconnect_signals() |
1229 | |
1230 | def _classroom_msg_received(self, classroom, chan, sender, text): |
1231 | - matches = re.search(r'\[SLIDE (\d+)\]', text.upper()) |
1232 | + matches = re.search(r'\[(?i)SLIDE\s+(\d+).*\]', text) |
1233 | if matches and self._session_slide_downloaded: |
1234 | if chan.is_moderated() or self._on_faculty(sender): |
1235 | self._change_slide_page(int(matches.groups()[0])) |
1236 | + return |
1237 | + matches = re.search(r'\[(?i)SLIDEFILE\s+(\S+)\s+(\d+).*\]', text) |
1238 | + if matches: |
1239 | + if chan.is_moderated() or self._on_faculty(sender): |
1240 | + self._download_slides(matches.groups()[0], int(matches.groups()[1])) |
1241 | + return |
1242 | + matches = re.search(r'\[(?i)SLIDEFILE\s+(\S+).*\]', text) |
1243 | + if matches: |
1244 | + if chan.is_moderated() or self._on_faculty(sender): |
1245 | + self._download_slides(matches.groups()[0], 1) |
1246 | + return |
1247 | |
1248 | def _change_slide_page(self, pagenumber): |
1249 | logging.debug('changing slide to page %d' % pagenumber) |
1250 | @@ -142,31 +173,55 @@ |
1251 | self._image.set_from_pixbuf(newpb) |
1252 | self._image.show() |
1253 | |
1254 | - def _download_slides(self, session): |
1255 | + def _download_slides(self, slideurl, page): |
1256 | path = os.path.join(save_cache_path('lernid'), 'slides.pdf') |
1257 | try: |
1258 | - logging.debug('downloading slides from %s' % session.slides) |
1259 | - def reporthook(blocks, bs, size): |
1260 | + logging.debug('downloading slides from %s' % slideurl) |
1261 | + slidefile = gio.File(slideurl) |
1262 | + destfile = gio.File(path) |
1263 | +# It doesn't seem that the gio.FILE_COPY_OVERWRITE is effective for copy_async |
1264 | + if destfile.query_exists(): |
1265 | + try: |
1266 | + destfile.delete(None) |
1267 | + except: pass |
1268 | + def reporthook(done_so_far, size): |
1269 | Statusbar.pop_message('slidesession') |
1270 | - msg = _('Downloading session slides (%i%% of %i MB)...') % (100*blocks*bs/size, float(size)/float(1024*1024)) |
1271 | + msg = _('Downloading session slides (%i%% of %i KB)...') % (100*(float(done_so_far) / float(size)), float(size)/float(1024)) |
1272 | Statusbar.push_message(msg, 'slidesession') |
1273 | - urllib.urlretrieve(session.slides, path, reporthook) |
1274 | - Statusbar.push_message(_('Slides have been downloaded'), 'slidesession') |
1275 | - self._session_slide_downloaded = True |
1276 | - self.emit('slides-downloaded') |
1277 | - self._change_slide_page(1) |
1278 | - except: |
1279 | + def download_done(giofile, result, self): |
1280 | + try: |
1281 | + giofile.copy_finish(result) |
1282 | + except gio.Error, information: |
1283 | + Statusbar.push_message(_('An error was encountered while downloading slides'), 'slidesession') |
1284 | + logging.debug('Error when downloading slides from %s; %s' % (slideurl, information)) |
1285 | + return |
1286 | + logging.debug('slide download completed') |
1287 | + Statusbar.push_message(_('Slides have been downloaded'), 'slidesession') |
1288 | + self._session_slide_downloaded = True |
1289 | + self.emit('slides-downloaded') |
1290 | + self._top.set_tooltip_text(slideurl) |
1291 | + if self._slidebutton_cb: |
1292 | + self._slidebutton.disconnect(self._slidebutton_cb) |
1293 | + self._slidebutton_cb = self._slidebutton.connect("clicked", |
1294 | + self._open_slides_in_browser, |
1295 | + slideurl) |
1296 | + self._change_slide_page(page) |
1297 | + slidefile.copy_async(destfile, download_done, reporthook, user_data=self, flags=gio.FILE_COPY_OVERWRITE) |
1298 | + except gio.Error, information: |
1299 | Statusbar.push_message(_('An error was encountered while downloading slides'), 'slidesession') |
1300 | - logging.debug('Error when downloading slides from %s' % session.slides) |
1301 | + logging.debug('Error when downloading slides from %s; %s' % (slideurl, information)) |
1302 | |
1303 | def _session_changed(self, schedule, session): |
1304 | Statusbar.pop_message('slidesession') |
1305 | if session.slides: |
1306 | - self._download_slides(session) |
1307 | + self._download_slides(session.slides, 1) |
1308 | else: |
1309 | self._image.clear() |
1310 | self.hide() |
1311 | self._session_slides_downloaded = False |
1312 | + if self._slidebutton_cb: |
1313 | + self._slidebutton.disconnect(self._slidebutton_cb) |
1314 | + self._slidebutton_cb = None |
1315 | Statusbar.push_message(_('This session does not use slides'), 'slidesession') |
1316 | |
1317 | def _session_ended(self, schedule): |
1318 | @@ -174,4 +229,9 @@ |
1319 | self.hide() |
1320 | Statusbar.pop_message('slidesession') |
1321 | self._session_slide_downloaded = False |
1322 | + if self._slidebutton_cb: |
1323 | + self._slidebutton.disconnect(self._slidebutton_cb) |
1324 | + self._slidebutton_cb = None |
1325 | |
1326 | + def _open_slides_in_browser(self, widget, url): |
1327 | + webbrowser.open_new_tab(url) |
1328 | |
1329 | === modified file 'setup.py' |
1330 | --- setup.py 2011-07-10 03:04:14 +0000 |
1331 | +++ setup.py 2011-08-29 02:52:17 +0000 |
1332 | @@ -68,7 +68,7 @@ |
1333 | |
1334 | setup( |
1335 | name = 'lernid', |
1336 | - version = '0.8.1.5', |
1337 | + version = '0.8.2', |
1338 | license = 'GPL-3', |
1339 | author = 'John S Gruber', |
1340 | author_email = 'johnsgruber@gmail.com', |