diff --git a/TODO.pep b/TODO.pep
new file mode 100644
index 000000000..05c924dbf
--- /dev/null
+++ b/TODO.pep
@@ -0,0 +1,15 @@
+• configure access model when changing it in the combobox
+• PEP in status change
+
+Tune use cases:
+• on disconnection of an account set Tune to None
+
+Tooltips use cases:
+• Show PEP in contact tooltips
+• Show PEP in GC tooltips
+• Show PEP in account tooltips
+
+Mood/Activity use cases
+• on connection of an account set them to None
+• on disconnection of an account set them to None
+• on explicit set publish them
diff --git a/data/glade/account_context_menu.glade b/data/glade/account_context_menu.glade
index 08bd16859..4316c5f0c 100644
--- a/data/glade/account_context_menu.glade
+++ b/data/glade/account_context_menu.glade
@@ -17,6 +17,20 @@
+
+
+
-
- False
-
@@ -449,7 +446,6 @@ Single message
tab
- False
False
@@ -880,7 +876,6 @@ Disabled
1
- False
@@ -891,7 +886,6 @@ Disabled
tab
1
- False
False
@@ -1229,7 +1223,6 @@ Show only in roster
2
- False
@@ -1240,7 +1233,6 @@ Show only in roster
tab
2
- False
False
@@ -1638,7 +1630,6 @@ Show only in roster
3
- False
@@ -1649,7 +1640,6 @@ Show only in roster
tab
3
- False
False
@@ -2048,7 +2038,6 @@ Custom
4
- False
@@ -2059,7 +2048,193 @@ Custom
tab
4
- False
+ False
+
+
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ 6
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ 0
+ GTK_SHADOW_NONE
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ 12
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+
+
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ Publish _Mood
+ True
+ 0
+ True
+
+
+
+ False
+
+
+
+
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ Publish _Activity
+ True
+ 0
+ True
+
+
+
+ False
+ 1
+
+
+
+
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ Publish _Tune
+ True
+ 0
+ True
+
+
+
+ False
+ 2
+
+
+
+
+
+
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ <b>Publish Personal Events</b>
+ True
+
+
+ label_item
+
+
+
+
+ False
+
+
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ 0
+ GTK_SHADOW_NONE
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ 12
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+
+
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ Subscribe to M_ood
+ True
+ 0
+ True
+
+
+
+ False
+
+
+
+
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ Subscribe to A_ctivity
+ True
+ 0
+ True
+
+
+
+ False
+ 1
+
+
+
+
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ Subscribe to T_une
+ True
+ 0
+ True
+
+
+
+ False
+ 2
+
+
+
+
+
+
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ <b>Subscribe to Personal Events</b>
+ True
+
+
+ label_item
+
+
+
+
+ False
+ 1
+
+
+
+
+ 5
+
+
+
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ Personal Events
+
+
+ tab
+ 5
False
diff --git a/data/glade/roster_window.glade b/data/glade/roster_window.glade
index 625a713b2..dc482bb8a 100644
--- a/data/glade/roster_window.glade
+++ b/data/glade/roster_window.glade
@@ -193,6 +193,14 @@
+
+
+
diff --git a/po/fr.po b/po/fr.po
index eb9988722..7149ff9ff 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -6537,6 +6537,406 @@ msgstr "Connecté"
msgid "Disconnected"
msgstr "Déconnecté"
+#pep
+msgid "afraid"
+msgstr "appeuré"
+
+msgid "amazed"
+msgstr "surpris, amusé"
+
+msgid "angry"
+msgstr "en colère"
+
+msgid "annoyed"
+msgstr "dérangé"
+
+msgid "anxious"
+msgstr "anxieux"
+
+msgid "aroused"
+msgstr "excité"
+
+msgid "ashamed"
+msgstr "honteux/éhonté"
+
+msgid "bored"
+msgstr "ennuyé"
+
+msgid "brave"
+msgstr "courageux"
+
+msgid "calm"
+msgstr "calme"
+
+msgid "cold"
+msgstr "froid"
+
+msgid "confused"
+msgstr "confus"
+
+msgid "contented"
+msgstr "contenté"
+
+msgid "cranky"
+msgstr "excentrique"
+
+msgid "curious"
+msgstr "curieux"
+
+msgid "depressed"
+msgstr "déprimé"
+
+msgid "disappointed"
+msgstr "décu"
+
+msgid "disgusted"
+msgstr "dégouté"
+
+msgid "distracted"
+msgstr "distrait"
+
+msgid "embarrassed"
+msgstr "embarssé"
+
+msgid "excited"
+msgstr "excité"
+
+msgid "flirtatious"
+msgstr "coquet"
+
+msgid "frustrated"
+msgstr "frustré"
+
+msgid "grumpy"
+msgstr "grognon"
+
+msgid "guilty"
+msgstr "coupable"
+
+msgid "happy"
+msgstr "joyeux"
+
+msgid "hot"
+msgstr "chaud"
+
+msgid "humbled"
+msgstr "humilié"
+
+msgid "humiliated"
+msgstr "humilié"
+
+msgid "hungry"
+msgstr "affamé"
+
+msgid "hurt"
+msgstr "blessé"
+
+msgid "impressed"
+msgstr "impressionné"
+
+msgid "in_awe"
+msgstr "dans la crainte"
+
+msgid "in_love"
+msgstr "amoureux"
+
+msgid "indignant"
+msgstr "indigné"
+
+msgid "interested"
+msgstr "interessé"
+
+msgid "intoxicated"
+msgstr "intoxiqué"
+
+msgid "invincible"
+msgstr "invincible"
+
+msgid "jealous"
+msgstr "jaloux"
+
+msgid "lonely"
+msgstr "seul/esseulé"
+
+msgid "mean"
+msgstr "méchant"
+
+msgid "moody"
+msgstr "déprimé"
+
+msgid "nervous"
+msgstr "nerveux"
+
+msgid "neutral"
+msgstr "neutre"
+
+msgid "offended"
+msgstr "offensé"
+
+msgid "playful"
+msgstr "joueur"
+
+msgid "proud"
+msgstr "fier"
+
+msgid "relieved"
+msgstr "soulagé"
+
+msgid "remorseful"
+msgstr "plein de remord"
+
+msgid "restless"
+msgstr "infatiguable"
+
+msgid "sad"
+msgstr "triste"
+
+msgid "sarcastic"
+msgstr "sarcastique"
+
+msgid "serious"
+msgstr "serieux"
+
+msgid "shocked"
+msgstr "choqué"
+
+msgid "shy"
+msgstr "timide"
+
+msgid "sick"
+msgstr "malade"
+
+msgid "sleepy"
+msgstr "endormi"
+
+msgid "stressed"
+msgstr "stressé"
+
+msgid "surprised"
+msgstr "surpris"
+
+msgid "thirsty"
+msgstr "assoiffé"
+
+msgid "worried"
+msgstr "inquiet"
+
+msgid "_Personnal Events"
+msgstr "Évènements _Personnels"
+
+msgid "Activity"
+msgstr "Activité"
+
+msgid "doing_chores"
+msgstr "fait des corvées"
+
+msgid "buying_groceries"
+msgstr "achète des épiceries"
+
+msgid "cleaning"
+msgstr "nettoie"
+
+msgid "cooking"
+msgstr "cuisine"
+
+msgid "doing_maintenance"
+msgstr "fait de la maintenance"
+
+msgid "doing_the_dishes"
+msgstr "fait la vaiselle"
+
+msgid "doing_the_laundry"
+msgstr "fait la blanchisserie"
+
+msgid "gardening"
+msgstr "jardine"
+
+msgid "running_an_errand"
+msgstr "fait une course"
+
+msgid "walking_the_dog"
+msgstr "promène le chien"
+
+msgid "drinking"
+msgstr "boit"
+
+msgid "having_a_beer"
+msgstr "prend une bière"
+
+msgid "having_coffee"
+msgstr "prend un café"
+
+msgid "having_tea"
+msgstr "prend un thé"
+
+msgid "eating"
+msgstr "mange"
+
+msgid "having_a_snack"
+msgstr "prend un snack"
+
+msgid "having_breakfast"
+msgstr "prend le petit-déjeuner"
+
+msgid "having_dinner"
+msgstr "soupe"
+
+msgid "having_lunch"
+msgstr "dîne"
+
+msgid "exercising"
+msgstr "fait de l'exercice"
+
+msgid "cycling"
+msgstr "fait du vélo"
+
+msgid "hiking"
+msgstr "fait de la randonnée"
+
+msgid "jogging"
+msgstr "fait un jogging"
+
+msgid "playing_sports"
+msgstr "fait du sport"
+
+msgid "running"
+msgstr "court"
+
+msgid "skiing"
+msgstr "skie"
+
+msgid "swimming"
+msgstr "nage"
+
+msgid "working_out"
+msgstr "élabore"
+
+msgid "grooming"
+msgstr "se toilette"
+
+msgid "at_the_spa"
+msgstr "à la station thermale"
+
+msgid "brushing_teeth"
+msgstr "se brosse les dents"
+
+msgid "getting_a_haircut"
+msgstr "se fait couper les cheveux"
+
+msgid "shaving"
+msgstr "se rase"
+
+msgid "taking_a_bath"
+msgstr "prend un bain"
+
+msgid "taking_a_shower"
+msgstr "prend une douche"
+
+msgid "having_appointment"
+msgstr "à un rendez-vous"
+
+msgid "inactive"
+msgstr "inactif"
+
+msgid "day_off"
+msgstr "en congé"
+
+msgid "hanging_out"
+msgstr "traîne"
+
+msgid "on_vacation"
+msgstr "en vacances"
+
+msgid "scheduled_holiday"
+msgstr "en vacances organisées"
+
+msgid "sleeping"
+msgstr "dort"
+
+msgid "relaxing"
+msgstr "se relaxe"
+
+msgid "gaming"
+msgstr "joue"
+
+msgid "going_out"
+msgstr "sort"
+
+msgid "partying"
+msgstr "fait la fête"
+
+msgid "reading"
+msgstr "lit"
+
+msgid "rehearsing"
+msgstr "se prépare"
+
+msgid "shopping"
+msgstr "fait les magasins"
+
+msgid "socializing"
+msgstr "se socialise"
+
+msgid "sunbathing"
+msgstr "prend un bain de soleil"
+
+msgid "watching_tv"
+msgstr "regarde la TV"
+
+msgid "watching_a_movie"
+msgstr "regarde un film"
+
+msgid "talking"
+msgstr "discute"
+
+msgid "in_real_life"
+msgstr "dans la vraie vie"
+
+msgid "on_the_phone"
+msgstr "au téléphone"
+
+msgid "traveling"
+msgstr "voyage"
+
+msgid "commuting"
+msgstr "permute"
+
+msgid "driving"
+msgstr "conduit"
+
+msgid "in_a_car"
+msgstr "en voiture"
+
+msgid "on_a_bus"
+msgstr "en bus"
+
+msgid "on_a_plane"
+msgstr "en avion"
+
+msgid "on_a_train"
+msgstr "en train"
+
+msgid "on_a_trip"
+msgstr "en séjour"
+
+msgid "walking"
+msgstr "marche"
+
+msgid "working"
+msgstr "travaille"
+
+msgid "coding"
+msgstr "programme"
+
+msgid "in_a_meeting"
+msgstr "en réunion"
+
+msgid "studying"
+msgstr "étudie"
+
+msgid "writing"
+msgstr "écrit"
+
#~ msgid "2003-12-13T18:30:02Z"
#~ msgstr "2003-12-13T18:30:02Z"
#~ msgid "Romeo and Juliet"
diff --git a/src/common/config.py b/src/common/config.py
index 0e8282daf..1570059ad 100644
--- a/src/common/config.py
+++ b/src/common/config.py
@@ -250,7 +250,14 @@ class Config:
'use_latex': [opt_bool, False, _('If True, Gajim will convert string between $$ and $$ to an image using dvips and convert before insterting it in chat window.')],
'change_status_window_timeout': [opt_int, 15, _('Time of inactivity needed before the change status window closes down.')],
'max_conversation_lines': [opt_int, 500, _('Maximum number of lines that are printed in conversations. Oldest lines are cleared.')],
+ 'publish_mood': [opt_bool, False],
+ 'publish_activity': [opt_bool, False],
+ 'publish_tune': [opt_bool, False],
+ 'subscribe_mood': [opt_bool, True],
+ 'subscribe_activity': [opt_bool, True],
+ 'subscribe_tune': [opt_bool, True],
'attach_notifications_to_systray': [opt_bool, False, _('If True, notification windows from notification-daemon will be attached to systray icon.')],
+ 'use_pep': [opt_bool, False, 'temporary variable to enable pep support'],
}
__options_per_key = {
diff --git a/src/common/connection.py b/src/common/connection.py
index 34527e45a..91c89f220 100644
--- a/src/common/connection.py
+++ b/src/common/connection.py
@@ -186,6 +186,8 @@ class Connection(ConnectionHandlers):
# We are doing disconnect at so many places, better use one function in all
def disconnect(self, on_purpose=False):
+ #FIXME: set the Tune to None before disconnection per account
+ #gajim.interface.roster._music_track_changed(None, None)
self.on_purpose = on_purpose
self.connected = 0
self.time_to_reconnect = None
diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py
index baea7e087..6423d4154 100644
--- a/src/common/connection_handlers.py
+++ b/src/common/connection_handlers.py
@@ -39,11 +39,17 @@ from common import GnuPG
from common import helpers
from common import gajim
from common import atom
+from common import pep
from common import exceptions
from common.commands import ConnectionCommands
from common.pubsub import ConnectionPubSub
from common.caps import ConnectionCaps
+from common import dbus_support
+if dbus_support.supported:
+ import dbus
+ from music_track_listener import MusicTrackListener
+
from common.stanza_session import EncryptedStanzaSession
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
@@ -54,6 +60,7 @@ VCARD_ARRIVED = 'vcard_arrived'
AGENT_REMOVED = 'agent_removed'
METACONTACTS_ARRIVED = 'metacontacts_arrived'
PRIVACY_ARRIVED = 'privacy_arrived'
+PEP_ACCESS_MODEL = 'pep_access_model'
HAS_IDLE = True
try:
import idle
@@ -750,6 +757,13 @@ class ConnectionDisco:
q.addChild('feature', attrs = {'var': common.xmpp.NS_MUC})
q.addChild('feature', attrs = {'var': common.xmpp.NS_COMMANDS})
q.addChild('feature', attrs = {'var': common.xmpp.NS_DISCO_INFO})
+ if gajim.config.get('use_pep'):
+ q.addChild('feature', attrs = {'var': common.xmpp.NS_ACTIVITY})
+ q.addChild('feature', attrs = {'var': common.xmpp.NS_ACTIVITY + '+notify'})
+ q.addChild('feature', attrs = {'var': common.xmpp.NS_TUNE})
+ q.addChild('feature', attrs = {'var': common.xmpp.NS_TUNE + '+notify'})
+ q.addChild('feature', attrs = {'var': common.xmpp.NS_MOOD})
+ q.addChild('feature', attrs = {'var': common.xmpp.NS_MOOD + '+notify'})
q.addChild('feature', attrs = {'var': common.xmpp.NS_ESESSION_INIT})
if (node is None or extension == 'cstates') and gajim.config.get('outgoing_chat_state_notifactions') != 'disabled':
@@ -821,6 +835,11 @@ class ConnectionDisco:
if identity['category'] == 'pubsub' and identity['type'] == \
'pep':
self.pep_supported = True
+ listener = MusicTrackListener.get()
+ track = listener.get_playing_track()
+ if gajim.config.get('publish_tune'):
+ gajim.interface.roster._music_track_changed(listener,
+ track, self.name)
break
if features.__contains__(common.xmpp.NS_BYTESTREAM):
gajim.proxy65_manager.resolve(jid, self.connection, self.name)
@@ -1099,6 +1118,15 @@ class ConnectionVcard:
self.get_privacy_list('block')
# Ask metacontacts before roster
self.get_metacontacts()
+ elif self.awaiting_answers[id][0] == PEP_ACCESS_MODEL:
+ conf = iq_obj.getTag('pubsub').getTag('configure')
+ node = conf.getAttr('node')
+ form_tag = conf.getTag('x', namespace=common.xmpp.NS_DATA)
+ form = common.dataforms.ExtendForm(node=form_tag)
+ for field in form.iter_fields():
+ if field.var == 'pubsub#access_model':
+ self.dispatch('PEP_ACCESS_MODEL', (node, field.value))
+ break
del self.awaiting_answers[id]
@@ -1770,8 +1798,22 @@ returns the session that we last sent a message to.'''
''' Called when we receive with pubsub event. '''
# TODO: Logging? (actually services where logging would be useful, should
# TODO: allow to access archives remotely...)
+ jid = helpers.get_full_jid_from_iq(msg)
event = msg.getTag('event')
+ # XEP-0107: User Mood
+ items = event.getTag('items', {'node': common.xmpp.NS_MOOD})
+ if items: pep.user_mood(items, self.name, jid)
+ # XEP-0118: User Tune
+ items = event.getTag('items', {'node': common.xmpp.NS_TUNE})
+ if items: pep.user_tune(items, self.name, jid)
+ # XEP-0080: User Geolocation
+ items = event.getTag('items', {'node': common.xmpp.NS_GEOLOC})
+ if items: pep.user_geoloc(items, self.name, jid)
+ # XEP-0108: User Activity
+ items = event.getTag('items', {'node': common.xmpp.NS_ACTIVITY})
+ if items: pep.user_activity(items, self.name, jid)
+
items = event.getTag('items')
if items is None: return
diff --git a/src/common/contacts.py b/src/common/contacts.py
index 262edd1d8..ef02f43da 100644
--- a/src/common/contacts.py
+++ b/src/common/contacts.py
@@ -34,6 +34,10 @@ class Contact:
self.groups = groups
self.show = show
self.status = status
+ # FIXME
+ self.mood = dict()
+ self.activity = dict()
+ self.tune = dict()
self.sub = sub
self.ask = ask
self.resource = resource
@@ -169,8 +173,8 @@ class Contacts:
def copy_contact(self, contact):
return self.create_contact(jid = contact.jid, name = contact.name,
- groups = contact.groups, show = contact.show, status = contact.status,
- sub = contact.sub, ask = contact.ask, resource = contact.resource,
+ groups = contact.groups, show = contact.show, status =
+ contact.status, sub = contact.sub, ask = contact.ask, resource = contact.resource,
priority = contact.priority, keyID = contact.keyID,
our_chatstate = contact.our_chatstate, chatstate = contact.chatstate,
last_status_time = contact.last_status_time)
diff --git a/src/common/gajim.py b/src/common/gajim.py
index dcb294034..bfc7a555a 100644
--- a/src/common/gajim.py
+++ b/src/common/gajim.py
@@ -56,7 +56,7 @@ If you start gajim from svn:
interface = None # The actual interface (the gtk one for the moment)
config = config.Config()
-version = config.get('version')
+version = config.get('version') + 'dh'
connections = {} # 'account name': 'account (connection.Connection) instance'
verbose = False
diff --git a/src/common/pep.py b/src/common/pep.py
new file mode 100644
index 000000000..9259820f5
--- /dev/null
+++ b/src/common/pep.py
@@ -0,0 +1,129 @@
+from common import gajim, xmpp
+
+def user_mood(items, name, jid):
+ (user, resource) = gajim.get_room_and_nick_from_fjid(jid)
+ contact = gajim.contacts.get_contact(name, user, resource=resource)
+ if not contact:
+ return
+ for item in items.getTags('item'):
+ child = item.getTag('mood')
+ if child is not None:
+ if contact.mood.has_key('mood'):
+ del contact.mood['mood']
+ if contact.mood.has_key('text'):
+ del contact.mood['text']
+ for ch in child.getChildren():
+ if ch.getName() != 'text':
+ contact.mood['mood'] = ch.getName()
+ else:
+ contact.mood['text'] = ch.getData()
+
+def user_tune(items, name, jid):
+ (user, resource) = gajim.get_room_and_nick_from_fjid(jid)
+ contact = gajim.contacts.get_contact(name, user, resource=resource)
+ if not contact:
+ return
+ for item in items.getTags('item'):
+ child = item.getTag('tune')
+ if child is not None:
+ if contact.tune.has_key('artist'):
+ del contact.tune['artist']
+ if contact.tune.has_key('title'):
+ del contact.tune['title']
+ if contact.tune.has_key('source'):
+ del contact.tune['source']
+ if contact.tune.has_key('track'):
+ del contact.tune['track']
+ if contact.tune.has_key('length'):
+ del contact.tune['length']
+ for ch in child.getChildren():
+ if ch.getName() == 'artist':
+ contact.tune['artist'] = ch.getData()
+ elif ch.getName() == 'title':
+ contact.tune['title'] = ch.getData()
+ elif ch.getName() == 'source':
+ contact.tune['source'] = ch.getData()
+ elif ch.getName() == 'track':
+ contact.tune['track'] = ch.getData()
+ elif ch.getName() == 'length':
+ contact.tune['length'] = ch.getData()
+
+def user_geoloc(items, name, jid):
+ pass
+
+def user_activity(items, name, jid):
+ (user, resource) = gajim.get_room_and_nick_from_fjid(jid)
+ contact = gajim.contacts.get_contact(name, user, resource=resource)
+ if not contact:
+ return
+ for item in items.getTags('item'):
+ child = item.getTag('activity')
+ if child is not None:
+ if contact.activity.has_key('activity'):
+ del contact.activity['activity']
+ if contact.activity.has_key('subactivity'):
+ del contact.activity['subactivity']
+ if contact.activity.has_key('text'):
+ del contact.activity['text']
+ for ch in child.getChildren():
+ if ch.getName() != 'text':
+ contact.activity['activity'] = ch.getName()
+ for chi in ch.getChildren():
+ contact.activity['subactivity'] = chi.getName()
+ else:
+ contact.activity['text'] = ch.getData()
+
+def user_send_mood(account, mood, message = ''):
+ print "Sending %s: %s" % (mood, message)
+ if gajim.config.get('publish_mood') == False:
+ return
+ item = xmpp.Node('mood', {'xmlns': xmpp.NS_MOOD})
+ if mood != '':
+ item.addChild(mood)
+ if message != '':
+ i = item.addChild('text')
+ i.addData(message)
+
+ gajim.connections[account].send_pb_publish('', xmpp.NS_MOOD, item, '0')
+
+def user_send_activity(account, activity, subactivity = '', message = ''):
+ if gajim.config.get('publish_activity') == False:
+ return
+ item = xmpp.Node('activity', {'xmlns': xmpp.NS_ACTIVITY})
+ if activity != '':
+ i = item.addChild(activity)
+ if subactivity != '':
+ i.addChild(subactivity)
+ if message != '':
+ i = item.addChild('text')
+ i.addData(message)
+
+ gajim.connections[account].send_pb_publish('', xmpp.NS_ACTIVITY, item, '0')
+
+def user_send_tune(account, artist = '', title = '', source = '', track = 0,length = 0, items = None):
+ print "Tune to be created"
+ if (gajim.config.get('publish_tune') == False) or \
+ (gajim.connections[account].pep_supported == False):
+ return
+ print "publish_tune == True and pep_supported"
+ item = xmpp.Node('tune', {'xmlns': xmpp.NS_TUNE})
+ if artist != '':
+ i = item.addChild('artist')
+ i.addData(artist)
+ if title != '':
+ i = item.addChild('title')
+ i.addData(title)
+ if source != '':
+ i = item.addChild('source')
+ i.addData(source)
+ if track != 0:
+ i = item.addChild('track')
+ i.addData(track)
+ if length != 0:
+ i = item.addChild('length')
+ i.addData(length)
+ if items is not None:
+ item.addChild(payload=items)
+
+ gajim.connections[account].send_pb_publish('', xmpp.NS_TUNE, item, '0')
+ print "Tune published"
diff --git a/src/common/pubsub.py b/src/common/pubsub.py
index 8a6da05cc..1cead4d4c 100644
--- a/src/common/pubsub.py
+++ b/src/common/pubsub.py
@@ -1,5 +1,7 @@
import xmpp
import gajim
+import dataforms
+import connection_handlers
class ConnectionPubSub:
def __init__(self):
@@ -43,9 +45,61 @@ class ConnectionPubSub:
self.connection.send(query)
+ def send_pb_delete(self, jid, node):
+ '''Deletes node.'''
+ query = xmpp.Iq('set', to=jid)
+ d = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
+ d = d.addChild('delete', {'node': node})
+
+ self.connection.send(query)
+
+ def send_pb_create(self, jid, node, configure = False, configure_form = None):
+ '''Creates new node.'''
+ query = xmpp.Iq('set', to=jid)
+ c = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
+ c = c.addChild('create', {'node': node})
+ if configure:
+ conf = c.addChild('configure')
+ if configure_form is not None:
+ conf.addChild(node=configuration_form)
+
+ self.connection.send(query)
+
+ def send_pb_configure(self, jid, node, cb, *cbargs, **cbkwargs):
+ query = xmpp.Iq('set', to=jid)
+ c = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
+ c = c.addChild('configure', {'node': node})
+
+ id = self.connection.send(query)
+
+ def on_configure(self, connection, query):
+ try:
+ filledform = cb(stanza['pubsub']['configure']['x'], *cbargs, **cbkwargs)
+ #TODO: Build a form
+ #TODO: Send it
+
+ except CancelConfigure:
+ cancel = xmpp.Iq('set', to=jid)
+ ca = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
+ ca = ca.addChild('configure', {'node': node})
+ #ca = ca.addChild('x', namespace=xmpp.NS_DATA, {'type': 'cancel'})
+
+ self.connection.send(cancel)
+
+ self.__callbacks[id] = (on_configure, (), {})
+
def _PubSubCB(self, conn, stanza):
try:
cb, args, kwargs = self.__callbacks.pop(stanza.getID())
cb(conn, stanza, *args, **kwargs)
except:
pass
+
+ def request_pb_configuration(self, jid, node):
+ query = xmpp.Iq('get', to=jid)
+ e = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB_OWNER)
+ e = e.addChild('configure', {'node': node})
+ id = self.connection.getAnID()
+ query.setID(id)
+ self.awaiting_answers[id] = (connection_handlers.PEP_ACCESS_MODEL,)
+ self.connection.send(query)
diff --git a/src/common/xmpp/protocol.py b/src/common/xmpp/protocol.py
index 2edb5be83..c09b9921f 100644
--- a/src/common/xmpp/protocol.py
+++ b/src/common/xmpp/protocol.py
@@ -27,11 +27,14 @@ NS_AGENTS ='jabber:iq:agents'
NS_AMP ='http://jabber.org/protocol/amp'
NS_AMP_ERRORS =NS_AMP+'#errors'
NS_AUTH ='jabber:iq:auth'
+NS_AVATAR ='http://www.xmpp.org/extensions/xep-0084.html#ns-metadata'
NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind'
NS_BROWSE ='jabber:iq:browse'
-NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # XEP-0065
-NS_CAPS ='http://jabber.org/protocol/caps' # XEP-0115
-NS_CHATSTATES ='http://jabber.org/protocol/chatstates' # XEP-0085
+NS_BROWSING ='http://jabber.org/protocol/browsing' # XEP-0195
+NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # JEP-0065
+NS_CAPS ='http://jabber.org/protocol/caps' # JEP-0115
+NS_CHATSTATES ='http://jabber.org/protocol/chatstates' # JEP-0085
+NS_CHATTING ='http://jabber.org/protocol/chatting' # XEP-0194
NS_CLIENT ='jabber:client'
NS_COMMANDS ='http://jabber.org/protocol/commands'
NS_COMPONENT_ACCEPT='jabber:component:accept'
@@ -48,8 +51,9 @@ NS_ENCRYPTED ='jabber:x:encrypted' # XEP-00
NS_ESESSION_INIT='http://www.xmpp.org/extensions/xep-0116.html#ns-init' # XEP-0116
NS_EVENT ='jabber:x:event' # XEP-0022
NS_FEATURE ='http://jabber.org/protocol/feature-neg'
-NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # XEP-0096
-NS_GEOLOC ='http://jabber.org/protocol/geoloc' # XEP-0080
+NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # JEP-0096
+NS_GAMING ='http://jabber.org/protocol/gaming' # XEP-0196
+NS_GEOLOC ='http://jabber.org/protocol/geoloc' # JEP-0080
NS_GROUPCHAT ='gc-1.0'
NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # XEP-0070
NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # XEP-0124
@@ -72,6 +76,7 @@ NS_PRIVACY ='jabber:iq:privacy'
NS_PRIVATE ='jabber:iq:private'
NS_PROFILE ='http://jabber.org/protocol/profile' # XEP-0154
NS_PUBSUB ='http://jabber.org/protocol/pubsub' # XEP-0060
+NS_PUBSUB_OWNER ='http://jabber.org/protocol/pubsub#owner' # JEP-0060
NS_REGISTER ='jabber:iq:register'
NS_ROSTER ='jabber:iq:roster'
NS_ROSTERX ='http://jabber.org/protocol/rosterx' # XEP-0144
@@ -90,12 +95,14 @@ NS_STREAMS ='http://etherx.jabber.org/streams'
NS_TIME ='jabber:iq:time' # XEP-0900
NS_TIME_REVISED ='urn:xmpp:time' # XEP-0202
NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls'
+NS_TUNE ='http://jabber.org/protocol/tune' # XEP-0118
NS_VACATION ='http://jabber.org/protocol/vacation'
NS_VCARD ='vcard-temp'
NS_GMAILNOTIFY ='google:mail:notify'
NS_GTALKSETTING ='google:setting'
NS_VCARD_UPDATE =NS_VCARD+':x:update'
NS_VERSION ='jabber:iq:version'
+NS_VIEWING ='http://jabber.org/protocol/viewing' # XEP--197
NS_PING ='urn:xmpp:ping' # XEP-0199
NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # XEP-0130
NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # XEP-0071
diff --git a/src/config.py b/src/config.py
index a47844400..e2a8999dc 100644
--- a/src/config.py
+++ b/src/config.py
@@ -49,6 +49,7 @@ from common import passwords
from common import zeroconf
from common import dbus_support
from common import dataforms
+from common import pep
from common.exceptions import GajimGeneralException
@@ -458,6 +459,25 @@ class PreferencesWindow:
else:
widget.set_sensitive(False)
+ # PEP
+ st = gajim.config.get('publish_mood')
+ self.xml.get_widget('publish_mood_checkbutton').set_active(st)
+
+ st = gajim.config.get('publish_activity')
+ self.xml.get_widget('publish_activity_checkbutton').set_active(st)
+
+ st = gajim.config.get('publish_tune')
+ self.xml.get_widget('publish_tune_checkbutton').set_active(st)
+
+ st = gajim.config.get('subscribe_mood')
+ self.xml.get_widget('subscribe_mood_checkbutton').set_active(st)
+
+ st = gajim.config.get('subscribe_activity')
+ self.xml.get_widget('subscribe_activity_checkbutton').set_active(st)
+
+ st = gajim.config.get('subscribe_tune')
+ self.xml.get_widget('subscribe_tune_checkbutton').set_active(st)
+
# Notify user of new gmail e-mail messages,
# only show checkbox if user has a gtalk account
frame_gmail = self.xml.get_widget('frame_gmail')
@@ -521,6 +541,8 @@ class PreferencesWindow:
self.theme_preferences = None
self.notebook.set_current_page(0)
+ if not gajim.config.get('use_pep'):
+ self.notebook.remove_page(5)
self.window.show_all()
gtkgui_helpers.possibly_move_window_in_current_desktop(self.window)
@@ -536,6 +558,45 @@ class PreferencesWindow:
w.set_sensitive(widget.get_active())
gajim.interface.save_config()
+ def on_publish_mood_checkbutton_toggled(self, widget):
+ if widget.get_active() == False:
+ for account in gajim.connections:
+ if gajim.connections[account].pep_supported:
+ pep.user_send_mood(account, '')
+ self.on_checkbutton_toggled(widget, 'publish_mood')
+
+ def on_publish_activity_checkbutton_toggled(self, widget):
+ if widget.get_active() == False:
+ for account in gajim.connections:
+ if gajim.connections[account].pep_supported:
+ pep.user_send_activity(account, '')
+ self.on_checkbutton_toggled(widget, 'publish_activity')
+
+ def on_publish_tune_checkbutton_toggled(self, widget):
+ if widget.get_active() == False:
+ for account in gajim.connections:
+ if gajim.connections[account].pep_supported:
+ pep.user_send_tune(account, '')
+ self.on_checkbutton_toggled(widget, 'publish_tune')
+ gajim.interface.roster.enable_syncing_status_msg_from_current_music_track(
+ widget.get_active())
+
+ def on_subscribe_mood_checkbutton_toggled(self, widget):
+ self.on_checkbutton_toggled(widget, 'subscribe_mood')
+
+ def on_subscribe_activity_checkbutton_toggled(self, widget):
+ self.on_checkbutton_toggled(widget, 'subscribe_activity')
+
+ def on_subscribe_tune_checkbutton_toggled(self, widget):
+ self.on_checkbutton_toggled(widget, 'subscribe_tune')
+
+ def on_save_position_checkbutton_toggled(self, widget):
+ self.on_checkbutton_toggled(widget, 'saveposition')
+
+ def on_sort_by_show_checkbutton_toggled(self, widget):
+ self.on_checkbutton_toggled(widget, 'sort_by_show')
+ gajim.interface.roster.draw_roster()
+
def on_show_avatars_in_roster_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'show_avatars_in_roster')
gajim.interface.roster.draw_roster()
@@ -3406,3 +3467,278 @@ class AccountCreationWizardWindow:
gajim.interface.roster.draw_roster()
gajim.interface.roster.set_actions_menu_needs_rebuild()
gajim.interface.save_config()
+
+#---------- ZeroconfPropertiesWindow class -------------#
+class ZeroconfPropertiesWindow:
+ def __init__(self):
+ self.xml = gtkgui_helpers.get_glade('zeroconf_properties_window.glade')
+ self.window = self.xml.get_widget('zeroconf_properties_window')
+ self.window.set_transient_for(gajim.interface.roster.window)
+ self.xml.signal_autoconnect(self)
+
+ self.init_account()
+ self.init_account_gpg()
+
+ self.xml.get_widget('save_button').grab_focus()
+ self.window.show_all()
+
+ def init_account(self):
+ st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
+ 'autoconnect')
+ if st:
+ self.xml.get_widget('autoconnect_checkbutton').set_active(st)
+
+ list_no_log_for = gajim.config.get_per('accounts',
+ gajim.ZEROCONF_ACC_NAME,'no_log_for').split()
+ if gajim.ZEROCONF_ACC_NAME in list_no_log_for:
+ self.xml.get_widget('log_history_checkbutton').set_active(0)
+ else:
+ self.xml.get_widget('log_history_checkbutton').set_active(1)
+
+
+ st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
+ 'sync_with_global_status')
+ if st:
+ self.xml.get_widget('sync_with_global_status_checkbutton').set_active(
+ st)
+
+ for opt in ('first_name', 'last_name', 'jabber_id', 'email'):
+ st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
+ 'zeroconf_' + opt)
+ if st:
+ self.xml.get_widget(opt + '_entry').set_text(st)
+
+ st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
+ 'custom_port')
+ if st:
+ self.xml.get_widget('custom_port_entry').set_text(str(st))
+
+ st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
+ 'use_custom_host')
+ if st:
+ self.xml.get_widget('custom_port_checkbutton').set_active(st)
+
+ self.xml.get_widget('custom_port_entry').set_sensitive(bool(st))
+
+ if not st:
+ gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME,
+ 'custom_port', '5298')
+
+ def init_account_gpg(self):
+ keyid = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'keyid')
+ keyname = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
+ 'keyname')
+ savegpgpass = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
+ 'savegpgpass')
+
+ if not keyid or not gajim.config.get('usegpg'):
+ return
+
+ self.xml.get_widget('gpg_key_label').set_text(keyid)
+ self.xml.get_widget('gpg_name_label').set_text(keyname)
+ gpg_save_password_checkbutton = \
+ self.xml.get_widget('gpg_save_password_checkbutton')
+ gpg_save_password_checkbutton.set_sensitive(True)
+ gpg_save_password_checkbutton.set_active(savegpgpass)
+
+ if savegpgpass:
+ entry = self.xml.get_widget('gpg_password_entry')
+ entry.set_sensitive(True)
+ gpgpassword = gajim.config.get_per('accounts',
+ gajim.ZEROCONF_ACC_NAME, 'gpgpassword')
+ entry.set_text(gpgpassword)
+
+ def on_zeroconf_properties_window_destroy(self, widget):
+ # close window
+ if gajim.interface.instances.has_key('zeroconf_properties'):
+ del gajim.interface.instances['zeroconf_properties']
+
+ def on_custom_port_checkbutton_toggled(self, widget):
+ st = self.xml.get_widget('custom_port_checkbutton').get_active()
+ self.xml.get_widget('custom_port_entry').set_sensitive(bool(st))
+
+ def on_cancel_button_clicked(self, widget):
+ self.window.destroy()
+
+ def on_save_button_clicked(self, widget):
+ config = {}
+
+ st = self.xml.get_widget('autoconnect_checkbutton').get_active()
+ config['autoconnect'] = st
+ list_no_log_for = gajim.config.get_per('accounts',
+ gajim.ZEROCONF_ACC_NAME, 'no_log_for').split()
+ if gajim.ZEROCONF_ACC_NAME in list_no_log_for:
+ list_no_log_for.remove(gajim.ZEROCONF_ACC_NAME)
+ if not self.xml.get_widget('log_history_checkbutton').get_active():
+ list_no_log_for.append(gajim.ZEROCONF_ACC_NAME)
+ config['no_log_for'] = ' '.join(list_no_log_for)
+
+ st = self.xml.get_widget('sync_with_global_status_checkbutton').\
+ get_active()
+ config['sync_with_global_status'] = st
+
+ st = self.xml.get_widget('first_name_entry').get_text()
+ config['zeroconf_first_name'] = st.decode('utf-8')
+
+ st = self.xml.get_widget('last_name_entry').get_text()
+ config['zeroconf_last_name'] = st.decode('utf-8')
+
+ st = self.xml.get_widget('jabber_id_entry').get_text()
+ config['zeroconf_jabber_id'] = st.decode('utf-8')
+
+ st = self.xml.get_widget('email_entry').get_text()
+ config['zeroconf_email'] = st.decode('utf-8')
+
+ use_custom_port = self.xml.get_widget('custom_port_checkbutton').\
+ get_active()
+ config['use_custom_host'] = use_custom_port
+
+ old_port = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
+ 'custom_port')
+ if use_custom_port:
+ port = self.xml.get_widget('custom_port_entry').get_text()
+ else:
+ port = 5298
+
+ config['custom_port'] = port
+
+ config['keyname'] = self.xml.get_widget('gpg_name_label').get_text().\
+ decode('utf-8')
+ if config['keyname'] == '': # no key selected
+ config['keyid'] = ''
+ config['savegpgpass'] = False
+ config['gpgpassword'] = ''
+ else:
+ config['keyid'] = self.xml.get_widget('gpg_key_label').get_text().\
+ decode('utf-8')
+ config['savegpgpass'] = self.xml.get_widget(
+ 'gpg_save_password_checkbutton').get_active()
+ config['gpgpassword'] = self.xml.get_widget('gpg_password_entry'
+ ).get_text().decode('utf-8')
+
+ reconnect = False
+ for opt in ('zeroconf_first_name','zeroconf_last_name',
+ 'zeroconf_jabber_id', 'zeroconf_email', 'custom_port'):
+ if gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, opt) != \
+ config[opt]:
+ reconnect = True
+
+ for opt in config:
+ gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, opt,
+ config[opt])
+
+ if gajim.connections.has_key(gajim.ZEROCONF_ACC_NAME):
+ if port != old_port or reconnect:
+ gajim.connections[gajim.ZEROCONF_ACC_NAME].update_details()
+
+ self.window.destroy()
+
+ def on_gpg_choose_button_clicked(self, widget, data = None):
+ if gajim.connections.has_key(gajim.ZEROCONF_ACC_NAME):
+ secret_keys = gajim.connections[gajim.ZEROCONF_ACC_NAME].\
+ ask_gpg_secrete_keys()
+
+ # self.account is None and/or gajim.connections is {}
+ else:
+ from common import GnuPG
+ if GnuPG.USE_GPG:
+ secret_keys = GnuPG.GnuPG().get_secret_keys()
+ else:
+ secret_keys = []
+ if not secret_keys:
+ dialogs.ErrorDialog(_('Failed to get secret keys'),
+ _('There was a problem retrieving your OpenPGP secret keys.'))
+ return
+ secret_keys[_('None')] = _('None')
+ instance = dialogs.ChooseGPGKeyDialog(_('OpenPGP Key Selection'),
+ _('Choose your OpenPGP key'), secret_keys)
+ keyID = instance.run()
+ if keyID is None:
+ return
+ checkbutton = self.xml.get_widget('gpg_save_password_checkbutton')
+ gpg_key_label = self.xml.get_widget('gpg_key_label')
+ gpg_name_label = self.xml.get_widget('gpg_name_label')
+ if keyID[0] == _('None'):
+ gpg_key_label.set_text(_('No key selected'))
+ gpg_name_label.set_text('')
+ checkbutton.set_sensitive(False)
+ self.xml.get_widget('gpg_password_entry').set_sensitive(False)
+ else:
+ gpg_key_label.set_text(keyID[0])
+ gpg_name_label.set_text(keyID[1])
+ checkbutton.set_sensitive(True)
+ checkbutton.set_active(False)
+ self.xml.get_widget('gpg_password_entry').set_text('')
+
+ def on_gpg_save_password_checkbutton_toggled(self, widget):
+ st = widget.get_active()
+ w = self.xml.get_widget('gpg_password_entry')
+ w.set_sensitive(bool(st))
+
+class ManagePEPServicesWindow:
+ def __init__(self, account):
+ self.xml = gtkgui_helpers.get_glade('manage_pep_services_window.glade')
+ self.window = self.xml.get_widget('manage_pep_services_window')
+ self.window.set_transient_for(gajim.interface.roster.window)
+ self.xml.signal_autoconnect(self)
+ self.account = account
+
+ self.init_services()
+ self.window.show_all()
+
+ def on_manage_pep_services_window_destroy(self, widget):
+ '''close window'''
+ del gajim.interface.instances[self.account]['pep_services']
+
+ def on_ok_button_clicked(self, widget):
+ pass
+
+ def on_cancel_button_clicked(self, widget):
+ self.window.destroy()
+
+ def cellrenderer_combo_edited(self, cellrenderer, path, new_text):
+ self.treestore[path][1] = new_text
+
+ def init_services(self):
+ treeview = self.xml.get_widget('services_treeview')
+ # service, access_model, group
+ self.treestore = gtk.ListStore(str, str, str)
+ treeview.set_model(self.treestore)
+
+ col = gtk.TreeViewColumn('Service')
+ treeview.append_column(col)
+
+ cellrenderer_text = gtk.CellRendererText()
+ col.pack_start(cellrenderer_text)
+ col.add_attribute(cellrenderer_text, 'text', 0)
+
+ col = gtk.TreeViewColumn('access model')
+ treeview.append_column(col)
+
+ model = gtk.ListStore(str)
+ model.append(['open'])
+ model.append(['presence'])
+ model.append(['roster'])
+ model.append(['whitelist'])
+ cellrenderer_combo = gtk.CellRendererCombo()
+ cellrenderer_combo.set_property('text-column', 0)
+ cellrenderer_combo.set_property('model', model)
+ cellrenderer_combo.set_property('has-entry', False)
+ cellrenderer_combo.set_property('editable', True)
+ cellrenderer_combo.connect('edited', self.cellrenderer_combo_edited)
+ col.pack_start(cellrenderer_combo)
+ col.add_attribute(cellrenderer_combo, 'text', 1)
+
+ our_jid = gajim.get_jid_from_account(self.account)
+ gajim.connections[self.account].discoverItems(our_jid)
+
+ def items_received(self, items):
+ our_jid = gajim.get_jid_from_account(self.account)
+ for item in items:
+ if 'jid' in item and item['jid'] == our_jid and 'node' in item:
+ # ask to have access model
+ gajim.connections[self.account].request_pb_configuration(
+ item['jid'], item['node'])
+
+ def new_service(self, node, model):
+ self.treestore.append([node, model, ''])
diff --git a/src/dialogs.py b/src/dialogs.py
index 28e77f9de..e50b3abbd 100644
--- a/src/dialogs.py
+++ b/src/dialogs.py
@@ -370,6 +370,125 @@ class ChooseGPGKeyDialog:
self.keys_treeview.set_cursor(path)
+class ChangeActivityDialog:
+ activities = [_('doing_chores'), _('drinking'), _('eating'),
+ _('excercising'), _('grooming'), _('having_appointment'),
+ _('inactive'), _('relaxing'), _('talking'), _('traveling'),
+ _('working'), ]
+ subactivities = [_('at_the_spa'), _('brushing_teeth'),
+ _('buying_groceries'), _('cleaning'), _('coding'),
+ _('commuting'), _('cooking'), _('cycling'), _('day_off'),
+ _('doing_maintenance'), _('doing_the_dishes'),
+ _('doing_the_laundry'), _('driving'), _('gaming'),
+ _('gardening'), _('getting_a_haircut'), _('going_out'),
+ _('hanging_out'), _('having_a_beer'), _('having_a_snack'),
+ _('having_breakfast'), _('having_coffee'),
+ _('having_dinner'), _('having_lunch'), _('having_tea'),
+ _('hiking'), _('in_a_car'), _('in_a_meeting'),
+ _('in_real_life'), _('jogging'), _('on_a_bus'),
+ _('on_a_plane'), _('on_a_train'), _('on_a_trip'),
+ _('on_the_phone'), _('on_vacation'), _('other'),
+ _('partying'), _('playing_sports'), _('reading'),
+ _('rehearsing'), _('running'), _('running_an_errand'),
+ _('scheduled_holiday'), _('shaving'), _('shopping'),
+ _('skiing'), _('sleeping'), _('socializing'),
+ _('studying'), _('sunbathing'), _('swimming'),
+ _('taking_a_bath'), _('taking_a_shower'), _('walking'),
+ _('walking_the_dog'), _('watching_tv'),
+ _('watching_a_movie'), _('working_out'), _('writing'), ]
+ def __init__(self, account):
+ self.account = account
+ self.xml = gtkgui_helpers.get_glade('change_activity_dialog.glade')
+ self.window = self.xml.get_widget('change_activity_dialog')
+ self.window.set_transient_for(gajim.interface.roster.window)
+ self.window.set_title(_('Activity'))
+
+ self.entry = self.xml.get_widget('entry')
+
+ self.combo1 = self.xml.get_widget('combobox1')
+ self.liststore1 = gtk.ListStore(str)
+ self.combo1.set_model(self.liststore1)
+
+ for activity in self.activities:
+ self.liststore1.append((activity,))
+
+ cellrenderertext = gtk.CellRendererText()
+ self.combo1.pack_start(cellrenderertext, True)
+ self.combo1.add_attribute(cellrenderertext, 'text', 0)
+
+ self.combo2 = self.xml.get_widget('combobox2')
+ self.liststore2 = gtk.ListStore(str)
+ self.combo2.set_model(self.liststore2)
+
+ for subactivity in self.subactivities:
+ self.liststore2.append((subactivity,))
+
+ cellrenderertext = gtk.CellRendererText()
+ self.combo2.pack_start(cellrenderertext, True)
+ self.combo2.add_attribute(cellrenderertext, 'text', 0)
+
+ self.xml.signal_autoconnect(self)
+ self.window.show_all()
+
+ def on_ok_button_clicked(self, widget):
+ '''Return activity and messsage (None if no activity selected)'''
+ activity = None
+ subactivity = None
+ message = None
+ active1 = self.combo1.get_active()
+ active2 = self.combo2.get_active()
+ if active1 > -1:
+ activity = self.liststore1[active1][0].decode('utf-8')
+ if active2 > -1:
+ subactivity = self.liststore2[active2][0].decode('utf-8')
+ message = self.entry.get_text().decode('utf-8')
+ from common import pep
+ pep.user_send_activity(self.account, activity,
+ subactivity, message)
+ self.window.destroy()
+
+ def on_cancel_button_clicked(self, widget):
+ self.window.destroy()
+
+class ChangeMoodDialog:
+ moods = [_('afraid'), _('amazed'), _('angry'), _('annoyed'), _('anxious'), _('aroused'), _('ashamed'), _('bored'), _('brave'), _('calm'), _('cold'), _('confused'), _('contented'), _('cranky'), _('curious'), _('depressed'), _('disappointed'), _('disgusted'), _('distracted'), _('embarrassed'), _('excited'), _('flirtatious'), _('frustrated'), _('grumpy'), _('guilty'), _('happy'), _('hot'), _('humbled'), _('humiliated'), _('hungry'), _('hurt'), _('impressed'), _('in_awe'), _('in_love'), _('indignant'), _('interested'), _('intoxicated'), _('invincible'), _('jealous'), _('lonely'), _('mean'), _('moody'), _('nervous'), _('neutral'), _('offended'), _('playful'), _('proud'), _('relieved'), _('remorseful'), _('restless'), _('sad'), _('sarcastic'), _('serious'), _('shocked'), _('shy'), _('sick'), _('sleepy'), _('stressed'), _('surprised'), _('thirsty'), _('worried')]
+ def __init__(self, account):
+ self.account = account
+ self.xml = gtkgui_helpers.get_glade('change_mood_dialog.glade')
+ self.window = self.xml.get_widget('change_mood_dialog')
+ self.window.set_transient_for(gajim.interface.roster.window)
+ self.window.set_title(_('Mood'))
+
+ self.entry = self.xml.get_widget('entry')
+
+ self.combo = self.xml.get_widget('combobox')
+ self.liststore = gtk.ListStore(str)
+ self.combo.set_model(self.liststore)
+
+ for mood in self.moods:
+ self.liststore.append((mood,))
+
+ cellrenderertext = gtk.CellRendererText()
+ self.combo.pack_start(cellrenderertext, True)
+ self.combo.add_attribute(cellrenderertext, 'text', 0)
+ self.xml.signal_autoconnect(self)
+ self.window.show_all()
+
+ def on_ok_button_clicked(self, widget):
+ '''Return mood and messsage (None if no mood selected)'''
+ mood = None
+ message = None
+ active = self.combo.get_active()
+ if active > -1:
+ mood = self.liststore[active][0].decode('utf-8')
+ message = self.entry.get_text().decode('utf-8')
+ from common import pep
+ pep.user_send_mood(self.account, mood, message)
+ self.window.destroy()
+
+ def on_cancel_button_clicked(self, widget):
+ self.window.destroy()
+
class ChangeStatusMessageDialog:
def __init__(self, show = None):
self.show = show
diff --git a/src/gajim.py b/src/gajim.py
index 4453ab8be..33cd8a6b4 100755
--- a/src/gajim.py
+++ b/src/gajim.py
@@ -999,6 +999,11 @@ class Interface:
def handle_event_agent_info_items(self, account, array):
#('AGENT_INFO_ITEMS', account, (agent, node, items))
+ our_jid = gajim.get_jid_from_account(account)
+ if gajim.interface.instances[account].has_key('pep_services') and \
+ array[0] == our_jid:
+ gajim.interface.instances[account]['pep_services'].items_received(
+ array[2])
try:
gajim.connections[account].services_cache.agent_items(array[0],
array[1], array[2])
@@ -2169,6 +2174,11 @@ class Interface:
_('You are already connected to this account with the same resource. Please type a new one'), input_str = gajim.connections[account].server_resource,
is_modal = False, ok_handler = on_ok)
+ def handle_event_pep_access_model(self, account, data):
+ # ('PEP_ACCESS_MODEL', account, (node, model))
+ if self.instances[account].has_key('pep_services'):
+ self.instances[account]['pep_services'].new_service(data[0], data[1])
+
def handle_event_unique_room_id_supported(self, account, data):
'''Receive confirmation that unique_room_id are supported'''
# ('UNIQUE_ROOM_ID_SUPPORTED', server, instance, room_id)
@@ -2551,6 +2561,7 @@ class Interface:
'SEARCH_FORM': self.handle_event_search_form,
'SEARCH_RESULT': self.handle_event_search_result,
'RESOURCE_CONFLICT': self.handle_event_resource_conflict,
+ 'PEP_ACCESS_MODEL': self.handle_event_pep_access_model,
'UNIQUE_ROOM_ID_UNSUPPORTED': \
self.handle_event_unique_room_id_unsupported,
'UNIQUE_ROOM_ID_SUPPORTED': self.handle_event_unique_room_id_supported,
diff --git a/src/roster_window.py b/src/roster_window.py
index da17f4c3b..e7334dae2 100644
--- a/src/roster_window.py
+++ b/src/roster_window.py
@@ -931,7 +931,11 @@ class RosterWindow:
service_disco_menuitem = self.xml.get_widget('service_disco_menuitem')
advanced_menuitem = self.xml.get_widget('advanced_menuitem')
profile_avatar_menuitem = self.xml.get_widget('profile_avatar_menuitem')
+ pep_services_menuitem = self.xml.get_widget('pep_services_menuitem')
+ if not gajim.config.get('use_pep'):
+ pep_services_menuitem.set_no_show_all(True)
+ pep_services_menuitem.hide()
# destroy old advanced menus
for m in self.advanced_menus:
m.destroy()
@@ -954,6 +958,11 @@ class RosterWindow:
self.new_chat_menuitem_handler_id)
self.new_chat_menuitem_handler_id = None
+ if self.pep_services_menuitem_handler_id:
+ pep_services_menuitem.handler_disconnect(
+ self.pep_services_menuitem_handler_id)
+ self.pep_services_menuitem_handler_id = None
+
if self.single_message_menuitem_handler_id:
single_message_menuitem.handler_disconnect(
self.single_message_menuitem_handler_id)
@@ -964,7 +973,6 @@ class RosterWindow:
self.profile_avatar_menuitem_handler_id)
self.profile_avatar_menuitem_handler_id = None
-
# remove the existing submenus
add_new_contact_menuitem.remove_submenu()
service_disco_menuitem.remove_submenu()
@@ -973,6 +981,7 @@ class RosterWindow:
new_chat_menuitem.remove_submenu()
advanced_menuitem.remove_submenu()
profile_avatar_menuitem.remove_submenu()
+ pep_services_menuitem.remove_submenu()
# remove the existing accelerator
if self.have_new_chat_accel:
@@ -1131,7 +1140,21 @@ class RosterWindow:
if len(connected_accounts_with_vcard) > 1:
# 2 or more accounts? make submenus
profile_avatar_sub_menu = gtk.Menu()
+ pep_services_sub_menu = gtk.Menu()
for account in connected_accounts_with_vcard:
+ if gajim.connections[account].pep_supported:
+ # profile, avatar
+ profile_avatar_item = gtk.MenuItem(_('of account %s') % account,
+ False)
+ profile_avatar_sub_menu.append(profile_avatar_item)
+ profile_avatar_item.connect('activate',
+ self.on_profile_avatar_menuitem_activate, account)
+ # PEP services
+ pep_services_item = gtk.MenuItem(_('of account %s') % account,
+ False)
+ pep_services_sub_menu.append(pep_services_item)
+ pep_services_item.connect('activate',
+ self.on_pep_services_menuitem_activate, account)
# profile, avatar
profile_avatar_item = gtk.MenuItem(_('of account %s') % account,
False)
@@ -1140,18 +1163,27 @@ class RosterWindow:
self.on_profile_avatar_menuitem_activate, account)
profile_avatar_menuitem.set_submenu(profile_avatar_sub_menu)
profile_avatar_sub_menu.show_all()
+ pep_services_menuitem.set_submenu(pep_services_sub_menu)
+ pep_services_sub_menu.show_all()
elif len(connected_accounts_with_vcard) == 1: # user has only one account
account = connected_accounts_with_vcard[0]
# profile, avatar
if not self.profile_avatar_menuitem_handler_id:
self.profile_avatar_menuitem_handler_id = \
- profile_avatar_menuitem.connect('activate', self.\
- on_profile_avatar_menuitem_activate, account)
+ profile_avatar_menuitem.connect('activate',
+ self.on_profile_avatar_menuitem_activate, account)
+ # PEP services
+ if not self.pep_services_menuitem_handler_id:
+ self.pep_services_menuitem_handler_id = \
+ pep_services_menuitem.connect('activate',
+ self.on_pep_services_menuitem_activate, account)
if len(connected_accounts_with_vcard) == 0:
profile_avatar_menuitem.set_sensitive(False)
+ pep_services_menuitem.set_sensitive(False)
else:
profile_avatar_menuitem.set_sensitive(True)
+ pep_services_menuitem.set_sensitive(True)
# Advanced Actions
if len(gajim.connections) == 0: # user has no accounts
@@ -2945,6 +2977,12 @@ class RosterWindow:
if url:
helpers.launch_browser_mailer('url', url)
+ def on_change_activity_activate(self, widget, account):
+ dlg = dialogs.ChangeActivityDialog(account)
+
+ def on_change_mood_activate(self, widget, account):
+ dlg = dialogs.ChangeMoodDialog(account)
+
def on_change_status_message_activate(self, widget, account):
show = gajim.SHOW_LIST[gajim.connections[account].connected]
dlg = dialogs.ChangeStatusMessageDialog(show)
@@ -3010,6 +3048,23 @@ class RosterWindow:
sub_menu.append(item)
item.connect('activate', self.change_status, account, 'offline')
+ pep_menuitem = xml.get_widget('pep_menuitem')
+ if gajim.connections[account].pep_supported and gajim.config.get('use_pep'):
+ pep_submenu = gtk.Menu()
+ pep_menuitem.set_submenu(pep_submenu)
+ if gajim.config.get('publish_mood'):
+ item = gtk.MenuItem('Mood')
+ pep_submenu.append(item)
+ item.connect('activate', self.on_change_mood_activate, account)
+ if gajim.config.get('publish_activity'):
+ item = gtk.MenuItem('Activity')
+ pep_submenu.append(item)
+ item.connect('activate', self.on_change_activity_activate,
+ account)
+ else:
+ pep_menuitem.set_no_show_all(True)
+ pep_menuitem.hide()
+
if not gajim.connections[account].gmail_url:
open_gmail_inbox_menuitem.set_no_show_all(True)
open_gmail_inbox_menuitem.hide()
@@ -3038,8 +3093,7 @@ class RosterWindow:
# make some items insensitive if account is offline
if gajim.connections[account].connected < 2:
for widget in [add_contact_menuitem, service_discovery_menuitem,
- join_group_chat_menuitem,
- execute_command_menuitem,
+ join_group_chat_menuitem, execute_command_menuitem, pep_menuitem,
start_chat_menuitem]:
widget.set_sensitive(False)
else:
@@ -3663,14 +3717,14 @@ class RosterWindow:
listener = MusicTrackListener.get()
self._music_track_changed_signal = listener.connect(
'music-track-changed', self._music_track_changed)
- track = listener.get_playing_track()
- self._music_track_changed(listener, track)
+ track = listener.get_playing_track()
+ self._music_track_changed(listener, track)
else:
if self._music_track_changed_signal is not None:
listener = MusicTrackListener.get()
listener.disconnect(self._music_track_changed_signal)
self._music_track_changed_signal = None
- self._music_track_changed(None, None)
+ self._music_track_changed(None, None)
## enable setting status msg from a Last.fm account
def enable_syncing_status_msg_from_lastfm(self, enabled):
@@ -3718,7 +3772,43 @@ class RosterWindow:
except Exception, e:
pass
- def _music_track_changed(self, unused_listener, music_track_info):
+ def _music_track_changed(self, unused_listener, music_track_info,
+ account=''):
+ if gajim.config.get('use_pep'):
+ from common import pep
+ if account == '':
+ accounts = gajim.connections.keys()
+ if music_track_info is None:
+ artist = ''
+ title = ''
+ source = ''
+ track = ''
+ length = ''
+ elif hasattr(music_track_info, 'paused') and \
+ music_track_info.paused == 0:
+ artist = ''
+ title = ''
+ source = ''
+ track = ''
+ length = ''
+ else:
+ artist = music_track_info.artist
+ title = music_track_info.title
+ source = music_track_info.album
+ if account == '':
+ print "Multi accounts"
+ for account in accounts:
+ if not gajim.config.get_per('accounts', account,
+ 'sync_with_global_status'):
+ continue
+ if not gajim.connections[account].pep_supported:
+ continue
+ pep.user_send_tune(account, artist, title, source)
+ else:
+ print "Single account"
+ pep.user_send_tune(account, artist, title, source)
+ return
+ # No PEP
accounts = gajim.connections.keys()
if music_track_info is None:
status_message = ''
@@ -4002,6 +4092,13 @@ class RosterWindow:
else:
gajim.interface.instances['preferences'] = config.PreferencesWindow()
+ def on_pep_services_menuitem_activate(self, widget, account):
+ if gajim.interface.instances[account].has_key('pep_services'):
+ gajim.interface.instances[account]['pep_services'].window.present()
+ else:
+ gajim.interface.instances[account]['pep_services'] = \
+ config.ManagePEPServicesWindow(account)
+
def on_add_new_contact(self, widget, account):
dialogs.AddNewContactWindow(account)
@@ -5249,6 +5346,7 @@ class RosterWindow:
self.new_chat_menuitem_handler_id = False
self.single_message_menuitem_handler_id = False
self.profile_avatar_menuitem_handler_id = False
+ self.pep_services_menuitem_handler_id = False
self.actions_menu_needs_rebuild = True
self.regroup = gajim.config.get('mergeaccounts')
self.clicked_path = None # Used remember on wich row we clicked
@@ -5414,18 +5512,20 @@ class RosterWindow:
self.tooltip = tooltips.RosterTooltip()
self.draw_roster()
- ## Music Track notifications
- ## FIXME: we use a timeout because changing status of
- ## accounts has no effect until they are connected.
- st = gajim.config.get('set_status_msg_from_current_music_track')
- if st:
- gobject.timeout_add(1000,
- self.enable_syncing_status_msg_from_current_music_track,
- st)
+ if gajim.config.get('use_pep'):
+ self.enable_syncing_status_msg_from_current_music_track(gajim.config.get('publish_tune'))
else:
- gobject.timeout_add(1000,
- self.enable_syncing_status_msg_from_lastfm,
- gajim.config.get('set_status_msg_from_lastfm'))
+ ## Music Track notifications
+ ## FIXME: we use a timeout because changing status of
+ ## accounts has no effect until they are connected.
+ st = gajim.config.get('set_status_msg_from_current_music_track')
+ if st:
+ gobject.timeout_add(1000,
+ self.enable_syncing_status_msg_from_current_music_track, st)
+ else:
+ gobject.timeout_add(1000,
+ self.enable_syncing_status_msg_from_lastfm,
+ gajim.config.get('set_status_msg_from_lastfm'))
if gajim.config.get('show_roster_on_startup'):
self.window.show_all()
diff --git a/src/tooltips.py b/src/tooltips.py
index e07ae4675..ecc8741b8 100644
--- a/src/tooltips.py
+++ b/src/tooltips.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
## tooltips.py
##
## Copyright (C) 2005-2006 Dimitur Kirov
@@ -463,6 +464,10 @@ class RosterTooltip(NotificationAreaTooltip):
contact.last_status_time)
properties.append((self.table, None))
else: # only one resource
+
+ #FIXME: User {Mood, Activity, Tune} not shown if there are
+ #multiple resources
+ #FIXME: User {Mood, Activity, Tune} not shown for self
if contact.show:
show = helpers.get_uf_show(contact.show)
if contact.last_status_time:
@@ -494,6 +499,42 @@ class RosterTooltip(NotificationAreaTooltip):
show = '' + show + ''
# we append show below
+ if contact.mood.has_key('mood'):
+ mood_string = 'Mood: %s' % contact.mood['mood'].strip()
+ if contact.mood.has_key('text') and contact.mood['text'] != '':
+ mood_string += ' (%s)' % contact.mood['text'].strip()
+ properties.append((mood_string, None))
+
+ if contact.activity.has_key('activity'):
+ activity = contact.activity['activity'].strip()
+ activity_string = 'Activity: %s' % activity
+ if contact.activity.has_key('subactivity'):
+ activity_sub = contact.activity['subactivity'].strip()
+ activity_string += ' (%s)' % activity_sub
+ else:
+ activity_string += ''
+ if contact.activity.has_key('text'):
+ activity_text = contact.activity['text'].strip()
+ activity_string += ' (%s)' % activity_text
+ properties.append((activity_string, None))
+
+ if contact.tune.has_key('artist') or contact.tune.has_key('title'):
+ if contact.tune.has_key('artist'):
+ artist = contact.tune['artist'].strip()
+ else:
+ artist = _('Unknown Artist')
+ if contact.tune.has_key('title'):
+ title = contact.tune['title'].strip()
+ else:
+ title = _('Unknown Title')
+ if contact.tune.has_key('source'):
+ source = contact.tune['source'].strip()
+ else:
+ source = _('Unknown Source')
+ tune_string = '♪ ' + _('"%(title)s" by %(artist)s\nfrom %(source)s' %\
+ {'title': title, 'artist': artist, 'source': source}) + ' ♪'
+ properties.append((tune_string, None))
+
if contact.status:
status = contact.status.strip()
if status: