class USBCreator(object): def __init__(self): # Load window and widgets self.scriptName = basename(__file__) self.scriptDir = abspath(dirname(__file__)) self.mediaDir = join(self.scriptDir, '../../share/usb-creator') self.builder = Gtk.Builder() self.builder.add_from_file(join(self.mediaDir, 'usb-creator.glade')) # Main window objects go = self.builder.get_object self.window = go("usb-creator") self.lblDevice = go("lblDevice") self.lblIso = go("lblIso") self.lblAvailable = go("lblAvailable") self.lblRequired = go("lblRequired") self.cmbDevice = go("cmbDevice") self.cmbDeviceHandler = ComboBoxHandler(self.cmbDevice) self.txtIso = go("txtIso") self.btnRefresh = go("btnRefresh") self.btnUnmount = go("btnUnmount") self.btnBrowseIso = go("btnBrowseIso") self.btnClear = go("btnClear") self.chkFormatDevice = go("chkFormatDevice") self.chkRepairDevice = go("chkRepairDevice") self.btnExecute = go("btnExecute") self.lblUsb = go("lblUsb") self.tvUsbIsos = go("tvUsbIsos") self.btnDelete = go("btnDelete") self.pbUsbCreator = go("pbUsbCreator") self.statusbar = go("statusbar") # Translations self.window.set_title(_("USB Creator")) self.lblDevice.set_label(_("Device")) self.lblUsb.set_label(_("USB")) self.available_text = _("Available") self.required_text = _("Required") self.chkFormatDevice.set_label(_("Format device")) self.chkFormatDevice.set_tooltip_text(_("Warning: all data will be lost")) self.chkRepairDevice.set_label(_("Repair device")) self.chkRepairDevice.set_tooltip_text(_("Tries to repair an unbootable USB")) self.btnExecute.set_label("_{}".format(_("Execute"))) self.lblIso.set_label(_("ISO")) self.btnDelete.set_label("_{}".format(_("Delete"))) self.btnRefresh.set_tooltip_text(_("Refresh device list")) self.btnUnmount.set_tooltip_text(_("Unmount device")) self.btnBrowseIso.set_tooltip_text(_("Browse for ISO file")) self.btnClear.set_tooltip_text(_("Clear the ISO field")) # Log lines to show: check string, percent done (0=pulse, appends last word in log line), show line (translatable) self.log_lines = [] self.log_lines.append(["partitioning usb", 5, _("Partitioning USB...")]) self.log_lines.append(["searching for bad blocks", 0, _("Searching for bad block")]) self.log_lines.append(["installing", 15, _("Installing Grub...")]) self.log_lines.append(["rsync", 25, _("Start copying ISO...")]) self.log_lines.append(["left to copy", 0, _("kB left to copy:")]) self.log_lines.append(["check hash", 85, _("Check hash of ISO...")]) # Initiate variables self.devices = [] self.device = {} self.device['path'] = '' self.device['size'] = 0 self.device['has_partition'] = False self.device['mount'] = '' self.device['available'] = 0 self.device["new_iso"] = '' self.device["new_iso_required"] = 0 self.logos = self.get_logos() self.queue = Queue(-1) self.threads = {} self.htmlDir = join(self.mediaDir, "html") self.helpFile = join(self.get_language_dir(), "help.html") log = getoutput("cat /usr/bin/usb-creator | grep 'LOG=' | cut -d'=' -f 2") self.log_file = log[0] self.log = Logger(self.log_file, addLogTime=False, maxSizeKB=5120) self.tvUsbIsosHandler = TreeViewHandler(self.tvUsbIsos) self.lblAvailable.set_label('') self.lblRequired.set_label('') # Connect builder signals and show window self.builder.connect_signals(self) self.window.show_all() # Get attached devices self.on_btnRefresh_clicked() # Init log init_log = ">>> Start USB Creator: {} <<<".format(datetime.now()) self.log.write(init_log) # Version information self.version_text = _("Version") self.pck_version = getPackageVersion('usb-creator') self.set_statusbar_message("{}: {}".format(self.version_text, self.pck_version)) # =============================================== # Main window functions # =============================================== def on_btnExecute_clicked(self, widget): if exists(self.device["path"]): arguments = [] arguments.append("-d {}".format(self.device["path"])) clear = self.chkFormatDevice.get_active() repair = self.chkRepairDevice.get_active() iso = self.device["new_iso"] iso_path = self.txtIso.get_text().strip() # ISO path does not exist if iso != iso_path: msg = _("Cannot add ISO from path: {}.\n" "Please, remove the ISO path or browse for an existing ISO.") WarningDialog(self.btnExecute.get_label(), msg.format(iso_path)) return True # Check if there is enough space available = self.device["available"] if self.chkFormatDevice.get_active(): available = self.device["size"] if available - self.device["new_iso_required"] < 0: msg = _("There is not enough space available on the pen drive.\n" "Please, remove unneeded files before continuing.") WarningDialog(self.btnExecute.get_label(), msg) return True if clear: arguments.append("-f") arguments.append("-b") if repair: arguments.append("-r") arguments.append("-b") arguments.append("-g") # This should use the history file to get the original hash arguments.append("-s") if exists(iso): arguments.append("-i \"{}\"".format(iso)) arguments.append("-s") cmd = "usb-creator {}".format(" ".join(arguments)) self.log.write("Execute command: {}".format(cmd)) self.exec_command(cmd) def on_btnDelete_clicked(self, widget): selected_isos = self.tvUsbIsosHandler.getToggledValues(toggleColNr=0, valueColNr=2) if selected_isos: msg = _("Are you sure you want to remove the selected ISO from the device?") answer = QuestionDialog(self.btnDelete.get_label(), msg) if answer: for iso in selected_isos: iso_path = join(self.device["mount"], iso) if exists(iso_path): os.remove(iso_path) self.log.write("Remove ISO: {}".format(iso_path)) shell_exec("usb-creator -d {} -g".format(self.device["path"])) self.on_cmbDevice_changed() self.fill_treeview_usbcreator(self.device["mount"]) def on_btnBrowseIso_clicked(self, widget): file_filter = Gtk.FileFilter() file_filter.set_name("ISO") file_filter.add_mime_type("application/x-cd-image") file_filter.add_pattern("*.iso") start_dir = dirname(self.txtIso.get_text().strip()) if not exists(start_dir): start_dir = expanduser("~") iso = SelectFileDialog(title=_('Select ISO'), start_directory=start_dir, gtkFileFilter=file_filter).show() if iso is not None: self.log.write("Add ISO: {}".format(iso)) self.txtIso.set_text(iso) def on_btnClear_clicked(self, widget): self.txtIso.set_text('') def on_txtIso_changed(self, widget=None): iso_path = self.txtIso.get_text().strip() if exists(iso_path): if isdir(iso_path): isos = glob(join(iso_path, '*.iso')) if isos: required = 0 for iso in isos: # Check if these ISOs overwrite current USB ISOs check_usb_iso_size = 0 if not self.chkFormatDevice.get_active(): check_usb_iso = join(self.device["mount"], basename(iso)) if exists(check_usb_iso): check_usb_iso_size = self.get_iso_size(check_usb_iso) required += (self.get_iso_size(iso) - check_usb_iso_size) if required < 0: required = 0 self.lblRequired.set_label("{}: {} MB".format(self.required_text, int(required / 1024))) # Save the info self.device["new_iso"] = iso_path self.device["new_iso_required"] = required self.log.write("New ISO directory: {}, {}".format(iso_path, required)) else: self.device["new_iso"] = '' self.device["new_iso_required"] = 0 self.log.write("New ISO directory does not contain ISOs: {}".format(iso_path)) else: # Check if this ISO overwrites current USB ISO check_usb_iso_size = 0 if not self.chkFormatDevice.get_active(): check_usb_iso = join(self.device["mount"], basename(iso_path)) if exists(check_usb_iso): check_usb_iso_size = self.get_iso_size(check_usb_iso) required = (self.get_iso_size(iso_path) - check_usb_iso_size) self.lblRequired.set_label("{}: {} MB".format(self.required_text, int(required / 1024))) # Save the info self.device["new_iso"] = iso_path self.device["new_iso_required"] = required self.log.write("New ISO: {}, {}".format(iso_path, required)) else: self.device["new_iso"] = '' self.device["new_iso_required"] = 0 self.lblRequired.set_text('') def on_btnRefresh_clicked(self, widget=None): self.devices = self.get_devices() self.cmbDeviceHandler.fillComboBox(self.devices, 0) def on_btnUnmount_clicked(self, widget): unmount_text = _("Unmount") device = self.device["path"] self.unmount_device(device) self.on_btnRefresh_clicked() if device in self.devices: msg = _("Could not unmount the device.\n" "Please unmount the device manually.") else: msg = _("You can now safely remove the device.") MessageDialog(unmount_text, msg) def on_cmbDevice_changed(self, widget=None): device = self.cmbDeviceHandler.getValue() if device is not None: mount = '' size = 0 available = 0 # Get the size of the USB usb_size = getoutput("env LANG=C udisks --show-info {} | grep size".format(device)) if usb_size: # udisks returns bytes, while df returns kbs size = int(int(usb_size[0].split(":")[1].strip()) / 1024) # Assume that the USB is empty (will check later) available = size # Get free size on USB has_partition = self.device_has_partition(device) if has_partition: mount = self.get_device_mount(device) # This function can be called from on_chkFormatDevice_toggled if widget != self.chkFormatDevice: self.chkFormatDevice.set_sensitive(True) self.chkFormatDevice.set_active(False) free_size = getoutput("df --output=avail {}1 | awk 'NR==2'".format(device)) if free_size: available = int(free_size[0]) else: self.chkFormatDevice.set_active(True) self.chkFormatDevice.set_sensitive(False) self.chkRepairDevice.set_active(False) self.fill_treeview_usbcreator(mount) self.lblAvailable.set_label("{}: {} MB".format(self.available_text, int(available / 1024))) # Save the info self.device['path'] = device self.device['size'] = size self.device['has_partition'] = has_partition self.device['mount'] = mount self.device['available'] = available self.log.write("Selected device info: {}".format(self.device)) # Update info iso_path = self.txtIso.get_text().strip() if iso_path != "" and exists(iso_path): self.on_txtIso_changed() else: self.fill_treeview_usbcreator() self.lblAvailable.set_label('') self.lblRequired.set_label('') self.txtIso.set_text('') self.device['path'] = '' self.device['size'] = 0 self.device['has_partition'] = False self.device['mount'] = '' self.device['available'] = 0 self.device["new_iso"] = '' self.device["new_iso_required"] = 0 def on_chkFormatDevice_toggled(self, widget): # Recalculate available space and requied space self.on_cmbDevice_changed(widget) def on_btnHelp_clicked(self, widget): # Open the help file as the real user (not root) shell_exec("%s/open-as-user \"%s\"" % (self.scriptDir, self.helpFile)) def fill_treeview_usbcreator(self, mount=''): isos_list = [] # columns: checkbox, image (logo), device, driver column_types = ['bool', 'GdkPixbuf.Pixbuf', 'str', 'str'] if exists(mount): isos = glob(join(mount, '*.iso')) for iso in isos: iso_name = basename(iso) iso_name_lower = iso_name.lower() iso_size = "{} MB".format(int(self.get_iso_size(iso) / 1024)) iso_logo = "" for key, logo in list(self.logos.items()): if key != "iso": if key in iso_name_lower: if len(logo) > len(iso_logo): iso_logo = logo if iso_logo == "": iso_logo = self.logos["iso"] self.log.write("ISO on {}: {}, {}, {}".format(mount, iso_name, iso_size, iso_logo)) isos_list.append([False, iso_logo, iso_name, iso_size]) # Fill treeview self.tvUsbIsosHandler.fillTreeview(contentList=isos_list, columnTypesList=column_types) def exec_command(self, command): try: # Run the command in a separate thread self.set_buttons_state(False) name = 'cmd' t = ExecuteThreadedCommands([command], self.queue) self.threads[name] = t t.daemon = True t.start() self.queue.join() GObject.timeout_add(1000, self.check_thread, name) except Exception as detail: ErrorDialog(self.btnExecute.get_label(), detail) def check_thread(self, name): if self.threads[name].is_alive(): self.set_progress() if not self.queue.empty(): ret = self.queue.get() self.log.write("Queue returns: {}".format(ret), 'check_thread') self.queue.task_done() self.show_message(ret) return True # Thread is done self.log.write(">> Thread is done", 'check_thread') if not self.queue.empty(): ret = self.queue.get() self.queue.task_done() self.show_message(ret) del self.threads[name] self.set_buttons_state(True) self.on_cmbDevice_changed() self.fill_treeview_usbcreator(self.device["mount"]) self.set_statusbar_message("{}: {}".format(self.version_text, self.pck_version)) return False def set_buttons_state(self, enable): if not enable: # Disable buttons self.btnExecute.set_sensitive(False) self.btnDelete.set_sensitive(False) self.btnBrowseIso.set_sensitive(False) self.btnRefresh.set_sensitive(False) self.btnUnmount.set_sensitive(False) self.btnClear.set_sensitive(False) self.chkFormatDevice.set_sensitive(False) self.chkRepairDevice.set_sensitive(False) self.cmbDevice.set_sensitive(False) self.txtIso.set_sensitive(False) else: # Enable buttons and reset progress bar self.btnExecute.set_sensitive(True) self.btnDelete.set_sensitive(True) self.btnBrowseIso.set_sensitive(True) self.btnRefresh.set_sensitive(True) self.btnUnmount.set_sensitive(True) self.btnClear.set_sensitive(True) self.chkFormatDevice.set_sensitive(True) self.chkRepairDevice.set_sensitive(True) self.cmbDevice.set_sensitive(True) self.txtIso.set_sensitive(True) self.pbUsbCreator.set_fraction(0) def get_logos(self): logos_dict = {} logos_path = join(self.mediaDir, 'logos') logos = glob(join(logos_path, '*.png')) for logo in logos: key = splitext(basename(logo))[0] logos_dict[key] = logo return logos_dict def set_progress(self): if exists(self.log_file): msg = '' last_line = getoutput("tail -50 {} | grep -v DEBUG | grep -v ==".format(self.log_file)) for line in reversed(last_line): # Check for session start line: that is the last line to check if ">>>>>" in line and "<<<<<" in line: break for chk_line in self.log_lines: if chk_line[0] in line.lower(): #print((line)) word = '' if chk_line[1] == 0: self.pbUsbCreator.pulse() words = line.split(' ') for word in reversed(words): if word.strip() != '': break else: self.pbUsbCreator.set_fraction(float(chk_line[1] / 100)) msg = "{} {}".format(chk_line[2], word) break if msg != '': break self.set_statusbar_message(msg) def set_statusbar_message(self, message): if message is not None: context = self.statusbar.get_context_id('message') self.statusbar.push(context, message) def get_devices(self): devices = [] my_devices = getoutput("udisks --enumerate-device-files | egrep '/dev/sd[a-z]$'") for device in my_devices: info = getoutput("env LANG=C udisks --show-info {}".format(device)) detachable = False has_partition = False for line in info: if "detachable" in line and "1" in line: detachable = True elif "partition" in line: has_partition = True if detachable and has_partition: devices.append(device) break devices.sort() return devices def device_has_partition(self, device): part_count = getoutput("udisks --show-info {} | grep count | grep -v block".format(device)) if part_count: if "1" in part_count[0]: return True return False def get_device_mount(self, device): shell_exec("udisks --mount {}1".format(device)) mount = getoutput("grep %s1 /etc/mtab | awk '{print $2}' | sed 's/\\040/ /g'" % device) if mount: return mount[0] return '' def get_iso_size(self, iso): iso_size = getoutput("du -Lk \"%s\" | awk '{print $1}'" % iso) if iso_size: return int(iso_size[0]) return 0 def unmount_device(self, device): shell_exec("udisks --unmount {}1".format(device)) shell_exec("udisks --detach {}".format(device)) # Close the gui def on_usbcreator_destroy(self, widget): # Unmount devices for device in self.devices: if self.get_device_mount(device) != "": self.unmount_device(device) # Close the app Gtk.main_quit() def show_message(self, cmdOutput): try: self.log.write("Command output: {}".format(cmdOutput), 'show_message') ret = int(cmdOutput) if ret > 1 and ret != 255: if ret == 1: ErrorDialog(self.btnExecute.get_label(), _("Run this application with root permission.")) elif ret == 2: ErrorDialog(self.btnExecute.get_label(), _("Wrong arguments were passed to usb-creator.")) elif ret == 3: ErrorDialog(self.btnExecute.get_label(), _("The device was not found or no device was given.")) elif ret == 4: ErrorDialog(self.btnExecute.get_label(), _("Given ISO path was not found.")) elif ret == 5: ErrorDialog(self.btnExecute.get_label(), _("Device is in use by another application.")) elif ret == 6: ErrorDialog(self.btnExecute.get_label(), _("Unable to mount the device.")) elif ret == 7: ErrorDialog(self.btnExecute.get_label(), _("Hash mismatch.")) elif ret == 8: ErrorDialog(self.btnExecute.get_label(), _("The device has no fat32 partition.")) elif ret == 9: ErrorDialog(self.btnExecute.get_label(), _("The device has no bootloader installed.")) elif ret == 10: ErrorDialog(self.btnExecute.get_label(), _("There is not enough space available on the device.")) elif ret == 11: ErrorDialog(self.btnExecute.get_label(), _("Unable to guess distribution from ISO name.\n" "Make sure you have the distribution name in the ISO name.")) else: msg = _("An unknown error accured.\n" "Please, visit our forum for support: http://forums.solydxk.com") ErrorDialog(self.window.get_title(), msg) else: msg = _("The USB was successfully written.") MessageDialog(self.window.get_title(), msg) except: ErrorDialog(self.btnExecute.get_label(), cmdOutput) # =============================================== # Language specific functions # =============================================== def get_language_dir(self): # First test if full locale directory exists, e.g. html/pt_BR, # otherwise perhaps at least the language is there, e.g. html/pt # and if that doesn't work, try html/pt_PT lang = self.get_current_language() path = join(self.htmlDir, lang) if not isdir(path): base_lang = lang.split('_')[0].lower() path = join(self.htmlDir, base_lang) if not isdir(path): path = join(self.htmlDir, "{}_{}".format(base_lang, base_lang.upper())) if not isdir(path): path = join(self.htmlDir, 'en') return path def get_current_language(self): lang = os.environ.get('LANG', 'US').split('.')[0] if lang == '': lang = 'en' return lang
class USBCreator(object): def __init__(self): # Load window and widgets self.scriptName = basename(__file__) self.scriptDir = abspath(dirname(__file__)) self.mediaDir = join(self.scriptDir, '../../share/usb-creator') self.builder = Gtk.Builder() self.builder.add_from_file(join(self.mediaDir, 'usb-creator.glade')) # Main window objects go = self.builder.get_object self.window = go("usb-creator") self.lblDevice = go("lblDevice") self.lblIso = go("lblIso") self.lblAvailable = go("lblAvailable") self.lblRequired = go("lblRequired") self.cmbDevice = go("cmbDevice") self.cmbDeviceHandler = ComboBoxHandler(self.cmbDevice) self.txtIso = go("txtIso") self.btnRefresh = go("btnRefresh") self.btnUnmount = go("btnUnmount") self.btnBrowseIso = go("btnBrowseIso") self.btnClear = go("btnClear") self.chkFormatDevice = go("chkFormatDevice") self.chkRepairDevice = go("chkRepairDevice") self.btnExecute = go("btnExecute") self.lblUsb = go("lblUsb") self.tvUsbIsos = go("tvUsbIsos") self.btnDelete = go("btnDelete") self.pbUsbCreator = go("pbUsbCreator") self.statusbar = go("statusbar") # Translations self.window.set_title(_("USB Creator")) self.lblDevice.set_label(_("Device")) self.lblUsb.set_label(_("USB")) self.available_text = _("Available") self.required_text = _("Required") self.chkFormatDevice.set_label(_("Format device")) self.chkFormatDevice.set_tooltip_text( _("Warning: all data will be lost")) self.chkRepairDevice.set_label(_("Repair device")) self.chkRepairDevice.set_tooltip_text( _("Tries to repair an unbootable USB")) self.btnExecute.set_label("_{}".format(_("Execute"))) self.lblIso.set_label(_("ISO")) self.btnDelete.set_label("_{}".format(_("Delete"))) self.btnRefresh.set_tooltip_text(_("Refresh device list")) self.btnUnmount.set_tooltip_text(_("Unmount device")) self.btnBrowseIso.set_tooltip_text(_("Browse for ISO file")) self.btnClear.set_tooltip_text(_("Clear the ISO field")) # Log lines to show: check string, percent done (0=pulse, appends last word in log line), show line (translatable) self.log_lines = [] self.log_lines.append( ["partitioning usb", 5, _("Partitioning USB...")]) self.log_lines.append( ["searching for bad blocks", 0, _("Searching for bad block")]) self.log_lines.append(["installing", 15, _("Installing Grub...")]) self.log_lines.append(["rsync", 25, _("Start copying ISO...")]) self.log_lines.append(["left to copy", 0, _("kB left to copy:")]) self.log_lines.append(["check hash", 85, _("Check hash of ISO...")]) # Initiate variables self.device = {} self.device['path'] = '' self.device['mount'] = '' self.device['size'] = 0 self.device['available'] = 0 self.device["new_iso"] = '' self.device["new_iso_required"] = 0 self.logos = self.get_logos() self.queue = Queue(-1) self.threads = {} self.htmlDir = join(self.mediaDir, "html") self.helpFile = join(self.get_language_dir(), "help.html") log = getoutput( "cat /usr/bin/usb-creator | grep 'LOG=' | cut -d'=' -f 2") self.log_file = log[0] self.log = Logger(self.log_file, addLogTime=False, maxSizeKB=5120) self.tvUsbIsosHandler = TreeViewHandler(self.tvUsbIsos) self.udisks2 = Udisks2() self.lblAvailable.set_label('') self.lblRequired.set_label('') # Connect builder signals and show window self.builder.connect_signals(self) self.window.show_all() # Get attached devices self.on_btnRefresh_clicked() # Init log init_log = ">>> Start USB Creator: {} <<<".format(datetime.now()) self.log.write(init_log) # Version information self.version_text = _("Version") self.pck_version = getPackageVersion('usb-creator') self.set_statusbar_message("{}: {}".format(self.version_text, self.pck_version)) # =============================================== # Main window functions # =============================================== def on_btnExecute_clicked(self, widget): if exists(self.device["path"]): arguments = [] arguments.append("-d {}".format(self.device["path"])) clear = self.chkFormatDevice.get_active() repair = self.chkRepairDevice.get_active() iso = self.device["new_iso"] iso_path = self.txtIso.get_text().strip() # ISO path does not exist if iso != iso_path: msg = _( "Cannot add ISO from path: {}.\n" "Please, remove the ISO path or browse for an existing ISO." ) WarningDialog(self.btnExecute.get_label(), msg.format(iso_path)) return True # Check if there is enough space available = self.device["available"] if self.chkFormatDevice.get_active(): available = self.device["size"] if (available) - self.device["new_iso_required"] < 0: msg = _( "There is not enough space available on the pen drive.\n" "Please, remove unneeded files before continuing.") WarningDialog(self.btnExecute.get_label(), msg) return True if clear: arguments.append("-f") arguments.append("-b") if repair: arguments.append("-r") arguments.append("-b") arguments.append("-g") # This should use the history file to get the original hash arguments.append("-s") if exists(iso): arguments.append("-i \"{}\"".format(iso)) arguments.append("-s") cmd = "usb-creator {}".format(" ".join(arguments)) self.log.write("Execute command: {}".format(cmd)) self.exec_command(cmd) def on_btnDelete_clicked(self, widget): selected_isos = self.tvUsbIsosHandler.getToggledValues(toggleColNr=0, valueColNr=2) if selected_isos: msg = _( "Are you sure you want to remove the selected ISO from the device?" ) answer = QuestionDialog(self.btnDelete.get_label(), msg) if answer: for iso in selected_isos: iso_path = join(self.device["mount"], iso) if exists(iso_path): os.remove(iso_path) self.log.write("Remove ISO: {}".format(iso_path)) shell_exec("usb-creator -d {} -g".format(self.device["path"])) self.on_btnRefresh_clicked() self.fill_treeview_usbcreator(self.device["mount"]) def on_btnBrowseIso_clicked(self, widget): file_filter = Gtk.FileFilter() file_filter.set_name("ISO") file_filter.add_mime_type("application/x-cd-image") file_filter.add_pattern("*.iso") start_dir = dirname(self.txtIso.get_text().strip()) if not exists(start_dir): start_dir = expanduser("~") iso = SelectFileDialog(title=_('Select ISO'), start_directory=start_dir, gtkFileFilter=file_filter).show() if iso is not None: self.log.write("Add ISO: {}".format(iso)) self.txtIso.set_text(iso) def on_btnClear_clicked(self, widget): self.txtIso.set_text('') def on_txtIso_changed(self, widget=None): iso_path = self.txtIso.get_text().strip() if exists(iso_path): if isdir(iso_path): isos = glob(join(iso_path, '*.iso')) if isos: required = 0 for iso in isos: # Check if these ISOs overwrite current USB ISOs check_usb_iso_size = 0 if not self.chkFormatDevice.get_active(): check_usb_iso = join(self.device["mount"], basename(iso)) if exists(check_usb_iso): check_usb_iso_size = self.get_iso_size( check_usb_iso) required += (self.get_iso_size(iso) - check_usb_iso_size) if required < 0: required = 0 self.lblRequired.set_label("{}: {} MB".format( self.required_text, int(required / 1024))) # Save the info self.device["new_iso"] = iso_path self.device["new_iso_required"] = required self.log.write("New ISO directory: {}, {}".format( iso_path, required)) else: self.device["new_iso"] = '' self.device["new_iso_required"] = 0 self.log.write( "New ISO directory does not contain ISOs: {}".format( iso_path)) else: # Check if this ISO overwrites current USB ISO check_usb_iso_size = 0 if not self.chkFormatDevice.get_active(): check_usb_iso = join(self.device["mount"], basename(iso_path)) if exists(check_usb_iso): check_usb_iso_size = self.get_iso_size(check_usb_iso) required = (self.get_iso_size(iso_path) - check_usb_iso_size) self.lblRequired.set_label("{}: {} MB".format( self.required_text, int(required / 1024))) # Save the info self.device["new_iso"] = iso_path self.device["new_iso_required"] = required self.log.write("New ISO: {}, {}".format(iso_path, required)) else: self.device["new_iso"] = '' self.device["new_iso_required"] = 0 self.lblRequired.set_text('') def on_btnRefresh_clicked(self, widget=None): self.udisks2.fill_devices() drives = self.udisks2.get_drives() self.cmbDeviceHandler.fillComboBox(drives, 0) def on_btnUnmount_clicked(self, widget): unmount_text = _("Unmount") device = self.device["path"] try: self.udisks2.unmount_drive(device) self.on_btnRefresh_clicked() msg = _("You can now safely remove the device.") except Exception as e: msg = _("Could not unmount the device.\n" "Please unmount the device manually.") self.log.write("ERROR: %s" % str(e)) MessageDialog(unmount_text, msg) def on_cmbDevice_changed(self, widget=None): drive_path = self.cmbDeviceHandler.getValue() device_paths = [] if drive_path is not None: drive = self.udisks2.devices[drive_path] device_paths = self.udisks2.get_drive_device_paths(drive_path) device = '' if device_paths: device = device_paths[0] mount = '' size = 0 available = 0 # Get free size on USB size = drive['total_size'] available = drive['free_size'] if self.chkFormatDevice.get_active(): available = size if device != '': mount = drive[device]['mount_point'] if not exists(mount): # Mount if not already mounted try: mount = self.udisks2.mount_device(device) except Exception as e: self.show_message(6) self.log.write("ERROR: %s" % str(e)) if exists(mount) and not self.chkFormatDevice.get_active(): size = drive[device]['total_size'] available = drive[device]['free_size'] # This function can be called from on_chkFormatDevice_toggled if widget != self.chkFormatDevice: self.chkFormatDevice.set_sensitive(True) self.chkFormatDevice.set_active(False) else: # No partition: always format the device self.chkFormatDevice.set_active(True) self.chkFormatDevice.set_sensitive(False) self.chkRepairDevice.set_active(False) self.fill_treeview_usbcreator(mount) self.lblAvailable.set_label("{}: {} MB".format( self.available_text, int(available / 1024))) # Save the info self.device['path'] = drive_path self.device['mount'] = mount self.device['size'] = size self.device['available'] = available self.log.write("Selected device info: {}".format(self.device)) # Update info iso_path = self.txtIso.get_text().strip() if iso_path != "" and exists(iso_path): self.on_txtIso_changed() else: self.fill_treeview_usbcreator() self.lblAvailable.set_label('') self.lblRequired.set_label('') self.txtIso.set_text('') self.device['path'] = '' self.device['mount'] = '' self.device['size'] = 0 self.device['available'] = 0 self.device["new_iso"] = '' self.device["new_iso_required"] = 0 def on_chkFormatDevice_toggled(self, widget): # Recalculate available space and requied space self.on_cmbDevice_changed(widget) def on_btnHelp_clicked(self, widget): # Open the help file as the real user (not root) shell_exec("%s/open-as-user \"%s\"" % (self.scriptDir, self.helpFile)) def fill_treeview_usbcreator(self, mount=''): isos_list = [] # columns: checkbox, image (logo), device, driver column_types = ['bool', 'GdkPixbuf.Pixbuf', 'str', 'str'] if exists(mount): isos = glob(join(mount, '*.iso')) for iso in isos: iso_name = basename(iso) iso_name_lower = iso_name.lower() iso_size = "{} MB".format(int(self.get_iso_size(iso) / 1024)) iso_logo = "" for key, logo in list(self.logos.items()): if key != "iso": if key in iso_name_lower: if len(logo) > len(iso_logo): iso_logo = logo if iso_logo == "": iso_logo = self.logos["iso"] self.log.write("ISO on {}: {}, {}, {}".format( mount, iso_name, iso_size, iso_logo)) isos_list.append([False, iso_logo, iso_name, iso_size]) # Fill treeview self.tvUsbIsosHandler.fillTreeview(contentList=isos_list, columnTypesList=column_types) def exec_command(self, command): try: # Run the command in a separate thread self.set_buttons_state(False) name = 'cmd' t = ExecuteThreadedCommands([command], self.queue) self.threads[name] = t t.daemon = True t.start() self.queue.join() GObject.timeout_add(1000, self.check_thread, name) except Exception as detail: ErrorDialog(self.btnExecute.get_label(), detail) def check_thread(self, name): if self.threads[name].is_alive(): self.set_progress() if not self.queue.empty(): ret = self.queue.get() self.log.write("Queue returns: {}".format(ret), 'check_thread') self.queue.task_done() self.show_message(ret) return True # Thread is done self.log.write(">> Thread is done", 'check_thread') ret = 0 if not self.queue.empty(): ret = self.queue.get() self.queue.task_done() del self.threads[name] self.set_buttons_state(True) self.on_btnRefresh_clicked() self.fill_treeview_usbcreator(self.device["mount"]) self.set_statusbar_message("{}: {}".format(self.version_text, self.pck_version)) self.show_message(ret) return False def set_buttons_state(self, enable): if not enable: # Disable buttons self.btnExecute.set_sensitive(False) self.btnDelete.set_sensitive(False) self.btnBrowseIso.set_sensitive(False) self.btnRefresh.set_sensitive(False) self.btnUnmount.set_sensitive(False) self.btnClear.set_sensitive(False) self.chkFormatDevice.set_sensitive(False) self.chkRepairDevice.set_sensitive(False) self.cmbDevice.set_sensitive(False) self.txtIso.set_sensitive(False) else: # Enable buttons and reset progress bar self.btnExecute.set_sensitive(True) self.btnDelete.set_sensitive(True) self.btnBrowseIso.set_sensitive(True) self.btnRefresh.set_sensitive(True) self.btnUnmount.set_sensitive(True) self.btnClear.set_sensitive(True) self.chkFormatDevice.set_sensitive(True) self.chkRepairDevice.set_sensitive(True) self.cmbDevice.set_sensitive(True) self.txtIso.set_sensitive(True) self.pbUsbCreator.set_fraction(0) def get_logos(self): logos_dict = {} logos_path = join(self.mediaDir, 'logos') logos = glob(join(logos_path, '*.png')) for logo in logos: key = splitext(basename(logo))[0] logos_dict[key] = logo return logos_dict def set_progress(self): if exists(self.log_file): msg = '' last_line = getoutput( "tail -50 {} | grep -v DEBUG | grep -v ==".format( self.log_file)) for line in reversed(last_line): # Check for session start line: that is the last line to check if ">>>>>" in line and "<<<<<" in line: break for chk_line in self.log_lines: if chk_line[0] in line.lower(): #print((line)) word = '' if chk_line[1] == 0: self.pbUsbCreator.pulse() words = line.split(' ') for word in reversed(words): if word.strip() != '': break else: self.pbUsbCreator.set_fraction( float(chk_line[1] / 100)) msg = "{} {}".format(chk_line[2], word) break if msg != '': break self.set_statusbar_message(msg) def set_statusbar_message(self, message): if message is not None: context = self.statusbar.get_context_id('message') self.statusbar.push(context, message) def get_iso_size(self, iso): # Returns kilobytes iso_info = os.stat(iso) if iso_info: return int(iso_info.st_size / 1024) return 0 # Close the gui def on_usbcreator_destroy(self, widget): # Unmount devices of drive and power-off drive try: if exists(self.device['path']) and \ exists(self.device['mount']): self.udisks2.unmount_drive(self.device['path']) self.udisks2.poweroff_drive(self.device['path']) except Exception as e: self.log.write("ERROR: %s" % str(e)) # Close the app Gtk.main_quit() def show_message(self, cmdOutput): try: self.log.write("Command output: {}".format(cmdOutput), 'show_message') ret = int(cmdOutput) if ret > 1 and ret != 255: if ret == 1: ErrorDialog( self.btnExecute.get_label(), _("Run this application with root permission.")) elif ret == 2: ErrorDialog( self.btnExecute.get_label(), _("Wrong arguments were passed to usb-creator.")) elif ret == 3: ErrorDialog( self.btnExecute.get_label(), _("The device was not found or no device was given.")) elif ret == 4: ErrorDialog(self.btnExecute.get_label(), _("Given ISO path was not found.")) elif ret == 5: ErrorDialog(self.btnExecute.get_label(), _("Device is in use by another application.")) elif ret == 6: ErrorDialog(self.btnExecute.get_label(), _("Unable to mount the device.")) elif ret == 7: ErrorDialog(self.btnExecute.get_label(), _("Hash mismatch.")) elif ret == 8: ErrorDialog(self.btnExecute.get_label(), _("The device has no fat32 partition.")) elif ret == 9: ErrorDialog(self.btnExecute.get_label(), _("The device has no bootloader installed.")) elif ret == 10: ErrorDialog( self.btnExecute.get_label(), _("There is not enough space available on the device.") ) elif ret == 11: ErrorDialog( self.btnExecute.get_label(), _("Unable to guess distribution from ISO name.\n" "Make sure you have the distribution name in the ISO name." )) else: msg = _( "An unknown error accured.\n" "Please, visit our forum for support: http://forums.solydxk.com" ) ErrorDialog(self.window.get_title(), msg) else: msg = _("The USB was successfully written.") MessageDialog(self.window.get_title(), msg) except: ErrorDialog(self.btnExecute.get_label(), cmdOutput) # =============================================== # Language specific functions # =============================================== def get_language_dir(self): # First test if full locale directory exists, e.g. html/pt_BR, # otherwise perhaps at least the language is there, e.g. html/pt # and if that doesn't work, try html/pt_PT lang = self.get_current_language() path = join(self.htmlDir, lang) if not isdir(path): base_lang = lang.split('_')[0].lower() path = join(self.htmlDir, base_lang) if not isdir(path): path = join(self.htmlDir, "{}_{}".format(base_lang, base_lang.upper())) if not isdir(path): path = join(self.htmlDir, 'en') return path def get_current_language(self): lang = os.environ.get('LANG', 'US').split('.')[0] if lang == '': lang = 'en' return lang
class UpdateManagerPref(object): def __init__(self): # Check if script is running self.scriptName = basename(__file__) self.umglobal = UmGlobal() # Handle arguments parser = argparse.ArgumentParser( description='SolydXK Update Manager Preferences') parser.add_argument('-r', '--reload', action="store_true", help='') args, extra = parser.parse_known_args() print(("args = {}".format(args))) if args.reload: pids = self.umglobal.getProcessPids("updatemanagerpref.py") if len(pids) > 1: print(("updatemanagerpref.py already running - kill pid {}". format(pids[0]))) os.system("kill {}".format(pids[0])) # Initiate logging self.logFile = join('/var/log', self.umglobal.settings['log']) self.log = Logger(self.logFile) # Load window and widgets self.builder = Gtk.Builder() self.builder.add_from_file( join(self.umglobal.shareDir, 'updatemanagerpref.glade')) # Preferences window objects go = self.builder.get_object self.window = go("windowPref") self.nbPref = go('nbPref') self.btnSaveMirrors = go('btnSaveMirrors') self.btnCheckMirrorsSpeed = go("btnCheckMirrorsSpeed") self.lblMirrors = go('lblMirrors') self.tvMirrors = go("tvMirrors") self.btnRemoveBlackList = go("btnRemoveBlacklist") self.btnAddBlackList = go("btnAddBlacklist") self.tvBlacklist = go("tvBlacklist") self.tvAvailable = go("tvAvailable") self.lblGeneral = go("lblGeneral") self.btnSaveGeneral = go("btnSaveGeneral") self.chkHideMaintenance = go("chkHideMaintenance") self.chkAutostart = go("chkAutostart") # GUI translations self.window.set_title(_("Update Manager Preferences")) self.btnSaveMirrors.set_label(_("Save mirrors")) self.btnCheckMirrorsSpeed.set_label(_("Check mirrors speed")) self.btnRemoveBlackList.set_label(_("Remove")) self.btnAddBlackList.set_label(_("Blacklist")) self.lblMirrors.set_label(_("Repository mirrors")) self.lblGeneral.set_label(_("General")) go("lblHideMaintenance").set_label(_("Hide maintenance")) go("lblBlacklist").set_label(_("Blacklisted packages")) go("lblMirrorsText").set_label(_("Select the fastest repository")) go("lblBlacklistText").set_label(_("Blacklisted packages")) go("lblAvailableText").set_label(_("Available packages")) go("lblGlobalSettings").set_label(_("Global settings")) # Initiate the treeview handler and connect the custom toggle event with on_tvMirrors_toggle self.tvMirrorsHandler = TreeViewHandler(self.tvMirrors) self.tvMirrorsHandler.connect('checkbox-toggled', self.on_tvMirrors_toggle) self.tvBlacklistHandler = TreeViewHandler(self.tvBlacklist) self.tvAvailableHandler = TreeViewHandler(self.tvAvailable) # Initialize self.ec = ExecCmd(loggerObject=self.log) self.queue = Queue() self.excludeMirrors = ['security', 'community'] self.activeMirrors = self.umglobal.getMirrorData( excludeMirrors=self.excludeMirrors) self.deadMirrors = self.umglobal.getMirrorData(getDeadMirrors=True) self.mirrors = self.getMirrors() self.threads = {} self.blacklist = [] self.available = [] self.fillGeneralSettings() self.fillTreeViewMirrors() self.fillTreeViewBlackList() self.fillTreeViewAvailable() # Connect the signals and show the window self.builder.connect_signals(self) self.window.show() # =============================================== # Main window functions # =============================================== def on_btnSaveGeneral_clicked(self, widget): self.saveGeneralSettings() def on_btnCheckMirrorsSpeed_clicked(self, widget): self.checkMirrorsSpeed() def on_btnSaveMirrors_clicked(self, widget): self.saveMirrors() def on_btnCancel_clicked(self, widget): self.window.destroy() def on_btnRemoveBlacklist_clicked(self, widget): self.removeBlacklist() def on_btnAddBlacklist_clicked(self, widget): self.addBlacklist() # =============================================== # Blacklist functions # =============================================== def fillGeneralSettings(self): for tab in self.umglobal.settings["hide-tabs"]: if tab == "maintenance": self.chkHideMaintenance.set_active(True) self.chkAutostart.set_active(self.umglobal.settings["autostart"]) def fillTreeViewBlackList(self): self.blacklist = [] cmd = "env LANG=C dpkg --get-selections | grep hold$ | awk '{print $1}'" lst = self.ec.run(cmd, False) for pck in lst: self.blacklist.append([False, pck.strip()]) # Fill treeview columnTypesList = ['bool', 'str'] self.tvBlacklistHandler.fillTreeview(self.blacklist, columnTypesList, 0, 400, False) def fillTreeViewAvailable(self): self.available = [] cmd = "env LANG=C dpkg --get-selections | grep install$ | awk '{print $1}'" lst = self.ec.run(cmd, False) for pck in lst: self.available.append([False, pck.strip()]) # Fill treeview columnTypesList = ['bool', 'str'] self.tvAvailableHandler.fillTreeview(self.available, columnTypesList, 0, 400, False) def addBlacklist(self): packages = self.tvAvailableHandler.getToggledValues() for pck in packages: self.log.write("Blacklist package: %s" % pck, "UMPref.addBlacklist", "debug") cmd = "echo '%s hold' | dpkg --set-selections" % pck system(cmd) self.fillTreeViewBlackList() self.fillTreeViewAvailable() def removeBlacklist(self): packages = self.tvBlacklistHandler.getToggledValues() for pck in packages: self.log.write("Remove package from blacklist: %s" % pck, "UMPref.removeBlacklist", "debug") cmd = "echo '%s install' | dpkg --set-selections" % pck system(cmd) self.fillTreeViewBlackList() self.fillTreeViewAvailable() # =============================================== # Mirror functions # =============================================== def fillTreeViewMirrors(self): # Fill mirror list if len(self.mirrors) > 1: # Fill treeview columnTypesList = ['bool', 'str', 'str', 'str', 'str'] self.tvMirrorsHandler.fillTreeview(self.mirrors, columnTypesList, 0, 400, True) # TODO - We have no mirrors: hide the tab until we do #self.nbPref.get_nth_page(1).set_visible(False) else: self.nbPref.get_nth_page(1).set_visible(False) def saveMirrors(self): # Safe mirror settings replaceRepos = [] # Get user selected mirrors model = self.tvMirrors.get_model() itr = model.get_iter_first() while itr is not None: sel = model.get_value(itr, 0) if sel: repo = model.get_value(itr, 2) url = model.get_value(itr, 3) not_changed = '' # Get currently selected data for mirror in self.mirrors: if mirror[0] and mirror[2] == repo: if mirror[3] != url: # Currently selected mirror replaceRepos.append([mirror[3], url]) else: not_changed = url break if url not in replaceRepos and url not in not_changed: # Append the repositoriy to the sources file replaceRepos.append(['', url]) itr = model.iter_next(itr) if not replaceRepos: # Check for dead mirrors model = self.tvMirrors.get_model() itr = model.get_iter_first() while itr is not None: sel = model.get_value(itr, 0) if sel: repo = model.get_value(itr, 2) url = model.get_value(itr, 3) # Get currently selected data for mirror in self.deadMirrors: if mirror[1] == repo and mirror[2] != url: # Currently selected mirror replaceRepos.append([mirror[2], url]) break itr = model.iter_next(itr) if replaceRepos: self.btnSaveMirrors.set_sensitive(False) self.btnCheckMirrorsSpeed.set_sensitive(False) m = Mirror() ret = m.save(replaceRepos, self.excludeMirrors) if ret == '': self.ec.run(cmd="apt-get update", outputTreeView=self.tvMirrors) self.umglobal.getLocalInfo() self.mirrors = self.getMirrors() self.fillTreeViewMirrors() else: self.log.write(ret, "UMPref.saveMirrors", "exception") self.btnSaveMirrors.set_sensitive(True) self.btnCheckMirrorsSpeed.set_sensitive(True) else: msg = _("There are no repositories to save.") MessageDialog(self.lblMirrors.get_label(), msg) def getMirrors(self): mirrors = [[ _("Current"), _("Country"), _("Repository"), _("URL"), _("Speed") ]] for mirror in self.activeMirrors: if mirror: self.log.write("Mirror data: %s" % ' '.join(mirror), "UMPref.getMirrors", "debug") blnCurrent = self.isUrlInSources(mirror[2]) mirrors.append( [blnCurrent, mirror[0], mirror[1], mirror[2], '']) return mirrors def isUrlInSources(self, url): url = "://%s" % url blnRet = False for repo in self.umglobal.repos: if url in repo: blnRet = True for excl in self.excludeMirrors: if excl in repo: blnRet = False break break return blnRet def checkMirrorsSpeed(self): name = 'mirrorspeed' self.btnCheckMirrorsSpeed.set_sensitive(False) self.btnSaveMirrors.set_sensitive(False) t = MirrorGetSpeed(self.mirrors, self.queue, self.umglobal) self.threads[name] = t t.daemon = True t.start() self.queue.join() GObject.timeout_add(5, self.checkThread, name) def checkThread(self, name): if self.threads[name].is_alive(): lst = self.queue.get() if lst: self.writeSpeed(lst[0], lst[1]) self.queue.task_done() return True # Thread is done if not self.queue.empty(): lst = self.queue.get() if lst: self.writeSpeed(lst[0], lst[1]) self.queue.task_done() del self.threads[name] self.btnCheckMirrorsSpeed.set_sensitive(True) self.btnSaveMirrors.set_sensitive(True) return False def writeSpeed(self, url, speed): model = self.tvMirrors.get_model() itr = model.get_iter_first() while itr is not None: repo = model.get_value(itr, 3) if repo == url: self.log.write("Mirror speed for %s = %s" % (url, speed), "UMPref.writeSpeed", "debug") model.set_value(itr, 4, speed) path = model.get_path(itr) self.tvMirrors.scroll_to_cell(path) itr = model.iter_next(itr) self.tvMirrors.set_model(model) # Repaint GUI, or the update won't show while Gtk.events_pending(): Gtk.main_iteration() def on_tvMirrors_toggle(self, obj, path, colNr, toggleValue): path = int(path) model = self.tvMirrors.get_model() selectedIter = model.get_iter(path) selectedRepo = model.get_value(selectedIter, 2) rowCnt = 0 itr = model.get_iter_first() while itr is not None: if rowCnt != path: repo = model.get_value(itr, 2) if repo == selectedRepo: model[itr][0] = False itr = model.iter_next(itr) rowCnt += 1 # =============================================== # General functions # =============================================== def saveGeneralSettings(self): lst = [] for tab in self.umglobal.settings["hide-tabs"]: if tab != "maintenance": lst.append(tab) if self.chkHideMaintenance.get_active(): lst.append("maintenance") if lst: self.umglobal.saveSettings('misc', 'hide-tabs', ",".join(lst)) else: self.umglobal.saveSettings('misc', 'hide-tabs', "") # Automatically start updatemanager on boot autostart = self.chkAutostart.get_active() self.umglobal.settings["autostart"] = autostart if autostart: if exists(self.umglobal.autostartSourceFile) and \ exists(self.umglobal.autostartDir): copy(self.umglobal.autostartSourceFile, self.umglobal.autostartDir) elif exists(self.umglobal.autostartDestFile): remove(self.umglobal.autostartDestFile) msg = _("The new settings will take effect after UM restart.") MessageDialog(self.lblGeneral.get_label(), msg) # Close the gui def on_windowPref_destroy(self, widget): Gtk.main_quit()
class Constructor(object): def __init__(self): self.scriptDir = abspath(dirname(__file__)) self.shareDir = join(self.scriptDir, '../../../share/solydxk/constructor') self.userAppDir = join(get_user_home_dir(), ".constructor") self.distroFile = join(self.userAppDir, "distros.list") # Create the user's application directory if it doesn't exist if not isdir(self.userAppDir): user_name = getUserLoginName() makedirs(self.userAppDir) old_distro_file = join(self.scriptDir, "distros.list") if exists(old_distro_file): move(old_distro_file, self.distroFile) system("chown -R %s:%s %s" % (user_name, user_name, self.userAppDir)) # Load window and widgets self.builder = Gtk.Builder() self.builder.add_from_file(join(self.shareDir, 'constructor.glade')) # Main window objects go = self.builder.get_object self.window = go('constructorWindow') self.tvDistros = go('tvDistros') self.lblOutput = go('lblOutput') self.statusbar = go('statusbar') self.btnAdd = go('btnAdd') self.chkSelectAll = go('chkSelectAll') self.btnRemove = go('btnRemove') self.btnEdit = go('btnEdit') self.btnUpgrade = go('btnUpgrade') self.btnLocalize = go('btnLocalize') self.btnBuildIso = go('btnBuildIso') # Add iso window objects self.windowAddDistro = go('addDistroWindow') self.txtIso = go('txtIso') self.txtDir = go('txtDir') self.btnDir = go('btnDir') self.btnSave = go('btnSave') self.btnHelp = go('btnHelp') self.lblIso = go('lblIso') self.boxIso = go('boxIso') self.lblDir = go('lblDir') self.chkFromIso = go('chkFromIso') # Main window translations self.window.set_title(_("SolydXK Constructor")) self.chkSelectAll.set_label(_("Select all")) self.btnAdd.set_label("_{}".format(_("Add"))) self.btnRemove.set_label("_{}".format(_("Remove"))) self.btnEdit.set_label("_{}".format(_("Edit"))) self.btnUpgrade.set_label("_{}".format(_("Upgrade"))) self.btnLocalize.set_label("_{}".format(_("Localize"))) self.btnBuildIso.set_label("_{}".format(_("Build"))) self.btnHelp.set_label("_{}".format(_("Help"))) # Add iso window translations self.windowAddDistro.set_title(_("Add Distribution")) self.lblIso.set_text(_("ISO")) go('lblFromIso').set_label("Create from ISO") go('btnCancel').set_label("_{}".format(_("Cancel"))) # Init self.ec = ExecCmd() self.ec.run("modprobe loop", False) self.queue = Queue() self.mountDir = "/mnt/constructor" self.distroAdded = False self.iso = None self.dir = None self.isoName = None self.doneWav = join(self.shareDir, 'done.wav') self.htmlDir = join(self.shareDir, "html") self.help = join(self.get_language_dir(), "help.html") self.chkFromIso.set_active(True) self.toggleGuiElements(False) # Treeviews self.tvHandlerDistros = TreeViewHandler(self.tvDistros) self.fillTreeViewDistros() # Version information ver = _("Version") self.version = "%s: %s" % (ver, getPackageVersion('solydxk-constructor')) self.showOutput(self.version) # Connect the signals and show the window self.builder.connect_signals(self) self.window.show() # =============================================== # Main Window Functions # =============================================== def on_btnAdd_clicked(self, widget): self.windowAddDistro.show() def on_btnRemove_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: qd = QuestionDialog(self.btnRemove.get_label(), _("Are you sure you want to remove the selected distribution from the list?\n" \ "(This will not remove the directory and its data)"), self.window) answer = qd.show() if answer: self.saveDistroFile(distroPath=path, addDistro=False) self.fillTreeViewDistros() def on_btnEdit_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: de = EditDistro(path) services = [] if exists(join(path, 'root/etc/apache2/apache2.conf')): services.append("apache2") if exists(join(path, 'root/etc/mysql/debian.cnf')): services.append("mysql") if services: msg = "If you need to update packages that depend on these services,\n" \ "you will need to manually start them:\n" for service in services: msg += "\nservice %s start" % service msg += "\n\nWhen done:\n" for service in services: msg += "\nservice %s stop" % service self.showInfo(_("Services detected"), msg, self.window) repaintGui() de.openTerminal() def on_btnUpgrade_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) upgraded = False for path in selected: upgraded = True rootPath = "%s/root" % path force = get_apt_force(rootPath) de = EditDistro(path) de.openTerminal("apt-get update") if exists(join(rootPath, 'etc/apache2/apache2.conf')): de.openTerminal("service apache2 start") if exists(join(rootPath, 'etc/mysql/debian.cnf')): de.openTerminal("service mysql start") de.openTerminal("apt-get -y %s -o Dpkg::Options::=\"--force-confnew\" dist-upgrade" % force) if exists(join(rootPath, 'etc/apache2/apache2.conf')): de.openTerminal("service apache2 stop") if exists(join(rootPath, 'etc/mysql/debian.cnf')): de.openTerminal("service mysql stop") # Cleanup old kernel and headers script = "rmoldkernel.sh" scriptSource = join(self.scriptDir, "files/{}".format(script)) scriptTarget = join(rootPath, script) if exists(scriptSource): copy(scriptSource, scriptTarget) self.ec.run("chmod a+x '%s'" % scriptTarget) de.openTerminal("/bin/bash %s" % script) silent_remove(scriptTarget) # Download offline packages print(">> Start downloading offline packages") self.download_offline_packages(path) if upgraded and exists("/usr/bin/aplay") and exists(self.doneWav): self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False) def on_btnLocalize_clicked(self, widget): # Set locale selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: rootPath = "%s/root" % path de = EditDistro(path) script = "setlocale.sh" scriptSource = join(self.scriptDir, "files/{}".format(script)) scriptTarget = join(rootPath, script) if exists(scriptSource): copy(scriptSource, scriptTarget) self.ec.run("chmod a+x '%s'" % scriptTarget) de.openTerminal("/bin/bash %s" % script) silent_remove(scriptTarget) # Copy Grub locale files to ISO boot directory and configure grub.cfg grub_path = "%s/boot/boot/grub/" % path grubcfg_path = "%s/grub.cfg" % grub_path locale_path = "%s/root/boot/grub/locale" % path default_path = "%s/root/etc/default" % path locale = self.ec.run("grep -oP '(?<=LANG=).*?(?=\.)' %s/locale" % default_path)[0] if exists(locale_path) and \ exists(grubcfg_path) and \ locale: self.ec.run("cp -rf %s %s" % (locale_path, grub_path)) self.ec.run("sed -i 's/set lang=.*/set lang=%s/' %s" % (locale, grubcfg_path)) def download_offline_packages(self, path): rootPath = "%s/root" % path arch = getGuestEfiArchitecture(rootPath) de = EditDistro(path) script = "offline.sh" scriptSource = join(self.scriptDir, "files/{}".format(script)) scriptTarget = join(rootPath, script) offlineSource = join(rootPath, "offline") offlineTarget = join(rootPath, "../boot/offline") if exists(scriptSource): try: copy(scriptSource, scriptTarget) self.ec.run("chmod a+x '%s'" % scriptTarget) # Run the script de.openTerminal("/bin/bash {} {}".format(script, arch)) # Remove script silent_remove(scriptTarget) # Move offline directory to boot directory if exists(offlineSource): print(("%s exists" % offlineSource)) if exists(offlineTarget): print((">> Remove %s" % offlineTarget)) silent_remove(offlineTarget) print((">> Move %s to %s" % (offlineSource, offlineTarget))) move(offlineSource, offlineTarget) else: print((">> Cannot find: %s" % offlineSource)) except Exception as detail: self.showError("Error: getting offline packages", detail, self.window) else: print((">> Cannot find: %s" % scriptSource)) def on_btnBuildIso_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) msg = "" for path in selected: self.toggleGuiElements(True) self.showOutput("Start building ISO in: %s" % path) repaintGui() # Start building the ISO in a thread t = BuildIso(path, self.queue) t.start() self.queue.join() # Thread is done # Get the data from the queue ret = self.queue.get() self.queue.task_done() if ret is not None: self.showOutput(ret) if "error" in ret.lower(): self.showError("Error", ret, self.window) else: msg += "%s\n" % ret if msg != "": if exists("/usr/bin/aplay") and exists(self.doneWav): self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False) self.showInfo("", msg, self.window) self.toggleGuiElements(False) def on_chkSelectAll_toggled(self, widget): self.tvHandlerDistros.treeviewToggleAll(toggleColNrList=[0], toggleValue=widget.get_active()) def on_tvDistros_row_activated(self, widget, path, column): self.tvHandlerDistros.treeviewToggleRows(toggleColNrList=[0]) def on_btnHelp_clicked(self, widget): system("open-as-user %s" % self.help) def on_btnOpenDir_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: system("open-as-user %s" % path) def fillTreeViewDistros(self, selectDistros=[]): contentList = [[_("Select"), _("Distribution"), _("Working directory")]] distros = self.getDistros() for distro in distros: select = False for selectDistro in selectDistros: if distro[0] == selectDistro: select = True contentList.append([select, distro[0], distro[1]]) self.tvHandlerDistros.fillTreeview(contentList=contentList, columnTypesList=['bool', 'str', 'str'], firstItemIsColName=True) def getDistros(self): distros = [] if exists(self.distroFile): with open(self.distroFile, 'r') as f: lines = f.readlines() for line in lines: line = line.strip().rstrip('/') print(line) if exists(line): dg = DistroGeneral(line) isoName = dg.description distros.append([isoName, line]) # Sort on iso name if distros: distros = sorted(distros, key=operator.itemgetter(0)) return distros # =============================================== # Add ISO Window Functions # =============================================== def on_btnIso_clicked(self, widget): fleFilter = Gtk.FileFilter() fleFilter.set_name("ISO") fleFilter.add_mime_type("application/x-cd-image") fleFilter.add_pattern("*.iso") startDir = None if exists(dirname(self.txtIso.get_text())): startDir = dirname(self.txtIso.get_text()) filePath = SelectFileDialog(title=_('Select SolydXK ISO'), start_directory=startDir, parent=self.window, gtkFileFilter=fleFilter).show() if filePath is not None: self.txtIso.set_text(filePath) def on_btnDir_clicked(self, widget): startDir = None if exists(self.txtDir.get_text()): startDir = self.txtDir.get_text() dirText = SelectDirectoryDialog(title=_('Select directory'), start_directory=startDir, parent=self.window).show() if dirText is not None: self.txtDir.set_text(dirText) def on_btnSave_clicked(self, widget): self.iso = "" if self.chkFromIso.get_active(): self.iso = self.txtIso.get_text() self.dir = self.txtDir.get_text() title = _("Save existing working directory") if self.iso != "": title = _("Unpack ISO and save") if not exists(self.dir): makedirs(self.dir) if not exists(self.dir): self.showError(title, _("Could not create directory %(dir)s: exiting" % {"dir": self.dir}), self.window) else: self.windowAddDistro.hide() if self.iso != "": if not exists(self.iso): self.showInfo(self.btnSave.get_label(), _("The path to the ISO file does not exist:\n{}".format(self.iso)), self.window) return if listdir(self.dir): qd = QuestionDialog(self.btnSave.get_label(), _("The destination directory is not empty.\n" "Are you sure you want to overwrite all data in {}?".format(self.dir)), self.window) answer = qd.show() if not answer: return self.showOutput("Start unpacking the ISO...") self.toggleGuiElements(True) t = IsoUnpack(self.mountDir, self.iso, self.dir, self.queue) t.start() self.queue.join() GObject.timeout_add(1000, self.checkThread, True) else: self.saveDistroFile(self.dir, True) self.fillTreeViewDistros() self.showOutput(_("Existing working directory added")) #self.toggleGuiElements(False) def on_btnCancel_clicked(self, widget): self.windowAddDistro.hide() def on_addDistroWindow_delete_event(self, widget, data=None): self.windowAddDistro.hide() return True def on_txtIso_changed(self, widget): path = self.txtIso.get_text() if exists(path): self.txtDir.set_sensitive(True) self.btnDir.set_sensitive(True) if exists(self.txtDir.get_text()): self.btnSave.set_sensitive(True) else: self.txtDir.set_sensitive(False) self.btnDir.set_sensitive(False) self.btnSave.set_sensitive(False) def on_txtDir_changed(self, widget): blnFromIso = self.chkFromIso.get_active() isoPath = self.txtIso.get_text() dirText = self.txtDir.get_text() self.btnSave.set_sensitive(False) if exists(dirText): if blnFromIso: if exists(isoPath): self.btnSave.set_sensitive(True) else: self.btnSave.set_sensitive(True) def on_chkFromIso_toggled(self, widget): if widget.get_active(): self.lblIso.set_visible(True) self.boxIso.set_visible(True) self.txtDir.set_sensitive(False) self.btnDir.set_sensitive(False) self.btnSave.set_sensitive(False) self.lblDir.set_text(_("Unpack ISO to directory")) self.btnSave.set_label(_("Unpack & Save")) else: self.txtIso.set_text("") self.lblIso.set_visible(False) self.boxIso.set_visible(False) self.txtDir.set_sensitive(True) self.btnDir.set_sensitive(True) self.btnSave.set_sensitive(True) self.lblDir.set_text(_("Work directory")) self.btnSave.set_label(_("Save")) # =============================================== # General functions # =============================================== def showInfo(self, title, message, parent=None): MessageDialogSafe(title, message, Gtk.MessageType.INFO, parent).show() def showError(self, title, message, parent=None): MessageDialogSafe(title, message, Gtk.MessageType.ERROR, parent).show() def showOutput(self, message): print(message) pushMessage(self.statusbar, message) def checkThread(self, addDistro=None): #print 'Thread count = ' + str(threading.active_count()) # As long there's a thread active, keep spinning if threading.active_count() > 1: return True # Thread is done # Get the data from the queuez ret = self.queue.get() self.queue.task_done() # Thread is done if addDistro is not None: self.saveDistroFile(self.dir, addDistro) self.fillTreeViewDistros(self.isoName) self.toggleGuiElements(False) if exists("/usr/bin/aplay") and exists(self.doneWav): self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False) if ret is not None: self.showOutput(ret) if "error" in ret.lower(): self.showError("Error", ret, self.window) else: self.showInfo("", ret, self.window) return False def toggleGuiElements(self, startThread): if startThread: self.chkSelectAll.set_sensitive(False) self.tvDistros.set_sensitive(False) self.btnAdd.set_sensitive(False) self.btnBuildIso.set_sensitive(False) self.btnEdit.set_sensitive(False) self.btnRemove.set_sensitive(False) self.btnUpgrade.set_sensitive(False) self.btnLocalize.set_sensitive(False) self.btnDir.set_sensitive(False) self.btnHelp.set_sensitive(False) else: self.chkSelectAll.set_sensitive(True) self.tvDistros.set_sensitive(True) self.btnAdd.set_sensitive(True) self.btnBuildIso.set_sensitive(True) self.btnEdit.set_sensitive(True) self.btnRemove.set_sensitive(True) self.btnUpgrade.set_sensitive(True) self.btnLocalize.set_sensitive(True) self.btnDir.set_sensitive(True) self.btnHelp.set_sensitive(True) def saveDistroFile(self, distroPath, addDistro=True): newCont = [] dg = DistroGeneral(distroPath) self.isoName = dg.description cfg = [] if exists(self.distroFile): with open(self.distroFile, 'r') as f: cfg = f.readlines() for line in cfg: line = line.strip() if distroPath != line and exists(line): newCont.append(line) if addDistro: newCont.append(distroPath) with open(self.distroFile, 'w') as f: f.write('\n'.join(newCont)) self.iso = "" self.dir = "" # Close the gui def on_constructorWindow_destroy(self, widget): # Close the app Gtk.main_quit() # =============================================== # Language specific functions # =============================================== def get_language_dir(self): # First test if full locale directory exists, e.g. html/pt_BR, # otherwise perhaps at least the language is there, e.g. html/pt # and if that doesn't work, try html/pt_PT lang = self.get_current_language() path = join(self.htmlDir, lang) if not isdir(path): base_lang = lang.split('_')[0].lower() path = join(self.htmlDir, base_lang) if not isdir(path): path = join(self.htmlDir, "{}_{}".format(base_lang, base_lang.upper())) if not isdir(path): path = join(self.htmlDir, 'en') return path def get_current_language(self): lang = environ.get('LANG', 'US').split('.')[0] if lang == '': lang = 'en' return lang
class UpdateManagerPref(object): def __init__(self): # Check if script is running self.scriptName = basename(__file__) self.umglobal = UmGlobal() # Handle arguments parser = argparse.ArgumentParser(description='SolydXK Update Manager Preferences') parser.add_argument('-r','--reload', action="store_true", help='') args, extra = parser.parse_known_args() print(("args = {}".format(args))) if args.reload: pids = self.umglobal.getProcessPids("updatemanagerpref.py") if len(pids) > 1: print(("updatemanagerpref.py already running - kill pid {}".format(pids[0]))) os.system("kill {}".format(pids[0])) # Initiate logging self.logFile = join('/var/log', self.umglobal.settings['log']) self.log = Logger(self.logFile) # Load window and widgets self.builder = Gtk.Builder() self.builder.add_from_file(join(self.umglobal.shareDir, 'updatemanagerpref.glade')) # Preferences window objects go = self.builder.get_object self.window = go("windowPref") self.nbPref = go('nbPref') self.btnSaveMirrors = go('btnSaveMirrors') self.btnCheckMirrorsSpeed = go("btnCheckMirrorsSpeed") self.lblMirrors = go('lblMirrors') self.tvMirrors = go("tvMirrors") self.btnRemoveBlackList = go("btnRemoveBlacklist") self.btnAddBlackList = go("btnAddBlacklist") self.tvBlacklist = go("tvBlacklist") self.tvAvailable = go("tvAvailable") self.lblGeneral = go("lblGeneral") self.btnSaveGeneral = go("btnSaveGeneral") self.chkHideMaintenance = go("chkHideMaintenance") self.chkAutostart = go("chkAutostart") # GUI translations self.window.set_title(_("Update Manager Preferences")) self.btnSaveMirrors.set_label(_("Save mirrors")) self.btnCheckMirrorsSpeed.set_label(_("Check mirrors speed")) self.btnRemoveBlackList.set_label(_("Remove")) self.btnAddBlackList.set_label(_("Blacklist")) self.lblMirrors.set_label(_("Repository mirrors")) self.lblGeneral.set_label(_("General")) go("lblHideMaintenance").set_label(_("Hide maintenance")) go("lblBlacklist").set_label(_("Blacklisted packages")) go("lblMirrorsText").set_label(_("Select the fastest repository")) go("lblBlacklistText").set_label(_("Blacklisted packages")) go("lblAvailableText").set_label(_("Available packages")) go("lblGlobalSettings").set_label(_("Global settings")) # Initiate the treeview handler and connect the custom toggle event with on_tvMirrors_toggle self.tvMirrorsHandler = TreeViewHandler(self.tvMirrors) self.tvMirrorsHandler.connect('checkbox-toggled', self.on_tvMirrors_toggle) self.tvBlacklistHandler = TreeViewHandler(self.tvBlacklist) self.tvAvailableHandler = TreeViewHandler(self.tvAvailable) # Initialize self.ec = ExecCmd(loggerObject=self.log) self.queue = Queue() self.excludeMirrors = ['security', 'community'] self.activeMirrors = self.umglobal.getMirrorData(excludeMirrors=self.excludeMirrors) self.deadMirrors = self.umglobal.getMirrorData(getDeadMirrors=True) self.mirrors = self.getMirrors() self.threads = {} self.blacklist = [] self.available = [] self.fillGeneralSettings() self.fillTreeViewMirrors() self.fillTreeViewBlackList() self.fillTreeViewAvailable() # Connect the signals and show the window self.builder.connect_signals(self) self.window.show() # =============================================== # Main window functions # =============================================== def on_btnSaveGeneral_clicked(self, widget): self.saveGeneralSettings() def on_btnCheckMirrorsSpeed_clicked(self, widget): self.checkMirrorsSpeed() def on_btnSaveMirrors_clicked(self, widget): self.saveMirrors() def on_btnCancel_clicked(self, widget): self.window.destroy() def on_btnRemoveBlacklist_clicked(self, widget): self.removeBlacklist() def on_btnAddBlacklist_clicked(self, widget): self.addBlacklist() # =============================================== # Blacklist functions # =============================================== def fillGeneralSettings(self): for tab in self.umglobal.settings["hide-tabs"]: if tab == "maintenance": self.chkHideMaintenance.set_active(True) self.chkAutostart.set_active(self.umglobal.settings["autostart"]) def fillTreeViewBlackList(self): self.blacklist = [] cmd = "env LANG=C dpkg --get-selections | grep hold$ | awk '{print $1}'" lst = self.ec.run(cmd, False) for pck in lst: self.blacklist.append([False, pck.strip()]) # Fill treeview columnTypesList = ['bool', 'str'] self.tvBlacklistHandler.fillTreeview(self.blacklist, columnTypesList, 0, 400, False) def fillTreeViewAvailable(self): self.available = [] cmd = "env LANG=C dpkg --get-selections | grep install$ | awk '{print $1}'" lst = self.ec.run(cmd, False) for pck in lst: self.available.append([False, pck.strip()]) # Fill treeview columnTypesList = ['bool', 'str'] self.tvAvailableHandler.fillTreeview(self.available, columnTypesList, 0, 400, False) def addBlacklist(self): packages = self.tvAvailableHandler.getToggledValues() for pck in packages: self.log.write("Blacklist package: %s" % pck, "UMPref.addBlacklist", "debug") cmd = "echo '%s hold' | dpkg --set-selections" % pck system(cmd) self.fillTreeViewBlackList() self.fillTreeViewAvailable() def removeBlacklist(self): packages = self.tvBlacklistHandler.getToggledValues() for pck in packages: self.log.write("Remove package from blacklist: %s" % pck, "UMPref.removeBlacklist", "debug") cmd = "echo '%s install' | dpkg --set-selections" % pck system(cmd) self.fillTreeViewBlackList() self.fillTreeViewAvailable() # =============================================== # Mirror functions # =============================================== def fillTreeViewMirrors(self): # Fill mirror list if len(self.mirrors) > 1: # Fill treeview columnTypesList = ['bool', 'str', 'str', 'str', 'str'] self.tvMirrorsHandler.fillTreeview(self.mirrors, columnTypesList, 0, 400, True) # TODO - We have no mirrors: hide the tab until we do #self.nbPref.get_nth_page(1).set_visible(False) else: self.nbPref.get_nth_page(1).set_visible(False) def saveMirrors(self): # Safe mirror settings replaceRepos = [] # Get user selected mirrors model = self.tvMirrors.get_model() itr = model.get_iter_first() while itr is not None: sel = model.get_value(itr, 0) if sel: repo = model.get_value(itr, 2) url = model.get_value(itr, 3) not_changed = '' # Get currently selected data for mirror in self.mirrors: if mirror[0] and mirror[2] == repo: if mirror[3] != url: # Currently selected mirror replaceRepos.append([mirror[3], url]) else: not_changed = url break if url not in replaceRepos and url not in not_changed: # Append the repositoriy to the sources file replaceRepos.append(['', url]) itr = model.iter_next(itr) if not replaceRepos: # Check for dead mirrors model = self.tvMirrors.get_model() itr = model.get_iter_first() while itr is not None: sel = model.get_value(itr, 0) if sel: repo = model.get_value(itr, 2) url = model.get_value(itr, 3) # Get currently selected data for mirror in self.deadMirrors: if mirror[1] == repo and mirror[2] != url: # Currently selected mirror replaceRepos.append([mirror[2], url]) break itr = model.iter_next(itr) if replaceRepos: self.btnSaveMirrors.set_sensitive(False) self.btnCheckMirrorsSpeed.set_sensitive(False) m = Mirror() ret = m.save(replaceRepos, self.excludeMirrors) if ret == '': self.ec.run(cmd="apt-get update", outputTreeView=self.tvMirrors) self.umglobal.getLocalInfo() self.mirrors = self.getMirrors() self.fillTreeViewMirrors() else: self.log.write(ret, "UMPref.saveMirrors", "exception") self.btnSaveMirrors.set_sensitive(True) self.btnCheckMirrorsSpeed.set_sensitive(True) else: msg = _("There are no repositories to save.") MessageDialog(self.lblMirrors.get_label(), msg) def getMirrors(self): mirrors = [[_("Current"), _("Country"), _("Repository"), _("URL"), _("Speed")]] for mirror in self.activeMirrors: if mirror: self.log.write("Mirror data: %s" % ' '.join(mirror), "UMPref.getMirrors", "debug") blnCurrent = self.isUrlInSources(mirror[2]) mirrors.append([blnCurrent, mirror[0], mirror[1], mirror[2], '']) return mirrors def isUrlInSources(self, url): url = "://%s" % url blnRet = False for repo in self.umglobal.repos: if url in repo: blnRet = True for excl in self.excludeMirrors: if excl in repo: blnRet = False break break return blnRet def checkMirrorsSpeed(self): name = 'mirrorspeed' self.btnCheckMirrorsSpeed.set_sensitive(False) self.btnSaveMirrors.set_sensitive(False) t = MirrorGetSpeed(self.mirrors, self.queue, self.umglobal) self.threads[name] = t t.daemon = True t.start() self.queue.join() GObject.timeout_add(5, self.checkThread, name) def checkThread(self, name): if self.threads[name].is_alive(): lst = self.queue.get() if lst: self.writeSpeed(lst[0], lst[1]) self.queue.task_done() return True # Thread is done if not self.queue.empty(): lst = self.queue.get() if lst: self.writeSpeed(lst[0], lst[1]) self.queue.task_done() del self.threads[name] self.btnCheckMirrorsSpeed.set_sensitive(True) self.btnSaveMirrors.set_sensitive(True) return False def writeSpeed(self, url, speed): model = self.tvMirrors.get_model() itr = model.get_iter_first() while itr is not None: repo = model.get_value(itr, 3) if repo == url: self.log.write("Mirror speed for %s = %s" % (url, speed), "UMPref.writeSpeed", "debug") model.set_value(itr, 4, speed) path = model.get_path(itr) self.tvMirrors.scroll_to_cell(path) itr = model.iter_next(itr) self.tvMirrors.set_model(model) # Repaint GUI, or the update won't show while Gtk.events_pending(): Gtk.main_iteration() def on_tvMirrors_toggle(self, obj, path, colNr, toggleValue): path = int(path) model = self.tvMirrors.get_model() selectedIter = model.get_iter(path) selectedRepo = model.get_value(selectedIter, 2) rowCnt = 0 itr = model.get_iter_first() while itr is not None: if rowCnt != path: repo = model.get_value(itr, 2) if repo == selectedRepo: model[itr][0] = False itr = model.iter_next(itr) rowCnt += 1 # =============================================== # General functions # =============================================== def saveGeneralSettings(self): lst = [] for tab in self.umglobal.settings["hide-tabs"]: if tab != "maintenance": lst.append(tab) if self.chkHideMaintenance.get_active(): lst.append("maintenance") if lst: self.umglobal.saveSettings('misc', 'hide-tabs', ",".join(lst)) else: self.umglobal.saveSettings('misc', 'hide-tabs', "") # Automatically start updatemanager on boot autostart = self.chkAutostart.get_active() self.umglobal.settings["autostart"] = autostart if autostart: if exists(self.umglobal.autostartSourceFile) and \ exists(self.umglobal.autostartDir): copy(self.umglobal.autostartSourceFile, self.umglobal.autostartDir) elif exists(self.umglobal.autostartDestFile): remove(self.umglobal.autostartDestFile) msg = _("The new settings will take effect after UM restart.") MessageDialog(self.lblGeneral.get_label(), msg) # Close the gui def on_windowPref_destroy(self, widget): Gtk.main_quit()
class UserManager(object): def __init__(self): self.scriptDir = abspath(dirname(__file__)) # Load window and widgets self.builder = Gtk.Builder() self.builder.add_from_file(join(self.scriptDir, '../../share/usermanager/usermanager.glade')) # Main window objects go = self.builder.get_object self.window = go('usermanagerWindow') self.tvUsersMain = go('tvUsersMain') self.tvUserGroupsMain = go('tvUserGroupsMain') self.chkShowSystemUsers = go('chkShowSystemUsers') self.btnUserEdit = go('btnUserEdit') self.btnUserRemove = go('btnUserRemove') # User window objects self.windowUser = go('usermanagerUserWindow') self.nbUser = go('nbUser') self.txtLoginName = go('txtLoginName') self.txtRealName = go('txtRealName') self.txtUserID = go('txtUserID') self.txtHomeDirectory = go('txtHomeDirectory') self.cmbShells = go('cmbShells') self.cmbPrimaryGroup = go('cmbPrimaryGroup') self.cmbPrimaryGroupEntry = go('cmbPrimaryGroupEntry') self.tvUserGroups = go('tvUserGroups') self.ebFace = go('ebFace') self.imgFace = go('imgFace') self.lblUserID = go('lblUserID') self.txtPassword = go('txtPassword') self.txtLastChanged = go('txtLastChanged') self.cmbValidUntilMonth = go('cmbValidUntilMonth') self.spbValidUntilDay = go('spbValidUntilDay') self.spbValidUntilYear = go('spbValidUntilYear') self.radEnabled = go('radEnabled') self.radDisabled = go('radDisabled') self.radValidUntilAlways = go('radValidUntilAlways') self.radValidUntilDate = go('radValidUntilDate') self.spbRequirePasswordAfter = go('spbRequirePasswordAfter') self.spbWarnBeforePasswordExpires = go('spbWarnBeforePasswordExpires') self.spbDisableAccountWhenPasswordExpires = go('spbDisableAccountWhenPasswordExpires') self.spbEnforceMinimumPasswordAge = go('spbEnforceMinimumPasswordAge') self.chkRequirePasswordAfter = go('chkRequirePasswordAfter') self.chkEnforceMinimumPasswordAge = go('chkEnforceMinimumPasswordAge') self.tvGroupsMain = go('tvGroupsMain') # Group window objects self.windowGroup = go('usermanagerGroupWindow') self.txtGroupName = go('txtGroupName') self.txtGroupID = go('txtGroupID') self.tvAccounts = go('tvAccounts') self.tvSelectedAccounts = go('tvSelectedAccounts') self.tvGroupAccounts = go('tvGroupAccounts') # Main window translations self.window.set_title(_("User manager")) self.chkShowSystemUsers.set_label(_("Show system users")) self.btnUserEdit.set_label(_("Edit")) self.btnUserRemove.set_label(_("Remove")) go('btnUserAdd').set_label(_("Add")) go('lblUsersTab').set_text(_("Users")) go('lblGroupsTab').set_text(_("Groups")) go('lblUsersMain').set_text(go('lblUsersTab').get_text()) go('lblUserGroupsMain').set_text(_("User groups")) go('lblGroupsMain').set_text(go('lblGroupsTab').get_text()) go('lblGroupAccountsMain').set_text(_("Group accounts")) # User window translations self.windowUser.set_title(_("User settings")) go('lblUserDetails').set_text(_("Details")) go('lblStatus').set_text(_("Status")) go('lblLoginName').set_text(_("Login name")) go('lblRealName').set_text(_("Real name")) go('lblUserID').set_text(_("User ID")) go('lblPrimaryGroup').set_text(_("Primary group")) go('lblHomeDirectory').set_text(_("Home directory")) go('lblShell').set_text(_("Shell")) self.radEnabled.set_label(_("Enabled")) self.radDisabled.set_label(_("Disabled")) #go('lblPrivilegesAndGroups').set_text(_("Privileges and groups")) go('lblPrivilegesAndGroups').set_text(go('lblGroupsTab').get_text()) go('lblPrivileges').set_text(_("Privileges")) go('lblUserGroups').set_text(go('lblGroupsTab').get_text()) go('lblPasswordSecurity').set_text(_("Password")) go('lblPassword').set_text(go('lblPasswordSecurity').get_text()) go('lblLastChanged').set_text(_("Last changed")) go('lblValidUntil').set_text(_("Valid until")) self.radValidUntilAlways.set_label(_("Always")) go('lblPasswordAging').set_text(_("Password aging")) self.chkRequirePasswordAfter.set_label(_("Require password after")) go('lblWarnBeforePasswordExpires').set_text(_("Warn before password expires after")) go('lblDisableAccountWhenPasswordExpires').set_text(_("Disable account when password expires after")) self.chkEnforceMinimumPasswordAge.set_label(_("Enforce minimum password age")) go('lblDays1').set_text(_("days")) go('lblDays2').set_text(go('lblDays1').get_text()) go('lblDays3').set_text(go('lblDays1').get_text()) go('lblDays4').set_text(go('lblDays1').get_text()) go('btnSaveUser').set_label(_("Save")) go('btnCancelUser').set_label(_("Cancel")) # Group window translations self.windowGroup.set_title(_("Group settings")) go('lblGroupName').set_text(_("Group name")) go('lblGroupID').set_text(_("Group ID")) go('lblAccounts').set_text(_("Available accounts")) go('lblSelectedAccounts').set_text(_("Selected accounts")) go('btnOkGroup').set_label(go('btnSaveUser').get_label()) go('btnCancelGroup').set_label(go('btnCancelUser').get_label()) go('btnAddAccount').set_label(go('btnUserAdd').get_label()) go('btnRemoveAccount').set_label(self.btnUserRemove.get_label()) # Init self.ec = ExecCmd() self.usr = User() self.usersInfo = {} self.user = {} self.users = [] self.accounts = [] self.shells = self.usr.getShells() self.groups = None self.loggedinUser = self.usr.getLoggedinUser() self.loggedinUserPrimaryGroup = self.usr.getUserPrimaryGroupName(self.loggedinUser) self.selectedUser = None self.selectedGroup = None self.selectedGroupAccounts = None self.userFace = None self.radEnabled.set_active(True) self.radValidUntilAlways.set_active(True) self.setValidUntil(False) self.setRequirePasswordAfter(False) self.setEnforceMinimumPasswordAge(False) self.txtUserID.set_editable(False) self.txtUserID.set_can_focus(False) self.txtLastChanged.set_editable(False) self.txtLastChanged.set_can_focus(False) self.txtGroupID.set_editable(False) self.txtGroupID.set_can_focus(False) self.passwordChanged = False self.homeDirChanged = False self.tempFace = "/tmp/face.png" self.filterText(self.txtLoginName) self.filterText(self.txtGroupName) self.filterText(self.cmbPrimaryGroupEntry) # Treeviews self.tvHandlerUsersMain = TreeViewHandler(self.tvUsersMain) self.tvHandlerUserGroupsMain = TreeViewHandler(self.tvUserGroupsMain) self.tvHandlerUserGroups = TreeViewHandler(self.tvUserGroups) self.tvHandlerAccounts = TreeViewHandler(self.tvAccounts) self.tvHandlerSelectedAccounts = TreeViewHandler(self.tvSelectedAccounts) self.tvHandlerGroupsMain = TreeViewHandler(self.tvGroupsMain) self.tvHandlerGroupAccounts = TreeViewHandler(self.tvGroupAccounts) # Comboboxes self.cmbHandlerShells = ComboBoxHandler(self.cmbShells) self.cmbHandlerPrimaryGroup = ComboBoxHandler(self.cmbPrimaryGroup) self.cmbHandlerValidUntilMonth = ComboBoxHandler(self.cmbValidUntilMonth) # Get data self.refreshData() self.cmbHandlerShells.fillComboBox(self.shells) self.cmbHandlerValidUntilMonth.fillComboBox(functions.getMonthsList()) year = datetime.now().year adj = Gtk.Adjustment(value=year, lower=year, upper=year + 10, step_incr=1, page_incr=0, page_size=0) self.spbValidUntilYear.set_adjustment(adj) # Connect the signals and show the window self.builder.connect_signals(self) self.window.show() # =============================================== # User functions # =============================================== def on_tvUsersMain_cursor_changed(self, widget, event=None): self.showUserData() def on_tvUsersMain_row_activated(self, widget, path, column): self.on_btnUserEdit_clicked(None) def on_btnUserAdd_clicked(self, widget): self.imgFace.set_from_pixbuf(self.usr.getUserFacePixbuf()) self.tvHandlerUserGroups.fillTreeview(contentList=self.getUserGroupsComplete(), columnTypesList=['bool', 'str']) self.cmbHandlerShells.selectValue('/bin/bash') self.cmbHandlerPrimaryGroup.fillComboBox(self.groups) self.txtLoginName.set_editable(True) self.txtLoginName.set_can_focus(True) self.txtLoginName.set_text("new_user") self.txtUserID.set_text(str(self.usr.getNewUserID())) self.updateNewLogin() self.txtPassword.set_text("") self.radValidUntilAlways.set_active(True) self.windowUser.show() def on_btnUserEdit_clicked(self, widget): if self.selectedUser is not None: if not self.userFace is None: self.imgFace.set_from_pixbuf(self.userFace) self.imgFace.show() self.nbUser.get_nth_page(2).show() else: self.imgFace.hide() # Hide passwords tab self.nbUser.get_nth_page(2).hide() self.txtLoginName.set_editable(False) self.txtLoginName.set_can_focus(False) self.txtLoginName.set_text(self.user['user'].pw_name) if ",,," in self.user['user'].pw_gecos: self.txtRealName.set_text(self.user['user'].pw_name) else: self.txtRealName.set_text(self.user['user'].pw_gecos) self.txtUserID.set_text(str(self.user['user'].pw_uid)) self.txtHomeDirectory.set_text(self.user['user'].pw_dir) self.tvHandlerUserGroups.fillTreeview(contentList=self.getUserGroupsComplete(self.user['groups']), columnTypesList=['bool', 'str']) self.cmbHandlerShells.selectValue(self.user['user'].pw_shell) self.cmbHandlerPrimaryGroup.fillComboBox(self.groups, self.user['prgrp']) self.txtPassword.set_text("") self.txtLastChanged.set_text(self.usr.intToDate(nr_days=self.user['pwd'].sp_lstchg, format_string='%d %B %Y', start_date=datetime(1970, 1, 1))) daysMin = self.user['pwd'].sp_min print((">>> daysMin = %d" % daysMin)) if daysMin > 0: self.radValidUntilDate.set_active(True) dt = self.usr.intToDate(nr_days=daysMin) self.spbValidUntilDay.set_value(dt.day) self.cmbValidUntilMonth.set_active(dt.month - 1) self.spbValidUntilYear.set_value(dt.year) else: self.radValidUntilAlways.set_active(True) daysMax = self.user['pwd'].sp_max daysWarn = self.user['pwd'].sp_warn daysInact = self.user['pwd'].sp_inact print((">>> daysMax = %d" % daysMax)) print((">>> daysWarn = %d" % daysWarn)) print((">>> daysInact = %d" % daysInact)) if daysMax < 1000 and daysWarn >= 0 and daysInact >= 0: self.chkRequirePasswordAfter.set_active(True) self.spbRequirePasswordAfter.set_value(daysMax) self.spbWarnBeforePasswordExpires.set_value(daysWarn) self.spbDisableAccountWhenPasswordExpires.set_value(daysInact) else: self.chkRequirePasswordAfter.set_active(False) daysExpire = self.user['pwd'].sp_expire print((">>> daysExpire = %d" % daysExpire)) if daysExpire >= 0: self.chkEnforceMinimumPasswordAge.set_active(True) self.spbEnforceMinimumPasswordAge.set_value(daysExpire) else: self.chkEnforceMinimumPasswordAge.set_active(False) self.windowUser.show() def on_btnUserRemove_clicked(self, widget): # Remove user if self.selectedUser is not None: title = _("Remove user") if self.selectedUser != self.loggedinUser: qd = QuestionDialog(title, _("Are you sure you want to remove the following user:\n\n'%(user)s'") % { "user": self.selectedUser }, self.window) answer = qd.show() if answer: # TODO ret = self.usr.deleteUser(self.selectedUser) if ret == "": self.showInfo(title, _("User successfully removed: %(user)s") % {"user": self.selectedUser}, self.window) self.refreshData() else: retMsg = "\n\n%s" % ret self.showError(title, _("Could not remove user: %(user)s %(retmsg)s") % {"user": self.selectedUser, "retmsg": retMsg}, self.window) else: self.showError(title, _("You cannot remove the currently logged in user"), self.window) def on_chkShowSystemUsers_toggled(self, widget): self.refreshData() def on_btnBrowse_clicked(self, widget): directory = SelectDirectoryDialog(_('Select user directory'), self.txtHomeDirectory.get_text(), self.windowUser).show() if directory is not None: self.user['user'].pw_dir = directory self.txtHomeDirectory.set_text(self.user['user'].pw_dir) def on_radEnabled_toggled(self, widget): if widget.get_active(): print(">>> Enable user") def on_radDisabled_toggled(self, widget): if widget.get_active(): print(">>> Disable user") def on_radValidUntilAlways_toggled(self, widget): if widget.get_active(): self.setValidUntil(False) def on_radValidUntilDate_toggled(self, widget): if widget.get_active(): self.setValidUntil(True) def setValidUntil(self, boolean=True): self.spbValidUntilDay.set_sensitive(boolean) self.cmbValidUntilMonth.set_sensitive(boolean) self.spbValidUntilYear.set_sensitive(boolean) if not boolean: self.spbValidUntilDay.set_value(0) self.cmbValidUntilMonth.set_active(-1) self.spbValidUntilYear.set_value(0) def on_chkRequirePasswordAfter_toggled(self, widget): if widget.get_active(): self.setRequirePasswordAfter(True) else: self.setRequirePasswordAfter(False) def setRequirePasswordAfter(self, boolean=True): self.spbRequirePasswordAfter.set_sensitive(boolean) self.spbWarnBeforePasswordExpires.set_sensitive(boolean) self.spbDisableAccountWhenPasswordExpires.set_sensitive(boolean) if not boolean: self.spbRequirePasswordAfter.set_value(0) self.spbWarnBeforePasswordExpires.set_value(0) self.spbDisableAccountWhenPasswordExpires.set_value(0) def on_chkEnforceMinimumPasswordAge_toggled(self, widget): if widget.get_active(): self.setEnforceMinimumPasswordAge(True) else: self.setEnforceMinimumPasswordAge(False) def setEnforceMinimumPasswordAge(self, boolean=True): self.spbEnforceMinimumPasswordAge.set_sensitive(boolean) if not boolean: self.spbEnforceMinimumPasswordAge.set_value(0) def on_txtLoginName_changed(self, widget): self.updateNewLogin() def on_btnSaveUser_clicked(self, widget): errMsgs = [] changed = False name = self.txtLoginName.get_text().strip() if name != "": userExists = self.usr.doesUserExist(name) realName = self.txtRealName.get_text().strip() if realName != "": if self.user['user'].pw_gecos == realName: realName = "" else: print(">>> realName changed") changed = True prGroup = self.cmbHandlerPrimaryGroup.getValue() if prGroup != "": if self.user['prgrp'] == prGroup: prGroup = "" else: print(">>> prGroup changed") changed = True elif not userExists: errMsgs.append(_("You need to provide a primary group for a new user")) home = self.txtHomeDirectory.get_text().strip() facePath = "" if home != "": if exists(self.tempFace): facePath = join(home, ".face") if self.user['user'].pw_dir == home: home = "" else: print(">>> home changed") changed = True groups = self.tvHandlerUserGroups.getToggledValues() if functions.areListsEqual(groups, self.user['groups']): groups = [] else: print(">>> groups changed") changed = True password = self.txtPassword.get_text().strip() if password != "": encPwd = self.usr.encryptPassword(password) if self.user['pwd'].sp_pwd == encPwd: password = "" else: print(">>> password changed") changed = True elif not userExists: errMsgs.append(_("You need to provide a password for a new user")) else: errMsgs.append(_("You need to provide a name for a new user")) shell = self.cmbHandlerShells.getValue() # TODO #if self.radValidUntilDate.get_active(): #vuDay = self.spbValidUntilDay.get_value_as_int() #vuMonth = self.cmbValidUntilMonth.get_active() + 1 #vuYear = self.spbValidUntilYear.get_value_as_int() #(datetime(vuYear, vuMonth, vuDay, 0, 0, 0) - datetime.now()).days #manageUser(self, user, primary_group="", group_list=[], shell="", home_dir="", full_name="", password="", expire_date="", inactive_days=""): title = _("Save user settings") if errMsgs: msg = "\n".join(errMsgs) self.showError(title, msg, self.windowUser) elif changed or facePath != "": err = 0 if changed: err = self.usr.manageUser(user=name, full_name=realName, primary_group=prGroup, home_dir=home, group_list=groups, shell=shell, password=password) if err == 0: if exists(self.tempFace): move(self.tempFace, facePath) self.showInfo(title, _("User saved: %(user)s") % {"user": name}, self.windowUser) self.refreshData() else: # TODO pass if exists(self.tempFace): os.remove(self.tempFace) self.windowUser.hide() else: self.showInfo(title, _("Nothing was changed on user: %(user)s") % {"user": name}, self.windowUser) self.windowUser.hide() if exists(self.tempFace): os.remove(self.tempFace) def on_cmbValidUntilMonth_changed(self, widget): monthDays = functions.getDaysInMonth(widget.get_active() + 1) adj = Gtk.Adjustment(value=1, lower=1, upper=monthDays, step_incr=1, page_incr=0, page_size=0) self.spbValidUntilDay.set_adjustment(adj) self.spbValidUntilDay.set_value(1) def updateNewLogin(self): txt = self.txtLoginName.get_text() self.txtRealName.set_text(txt) self.txtHomeDirectory.set_text("/home/%s" % txt) self.cmbHandlerPrimaryGroup.setValue(txt) def getUsers(self): self.users = [] self.usersInfo = self.usr.getAllUsersInfoDict(self.chkShowSystemUsers.get_active()) for ui in self.usersInfo: self.users.append([ui['face'], ui['user'].pw_name]) def showUserData(self): # Show user groups in tvUserGroupsMain self.selectedUser = self.tvHandlerUsersMain.getSelectedValue(1) print((">>> selectedUser = %s" % self.selectedUser)) for ui in self.usersInfo: if ui['user'].pw_name == self.selectedUser: self.user = ui break self.tvHandlerUserGroupsMain.fillTreeview(contentList=self.user['groups'], columnTypesList=['str']) self.userFace = self.usr.getUserFacePixbuf(self.user['user'].pw_name, None, 32) def getUserGroupsComplete(self, userGroups=None): ugc = [] for group in self.groups: if group != self.user['prgrp']: isUg = False if userGroups is not None: for ug in userGroups: if ug == group: isUg = True break ugc.append([isUg, group]) return ugc def on_btnCancelUser_clicked(self, widget): # Close add share window without saving if exists(self.tempFace): os.remove(self.tempFace) self.windowUser.hide() def on_ebFace_button_release_event(self, widget, data=None): imagePath = SelectImageDialog(_('Select user image'), self.user['user'].pw_dir, self.windowUser).show() if imagePath is not None: ih = ImageHandler(imagePath) ih.makeFaceImage(self.tempFace) if exists(self.tempFace): self.imgFace.set_from_pixbuf(ih.pixbuf) def on_usermanagerUserWindow_delete_event(self, widget, data=None): if exists(self.tempFace): os.remove(self.tempFace) self.windowUser.hide() return True def fillTreeViewUsers(self): self.tvHandlerUsersMain.fillTreeview(contentList=self.users, columnTypesList=['GdkPixbuf.Pixbuf', 'str'], fixedImgHeight=48) def on_ebFace_enter_notify_event(self, widget, data=None): self.windowUser.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2)) def on_ebFace_leave_notify_event(self, widget, data=None): self.windowUser.get_window().set_cursor(None) # =============================================== # Group functions # =============================================== def on_tvGroupsMain_cursor_changed(self, widget, event=None): self.selectedGroup = self.tvHandlerGroupsMain.getSelectedValue() self.selectedGroupAccounts = self.usr.getGroupAccounts(self.selectedGroup) print((">>> selectedGroup 1 = %s" % str(self.selectedGroup))) print((">>> selectedGroupAccounts 1 = %s" % str(self.selectedGroupAccounts))) self.tvHandlerGroupAccounts.fillTreeview(self.selectedGroupAccounts, ['str']) def on_tvGroupsMain_row_activated(self, widget, path, column): self.on_btnGroupEdit_clicked(None) def on_btnGroupAdd_clicked(self, widget): self.selectedGroup = None self.selectedGroupAccounts = None print((">>> selectedGroup 2 = %s" % str(self.selectedGroup))) print((">>> selectedGroupAccounts 2 = %s" % str(self.selectedGroupAccounts))) self.txtGroupName.set_editable(True) self.txtGroupName.set_can_focus(True) self.txtGroupName.set_text("") self.txtGroupID.set_text(str(self.usr.getNewGroupID())) self.fillTreeViewAccounts(self.users) self.windowGroup.show() def on_btnGroupEdit_clicked(self, widget): if self.selectedGroup is not None: availableAccounts = [] groupAccounts = [] for acc in self.users: if acc[1] in self.selectedGroupAccounts: groupAccounts.append(acc) else: availableAccounts.append(acc) self.fillTreeViewAccounts(availableAccounts, groupAccounts) self.txtGroupName.set_text(self.selectedGroup) self.txtGroupName.set_editable(False) self.txtGroupName.set_can_focus(False) self.txtGroupID.set_text(str(self.usr.getGroupID(self.selectedGroup))) self.tvAccounts.grab_focus() self.windowGroup.show() def on_btnGroupRemove_clicked(self, widget): # Remove group if self.selectedGroup is not None: title = _("Remove group") if self.selectedGroup != self.loggedinUserPrimaryGroup: qd = QuestionDialog(title, _("Are you sure you want to remove the following group:\n\n'%(group)s'") % { "group": self.selectedGroup }, self.windowGroup) answer = qd.show() if answer: ret = self.usr.deleteGroup(self.selectedGroup) if ret == "": self.showInfo(title, _("Group successfully removed: %(group)s") % {"group": self.selectedGroup}, self.windowGroup) self.refreshData() else: retMsg = "\n\n%s" % ret self.showError(title, _("Could not remove group: %(group)s %(retmsg)s") % {"group": self.selectedGroup, "retmsg": retMsg}, self.windowGroup) else: self.showError(title, _("You cannot remove the currently logged in user's primary group"), self.windowGroup) def on_btnAddAccount_clicked(self, widget): rows = self.tvHandlerAccounts.getSelectedRows() for row in rows: self.tvHandlerAccounts.delRow(row[0]) self.tvHandlerSelectedAccounts.addRow(row[1]) def on_btnRemoveAccount_clicked(self, widget): # TODO: check if you want to remove an account from its own primary group rows = self.tvHandlerSelectedAccounts.getSelectedRows() for row in rows: acc = row[1][1] pg = self.usr.getUserPrimaryGroupName(acc) if pg != self.selectedGroup: self.tvHandlerSelectedAccounts.delRow(row[0]) self.tvHandlerAccounts.addRow(row[1]) else: self.showError(_("Remove account"), _("You cannot remove an account from its own primary group"), self.windowGroup) def on_btnOkGroup_clicked(self, widget): group = self.txtGroupName.get_text() if group != "": newSelAccs = self.tvHandlerSelectedAccounts.getColumnValues(1) if self.selectedGroupAccounts is not None: # New accounts in group for a in newSelAccs: if a not in self.selectedGroupAccounts: # Add group to account self.usr.addGroupToAccount(a, group) # Removed accounts in group for a in self.selectedGroupAccounts: if a not in newSelAccs: # Remove group from account self.usr.removeGroupFromAccount(a, group) else: # Create new group self.usr.createGroup(group) # Add group to each selected account for a in newSelAccs: self.usr.addGroupToAccount(a, group) # Close the user window self.windowGroup.hide() self.refreshData() def on_btnCancelGroup_clicked(self, widget): self.windowGroup.hide() def on_usermanagerGroupWindow_delete_event(self, widget, data=None): self.windowGroup.hide() return True def on_tvAccounts_row_activated(self, widget, path, column): self.on_btnAddAccount_clicked(None) def on_tvSelectedAccounts_row_activated(self, widget, path, column): self.on_btnRemoveAccount_clicked(None) def fillTreeViewAccounts(self, availableAccounts, selectedAccounts=None): if (availableAccounts is None or not availableAccounts) and selectedAccounts: # Dirty hack to prepare the available accounts treeview self.tvHandlerAccounts.fillTreeview(contentList=selectedAccounts, columnTypesList=['GdkPixbuf.Pixbuf', 'str'], fixedImgHeight=24) self.tvHandlerAccounts.clearTreeView() else: self.tvHandlerAccounts.fillTreeview(contentList=availableAccounts, columnTypesList=['GdkPixbuf.Pixbuf', 'str'], fixedImgHeight=24) if (selectedAccounts is None or not selectedAccounts) and availableAccounts: # Dirty hack to prepare the selected accounts treeview self.tvHandlerSelectedAccounts.fillTreeview(contentList=availableAccounts, columnTypesList=['GdkPixbuf.Pixbuf', 'str'], fixedImgHeight=24) self.tvHandlerSelectedAccounts.clearTreeView() else: self.tvHandlerSelectedAccounts.fillTreeview(contentList=selectedAccounts, columnTypesList=['GdkPixbuf.Pixbuf', 'str'], fixedImgHeight=24) def fillTreeViewGroups(self): self.tvHandlerGroupsMain.fillTreeview(contentList=self.groups, columnTypesList=['str']) self.on_tvGroupsMain_cursor_changed(None) # =============================================== # General functions # =============================================== def refreshData(self): self.getUsers() self.groups = self.usr.getGroups() self.fillTreeViewUsers() self.fillTreeViewGroups() self.showUserData() def showInfo(self, title, message, parent): MessageDialogSave(title, message, Gtk.MessageType.INFO, parent).show() def showError(self, title, message, parent): MessageDialogSave(title, message, Gtk.MessageType.ERROR, parent).show() def filterText(self, widget): def filter(entry, *args): text = entry.get_text().strip().lower() entry.set_text(''.join([i for i in text if i in '0123456789abcdefghijklmnopqrstuvwxyz-._'])) widget.connect('changed', filter) # Close the gui def on_usermanagerWindow_destroy(self, widget): # Close the app Gtk.main_quit()
class Constructor(object): def __init__(self): self.scriptDir = abspath(dirname(__file__)) self.shareDir = join(self.scriptDir, '../../../share/solydxk/constructor') # Load window and widgets self.builder = Gtk.Builder() self.builder.add_from_file(join(self.shareDir, 'constructor.glade')) # Main window objects go = self.builder.get_object self.window = go('constructorWindow') self.tvDistros = go('tvDistros') self.lblOutput = go('lblOutput') self.statusbar = go('statusbar') self.btnAdd = go('btnAdd') self.chkSelectAll = go('chkSelectAll') self.btnRemove = go('btnRemove') self.btnEdit = go('btnEdit') self.btnUpgrade = go('btnUpgrade') self.btnLocalize = go('btnLocalize') self.btnBuildIso = go('btnBuildIso') # Add iso window objects self.windowAddDistro = go('addDistroWindow') self.txtIso = go('txtIso') self.txtDir = go('txtDir') self.btnDir = go('btnDir') self.btnSave = go('btnSave') self.btnHelp = go('btnHelp') self.lblIso = go('lblIso') self.boxIso = go('boxIso') self.lblDir = go('lblDir') self.chkFromIso = go('chkFromIso') # Main window translations self.window.set_title(_("SolydXK Constructor")) self.chkSelectAll.set_label(_("Select all")) self.btnAdd.set_label("_{}".format(_("Add"))) self.btnRemove.set_label("_{}".format(_("Remove"))) self.btnEdit.set_label("_{}".format(_("Edit"))) self.btnUpgrade.set_label("_{}".format(_("Upgrade"))) self.btnLocalize.set_label("_{}".format(_("Localize"))) self.btnBuildIso.set_label("_{}".format(_("Build"))) self.btnHelp.set_label("_{}".format(_("Help"))) # Add iso window translations self.lblIso.set_text(_("ISO")) go('btnCancel').set_label("_{}".format(_("Cancel"))) # Init self.ec = ExecCmd() self.ec.run("modprobe loop", False) self.queue = Queue() self.mountDir = "/mnt/constructor" self.distroFile = join(self.scriptDir, "distros.list") self.distroAdded = False self.iso = None self.dir = None self.isoName = None self.doneWav = join(self.shareDir, 'done.wav') self.help = join(self.shareDir, 'help.html') self.chkFromIso.set_active(True) self.toggleGuiElements(False) self.hostEfiArchitecture = functions.getHostEfiArchitecture() # Treeviews self.tvHandlerDistros = TreeViewHandler(self.tvDistros) self.fillTreeViewDistros() # Version information ver = _("Version") self.version = "%s: %s" % (ver, functions.getPackageVersion('solydxk-constructor')) self.showOutput(self.version) # Connect the signals and show the window self.builder.connect_signals(self) self.window.show() # =============================================== # Main Window Functions # =============================================== def on_btnAdd_clicked(self, widget): self.windowAddDistro.show() def on_btnRemove_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: qd = QuestionDialog(self.btnRemove.get_label(), _("Are you sure you want to remove the selected distribution from the list?\n" \ "(This will not remove the directory and its data)"), self.window) answer = qd.show() if answer: self.saveDistroFile(distroPath=path, addDistro=False) self.fillTreeViewDistros() def on_btnEdit_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: de = EditDistro(path) services = [] if exists(join(path, 'root/etc/apache2/apache2.conf')): services.append("apache2") if exists(join(path, 'root/etc/mysql/debian.cnf')): services.append("mysql") if services: msg = "If you need to update packages that depend on these services,\n" \ "you will need to manually start them:\n" for service in services: msg += "\nservice %s start" % service msg += "\n\nWhen done:\n" for service in services: msg += "\nservice %s stop" % service self.showInfo(_("Services detected"), msg, self.window) functions.repaintGui() de.openTerminal() def on_btnUpgrade_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) upgraded = False for path in selected: upgraded = True rootPath = "%s/root" % path de = EditDistro(path) de.openTerminal("apt-get update") if exists(join(rootPath, 'etc/apache2/apache2.conf')): de.openTerminal("service apache2 start") if exists(join(rootPath, 'etc/mysql/debian.cnf')): de.openTerminal("service mysql start") de.openTerminal("apt-get -y --force-yes -o Dpkg::Options::=\"--force-confnew\" dist-upgrade") if exists(join(rootPath, 'etc/apache2/apache2.conf')): de.openTerminal("service apache2 stop") if exists(join(rootPath, 'etc/mysql/debian.cnf')): de.openTerminal("service mysql stop") # Cleanup old kernel and headers script = "rmoldkernel.sh" scriptSource = join(self.scriptDir, "files/{}".format(script)) scriptTarget = join(rootPath, script) if exists(scriptSource): copy(scriptSource, scriptTarget) self.ec.run("chmod a+x %s" % scriptTarget) de.openTerminal("/bin/bash %s" % script) remove(scriptTarget) # Build EFI files if self.hostEfiArchitecture != "": print(">> Start building EFI files") self.build_efi_files() # Download offline packages print(">> Start downloading offline packages") self.download_offline_packages() if upgraded and exists("/usr/bin/aplay") and exists(self.doneWav): self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False) def on_btnLocalize_clicked(self, widget): # Set locale selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: rootPath = "%s/root" % path de = EditDistro(path) script = "setlocale.sh" scriptSource = join(self.scriptDir, "files/{}".format(script)) scriptTarget = join(rootPath, script) if exists(scriptSource): copy(scriptSource, scriptTarget) self.ec.run("chmod a+x %s" % scriptTarget) de.openTerminal("/bin/bash %s" % script) remove(scriptTarget) def build_efi_files(self): # TODO - also 32-bit installs (haven't tested this) modules = "part_gpt part_msdos ntfs ntfscomp hfsplus fat ext2 normal chain boot configfile linux " \ "multiboot iso9660 gfxmenu gfxterm loadenv efi_gop efi_uga loadbios fixvideo png " \ "ext2 ntfscomp loopback search minicmd cat cpuid appleldr elf usb videotest " \ "halt help ls reboot echo test normal sleep memdisk tar font video_fb video " \ "gettext true video_bochs video_cirrus multiboot2 acpi gfxterm_background gfxterm_menu" selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: rootPath = "%s/root" % path bootPath = "{}/boot".format(path) arch = functions.getGuestEfiArchitecture(rootPath) grubEfiName = "bootx64" efiName = "x64" if arch != "x86_64": arch = "i386" grubEfiName = "bootia32" efiName = "ia32" try: if not exists("{}/efi/boot".format(bootPath)): makedirs("{}/efi/boot".format(bootPath)) self.ec.run("efi-image {}/~tmp {}-efi {}".format(bootPath, arch, efiName)) if exists("{}/~tmp/efi.img".format(bootPath) and exists("{}/~tmp/boot/grub/{}-efi".format(bootPath, arch))): self.ec.run("rm -r {}/boot/grub/{}-efi".format(bootPath, arch)) self.ec.run("mv -vf {}/~tmp/boot/grub/{}-efi {}/boot/grub/".format(bootPath, arch, bootPath)) self.ec.run("mv -vf {}/~tmp/efi.img {}/boot/grub/".format(bootPath, bootPath)) self.ec.run("rm -r {}/~tmp".format(bootPath)) self.ec.run("grub-mkimage -O {}-efi -d /usr/lib/grub/{}-efi " "-o {}/efi/boot/{}.efi " "-p \"/boot/grub\" {}".format(arch, arch, bootPath, grubEfiName, modules)) print((">> Finished building EFI files")) except Exception as detail: self.showError("Error: build EFI files", detail, self.window) def download_offline_packages(self): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: rootPath = "%s/root" % path arch = functions.getGuestEfiArchitecture(rootPath) de = EditDistro(path) script = "offline.sh" scriptSource = join(self.scriptDir, "files/{}".format(script)) scriptTarget = join(rootPath, script) offlineSource = join(rootPath, "offline") offlineTarget = join(rootPath, "../boot/offline") if exists(scriptSource): try: copy(scriptSource, scriptTarget) self.ec.run("chmod a+x %s" % scriptTarget) # Run the script de.openTerminal("/bin/bash {} {}".format(script, arch)) # Remove script remove(scriptTarget) # Move offline directory to boot directory if exists(offlineSource): print(("%s exists" % offlineSource)) if exists(offlineTarget): print((">> Remove %s" % offlineTarget)) rmtree(offlineTarget) print((">> Move %s to %s" % (offlineSource, offlineTarget))) move(offlineSource, offlineTarget) else: print((">> Cannot find: %s" % offlineSource)) except Exception as detail: self.showError("Error: getting offline packages", detail, self.window) else: print((">> Cannot find: %s" % scriptSource)) def on_btnBuildIso_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) msg = "" for path in selected: self.toggleGuiElements(True) self.showOutput("Start building ISO in: %s" % path) functions.repaintGui() # Start building the ISO in a thread t = BuildIso(path, self.queue) t.start() self.queue.join() # Thread is done # Get the data from the queue ret = self.queue.get() self.queue.task_done() if ret is not None: self.showOutput(ret) if "error" in ret.lower(): self.showError("Error", ret, self.window) else: msg += "%s\n" % ret if msg != "": if exists("/usr/bin/aplay") and exists(self.doneWav): self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False) self.showInfo("", msg, self.window) self.toggleGuiElements(False) def on_chkSelectAll_toggled(self, widget): self.tvHandlerDistros.treeviewToggleAll(toggleColNrList=[0], toggleValue=widget.get_active()) def on_tvDistros_row_activated(self, widget, path, column): self.tvHandlerDistros.treeviewToggleRows(toggleColNrList=[0]) def on_btnHelp_clicked(self, widget): if functions.isPackageInstalled("firefox"): system("firefox file://%s &" % self.help) else: system("xdg-open file://%s &" % self.help) def on_btnOpenDir_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: system("xdg-open %s &" % path) def fillTreeViewDistros(self, selectDistros=[]): contentList = [[_("Select"), _("Distribution"), _("Working directory")]] distros = self.getDistros() for distro in distros: select = False for selectDistro in selectDistros: if distro[0] == selectDistro: select = True contentList.append([select, distro[0], distro[1]]) self.tvHandlerDistros.fillTreeview(contentList=contentList, columnTypesList=['bool', 'str', 'str'], firstItemIsColName=True) def getDistros(self): distros = [] if exists(self.distroFile): with open(self.distroFile, 'r') as f: lines = f.readlines() for line in lines: line = line.strip().rstrip('/') print(line) if exists(line): dg = DistroGeneral(line) isoName = dg.description distros.append([isoName, line]) # Sort on iso name if distros: distros = sorted(distros, key=operator.itemgetter(0)) return distros # =============================================== # Add ISO Window Functions # =============================================== def on_btnIso_clicked(self, widget): fleFilter = Gtk.FileFilter() fleFilter.set_name("ISO") fleFilter.add_mime_type("application/x-cd-image") fleFilter.add_pattern("*.iso") startDir = None if exists(dirname(self.txtIso.get_text())): startDir = dirname(self.txtIso.get_text()) filePath = SelectFileDialog(title=_('Select SolydXK ISO'), start_directory=startDir, parent=self.window, gtkFileFilter=fleFilter).show() if filePath is not None: self.txtIso.set_text(filePath) def on_btnDir_clicked(self, widget): startDir = None if exists(self.txtDir.get_text()): startDir = self.txtDir.get_text() dirText = SelectDirectoryDialog(title=_('Select directory'), start_directory=startDir, parent=self.window).show() if dirText is not None: self.txtDir.set_text(dirText) def on_btnSave_clicked(self, widget): self.iso = "" if self.chkFromIso.get_active(): self.iso = self.txtIso.get_text() self.dir = self.txtDir.get_text() title = _("Save existing working directory") if self.iso != "": title = _("Unpack ISO and save") if not exists(self.dir): makedirs(self.dir) if not exists(self.dir): self.showError(title, _("Could not create directory %(dir)s: exiting" % {"dir": self.dir}), self.window) else: self.windowAddDistro.hide() if self.iso != "": if not exists(self.iso): self.showInfo(self.btnSave.get_label(), _("The path to the ISO file does not exist:\n{}".format(self.iso)), self.window) return if listdir(self.dir): qd = QuestionDialog(self.btnSave.get_label(), _("The destination directory is not empty.\n" "Are you sure you want to overwrite all data in {}?".format(self.dir)), self.window) answer = qd.show() if not answer: return self.showOutput("Start unpacking the ISO...") self.toggleGuiElements(True) t = IsoUnpack(self.mountDir, self.iso, self.dir, self.queue) t.start() self.queue.join() GObject.timeout_add(1000, self.checkThread, True) else: self.saveDistroFile(self.dir, True) self.fillTreeViewDistros() self.showOutput(_("Existing working directory added")) #self.toggleGuiElements(False) def on_btnCancel_clicked(self, widget): self.windowAddDistro.hide() def on_addDistroWindow_delete_event(self, widget, data=None): self.windowAddDistro.hide() return True def on_txtIso_changed(self, widget): path = self.txtIso.get_text() if exists(path): self.txtDir.set_sensitive(True) self.btnDir.set_sensitive(True) if exists(self.txtDir.get_text()): self.btnSave.set_sensitive(True) else: self.txtDir.set_sensitive(False) self.btnDir.set_sensitive(False) self.btnSave.set_sensitive(False) def on_txtDir_changed(self, widget): blnFromIso = self.chkFromIso.get_active() isoPath = self.txtIso.get_text() dirText = self.txtDir.get_text() self.btnSave.set_sensitive(False) if exists(dirText): if blnFromIso: if exists(isoPath): self.btnSave.set_sensitive(True) else: self.btnSave.set_sensitive(True) def on_chkFromIso_toggled(self, widget): if widget.get_active(): self.lblIso.set_visible(True) self.boxIso.set_visible(True) self.txtDir.set_sensitive(False) self.btnDir.set_sensitive(False) self.btnSave.set_sensitive(False) self.lblDir.set_text(_("Unpack ISO to directory")) self.btnSave.set_label(_("Unpack & Save")) else: self.txtIso.set_text("") self.lblIso.set_visible(False) self.boxIso.set_visible(False) self.txtDir.set_sensitive(True) self.btnDir.set_sensitive(True) self.btnSave.set_sensitive(True) self.lblDir.set_text(_("Work directory")) self.btnSave.set_label(_("Save")) # =============================================== # General functions # =============================================== def showInfo(self, title, message, parent=None): MessageDialogSafe(title, message, Gtk.MessageType.INFO, parent).show() def showError(self, title, message, parent=None): MessageDialogSafe(title, message, Gtk.MessageType.ERROR, parent).show() def showOutput(self, message): print(message) functions.pushMessage(self.statusbar, message) def checkThread(self, addDistro=None): #print 'Thread count = ' + str(threading.active_count()) # As long there's a thread active, keep spinning if threading.active_count() > 1: return True # Thread is done # Get the data from the queuez ret = self.queue.get() self.queue.task_done() # Thread is done if addDistro is not None: self.saveDistroFile(self.dir, addDistro) self.fillTreeViewDistros(self.isoName) self.toggleGuiElements(False) if exists("/usr/bin/aplay") and exists(self.doneWav): self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False) if ret is not None: self.showOutput(ret) if "error" in ret.lower(): self.showError("Error", ret, self.window) else: self.showInfo("", ret, self.window) return False def toggleGuiElements(self, startThread): if startThread: self.chkSelectAll.set_sensitive(False) self.tvDistros.set_sensitive(False) self.btnAdd.set_sensitive(False) self.btnBuildIso.set_sensitive(False) self.btnEdit.set_sensitive(False) self.btnRemove.set_sensitive(False) self.btnUpgrade.set_sensitive(False) else: self.chkSelectAll.set_sensitive(True) self.tvDistros.set_sensitive(True) self.btnAdd.set_sensitive(True) self.btnBuildIso.set_sensitive(True) self.btnEdit.set_sensitive(True) self.btnRemove.set_sensitive(True) self.btnUpgrade.set_sensitive(True) def saveDistroFile(self, distroPath, addDistro=True): newCont = [] dg = DistroGeneral(distroPath) self.isoName = dg.description cfg = [] if exists(self.distroFile): with open(self.distroFile, 'r') as f: cfg = f.readlines() for line in cfg: line = line.strip() if distroPath not in line and exists(line): newCont.append(line) if addDistro: newCont.append(distroPath) with open(self.distroFile, 'w') as f: f.write('\n'.join(newCont)) self.iso = "" self.dir = "" # Close the gui def on_constructorWindow_destroy(self, widget): # Close the app Gtk.main_quit()
class Constructor(object): def __init__(self): self.scriptDir = abspath(dirname(__file__)) self.shareDir = join(self.scriptDir, '../../../share/trail/constructor') self.userAppDir = join(get_user_home_dir(), ".constructor") self.distroFile = join(self.userAppDir, "distros.list") # Create the user's application directory if it doesn't exist if not isdir(self.userAppDir): user_name = getUserLoginName() makedirs(self.userAppDir) old_distro_file = join(self.scriptDir, "distros.list") if exists(old_distro_file): move(old_distro_file, self.distroFile) system("chown -R %s:%s %s" % (user_name, user_name, self.userAppDir)) # Load window and widgets self.builder = Gtk.Builder() self.builder.add_from_file(join(self.shareDir, 'constructor.glade')) # Main window objects go = self.builder.get_object self.window = go('constructorWindow') self.tvDistros = go('tvDistros') self.lblOutput = go('lblOutput') self.statusbar = go('statusbar') self.btnAdd = go('btnAdd') self.chkSelectAll = go('chkSelectAll') self.btnRemove = go('btnRemove') self.btnEdit = go('btnEdit') self.btnUpgrade = go('btnUpgrade') self.btnLocalize = go('btnLocalize') self.btnBuildIso = go('btnBuildIso') # Add iso window objects self.windowAddDistro = go('addDistroWindow') self.txtIso = go('txtIso') self.txtDir = go('txtDir') self.btnDir = go('btnDir') self.btnSave = go('btnSave') self.lblIso = go('lblIso') self.boxIso = go('boxIso') self.lblDir = go('lblDir') self.chkFromIso = go('chkFromIso') # Main window translations self.window.set_title(_("Constructor")) self.chkSelectAll.set_label(_("Select all")) self.btnAdd.set_label("_{}".format(_("Add"))) self.btnRemove.set_label("_{}".format(_("Remove"))) self.btnEdit.set_label("_{}".format(_("Edit"))) self.btnUpgrade.set_label("_{}".format(_("Upgrade"))) self.btnLocalize.set_label("_{}".format(_("Localize"))) self.btnBuildIso.set_label("_{}".format(_("Build"))) # Add iso window translations self.lblIso.set_text(_("ISO")) go('btnCancel').set_label("_{}".format(_("Cancel"))) # Init self.ec = ExecCmd() self.ec.run("modprobe loop", False) self.queue = Queue() self.mountDir = "/mnt/constructor" self.distroAdded = False self.iso = None self.dir = None self.isoName = None self.chkFromIso.set_active(True) self.toggleGuiElements(False) # Treeviews self.tvHandlerDistros = TreeViewHandler(self.tvDistros) self.fillTreeViewDistros() # Version information ver = _("Version") self.version = "%s: %s" % (ver, getPackageVersion('constructor')) self.showOutput(self.version) # Connect the signals and show the window self.builder.connect_signals(self) self.window.show() # =============================================== # Main Window Functions # =============================================== def on_btnAdd_clicked(self, widget): self.windowAddDistro.show() def on_btnRemove_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: qd = QuestionDialog(self.btnRemove.get_label(), _("Are you sure you want to remove the selected distribution from the list?\n" \ "(This will not remove the directory and its data)"), self.window) answer = qd.show() if answer: self.saveDistroFile(distroPath=path, addDistro=False) self.fillTreeViewDistros() def on_btnEdit_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: de = EditDistro(path) services = [] if exists(join(path, 'root/etc/apache2/apache2.conf')): services.append("apache2") if exists(join(path, 'root/etc/mysql/debian.cnf')): services.append("mysql") if services: msg = "If you need to update packages that depend on these services,\n" \ "you will need to manually start them:\n" for service in services: msg += "\nservice %s start" % service msg += "\n\nWhen done:\n" for service in services: msg += "\nservice %s stop" % service self.showInfo(_("Services detected"), msg, self.window) repaintGui() de.openTerminal() def on_btnUpgrade_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) upgraded = False for path in selected: upgraded = True rootPath = "%s/root" % path force = get_apt_force(rootPath) de = EditDistro(path) de.openTerminal("apt-get update") if exists(join(rootPath, 'etc/apache2/apache2.conf')): de.openTerminal("service apache2 start") if exists(join(rootPath, 'etc/mysql/debian.cnf')): de.openTerminal("service mysql start") de.openTerminal( "apt-get -y %s -o Dpkg::Options::=\"--force-confnew\" dist-upgrade" % force) if exists(join(rootPath, 'etc/apache2/apache2.conf')): de.openTerminal("service apache2 stop") if exists(join(rootPath, 'etc/mysql/debian.cnf')): de.openTerminal("service mysql stop") # Cleanup old kernel and headers script = "rmoldkernel.sh" scriptSource = join(self.scriptDir, "files/{}".format(script)) scriptTarget = join(rootPath, script) if exists(scriptSource): copy(scriptSource, scriptTarget) self.ec.run("chmod a+x '%s'" % scriptTarget) de.openTerminal("/bin/bash %s" % script) silent_remove(scriptTarget) # Download offline packages print(">> Start downloading offline packages") self.download_offline_packages() if upgraded and exists("/usr/bin/aplay") and exists(self.doneWav): self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False) def on_btnLocalize_clicked(self, widget): # Set locale selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: rootPath = "%s/root" % path de = EditDistro(path) script = "setlocale.sh" scriptSource = join(self.scriptDir, "files/{}".format(script)) scriptTarget = join(rootPath, script) if exists(scriptSource): copy(scriptSource, scriptTarget) self.ec.run("chmod a+x '%s'" % scriptTarget) de.openTerminal("/bin/bash %s" % script) silent_remove(scriptTarget) def download_offline_packages(self): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: rootPath = "%s/root" % path arch = getGuestEfiArchitecture(rootPath) de = EditDistro(path) script = "offline.sh" scriptSource = join(self.scriptDir, "files/{}".format(script)) scriptTarget = join(rootPath, script) offlineSource = join(rootPath, "offline") offlineTarget = join(rootPath, "../boot/offline") if exists(scriptSource): try: copy(scriptSource, scriptTarget) self.ec.run("chmod a+x '%s'" % scriptTarget) # Run the script de.openTerminal("/bin/bash {} {}".format(script, arch)) # Remove script silent_remove(scriptTarget) # Move offline directory to boot directory if exists(offlineSource): print(("%s exists" % offlineSource)) if exists(offlineTarget): print((">> Remove %s" % offlineTarget)) silent_remove(offlineTarget) print((">> Move %s to %s" % (offlineSource, offlineTarget))) move(offlineSource, offlineTarget) else: print((">> Cannot find: %s" % offlineSource)) except Exception as detail: self.showError("Error: getting offline packages", detail, self.window) else: print((">> Cannot find: %s" % scriptSource)) def on_btnBuildIso_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) msg = "" for path in selected: self.toggleGuiElements(True) self.showOutput("Start building ISO in: %s" % path) repaintGui() # Start building the ISO in a thread t = BuildIso(path, self.queue) t.start() self.queue.join() # Thread is done # Get the data from the queue ret = self.queue.get() self.queue.task_done() if ret is not None: self.showOutput(ret) if "error" in ret.lower(): self.showError("Error", ret, self.window) else: msg += "%s\n" % ret if msg != "": if exists("/usr/bin/aplay") and exists(self.doneWav): self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False) self.showInfo("", msg, self.window) self.toggleGuiElements(False) def on_chkSelectAll_toggled(self, widget): self.tvHandlerDistros.treeviewToggleAll( toggleColNrList=[0], toggleValue=widget.get_active()) def on_tvDistros_row_activated(self, widget, path, column): self.tvHandlerDistros.treeviewToggleRows(toggleColNrList=[0]) def on_btnOpenDir_clicked(self, widget): selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2) for path in selected: system("open-as-user %s root" % path) def fillTreeViewDistros(self, selectDistros=[]): contentList = [[ _("Select"), _("Distribution"), _("Working directory") ]] distros = self.getDistros() for distro in distros: select = False for selectDistro in selectDistros: if distro[0] == selectDistro: select = True contentList.append([select, distro[0], distro[1]]) self.tvHandlerDistros.fillTreeview( contentList=contentList, columnTypesList=['bool', 'str', 'str'], firstItemIsColName=True) def getDistros(self): distros = [] if exists(self.distroFile): with open(self.distroFile, 'r') as f: lines = f.readlines() for line in lines: line = line.strip().rstrip('/') print(line) if exists(line): dg = DistroGeneral(line) isoName = dg.description distros.append([isoName, line]) # Sort on iso name if distros: distros = sorted(distros, key=operator.itemgetter(0)) return distros # =============================================== # Add ISO Window Functions # =============================================== def on_btnIso_clicked(self, widget): fleFilter = Gtk.FileFilter() fleFilter.set_name("ISO") fleFilter.add_mime_type("application/x-cd-image") fleFilter.add_pattern("*.iso") startDir = None if exists(dirname(self.txtIso.get_text())): startDir = dirname(self.txtIso.get_text()) filePath = SelectFileDialog(title=_('Select ISO'), start_directory=startDir, parent=self.window, gtkFileFilter=fleFilter).show() if filePath is not None: self.txtIso.set_text(filePath) def on_btnDir_clicked(self, widget): startDir = None if exists(self.txtDir.get_text()): startDir = self.txtDir.get_text() dirText = SelectDirectoryDialog(title=_('Select directory'), start_directory=startDir, parent=self.window).show() if dirText is not None: self.txtDir.set_text(dirText) def on_btnSave_clicked(self, widget): self.iso = "" if self.chkFromIso.get_active(): self.iso = self.txtIso.get_text() self.dir = self.txtDir.get_text() title = _("Save existing working directory") if self.iso != "": title = _("Unpack ISO and save") if not exists(self.dir): makedirs(self.dir) if not exists(self.dir): self.showError( title, _("Could not create directory %(dir)s: exiting" % {"dir": self.dir}), self.window) else: self.windowAddDistro.hide() if self.iso != "": if not exists(self.iso): self.showInfo( self.btnSave.get_label(), _("The path to the ISO file does not exist:\n{}". format(self.iso)), self.window) return if listdir(self.dir): qd = QuestionDialog( self.btnSave.get_label(), _("The destination directory is not empty.\n" "Are you sure you want to overwrite all data in {}?". format(self.dir)), self.window) answer = qd.show() if not answer: return self.showOutput("Start unpacking the ISO...") self.toggleGuiElements(True) t = IsoUnpack(self.mountDir, self.iso, self.dir, self.queue) t.start() self.queue.join() GObject.timeout_add(1000, self.checkThread, True) else: self.saveDistroFile(self.dir, True) self.fillTreeViewDistros() self.showOutput(_("Existing working directory added")) #self.toggleGuiElements(False) def on_btnCancel_clicked(self, widget): self.windowAddDistro.hide() def on_addDistroWindow_delete_event(self, widget, data=None): self.windowAddDistro.hide() return True def on_txtIso_changed(self, widget): path = self.txtIso.get_text() if exists(path): self.txtDir.set_sensitive(True) self.btnDir.set_sensitive(True) if exists(self.txtDir.get_text()): self.btnSave.set_sensitive(True) else: self.txtDir.set_sensitive(False) self.btnDir.set_sensitive(False) self.btnSave.set_sensitive(False) def on_txtDir_changed(self, widget): blnFromIso = self.chkFromIso.get_active() isoPath = self.txtIso.get_text() dirText = self.txtDir.get_text() self.btnSave.set_sensitive(False) if exists(dirText): if blnFromIso: if exists(isoPath): self.btnSave.set_sensitive(True) else: self.btnSave.set_sensitive(True) def on_chkFromIso_toggled(self, widget): if widget.get_active(): self.lblIso.set_visible(True) self.boxIso.set_visible(True) self.txtDir.set_sensitive(False) self.btnDir.set_sensitive(False) self.btnSave.set_sensitive(False) self.lblDir.set_text(_("Unpack ISO to directory")) self.btnSave.set_label(_("Unpack & Save")) else: self.txtIso.set_text("") self.lblIso.set_visible(False) self.boxIso.set_visible(False) self.txtDir.set_sensitive(True) self.btnDir.set_sensitive(True) self.btnSave.set_sensitive(True) self.lblDir.set_text(_("Work directory")) self.btnSave.set_label(_("Save")) # =============================================== # General functions # =============================================== def showInfo(self, title, message, parent=None): MessageDialogSafe(title, message, Gtk.MessageType.INFO, parent).show() def showError(self, title, message, parent=None): MessageDialogSafe(title, message, Gtk.MessageType.ERROR, parent).show() def showOutput(self, message): print(message) pushMessage(self.statusbar, message) def checkThread(self, addDistro=None): #print 'Thread count = ' + str(threading.active_count()) # As long there's a thread active, keep spinning if threading.active_count() > 1: return True # Thread is done # Get the data from the queuez ret = self.queue.get() self.queue.task_done() # Thread is done if addDistro is not None: self.saveDistroFile(self.dir, addDistro) self.fillTreeViewDistros(self.isoName) self.toggleGuiElements(False) if exists("/usr/bin/aplay") and exists(self.doneWav): self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False) if ret is not None: self.showOutput(ret) if "error" in ret.lower(): self.showError("Error", ret, self.window) else: self.showInfo("", ret, self.window) return False def toggleGuiElements(self, startThread): if startThread: self.chkSelectAll.set_sensitive(False) self.tvDistros.set_sensitive(False) self.btnAdd.set_sensitive(False) self.btnBuildIso.set_sensitive(False) self.btnEdit.set_sensitive(False) self.btnRemove.set_sensitive(False) self.btnUpgrade.set_sensitive(False) self.btnLocalize.set_sensitive(False) self.btnDir.set_sensitive(False) else: self.chkSelectAll.set_sensitive(True) self.tvDistros.set_sensitive(True) self.btnAdd.set_sensitive(True) self.btnBuildIso.set_sensitive(True) self.btnEdit.set_sensitive(True) self.btnRemove.set_sensitive(True) self.btnUpgrade.set_sensitive(True) self.btnLocalize.set_sensitive(True) self.btnDir.set_sensitive(True) def saveDistroFile(self, distroPath, addDistro=True): newCont = [] dg = DistroGeneral(distroPath) self.isoName = dg.description cfg = [] if exists(self.distroFile): with open(self.distroFile, 'r') as f: cfg = f.readlines() for line in cfg: line = line.strip() if distroPath not in line and exists(line): newCont.append(line) if addDistro: newCont.append(distroPath) with open(self.distroFile, 'w') as f: f.write('\n'.join(newCont)) self.iso = "" self.dir = "" # Close the gui def on_constructorWindow_destroy(self, widget): # Close the app Gtk.main_quit()