bugfix: error when creating a new db
[openerp-client.git] / bin / modules / gui / main.py
blobeb8b0fe903e0f5b7b55c071660eec05e174718b1
1 ##############################################################################
3 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
5 # $Id$
7 # WARNING: This program as such is intended to be used by professional
8 # programmers who take the whole responsability of assessing all potential
9 # consequences resulting from its eventual inadequacies and bugs
10 # End users who are looking for a ready-to-use solution with commercial
11 # garantees and support are strongly adviced to contract a Free Software
12 # Service Company
14 # This program is Free Software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; either version 2
17 # of the License, or (at your option) any later version.
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 ##############################################################################
30 import time
31 import os
32 import gettext
33 import urlparse
35 import gobject
36 import gtk
37 from gtk import glade
39 import rpc
41 import service
42 import options
43 import common
45 from window import win_preference
46 import tools
47 import re
48 import xmlrpclib
49 import base64
51 def _refresh_dblist(db_widget, url, dbtoload=None):
52 if not dbtoload:
53 dbtoload = options.options['login.db']
54 index = 0
55 liststore = db_widget.get_model()
56 liststore.clear()
57 result = rpc.session.list_db(url)
58 if result==-1:
59 return -1
60 for db_num, db_name in enumerate(rpc.session.list_db(url)):
61 liststore.append([db_name])
62 if db_name == dbtoload:
63 index = db_num
64 db_widget.set_active(index)
65 return len(liststore)
67 def _refresh_langlist(lang_widget, url):
68 liststore = lang_widget.get_model()
69 liststore.clear()
70 lang_list = rpc.session.db_exec_no_except(url, 'list_lang')
71 lang_list.append( ('en_US','English') )
72 for key,val in lang_list:
73 liststore.insert(0, (val,key))
74 lang_widget.set_active(0)
75 return lang_list
77 def _server_ask(server_widget, parent=None):
78 result = False
79 win_gl = glade.XML(common.terp_path("terp.glade"),"win_server",gettext.textdomain())
80 win = win_gl.get_widget('win_server')
81 if not parent:
82 parent = service.LocalService('gui.main').window
83 win.set_transient_for(parent)
84 win.set_icon(common.TINYERP_ICON)
85 win.show_all()
86 win.set_default_response(gtk.RESPONSE_OK)
87 host_widget = win_gl.get_widget('ent_host')
88 port_widget = win_gl.get_widget('ent_port')
89 protocol_widget = win_gl.get_widget('protocol')
91 protocol={'XML-RPC': 'http://',
92 'XML-RPC secure': 'https://',
93 'NET-RPC (faster)': 'socket://',}
94 listprotocol = gtk.ListStore(str)
95 protocol_widget.set_model(listprotocol)
98 m = re.match('^(http[s]?://|socket://)([\w.-]+):(\d{1,5})$', server_widget.get_text())
99 if m:
100 host_widget.set_text(m.group(2))
101 port_widget.set_text(m.group(3))
103 index = 0
104 i = 0
105 for p in protocol:
106 listprotocol.append([p])
107 if m and protocol[p] == m.group(1):
108 index = i
109 i += 1
110 protocol_widget.set_active(index)
112 res = win.run()
113 if res == gtk.RESPONSE_OK:
114 protocol = protocol[protocol_widget.get_active_text()]
115 url = '%s%s:%s' % (protocol, host_widget.get_text(), port_widget.get_text())
116 server_widget.set_text(url)
117 result = url
118 parent.present()
119 win.destroy()
120 return result
123 class db_login(object):
124 def __init__(self):
125 self.win_gl = glade.XML(common.terp_path("terp.glade"),"win_login",gettext.textdomain())
127 def refreshlist(self, widget, db_widget, label, url, butconnect=False):
128 res = _refresh_dblist(db_widget, url)
129 if res == -1:
130 label.set_label('<b>'+_('Could not connect to server !')+'</b>')
131 db_widget.hide()
132 label.show()
133 if butconnect:
134 butconnect.set_sensitive(False)
135 elif res==0:
136 label.set_label('<b>'+_('No database found, you must create one !')+'</b>')
137 db_widget.hide()
138 label.show()
139 if butconnect:
140 butconnect.set_sensitive(False)
141 else:
142 label.hide()
143 db_widget.show()
144 if butconnect:
145 butconnect.set_sensitive(True)
146 return res
148 def refreshlist_ask(self,widget, server_widget, db_widget, label, butconnect = False, url=False, parent=None):
149 url = _server_ask(server_widget, parent) or url
150 return self.refreshlist(widget, db_widget, label, url, butconnect)
152 def run(self, dbname=None, parent=None):
153 uid = 0
154 win = self.win_gl.get_widget('win_login')
155 if not parent:
156 parent = service.LocalService('gui.main').window
157 win.set_transient_for(parent)
158 win.set_icon(common.TINYERP_ICON)
159 win.show_all()
160 img = self.win_gl.get_widget('image_tinyerp')
161 img.set_from_file(common.terp_path_pixmaps('tinyerp.png'))
162 login = self.win_gl.get_widget('ent_login')
163 passwd = self.win_gl.get_widget('ent_passwd')
164 server_widget = self.win_gl.get_widget('ent_server')
165 but_connect = self.win_gl.get_widget('button_connect')
166 db_widget = self.win_gl.get_widget('combo_db')
167 change_button = self.win_gl.get_widget('but_server')
168 label = self.win_gl.get_widget('combo_label')
169 label.hide()
171 host = options.options['login.server']
172 port = options.options['login.port']
173 protocol = options.options['login.protocol']
175 url = '%s%s:%s' % (protocol, host, port)
176 server_widget.set_text(url)
177 login.set_text(options.options['login.login'])
179 # construct the list of available db and select the last one used
180 liststore = gtk.ListStore(str)
181 db_widget.set_model(liststore)
182 cell = gtk.CellRendererText()
183 db_widget.pack_start(cell, True)
184 db_widget.add_attribute(cell, 'text', 0)
186 res = self.refreshlist(None, db_widget, label, url, but_connect)
187 change_button.connect_after('clicked', self.refreshlist_ask, server_widget, db_widget, label, but_connect, url, win)
189 if dbname:
190 iter = liststore.get_iter_root()
191 while iter:
192 if liststore.get_value(iter, 0)==dbname:
193 db_widget.set_active_iter(iter)
194 break
195 iter = liststore.iter_next(iter)
197 res = win.run()
198 m = re.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', server_widget.get_text() or '')
199 if m:
200 options.options['login.server'] = m.group(2)
201 options.options['login.login'] = login.get_text()
202 options.options['login.port'] = m.group(3)
203 options.options['login.protocol'] = m.group(1)
204 options.options['login.db'] = db_widget.get_active_text()
205 result = (login.get_text(), passwd.get_text(), m.group(2), m.group(3), m.group(1), db_widget.get_active_text())
206 else:
207 parent.present()
208 win.destroy()
209 raise Exception('QueryCanceled')
210 if res <> gtk.RESPONSE_OK:
211 parent.present()
212 win.destroy()
213 raise Exception('QueryCanceled')
214 parent.present()
215 win.destroy()
216 return result
218 class db_create(object):
219 def set_sensitive(self, sensitive):
220 if sensitive:
221 label = self.dialog.get_widget('db_label_info')
222 label.set_text(_('Do not use special characters !'))
223 self.dialog.get_widget('button_db_ok').set_sensitive(True)
224 else:
225 label = self.dialog.get_widget('db_label_info')
226 label.set_markup('<b>'+_('Can not connect to server, please change it !')+'</b>')
227 self.dialog.get_widget('button_db_ok').set_sensitive(False)
228 return sensitive
230 def server_change(self, widget=None, parent=None):
231 url = _server_ask(self.server_widget)
232 try:
233 if self.lang_widget and url:
234 _refresh_langlist(self.lang_widget, url)
235 self.set_sensitive(True)
236 except:
237 self.set_sensitive(False)
238 return False
239 return url
241 def __init__(self, sig_login):
242 self.dialog = glade.XML(common.terp_path("terp.glade"), "win_createdb", gettext.textdomain())
243 self.sig_login = sig_login
245 def run(self, parent=None):
246 win = self.dialog.get_widget('win_createdb')
247 win.set_default_response(gtk.RESPONSE_OK)
248 if not parent:
249 parent = service.LocalService('gui.main').window
250 win.set_transient_for(parent)
251 win.show_all()
252 lang_dict = {}
253 pass_widget = self.dialog.get_widget('ent_password_new')
254 self.server_widget = self.dialog.get_widget('ent_server_new')
255 change_button = self.dialog.get_widget('but_server_new')
256 self.lang_widget = self.dialog.get_widget('db_create_combo')
257 self.db_widget = self.dialog.get_widget('ent_db_new')
258 demo_widget = self.dialog.get_widget('check_demo')
259 demo_widget.set_active(True)
261 change_button.connect_after('clicked', self.server_change, win)
262 protocol = options.options['login.protocol']
263 url = '%s%s:%s' % (protocol, options.options['login.server'], options.options['login.port'])
265 self.server_widget.set_text(url)
266 liststore = gtk.ListStore(str, str)
267 self.lang_widget.set_model(liststore)
268 try:
269 _refresh_langlist(self.lang_widget, url)
270 except:
271 self.set_sensitive(False)
273 while True:
274 res = win.run()
275 db_name = self.db_widget.get_text()
276 if (res==gtk.RESPONSE_OK) and ((not db_name) or (not re.match('^[a-zA-Z][a-zA-Z0-9_]+$', db_name))):
277 common.warning(_('The database name must contain only normal characters or "_".\nYou must avoid all accents, space or special characters.'), _('Bad database name !'), parent=parent)
279 else:
280 break
281 demo_data = demo_widget.get_active()
283 langidx = self.lang_widget.get_active_iter()
284 langreal = langidx and self.lang_widget.get_model().get_value(langidx,1)
285 passwd = pass_widget.get_text()
286 url = self.server_widget.get_text()
287 m = re.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url or '')
288 if m:
289 options.options['login.server'] = m.group(2)
290 options.options['login.port'] = m.group(3)
291 options.options['login.protocol'] = m.group(1)
292 parent.present()
293 win.destroy()
295 if res == gtk.RESPONSE_OK:
296 try:
297 id=rpc.session.db_exec(url, 'list')
298 if db_name in id:
299 raise Exception('DbExist')
300 id = rpc.session.db_exec(url, 'create', passwd, db_name, demo_data, langreal)
301 win = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
302 win.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
303 vbox = gtk.VBox(False, 0)
304 hbox = gtk.HBox(False, 13)
305 hbox.set_border_width(10)
306 img = gtk.Image()
307 img.set_from_stock('gtk-dialog-info', gtk.ICON_SIZE_DIALOG)
308 hbox.pack_start(img, expand=True, fill=False)
309 vbox2 = gtk.VBox(False, 0)
310 label = gtk.Label()
311 label.set_markup(_('<b>Operation in progress</b>'))
312 label.set_alignment(0.0, 0.5)
313 vbox2.pack_start(label, expand=True, fill=False)
314 vbox2.pack_start(gtk.HSeparator(), expand=True, fill=True)
315 vbox2.pack_start(gtk.Label(_("Please wait,\nthis operation may take a while...")), expand=True, fill=False)
316 hbox.pack_start(vbox2, expand=True, fill=True)
317 vbox.pack_start(hbox)
318 pb = gtk.ProgressBar()
319 pb.set_orientation(gtk.PROGRESS_LEFT_TO_RIGHT)
320 vbox.pack_start(pb, expand=True, fill=False)
321 win.add(vbox)
322 if not parent:
323 parent = service.LocalService('gui.main').window
324 win.set_transient_for(parent)
325 win.show_all()
326 self.timer = gobject.timeout_add(1000, self.progress_timeout, pb, url, passwd, id, win, db_name, parent)
327 except Exception, e:
328 if e.args == ('DbExist',):
329 common.warning(_("Could not create database."),_('Database alreayd exists !'))
330 elif ('faultString' in e and e.faultString=='AccessDenied:None') or str(e)=='AccessDenied':
331 common.warning(_('Bad database administrator password !'), _("Could not create database."))
332 else:
333 print e
334 print str(e)
335 print e.faultString
336 print e.faultCode
337 common.warning(_("Could not create database."),_('Error during database creation !'))
339 def progress_timeout(self, pbar, url, passwd, id, win, dbname, parent=None):
340 try:
341 progress,users = rpc.session.db_exec_no_except(url, 'get_progress', passwd, id)
342 except:
343 win.destroy()
344 common.warning(_("The server crashed during installation.\nWe suggest you to drop this database."),_("Error during database creation !"))
345 return False
347 pbar.pulse()
348 if progress == 1.0:
349 win.destroy()
351 pwdlst = '\n'.join(map(lambda x: ' - %s: %s / %s' % (x['name'],x['login'],x['password']), users))
352 dialog = glade.XML(common.terp_path("terp.glade"), "dia_dbcreate_ok", gettext.textdomain())
353 win = dialog.get_widget('dia_dbcreate_ok')
354 if not parent:
355 parent = service.LocalService('gui.main').window
356 win.set_transient_for(parent)
357 win.show_all()
358 buffer = dialog.get_widget('dia_tv').get_buffer()
360 buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
361 iter_start = buffer.get_start_iter()
362 buffer.insert(iter_start, _('The following users have been installed on your database:')+'\n\n'+ pwdlst + '\n\n'+_('You can now connect to the database as an administrator.'))
363 res = win.run()
364 parent.present()
365 win.destroy()
367 if res == gtk.RESPONSE_OK:
368 m = re.match('^(http[s]?://|socket://)([\w.]+):(\d{1,5})$', url)
369 res = ['admin', 'admin']
370 if m:
371 res.append( m.group(2) )
372 res.append( m.group(3) )
373 res.append( m.group(1) )
374 res.append( dbname )
376 self.sig_login(dbname=dbname)
377 return False
378 return True
380 def process(self):
381 return False
384 class terp_main(service.Service):
385 def __init__(self, name='gui.main', audience='gui.*'):
386 service.Service.__init__(self, name, audience)
387 self.exportMethod(self.win_add)
389 self.glade = glade.XML(common.terp_path("terp.glade"),"win_main",gettext.textdomain())
390 self.status_bar_main = self.glade.get_widget('hbox_status_main')
391 self.toolbar = self.glade.get_widget('main_toolbar')
392 self.sb_requests = self.glade.get_widget('sb_requests')
393 self.sb_username = self.glade.get_widget('sb_user_name')
394 self.sb_servername = self.glade.get_widget('sb_user_server')
395 id = self.sb_servername.get_context_id('message')
396 self.sb_servername.push(id, _('Press Ctrl+O to login'))
397 self.secure_img = self.glade.get_widget('secure_img')
398 self.secure_img.hide()
400 window = self.glade.get_widget('win_main')
401 window.connect("destroy", self.sig_quit)
402 window.connect("delete_event", self.sig_delete)
403 self.window = window
404 self.window.set_icon(common.TINYERP_ICON)
406 self.notebook = gtk.Notebook()
407 self.notebook.popup_enable()
408 self.notebook.set_scrollable(True)
409 self.sig_id = self.notebook.connect_after('switch-page', self._sig_page_changt)
410 vbox = self.glade.get_widget('vbox_main')
411 vbox.pack_start(self.notebook, expand=True, fill=True)
413 self.shortcut_menu = self.glade.get_widget('shortcut')
416 # Code to add themes to the options->theme menu
418 submenu = gtk.Menu()
419 menu = self.glade.get_widget('menu_theme')
420 old = None
421 themes_path = common.terp_path('themes')
422 if themes_path:
423 for dname in os.listdir(themes_path):
424 if dname.startswith('.'):
425 continue
426 fname = common.terp_path(os.path.join('themes', dname, 'gtkrc'))
427 if fname and os.path.isfile(fname):
428 open_item = gtk.RadioMenuItem(old, dname)
429 old = open_item
430 submenu.append(open_item)
431 if dname == options.options['client.theme']:
432 open_item.set_active(True)
433 open_item.connect('toggled', self.theme_select, dname)
435 submenu.append(gtk.SeparatorMenuItem())
436 open_item = gtk.RadioMenuItem(old, _('Default Theme'))
437 submenu.append(open_item)
438 if 'none'==options.options['client.theme']:
439 open_item.set_active(True)
440 open_item.connect('toggled', self.theme_select, 'none')
441 submenu.show_all()
442 menu.set_submenu(submenu)
445 # Default Notebook
448 self.notebook.show()
449 self.pages = []
450 self.current_page = 0
451 self.last_page = 0
453 dict = {
454 'on_login_activate': self.sig_login,
455 'on_logout_activate': self.sig_logout,
456 'on_win_next_activate': self.sig_win_next,
457 'on_win_prev_activate': self.sig_win_prev,
458 'on_plugin_execute_activate': self.sig_plugin_execute,
459 'on_quit_activate': self.sig_close,
460 'on_but_menu_clicked': self.sig_win_menu,
461 'on_win_new_activate': self.sig_win_menu,
462 'on_win_home_activate': self.sig_home_new,
463 'on_win_close_activate': self.sig_win_close,
464 'on_support_activate': common.support,
465 'on_preference_activate': self.sig_user_preferences,
466 'on_read_requests_activate': self.sig_request_open,
467 'on_send_request_activate': self.sig_request_new,
468 'on_request_wait_activate': self.sig_request_wait,
469 'on_opt_save_activate': lambda x: options.options.save(),
470 'on_menubar_icons_activate': lambda x: self.sig_menubar('icons'),
471 'on_menubar_text_activate': lambda x: self.sig_menubar('text'),
472 'on_menubar_both_activate': lambda x: self.sig_menubar('both'),
473 'on_mode_normal_activate': lambda x: self.sig_mode_change(False),
474 'on_mode_pda_activate': lambda x: self.sig_mode_change(True),
475 'on_opt_form_tab_top_activate': lambda x: self.sig_form_tab('top'),
476 'on_opt_form_tab_left_activate': lambda x: self.sig_form_tab('left'),
477 'on_opt_form_tab_right_activate': lambda x: self.sig_form_tab('right'),
478 'on_opt_form_tab_bottom_activate': lambda x: self.sig_form_tab('bottom'),
479 'on_opt_form_tab_orientation_horizontal_activate': lambda x: self.sig_form_tab_orientation(0),
480 'on_opt_form_tab_orientation_vertical_activate': lambda x: self.sig_form_tab_orientation(90),
481 'on_help_index_activate': self.sig_help_index,
482 'on_help_contextual_activate': self.sig_help_context,
483 'on_help_tips_activate': self.sig_tips,
484 'on_help_licence_activate': self.sig_licence,
485 'on_about_activate': self.sig_about,
486 'on_shortcuts_activate' : self.sig_shortcuts,
487 'on_db_new_activate': self.sig_db_new,
488 'on_db_restore_activate': self.sig_db_restore,
489 'on_db_backup_activate': self.sig_db_dump,
490 'on_db_drop_activate': self.sig_db_drop,
491 'on_admin_password_activate': self.sig_db_password,
493 for signal in dict:
494 self.glade.signal_connect(signal, dict[signal])
496 self.buttons = {}
497 for button in ('but_new', 'but_save', 'but_remove', 'but_search', 'but_previous', 'but_next', 'but_action', 'but_open', 'but_print', 'but_close', 'but_reload', 'but_switch','but_attach'):
498 self.glade.signal_connect('on_'+button+'_clicked', self._sig_child_call, button)
499 self.buttons[button]=self.glade.get_widget(button)
501 menus = {
502 'form_del': 'but_remove',
503 'form_new': 'but_new',
504 'form_copy': 'but_copy',
505 'form_reload': 'but_reload',
506 'form_log': 'but_log',
507 'form_open': 'but_open',
508 'form_search': 'but_search',
509 'form_previous': 'but_previous',
510 'form_next': 'but_next',
511 'form_save': 'but_save',
512 'goto_id': 'but_goto_id',
513 'form_print': 'but_print',
514 'form_print_html': 'but_print_html',
515 'form_save_as': 'but_save_as',
516 'form_import': 'but_import',
517 'form_filter': 'but_filter',
518 'form_repeat': 'but_print_repeat'
520 for menu in menus:
521 self.glade.signal_connect('on_'+menu+'_activate', self._sig_child_call, menus[menu])
523 spool = service.LocalService('spool')
524 spool.subscribe('gui.window', self.win_add)
527 # we now create the icon for the attachment button when there are attachments
528 self.__img_no_attachments = gtk.Image()
529 pxbf = self.window.render_icon(self.buttons['but_attach'].get_stock_id(), self.toolbar.get_icon_size())
530 self.__img_no_attachments.set_from_pixbuf(pxbf)
531 self.__img_no_attachments.show()
533 pxbf = pxbf.copy()
534 w, h = pxbf.get_width(), pxbf.get_height()
535 overlay = self.window.render_icon(gtk.STOCK_APPLY, gtk.ICON_SIZE_MENU)
536 ow, oh = overlay.get_width(), overlay.get_height()
537 overlay.composite(pxbf,
538 0, h - oh,
539 ow, oh,
540 0, h - oh,
541 1.0, 1.0,
542 gtk.gdk.INTERP_NEAREST,
543 255)
545 self.__img_attachments = gtk.Image()
546 self.__img_attachments.set_from_pixbuf(pxbf)
547 self.__img_attachments.show()
549 self.sb_set()
551 settings = gtk.settings_get_default()
552 settings.set_long_property('gtk-button-images', 1, 'TinyERP:gui.main')
554 def fnc_menuitem(menuitem, opt_name):
555 options.options[opt_name] = menuitem.get_active()
556 dict = {
557 'on_opt_print_preview_activate': (fnc_menuitem, 'printer.preview', 'opt_print_preview'),
558 'on_opt_form_toolbar_activate': (fnc_menuitem, 'form.toolbar', 'opt_form_toolbar'),
560 self.glade.get_widget('menubar_'+(options.options['client.toolbar'] or 'both')).set_active(True)
561 self.sig_menubar(options.options['client.toolbar'] or 'both')
562 self.glade.get_widget('opt_form_tab_'+(options.options['client.form_tab'] or 'left')).set_active(True)
563 self.sig_form_tab(options.options['client.form_tab'] or 'left')
564 self.glade.get_widget('opt_form_tab_orientation_'+(str(options.options['client.form_tab_orientation']) or '0')).set_active(True)
565 self.sig_form_tab_orientation(options.options['client.form_tab_orientation'] or 0)
566 if options.options['client.modepda']:
567 self.glade.get_widget('mode_pda').set_active(True)
568 else:
569 self.glade.get_widget('mode_normal').set_active(True)
570 self.sig_mode()
571 for signal in dict:
572 self.glade.signal_connect(signal, dict[signal][0], dict[signal][1])
573 self.glade.get_widget(dict[signal][2]).set_active(int(bool(options.options[dict[signal][1]])))
575 # Adding a timer the check to requests
576 gobject.timeout_add(5 * 60 * 1000, self.request_set)
579 def shortcut_edit(self, widget, model='ir.ui.menu'):
580 obj = service.LocalService('gui.window')
581 domain = [('user_id', '=', rpc.session.uid), ('resource', '=', model)]
582 obj.create(False, 'ir.ui.view_sc', res_id=None, domain=domain, view_type='form', mode='tree,form')
584 def shortcut_set(self, sc=None):
585 def _action_shortcut(widget, action):
586 if action:
587 ctx = rpc.session.context.copy()
588 obj = service.LocalService('action.main')
589 obj.exec_keyword('tree_but_open', {'model': 'ir.ui.menu', 'id': action,
590 'ids': [action], 'report_type': 'pdf', 'window': self.window}, context=ctx)
592 if sc is None:
593 uid = rpc.session.uid
594 sc = rpc.session.rpc_exec_auth('/object', 'execute', 'ir.ui.view_sc', 'get_sc', uid, 'ir.ui.menu', rpc.session.context) or []
596 menu = gtk.Menu()
597 for s in sc:
598 menuitem = gtk.MenuItem(s['name'])
599 menuitem.connect('activate', _action_shortcut, s['res_id'])
600 menu.add(menuitem)
602 menu.add(gtk.SeparatorMenuItem())
603 menuitem = gtk.MenuItem(_('Edit'))
604 menuitem.connect('activate', self.shortcut_edit)
605 menu.add(menuitem)
607 menu.show_all()
608 self.shortcut_menu.set_submenu(menu)
609 self.shortcut_menu.set_sensitive(True)
611 def shortcut_unset(self):
612 menu = gtk.Menu()
613 menu.show_all()
614 self.shortcut_menu.set_submenu(menu)
615 self.shortcut_menu.set_sensitive(False)
617 def theme_select(self, widget, theme):
618 options.options['client.theme'] = theme
619 common.theme_set()
620 self.window.show_all()
621 return True
623 def sig_mode_change(self, pda_mode=False):
624 options.options['client.modepda'] = pda_mode
625 return self.sig_mode()
627 def sig_mode(self):
628 pda_mode = options.options['client.modepda']
629 if pda_mode:
630 self.status_bar_main.hide()
631 else:
632 self.status_bar_main.show()
633 return pda_mode
635 def sig_menubar(self, option):
636 options.options['client.toolbar'] = option
637 if option=='both':
638 self.toolbar.set_style(gtk.TOOLBAR_BOTH)
639 elif option=='text':
640 self.toolbar.set_style(gtk.TOOLBAR_TEXT)
641 elif option=='icons':
642 self.toolbar.set_style(gtk.TOOLBAR_ICONS)
644 def sig_form_tab(self, option):
645 options.options['client.form_tab'] = option
647 def sig_form_tab_orientation(self, option):
648 options.options['client.form_tab_orientation'] = option
650 def sig_win_next(self, args):
651 pn = self.notebook.get_current_page()
652 if pn == len(self.pages)-1:
653 pn = -1
654 self.notebook.set_current_page(pn+1)
656 def sig_win_prev(self, args):
657 pn = self.notebook.get_current_page()
658 self.notebook.set_current_page(pn-1)
660 def sig_user_preferences(self, *args):
661 win =win_preference.win_preference(parent=self.window)
662 win.run()
663 return True
665 def sig_win_close(self, *args):
666 self._sig_child_call(args[0], 'but_close')
668 def sig_request_new(self, args=None):
669 obj = service.LocalService('gui.window')
670 try:
671 return obj.create(None, 'res.request', False,
672 [('act_from', '=', rpc.session.uid)], 'form',
673 mode='form,tree', window=self.window,
674 context={'active_test': False})
675 except:
676 return False
678 def sig_request_open(self, args=None):
679 ids,ids2 = self.request_set()
680 obj = service.LocalService('gui.window')
681 try:
682 return obj.create(False, 'res.request', ids,
683 [('act_to', '=', rpc.session.uid), ('active', '=', True)],
684 'form', mode='tree,form', window=self.window,
685 context={'active_test': False})
686 except:
687 return False
689 def sig_request_wait(self, args=None):
690 ids,ids2 = self.request_set()
691 obj = service.LocalService('gui.window')
692 try:
693 return obj.create(False, 'res.request', ids,
694 [('act_from', '=', rpc.session.uid),
695 ('state', '=', 'waiting'), ('active', '=', True)],
696 'form', mode='tree,form', window=self.window,
697 context={'active_test': False})
698 except:
699 return False
701 def request_set(self):
702 try:
703 uid = rpc.session.uid
704 ids,ids2 = rpc.session.rpc_exec_auth_try('/object', 'execute',
705 'res.request', 'request_get')
706 if len(ids):
707 message = _('%s request(s)') % len(ids)
708 else:
709 message = _('No request')
710 if len(ids2):
711 message += _(' - %s request(s) sended') % len(ids2)
712 id = self.sb_requests.get_context_id('message')
713 self.sb_requests.push(id, message)
714 return (ids,ids2)
715 except:
716 return ([],[])
718 def sig_login(self, widget=None, dbname=False):
719 RES_OK = 1
720 RES_BAD_PASSWORD = -2
721 RES_CNX_ERROR = -1
723 try:
724 log_response = RES_BAD_PASSWORD
725 res = None
726 while log_response == RES_BAD_PASSWORD:
727 try:
728 l = db_login()
729 res = l.run(dbname=dbname, parent=self.window)
730 except Exception, e:
731 if e.args == ('QueryCanceled',):
732 return False
733 raise
734 service.LocalService('gui.main').window.present()
735 self.sig_logout(widget)
736 log_response = rpc.session.login(*res)
737 if log_response == RES_OK:
738 options.options.save()
739 id = self.sig_win_menu(quiet=False)
740 if id:
741 self.sig_home_new(quiet=True, except_id=id)
742 if res[4] == 'https://':
743 self.secure_img.show()
744 else:
745 self.secure_img.hide()
746 self.request_set()
747 elif log_response == RES_CNX_ERROR:
748 common.message(_('Connection error !\nUnable to connect to the server !'))
749 elif log_response == RES_BAD_PASSWORD:
750 common.message(_('Connection error !\nBad username or password !'))
751 except rpc.rpc_exception:
752 rpc.session.logout()
753 raise
754 self.glade.get_widget('but_menu').set_sensitive(True)
755 self.glade.get_widget('user').set_sensitive(True)
756 self.glade.get_widget('form').set_sensitive(True)
757 self.glade.get_widget('plugins').set_sensitive(True)
758 return True
760 def sig_logout(self, widget):
761 res = True
762 while res:
763 wid = self._wid_get()
764 if wid:
765 if 'but_close' in wid.handlers:
766 res = wid.handlers['but_close']()
767 if not res:
768 return False
769 res = self._win_del()
770 else:
771 res = False
772 id = self.sb_requests.get_context_id('message')
773 self.sb_requests.push(id, '')
774 id = self.sb_username.get_context_id('message')
775 self.sb_username.push(id, _('Not logged !'))
776 id = self.sb_servername.get_context_id('message')
777 self.sb_servername.push(id, _('Press Ctrl+O to login'))
778 self.secure_img.hide()
779 self.shortcut_unset()
780 self.glade.get_widget('but_menu').set_sensitive(False)
781 self.glade.get_widget('user').set_sensitive(False)
782 self.glade.get_widget('form').set_sensitive(False)
783 self.glade.get_widget('plugins').set_sensitive(False)
784 rpc.session.logout()
785 return True
787 def sig_help_index(self, widget):
788 tools.launch_browser('http://www.openerp.com/documentation/user-manual/')
790 def sig_help_context(self, widget):
791 model = self._wid_get().model
792 l = rpc.session.context.get('lang','en_US')
793 tools.launch_browser('http://www.topenerp.com/scripts/context_index.php?model=%s&lang=%s' % (model,l))
795 def sig_tips(self, *args):
796 common.tipoftheday(self.window)
798 def sig_licence(self, widget):
799 dialog = glade.XML(common.terp_path("terp.glade"), "win_licence", gettext.textdomain())
800 dialog.signal_connect("on_but_ok_pressed", lambda obj: dialog.get_widget('win_licence').destroy())
802 win = dialog.get_widget('win_licence')
803 win.set_transient_for(self.window)
804 win.show_all()
806 def sig_about(self, widget):
807 about = glade.XML(common.terp_path("terp.glade"), "win_about", gettext.textdomain())
808 buffer = about.get_widget('textview2').get_buffer()
809 about_txt = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
810 buffer.set_text(about_txt % tinyerp_version)
811 about.signal_connect("on_but_ok_pressed", lambda obj: about.get_widget('win_about').destroy())
813 win = about.get_widget('win_about')
814 win.set_transient_for(self.window)
815 win.show_all()
817 def sig_shortcuts(self, widget):
818 shortcuts_win = glade.XML(common.terp_path('terp.glade'), 'shortcuts_dia', gettext.textdomain())
819 shortcuts_win.signal_connect("on_but_ok_pressed", lambda obj: shortcuts_win.get_widget('shortcuts_dia').destroy())
821 win = shortcuts_win.get_widget('shortcuts_dia')
822 win.set_transient_for(self.window)
823 win.show_all()
825 def sig_win_menu(self, widget=None, quiet=True):
826 for p in range(len(self.pages)):
827 if self.pages[p].model=='ir.ui.menu':
828 self.notebook.set_current_page(p)
829 return True
830 res = self.sig_win_new(widget, type='menu_id', quiet=quiet)
831 if not res:
832 return self.sig_win_new(widget, type='action_id', quiet=quiet)
833 return res
835 def sig_win_new(self, widget=None, type='menu_id', quiet=True, except_id=False):
836 try:
837 act_id = rpc.session.rpc_exec_auth('/object', 'execute', 'res.users',
838 'read', [rpc.session.uid], [type,'name'], rpc.session.context)
839 except:
840 return False
841 id = self.sb_username.get_context_id('message')
842 self.sb_username.push(id, act_id[0]['name'] or '')
843 id = self.sb_servername.get_context_id('message')
844 data = urlparse.urlsplit(rpc.session._url)
845 self.sb_servername.push(id, data[0]+':'+(data[1] and '//'+data[1] \
846 or data[2])+' ['+options.options['login.db']+']')
847 if not act_id[0][type]:
848 if quiet:
849 return False
850 common.warning(_("You can not log into the system !\nAsk the administrator to verify\nyou have an action defined for your user."),'Access Denied !')
851 rpc.session.logout()
852 return False
853 act_id = act_id[0][type][0]
854 if except_id and act_id == except_id:
855 return act_id
856 obj = service.LocalService('action.main')
857 win = obj.execute(act_id, {'window':self.window})
858 try:
859 user = rpc.session.rpc_exec_auth_wo('/object', 'execute', 'res.users',
860 'read', [rpc.session.uid], [type,'name'], rpc.session.context)
861 if user[0][type]:
862 act_id = user[0][type][0]
863 except:
864 pass
865 return act_id
867 def sig_home_new(self, widget=None, quiet=True, except_id=False):
868 return self.sig_win_new(widget, type='action_id', quiet=quiet,
869 except_id=except_id)
871 def sig_plugin_execute(self, widget):
872 import plugins
873 pn = self.notebook.get_current_page()
874 datas = {'model': self.pages[pn].model, 'ids':self.pages[pn].ids_get(), 'id' : self.pages[pn].id_get()}
875 plugins.execute(datas)
877 def sig_quit(self, widget):
878 options.options.save()
879 gtk.main_quit()
881 def sig_close(self, widget):
882 if common.sur(_("Do you really want to quit ?"), parent=self.window):
883 if not self.sig_logout(widget):
884 return False
885 options.options.save()
886 gtk.main_quit()
888 def sig_delete(self, widget, event, data=None):
889 if common.sur(_("Do you really want to quit ?"), parent=self.window):
890 if not self.sig_logout(widget):
891 return True
892 return False
893 return True
895 def win_add(self, win, datas):
896 self.pages.append(win)
897 self.notebook.append_page(win.widget, gtk.Label(win.name))
898 self.notebook.set_current_page(-1)
900 def message(self, message):
901 id = self.status_bar.get_context_id('message')
902 self.status_bar.push(id, message)
904 def __attachment_callback(self, view, objid):
905 current_view = self._wid_get()
906 current_id = current_view and current_view.id_get()
907 if current_view == view and objid == current_id:
908 cpt = None
909 if objid and view.screen.current_view.view_type == 'form':
910 cpt = rpc.session.rpc_exec_auth('/object', 'execute',
911 'ir.attachment', 'search_count',
912 [('res_model', '=', view.model), ('res_id', '=', objid)])
913 if cpt:
914 self.buttons['but_attach'].set_icon_widget(self.__img_attachments)
915 self.buttons['but_attach'].set_label(_('Attachments (%d)') % cpt)
918 def _update_attachment_button(self, view = None):
920 Update the attachment icon for display the number of attachments
922 if not view:
923 view = self._wid_get()
925 id = view and view.id_get()
926 gobject.timeout_add(1500, self.__attachment_callback, view, id)
927 self.buttons['but_attach'].set_icon_widget(self.__img_no_attachments)
928 self.buttons['but_attach'].set_label(_('Attachments'))
931 def sb_set(self, view=None):
932 if not view:
933 view = self._wid_get()
934 self._update_attachment_button(view)
935 for x in self.buttons:
936 if self.buttons[x]:
937 self.buttons[x].set_sensitive(view and (x in view.handlers))
939 def _win_del(self):
940 pn = self.notebook.get_current_page()
941 if pn != -1:
942 self.notebook.disconnect(self.sig_id)
943 page = self.pages.pop(pn)
944 self.notebook.remove_page(pn)
945 self.sig_id = self.notebook.connect_after('switch-page', self._sig_page_changt)
946 self.sb_set()
948 page.destroy()
949 del page
950 return self.notebook.get_current_page() != -1
952 def _wid_get(self):
953 pn = self.notebook.get_current_page()
954 if pn == -1:
955 return False
956 return self.pages[pn]
958 def _sig_child_call(self, widget, button_name, *args):
959 wid = self._wid_get()
960 if wid:
961 res = True
962 if button_name in wid.handlers:
963 res = wid.handlers[button_name]()
964 # for those buttons, we refresh the attachment button.
965 # for the "switch view" button, the action has already
966 # been called by the Screen object of the view (wid)
967 if button_name in ('but_new', 'but_remove', 'but_search', \
968 'but_previous', 'but_next', 'but_open', \
969 'but_close', 'but_reload', 'but_attach', 'but_goto_id'):
970 self._update_attachment_button(wid)
971 if button_name=='but_close' and res:
972 self._win_del()
974 def _sig_page_changt(self, widget=None, *args):
975 self.last_page = self.current_page
976 self.current_page = self.notebook.get_current_page()
977 self.sb_set()
979 def sig_db_new(self, widget):
980 if not self.sig_logout(widget):
981 return False
982 dia = db_create(self.sig_login)
983 res = dia.run(self.window)
984 if res:
985 options.options.save()
986 return res
988 def sig_db_drop(self, widget):
989 if not self.sig_logout(widget):
990 return False
991 url, db_name, passwd = self._choose_db_select(_('Delete a database'))
992 if not db_name:
993 return
995 try:
996 rpc.session.db_exec(url, 'drop', passwd, db_name)
997 common.message(_("Database dropped successfully !"), parent=self.window)
998 except Exception, e:
999 if ('faultString' in e and e.faultString=='AccessDenied:None') or str(e)=='AccessDenied':
1000 common.warning(_('Bad database administrator password !'),_("Could not drop database."), parent=self.window)
1001 else:
1002 common.warning(_("Couldn't drop database"), parent=self.window)
1004 def sig_db_restore(self, widget):
1005 filename = common.file_selection(_('Open...'), parent=self.window, preview=False)
1006 if not filename:
1007 return
1009 url, db_name, passwd = self._choose_db_ent()
1010 if db_name:
1011 try:
1012 f = file(filename, 'rb')
1013 data_b64 = base64.encodestring(f.read())
1014 f.close()
1015 rpc.session.db_exec(url, 'restore', passwd, db_name, data_b64)
1016 common.message(_("Database restored successfully !"), parent=self.window)
1017 except Exception,e:
1018 if ('faultString' in e and e.faultString=='AccessDenied:None') or str(e)=='AccessDenied':
1019 common.warning(_('Bad database administrator password !'),_("Could not restore database."), parent=self.window)
1020 else:
1021 common.warning(_("Couldn't restore database"), parent=self.window)
1023 def sig_db_password(self, widget):
1024 dialog = glade.XML(common.terp_path("terp.glade"), "dia_passwd_change",
1025 gettext.textdomain())
1026 win = dialog.get_widget('dia_passwd_change')
1027 win.set_icon(common.TINYERP_ICON)
1028 win.set_transient_for(self.window)
1029 win.show_all()
1030 server_widget = dialog.get_widget('ent_server')
1031 old_pass_widget = dialog.get_widget('old_passwd')
1032 new_pass_widget = dialog.get_widget('new_passwd')
1033 new_pass2_widget = dialog.get_widget('new_passwd2')
1034 change_button = dialog.get_widget('but_server_change')
1035 change_button.connect_after('clicked', lambda a,b: _server_ask(b, win), server_widget)
1037 host = options.options['login.server']
1038 port = options.options['login.port']
1039 protocol = options.options['login.protocol']
1040 url = '%s%s:%s' % (protocol, host, port)
1041 server_widget.set_text(url)
1043 res = win.run()
1044 if res == gtk.RESPONSE_OK:
1045 url = server_widget.get_text()
1046 old_passwd = old_pass_widget.get_text()
1047 new_passwd = new_pass_widget.get_text()
1048 new_passwd2 = new_pass2_widget.get_text()
1049 if new_passwd != new_passwd2:
1050 common.warning(_("Confirmation password do not match " \
1051 "new password, operation cancelled!"),
1052 _("Validation Error."), parent=win)
1053 else:
1054 try:
1055 rpc.session.db_exec(url, 'change_admin_password',
1056 old_passwd, new_passwd)
1057 except Exception,e:
1058 if ('faultString' in e and e.faultString=='AccessDenied:None') \
1059 or str(e)=='AccessDenied':
1060 common.warning(_("Could not change password database."),
1061 _('Bas password provided !'), parent=win)
1062 else:
1063 common.warning(_("Error, password not changed."),
1064 parent=win)
1065 self.window.present()
1066 win.destroy()
1068 def sig_db_dump(self, widget):
1069 url, db_name, passwd = self._choose_db_select(_('Backup a database'))
1070 if not db_name:
1071 return
1072 filename = common.file_selection(_('Save As...'),
1073 action=gtk.FILE_CHOOSER_ACTION_SAVE, parent=self.window, preview=False)
1075 if filename:
1076 try:
1077 dump_b64 = rpc.session.db_exec(url, 'dump', passwd, db_name)
1078 dump = base64.decodestring(dump_b64)
1079 f = file(filename, 'wb')
1080 f.write(dump)
1081 f.close()
1082 common.message(_("Database backuped successfully !"), parent=self.window)
1083 except:
1084 common.warning(_("Couldn't backup database."), parent=self.window)
1086 def _choose_db_select(self, title=_("Backup a database")):
1087 def refreshlist(widget, db_widget, label, url):
1088 res = _refresh_dblist(db_widget, url)
1089 if res == -1:
1090 label.set_label('<b>'+_('Could not connect to server !')+'</b>')
1091 db_widget.hide()
1092 label.show()
1093 elif res==0:
1094 label.set_label('<b>'+_('No database found, you must create one !')+'</b>')
1095 db_widget.hide()
1096 label.show()
1097 else:
1098 label.hide()
1099 db_widget.show()
1100 return res
1102 def refreshlist_ask(widget, server_widget, db_widget, label, parent=None):
1103 url = _server_ask(server_widget, parent)
1104 if not url:
1105 return None
1106 refreshlist(widget, db_widget, label, url)
1107 return url
1109 dialog = glade.XML(common.terp_path("terp.glade"), "win_db_select",
1110 gettext.textdomain())
1111 win = dialog.get_widget('win_db_select')
1112 win.set_icon(common.TINYERP_ICON)
1113 win.set_default_response(gtk.RESPONSE_OK)
1114 win.set_transient_for(self.window)
1115 win.show_all()
1117 pass_widget = dialog.get_widget('ent_passwd_select')
1118 server_widget = dialog.get_widget('ent_server_select')
1119 db_widget = dialog.get_widget('combo_db_select')
1120 label = dialog.get_widget('label_db_select')
1123 dialog.get_widget('db_select_label').set_markup('<b>'+title+'</b>')
1125 protocol = options.options['login.protocol']
1126 url = '%s%s:%s' % (protocol, options.options['login.server'], options.options['login.port'])
1127 server_widget.set_text(url)
1129 liststore = gtk.ListStore(str)
1130 db_widget.set_model(liststore)
1132 refreshlist(None, db_widget, label, url)
1133 change_button = dialog.get_widget('but_server_select')
1134 change_button.connect_after('clicked', refreshlist_ask, server_widget, db_widget, label, win)
1136 cell = gtk.CellRendererText()
1137 db_widget.pack_start(cell, True)
1138 db_widget.add_attribute(cell, 'text', 0)
1140 res = win.run()
1142 db = False
1143 url = False
1144 passwd = False
1145 if res == gtk.RESPONSE_OK:
1146 db = db_widget.get_active_text()
1147 url = server_widget.get_text()
1148 passwd = pass_widget.get_text()
1149 self.window.present()
1150 win.destroy()
1151 return (url,db,passwd)
1153 def _choose_db_ent(self):
1154 dialog = glade.XML(common.terp_path("terp.glade"), "win_db_ent",
1155 gettext.textdomain())
1156 win = dialog.get_widget('win_db_ent')
1157 win.set_icon(common.TINYERP_ICON)
1158 win.set_transient_for(self.window)
1159 win.show_all()
1161 db_widget = dialog.get_widget('ent_db')
1162 widget_pass = dialog.get_widget('ent_password')
1163 widget_url = dialog.get_widget('ent_server')
1165 protocol = options.options['login.protocol']
1166 url = '%s%s:%s' % (protocol, options.options['login.server'],
1167 options.options['login.port'])
1168 widget_url.set_text(url)
1170 change_button = dialog.get_widget('but_server_change')
1171 change_button.connect_after('clicked', lambda a,b: _server_ask(b, win),
1172 widget_url)
1174 res = win.run()
1176 db = False
1177 passwd = False
1178 url = False
1179 if res == gtk.RESPONSE_OK:
1180 db = db_widget.get_text()
1181 url = widget_url.get_text()
1182 passwd = widget_pass.get_text()
1183 self.window.present()
1184 win.destroy()
1185 return url, db, passwd