Merge lp:~jsjgruber/lernid/lernid-proposed into lp:lernid

Proposed by John S. Gruber
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
Reviewer Review Type Date Requested Status
John S. Gruber Pending
Review via email: mp+73198@code.launchpad.net

Description of the change

Proposed as Release 0.8.2 (0.8.2.1)

Thanks to enli, coalwater, and diogonese for their contributions.

To post a comment you must log in.
lp:~jsjgruber/lernid/lernid-proposed updated
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-improvements 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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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">&lt;b&gt;Classroom&lt;/b&gt;</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">&lt;b&gt;_Chatroom&lt;/b&gt;</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">&lt;b&gt;Classroom&lt;/b&gt;</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">&lt;b&gt;_Chatroom&lt;/b&gt;</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 '<' : '&lt;',
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',

Subscribers

People subscribed via source and target branches