# HG changeset patch # User Edouard Tisserant # Date 1542358806 -3600 # Node ID 869a61616b4247e0a21ee53117653f609cfce9eb # Parent 4262256e1d289479a21e34cbc488e5034d4ac055 Renamed IDManager control into IDBrowser, because dialog will be named IDManager diff -r 4262256e1d28 -r 869a61616b42 connectors/SchemeEditor.py --- a/connectors/SchemeEditor.py Wed Nov 14 14:09:18 2018 +0100 +++ b/connectors/SchemeEditor.py Fri Nov 16 10:00:06 2018 +0100 @@ -9,7 +9,7 @@ from functools import partial import wx -from controls.IDManager import IDManager +from controls.IDBrowser import IDBrowser class SchemeEditor(wx.Panel): def __init__(self, scheme, parent, *args, **kwargs): @@ -34,7 +34,7 @@ if self.EnableIDSelector: self.mainsizer = wx.FlexGridSizer(cols=2, hgap=10, vgap=10) self.mainsizer.AddSizer(self.fieldsizer) - self.idselector = IDManager( + self.idselector = IDBrowser( self, parent.ctr, # use a callafter, as editor can be deleted by calling SetURI partial(wx.CallAfter, parent.SetURI), diff -r 4262256e1d28 -r 869a61616b42 controls/IDBrowser.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/controls/IDBrowser.py Fri Nov 16 10:00:06 2018 +0100 @@ -0,0 +1,226 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# See COPYING file for copyrights details. + +from __future__ import absolute_import +import os +import wx +import wx.dataview as dv +import json + +def _GetInitialData(psk_path): + # [(ID, Desc, LastKnownURI, LastConnect) + data = [] + + data_path = os.path.join(psk_path, 'management.json') + + if os.path.isdir(psk_path): + # load known keys metadata + # {ID:(Desc, LastKnownURI, LastConnect)} + recovered_data = json.loads(open(data_path).read()) \ + if os.path.exists(data_path) else {} + + # go through all secret files available an build data + # out of data recoverd from json and list of secret. + # this implicitly filters IDs out of metadata who's + # secret is missing + psk_files = os.listdir(psk_path) + for filename in psk_files: + if filename.endswith('.secret'): + ID = filename[:-7] # strip filename extension + meta = recovered_data.get(ID, + ['', # default description + None, # last known URI + None]) # last connection date + + data.append([ID]+meta) + return data + +def _DeleteID(psk_path, ID): + secret_path = os.path.join(psk_path, ID+'.secret') + os.remove(secret_path) + +def _SaveData(psk_path, data): + if not os.path.isdir(psk_path): + os.mkdir(psk_path) + data_path = os.path.join(psk_path, 'management.json') + to_store = {row[0]:row[1:] for row in data} + with open(data_path, 'w') as f: + f.write(json.dumps(to_store)) + +class IDBrowserModel(dv.PyDataViewIndexListModel): + def __init__(self, psk_path, columncount): + self.psk_path = psk_path + self.columncount = columncount + self.data = _GetInitialData(psk_path) + dv.PyDataViewIndexListModel.__init__(self, len(self.data)) + + def _saveData(self): + _SaveData(self.psk_path, self.data) + + def GetColumnType(self, col): + return "string" + + def GetValueByRow(self, row, col): + return self.data[row][col] + + def SetValueByRow(self, value, row, col): + self.data[row][col] = value + self._saveData() + + def GetColumnCount(self): + return len(self.data[0]) if self.data else self.columncount + + def GetCount(self): + return len(self.data) + + def GetAttrByRow(self, row, col, attr): + if col == 3: + attr.SetColour('blue') + attr.SetBold(True) + return True + return False + + + def Compare(self, item1, item2, col, ascending): + if not ascending: # swap sort order? + item2, item1 = item1, item2 + row1 = self.GetRow(item1) + row2 = self.GetRow(item2) + if col == 0: + return cmp(int(self.data[row1][col]), int(self.data[row2][col])) + else: + return cmp(self.data[row1][col], self.data[row2][col]) + + def DeleteRows(self, rows): + rows = list(rows) + rows.sort(reverse=True) + + for row in rows: + del self.data[row] + _DeleteID(self.psk_path, ID) + self.RowDeleted(row) + self._saveData() + + def AddRow(self, value): + self.data.append(value) + self.RowAppended() + self._saveData() + +colflags = dv.DATAVIEW_COL_RESIZABLE|dv.DATAVIEW_COL_SORTABLE +COL_ID,COL_URI,COL_DESC,COL_LAST = range(4) + +class IDBrowser(wx.Panel): + def __init__(self, parent, ctr, SelectURICallBack=None, SelectIDCallBack=None, **kwargs): + wx.Panel.__init__(self, parent, -1, size=(400,200)) + + self.isManager = SelectURICallBack is None and SelectIDCallBack is None + self.SelectURICallBack = SelectURICallBack + self.SelectIDCallBack = SelectIDCallBack + + dvStyle = wx.BORDER_THEME | dv.DV_ROW_LINES + if self.isManager : + # no multiple selection in selector mode + dvStyle |= dv.DV_MULTIPLE + self.dvc = dv.DataViewCtrl(self, style = dvStyle) + + args = lambda *a,**k:(a,k) + + ColumnsDesc = [ + args(_("ID"), COL_ID, width = 100), + args(_("Last URI"), COL_URI, width = 80), + args(_("Description"), COL_DESC, width = 200, + mode = dv.DATAVIEW_CELL_EDITABLE + if self.isManager + else dv.DATAVIEW_CELL_INERT), + args(_("Last connection"), COL_LAST, width = 100), + ] + + self.model = IDBrowserModel( + os.path.join(str(ctr.ProjectPath), 'psk'), + len(ColumnsDesc)) + self.dvc.AssociateModel(self.model) + + for a,k in ColumnsDesc: + self.dvc.AppendTextColumn(*a,**dict(k, flags = colflags)) + + # TODO : when select, + # - update ID field of scheme editor + # - enable use URI button + + self.Sizer = wx.BoxSizer(wx.VERTICAL) + self.Sizer.Add(self.dvc, 1, wx.EXPAND) + + btnbox = wx.BoxSizer(wx.HORIZONTAL) + if self.isManager : + + # deletion of secret and metadata + deleteButton = wx.Button(self, label=_("Delete ID")) + self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, deleteButton) + btnbox.Add(deleteButton, 0, wx.LEFT|wx.RIGHT, 5) + + # export all + exportButton = wx.Button(self, label=_("Export all")) + self.Bind(wx.EVT_BUTTON, self.OnExportButton, exportButton) + btnbox.Add(exportButton, 0, wx.LEFT|wx.RIGHT, 5) + + # import with a merge -> duplicates are asked for + importButton = wx.Button(self, label=_("Import")) + self.Bind(wx.EVT_BUTTON, self.OnImportButton, importButton) + btnbox.Add(importButton, 0, wx.LEFT|wx.RIGHT, 5) + + else : + # selector mode + # use last known URI button + # TODO : disable use URI button until something selected + self.useURIButton = wx.Button(self, label=_("Use last URI")) + self.Bind(wx.EVT_BUTTON, self.OnUseURIButton, self.useURIButton) + self.useURIButton.Disable() + btnbox.Add(self.useURIButton, 0, wx.LEFT|wx.RIGHT, 5) + + self.Sizer.Add(btnbox, 0, wx.TOP|wx.BOTTOM, 5) + self.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, self.OnSelectionChanged, self.dvc) + + + def OnDeleteButton(self, evt): + items = self.dvc.GetSelections() + rows = [self.model.GetRow(item) for item in items] + + # Ask if user really wants to delete + if wx.MessageBox(_('Are you sure to delete selected IDs?'), + _('Delete IDs'), + wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) != wx.YES: + return + + self.model.DeleteRows(rows) + + def OnSelectionChanged(self, evt): + if not self.isManager : + items = self.dvc.GetSelections() + somethingSelected = len(items) > 0 + self.useURIButton.Enable(somethingSelected) + if somethingSelected: + row = self.model.GetRow(items[0]) + ID = self.model.GetValueByRow(row, COL_ID) + self.SelectIDCallBack(ID) + + + def OnUseURIButton(self, evt): + row = self.model.GetRow(self.dvc.GetSelections()[0]) + URI = self.model.GetValueByRow(row, COL_URI) + if URI: + self.SelectURICallBack(URI) + + def OnExportButton(self, evt): + # TODO + wx.MessageBox(_('?'), + _('Mhe'), + wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) + + def OnImportButton(self, evt): + # TODO + wx.MessageBox(_('?'), + _('Mhe'), + wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) + diff -r 4262256e1d28 -r 869a61616b42 controls/IDManager.py --- a/controls/IDManager.py Wed Nov 14 14:09:18 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,226 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# See COPYING file for copyrights details. - -from __future__ import absolute_import -import os -import wx -import wx.dataview as dv -import json - -def _GetInitialData(psk_path): - # [(ID, Desc, LastKnownURI, LastConnect) - data = [] - - data_path = os.path.join(psk_path, 'management.json') - - if os.path.isdir(psk_path): - # load known keys metadata - # {ID:(Desc, LastKnownURI, LastConnect)} - recovered_data = json.loads(open(data_path).read()) \ - if os.path.exists(data_path) else {} - - # go through all secret files available an build data - # out of data recoverd from json and list of secret. - # this implicitly filters IDs out of metadata who's - # secret is missing - psk_files = os.listdir(psk_path) - for filename in psk_files: - if filename.endswith('.secret'): - ID = filename[:-7] # strip filename extension - meta = recovered_data.get(ID, - ['', # default description - None, # last known URI - None]) # last connection date - - data.append([ID]+meta) - return data - -def _DeleteID(psk_path, ID): - secret_path = os.path.join(psk_path, ID+'.secret') - os.remove(secret_path) - -def _SaveData(psk_path, data): - if not os.path.isdir(psk_path): - os.mkdir(psk_path) - data_path = os.path.join(psk_path, 'management.json') - to_store = {row[0]:row[1:] for row in data} - with open(data_path, 'w') as f: - f.write(json.dumps(to_store)) - -class IDManagerModel(dv.PyDataViewIndexListModel): - def __init__(self, psk_path, columncount): - self.psk_path = psk_path - self.columncount = columncount - self.data = _GetInitialData(psk_path) - dv.PyDataViewIndexListModel.__init__(self, len(self.data)) - - def _saveData(self): - _SaveData(self.psk_path, self.data) - - def GetColumnType(self, col): - return "string" - - def GetValueByRow(self, row, col): - return self.data[row][col] - - def SetValueByRow(self, value, row, col): - self.data[row][col] = value - self._saveData() - - def GetColumnCount(self): - return len(self.data[0]) if self.data else self.columncount - - def GetCount(self): - return len(self.data) - - def GetAttrByRow(self, row, col, attr): - if col == 3: - attr.SetColour('blue') - attr.SetBold(True) - return True - return False - - - def Compare(self, item1, item2, col, ascending): - if not ascending: # swap sort order? - item2, item1 = item1, item2 - row1 = self.GetRow(item1) - row2 = self.GetRow(item2) - if col == 0: - return cmp(int(self.data[row1][col]), int(self.data[row2][col])) - else: - return cmp(self.data[row1][col], self.data[row2][col]) - - def DeleteRows(self, rows): - rows = list(rows) - rows.sort(reverse=True) - - for row in rows: - del self.data[row] - _DeleteID(self.psk_path, ID) - self.RowDeleted(row) - self._saveData() - - def AddRow(self, value): - self.data.append(value) - self.RowAppended() - self._saveData() - -colflags = dv.DATAVIEW_COL_RESIZABLE|dv.DATAVIEW_COL_SORTABLE -COL_ID,COL_URI,COL_DESC,COL_LAST = range(4) - -class IDManager(wx.Panel): - def __init__(self, parent, ctr, SelectURICallBack=None, SelectIDCallBack=None, **kwargs): - wx.Panel.__init__(self, parent, -1, size=(400,200)) - - self.isManager = SelectURICallBack is None and SelectIDCallBack is None - self.SelectURICallBack = SelectURICallBack - self.SelectIDCallBack = SelectIDCallBack - - dvStyle = wx.BORDER_THEME | dv.DV_ROW_LINES - if self.isManager : - # no multiple selection in selector mode - dvStyle |= dv.DV_MULTIPLE - self.dvc = dv.DataViewCtrl(self, style = dvStyle) - - args = lambda *a,**k:(a,k) - - ColumnsDesc = [ - args(_("ID"), COL_ID, width = 100), - args(_("Last URI"), COL_URI, width = 80), - args(_("Description"), COL_DESC, width = 200, - mode = dv.DATAVIEW_CELL_EDITABLE - if self.isManager - else dv.DATAVIEW_CELL_INERT), - args(_("Last connection"), COL_LAST, width = 100), - ] - - self.model = IDManagerModel( - os.path.join(str(ctr.ProjectPath), 'psk'), - len(ColumnsDesc)) - self.dvc.AssociateModel(self.model) - - for a,k in ColumnsDesc: - self.dvc.AppendTextColumn(*a,**dict(k, flags = colflags)) - - # TODO : when select, - # - update ID field of scheme editor - # - enable use URI button - - self.Sizer = wx.BoxSizer(wx.VERTICAL) - self.Sizer.Add(self.dvc, 1, wx.EXPAND) - - btnbox = wx.BoxSizer(wx.HORIZONTAL) - if self.isManager : - - # deletion of secret and metadata - deleteButton = wx.Button(self, label=_("Delete ID")) - self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, deleteButton) - btnbox.Add(deleteButton, 0, wx.LEFT|wx.RIGHT, 5) - - # export all - exportButton = wx.Button(self, label=_("Export all")) - self.Bind(wx.EVT_BUTTON, self.OnExportButton, exportButton) - btnbox.Add(exportButton, 0, wx.LEFT|wx.RIGHT, 5) - - # import with a merge -> duplicates are asked for - importButton = wx.Button(self, label=_("Import")) - self.Bind(wx.EVT_BUTTON, self.OnImportButton, importButton) - btnbox.Add(importButton, 0, wx.LEFT|wx.RIGHT, 5) - - else : - # selector mode - # use last known URI button - # TODO : disable use URI button until something selected - self.useURIButton = wx.Button(self, label=_("Use last URI")) - self.Bind(wx.EVT_BUTTON, self.OnUseURIButton, self.useURIButton) - self.useURIButton.Disable() - btnbox.Add(self.useURIButton, 0, wx.LEFT|wx.RIGHT, 5) - - self.Sizer.Add(btnbox, 0, wx.TOP|wx.BOTTOM, 5) - self.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, self.OnSelectionChanged, self.dvc) - - - def OnDeleteButton(self, evt): - items = self.dvc.GetSelections() - rows = [self.model.GetRow(item) for item in items] - - # Ask if user really wants to delete - if wx.MessageBox(_('Are you sure to delete selected IDs?'), - _('Delete IDs'), - wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) != wx.YES: - return - - self.model.DeleteRows(rows) - - def OnSelectionChanged(self, evt): - if not self.isManager : - items = self.dvc.GetSelections() - somethingSelected = len(items) > 0 - self.useURIButton.Enable(somethingSelected) - if somethingSelected: - row = self.model.GetRow(items[0]) - ID = self.model.GetValueByRow(row, COL_ID) - self.SelectIDCallBack(ID) - - - def OnUseURIButton(self, evt): - row = self.model.GetRow(self.dvc.GetSelections()[0]) - URI = self.model.GetValueByRow(row, COL_URI) - if URI: - self.SelectURICallBack(URI) - - def OnExportButton(self, evt): - # TODO - wx.MessageBox(_('?'), - _('Mhe'), - wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) - - def OnImportButton(self, evt): - # TODO - wx.MessageBox(_('?'), - _('Mhe'), - wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) -