6f7f173b9ae72aacfb350ff819f46f3755124686
[ipypbx] / src / ipypbx / controllers.py
1 # Copyright (c) Stas Shtin, 2010
2
3 # This file is part of IPyPBX.
4
5 # IPyPBX is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9
10 # IPyPBX is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14
15 # You should have received a copy of the GNU General Public License
16 # along with IPyPBX.  If not, see <http://www.gnu.org/licenses/>.
17
18 from PyQt4 import QtCore, QtGui, QtSql
19
20
21 class BaseController(QtCore.QObject):
22     """
23     Base class for other controllers.
24
25     Doesn't do anything useful on its own.
26     """
27     # TODO: possibly use a separate class for options and a meta-class.
28     fields = ()
29     view_list_fields = ()
30     view_display_fields = ()
31     view_display_fields_hidden = 'ID', 'Connection ID'
32     is_bound_to_connection = True
33     
34     def __init__(self, model=None, view_list=None, view_display=None, parent=None, views=None):
35         super(BaseController, self).__init__(parent=parent)
36
37         self.views = views
38         
39         # Find out base name.
40         classname = self.__class__.__name__
41         self.basename = (
42             classname[:-10] if classname.endswith('Controller')
43             else classname)
44         self.basename = self.basename[0].lower() + self.basename[1:]
45
46         # Are we given an existing model?
47         if model:
48             self.model = model
49         # Otherwise initialize a new model.
50         else:
51             self.model = QtSql.QSqlTableModel(parent)
52             self.model.setTable('ipypbxweb_%s' % self.basename.lower())
53             self.model.setEditStrategy(self.model.OnRowChange)
54
55             # Create model header from fields list.
56             for i, field in enumerate(self.fields):
57                 self.model.setHeaderData(
58                     i, QtCore.Qt.Horizontal,
59                     QtCore.QVariant(QtGui.QApplication.translate(
60                         "MainWindow", field, None,
61                         QtGui.QApplication.UnicodeUTF8)))
62
63             # Fetch model data.
64             self.model.select()
65
66         # Are we given an existing view list?
67         if view_list:
68             self.view_list = view_list
69         # Otherwise get view list from the parent.            
70         else:
71             self.view_list = getattr(views, self.basename + 'ViewList')
72             self.view_list.setModel(self.model)
73
74             # Hide fields not meant for display.
75             for i, field in enumerate(self.fields):
76                 if field not in self.view_list_fields:
77                     self.view_list.hideColumn(i)
78
79             # Stretch headers to fill all available width.
80             self.view_list.setSelectionMode(QtGui.QTableView.SingleSelection)
81             self.view_list.setSelectionBehavior(QtGui.QTableView.SelectRows)
82             self.view_list.resizeColumnsToContents()
83             self.view_list.resizeRowsToContents()
84             self.view_list.horizontalHeader().setStretchLastSection(True)
85
86         # Select first row.
87         self.view_list.selectRow(0)
88
89         # Are we given an existing view display?
90         if view_display:
91             self.view_display = view_display
92         # Otherwise get view display from the parent.
93         else:
94             self.view_display = QtGui.QDataWidgetMapper(parent)
95             self.view_display.setModel(self.model)
96
97             display_fields = self.getDisplayFields()
98             
99             for i, field in enumerate(self.fields):
100                 if field in display_fields:
101                     field_widget = self.getFieldWidget(field)
102                     self.view_display.addMapping(field_widget, i)
103
104         # Select first row in the view list.
105         self.view_display.toFirst()
106         
107         # Register signals for this controller.
108         for data in self.getSignalsData():
109             if len(data) == 3:
110                 sender, signal, receiver = data
111                 QtCore.QObject.connect(sender, QtCore.SIGNAL(signal), receiver)
112             elif len(data) == 4:
113                 sender, signal, receiver, slot = data
114                 QtCore.QObject.connect(
115                     sender, QtCore.SIGNAL(signal), receiver, QtCore.SLOT(slot))
116                                        
117     def getFieldWidget(self, field):
118         """
119         Return widget for given field name.
120         """
121         return getattr(
122             self.views,
123             self.basename + ''.join(word.capitalize()
124                                     for word in field.split(' ')))
125
126     def getDisplayFields(self):
127         """
128         Return list of display fields.
129         
130         If view_display_fields is not send, display all fields except
131         the first one that is usually the ID.
132         """
133         return [
134             field for field in self.fields
135             if not field in self.view_display_fields_hidden]        
136
137     def getSignalsData(self):
138         """
139         Default signals built from controller's base name.
140         """
141         # Default signals handle row selection, Add and Save buttons.
142         return [
143             (getattr(self.views, self.basename + 'Add'), 'clicked()', self.add),
144             (self.view_list.selectionModel(),
145              'currentRowChanged(QModelIndex,QModelIndex)',
146              self.view_display, 'setCurrentModelIndex(QModelIndex)'),
147             (getattr(self.views, self.basename + 'Save'), 'clicked()',
148              self.save)]
149
150     def add(self):
151         """
152         Add new object.
153         """
154         # Add a new row to list view.
155         num_rows = self.model.rowCount()
156         self.model.insertRows(num_rows, 1)
157         self.view_list.selectRow(num_rows)
158
159         # Disable adding more than one row.
160         self.getFieldWidget('Add').setEnabled(False)
161
162         # Focust to the first displayed field.
163         self.getFieldWidget(self.getDisplayFields()[0]).setFocus()
164
165         # TODO: set default values?
166
167     def save(self):
168         """
169         Save to database.
170         """
171         self.view_display.submit()
172         self.getFieldWidget('Add').setEnabled(True)
173
174
175 class ConnectionController(BaseController):
176     """
177     Connections controller.
178     """
179     fields = (
180         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'),
181         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Name'),
182         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Local IP Address'),
183         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Local Port'),
184         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Freeswitch IP Address'),
185         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Freeswitch Port'))
186     view_list_fields = 'Name', 'Freeswitch IP Address', 'Freeswitch Port'
187
188
189 class ConnectionChangeListenerController(BaseController):
190     """
191     Mixin class for reacting on connection change.
192     """
193     def getSignalsData(self):
194         """
195         Listen to connection change signal.
196         """
197         connection_controller = self.parent().controllers[0]
198
199         signals = [
200             (connection_controller.view_list.selectionModel(),
201              'currentRowChanged(QModelIndex,QModelIndex)',
202              self.connectionChange),
203             (self.model, 'primeInsert(int,QSqlRecord&)',
204              self.setConnectionId)] 
205         signals.extend(super(
206             ConnectionChangeListenerController, self).getSignalsData())
207         return signals
208         
209     def connectionChange(self, index):
210         """
211         Connection change handler.
212
213         Filters table by a new connection ID and stores last connection ID
214         locally.
215         """
216         if index.row() != -1:
217             connection_id, ok = index.model().data(
218                 index.sibling(index.row(), 0)).toInt()
219             self.connection_id = connection_id
220             self.model.setFilter('connection_id = %i' % connection_id)
221
222     def setConnectionId(self, row, record):
223         record.setValue('connection_id', self.connection_id)
224
225         
226 class SipProfileController(ConnectionChangeListenerController):
227     """
228     SIP Profile controller.
229     """
230     fields = (
231         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'),
232         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Connection ID'),
233         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Name'),
234         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'External RTP IP'),
235         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'External SIP IP'),
236         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'RTP IP'),
237         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'SIP IP'),
238         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'SIP Port'),
239         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Accept Blind Registration'),
240         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Authenticate Calls'),
241         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Is Active'))
242     view_list_fields = 'Name', 'SIP IP', 'SIP Port'
243     
244
245 class DomainController(ConnectionChangeListenerController):
246     """
247     Domain controller.
248     """
249     fields = (
250         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'),
251         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Connection ID'),
252         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'SIP Profile ID'),
253         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Host Name'),
254         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Is Active'))
255     view_list_fields = 'SIP Profile', 'Host Name'
256
257
258 class GatewayController(ConnectionChangeListenerController):
259     """
260     Gateway controller.
261     """
262     fields = (
263         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'),
264         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Connection ID'),
265         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'SIP Profile ID'),
266         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Name'),
267         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Username'),
268         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Password'),
269         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Realm'),
270         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'From Domain'),
271         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Expire In Seconds'),
272         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Retry In Seconds'),
273         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Caller ID In From Field'),
274         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Is Active'))
275     view_list_fields = 'SIP Profile ID', 'Name'
276
277
278 class EndpointController(ConnectionChangeListenerController):
279     """
280     Endpoint controller.
281     """
282     fields = (
283         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'),
284         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Connection ID'),
285         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'User ID'),
286         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Password'),
287         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Domain ID'),
288         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Is Active'))
289     view_list_fields = 'User ID', 'Domain ID'
290     
291
292 class ExtensionController(ConnectionChangeListenerController):
293     """
294     Extension controller.
295     """
296     fields = (
297         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'ID'),
298         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Connection ID'),
299         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Destination Match'),
300         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'XML Dialplan'),
301         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Domain ID'),
302         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Endpoint ID'),
303         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Authenticate Calls'),
304         QtCore.QT_TRANSLATE_NOOP('MainWindow', 'Is Active'))
305     view_list_fields = 'Destination Match',
306