def ftp_upload(self, uploadlist=None, message=True): ftp1 = self.ftp_config msg = "" OK = True ftp = ftp_connect(ftp1["server"], ftp1["login"], ftp1["pass"]) if ftp is None: msg += _("No FTP connexion") return if uploadlist is None: uploadlist = ["./idefix.json"] for file1 in uploadlist: ret = ftp_send(ftp, get_config_path(file1)) if ret == True: msg += file1 + _(" sent\n") else: msg += ret OK = False ftp.close() if OK: title = "Upload OK" else: title = "ERRORS in upload" print(msg) if message: showwarning(title, msg, 1)
def action_export_repository(self, widget): """Create a new unverified group""" smodel, siter = self.widgets['groups_tree'].get_selection( ).get_selected() if not siter: return if self.imported_groups or self.groups_changed: if askyesno(_("Save Changes"), _("Do you want to save your changes?")): self.save_groups() iter = smodel.convert_iter_to_child_iter(siter) model = smodel.get_model() if askyesno( _("Export Group"), _("Are you sure you want to send this group to the repository?" )): if upload_group(model.get_value(iter, 0), model.get_value(iter, 1).split('\n')): showwarning(_("Exported"), _("Thank you for sending us the group details")) else: showwarning(_("Error"), _("Please try again"), 4)
def idefix2_test_ftp(self, *args): valid = ftp_connect(self.config['ftp']['ftp'], self.config['ftp']['login'], self.config['ftp']['password']) if not valid: alert(_("Could not conect to FTP")) else: showwarning(_("Test FTP"), _("FTP Connection Success"), 5)
def summary_warning(self, widget=None, event=None): if event.get_keyval()[1] in range(65360, 65370): # allow direction keys return message = _( "The user summary is not editable. \n See the Internet Filter tab." ) showwarning(_("Not editable"), message, 2)
def selection_changed(self, widget): if self.groups_changed: showwarning(_("Save"), _("Don't forget to save"), 2) self.groups_changed = False self.buffer = Gtk.TextBuffer() self.widgets['groups_view'].set_buffer(self.buffer) model, iter = widget.get_selected() if not iter: return self.buffer.set_text(model.get_value(iter, 1)) self.buffer.connect('changed', self.set_groups_dirty)
def add_new_category(self, widget): (model, node) = self.arw["treeview1"].get_selection().get_selected() if node is None: level = 1 else: level = model.get_path(node).get_depth() name = self.ask_user_dialog(level) if name: if self.does_category_exist(name): showwarning(_("Category Exists"), _("The category name already exists")) return self.users_store.insert_after( None, node, [name, "", "", "", 0, 0, 0, 0, 0, "", "", None, None])
def check_addresses(self, widget): buffer = self.arw['maclist'].get_buffer() (start_iter, end_iter) = buffer.get_bounds() value = buffer.get_text(start_iter, end_iter, False) OK = True for v in value.split('\n'): if v.startswith("#"): continue if v.strip() == "": continue if not mac_address_test(v) and not ip_address_test(v): showwarning(_("Address Invalid"), _("The address \n%s\n entered is not valid") % v) OK = False if OK: showwarning(_("Addresses OK"), _("All addresses are valid"))
def save_options(self, widget): if self.block_signals: return if self.arw['option_password_check'].get_active( ) and not self.arw['option_password_entry'].get_text(): showwarning(_("No Password"), _("Please enter a password")) return gui_check = self.arw['option_checkbox_gui_check'].get_active() filter_tab = self.arw['option_filter_tab_check'].get_active() auto_load = self.arw['menu_autoload_check'].get_active() developper_menu = self.arw['option_developper_check'].get_active() advanced_filter = self.arw['option_advanced_filter_check'].get_active() self.profiles.config['__options'] = { 'checkbox_config': '1' if gui_check else '0', 'filter_tab': '1' if filter_tab else '0', 'auto_load': '1' if auto_load else '0', 'developper_menu': '1' if developper_menu else '0', 'advanced_filter': '1' if advanced_filter else '0', } if self.arw['option_password_check'].get_active(): self.profiles.config['__options']['password'] = encrypt_password( self.arw['option_password_entry'].get_text(), self.arw['option_password_entry'].get_text()) self.profiles.password = self.arw[ 'option_password_entry'].get_text() else: self.profiles.password = DEFAULT_KEY self.arw['option_password_entry'].set_text('') if gui_check: self.filter_rules.set_gui('check') else: self.filter_rules.set_gui('buttons') self.arw['developper_menu'].set_sensitive(developper_menu) self.arw['developper_menu'].set_visible(developper_menu) self.arw['filter_rules_box'].set_visible(advanced_filter) # Save to config self.profiles.profile_save_config() self.arw['options_window'].hide()
def update_selected_connected_user(self, widget): """Add the selected mac address to an existing user""" model, iter = self.arw['connected_users_list'].get_selection( ).get_selected() mac_address = model.get_value(iter, 0) self.refresh_users() response = self.arw['select_user_dialog'].run() self.arw['select_user_dialog'].hide() if response == Gtk.ResponseType.OK: model, iter = self.arw['select_user_view'].get_selection( ).get_selected() if not iter: return username = model.get_value(iter, 0) self.controller.maclist.get(username, []).append(mac_address) showwarning(_("User updated"), _("Mac address was added to the user")) self.controller.build_files(None)
def find_good_backup(self): """Find a good backup from Idefix""" # Download list of available back ups try: backup_list = json.loads( self.information.get_infos('list_backups')) backup_list.sort(reverse=True) except: backup_list = [] for backup in backup_list: # Download the zip file and extract it filedata = self.information.get_infos('get_backup ' + backup, result='result.zip') backup_f = io.BytesIO() backup_f.write(filedata) backup_f.seek(0) try: zf = zipfile.ZipFile(backup_f) except zipfile.BadZipFile as e: print("Error extracting backup", backup, e) continue # Try load configuration from the file if self.restore_dialog.load_from_backup(zf, type=['permissions'], show_warning=True): print("Error loading backup", backup) continue else: showwarning(_("Restore Backup"), _("Restored from backup %s") % backup) self.load_connection() return self.ftp.close() # No good backups were found return alert( "Unable to load configuration. Please import another one.")
def add_new_user(self, widget, mode=None, parent=None): """ adds a new user under the category selected, or below the user selected """ (model, node) = self.arw["treeview1"].get_selection().get_selected() level = model.get_path(node).get_depth() name = self.ask_user_dialog(2) if name: if self.does_user_exist(name): showwarning(_("User Exists"), _("Username exists")) return if level == 1: # if a category is selected, insert below it iternew = self.users_store.insert( node, 1, [name, "", "", "", 0, 0, 0, 0, 0, "", "", None, None]) path = model.get_path(node) self.arw["treeview1"].expand_row( path, True) # open the path to allow entering the adress elif level == 2: # A user is selected if mode == "above": iternew = self.users_store.insert_before( None, node, [name, "", "", "", 0, 0, 0, 0, 0, "", "", None, None]) else: iternew = self.users_store.insert_after( None, node, [name, "", "", "", 0, 0, 0, 0, 0, "", "", None, None]) else: return self.controller.populate_users_chooser() # open the right tab to allow entering the address sel = self.arw["treeview1"].get_selection() sel.select_iter(iternew) self.load_user("", "", iternew) self.arw["notebook5"].set_current_page(1) showwarning(_("Enter address"), _("Please, enter the Mac or IP address for this user"))
def add_subuser(self, widget): """Adds a new sub user to the selected user""" (model, node) = self.arw["treeview1"].get_selection().get_selected() level = model.get_path(node).get_depth() if level == 1: # category selected return elif level == 3: # sub user selected, get parent node = model.iter_parent(node) name = self.ask_user_dialog(2) if name: if self.does_user_exist(name): showwarning(_("User Exists"), _("Username exists")) return iternew = self.users_store.insert( node, 1, [name, "", "", "", 0, 0, 0, 0, 0, "", "", None, None]) self.controller.populate_users_chooser() # open the right tab to allow entering the address self.arw["treeview1"].expand_row(model.get_path(iternew), True) sel = self.arw["treeview1"].get_selection() sel.select_iter(iternew) self.load_user("", "", iternew)
def update_filter_user_list_view(self, widget, ctx, x, y, data, info, etime): """Add a user or a group to the list""" # called by the drag_data_received signal # TODO name should be changed, because it is not clear parts = data.get_text().split("#") new_name = parts[0].strip() if self.filter_user_has_any(): return if not parts[1] or parts[ 1] != "chooser1": # if data does not come from the right chooser, return return model, iter = self.arw['chooser1'].get_selection().get_selected() if time.time( ) - self.mem_time < 1: # dirty workaround to prevent two drags return self.mem_time = time.time() if model.get_value(iter, 3): # This is a category, not a user so show a warning and do nothing showwarning(_("Not Supported"), _("Choosing a category is not yet supported")) return position = None model = widget.get_model() data1 = data.get_text().split("#") path = data1[0] if len(data1) == 2: source_model = self.arw[data1[1]].get_model() else: source_model = model try: iter_source = source_model.get_iter(path) values = [ source_model.get_value(iter_source, i) for i in range(model.get_n_columns()) ] except TypeError: iter_source = None values = None dest = widget.get_dest_row_at_pos(x, y) if dest: drop_path, position = dest iter1 = model.get_iter(drop_path) if (position == Gtk.TreeViewDropPosition.BEFORE or position == Gtk.TreeViewDropPosition.INTO_OR_BEFORE): iter_dest = model.insert_before( iter1, ['' for x in range(model.get_n_columns())]) else: iter_dest = model.insert_after( iter1, ['' for x in range(model.get_n_columns())]) else: iter_dest = model.insert(-1) if iter_source: for i in range(model.get_n_columns()): model.set_value(iter_dest, i, values[i]) if source_model == model: # move row in the list model.remove(iter_source) names = [name[0] for name in model] self.current_store.set_value(self.controller.iter_filter, 5, '\n'.join(names)) return names = self.current_store.get_value(self.controller.iter_filter, 5).split('\n') if new_name in names: return names.append(new_name) self.current_store.set_value(self.controller.iter_filter, 5, '\n'.join(names)) self.update_filter_user_list(self.controller.iter_filter)
def check_permissions(self, widget): """Run the permissions check after the check permission dialog has been completed""" self.refresh_users() # Show the selection dialog dialog = self.arw['permission_check_dialog'] response = dialog.run() dialog.hide() if response != Gtk.ResponseType.OK: return model, selected_iter = self.arw['permission_check_user'].get_selection( ).get_selected() if not model or not selected_iter: showwarning(_("No User Selected"), _("Please select a user")) return user = model.get_value(selected_iter, 0) domain = self.arw['permission_check_domain'].get_text() if not domain: showwarning(_("No Domain Entered"), _("Please enter a valid domain")) return self.arw['why_stack'].set_visible_child_name('why') # Retrieve the unbound json data from idefix ftp = ftp_connect(self.controller.ftp_config["server"], self.controller.ftp_config["login"], self.controller.ftp_config["pass"]) if not ftp: return False data = ftp_get(ftp, 'unbound.json') if not data: showwarning(_("Could not get data"), _("Could not retrieve permission data from idefix")) return False ftp.close() config = json.loads(data, object_pairs_hook=OrderedDict) self.arw['why_store'].clear() rule_info = self.is_allowed(user, domain, config) self.arw['why_info_label'].set_text( _("%s rules for %s") % (domain, user)) for name, info in rule_info['rules'].items(): rule_iter = self.arw['why_store'].append() self.arw['why_store'].set_value(rule_iter, WHY_COLUMN_NAME, name) # The domain is a list of matches to compare against the target domain # eg: ['com', 'microsoft', '*'] will match 'www.microsoft.com' # iterate through both target and rule domains looking for matches # if '*' is encountered then mark the rest of the domain as a pass # if a part of the domain doesn't match then mark the rest of the domain as fail rule_domain = iter(info.get('domain', [])) domain_parts = [] target_domain = reversed(domain.split('.')) while True: try: source = rule_domain.__next__() except StopIteration: source = '' try: target = target_domain.__next__() except StopIteration: break if source.lower() == target.lower(): domain_parts.append("<span foreground='green'>" + source + "</span>") continue # Get the rest of the target parts = [target] while True: try: parts.append(target_domain.__next__()) except StopIteration: break rest = '.'.join(reversed(parts)) if source == '*': # We match everything from here further down green domain_parts.append("<span foreground='green'>" + rest + "</span>") else: # This and everything below does not match and is red domain_parts.append("<span foreground='red'>" + rest + "</span>") self.arw['why_store'].set_value(rule_iter, WHY_COLUMN_DOMAIN, '.'.join(reversed(domain_parts))) notes = '' if not info.get('user', True): notes += "<span foreground='red'>☹</span> " else: notes += "<span foreground='green'>☺</span> " if 'time_condition' in info: if not info.get('time_condition'): notes += "<span foreground='red'>⏰</span> " else: notes += "<span foreground='green'>⏰</span> " self.arw['why_store'].set_value(rule_iter, WHY_COLUMN_RULE_DISABLED, not info.get('enabled')) if not info.get('valid', True): self.arw['why_store'].set_value(rule_iter, WHY_COLUMN_RULE_INVALID, True) notes = info.get('reason') if 'action' in info: if info['action']: self.arw['why_store'].set_value(rule_iter, WHY_COLUMN_RULE_TYPE, Gtk.STOCK_APPLY) else: self.arw['why_store'].set_value(rule_iter, WHY_COLUMN_RULE_TYPE, Gtk.STOCK_CANCEL) else: self.arw['why_store'].set_value(rule_iter, WHY_COLUMN_RULE_TYPE, Gtk.STOCK_JUMP_TO) self.arw['why_store'].set_value(rule_iter, WHY_COLUMN_INFO, notes)
def idefix_filter_infos(self, widget): """Retrieve filter log from idefix""" self.arw['why_stack'].set_visible_child_name('filter_log') spinner = self.arw["infos_spinner"] if self.arw["infos_filter_dns"].get_active(): command = "unbound" else: command = "squid" ip_address = None search_string = self.arw['filter_log_search_entry'].get_text() if search_string.lower().startswith('ip-address='): address = search_string.split('=')[1] if ip_address_test(address): ip_address = address if not ip_address: if not hasattr(self.controller, 'myip'): showwarning(_("IP Not Found"), _("The command cannot be executed")) return else: ip_address = self.controller.myip command += " " + ip_address spinner.start() result = self.get_infos(command) spinner.stop() result = me(result) self.arw['filter_log_store'].clear() for line in result.split('\n'): log_iter = self.arw['filter_log_store'].append() text = "" if "no match" in line: text = '<span foreground="blue">' + line.strip() + "</span>" elif "denied" in line: text = '<span foreground="red">' + line.strip() + "</span>" elif "allowed" in line: text = '<span foreground="green">' + line.strip() + "</span>" if "validation failure" in line: text = '<span background="#ff9999">' + line.strip() + "</span>" # Extract the domain from the log if 'validation failure' in line: match = re.search(r">([^\s]+)", line) if match: domain = match.group(1) if domain.endswith('.'): domain = domain[:len(domain) - 1] self.arw['filter_log_store'].set_value( log_iter, 1, domain.lower()) else: match = re.search(r"===> ([^\s]+)", line) if match: domain = match.group(1) if domain.endswith('.'): domain = domain[:len(domain) - 1] self.arw['filter_log_store'].set_value( log_iter, 1, domain.lower()) self.arw['filter_log_store'].set_value(log_iter, 0, text) last_iter = self.arw['filter_log_store'].iter_nth_child( None, self.arw['filter_log_store'].iter_n_children() - 1) last_path = self.arw['filter_log_store'].get_path(last_iter) self.arw['filter_log_treeview'].scroll_to_cell( last_path, self.arw['filter_log_treeview'].get_column(0))
def filter_log_show_add_rule(self, widget): """Show a dialog to allow the user to create a rule based on their selection""" model, iter = self.arw['filter_log_treeview'].get_selection( ).get_selected() if not model or not iter: return self.arw['filter_log_rule_domains_store'].clear() self.arw['filter_log_rule_destination_store'].clear() domain = model.get_value(iter, 1) domain_parts = extract_domain_parts(domain) if not domain_parts.subdomain: # We got a top-level domain (eg. google.com) iter = self.arw['filter_log_rule_domains_store'].append() self.arw['filter_log_rule_domains_store'].set_value( iter, 0, domain) self.arw['filter_log_rule_domains_store'].set_value( iter, 1, _("Excludes any subdomains")) else: # We got a sub-domain (eg: www.google.com) subdomains = domain_parts.subdomain.split('.') for n, subdomain in enumerate(reversed(subdomains)): sd = '.'.join(subdomains[n:]) iter = self.arw['filter_log_rule_domains_store'].append() self.arw['filter_log_rule_domains_store'].set_value( iter, 0, '*.' + sd + '.' + domain_parts.registered_domain) self.arw['filter_log_rule_domains_store'].set_value( iter, 1, _("Access limited to subdomain and below")) iter = self.arw['filter_log_rule_domains_store'].append() self.arw['filter_log_rule_domains_store'].set_value( iter, 0, sd + '.' + domain_parts.registered_domain) self.arw['filter_log_rule_domains_store'].set_value( iter, 1, _("Access limited to subdomain only")) # Add wildcard for the top-level domain iter = self.arw['filter_log_rule_domains_store'].append() self.arw['filter_log_rule_domains_store'].set_value( iter, 0, '*.' + domain_parts.registered_domain) self.arw['filter_log_rule_domains_store'].set_value( iter, 1, _("Full access to %s") % domain_parts.registered_domain) # Populate the potential group/rules to add to rule_iter = self.arw['filter_log_rule_destination_store'].append(None) self.arw['filter_log_rule_destination_store'].set_value( rule_iter, 0, _("Rules")) for index, item in enumerate( self.controller.filter_rules.filter_store): iter = self.arw['filter_log_rule_destination_store'].append( rule_iter) self.arw['filter_log_rule_destination_store'].set_value( iter, RULE_DESTINATION_COLUMN_NAME, item[0]) self.arw['filter_log_rule_destination_store'].set_value( iter, RULE_DESTINATION_COLUMN_TYPE, 'rule') self.arw['filter_log_rule_destination_store'].set_value( iter, RULE_DESTINATION_COLUMN_FOREGROUND, item[15]) self.arw['filter_log_rule_destination_store'].set_value( iter, RULE_DESTINATION_COLUMN_BACKGROUND, item[16]) self.arw['filter_log_rule_destination_store'].set_value( iter, RULE_DESTINATION_COLUMN_INDEX, index) group_iter = self.arw['filter_log_rule_destination_store'].append(None) self.arw['filter_log_rule_destination_store'].set_value( group_iter, 0, _("Groups")) for index, item in enumerate(self.controller.proxy_group.groups_store): iter = self.arw['filter_log_rule_destination_store'].append( group_iter) self.arw['filter_log_rule_destination_store'].set_value( iter, RULE_DESTINATION_COLUMN_NAME, item[0]) self.arw['filter_log_rule_destination_store'].set_value( iter, RULE_DESTINATION_COLUMN_TYPE, 'group') self.arw['filter_log_rule_destination_store'].set_value( iter, RULE_DESTINATION_COLUMN_INDEX, index) # Set the information response = self.arw['filter_log_add_rule_dialog'].run() if response == Gtk.ResponseType.OK: active_iter = self.arw[ 'filter_log_rule_domains_combo'].get_active_iter() dest_model, dest_iter = self.arw[ 'filter_log_rule_destination_view'].get_selection( ).get_selected() index = dest_model.get_value(dest_iter, RULE_DESTINATION_COLUMN_INDEX) type = dest_model.get_value(dest_iter, RULE_DESTINATION_COLUMN_TYPE) domain_to_add = self.arw[ 'filter_log_rule_domains_store'].get_value(active_iter, 0) # Add the rule to the correct place if type == 'group': current_domains = self.controller.proxy_group.groups_store[ index][1].split('\n') if domain_to_add in current_domains: showwarning( _("Domain Already Exists"), _("The domain already exists in the group. Skipping.")) else: current_domains.append(domain_to_add) self.controller.proxy_group.groups_store[index][1] = '\n'.join( current_domains) elif type == 'rule': current_domains = self.controller.filter_rules.filter_store[ index][8].strip().split('\n') if domain_to_add in current_domains: showwarning( _("Domain Already Exists"), _("The domain already exists in the rule. Skipping.")) else: current_domains.append(domain_to_add) self.controller.filter_rules.filter_store[index][ 8] = '\n'.join(current_domains) self.arw['filter_log_add_rule_dialog'].hide()
if __name__ == "__main__": global win, parser, configname, load_locale # Get the configuration if len(sys.argv) > 1: # if the config is indicated on the command line if len(sys.argv[1].strip()) > 0: configname = sys.argv[1] else: configname = "" password = "" if requires_password(configname): dialog = PasswordDialog() password = dialog.run() while password: if test_decrypt_config(configname, password): break showwarning(_("Password"), _("Entered password is incorrect")) password = dialog.run() if not password: showwarning(_("Cancelling"), _("No password entered, quitting")) sys.exit(1) dialog.destroy() win = Confix(configname, password) gtk.main()
def load_from_backup(self, zf, offline=False, type=None, show_warning=True): permission_path = self.find_in_zip(zf, 'idefix.json') network_path = self.find_in_zip(zf, 'idefix2_conf.json') confix_path = self.find_in_zip(zf, 'confix.cfg') autoconf_path = self.find_in_zip(zf, 'idefix_auto.conf') self.arw['restore_config_permission_check'].set_sensitive(permission_path is not None) self.arw['restore_config_network_check'].set_sensitive(network_path is not None) self.arw['restore_config_confix_check'].set_sensitive(confix_path is not None) self.arw['restore_config_permission_check'].set_active(False) self.arw['restore_config_network_check'].set_active(False) self.arw['restore_config_confix_check'].set_active(False) if type: response = Gtk.ResponseType.OK if 'permissions' in type: self.arw['restore_config_permission_check'].set_active(True) if 'network' in type: self.arw['restore_config_network_check'].set_active(True) if 'restore_config_confix_check' in type: self.arw['restore_config_confix_check'].set_active(True) else: response = self.arw['restore_config_dialog'].run() if response != Gtk.ResponseType.OK: self.arw['restore_config_dialog'].hide() return import_permissions = self.arw['restore_config_permission_check'].get_active() import_network = self.arw['restore_config_network_check'].get_active() import_confix = self.arw['restore_config_confix_check'].get_active() if not import_confix and not import_network and not import_permissions: alert(_("You must choose at least one option to restore")) self.arw['restore_config_dialog'].hide() return skip_confix = '' skip_network = '' skip_permissions = '' if import_permissions: try: data1 = zf.open(permission_path).read().decode("cp850") except KeyError: skip_permissions = _("Permissions file does not exist in backup") else: self.import_permissions(data1) if import_network: try: data1 = zf.open(network_path).read().decode("cp850").replace('\r\n', '\n') except KeyError: skip_network = _("Network file does not exist in backup") else: # Check if idefix_auto is present in the zip file and restore it. try: data2 = zf.open(autoconf_path).read().decode('utf-8').replace('\r\n', '\n') except KeyError: data2 = None self.import_network(data1, data2) if import_confix: try: data1 = zf.open(confix_path).read().decode("cp850") except KeyError: skip_confix = _("Connections file not exist in backup") else: with open(self.controller.profiles.filename, 'w') as f: f.write(data1) self.controller.update() self.update_gui() msg = None if skip_permissions or skip_network or skip_confix: msg = '' if skip_permissions: msg += skip_permissions + '\n' if skip_network: msg += skip_network + '\n' if skip_confix: msg += skip_confix + '\n' if show_warning: showwarning(_("Some files were not restored"), msg) if offline: configname = os.path.split(self.configpath)[1] self.controller.offline = True # close ftp connection try: self.controller.ftp.close() except: pass # disable the save button self.controller.arw["save_button1"].set_sensitive(False) self.controller.arw["save_button2"].set_sensitive(False) self.controller.arw["configname"].set_text(configname) self.arw['restore_config_dialog'].hide() if import_confix and not skip_confix: alert(_("Please restart Confix to use your restored profiles")) sys.exit(1) return msg
def action_import_repository(self, widget): if self.imported_groups or self.groups_changed: if askyesno(_("Save Changes"), _("Do you want to save your changes?")): self.save_groups() self.imported_groups = False self.groups_changed = False self.widgets['add_group_menu'].hide() # Get the categories from the server data = fetch_repository_categories() if not data: showwarning(_("Repository"), _("Could not get files from server")) return self._cached_categories = {} self._cached_groups = {} self._cached_groups_by_category_id = {} self.group_results = [] self.categories_in_results = [] self.widgets['repository_store'].clear() category_iters = {None: None} # Always make sure parent id is at the top data.sort(key=lambda x: x['parent_id'] is None, reverse=True) for category in data: self._cached_categories[int(category['id'])] = category category_id = int(category['id']) if category.get('parent_id'): parent_id = int(category['parent_id']) else: parent_id = None category_iters[category_id] = iter = self.widgets[ 'repository_store'].append(category_iters.get(parent_id)) self.widgets['repository_store'].set_value(iter, IMPORT_COLUMN_NAME, category['name']) self.widgets['repository_store'].set_value(iter, IMPORT_COLUMN_ID, category_id) self.widgets['repository_store'].set_value( iter, IMPORT_COLUMN_PARENT_ID, parent_id) self.widgets['repository_store'].set_value(iter, IMPORT_COLUMN_TYPE, CATEGORY_TYPE) self.widgets['repository_store'].set_value(iter, IMPORT_COLUMN_CATEGORY, True) self._cached_categories[int( category['id'] )]['path'] = self.widgets['repository_store'].get_path(iter) for group in search_repository_groups(): self.add_group_to_store(group) self.widgets['import_window'].show_all()