Rewrite GroupchatConfig dialog
- Complete rewrite of the dialog - Use new DataFormWidget
This commit is contained in:
parent
e615a8e2e3
commit
61a791d67c
|
@ -193,6 +193,16 @@ class SyncThreshold(IntEnum):
|
||||||
return str(self.value)
|
return str(self.value)
|
||||||
|
|
||||||
|
|
||||||
|
class MUCUser(IntEnum):
|
||||||
|
JID = 0
|
||||||
|
NICK = 1
|
||||||
|
REASON = 1
|
||||||
|
NICK_OR_REASON = 1
|
||||||
|
ROLE = 2
|
||||||
|
AFFILIATION = 3
|
||||||
|
AFFILIATION_TEXT = 4
|
||||||
|
|
||||||
|
|
||||||
ACTIVITIES = {
|
ACTIVITIES = {
|
||||||
'doing_chores': {
|
'doing_chores': {
|
||||||
'category': _('Doing Chores'),
|
'category': _('Doing Chores'),
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
import weakref
|
||||||
|
|
||||||
import nbxmpp
|
import nbxmpp
|
||||||
|
|
||||||
|
@ -187,28 +188,40 @@ class MUC:
|
||||||
item = iq.setQuery()
|
item = iq.setQuery()
|
||||||
for jid in users_dict:
|
for jid in users_dict:
|
||||||
affiliation = users_dict[jid].get('affiliation')
|
affiliation = users_dict[jid].get('affiliation')
|
||||||
reason = users_dict[jid].get('reason') or None
|
reason = users_dict[jid].get('reason')
|
||||||
|
nick = users_dict[jid].get('nick')
|
||||||
item_tag = item.addChild('item', {'jid': jid,
|
item_tag = item.addChild('item', {'jid': jid,
|
||||||
'affiliation': affiliation})
|
'affiliation': affiliation})
|
||||||
if reason is not None:
|
if reason is not None:
|
||||||
item_tag.setTagData('reason', reason)
|
item_tag.setTagData('reason', reason)
|
||||||
|
|
||||||
|
if nick is not None:
|
||||||
|
item_tag.setAttr('nick', nick)
|
||||||
log.info('Set affiliation for %s: %s', room_jid, users_dict)
|
log.info('Set affiliation for %s: %s', room_jid, users_dict)
|
||||||
self._con.connection.SendAndCallForResponse(
|
self._con.connection.SendAndCallForResponse(
|
||||||
iq, self._default_response, {})
|
iq, self._default_response, {})
|
||||||
|
|
||||||
def get_affiliation(self, room_jid, affiliation):
|
def get_affiliation(self, room_jid, affiliation, success_cb, error_cb):
|
||||||
if not app.account_is_connected(self._account):
|
if not app.account_is_connected(self._account):
|
||||||
return
|
return
|
||||||
iq = nbxmpp.Iq(typ='get', to=room_jid, queryNS=nbxmpp.NS_MUC_ADMIN)
|
iq = nbxmpp.Iq(typ='get', to=room_jid, queryNS=nbxmpp.NS_MUC_ADMIN)
|
||||||
item = iq.setQuery().setTag('item')
|
item = iq.setQuery().setTag('item')
|
||||||
item.setAttr('affiliation', affiliation)
|
item.setAttr('affiliation', affiliation)
|
||||||
log.info('Get affiliation %s for %s', affiliation, room_jid)
|
log.info('Get affiliation %s for %s', affiliation, room_jid)
|
||||||
self._con.connection.SendAndCallForResponse(
|
|
||||||
iq, self._affiliation_received)
|
|
||||||
|
|
||||||
def _affiliation_received(self, stanza):
|
weak_success_cb = weakref.WeakMethod(success_cb)
|
||||||
|
weak_error_cb = weakref.WeakMethod(error_cb)
|
||||||
|
|
||||||
|
self._con.connection.SendAndCallForResponse(
|
||||||
|
iq, self._affiliation_received, {'affiliation': affiliation,
|
||||||
|
'success_cb': weak_success_cb,
|
||||||
|
'error_cb': weak_error_cb})
|
||||||
|
|
||||||
|
def _affiliation_received(self, _con, stanza, affiliation,
|
||||||
|
success_cb, error_cb):
|
||||||
if not nbxmpp.isResultNode(stanza):
|
if not nbxmpp.isResultNode(stanza):
|
||||||
log.info('Error: %s', stanza.getError())
|
if error_cb() is not None:
|
||||||
|
error_cb()(affiliation, stanza.getError())
|
||||||
return
|
return
|
||||||
|
|
||||||
room_jid = stanza.getFrom().getStripped()
|
room_jid = stanza.getFrom().getStripped()
|
||||||
|
@ -222,8 +235,8 @@ class MUC:
|
||||||
log.warning('Invalid JID: %s, ignoring it',
|
log.warning('Invalid JID: %s, ignoring it',
|
||||||
item.getAttr('jid'))
|
item.getAttr('jid'))
|
||||||
continue
|
continue
|
||||||
affiliation = item.getAttr('affiliation')
|
|
||||||
users_dict[jid] = {'affiliation': affiliation}
|
users_dict[jid] = {}
|
||||||
if item.has_attr('nick'):
|
if item.has_attr('nick'):
|
||||||
users_dict[jid]['nick'] = item.getAttr('nick')
|
users_dict[jid]['nick'] = item.getAttr('nick')
|
||||||
if item.has_attr('role'):
|
if item.has_attr('role'):
|
||||||
|
@ -231,9 +244,12 @@ class MUC:
|
||||||
reason = item.getTagData('reason')
|
reason = item.getTagData('reason')
|
||||||
if reason:
|
if reason:
|
||||||
users_dict[jid]['reason'] = reason
|
users_dict[jid]['reason'] = reason
|
||||||
log.info('Affiliations received from %s: %s', room_jid, users_dict)
|
|
||||||
app.nec.push_incoming_event(MucAdminReceivedEvent(
|
log.info('%s affiliations received from %s: %s',
|
||||||
None, conn=self._con, room_jid=room_jid, users_dict=users_dict))
|
affiliation, room_jid, users_dict)
|
||||||
|
|
||||||
|
if success_cb() is not None:
|
||||||
|
success_cb()(self._account, room_jid, affiliation, users_dict)
|
||||||
|
|
||||||
def set_role(self, room_jid, nick, role, reason=''):
|
def set_role(self, room_jid, nick, role, reason=''):
|
||||||
if not app.account_is_connected(self._account):
|
if not app.account_is_connected(self._account):
|
||||||
|
@ -423,10 +439,6 @@ class GcDeclineReceived(NetworkIncomingEvent):
|
||||||
name = 'gc-decline-received'
|
name = 'gc-decline-received'
|
||||||
|
|
||||||
|
|
||||||
class MucAdminReceivedEvent(NetworkIncomingEvent):
|
|
||||||
name = 'muc-admin-received'
|
|
||||||
|
|
||||||
|
|
||||||
class MucOwnerReceivedEvent(NetworkIncomingEvent):
|
class MucOwnerReceivedEvent(NetworkIncomingEvent):
|
||||||
name = 'muc-owner-received'
|
name = 'muc-owner-received'
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,493 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.22.1 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.20"/>
|
||||||
|
<object class="GtkListStore" id="affiliation_store">
|
||||||
|
<columns>
|
||||||
|
<!-- column-name jabberid -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name nickname -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name role -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name affiliation -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name affiliation-text -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name editable-affiliation -->
|
||||||
|
<column type="gboolean"/>
|
||||||
|
<!-- column-name editable-jid -->
|
||||||
|
<column type="gboolean"/>
|
||||||
|
</columns>
|
||||||
|
</object>
|
||||||
|
<object class="GtkListStore" id="combo_store">
|
||||||
|
<columns>
|
||||||
|
<!-- column-name affiliation -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name affiliation-text -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
</columns>
|
||||||
|
<data>
|
||||||
|
<row>
|
||||||
|
<col id="0" translatable="yes">Owner</col>
|
||||||
|
<col id="1">owner</col>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<col id="0" translatable="yes">Admin</col>
|
||||||
|
<col id="1">admin</col>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<col id="0" translatable="yes">Member</col>
|
||||||
|
<col id="1">member</col>
|
||||||
|
</row>
|
||||||
|
</data>
|
||||||
|
</object>
|
||||||
|
<object class="GtkPopover" id="info_popover">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_left">12</property>
|
||||||
|
<property name="margin_right">12</property>
|
||||||
|
<property name="margin_top">12</property>
|
||||||
|
<property name="margin_bottom">12</property>
|
||||||
|
<property name="label" translatable="yes"><b>Jabber-ID</b>
|
||||||
|
&lt;user@domain/resource&gt; (only that resource matches)
|
||||||
|
&lt;user@domain&gt; (any resource matches)
|
||||||
|
&lt;domain/resource&gt; (only that resource matches)
|
||||||
|
&lt;domain&gt; (the domain itself matches, as does any user@domain or domain/resource)
|
||||||
|
</property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<object class="GtkListStore" id="outcast_store">
|
||||||
|
<columns>
|
||||||
|
<!-- column-name jabberid -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name reason -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name dummy -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name affiliation -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name dummy1 -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name dummy2 -->
|
||||||
|
<column type="gboolean"/>
|
||||||
|
<!-- column-name editable-jid -->
|
||||||
|
<column type="gboolean"/>
|
||||||
|
</columns>
|
||||||
|
</object>
|
||||||
|
<object class="GtkGrid" id="grid">
|
||||||
|
<property name="name">GroupchatConfig</property>
|
||||||
|
<property name="width_request">700</property>
|
||||||
|
<property name="height_request">500</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="row_spacing">12</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="hexpand">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="treeview_buttonbox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="margin_left">12</property>
|
||||||
|
<property name="vexpand">False</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuButton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="popover">info_popover</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">dialog-information-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="add_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Only Admins and Owners can modify the affiliation</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<signal name="clicked" handler="_on_add" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">list-add-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="remove_button">
|
||||||
|
<property name="label" translatable="yes">Remove</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Only Admins and Owners can modify the affiliation</property>
|
||||||
|
<signal name="clicked" handler="_on_remove" swapped="no"/>
|
||||||
|
<style>
|
||||||
|
<class name="destructive-action"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButtonBox" id="close_ok_button_box">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<property name="layout_style">end</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="label">gtk-cancel</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<signal name="clicked" handler="_on_cancel" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="pack_type">end</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="label">gtk-ok</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<signal name="clicked" handler="_on_ok" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="pack_type">end</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="pack_type">end</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkStack" id="stack">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<property name="transition_duration">300</property>
|
||||||
|
<property name="transition_type">crossfade</property>
|
||||||
|
<signal name="notify::visible-child-name" handler="_on_switch_page" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkGrid" id="config_grid">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">config</property>
|
||||||
|
<property name="title" translatable="yes">Configuration</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkGrid">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<property name="hscrollbar_policy">never</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeView" id="affiliation_treeview">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="model">affiliation_store</property>
|
||||||
|
<property name="search_column">0</property>
|
||||||
|
<child internal-child="selection">
|
||||||
|
<object class="GtkTreeSelection">
|
||||||
|
<property name="mode">multiple</property>
|
||||||
|
<signal name="changed" handler="_on_selection_changed" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeViewColumn">
|
||||||
|
<property name="resizable">True</property>
|
||||||
|
<property name="sizing">autosize</property>
|
||||||
|
<property name="title">Jabber-ID</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="sort_indicator">True</property>
|
||||||
|
<property name="sort_column_id">0</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererText">
|
||||||
|
<property name="placeholder_text">user@example.org</property>
|
||||||
|
<signal name="edited" handler="_on_jid_edited" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="editable">6</attribute>
|
||||||
|
<attribute name="text">0</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeViewColumn" id="reserved_name_column">
|
||||||
|
<property name="resizable">True</property>
|
||||||
|
<property name="sizing">autosize</property>
|
||||||
|
<property name="title" translatable="yes">Reserved Name</property>
|
||||||
|
<property name="sort_indicator">True</property>
|
||||||
|
<property name="sort_column_id">1</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererText">
|
||||||
|
<signal name="edited" handler="_on_nick_edited" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="editable">6</attribute>
|
||||||
|
<attribute name="text">1</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeViewColumn" id="role_column">
|
||||||
|
<property name="visible">False</property>
|
||||||
|
<property name="resizable">True</property>
|
||||||
|
<property name="sizing">autosize</property>
|
||||||
|
<property name="min_width">100</property>
|
||||||
|
<property name="title" translatable="yes">Role</property>
|
||||||
|
<property name="sort_indicator">True</property>
|
||||||
|
<property name="sort_column_id">2</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererText"/>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="text">2</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeViewColumn">
|
||||||
|
<property name="resizable">True</property>
|
||||||
|
<property name="sizing">autosize</property>
|
||||||
|
<property name="min_width">120</property>
|
||||||
|
<property name="title" translatable="yes">Affiliation</property>
|
||||||
|
<property name="sort_indicator">True</property>
|
||||||
|
<property name="sort_column_id">3</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererCombo">
|
||||||
|
<property name="has_entry">False</property>
|
||||||
|
<property name="model">combo_store</property>
|
||||||
|
<property name="text_column">0</property>
|
||||||
|
<signal name="changed" handler="_on_affiliation_changed" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="editable">5</attribute>
|
||||||
|
<attribute name="text">4</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="margin-12"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">affiliation</property>
|
||||||
|
<property name="title" translatable="yes">Affiliations</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkGrid">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="no_show_all">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeView" id="outcast_treeview">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="model">outcast_store</property>
|
||||||
|
<property name="search_column">0</property>
|
||||||
|
<child internal-child="selection">
|
||||||
|
<object class="GtkTreeSelection">
|
||||||
|
<property name="mode">multiple</property>
|
||||||
|
<signal name="changed" handler="_on_selection_changed" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeViewColumn">
|
||||||
|
<property name="resizable">True</property>
|
||||||
|
<property name="sizing">autosize</property>
|
||||||
|
<property name="title">Jabber-ID</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="sort_indicator">True</property>
|
||||||
|
<property name="sort_column_id">0</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererText">
|
||||||
|
<property name="placeholder_text">user@example.org</property>
|
||||||
|
<signal name="edited" handler="_on_outcast_jid_edited" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="editable">6</attribute>
|
||||||
|
<attribute name="text">0</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeViewColumn">
|
||||||
|
<property name="resizable">True</property>
|
||||||
|
<property name="sizing">autosize</property>
|
||||||
|
<property name="min_width">250</property>
|
||||||
|
<property name="title" translatable="yes">Reason</property>
|
||||||
|
<property name="sort_indicator">True</property>
|
||||||
|
<property name="sort_column_id">1</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererText">
|
||||||
|
<property name="placeholder_text">spam</property>
|
||||||
|
<signal name="edited" handler="_on_reason_edited" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="editable">6</attribute>
|
||||||
|
<attribute name="text">1</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="margin-12"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">outcast</property>
|
||||||
|
<property name="title" translatable="yes">Ban List</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkStackSidebar">
|
||||||
|
<property name="width_request">140</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stack">stack</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="height">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
|
@ -146,6 +146,14 @@ list.settings > row > box {
|
||||||
#StartChatListBox > row { padding: 10px 20px 10px 10px; }
|
#StartChatListBox > row { padding: 10px 20px 10px 10px; }
|
||||||
#StartChatListBox > row:not(.activatable) label { color: @insensitive_fg_color }
|
#StartChatListBox > row:not(.activatable) label { color: @insensitive_fg_color }
|
||||||
|
|
||||||
|
/* GroupchatConfig */
|
||||||
|
#GroupchatConfig buttonbox { margin: 0px 12px 12px 12px; }
|
||||||
|
#GroupchatConfig stack { border-bottom: 1px solid; border-color: @borders;}
|
||||||
|
#GroupchatConfig stacksidebar > scrolledwindow {
|
||||||
|
background-color:@theme_base_color;
|
||||||
|
border-bottom: 1px solid;
|
||||||
|
border-color: @borders;}
|
||||||
|
|
||||||
/* Popover Treeview */
|
/* Popover Treeview */
|
||||||
.popover_treeview { border-radius: 3px; background-color: @theme_bg_color; }
|
.popover_treeview { border-radius: 3px; background-color: @theme_bg_color; }
|
||||||
.popover_treeview { padding: 6px; }
|
.popover_treeview { padding: 6px; }
|
||||||
|
@ -211,6 +219,7 @@ list.settings > row > box {
|
||||||
|
|
||||||
/* Padding/Margins */
|
/* Padding/Margins */
|
||||||
.margin-top6 { margin-top: 6px; }
|
.margin-top6 { margin-top: 6px; }
|
||||||
|
.margin-12 { margin: 12px; }
|
||||||
|
|
||||||
/* Treeview */
|
/* Treeview */
|
||||||
treeview.space { padding: 6px; }
|
treeview.space { padding: 6px; }
|
||||||
|
|
|
@ -728,18 +728,21 @@ class GroupchatControl(ChatControlBase):
|
||||||
_('You may also enter an alternate venue:'), ok_handler=on_ok,
|
_('You may also enter an alternate venue:'), ok_handler=on_ok,
|
||||||
transient_for=self.parent_win.window)
|
transient_for=self.parent_win.window)
|
||||||
|
|
||||||
def _on_configure_room(self, action, param):
|
def _on_configure_room(self, _action, _param):
|
||||||
c = app.contacts.get_gc_contact(
|
contact = app.contacts.get_gc_contact(
|
||||||
self.account, self.room_jid, self.nick)
|
self.account, self.room_jid, self.nick)
|
||||||
if c.affiliation == 'owner':
|
if contact.affiliation == 'owner':
|
||||||
con = app.connections[self.account]
|
con = app.connections[self.account]
|
||||||
con.get_module('MUC').request_config(self.room_jid)
|
con.get_module('MUC').request_config(self.room_jid)
|
||||||
elif c.affiliation == 'admin':
|
elif contact.affiliation == 'admin':
|
||||||
if self.room_jid not in app.interface.instances[self.account][
|
win = app.get_app_window(
|
||||||
'gc_config']:
|
'GroupchatConfig', self.account, self.room_jid)
|
||||||
app.interface.instances[self.account]['gc_config'][
|
if win is not None:
|
||||||
self.room_jid] = GroupchatConfig(self.account,
|
win.present()
|
||||||
self.room_jid)
|
else:
|
||||||
|
GroupchatConfig(self.account,
|
||||||
|
self.room_jid,
|
||||||
|
contact.affiliation)
|
||||||
|
|
||||||
def _on_bookmark_room(self, action, param):
|
def _on_bookmark_room(self, action, param):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -12,237 +12,365 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import nbxmpp
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
|
|
||||||
from gajim.common import app
|
from gajim.common import app
|
||||||
from gajim.common.i18n import _
|
from gajim.common.i18n import _
|
||||||
|
from gajim.common.const import MUCUser
|
||||||
|
from gajim.common.caps_cache import muc_caps_cache
|
||||||
|
|
||||||
from gajim import dataforms_widget
|
from gajim.gtk.dialogs import ErrorDialog
|
||||||
|
from gajim.gtk.dataform import DataFormWidget
|
||||||
from gajim.gtk.util import get_builder
|
from gajim.gtk.util import get_builder
|
||||||
from gajim.gtk.dialogs import InputDialog
|
|
||||||
|
log = logging.getLogger('gajim.gtk.groupchat_config')
|
||||||
|
|
||||||
|
|
||||||
class GroupchatConfig:
|
class GroupchatConfig(Gtk.ApplicationWindow):
|
||||||
def __init__(self, account, room_jid, form=None):
|
def __init__(self, account, jid, own_affiliation, form=None):
|
||||||
|
Gtk.ApplicationWindow.__init__(self)
|
||||||
|
self.set_application(app.app)
|
||||||
|
self.set_position(Gtk.WindowPosition.CENTER)
|
||||||
|
self.set_show_menubar(False)
|
||||||
|
self.set_title(_('Group Chat Configuration'))
|
||||||
|
|
||||||
self.account = account
|
self.account = account
|
||||||
self.room_jid = room_jid
|
self.jid = jid
|
||||||
self.form = form
|
self._own_affiliation = own_affiliation
|
||||||
self.remove_button = {}
|
|
||||||
self.affiliation_treeview = {}
|
|
||||||
self.start_users_dict = {} # list at the beginning
|
|
||||||
self.affiliation_labels = {
|
|
||||||
'outcast': _('Ban List'),
|
|
||||||
'member': _('Member List'),
|
|
||||||
'owner': _('Owner List'),
|
|
||||||
'admin':_('Administrator List')
|
|
||||||
}
|
|
||||||
|
|
||||||
self._ui = get_builder('data_form_window.ui', ['data_form_window'])
|
self._ui = get_builder('groupchat_config.ui')
|
||||||
self.window = self._ui.data_form_window
|
self.add(self._ui.grid)
|
||||||
self.window.set_transient_for(app.interface.roster.window)
|
|
||||||
|
|
||||||
if self.form:
|
# Activate Add button only for Admins and Owners
|
||||||
self.data_form_widget = dataforms_widget.DataFormWidget(self.form)
|
if self._own_affiliation in ('admin', 'owner'):
|
||||||
# hide scrollbar of this data_form_widget, we already have in this
|
self._ui.add_button.set_sensitive(True)
|
||||||
# widget
|
self._ui.add_button.set_tooltip_text('')
|
||||||
sw = self.data_form_widget.xml.get_object(
|
|
||||||
'single_form_scrolledwindow')
|
|
||||||
sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER)
|
|
||||||
if self.form.title:
|
|
||||||
self._ui.title_label.set_text(self.form.title)
|
|
||||||
else:
|
|
||||||
self._ui.title_hseparator.set_no_show_all(True)
|
|
||||||
self._ui.title_hseparator.hide()
|
|
||||||
|
|
||||||
self.data_form_widget.show()
|
visible = muc_caps_cache.supports(jid, nbxmpp.NS_REGISTER)
|
||||||
self._ui.config_vbox.pack_start(self.data_form_widget, True, True, 0)
|
self._ui.reserved_name_column.set_visible(visible)
|
||||||
else:
|
|
||||||
self._ui.title_label.set_no_show_all(True)
|
|
||||||
self._ui.title_label.hide()
|
|
||||||
self._ui.title_hseparator.set_no_show_all(True)
|
|
||||||
self._ui.title_hseparator.hide()
|
|
||||||
self._ui.config_hseparator.set_no_show_all(True)
|
|
||||||
self._ui.config_hseparator.hide()
|
|
||||||
|
|
||||||
# Draw the edit affiliation list things
|
self._form = form
|
||||||
add_on_vbox = self._ui.add_on_vbox
|
self._affiliations = {}
|
||||||
|
self._new_affiliations = {}
|
||||||
|
|
||||||
for affiliation in self.affiliation_labels:
|
|
||||||
self.start_users_dict[affiliation] = {}
|
|
||||||
hbox = Gtk.HBox(spacing=5)
|
|
||||||
add_on_vbox.pack_start(hbox, False, True, 0)
|
|
||||||
|
|
||||||
label = Gtk.Label(label=self.affiliation_labels[affiliation])
|
|
||||||
hbox.pack_start(label, False, True, 0)
|
|
||||||
|
|
||||||
bb = Gtk.HButtonBox()
|
|
||||||
bb.set_layout(Gtk.ButtonBoxStyle.END)
|
|
||||||
bb.set_spacing(5)
|
|
||||||
hbox.pack_start(bb, True, True, 0)
|
|
||||||
add_button = Gtk.Button(stock=Gtk.STOCK_ADD)
|
|
||||||
add_button.connect(
|
|
||||||
'clicked', self.on_add_button_clicked, affiliation)
|
|
||||||
bb.pack_start(add_button, True, True, 0)
|
|
||||||
self.remove_button[affiliation] = Gtk.Button(stock=Gtk.STOCK_REMOVE)
|
|
||||||
self.remove_button[affiliation].set_sensitive(False)
|
|
||||||
self.remove_button[affiliation].connect(
|
|
||||||
'clicked', self.on_remove_button_clicked, affiliation)
|
|
||||||
bb.pack_start(self.remove_button[affiliation], True, True, 0)
|
|
||||||
|
|
||||||
# jid, reason, nick, role
|
|
||||||
liststore = Gtk.ListStore(str, str, str, str)
|
|
||||||
self.affiliation_treeview[affiliation] = Gtk.TreeView(liststore)
|
|
||||||
self.affiliation_treeview[affiliation].get_selection().set_mode(
|
|
||||||
Gtk.SelectionMode.MULTIPLE)
|
|
||||||
self.affiliation_treeview[affiliation].connect(
|
|
||||||
'cursor-changed',
|
|
||||||
self.on_affiliation_treeview_cursor_changed,
|
|
||||||
affiliation)
|
|
||||||
renderer = Gtk.CellRendererText()
|
|
||||||
col = Gtk.TreeViewColumn(_('JID'), renderer)
|
|
||||||
col.add_attribute(renderer, 'text', 0)
|
|
||||||
col.set_resizable(True)
|
|
||||||
col.set_sort_column_id(0)
|
|
||||||
self.affiliation_treeview[affiliation].append_column(col)
|
|
||||||
|
|
||||||
if affiliation == 'outcast':
|
|
||||||
renderer = Gtk.CellRendererText()
|
|
||||||
renderer.set_property('editable', True)
|
|
||||||
renderer.connect('edited', self.on_cell_edited)
|
|
||||||
col = Gtk.TreeViewColumn(_('Reason'), renderer)
|
|
||||||
col.add_attribute(renderer, 'text', 1)
|
|
||||||
col.set_resizable(True)
|
|
||||||
col.set_sort_column_id(1)
|
|
||||||
self.affiliation_treeview[affiliation].append_column(col)
|
|
||||||
elif affiliation == 'member':
|
|
||||||
renderer = Gtk.CellRendererText()
|
|
||||||
col = Gtk.TreeViewColumn(_('Nick'), renderer)
|
|
||||||
col.add_attribute(renderer, 'text', 2)
|
|
||||||
col.set_resizable(True)
|
|
||||||
col.set_sort_column_id(2)
|
|
||||||
self.affiliation_treeview[affiliation].append_column(col)
|
|
||||||
renderer = Gtk.CellRendererText()
|
|
||||||
col = Gtk.TreeViewColumn(_('Role'), renderer)
|
|
||||||
col.add_attribute(renderer, 'text', 3)
|
|
||||||
col.set_resizable(True)
|
|
||||||
col.set_sort_column_id(3)
|
|
||||||
self.affiliation_treeview[affiliation].append_column(col)
|
|
||||||
|
|
||||||
sw = Gtk.ScrolledWindow()
|
|
||||||
sw.add(self.affiliation_treeview[affiliation])
|
|
||||||
add_on_vbox.pack_start(sw, True, True, 0)
|
|
||||||
con = app.connections[self.account]
|
con = app.connections[self.account]
|
||||||
con.get_module('MUC').get_affiliation(self.room_jid, affiliation)
|
for affiliation in ('owner', 'admin', 'member', 'outcast'):
|
||||||
|
con.get_module('MUC').get_affiliation(
|
||||||
|
self.jid,
|
||||||
|
affiliation,
|
||||||
|
self._on_affiliations_received,
|
||||||
|
self._on_affiliations_error)
|
||||||
|
|
||||||
|
if form is not None:
|
||||||
|
self._ui.stack.set_visible_child_name('config')
|
||||||
|
self._data_form_widget = DataFormWidget(form)
|
||||||
|
self._ui.config_grid.add(self._data_form_widget)
|
||||||
|
# self._ui.stack.set_visible_child_name('affiliation')
|
||||||
|
else:
|
||||||
|
self._ui.stack.get_child_by_name('config').hide()
|
||||||
|
self._ui.stack.get_child_by_name('config').set_no_show_all(True)
|
||||||
|
self._ui.stack.set_visible_child_name('affiliation')
|
||||||
|
|
||||||
self._ui.connect_signals(self)
|
self._ui.connect_signals(self)
|
||||||
self.window.connect('delete-event', self.on_cancel_button_clicked)
|
self.show_all()
|
||||||
self.window.show_all()
|
self._ui.stack.notify('visible-child-name')
|
||||||
|
|
||||||
def on_cancel_button_clicked(self, *args):
|
def _get_current_treeview(self):
|
||||||
if self.form:
|
page_name = self._ui.stack.get_visible_child_name()
|
||||||
con = app.connections[self.account]
|
return getattr(self._ui, '%s_treeview' % page_name)
|
||||||
con.get_module('MUC').cancel_config(self.room_jid)
|
|
||||||
self.window.destroy()
|
|
||||||
|
|
||||||
def on_cell_edited(self, _cell, path, new_text):
|
def _on_add(self, *args):
|
||||||
model = self.affiliation_treeview['outcast'].get_model()
|
page_name = self._ui.stack.get_visible_child_name()
|
||||||
new_text = new_text
|
if page_name == 'outcast':
|
||||||
iter_ = model.get_iter(path)
|
affiliation_edit, jid_edit = self._allowed_to_edit('outcast')
|
||||||
model[iter_][1] = new_text
|
text = None
|
||||||
|
affiliation = 'outcast'
|
||||||
def on_add_button_clicked(self, _widget, affiliation):
|
|
||||||
if affiliation == 'outcast':
|
|
||||||
title = _('Banning…')
|
|
||||||
#You can move '\n' before user@domain if that line is TOO BIG
|
|
||||||
prompt = _('<b>Whom do you want to ban?</b>\n\n')
|
|
||||||
elif affiliation == 'member':
|
|
||||||
title = _('Adding Member…')
|
|
||||||
prompt = _('<b>Whom do you want to make a member?</b>\n\n')
|
|
||||||
elif affiliation == 'owner':
|
|
||||||
title = _('Adding Owner…')
|
|
||||||
prompt = _('<b>Whom do you want to make an owner?</b>\n\n')
|
|
||||||
else:
|
else:
|
||||||
title = _('Adding Administrator…')
|
affiliation_edit, jid_edit = self._allowed_to_edit('member')
|
||||||
prompt = _('<b>Whom do you want to make an administrator?</b>\n\n')
|
text = _('Member')
|
||||||
prompt += _(
|
affiliation = 'member'
|
||||||
'Can be one of the following:\n'
|
|
||||||
'1. user@domain/resource (only that resource matches).\n'
|
|
||||||
'2. user@domain (any resource matches).\n'
|
|
||||||
'3. domain/resource (only that resource matches).\n'
|
|
||||||
'4. domain (the domain itself matches, as does any user@domain,\n'
|
|
||||||
'domain/resource, or address containing a subdomain).')
|
|
||||||
|
|
||||||
def on_ok(jid):
|
treeview = self._get_current_treeview()
|
||||||
if not jid:
|
treeview.get_model().append([None,
|
||||||
return
|
None,
|
||||||
model = self.affiliation_treeview[affiliation].get_model()
|
None,
|
||||||
model.append((jid, '', '', ''))
|
affiliation,
|
||||||
InputDialog(title, prompt, ok_handler=on_ok)
|
text,
|
||||||
|
affiliation_edit,
|
||||||
|
jid_edit])
|
||||||
|
|
||||||
def on_remove_button_clicked(self, _widget, affiliation):
|
# Scroll to added row
|
||||||
selection = self.affiliation_treeview[affiliation].get_selection()
|
row = treeview.get_model()[-1]
|
||||||
model, paths = selection.get_selected_rows()
|
treeview.scroll_to_cell(row.path, None, False, 0, 0)
|
||||||
row_refs = []
|
treeview.get_selection().unselect_all()
|
||||||
|
treeview.get_selection().select_path(row.path)
|
||||||
|
|
||||||
|
def _on_remove(self, *args):
|
||||||
|
treeview = self._get_current_treeview()
|
||||||
|
model, paths = treeview.get_selection().get_selected_rows()
|
||||||
|
|
||||||
|
owner_count = self._get_owner_count()
|
||||||
|
references = []
|
||||||
for path in paths:
|
for path in paths:
|
||||||
row_refs.append(Gtk.TreeRowReference.new(model, path))
|
if model[path][MUCUser.AFFILIATION] == 'owner':
|
||||||
for row_ref in row_refs:
|
if owner_count < 2:
|
||||||
path = row_ref.get_path()
|
# There must be at least one owner
|
||||||
iter_ = model.get_iter(path)
|
ErrorDialog(_('Error'),
|
||||||
|
_('A Group Chat needs at least one Owner'))
|
||||||
|
return
|
||||||
|
owner_count -= 1
|
||||||
|
references.append(Gtk.TreeRowReference.new(model, path))
|
||||||
|
|
||||||
|
for ref in references:
|
||||||
|
iter_ = model.get_iter(ref.get_path())
|
||||||
model.remove(iter_)
|
model.remove(iter_)
|
||||||
self.remove_button[affiliation].set_sensitive(False)
|
|
||||||
|
|
||||||
def on_affiliation_treeview_cursor_changed(self, _widget, affiliation):
|
def _on_jid_edited(self, _renderer, path, new_text):
|
||||||
self.remove_button[affiliation].set_sensitive(True)
|
old_text = self._ui.affiliation_store[path][MUCUser.JID]
|
||||||
|
if new_text == old_text:
|
||||||
|
return
|
||||||
|
|
||||||
def affiliation_list_received(self, users_dict):
|
if self._jid_exists(new_text):
|
||||||
"""
|
self._raise_error()
|
||||||
Fill the affiliation treeview
|
return
|
||||||
"""
|
|
||||||
for jid in users_dict:
|
self._ui.affiliation_store[path][MUCUser.JID] = new_text
|
||||||
affiliation = users_dict[jid]['affiliation']
|
|
||||||
if affiliation not in self.affiliation_labels.keys():
|
def _on_outcast_jid_edited(self, _renderer, path, new_text):
|
||||||
# Unknown affiliation or 'none' affiliation, do not show it
|
old_text = self._ui.outcast_store[path][MUCUser.JID]
|
||||||
|
if new_text == old_text:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._jid_exists(new_text):
|
||||||
|
self._raise_error()
|
||||||
|
return
|
||||||
|
|
||||||
|
self._ui.outcast_store[path][MUCUser.JID] = new_text
|
||||||
|
self._ui.outcast_store[path][MUCUser.AFFILIATION] = 'outcast'
|
||||||
|
|
||||||
|
def _on_nick_edited(self, _renderer, path, new_text):
|
||||||
|
self._ui.affiliation_store[path][MUCUser.NICK] = new_text
|
||||||
|
|
||||||
|
def _on_reason_edited(self, _renderer, path, new_text):
|
||||||
|
self._ui.outcast_store[path][MUCUser.REASON] = new_text
|
||||||
|
|
||||||
|
def _on_affiliation_changed(self, cell_renderer_combo,
|
||||||
|
path_string, new_iter):
|
||||||
|
combo_store = cell_renderer_combo.get_property('model')
|
||||||
|
affiliation_text = combo_store.get_value(new_iter, 0)
|
||||||
|
affiliation = combo_store.get_value(new_iter, 1)
|
||||||
|
|
||||||
|
store = self._ui.affiliation_treeview.get_model()
|
||||||
|
|
||||||
|
store[path_string][MUCUser.AFFILIATION] = affiliation
|
||||||
|
store[path_string][MUCUser.AFFILIATION_TEXT] = affiliation_text
|
||||||
|
|
||||||
|
def _on_selection_changed(self, tree_selection):
|
||||||
|
sensitive = bool(tree_selection.count_selected_rows())
|
||||||
|
selected_affiliations = self._get_selected_affiliations(tree_selection)
|
||||||
|
self._set_remove_button_state(sensitive, selected_affiliations)
|
||||||
|
|
||||||
|
def _jid_exists(self, jid):
|
||||||
|
stores = [self._ui.affiliation_store, self._ui.outcast_store]
|
||||||
|
|
||||||
|
for store in stores:
|
||||||
|
for row in store:
|
||||||
|
if row[MUCUser.JID] == jid:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_selected_affiliations(tree_selection):
|
||||||
|
model, paths = tree_selection.get_selected_rows()
|
||||||
|
selected_affiliations = set()
|
||||||
|
for path in paths:
|
||||||
|
selected_affiliations.add(model[path][MUCUser.AFFILIATION])
|
||||||
|
return selected_affiliations
|
||||||
|
|
||||||
|
def _on_switch_page(self, stack, _pspec):
|
||||||
|
page_name = stack.get_visible_child_name()
|
||||||
|
self._set_button_box_state(page_name)
|
||||||
|
if page_name == 'config':
|
||||||
|
return
|
||||||
|
|
||||||
|
treeview = getattr(self._ui, '%s_treeview' % page_name)
|
||||||
|
sensitive = bool(treeview.get_selection().count_selected_rows())
|
||||||
|
|
||||||
|
selected_affiliations = self._get_selected_affiliations(
|
||||||
|
treeview.get_selection())
|
||||||
|
self._set_remove_button_state(sensitive, selected_affiliations)
|
||||||
|
|
||||||
|
def _set_button_box_state(self, page_name):
|
||||||
|
affiliation = self._own_affiliation in ('admin', 'owner')
|
||||||
|
page = page_name != 'config'
|
||||||
|
self._ui.treeview_buttonbox.set_visible(affiliation and page)
|
||||||
|
|
||||||
|
def _set_remove_button_state(self, sensitive, selected_affiliations):
|
||||||
|
if self._own_affiliation not in ('admin', 'owner'):
|
||||||
|
self._ui.remove_button.set_sensitive(False)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._ui.remove_button.set_tooltip_text('')
|
||||||
|
|
||||||
|
if not sensitive:
|
||||||
|
self._ui.remove_button.set_sensitive(False)
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._own_affiliation == 'owner':
|
||||||
|
self._ui.remove_button.set_sensitive(True)
|
||||||
|
return
|
||||||
|
|
||||||
|
if set(['owner', 'admin']).intersection(selected_affiliations):
|
||||||
|
self._ui.remove_button.set_sensitive(False)
|
||||||
|
self._ui.remove_button.set_tooltip_text(
|
||||||
|
_('You are not allowed to modify the affiliation '
|
||||||
|
'of Admins and Owners'))
|
||||||
|
return
|
||||||
|
|
||||||
|
self._ui.remove_button.set_sensitive(True)
|
||||||
|
|
||||||
|
def _get_owner_count(self):
|
||||||
|
owner_count = 0
|
||||||
|
for row in self._ui.affiliation_store:
|
||||||
|
if row[MUCUser.AFFILIATION] == 'owner':
|
||||||
|
owner_count += 1
|
||||||
|
return owner_count
|
||||||
|
|
||||||
|
def _allowed_to_edit(self, affiliation):
|
||||||
|
if self._own_affiliation == 'owner':
|
||||||
|
return True, True
|
||||||
|
|
||||||
|
if self._own_affiliation == 'admin':
|
||||||
|
if affiliation in ('admin', 'owner'):
|
||||||
|
return False, False
|
||||||
|
return False, True
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
def _on_ok(self, *args):
|
||||||
|
if self._own_affiliation in ('admin', 'owner'):
|
||||||
|
self._set_affiliations()
|
||||||
|
|
||||||
|
if self._form is not None and self._own_affiliation == 'owner':
|
||||||
|
form = self._data_form_widget.get_submit_form()
|
||||||
|
con = app.connections[self.account]
|
||||||
|
con.get_module('MUC').set_config(self.jid, form)
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
def _get_diff(self):
|
||||||
|
stores = [self._ui.affiliation_store, self._ui.outcast_store]
|
||||||
|
|
||||||
|
self._new_affiliations = {}
|
||||||
|
for store in stores:
|
||||||
|
for row in store:
|
||||||
|
if not row[MUCUser.JID]:
|
||||||
|
# Ignore empty JID field
|
||||||
continue
|
continue
|
||||||
self.start_users_dict[affiliation][jid] = users_dict[jid]
|
|
||||||
tv = self.affiliation_treeview[affiliation]
|
|
||||||
model = tv.get_model()
|
|
||||||
reason = users_dict[jid].get('reason', '')
|
|
||||||
nick = users_dict[jid].get('nick', '')
|
|
||||||
role = users_dict[jid].get('role', '')
|
|
||||||
model.append((jid, reason, nick, role))
|
|
||||||
|
|
||||||
def on_data_form_window_destroy(self, _widget):
|
attr = 'nick'
|
||||||
del app.interface.instances[self.account]['gc_config'][self.room_jid]
|
if row[MUCUser.AFFILIATION] == 'outcast':
|
||||||
|
attr = 'reason'
|
||||||
|
|
||||||
def on_ok_button_clicked(self, _widget):
|
self._new_affiliations[row[MUCUser.JID]] = {
|
||||||
if self.form:
|
'affiliation': row[MUCUser.AFFILIATION],
|
||||||
form = self.data_form_widget.data_form
|
attr: row[MUCUser.NICK_OR_REASON]}
|
||||||
|
|
||||||
|
old_jids = set(self._affiliations.keys())
|
||||||
|
new_jids = set(self._new_affiliations.keys())
|
||||||
|
remove = old_jids - new_jids
|
||||||
|
add = new_jids - old_jids
|
||||||
|
modified = new_jids - remove - add
|
||||||
|
|
||||||
|
return add, remove, modified
|
||||||
|
|
||||||
|
def _on_cancel(self, *args):
|
||||||
|
if self._form and self._own_affiliation == 'owner':
|
||||||
con = app.connections[self.account]
|
con = app.connections[self.account]
|
||||||
con.get_module('MUC').set_config(self.room_jid, form)
|
con.get_module('MUC').cancel_config(self.jid)
|
||||||
for affiliation in self.affiliation_labels:
|
self.destroy()
|
||||||
users_dict = {}
|
|
||||||
actual_jid_list = []
|
def _set_affiliations(self):
|
||||||
model = self.affiliation_treeview[affiliation].get_model()
|
add, remove, modified = self._get_diff()
|
||||||
iter_ = model.get_iter_first()
|
|
||||||
# add new jid
|
diff_dict = {}
|
||||||
while iter_:
|
for jid in add:
|
||||||
jid = model[iter_][0]
|
diff_dict[jid] = self._new_affiliations[jid]
|
||||||
actual_jid_list.append(jid)
|
|
||||||
if jid not in self.start_users_dict[affiliation] or \
|
for jid in remove:
|
||||||
(affiliation == 'outcast' and 'reason' in self.start_users_dict[
|
diff_dict[jid] = {'affiliation': 'none'}
|
||||||
affiliation][jid] and self.start_users_dict[affiliation][jid]\
|
|
||||||
['reason'] != model[iter_][1]):
|
for jid in modified:
|
||||||
users_dict[jid] = {'affiliation': affiliation}
|
if self._new_affiliations[jid] == self._affiliations[jid]:
|
||||||
|
# Not modified
|
||||||
|
continue
|
||||||
|
|
||||||
|
diff_dict[jid] = self._new_affiliations[jid]
|
||||||
|
if self._new_affiliations[jid]['affiliation'] == 'outcast':
|
||||||
|
# New affiliation is outcast, check if the reason changed.
|
||||||
|
# In case the affiliation was 'admin', 'owner' or 'member'
|
||||||
|
# before, there is no reason.
|
||||||
|
new_reason = self._new_affiliations[jid]['reason']
|
||||||
|
old_reason = self._affiliations[jid].get('reason')
|
||||||
|
if new_reason == old_reason:
|
||||||
|
diff_dict[jid].pop('reason', None)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# New affiliation is not outcast, check if the nick has changed.
|
||||||
|
# In case the affiliation was 'outcast' there is no nick.
|
||||||
|
new_nick = self._new_affiliations[jid]['nick']
|
||||||
|
old_nick = self._affiliations[jid].get('nick')
|
||||||
|
if new_nick == old_nick:
|
||||||
|
diff_dict[jid].pop('nick', None)
|
||||||
|
|
||||||
|
if not diff_dict:
|
||||||
|
# No changes were made
|
||||||
|
return
|
||||||
|
con = app.connections[self.account]
|
||||||
|
con.get_module('MUC').set_affiliation(self.jid, diff_dict)
|
||||||
|
|
||||||
|
def _on_affiliations_error(self, affiliation, error):
|
||||||
|
log.info('Error while requesting %s affiliations: %s',
|
||||||
|
affiliation, error)
|
||||||
|
|
||||||
|
def _on_affiliations_received(self, _account, _room_jid,
|
||||||
|
affiliation, users):
|
||||||
|
|
||||||
if affiliation == 'outcast':
|
if affiliation == 'outcast':
|
||||||
users_dict[jid]['reason'] = model[iter_][1]
|
self._ui.stack.get_child_by_name('outcast').show()
|
||||||
iter_ = model.iter_next(iter_)
|
|
||||||
# remove removed one
|
for jid, attrs in users.items():
|
||||||
for jid in self.start_users_dict[affiliation]:
|
affiliation_edit, jid_edit = self._allowed_to_edit(affiliation)
|
||||||
if jid not in actual_jid_list:
|
if affiliation == 'outcast':
|
||||||
users_dict[jid] = {'affiliation': 'none'}
|
reason = attrs.get('reason')
|
||||||
if users_dict:
|
self._ui.outcast_store.append(
|
||||||
con = app.connections[self.account]
|
[jid,
|
||||||
con.get_module('MUC').set_affiliation(
|
reason,
|
||||||
self.room_jid, users_dict)
|
None,
|
||||||
self.window.destroy()
|
affiliation,
|
||||||
|
None,
|
||||||
|
affiliation_edit,
|
||||||
|
jid_edit])
|
||||||
|
self._affiliations[jid] = {'affiliation': affiliation,
|
||||||
|
'reason': reason}
|
||||||
|
else:
|
||||||
|
nick = attrs.get('nick')
|
||||||
|
role = attrs.get('role')
|
||||||
|
self._ui.affiliation_store.append(
|
||||||
|
[jid,
|
||||||
|
nick,
|
||||||
|
role,
|
||||||
|
affiliation,
|
||||||
|
_(affiliation.capitalize()),
|
||||||
|
affiliation_edit,
|
||||||
|
jid_edit])
|
||||||
|
self._affiliations[jid] = {'affiliation': affiliation,
|
||||||
|
'nick': nick}
|
||||||
|
if role is not None:
|
||||||
|
self._ui.role_column.set_visible(True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _raise_error():
|
||||||
|
ErrorDialog(_('Error'),
|
||||||
|
_('An entry with this Jabber-ID already exists'))
|
||||||
|
|
|
@ -601,16 +601,12 @@ class Interface:
|
||||||
_('%(jid)s has been invited in this room') % {
|
_('%(jid)s has been invited in this room') % {
|
||||||
'jid': jid}, graphics=False)
|
'jid': jid}, graphics=False)
|
||||||
del app.automatic_rooms[account][obj.jid]
|
del app.automatic_rooms[account][obj.jid]
|
||||||
elif obj.jid not in self.instances[account]['gc_config']:
|
else:
|
||||||
self.instances[account]['gc_config'][obj.jid] = \
|
win = app.get_app_window('GroupchatConfig', account, obj.jid)
|
||||||
GroupchatConfig(account, obj.jid, obj.dataform)
|
if win is not None:
|
||||||
|
win.present()
|
||||||
def handle_event_gc_affiliation(self, obj):
|
else:
|
||||||
#('GC_AFFILIATION', account, (room_jid, users_dict))
|
GroupchatConfig(account, obj.jid, 'owner', obj.dataform)
|
||||||
account = obj.conn.name
|
|
||||||
if obj.room_jid in self.instances[account]['gc_config']:
|
|
||||||
self.instances[account]['gc_config'][obj.room_jid].\
|
|
||||||
affiliation_list_received(obj.users_dict)
|
|
||||||
|
|
||||||
def handle_event_gc_decline(self, obj):
|
def handle_event_gc_decline(self, obj):
|
||||||
gc_control = self.msg_win_mgr.get_gc_control(obj.room_jid, obj.account)
|
gc_control = self.msg_win_mgr.get_gc_control(obj.room_jid, obj.account)
|
||||||
|
@ -1531,7 +1527,6 @@ class Interface:
|
||||||
'message-not-sent': [self.handle_event_msgnotsent],
|
'message-not-sent': [self.handle_event_msgnotsent],
|
||||||
'message-sent': [self.handle_event_msgsent],
|
'message-sent': [self.handle_event_msgsent],
|
||||||
'metacontacts-received': [self.handle_event_metacontacts],
|
'metacontacts-received': [self.handle_event_metacontacts],
|
||||||
'muc-admin-received': [self.handle_event_gc_affiliation],
|
|
||||||
'muc-owner-received': [self.handle_event_gc_config],
|
'muc-owner-received': [self.handle_event_gc_config],
|
||||||
'oauth2-credentials-required': [self.handle_oauth2_credentials],
|
'oauth2-credentials-required': [self.handle_oauth2_credentials],
|
||||||
'our-show': [self.handle_event_status],
|
'our-show': [self.handle_event_status],
|
||||||
|
|
Loading…
Reference in New Issue