4
from gettext import gettext as _
6
from gi.repository import GObject, Gtk
8
from landscape.ui.constants import (
9
CANONICAL_MANAGED, LOCAL_MANAGED, NOT_MANAGED)
11
# Note, I think this may not be fully compliant with the changes in RFC 1123
12
HOST_NAME_REGEXP = re.compile("^(([a-zA-Z][a-zA-Z0-9\-]*)?[a-zA-Z0-9][\.]?)*"
13
"(([A-Za-z][A-Za-z0-9\-]*)?[A-Za-z0-9])$")
16
def sanitise_host_name(host_name):
18
Do some minimal host name sanitation.
20
return host_name.strip()
23
def is_valid_host_name(host_name):
25
Check that the provided host name complies with L{HOST_NAME_REGEXP} and is
28
return HOST_NAME_REGEXP.match(host_name) is not None
33
Test that the provided string contains only characters from the ASCII set.
38
except UnicodeDecodeError:
42
class ClientSettingsDialog(Gtk.Dialog):
44
L{ClientSettingsDialog} is a subclass of Gtk.Dialog that loads the UI
45
components from the associated Glade XML file and wires everything up to
49
GLADE_FILE = "landscape-client-settings.glade"
53
def __init__(self, controller):
54
super(ClientSettingsDialog, self).__init__(
55
title=_("Management Service"),
56
flags=Gtk.DialogFlags.MODAL)
57
self.set_default_icon_name("preferences-management-service")
58
self.set_resizable(False)
59
self._initialised = False
60
self._validation_errors = set()
61
self._errored_entries = []
62
self.controller = controller
65
# One extra revert to reset after loading data
66
self.controller.revert()
68
def indicate_error_on_entry(self, entry):
70
Show a warning icon on a L{Gtk.Entry} to indicate some associated
73
entry.set_icon_from_stock(
74
Gtk.EntryIconPosition.PRIMARY, Gtk.STOCK_DIALOG_WARNING)
75
self._errored_entries.append(entry)
77
def check_local_landscape_host_name_entry(self):
78
host_name = sanitise_host_name(
79
self.local_landscape_host_entry.get_text())
80
ascii_ok = is_ascii(host_name)
81
host_name_ok = is_valid_host_name(host_name)
82
if ascii_ok and host_name_ok:
83
self.local_landscape_host_entry.set_text(host_name)
86
self.indicate_error_on_entry(self.local_landscape_host_entry)
88
self._validation_errors.add(self.INVALID_HOST_NAME)
90
self._validation_errors.add(self.UNICODE_IN_ENTRY)
93
def check_entry(self, entry):
95
Check that the text content of a L{Gtk.Entry} is acceptable.
97
if is_ascii(entry.get_text()):
100
self.indicate_error_on_entry(entry)
101
self._validation_errors.add(self.UNICODE_IN_ENTRY)
104
def validity_check(self):
105
self._validation_errors = set()
106
if self._info_bar_container.get_visible():
107
self.dismiss_infobar(None)
108
active_iter = self.liststore.get_iter(
109
self.use_type_combobox.get_active())
110
[management_type] = self.liststore.get(active_iter, 0)
111
if management_type == NOT_MANAGED:
113
elif management_type == CANONICAL_MANAGED:
114
account_name_ok = self.check_entry(self.hosted_account_name_entry)
115
password_ok = self.check_entry(self.hosted_password_entry)
116
return account_name_ok and password_ok
118
host_name_ok = self.check_local_landscape_host_name_entry()
119
password_ok = self.check_entry(self.local_password_entry)
120
return host_name_ok and password_ok
123
def NO_SERVICE_TEXT(self):
127
def HOSTED_SERVICE_TEXT(self):
128
return _("Landscape - hosted by Canonical")
131
def LOCAL_SERVICE_TEXT(self):
132
return _("Landscape - dedicated server")
135
def REGISTER_BUTTON_TEXT(self):
139
def DISABLE_BUTTON_TEXT(self):
143
def INVALID_HOST_NAME_MESSAGE(self):
144
return _("Invalid host name.")
147
def UNICODE_IN_ENTRY_MESSAGE(self):
148
return _("Only ASCII characters are allowed.")
150
def _set_use_type_combobox_from_controller(self):
152
Load the persisted L{management_type} from the controller and set the
153
combobox appropriately.
155
Note that Gtk makes us jump through some hoops by having it's own model
156
level to deal with here. The conversion between paths and iters makes
157
more sense if you understand that treeviews use the same model.
159
list_iter = self.liststore.get_iter_first()
160
while (self.liststore.get(list_iter, 0)[0] !=
161
self.controller.management_type):
162
list_iter = self.liststore.iter_next(list_iter)
163
path = self.liststore.get_path(list_iter)
164
[index] = path.get_indices()
165
self.use_type_combobox.set_active(index)
167
def _set_hosted_values_from_controller(self):
168
self.hosted_account_name_entry.set_text(
169
self.controller.hosted_account_name)
170
self.hosted_password_entry.set_text(self.controller.hosted_password)
172
def _set_local_values_from_controller(self):
173
self.local_landscape_host_entry.set_text(
174
self.controller.local_landscape_host)
175
self.local_password_entry.set_text(self.controller.local_password)
178
self._initialised = False
179
self.controller.load()
180
self._set_hosted_values_from_controller()
181
self._set_local_values_from_controller()
182
self._set_use_type_combobox_from_controller()
183
self._initialised = True
185
def make_liststore(self):
187
Construct the correct L{Gtk.ListStore} to drive the L{Gtk.ComboBox} for
188
use-type. This a table of:
190
* Management type (key)
191
* Text to display in the combobox
192
* L{Gtk.Frame} to load when this item is selected.
194
liststore = Gtk.ListStore(GObject.TYPE_PYOBJECT,
196
GObject.TYPE_PYOBJECT)
197
self.active_widget = None
198
liststore.append([NOT_MANAGED, self.NO_SERVICE_TEXT,
199
self._builder.get_object("no-service-frame")])
200
liststore.append([CANONICAL_MANAGED, self.HOSTED_SERVICE_TEXT,
201
self._builder.get_object("hosted-service-frame")])
202
liststore.append([LOCAL_MANAGED, self.LOCAL_SERVICE_TEXT,
203
self._builder.get_object("local-service-frame")])
206
def link_hosted_service_widgets(self):
207
self.hosted_account_name_entry = self._builder.get_object(
208
"hosted-account-name-entry")
209
self.hosted_account_name_entry.connect(
210
"changed", self.on_changed_event, "hosted_account_name")
212
self.hosted_password_entry = self._builder.get_object(
213
"hosted-password-entry")
214
self.hosted_password_entry.connect(
215
"changed", self.on_changed_event, "hosted_password")
217
def link_local_service_widgets(self):
218
self.local_landscape_host_entry = self._builder.get_object(
219
"local-landscape-host-entry")
220
self.local_landscape_host_entry.connect(
221
"changed", self.on_changed_event, "local_landscape_host")
223
self.local_password_entry = self._builder.get_object(
224
"local-password-entry")
225
self.local_password_entry.connect(
226
"changed", self.on_changed_event, "local_password")
228
def link_use_type_combobox(self, liststore):
229
self.use_type_combobox = self._builder.get_object("use-type-combobox")
230
self.use_type_combobox.connect("changed", self.on_combo_changed)
231
self.use_type_combobox.set_model(liststore)
232
cell = Gtk.CellRendererText()
233
self.use_type_combobox.pack_start(cell, True)
234
self.use_type_combobox.add_attribute(cell, 'text', 1)
236
def cancel_response(self, widget):
237
self.response(Gtk.ResponseType.CANCEL)
239
def register_response(self, widget):
240
if self.validity_check():
241
self.response(Gtk.ResponseType.OK)
244
if self.UNICODE_IN_ENTRY in self._validation_errors:
245
error_text.append(self.UNICODE_IN_ENTRY_MESSAGE)
246
if self.INVALID_HOST_NAME in self._validation_errors:
247
error_text.append(self.INVALID_HOST_NAME_MESSAGE)
248
self.info_message.set_text("\n".join(error_text))
249
self._info_bar_container.show()
251
def set_button_text(self, management_type):
252
[alignment] = self.register_button.get_children()
253
[hbox] = alignment.get_children()
254
[image, label] = hbox.get_children()
255
if management_type == NOT_MANAGED:
256
label.set_text(self.DISABLE_BUTTON_TEXT)
258
label.set_text(self.REGISTER_BUTTON_TEXT)
260
def setup_buttons(self):
261
self.revert_button = Gtk.Button(stock=Gtk.STOCK_REVERT_TO_SAVED)
262
self.action_area.pack_start(self.revert_button, True, True, 0)
263
self.revert_button.connect("clicked", self.revert)
264
self.revert_button.show()
265
self.cancel_button = Gtk.Button(stock=Gtk.STOCK_CANCEL)
266
self.action_area.pack_start(self.cancel_button, True, True, 0)
267
self.cancel_button.show()
268
self.cancel_button.connect("clicked", self.cancel_response)
269
self.register_button = Gtk.Button(stock=Gtk.STOCK_OK)
270
self.action_area.pack_start(self.register_button, True, True, 0)
271
self.register_button.show()
272
self.register_button.connect("clicked", self.register_response)
274
def dismiss_infobar(self, widget):
275
self._info_bar_container.hide()
276
for entry in self._errored_entries:
277
entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY, None)
278
self._errored_entries = []
280
def setup_info_bar(self):
281
labels_size_group = self._builder.get_object("labels-sizegroup")
282
entries_size_group = self._builder.get_object("entries-sizegroup")
283
labels_size_group.set_ignore_hidden(False)
284
entries_size_group.set_ignore_hidden(False)
285
self._info_bar_container = Gtk.HBox()
286
self._info_bar_container.set_spacing(12)
287
info_bar = Gtk.InfoBar()
288
entries_size_group.add_widget(info_bar)
290
empty_label = Gtk.Label()
291
labels_size_group.add_widget(empty_label)
293
self._info_bar_container.pack_start(empty_label, expand=False,
294
fill=False, padding=0)
295
self._info_bar_container.pack_start(info_bar, expand=False, fill=False,
297
content_area = info_bar.get_content_area()
299
self.info_message = Gtk.Label()
300
self.info_message.set_alignment(0, 0.5)
301
self.info_message.show()
302
hbox.pack_start(self.info_message, expand=True, fill=True, padding=6)
303
ok_button = Gtk.Button("Dismiss")
304
ok_button.connect("clicked", self.dismiss_infobar)
306
hbox.pack_start(ok_button, expand=True, fill=True, padding=0)
308
content_area.pack_start(hbox, expand=True, fill=True, padding=0)
311
self._builder = Gtk.Builder()
312
self._builder.set_translation_domain("landscape-client")
313
self._builder.add_from_file(
315
os.path.dirname(__file__), "ui", self.GLADE_FILE))
316
content_area = self.get_content_area()
317
content_area.set_spacing(12)
318
self.set_border_width(12)
319
self.setup_info_bar()
320
self._vbox = self._builder.get_object("toplevel-vbox")
321
self._vbox.unparent()
322
content_area.pack_start(self._vbox, expand=True, fill=True,
324
self._vbox.pack_start(self._info_bar_container, expand=False,
325
fill=False, padding=0)
326
self.liststore = self.make_liststore()
327
self.link_use_type_combobox(self.liststore)
328
self.link_hosted_service_widgets()
329
self.link_local_service_widgets()
332
def on_combo_changed(self, combobox):
333
list_iter = self.liststore.get_iter(combobox.get_active())
334
if not self.active_widget is None:
335
self._vbox.remove(self.active_widget)
336
[management_type] = self.liststore.get(list_iter, 0)
337
self.set_button_text(management_type)
338
if self._initialised:
339
self.controller.management_type = management_type
340
self.controller.modify()
341
[self.active_widget] = self.liststore.get(list_iter, 2)
342
self.active_widget.unparent()
343
self._vbox.add(self.active_widget)
345
def on_changed_event(self, widget, attribute):
346
setattr(self.controller, attribute, widget.get_text())
347
self.controller.modify()
349
def quit(self, *args):
352
def revert(self, button):
353
self.controller.revert()
355
# One extra revert to reset after loading data
356
self.controller.revert()