Simple support for pubsub.com notifications.
This commit is contained in:
parent
fd5294bd21
commit
fa6ae3b4be
|
@ -0,0 +1,371 @@
|
||||||
|
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||||
|
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||||
|
|
||||||
|
<glade-interface>
|
||||||
|
|
||||||
|
<widget class="GtkWindow" id="atom_entry_window">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="title" translatable="yes">New entry received</property>
|
||||||
|
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||||
|
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||||
|
<property name="modal">False</property>
|
||||||
|
<property name="resizable">True</property>
|
||||||
|
<property name="destroy_with_parent">False</property>
|
||||||
|
<property name="decorated">True</property>
|
||||||
|
<property name="skip_taskbar_hint">False</property>
|
||||||
|
<property name="skip_pager_hint">False</property>
|
||||||
|
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
|
||||||
|
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||||
|
<property name="focus_on_map">True</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkVBox" id="vbox112">
|
||||||
|
<property name="border_width">6</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">0</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="new_entry_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">You have received new entry:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHSeparator" id="hseparator14">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkTable" id="table4">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="n_rows">4</property>
|
||||||
|
<property name="n_columns">2</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="row_spacing">0</property>
|
||||||
|
<property name="column_spacing">6</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Feed name:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_RIGHT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="bottom_attach">1</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Entry:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="bottom_attach">3</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label3">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Last modified:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="bottom_attach">4</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="feed_tagline_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes"><small>Romeo and Juliet</small></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="last_modified_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">2003-12-13T18:30:02Z</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="bottom_attach">4</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEventBox" id="feed_title_eventbox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="visible_window">True</property>
|
||||||
|
<property name="above_child">False</property>
|
||||||
|
<signal name="button_press_event" handler="on_feed_title_eventbox_button_press_event" last_modification_time="Thu, 20 Jul 2006 21:53:07 GMT"/>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="feed_title_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Old stories</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="bottom_attach">1</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options">fill</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEventBox" id="entry_title_eventbox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="visible_window">True</property>
|
||||||
|
<property name="above_child">False</property>
|
||||||
|
<signal name="button_press_event" handler="on_entry_title_eventbox_button_press_event" last_modification_time="Thu, 20 Jul 2006 21:53:12 GMT"/>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="entry_title_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Soliloquy</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="bottom_attach">3</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options">fill</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHSeparator" id="hseparator15">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHBox" id="hbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">0</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHBox" id="hbox2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">0</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="close_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label">gtk-close</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<signal name="clicked" handler="on_close_button_clicked" last_modification_time="Thu, 20 Jul 2006 21:29:17 GMT"/>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="next_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label">Next entry</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<signal name="clicked" handler="on_next_button_clicked" last_modification_time="Thu, 20 Jul 2006 21:29:21 GMT"/>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="pack_type">GTK_PACK_END</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
|
||||||
|
</glade-interface>
|
|
@ -0,0 +1,115 @@
|
||||||
|
'''atom_window.py - a window to display atom entries from pubsub. For now greatly simplified,
|
||||||
|
supports only simple feeds like the one from pubsub.com. '''
|
||||||
|
|
||||||
|
import gtk
|
||||||
|
import gtk.gdk
|
||||||
|
|
||||||
|
import gtkgui_helpers
|
||||||
|
from common import helpers
|
||||||
|
|
||||||
|
class AtomWindow:
|
||||||
|
window = None
|
||||||
|
entries = []
|
||||||
|
|
||||||
|
@classmethod # python2.4 decorator
|
||||||
|
def newAtomEntry(cls, entry):
|
||||||
|
''' Queue new entry, open window if there's no one opened. '''
|
||||||
|
cls.entries.append(entry)
|
||||||
|
|
||||||
|
if cls.window is None:
|
||||||
|
cls.window = AtomWindow()
|
||||||
|
else:
|
||||||
|
cls.window.updateCounter()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
''' Create new window... only if we have anything to show. '''
|
||||||
|
assert len(self.__class__.entries)>0
|
||||||
|
|
||||||
|
self.entry = None # the entry actually displayed
|
||||||
|
|
||||||
|
self.xml = gtkgui_helpers.get_glade('atom_entry_window.glade')
|
||||||
|
self.window = self.xml.get_widget('atom_entry_window')
|
||||||
|
for name in ('new_entry_label', 'feed_title_label', 'feed_title_eventbox',
|
||||||
|
'feed_tagline_label', 'entry_title_label', 'entry_title_eventbox',
|
||||||
|
'last_modified_label', 'close_button', 'next_button'):
|
||||||
|
self.__dict__[name] = self.xml.get_widget(name)
|
||||||
|
|
||||||
|
self.displayNextEntry()
|
||||||
|
|
||||||
|
self.xml.signal_autoconnect(self)
|
||||||
|
self.window.show_all()
|
||||||
|
|
||||||
|
self.entry_title_eventbox.add_events(gtk.gdk.BUTTON_PRESS_MASK)
|
||||||
|
self.feed_title_eventbox.add_events(gtk.gdk.BUTTON_PRESS_MASK)
|
||||||
|
|
||||||
|
def displayNextEntry(self):
|
||||||
|
''' Get next entry from the queue and display it in the window. '''
|
||||||
|
assert len(self.__class__.entries)>0
|
||||||
|
|
||||||
|
newentry = self.__class__.entries.pop(0)
|
||||||
|
|
||||||
|
# fill the fields
|
||||||
|
if newentry.feed_link is not None:
|
||||||
|
self.feed_title_label.set_markup(
|
||||||
|
u'<span foreground="blue" underline="single">%s</span>' % \
|
||||||
|
gtkgui_helpers.escape_for_pango_markup(newentry.feed_title))
|
||||||
|
else:
|
||||||
|
self.feed_title_label.set_markup(
|
||||||
|
gtkgui_helpers.escape_for_pango_markup(newentry.feed_title))
|
||||||
|
|
||||||
|
self.feed_tagline_label.set_markup(
|
||||||
|
u'<small>%s</small>' % \
|
||||||
|
gtkgui_helpers.escape_for_pango_markup(newentry.feed_tagline))
|
||||||
|
|
||||||
|
if newentry.uri is not None:
|
||||||
|
self.entry_title_label.set_markup(
|
||||||
|
u'<span foreground="blue" underline="single">%s</span>' % \
|
||||||
|
gtkgui_helpers.escape_for_pango_markup(newentry.title))
|
||||||
|
else:
|
||||||
|
self.entry_title_label.set_markup(
|
||||||
|
gtkgui_helpers.escape_for_pango_markup(newentry.title))
|
||||||
|
|
||||||
|
self.last_modified_label.set_text(newentry.updated)
|
||||||
|
|
||||||
|
# update the counters
|
||||||
|
self.updateCounter()
|
||||||
|
|
||||||
|
self.entry = newentry
|
||||||
|
|
||||||
|
def updateCounter(self):
|
||||||
|
''' We display number of events on the top of window, sometimes it needs to be
|
||||||
|
changed...'''
|
||||||
|
count = len(self.__class__.entries)
|
||||||
|
# TODO: translate
|
||||||
|
if count>0:
|
||||||
|
self.new_entry_label.set_text( \
|
||||||
|
'You have received new entries (and %(count)d not displayed):' % \
|
||||||
|
{'count': count})
|
||||||
|
self.next_button.set_sensitive(True)
|
||||||
|
else:
|
||||||
|
self.new_entry_label.set_text('You have received new entry:')
|
||||||
|
self.next_button.set_sensitive(False)
|
||||||
|
|
||||||
|
def on_close_button_clicked(self, widget):
|
||||||
|
self.window.destroy()
|
||||||
|
|
||||||
|
def on_next_button_clicked(self, widget):
|
||||||
|
self.displayNextEntry()
|
||||||
|
|
||||||
|
def on_entry_title_button_press_event(self, widget, event):
|
||||||
|
# TODO: make it using special gtk2.10 widget
|
||||||
|
print 1
|
||||||
|
if event.button == 1: # left click
|
||||||
|
uri = self.entry.uri
|
||||||
|
if uri is not None:
|
||||||
|
helpers.launch_browser_mailer('url', uri)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def on_feed_title_button_press_event(self, widget, event):
|
||||||
|
# TODO: make it using special gtk2.10 widget
|
||||||
|
print 2
|
||||||
|
if event.button == 1: # left click
|
||||||
|
uri = self.entry.feed_uri
|
||||||
|
if uri is not None:
|
||||||
|
helpers.launch_browser_mailer('url', uri)
|
||||||
|
return True
|
|
@ -0,0 +1,138 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
''' Atom (rfc 4287) feed parser, used to read data from atom-over-pubsub transports
|
||||||
|
and services. Very simple. Actually implements only atom:entry. Implement more features
|
||||||
|
if you need. '''
|
||||||
|
|
||||||
|
# suggestion: rewrite functions that return dates to return standard python time tuples,
|
||||||
|
# exteneded to contain timezone
|
||||||
|
|
||||||
|
import xmpp
|
||||||
|
import time
|
||||||
|
|
||||||
|
class PersonConstruct(xmpp.Node, object):
|
||||||
|
''' Not used for now, as we don't need authors/contributors in pubsub.com feeds.
|
||||||
|
They rarely exist there. '''
|
||||||
|
def __init__(self, node):
|
||||||
|
''' Create person construct from node. '''
|
||||||
|
xmpp.Node.__init__(self, node=node)
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return self.getTagData('name')
|
||||||
|
|
||||||
|
name = property(get_name, None, None,
|
||||||
|
'''Conveys a human-readable name for the person. Should not be None,
|
||||||
|
although some badly generated atom feeds don't put anything here
|
||||||
|
(this is non-standard behavior, still pubsub.com sometimes does that.)''')
|
||||||
|
|
||||||
|
def get_uri(self):
|
||||||
|
return self.getTagData('uri')
|
||||||
|
|
||||||
|
uri = property(get_uri, None, None,
|
||||||
|
'''Conveys an IRI associated with the person. Might be None when not set.''')
|
||||||
|
|
||||||
|
def get_email(self):
|
||||||
|
return self.getTagData('email')
|
||||||
|
|
||||||
|
email = property(get_email, None, None,
|
||||||
|
'''Conveys an e-mail address associated with the person. Might be None when
|
||||||
|
not set.''')
|
||||||
|
|
||||||
|
class Entry(xmpp.Node, object):
|
||||||
|
def __init__(self, node=None):
|
||||||
|
''' Create new atom entry object. '''
|
||||||
|
xmpp.Node.__init__(self, 'entry', node=node)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Atom:Entry object of id="%r">' % self.id
|
||||||
|
|
||||||
|
class OldEntry(xmpp.Node, object):
|
||||||
|
''' Parser for feeds from pubsub.com. They use old Atom 0.3 format with
|
||||||
|
their extensions. '''
|
||||||
|
def __init__(self, node=None):
|
||||||
|
''' Create new Atom 0.3 entry object. '''
|
||||||
|
xmpp.Node.__init__(self, 'entry', node=node)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Atom0.3:Entry object of id="%r">' % self.id
|
||||||
|
|
||||||
|
def get_feed_title(self):
|
||||||
|
''' Returns title of feed, where the entry was created. The result is the feed name
|
||||||
|
concatenated with source-feed title. '''
|
||||||
|
title = u''
|
||||||
|
|
||||||
|
if self.parent is not None:
|
||||||
|
main_feed = self.parent.getTagData('title')
|
||||||
|
else:
|
||||||
|
main_feed = None
|
||||||
|
|
||||||
|
if self.getTag('source-feed') is not None:
|
||||||
|
source_feed = self.getTag('source-feed').getTagData('title')
|
||||||
|
else:
|
||||||
|
source_feed = None
|
||||||
|
|
||||||
|
if main_feed is not None and source_feed is not None:
|
||||||
|
return u'%s: %s' % (main_feed, source_feed)
|
||||||
|
elif main_feed is not None:
|
||||||
|
return main_feed
|
||||||
|
elif source_feed is not None:
|
||||||
|
return source_feed
|
||||||
|
else:
|
||||||
|
return u''
|
||||||
|
|
||||||
|
feed_title = property(get_feed_title, None, None,
|
||||||
|
''' Title of feed. It is built from entry's original feed title and title of feed
|
||||||
|
which delivered this entry. ''')
|
||||||
|
|
||||||
|
def get_feed_link(self):
|
||||||
|
''' Get a link to main page of feed (in pubsub.com: second link of rel='alternate',
|
||||||
|
first contains raw xml data). '''
|
||||||
|
try:
|
||||||
|
return self.getTag('source-feed').getTags('link', {'rel':'alternate'})[1].getData()
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
feed_link = property(get_feed_link, None, None,
|
||||||
|
''' Link to main webpage of the feed. ''')
|
||||||
|
|
||||||
|
def get_title(self):
|
||||||
|
''' Get an entry's title. '''
|
||||||
|
return self.getTagData('title')
|
||||||
|
|
||||||
|
title = property(get_title, None, None,
|
||||||
|
''' Entry's title. ''')
|
||||||
|
|
||||||
|
def get_uri(self):
|
||||||
|
''' Get the uri the entry points to (entry's first link element with rel='alternate'
|
||||||
|
or without rel attribute). '''
|
||||||
|
for element in self.getTags('link'):
|
||||||
|
if element.attrs.has_key('rel') and element.attrs['rel']<>'alternate': continue
|
||||||
|
try:
|
||||||
|
return element.attrs['href']
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
uri = property(get_uri, None, None,
|
||||||
|
''' URI that is pointed by the entry. ''')
|
||||||
|
|
||||||
|
def get_updated(self):
|
||||||
|
''' Get the time the entry was updated last time. This should be standarized,
|
||||||
|
but pubsub.com sends it in human-readable format. We won't try to parse it.
|
||||||
|
(Atom 0.3 uses the word «modified» for that).
|
||||||
|
|
||||||
|
If there's no time given in the entry, we try with <published>
|
||||||
|
and <issued> elements. '''
|
||||||
|
for name in ('updated', 'modified', 'published', 'issued'):
|
||||||
|
date = self.getTagData(name)
|
||||||
|
if date is not None: break
|
||||||
|
|
||||||
|
if date is None:
|
||||||
|
# it is not in the standard format
|
||||||
|
return time.asctime()
|
||||||
|
|
||||||
|
return date
|
||||||
|
|
||||||
|
updated = property(get_updated, None, None,
|
||||||
|
''' Last significant modification time. ''')
|
||||||
|
|
||||||
|
feed_tagline = u''
|
|
@ -33,6 +33,7 @@ from common import GnuPG
|
||||||
from common import helpers
|
from common import helpers
|
||||||
from common import gajim
|
from common import gajim
|
||||||
from common import dataforms
|
from common import dataforms
|
||||||
|
from common import atom
|
||||||
from common.commands import ConnectionCommands
|
from common.commands import ConnectionCommands
|
||||||
|
|
||||||
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
||||||
|
@ -1275,6 +1276,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
||||||
|
|
||||||
def _messageCB(self, con, msg):
|
def _messageCB(self, con, msg):
|
||||||
'''Called when we receive a message'''
|
'''Called when we receive a message'''
|
||||||
|
# check if the message is pubsub#event
|
||||||
|
if msg.getTag('event') is not None:
|
||||||
|
self._pubsubEventCB(con, msg)
|
||||||
|
return
|
||||||
msgtxt = msg.getBody()
|
msgtxt = msg.getBody()
|
||||||
mtype = msg.getType()
|
mtype = msg.getType()
|
||||||
subject = msg.getSubject() # if not there, it's None
|
subject = msg.getSubject() # if not there, it's None
|
||||||
|
@ -1394,6 +1399,32 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
||||||
subject, chatstate, msg_id, composing_jep, user_nick))
|
subject, chatstate, msg_id, composing_jep, user_nick))
|
||||||
# END messageCB
|
# END messageCB
|
||||||
|
|
||||||
|
def _pubsubEventCB(self, con, msg):
|
||||||
|
''' Called when we receive <message/> with pubsub event. '''
|
||||||
|
# TODO: Logging? (actually services where logging would be useful, should
|
||||||
|
# TODO: allow to access archives remotely...)
|
||||||
|
event = msg.getTag('event')
|
||||||
|
|
||||||
|
items = event.getTag('items')
|
||||||
|
if items is None: return
|
||||||
|
|
||||||
|
for item in items.getTags('item'):
|
||||||
|
# check for event type (for now only one type supported: pubsub.com events)
|
||||||
|
child = item.getTag('pubsub-message')
|
||||||
|
if child is not None:
|
||||||
|
# we have pubsub.com notification
|
||||||
|
child = child.getTag('feed')
|
||||||
|
if child is None: continue
|
||||||
|
|
||||||
|
for entry in child.getTags('entry'):
|
||||||
|
# for each entry in feed (there shouldn't be more than one,
|
||||||
|
# but to be sure...
|
||||||
|
self.dispatch('ATOM_ENTRY', (atom.OldEntry(node=entry),))
|
||||||
|
continue
|
||||||
|
# unknown type... probably user has another client who understands that event
|
||||||
|
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
def _presenceCB(self, con, prs):
|
def _presenceCB(self, con, prs):
|
||||||
'''Called when we receive a presence'''
|
'''Called when we receive a presence'''
|
||||||
ptype = prs.getType()
|
ptype = prs.getType()
|
||||||
|
|
|
@ -38,6 +38,7 @@ from common import i18n
|
||||||
import message_control
|
import message_control
|
||||||
|
|
||||||
from chat_control import ChatControlBase
|
from chat_control import ChatControlBase
|
||||||
|
from atom_window import AtomWindow
|
||||||
|
|
||||||
from common import exceptions
|
from common import exceptions
|
||||||
|
|
||||||
|
@ -1346,6 +1347,10 @@ class Interface:
|
||||||
def handle_event_metacontacts(self, account, tags_list):
|
def handle_event_metacontacts(self, account, tags_list):
|
||||||
gajim.contacts.define_metacontacts(account, tags_list)
|
gajim.contacts.define_metacontacts(account, tags_list)
|
||||||
|
|
||||||
|
def handle_atom_entry(self, account, data):
|
||||||
|
atom_entry, = data
|
||||||
|
AtomWindow.newAtomEntry(atom_entry)
|
||||||
|
|
||||||
def handle_event_privacy_lists_received(self, account, data):
|
def handle_event_privacy_lists_received(self, account, data):
|
||||||
# ('PRIVACY_LISTS_RECEIVED', account, list)
|
# ('PRIVACY_LISTS_RECEIVED', account, list)
|
||||||
if not self.instances.has_key(account):
|
if not self.instances.has_key(account):
|
||||||
|
@ -1671,6 +1676,7 @@ class Interface:
|
||||||
'ASK_NEW_NICK': self.handle_event_ask_new_nick,
|
'ASK_NEW_NICK': self.handle_event_ask_new_nick,
|
||||||
'SIGNED_IN': self.handle_event_signed_in,
|
'SIGNED_IN': self.handle_event_signed_in,
|
||||||
'METACONTACTS': self.handle_event_metacontacts,
|
'METACONTACTS': self.handle_event_metacontacts,
|
||||||
|
'ATOM_ENTRY': self.handle_atom_entry,
|
||||||
'PRIVACY_LISTS_RECEIVED': self.handle_event_privacy_lists_received,
|
'PRIVACY_LISTS_RECEIVED': self.handle_event_privacy_lists_received,
|
||||||
'PRIVACY_LIST_RECEIVED': self.handle_event_privacy_list_received,
|
'PRIVACY_LIST_RECEIVED': self.handle_event_privacy_list_received,
|
||||||
'PRIVACY_LISTS_ACTIVE_DEFAULT': \
|
'PRIVACY_LISTS_ACTIVE_DEFAULT': \
|
||||||
|
|
Loading…
Reference in New Issue