X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=src%2Fipypbx%2Fcontrollers.py;h=316e329b8536decb8489c31f62b1200cb3e3c803;hb=bd5981b657ca38beb48faca23950ead346da102d;hp=b233cfc71504fde177501c2bd0d996eae044e37c;hpb=db5ff277e0e5f4d3c2837fc8b50718915f8fc5a6;p=ipypbx diff --git a/src/ipypbx/controllers.py b/src/ipypbx/controllers.py index b233cfc..316e329 100644 --- a/src/ipypbx/controllers.py +++ b/src/ipypbx/controllers.py @@ -15,74 +15,404 @@ # You should have received a copy of the GNU General Public License # along with IPyPBX. If not, see . -from ipypbx import models, state +""" +GUI controllers. +""" +from ipypbx import http +from PyQt4 import QtCore, QtGui, QtSql -class BaseHandler(object): + +class BaseController(QtCore.QObject): """ - Base class for various entity handlers. + Base class for other controllers. Doesn't do anything useful on its own. """ - items = None - current_items = None + # 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, parent=None, views=None): + super(BaseController, self).__init__(parent=parent) + + self.views = views + + # Find out base name. + classname = self.__class__.__name__ + self.basename = ( + classname[:-10] if classname.endswith('Controller') + else classname) + self.basename = self.basename[0].lower() + self.basename[1:] - def __init__(self, parent): - self.parent = parent - self.initState() + # Initialize a new model. + self.model = QtSql.QSqlRelationalTableModel(parent) + self.model.setTable('ipypbxweb_%s' % self.basename.lower()) + self.model.setEditStrategy(self.model.OnRowChange) - def initState(self): + # 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. + 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) + + # 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() + + # 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 getFieldWidget(self, field): + """ + Return widget for given field name. + """ + return getattr( + self.views, + self.basename + ''.join(word.capitalize() + for word in field.split(' '))) + + def getDisplayFields(self): + """ + 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 [ + field for field in self.fields + if not field in self.view_display_fields_hidden] + + def add(self): + """ + Add new object. + """ + # Add a new row to list view. + num_rows = self.model.rowCount() + self.model.insertRows(num_rows, 1) + self.view_list.selectRow(num_rows) + + # Disable adding more than one row. + self.getFieldWidget('Add').setEnabled(False) + + # Focust to the first displayed field. + self.getFieldWidget(self.getDisplayFields()[0]).setFocus() + + def connectionChange(self, index, row): + """ + Overload to handle connection change. + """ return NotImplemented + 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 ConnectionsHandler(BaseHandler): +class ConnectionController(BaseController): """ - Connections handler. + Connections controller. """ - def initState(self): - self.connections = list(state.store.query(models.Connection)) - self.currentConnection = None - - def select(self, index): + fields = ( + 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): """ - Select another connection as current. + Return socket data for given row number. """ - self.currentConnection = state.connections[index] + record = self.model.record(row) + + # Local IP address. + local_ip_address = record.value('local_ip_address').toString() - def clone(self): + # 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 connectionChange(self, index): """ - TODO: Clone an existing connection. + 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() - This creates a new connection with bound data copied from another one. + # Apply new socket location. + self.servers[current_row].setSocketData( + *self.getSocketData(current_row)) + + def objectAdded(self, row, record): + """ + New connection added. + """ + 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) - def add(self): + +class ConnectionChangeListenerController(BaseController): + """ + Mixin class for reacting on connection change. + """ + def connectionChange(self, index): """ - Add new connection. + Connection change handler. + + Filters table by a new connection ID and stores last connection ID + locally. """ - self.currentConnection = None - self.parent.ui.connectionName.setText('') - self.parent.ui.connectionLocalIpAddress.setText('') - self.parent.ui.connectionLocalPort.setText('') - self.parent.ui.connectionFreeswitchIpAddress.setText('') - self.parent.ui.connectionFreeswitchPort.setText('') + 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 - def save(self): + # 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): + """ + Set connection_id from currently selected connection. """ - Save new or existing connection. - """ - name = unicode(self.parent.ui.connectionName.text()) - connection = models.Connection( - name=name, - local_ip_address=unicode( - self.parent.ui.connectionLocalIpAddress.text()), - local_port=int( - self.parent.ui.connectionLocalPort.text()), - freeswitch_ip_address=unicode( - self.parent.ui.connectionFreeswitchIpAddress.text()), - freeswitch_port=int( - self.parent.ui.connectionFreeswitchPort.text())) - if not self.currentConnection: - self.connections.append(connection) - self.currentConnection = connection - self.parent.ui.connectionList.addItem(name) + record.setValue('connection_id', self.connection_id) + + +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'),) + + +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'),) + + +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)