X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=src%2Fipypbx%2Fcontrollers.py;h=316e329b8536decb8489c31f62b1200cb3e3c803;hb=bd5981b657ca38beb48faca23950ead346da102d;hp=3263273296fc7a50aabd4ea1ceb92fe449cae839;hpb=6de3b0951814710418d61efeb11d9c48b756eccc;p=ipypbx diff --git a/src/ipypbx/controllers.py b/src/ipypbx/controllers.py index 3263273..316e329 100644 --- a/src/ipypbx/controllers.py +++ b/src/ipypbx/controllers.py @@ -15,7 +15,11 @@ # You should have received a copy of the GNU General Public License # along with IPyPBX. If not, see . -#from ipypbx import models +""" +GUI controllers. +""" + +from ipypbx import http from PyQt4 import QtCore, QtGui, QtSql @@ -25,11 +29,16 @@ class BaseController(QtCore.QObject): Doesn't do anything useful on its own. """ + # TODO: possibly use a separate class for options and a meta-class. fields = () view_list_fields = () view_display_fields = () + view_display_fields_hidden = 'ID', 'Connection ID' + is_bound_to_connection = True + relations = () + delegate = None - def __init__(self, model=None, view_list=None, view_display=None, parent=None, views=None): + def __init__(self, parent=None, views=None): super(BaseController, self).__init__(parent=parent) self.views = views @@ -41,82 +50,106 @@ class BaseController(QtCore.QObject): else classname) self.basename = self.basename[0].lower() + self.basename[1:] - # Are we given an existing model? - if model: - self.model = model - # Otherwise initialize a new model. - else: - self.model = QtSql.QSqlTableModel(parent) - self.model.setTable(self.basename + 's') - - # Create model header from fields list. - for i, field in enumerate(self.fields): - self.model.setHeaderData( - i, QtCore.Qt.Horizontal, QtCore.QVariant(field)) - - # Fetch model data. - self.model.select() - - # Are we given an existing view list? - if view_list: - self.view_list = view_list + # Initialize a new model. + self.model = QtSql.QSqlRelationalTableModel(parent) + self.model.setTable('ipypbxweb_%s' % self.basename.lower()) + self.model.setEditStrategy(self.model.OnRowChange) + + # Create model header from fields list. + for i, field in enumerate(self.fields): + self.model.setHeaderData( + i, QtCore.Qt.Horizontal, + QtCore.QVariant(QtGui.QApplication.translate( + "MainWindow", field, None, + QtGui.QApplication.UnicodeUTF8))) + + # Fetch model data. + self.model.select() + # Otherwise get view list from the parent. - else: - self.view_list = getattr(views, self.basename + 'ViewList') - self.view_list.setModel(self.model) - - # Hide fields not meant for display. - for i, field in enumerate(self.fields): - if field not in self.view_list_fields: - self.view_list.hideColumn(i) - - # Stretch headers to fill all available width. - self.view_list.setSelectionMode(QtGui.QTableView.SingleSelection) - self.view_list.setSelectionBehavior(QtGui.QTableView.SelectRows) - self.view_list.resizeColumnsToContents() - self.view_list.resizeRowsToContents() - self.view_list.horizontalHeader().setStretchLastSection(True) + self.view_list = getattr(views, self.basename + 'ViewList') + self.view_list.setModel(self.model) + self.view_list.setSelectionMode(self.view_list.SingleSelection) + + # Hide fields not meant for display. + for i, field in enumerate(self.fields): + if field not in self.view_list_fields: + self.view_list.hideColumn(i) + + # Stretch headers to fill all available width. + self.view_list.setSelectionMode(QtGui.QTableView.SingleSelection) + self.view_list.setSelectionBehavior(QtGui.QTableView.SelectRows) + self.view_list.resizeColumnsToContents() + self.view_list.resizeRowsToContents() + self.view_list.horizontalHeader().setStretchLastSection(True) + + # Select first row. self.view_list.selectRow(0) - # Are we given an existing view display? - if view_display: - self.view_display = view_display - # Otherwise get view display from the parent. - else: - self.view_display = QtGui.QDataWidgetMapper(parent) - self.view_display.setModel(self.model) - - # If view_display_fields is not send, display all fields except - # the first one that is usually the ID. - display_fields = self.getDisplayFields() - for i, field in enumerate(self.fields): - if field in display_fields: - field_widget = self.getFieldWidget(field) - self.view_display.addMapping(field_widget, i) + # Get view display from the parent. + self.view_display = QtGui.QDataWidgetMapper(parent) + self.view_display.setModel(self.model) + + display_fields = self.getDisplayFields() + + for i, field in enumerate(self.fields): + if field in display_fields: + field_widget = self.getFieldWidget(field) + self.view_display.addMapping(field_widget, i) + + # Set relations for model & view display. + if self.relations: + self.delegate = QtSql.QSqlRelationalDelegate(self) + self.view_display.setItemDelegate(self.delegate) + + for data in self.relations: + column, name, table, display = data + column_index = self.model.fieldIndex(column) + + # SetRelation screws table data filtering? + self.model.setRelation( + column_index, + QtSql.QSqlRelation('ipypbxweb_%s' % table, 'id', display)) + #self.model.select() + + rel = self.model.relationModel(column_index) + + widget = self.getFieldWidget(name) + widget.setModel(self.parent().controllers[table].model) + widget.setModelColumn(rel.fieldIndex(display)) + #widget.setItemDelegate(self.delegate) + # Select first row in the view list. self.view_display.toFirst() - - # Register signals for this controller. - for data in self.getSignalsData(): + + # Signals for this controller. + signal_data = ( + (getattr(self.views, self.basename + 'Add'), 'clicked()', + self.add), + (self.model, 'primeInsert(int,QSqlRecord&)', self.objectAdded), + (self.view_list.selectionModel(), + 'currentRowChanged(QModelIndex,QModelIndex)', + self.view_display, 'setCurrentModelIndex(QModelIndex)'), + (self.parent().controllers.get('connection', self + ).view_list.selectionModel(), + 'currentRowChanged(QModelIndex,QModelIndex)', + self.connectionChange), + (getattr(self.views, self.basename + 'Save'), 'clicked()', + self.save)) + + # Connect all signals. + for data in signal_data: if len(data) == 3: + # Connect to python function. sender, signal, receiver = data QtCore.QObject.connect(sender, QtCore.SIGNAL(signal), receiver) elif len(data) == 4: + # Connect to Qt slot. sender, signal, receiver, slot = data QtCore.QObject.connect( sender, QtCore.SIGNAL(signal), receiver, QtCore.SLOT(slot)) - - def getDisplayFields(self): - """ - Return the list of fields to display. - - If it's not set explicitly, use all defined fields except the first - which usually is the primary key. - """ - return self.view_display_fields or self.fields[1:] - def getFieldWidget(self, field): """ Return widget for given field name. @@ -126,18 +159,16 @@ class BaseController(QtCore.QObject): self.basename + ''.join(word.capitalize() for word in field.split(' '))) - def getSignalsData(self): + def getDisplayFields(self): """ - Default signals built from controller's base name. + Return list of display fields. + + If view_display_fields is not send, display all fields except + the first one that is usually the ID. """ - return ( - (getattr(self.views, self.basename + 'Add'), 'clicked()', self.add), - (self.view_list.selectionModel(), - 'currentRowChanged(QModelIndex,QModelIndex)', - self.view_display, 'setCurrentModelIndex(QModelIndex)'), - (getattr(self.views, self.basename + 'Save'), 'clicked()', - self.save), - ) + return [ + field for field in self.fields + if not field in self.view_display_fields_hidden] def add(self): """ @@ -154,59 +185,234 @@ class BaseController(QtCore.QObject): # Focust to the first displayed field. self.getFieldWidget(self.getDisplayFields()[0]).setFocus() - # Clear all displayed fields. - for field in self.getDisplayFields(): - self.getFieldWidget(field).clear() - - def save(self): + def connectionChange(self, index, row): """ - Save currently selected object. + Overload to handle connection change. """ - #print self.model.isDirty(self.view_list.currentRow()) - #print self.view_list.selectedIndexes()[0].row() + return NotImplemented - #if self.model.isDirty(self.view_display.currentIndex()), self.model.rowCount() + def save(self): + """ + Save to database. + """ + index = self.view_list.currentIndex() self.view_display.submit() + self.view_list.setCurrentIndex(index) self.getFieldWidget('Add').setEnabled(True) - class ConnectionController(BaseController): """ - Connections handler. + Connections controller. """ fields = ( - 'ID', 'Name', 'Local IP Address', 'Local Port', - 'Freeswitch IP Address', 'Freeswitch Port') - view_list_fields = ('Name', 'Freeswitch IP Address', 'Freeswitch Port') + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Name'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Local IP Address'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Local Port'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Freeswitch IP Address'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Freeswitch Port')) + view_list_fields = 'Name', 'Freeswitch IP Address', 'Freeswitch Port' + servers = [] + + def __init__(self, parent=None, views=None): + super(ConnectionController, self).__init__(parent, views) + + for row in range(self.model.rowCount()): + # Get local IP address and port from the table for each row. + server = http.FreeswitchConfigServer(self.parent(), ) + server.setSocketData(*self.getSocketData(row)) + server.startServer() + self.servers.append(server) + + def getSocketData(self, row): + """ + Return socket data for given row number. + """ + record = self.model.record(row) + + # Local IP address. + local_ip_address = record.value('local_ip_address').toString() + + # Local port. + local_port, _ok = record.value('local_port').toInt() + if not _ok: + local_port = None + + # Connection ID. + connection_id, _ok = record.value('id').toInt() + if not _ok: + connection_id = None + + return local_ip_address, local_port, connection_id - def clone(self): + def connectionChange(self, index): + """ + Restart config server on connection change if necessary. + """ + current_row = index.row() + if current_row != -1: + # Select the new row. + connection_id, _ok = index.model().data( + index.sibling(current_row, 0)).toInt() + + # Apply new socket location. + self.servers[current_row].setSocketData( + *self.getSocketData(current_row)) + + def objectAdded(self, row, record): + """ + New connection added. """ - TODO: Clone an existing connection. + self.addServer(*self.getSocketData(row)) + + def addServer(self, host=None, port=None, connection_id=None): + """ + Add a new config server. + """ + server = http.FreeswitchConfigServer(self.model) + server.setSocketData(host, port, connection_id) + server.startServer() + self.servers.append(server) + - This creates a new connection with bound data copied from another one. +class ConnectionChangeListenerController(BaseController): + """ + Mixin class for reacting on connection change. + """ + def connectionChange(self, index): """ + Connection change handler. - def _save(self): + Filters table by a new connection ID and stores last connection ID + locally. """ - Save new or existing connection. + index_row = index.row() + if index_row != -1: + # Get connection_id field value. + connection_id, _ok = index.model().data( + index.sibling(index.row(), 0)).toInt() + self.connection_id = connection_id + + # Filter is customizable in order to allow ugly hacks :-) + self.model.setFilter(self.getFilter(connection_id)) + + # Select first row. + self.view_list.selectRow(0) + + # Create a new object if none exist. + if not self.model.rowCount(): + self.add() + + def getFilter(self, connection_id): + return 'ipypbxweb_%s.connection_id = %i' % ( + self.basename, connection_id) + + def objectAdded(self, row, record): """ - name = unicode(self.parent.ui.connectionName.text()) + Set connection_id from currently selected connection. + """ + record.setValue('connection_id', self.connection_id) - # Add to connection list if we've created it. - if self.currentConnection is None: - #self.currentConnection = models.Connection(store=state.store) - self.connections.append(self.currentConnection) - self.parent.ui.connectionList.addItem(name) + +class SipProfileController(ConnectionChangeListenerController): + """ + SIP Profile controller. + """ + fields = ( + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Connection ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Name'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'External RTP IP'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'External SIP IP'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'RTP IP'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'SIP IP'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'SIP Port'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Accept Blind Registration'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Authenticate Calls'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Is Active')) + view_list_fields = 'Name', 'SIP IP', 'SIP Port' + + +class DomainController(ConnectionChangeListenerController): + """ + Domain controller. + """ + fields = ( + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Connection ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'SIP Profile ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Host Name'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Is Active')) + view_list_fields = 'SIP Profile ID', 'Host Name' + relations = (('sip_profile_id', 'SIP Profile ID', 'sipprofile', 'name'),) + + +class GatewayController(ConnectionChangeListenerController): + """ + Gateway controller. + """ + fields = ( + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Connection ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'SIP Profile ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Name'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Username'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Password'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Realm'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'From Domain'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Extension'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Expire In Seconds'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Retry In Seconds'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Caller ID In From Field'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Is Active')) + view_list_fields = 'SIP Profile ID', 'Name' + relations = (('sip_profile_id', 'SIP Profile ID', 'sipprofile', 'name'),) + - self.currentConnection.name = name - self.currentConnection.local_ip_address = unicode( - self.parent.ui.connectionLocalIpAddress.text()) - self.currentConnection.local_port = int( - self.parent.ui.connectionLocalPort.text()) - self.currentConnection.freeswitch_ip_address = unicode( - self.parent.ui.connectionFreeswitchIpAddress.text()) - self.currentConnection.freeswitch_port = int( - self.parent.ui.connectionFreeswitchPort.text()) +class EndpointController(ConnectionChangeListenerController): + """ + Endpoint controller. + """ + fields = ( + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Connection ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'User ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Password'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Domain ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Is Active')) + view_list_fields = 'User ID', 'Domain ID' + relations = (('domain_id', 'Domain ID', 'domain', 'host_name'),) + - self.currentConnection.checkpoint() +class ExtensionController(ConnectionChangeListenerController): + """ + Extension controller. + """ + fields = ( + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Connection ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Destination Match'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'XML Dialplan'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Domain ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Endpoint ID'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Authenticate Calls'), + QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Is Active')) + view_list_fields = 'Destination Match', + view_display_fields_hidden = 'ID', 'Connection ID', 'Endpoint ID' + relations = ( + ('domain_id', 'Domain ID', 'domain', 'host_name'), +# ('endpoint_id', 'Endpoint ID', 'endpoint', 'user_id'), + ) + + def objectAdded(self, row, record): + record.setValue( + 'xml_dialplan', '') + super(ExtensionController, self).objectAdded(row, record) + + def getFilter(self, connection_id): + # Workaround for Qt bug: + # http://bugreports.qt.nokia.com/browse/QTBUG-8217 . Apparently they + # don't hurry to fix it. + return '1 = 1) or (ipypbxweb_%s.connection_id = %i' % ( + self.basename, connection_id)