class ChildDialogExportSelection: """ Open a child dialog of a tkinter application to ask the user to select fields between many. """ def __init__(self, parent, keys): """ Open a child dialog of a tkinter application to ask details about an export of treeview items. Args: parent: the tkinter parent view to use for this window construction. keys: The keys to export """ self.rvalue = None self.parent = parent self.app = tk.Toplevel(parent) self.app.title("Export selection") appFrame = ttk.Frame(self.app) self.form = FormPanel() self.form.addFormChecklist("Fields", sorted(keys), []) self.form.addFormButton("Export", self.onOk) self.rvalue = None self.form.constructView(appFrame) appFrame.pack(ipadx=10, ipady=10) try: self.app.wait_visibility() self.app.transient(parent) self.app.focus_force() self.app.grab_set() self.app.lift() except tk.TclError: pass def onOk(self, _event=None): """Called the the Export button is pressed. return a list of strings corresponding to the selected fields. Args: _event: not used but mandatory""" res, msg = self.form.checkForm() if res: form_values = self.form.getValue() form_values_as_dicts = ViewElement.list_tuple_to_dict(form_values) mfields = form_values_as_dicts["Fields"] fields = [k for k, v in mfields.items() if v == 1] self.rvalue = fields self.app.destroy() else: tk.messagebox.showwarning("Form not validated", msg, parent=self.app)
class ChildDialogEditCommandSettings: """ Open a child dialog of a tkinter application to fill settings for a command """ def __init__(self, parent, displayMsg="Choose a database to open:", default=None): """ Open a child dialog of a tkinter application to ask a combobox option. Args: parent: the tkinter parent view to use for this window construction. options: A list of string correspondig to options of the combobox displayMsg: The message that will explain to the user what he is choosing. default: Choose a default selected option (one of the string in options). default is None """ self.app = tk.Toplevel(parent) self.app.title("Upload result file") self.rvalue = None self.parent = parent appFrame = ttk.Frame(self.app) self.form = FormPanel() self.form.addFormLabel(displayMsg, side=tk.TOP) optionsFrame = self.form.addFormPanel(grid=True) optionsFrame.addFormLabel("Remote bin path", row=0, column=0) optionsFrame.addFormStr("bin", r".+", row=0, column=1) optionsFrame.addFormLabel("Plugin", row=1, column=0) apiclient = APIClient.getInstance() optionsFrame.addFormCombo("plugin", tuple(apiclient.getPlugins()), row=1, column=1) self.form.addFormButton("Cancel", self.onError) self.form.addFormButton("OK", self.onOk) self.form.constructView(appFrame) appFrame.pack(ipadx=10, ipady=10) try: self.app.wait_visibility() self.app.transient(parent) self.app.focus_force() self.app.grab_set() self.app.lift() except tk.TclError: pass def onOk(self, _event): """ Called when the user clicked the validation button. Set the rvalue attributes to the value selected and close the window. """ # send the data to the parent res, msg = self.form.checkForm() if not res: tk.messagebox.showwarning( "Form not validated", msg, parent=self.app) return form_values = self.form.getValue() form_values_as_dicts = ViewElement.list_tuple_to_dict(form_values) self.rvalue = (form_values_as_dicts["bin"], form_values_as_dicts["plugin"]) self.app.destroy() def onError(self, _event=None): """ Close the dialog and set rvalue to None """ self.rvalue = None self.app.destroy()
class Settings: """ Represents the settings of pollenisator. There are three level of settings: * local settings: stored in a file under ../../config/settings.cfg * pentest db settings: stored in the pentest database under settings collection * global settings: stored in the pollenisator database under settings collection """ tags_cache = None __pentest_types = None def __init__(self): """ Load the tree types of settings and stores them in dictionnaries """ dir_path = os.path.dirname(os.path.realpath(__file__)) self.confdir = os.path.join(dir_path, "../../config/settings.cfg") self.local_settings = {} try: with open(self.confdir, "r") as f: self.local_settings = json.loads(f.read()) except json.JSONDecodeError: self.local_settings = {} except IOError: self.local_settings = {} self.db_settings = {} self.global_settings = {} self.text_pentest_types = None self.text_tags = None self.text_db_tags = None self.box_pentest_type = None self.visual_include_domains_with_ip_in_scope = None self.visual_include_domains_with_topdomain_in_scope = None self.visual_search_show_hidden = None self.visual_search_exact_match = None self.visual_include_all_domains = None #self.text_pentesters = None self.box_favorite_term = None self.text_terms = None self.visual_trap_commands = None self.pentesters_treevw = None @classmethod def getPentestTags(cls): apiclient = APIClient.getInstance() db_tags = apiclient.find("settings", {"key": "tags"}, False) if db_tags is None: db_tags = {} else: db_tags = db_tags["value"] return db_tags @classmethod def getTags(cls, onlyGlobal=False, **kwargs): """ Returns tags defined in settings. Returns: If none are defined returns {"todo":"orange", "pwned":"red", "Interesting":"dark green", "Uninteresting":"sky blue", "neutral":"white"} otherwise returns a dict with defined key values """ apiclient = APIClient.getInstance() if kwargs.get("ignoreCache", False): #Check if ignore cache is true cls.tags_cache = None if cls.tags_cache is not None and not onlyGlobal: return cls.tags_cache cls.tags_cache = { "todo": "orange", "pwned": "red", "Interesting": "dark green", "Uninteresting": "sky blue", "neutral": "white" } try: global_tags = apiclient.getSettings({"key": "tags"}) except ErrorHTTP: global_tags = None if global_tags is not None: if isinstance(global_tags["value"], dict): global_tags = global_tags["value"] elif isinstance(global_tags["value"], str): global_tags = json.loads(global_tags["value"]) if global_tags is None: global_tags = {} if not onlyGlobal: try: db_tags = cls.getPentestTags() except ErrorHTTP: db_tags = {} cls.tags_cache = {**global_tags, **db_tags} return cls.tags_cache return global_tags @classmethod def getPentestTypes(cls): """ Returns pentest types and associeted defect type defined in settings. Returns: If none are defined returns {"Web":["Base", "Application", "Data", "Policy"], "LAN":["Infrastructure", "Active Directory", "Data", "Policy"]} otherwise returns a dict with defined key values """ apiclient = APIClient.getInstance() if cls.__pentest_types is None: pentest_types = apiclient.getSettings({"key": "pentest_types"}) if pentest_types is not None: if isinstance(pentest_types["value"], str): cls.__pentest_types = json.loads(pentest_types["value"]) elif isinstance(pentest_types["value"], dict): cls.__pentest_types = pentest_types["value"] else: cls.__pentest_types = { "Web": ["Base", "Application", "Data", "Policy"], "LAN": [ "Infrastructure", "Active Directory", "Data", "Policy" ] } else: cls.__pentest_types = { "Web": ["Base", "Application", "Data", "Policy"], "LAN": ["Infrastructure", "Active Directory", "Data", "Policy"] } return cls.__pentest_types def getTerms(self): """ Returns terminals configured with a command execution option Returns: If none are defined returns ['''gnome-terminal --window --''', '''xfce4-terminal -x'', '''xterm -hold -e''', '''konsole --noclose -e'''] otherwise returns a list with defined values """ self._reloadLocalSettings() return self.local_settings.get("terms", [ """gnome-terminal --window --""", """xfce4-terminal -x""", "xterm -e", "konsole --noclose -e" ]) def getFavoriteTerm(self): """ Returns favorite terminal configured Returns: If none are defined returns first in the list of terms Otherwise returns the favorite terminal configured """ self._reloadLocalSettings() fav = self.local_settings.get("fav_term", None) if fav is None: terms = self.getTerms() for term in terms: term_name = term.split(" ")[0].strip() if which(term_name): fav = term_name return fav def isTrapCommand(self): self._reloadLocalSettings() return self.local_settings.get("trap_commands", False) def setTrapCommand(self): self._reloadLocalSettings() self.local_settings["trap_commands"] = self.visual_trap_commands.get() self.saveLocalSettings() def setFavoriteTerm(self): """ Change favorite term """ self._reloadLocalSettings() self.local_settings["fav_term"] = self.box_favorite_term.get() self.saveLocalSettings() def _reloadLocalSettings(self): """ Reload local settings from local conf file """ try: with open(self.confdir, "r") as f: self.local_settings = json.loads(f.read()) except json.JSONDecodeError: self.local_settings = {} except IOError: self.local_settings = {} def _reloadDbSettings(self): """ Reload pentest database settings from pentest database """ apiclient = APIClient.getInstance() dbSettings = apiclient.find("settings", {}) if dbSettings is None: dbSettings = {} self.__class__.tags_cache = None for settings_dict in dbSettings: try: self.db_settings[settings_dict["key"]] = settings_dict["value"] except KeyError: pass def _reloadGlobalSettings(self): """ Reload pentest database settings from pollenisator database """ apiclient = APIClient.getInstance() globalSettings = apiclient.getSettings() self.__class__.tags_cache = None for settings_dict in globalSettings: self.global_settings[settings_dict["key"]] = settings_dict["value"] def reloadSettings(self): """ Reload local, database and global settings. """ self._reloadLocalSettings() self._reloadDbSettings() self._reloadGlobalSettings() def reloadUI(self): """ Reload all settings and refresh view with values """ self.reloadSettings() self.visual_include_all_domains.set( self.db_settings.get("include_all_domains", False)) self.visual_include_domains_with_ip_in_scope.set( self.db_settings.get("include_domains_with_ip_in_scope", False)) self.visual_include_domains_with_topdomain_in_scope.set( self.db_settings.get("include_domains_with_topdomain_in_scope", False)) self.visual_search_show_hidden.set( self.local_settings.get("search_show_hidden", True)) self.visual_search_exact_match.set( self.local_settings.get("search_exact_match", False)) self.visual_trap_commands.set( self.local_settings.get("trap_commands", False)) self.text_terms.delete('1.0', tk.END) terms_cmd = self.getTerms() self.text_terms.insert(tk.INSERT, "\n".join(terms_cmd)) #self.text_pentesters.delete('1.0', tk.END) #self.text_pentesters.insert( # tk.INSERT, "\n".join( # self.db_settings.get("pentesters", []))) self.pentesters_treevw.reset() pentesters_as_list = self.db_settings.get("pentesters", [""]) dict_for_tw = {} for pentester in pentesters_as_list: dict_for_tw[pentester] = tuple([pentester]) self.pentesters_treevw.recurse_insert(dict_for_tw) terms_name = [term_cmd.split(" ")[0] for term_cmd in terms_cmd] self.box_favorite_term.config(values=terms_name) fav_term = self.getFavoriteTerm() if fav_term in terms_name: self.box_favorite_term.set(fav_term) self.box_pentest_type.set(self.db_settings.get("pentest_type", "None")) self.text_pentest_types.delete('1.0', tk.END) pentestTypes = Settings.getPentestTypes() buffer = "" for pentestType, pentestTypeDefectTypes in pentestTypes.items(): buffer += pentestType + " : " + ( ", ".join(pentestTypeDefectTypes)) + "\n" self.text_pentest_types.insert(tk.INSERT, buffer) self.text_tags.delete('1.0', tk.END) tagsRegistered = Settings.getTags(onlyGlobal=True) buffer = "" for tagName, tagColor in tagsRegistered.items(): buffer += tagName + " : " + tagColor + "\n" self.text_tags.insert(tk.INSERT, buffer) self.text_db_tags.delete('1.0', tk.END) tagsPentestRegistered = Settings.getPentestTags() buffer = "" for tagName, tagColor in tagsPentestRegistered.items(): buffer += tagName + " : " + tagColor + "\n" self.text_db_tags.insert(tk.INSERT, buffer) self.canvas.bind('<Enter>', self.boundToMousewheel) self.canvas.bind('<Leave>', self.unboundToMousewheel) self.canvas.update() self.canvas.create_window((0, 0), window=self.settingsFrame, anchor='nw') self.canvas.configure(scrollregion=self.canvas.bbox("all")) self.canvas.configure(yscrollcommand=self.myscrollbar.set) def saveLocalSettings(self): """ Save local settings to conf file """ with open(self.confdir, "w") as f: f.write(json.dumps(self.local_settings)) def savePentestSettings(self): apiclient = APIClient.getInstance() settings = apiclient.find("settings") existing_settings = {} for setting in settings: existing_settings[setting["key"]] = setting for k, v in self.db_settings.items(): if k in existing_settings: if k == "tags": for line_key, line_value in v.items(): tag, color = line_key, line_value if tag not in existing_settings["tags"]["value"]: apiclient.registerTag(tag, color, False) else: apiclient.updateTag(tag, color, False) for tag in existing_settings["tags"].get("value", {}): if tag not in v: apiclient.unregisterTag(tag, False) else: apiclient.updateInDb(apiclient.getCurrentPentest(), "settings", {"key": k}, {"$set": { "value": v }}) def save(self): """ Save all the settings (local, database and global) """ apiclient = APIClient.getInstance() for k, v in self.global_settings.items(): if apiclient.getSettings({"key": k}) is None: apiclient.createSetting(k, v) else: apiclient.updateSetting(k, v) self.savePentestSettings() self.saveLocalSettings() self.reloadUI() def _onMousewheel(self, event): """Scroll the settings canvas Args: event: scroll info filled when scroll event is triggered""" if event.num == 5 or event.delta == -120: count = 1 if event.num == 4 or event.delta == 120: count = -1 self.canvas.yview_scroll(count, "units") def boundToMousewheel(self, _event): """Called when the main view canvas is focused. Bind the command scrollbar button on linux to the main view canvas Args: _event: not used but mandatory""" self.canvas.bind_all("<Button-4>", self._onMousewheel) self.canvas.bind_all("<Button-5>", self._onMousewheel) def unboundToMousewheel(self, _event): """Called when the main view canvas is unfocused. Unbind the command scrollbar button on linux to the main view canvas Args: _event: not used but mandatory""" self.canvas.unbind_all("<Button-4>") self.canvas.unbind_all("<Button-5>") def initUI(self, parent): """Create settings widgets and initialize them Args: parent: parent tkinter container widget""" if self.visual_include_all_domains is not None: # Already built self.reloadUI() return self.canvas = tk.Canvas(parent, bg="white") self.settingsFrame = ttk.Frame(self.canvas) self.myscrollbar = tk.Scrollbar(parent, orient="vertical", command=self.canvas.yview) self.myscrollbar.grid(column=1, row=0, sticky="ns") self.canvas.grid(column=0, row=0, sticky="nsew") parent.columnconfigure(0, weight=1) parent.rowconfigure(0, weight=1) self.visual_include_all_domains = tk.BooleanVar() self.visual_include_domains_with_ip_in_scope = tk.BooleanVar() self.visual_include_domains_with_topdomain_in_scope = tk.BooleanVar() self.visual_search_show_hidden = tk.BooleanVar() self.visual_search_exact_match = tk.BooleanVar() self.visual_trap_commands = tk.BooleanVar() lbl_domains = ttk.LabelFrame(self.settingsFrame, text="Discovered domains options:") lbl_domains.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W, fill=tk.X, expand=tk.YES) chkbox_include_domains_with_ip_in_scope = ttk.Checkbutton( lbl_domains, text="Check if discovered subdomains ips are in scope", variable=self.visual_include_domains_with_ip_in_scope) chkbox_include_domains_with_ip_in_scope.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W) chkbox_include_domains_with_topdomain_in_scope = ttk.Checkbutton( lbl_domains, text= "Check if discovered subdomains have a top domain already in scope", variable=self.visual_include_domains_with_topdomain_in_scope) chkbox_include_domains_with_topdomain_in_scope.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W) chkbox_include_all_domains = ttk.Checkbutton( lbl_domains, text="/!\\ Include every domain found in scope", variable=self.visual_include_all_domains) chkbox_include_all_domains.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W) frame_term = ttk.LabelFrame(self.settingsFrame, text="Local terminals:") self.text_terms = tk.scrolledtext.ScrolledText(frame_term, relief=tk.SUNKEN, height=4, width=130, font=("Sans", 10)) self.text_terms.pack(side=tk.TOP, fill=tk.X, pady=5) chkbox_trap_commands = ttk.Checkbutton( frame_term, text="Trap every command (instead of using pollex)", variable=self.visual_trap_commands) chkbox_trap_commands.pack(side=tk.TOP, pady=5) frame_fav_term = ttk.Frame(frame_term) lbl_fav_term = ttk.Label(frame_fav_term, text="Favorite term:") lbl_fav_term.grid(row=0, column=0, sticky=tk.E, pady=5) self.box_favorite_term = ttk.Combobox(frame_fav_term, values=(self.getTerms()), state="readonly") self.box_favorite_term.grid(row=0, column=1, sticky=tk.W, pady=5) frame_fav_term.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W, fill=tk.X, expand=tk.YES) frame_term.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W, fill=tk.X, expand=tk.YES) lbl_SearchBar = ttk.LabelFrame(self.settingsFrame, text="Search settings:") lbl_SearchBar.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W, fill=tk.X, expand=tk.YES) chkbox_search_show_hidden = ttk.Checkbutton( lbl_SearchBar, text="Show hidden objects", variable=self.visual_search_show_hidden) chkbox_search_show_hidden.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W) chkbox_search_exact_match = ttk.Checkbutton( lbl_SearchBar, text="Exact match", variable=self.visual_search_exact_match) chkbox_search_exact_match.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W) lblframe_pentest_params = ttk.LabelFrame(self.settingsFrame, text="Pentest parameters:") lblframe_pentest_params.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W, fill=tk.X, expand=tk.YES) lbl_pentest_type = ttk.Label(lblframe_pentest_params, text="Pentest type:") lbl_pentest_type.grid(row=0, column=0, sticky=tk.E) self.box_pentest_type = ttk.Combobox( lblframe_pentest_params, values=tuple(Settings.getPentestTypes().keys()), state="readonly") self.box_pentest_type.grid(row=1, column=1, sticky=tk.W) # self.text_pentesters = tk.scrolledtext.ScrolledText( # lblframe_pentest_params, relief=tk.SUNKEN, height=3, font = ("Sans", 10)) # lbl_pentesters = ttk.Label( # lblframe_pentest_params, text="Pentester names:") # lbl_pentesters.grid(row=2, column=0, sticky=tk.E) # self.text_pentesters.grid(row=2, column=1, sticky=tk.W, pady=5) form_pentesters_panel = ttk.Frame(self.settingsFrame) self.form_pentesters = FormPanel(side=tk.TOP, fill=tk.X, pady=5) self.form_pentesters.addFormSearchBar("Pentester search", self.searchCallback, self.form_pentesters, side=tk.TOP) self.form_pentesters.addFormLabel("Pentesters added", side=tk.LEFT) self.pentesters_treevw = self.form_pentesters.addFormTreevw( "Additional pentesters names", ["Additional pentesters names"], (""), height=30, width=200, pady=5, fill=tk.X, side=tk.RIGHT) self.form_pentesters.constructView(form_pentesters_panel) form_pentesters_panel.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W, fill=tk.X, expand=tk.YES) lblframe_global_params = ttk.LabelFrame(self.settingsFrame, text="Other parameters:") lblframe_global_params.pack(padx=10, pady=10, side=tk.TOP, anchor=tk.W, fill=tk.X, expand=tk.YES) lbl_pentest_types = ttk.Label(lblframe_global_params, text="Pentests possible types:") lbl_pentest_types.grid(row=0, column=0, sticky=tk.E) self.text_pentest_types = tk.scrolledtext.ScrolledText( lblframe_global_params, relief=tk.SUNKEN, height=6, font=("Sans", 10)) self.text_pentest_types.grid(row=0, column=1, sticky=tk.W) lbl_tags = ttk.Label(lblframe_global_params, text="Registered tags:") lbl_tags.grid(row=1, column=0, sticky=tk.E) self.text_tags = tk.scrolledtext.ScrolledText(lblframe_global_params, relief=tk.SUNKEN, height=6, font=("Sans", 10)) self.text_tags.grid(row=1, column=1, sticky=tk.W) lbl_db_tags = ttk.Label(lblframe_global_params, text="Pentest only tags:") lbl_db_tags.grid(row=2, column=0, sticky=tk.E) self.text_db_tags = tk.scrolledtext.ScrolledText( lblframe_global_params, relief=tk.SUNKEN, height=6, font=("Sans", 10)) self.text_db_tags.grid(row=2, column=1, sticky=tk.W) btn_save = ttk.Button(parent, text="Save", command=self.on_ok) btn_save.grid(row=3, column=0, padx=10, pady=10, sticky="s") self.settingsFrame.pack(fill=tk.BOTH, expand=1) #self.reloadUI() def searchCallback(self, searchreq): apiclient = APIClient.getInstance() users = apiclient.searchUsers(searchreq) if users is None: return [], "Invalid response from API" ret = [{ "title": user, "additional pentesters names": user } for user in users] return ret, "" def on_ok(self): """Callback on click save button. loads some data and calls save. Args: parent: parent tkinter container widget""" self.db_settings[ "include_all_domains"] = self.visual_include_all_domains.get() == 1 self.db_settings[ "include_domains_with_ip_in_scope"] = self.visual_include_domains_with_ip_in_scope.get( ) == 1 self.db_settings["pentest_type"] = self.box_pentest_type.get() self.db_settings[ "include_domains_with_topdomain_in_scope"] = self.visual_include_domains_with_topdomain_in_scope.get( ) == 1 self.db_settings["pentesters"] = [] form_values = self.form_pentesters.getValue() form_values_as_dicts = ViewElement.list_tuple_to_dict(form_values) self.db_settings["pentesters"] = [ x for x in form_values_as_dicts["Additional pentesters names"] if x != "" ] self.db_settings["tags"] = {} for tagRegistered in self.text_db_tags.get('1.0', tk.END).split("\n"): if tagRegistered.strip() != "": line_splitted = tagRegistered.strip().split(":") if len(line_splitted) == 2: self.db_settings["tags"][ line_splitted[0].strip()] = line_splitted[1].strip() self.local_settings[ "search_show_hidden"] = self.visual_search_show_hidden.get() == 1 self.local_settings[ "search_exact_match"] = self.visual_search_exact_match.get() == 1 self.local_settings["terms"] = [ x.strip() for x in self.text_terms.get('1.0', tk.END).split("\n") if x.strip() != "" ] self.local_settings["fav_term"] = self.box_favorite_term.get().strip() self.local_settings["trap_commands"] = self.visual_trap_commands.get( ) == 1 self.global_settings["pentest_types"] = {} for type_of_pentest in self.text_pentest_types.get('1.0', tk.END).split("\n"): if type_of_pentest.strip() != "": line_splitted = type_of_pentest.strip().split(":") if len(line_splitted) == 2: typesOfDefects = list( map(lambda x: x.strip(), line_splitted[1].split(","))) self.global_settings["pentest_types"][ line_splitted[0].strip()] = typesOfDefects self.global_settings["tags"] = {} for tagRegistered in self.text_tags.get('1.0', tk.END).split("\n"): if tagRegistered.strip() != "": line_splitted = tagRegistered.strip().split(":") if len(line_splitted) == 2: self.global_settings["tags"][ line_splitted[0].strip()] = line_splitted[1].strip() self.save() tkinter.messagebox.showinfo("Settings", "Settings saved.") def getPentestType(self): """Return selected database pentest type. Returns: Open database pentest type. string "None" if not defined""" return self.db_settings.get("pentest_type", "None") def getPentesters(self): """Return a list of pentesters registered for open pentest database Returns: List of pentesters names""" return self.db_settings.get("pentesters", []) def notify(self, db, iid, action): if db == "pollenisator": self._reloadGlobalSettings() else: self._reloadDbSettings()
class ViewElement(object): """ Defines a basic view to be inherited. Those functions are generic entry points to models. Most of them should not be redefined in other Views. Attributes: icon: icon name to show in treeview. Icon filename must be in icon directory cachedClassIcon: a cached loaded PIL image icon of ViewElement.icon. Starts as None. """ icon = 'undefined.png' cachedClassIcon = None def __init__(self, appTw, appViewFrame, mainApp, controller): """Constructor Args: appTw: a PollenisatorTreeview instance to put this view in appViewFrame: an view frame to build the forms in. mainApp: the Application instance controller: a CommandController for this view. """ self.appliTw = appTw self.appliViewFrame = appViewFrame self.mainApp = mainApp self.controller = controller self.form = FormPanel() @classmethod def getClassIcon(cls): """ Load the class icon in cache if it is not yet done, and returns it Return: Returns the ImageTk.PhotoImage icon representing this class . """ from PIL import Image, ImageTk if cls.cachedClassIcon == None: abs_path = os.path.dirname(os.path.abspath(__file__)) path = os.path.join(abs_path, "../../icon/" + cls.icon) cls.cachedClassIcon = ImageTk.PhotoImage(Image.open(path)) return cls.cachedClassIcon def getIcon(self): """ Load the object icon in cache if it is not yet done, and returns it Return: Returns the icon representing this object. """ return self.__class__.getClassIcon() def addChildrenBaseNodes(self, newNode): """ Add to the given node from a treeview the mandatory childrens. Will be redefined in children. Args: newNode: the newly created node we want to add children to. """ # pass def delete(self, _event=None, showWarning=True): """ Entry point to the model doDelete function. Args: _event: automatically filled if called by an event. Not used showWarning: a boolean. If true, the user will be asked a confirmation before supression. Default to True. """ ret = True if showWarning: ret = tkinter.messagebox.askokcancel( "Delete", "You are going to delete this element, do you want to continue?" ) if (ret): self.controller.doDelete() def update(self, event=None): """ Entry point to the model doUpdate function. Args: event: automatically filled if called by an event. Holds info on update clicked widget. Returns: * a boolean to shwo success or failure * an empty message on success, an error message on failure """ res, msg = self.form.checkForm() if (res): form_values = self.form.getValue() form_values_as_dicts = ViewElement.list_tuple_to_dict(form_values) self.controller.doUpdate(form_values_as_dicts) if event is not None: caller = event.widget toast = ChildDialogToast(self.appliViewFrame, "Done", x=caller.winfo_rootx(), y=caller.winfo_rooty() + caller.winfo_reqheight(), width=caller.winfo_reqwidth()) toast.show() return True, "" else: tkinter.messagebox.showwarning("Form not validated", msg, parent=self.appliViewFrame) return False, msg def insert(self, _event=None): """ Entry point to the model doInsert function. Args: _event: automatically filled if called by an event. Not used Returns: * a boolean to shwo success or failure * an empty message on success, an error message on failure """ res, msg = self.form.checkForm() if (res): form_values = self.form.getValue() form_values_as_dicts = ViewElement.list_tuple_to_dict(form_values) res, nbErrors = self.controller.doInsert(form_values_as_dicts) if not res: msg = "This element cannot be inserted, check for conflicts with existings elements." tkinter.messagebox.showerror("Insertion failed", msg, parent=self.appliViewFrame) return False, msg else: if nbErrors > 0: msg = str(len(res)) + " were inserted, " + str( nbErrors ) + " were not to avoid conflicts or out of wave elements." tkinter.messagebox.showwarning( "Insertion succeeded with warnings", msg, parent=self.appliViewFrame) return True, msg else: return True, "" else: tkinter.messagebox.showwarning("Form not validated", msg, parent=self.appliViewFrame) return False, msg def tagClicked(self, name): """Callback intermediate for tag clicked Ensure that the tag name clicked is added to View item Args: name: a tag name """ return lambda _event: self.tagButtonClicked(name) def tagButtonClicked(self, name): """Callback for tag button clicked Ensure that the tag name clicked is set to View item Args: name: a tag name """ self.controller.setTags([name]) def completeModifyWindow(self, editable=True, addTags=True): """ Add the buttons for an update window. -Submit button that validates the form with the update function. -Delete button that asks the user to delete the object with the delete function. """ pan = self.form.addFormPanel() if editable: pan.addFormButton("Submit", self.update) pan.addFormButton("Delete", self.delete) if addTags: registeredTags = Settings.Settings.getTags() keys = list(registeredTags.keys()) column = 0 item_no = 0 listOfLambdas = [ self.tagClicked(keys[i]) for i in range(len(keys)) ] for registeredTag, color in registeredTags.items(): if column == 0: panTags = self.form.addFormPanel(pady=0) s = ttk.Style(self.mainApp) try: # CHECK IF COLOR IS VALID ttk.Label(self.mainApp, background=color) except tkinter.TclError as e: #color incorrect color = "white" s.configure("" + color + "V.TButton", background=color, foreground="black", borderwidth=1, bordercolor="black") s.map("" + color + "V.TButton", foreground=[('active', "dark gray")], background=[('active', color)]) s.layout("" + color + "V.TButton", [('VButton.border', { 'sticky': 'nswe', 'border': '1', 'children': [('Button.focus', { 'sticky': 'nswe', 'children': [('Button.padding', { 'sticky': 'ew', 'children': [('Button.label', { 'sticky': 'ew' })] })] })] })]) btn_tag = panTags.addFormButton(registeredTag, listOfLambdas[item_no], side="left", padx=1, pady=0) btn_tag.configure(style="" + color + "V.TButton") column += 1 item_no += 1 if column == 4: column = 0 self.showForm() def showForm(self): """Resets the application view frame and start displaying the form in it """ for widget in self.appliViewFrame.winfo_children(): widget.destroy() self.form.constructView(self.appliViewFrame) def completeInsertWindow(self): """ Add the button for an insert window. -Insert button that validate the form with the insert function. """ pan = self.form.addFormPanel() pan.addFormButton("Insert", self.insert) for widget in self.appliViewFrame.winfo_children(): widget.destroy() self.form.constructView(self.appliViewFrame) def hide(self): """Tells the application treeview to hide this node """ self.appliTw.hide(str(self.controller.getDbId())) def unhide(self): """Tells the application treeview to unhide this node """ self.appliTw.unhide(self) def __str__(self): """ Return the __str__ method of the model """ return str(self.controller.getModelRepr()) @classmethod def DbToTreeviewListId(cls, parent_db_id): """Converts a mongo Id to a unique string identifying a list of view elemnt given its parent Args: parent_db_id: the parent node mongo ID Returns: A string that should be unique to describe the parent list of viewelement node """ return str(parent_db_id) def getParentId(self): """ Return the id of the parent node in treeview. Returns: return the model parent id DbToTreeviewListId """ return self.controller.getParentId() def getParentNode(self): """ Return the parent node in treeview. """ return self.__class__.DbToTreeviewListId(self.controller.getParentId()) def updateReceived(self): """Called when any view element update is received by notification. Resets the node tags according to database and hide it if "hidden" is in tags """ if self.controller.getDbId() is None: return tags = self.controller.getTags() try: self.appliTw.item(str(self.controller.getDbId()), tags=tags) except TclError: pass if "hidden" in tags: self.hide() def insertReceived(self): """Called when any view element insert is received by notificaiton To be overriden """ pass def key(self): """Returns a key for sorting this node Returns: string, basic key: string so alphanumerical sorting will be used """ return str(self.controller.getModelRepr()) @classmethod def list_tuple_to_dict(cls, list_of_tuple): """Transforms a list of 2-tuple to a dictionnary Args: list_of_tuple: a 2-tuple with (key, value) Returns: A dictionnary with all key-values pair inserted """ ret = dict() for key, value in list_of_tuple: ret[key] = value return ret
class ChildDialogEditPassword: """ Open a child dialog of a tkinter application to ask a user to reset its password. """ def __init__(self, parent, username, askOldPwd=True): """ Open a child dialog of a tkinter application to ask the new password and possibly the old Args: parent: the tkinter parent view to use for this window construction. username: The username to reset the password of askOldPwd : a boolean to use changePassword (user api) or resetPassword (admin api) """ self.parent = parent self.app = tk.Toplevel(parent) self.askOldPwd = askOldPwd self.app.title("Change " + str(username) + " password") appFrame = ttk.Frame(self.app) self.form = FormPanel() self.form.addFormLabel("Username") self.form.addFormStr("Username", ".+", default=username, readonly=True) if askOldPwd: self.form.addFormLabel("Old password") self.form.addFormStr("Old password", ".+", show="*") self.form.addFormLabel("New Password") self.form.addFormStr( "New password", ".{8,}", show="*", error_msg="New password must be at least 8 characters long") self.form.addFormButton("OK", self.onOk) self.rvalue = None self.form.constructView(appFrame) appFrame.pack(ipadx=10, ipady=10) try: self.app.wait_visibility() self.app.transient(parent) self.app.focus_force() self.app.grab_set() self.app.lift() except tk.TclError: pass def onOk(self, _event=None): """Called the ok button is pressed. Args: _event: not used but mandatory""" res, msg = self.form.checkForm() apiclient = APIClient.getInstance() success = False if res: form_values = self.form.getValue() form_values_as_dicts = ViewElement.list_tuple_to_dict(form_values) username = form_values_as_dicts["Username"] newPwd = form_values_as_dicts["New password"] if self.askOldPwd: oldPwd = form_values_as_dicts["Old password"] msg = apiclient.changeUserPassword(oldPwd, newPwd) else: msg = apiclient.resetPassword(username, newPwd) if msg != "": tk.messagebox.showwarning("Change password", msg, parent=self.app) else: tk.messagebox.showwarning("Form not validated", msg, parent=self.app) self.app.destroy()
class ChildDialogFileParser: """ Open a child dialog of a tkinter application to ask details about existing files parsing. """ def __init__(self, default_path=""): """ Open a child dialog of a tkinter application to ask details about existing files parsing. Args: default_path: a default path to be added """ self.app = tkinterDnD.Tk() Utils.setStyle(self.app) self.app.title("Upload result file") self.rvalue = None self.default = default_path appFrame = ttk.Frame(self.app) apiclient = APIClient.getInstance() self.form = FormPanel() self.form.addFormLabel("Import one file or choose a directory", "", side=tk.TOP) self.form.addFormFile("File", ".+", self.default, width=50, side=tk.TOP, mode="file|directory") self.form.addFormLabel("Plugins", side=tk.TOP) self.form.addFormCombo("Plugin", ["auto-detect"] + apiclient.getPlugins(), "auto-detect", side=tk.TOP) self.form.addFormButton("Parse", self.onOk, side=tk.RIGHT) self.form.constructView(appFrame) appFrame.pack(ipadx=10, ipady=10) try: self.app.wait_visibility() self.app.focus_force() self.app.grab_set() self.app.lift() except tk.TclError: pass self.app.mainloop() self.app.destroy() def onOk(self, _event=None): """ Called when the user clicked the validation button. launch parsing with selected parser on selected file/directory. Close the window. Args: _event: not used but mandatory """ res, msg = self.form.checkForm() if not res: tk.messagebox.showwarning("Form not validated", msg, parent=self.app) return notes = None tags = None form_values = self.form.getValue() form_values_as_dicts = ViewElement.list_tuple_to_dict(form_values) files_paths = form_values_as_dicts["File"] plugin = form_values_as_dicts["Plugin"] files = set() for filepath in files_paths: if os.path.isdir(filepath): # r=root, d=directories, f = files for r, _d, f in os.walk(filepath): for fil in f: files.add(os.path.join(r, fil)) else: files.add(filepath) dialog = ChildDialogProgress( self.app, "Importing files", "Importing " + str(len(files)) + " files. Please wait for a few seconds.", 200, "determinate") dialog.show(len(files)) # LOOP ON FOLDER FILES results = {} apiclient = APIClient.getInstance() for f_i, file_path in enumerate(files): additional_results = apiclient.importExistingResultFile( file_path, plugin) for key, val in additional_results.items(): results[key] = results.get(key, 0) + val dialog.update(f_i) dialog.destroy() # DISPLAY RESULTS presResults = "" filesIgnored = 0 for key, value in results.items(): presResults += str(value) + " " + str(key) + ".\n" if key == "Ignored": filesIgnored += 1 if plugin == "auto-detect": if filesIgnored > 0: tk.messagebox.showwarning("Auto-detect ended", presResults, parent=self.app) else: tk.messagebox.showinfo("Auto-detect ended", presResults, parent=self.app) else: if filesIgnored > 0: tk.messagebox.showwarning("Parsing ended", presResults, parent=self.app) else: tk.messagebox.showinfo("Parsing ended", presResults, parent=self.app) self.rvalue = None self.app.quit()