Edouard@2334: #!/usr/bin/env python Edouard@2334: # -*- coding: utf-8 -*- Edouard@2334: Edouard@2334: # See COPYING file for copyrights details. Edouard@2334: Edouard@2334: from __future__ import absolute_import Edouard@2334: import os Edouard@2334: import wx Edouard@2334: import wx.dataview as dv Edouard@2334: import json Edouard@2334: Edouard@2334: def _GetInitialData(psk_path): Edouard@2334: # [(ID, Desc, LastKnownURI, LastConnect) Edouard@2334: data = [] Edouard@2334: Edouard@2334: data_path = os.path.join(psk_path, 'management.json') Edouard@2334: Edouard@2334: if os.path.isdir(psk_path): Edouard@2334: # load known keys metadata Edouard@2334: # {ID:(Desc, LastKnownURI, LastConnect)} Edouard@2334: recovered_data = json.loads(open(data_path).read()) \ Edouard@2334: if os.path.exists(data_path) else {} Edouard@2334: Edouard@2334: # go through all secret files available an build data Edouard@2334: # out of data recoverd from json and list of secret. Edouard@2334: # this implicitly filters IDs out of metadata who's Edouard@2334: # secret is missing Edouard@2334: psk_files = os.listdir(psk_path) Edouard@2334: for filename in psk_files: Edouard@2334: if filename.endswith('.secret'): Edouard@2334: ID = filename[:-7] # strip filename extension Edouard@2334: meta = recovered_data.get(ID, Edouard@2334: ['', # default description Edouard@2334: None, # last known URI Edouard@2334: None]) # last connection date Edouard@2334: Edouard@2334: data.append([ID]+meta) Edouard@2334: return data Edouard@2334: Edouard@2334: def _DeleteID(psk_path, ID): Edouard@2334: secret_path = os.path.join(psk_path, ID+'.secret') Edouard@2334: os.remove(secret_path) Edouard@2334: Edouard@2334: def _SaveData(psk_path, data): Edouard@2334: if not os.path.isdir(psk_path): Edouard@2334: os.mkdir(psk_path) Edouard@2334: data_path = os.path.join(psk_path, 'management.json') Edouard@2334: to_store = {row[0]:row[1:] for row in data} Edouard@2334: with open(data_path, 'w') as f: Edouard@2334: f.write(json.dumps(to_store)) Edouard@2334: Edouard@2334: class IDManagerModel(dv.PyDataViewIndexListModel): Edouard@2334: def __init__(self, psk_path, columncount): Edouard@2334: self.psk_path = psk_path Edouard@2334: self.columncount = columncount Edouard@2334: self.data = _GetInitialData(psk_path) Edouard@2334: dv.PyDataViewIndexListModel.__init__(self, len(self.data)) Edouard@2334: Edouard@2334: def _saveData(self): Edouard@2334: _SaveData(self.psk_path, self.data) Edouard@2334: Edouard@2334: def GetColumnType(self, col): Edouard@2334: return "string" Edouard@2334: Edouard@2334: def GetValueByRow(self, row, col): Edouard@2334: return self.data[row][col] Edouard@2334: Edouard@2334: def SetValueByRow(self, value, row, col): Edouard@2334: self.data[row][col] = value Edouard@2334: self._saveData() Edouard@2334: Edouard@2334: def GetColumnCount(self): Edouard@2334: return len(self.data[0]) if self.data else self.columncount Edouard@2334: Edouard@2334: def GetCount(self): Edouard@2334: return len(self.data) Edouard@2334: Edouard@2334: def GetAttrByRow(self, row, col, attr): Edouard@2334: if col == 3: Edouard@2334: attr.SetColour('blue') Edouard@2334: attr.SetBold(True) Edouard@2334: return True Edouard@2334: return False Edouard@2334: Edouard@2334: Edouard@2334: def Compare(self, item1, item2, col, ascending): Edouard@2334: if not ascending: # swap sort order? Edouard@2334: item2, item1 = item1, item2 Edouard@2334: row1 = self.GetRow(item1) Edouard@2334: row2 = self.GetRow(item2) Edouard@2334: if col == 0: Edouard@2334: return cmp(int(self.data[row1][col]), int(self.data[row2][col])) Edouard@2334: else: Edouard@2334: return cmp(self.data[row1][col], self.data[row2][col]) Edouard@2334: Edouard@2334: def DeleteRows(self, rows): Edouard@2334: rows = list(rows) Edouard@2334: rows.sort(reverse=True) Edouard@2334: Edouard@2334: for row in rows: Edouard@2334: del self.data[row] Edouard@2334: _DeleteID(self.psk_path, ID) Edouard@2334: self.RowDeleted(row) Edouard@2334: self._saveData() Edouard@2334: Edouard@2334: def AddRow(self, value): Edouard@2334: self.data.append(value) Edouard@2334: self.RowAppended() Edouard@2334: self._saveData() Edouard@2334: Edouard@2334: colflags = dv.DATAVIEW_COL_RESIZABLE|dv.DATAVIEW_COL_SORTABLE Edouard@2334: Edouard@2334: class IDManager(wx.Panel): Edouard@2334: def __init__(self, parent, ctr, SelectURICallBack, *args, **kwargs): Edouard@2334: wx.Panel.__init__(self, parent, -1, size=(400,200)) Edouard@2334: Edouard@2334: self.isManager = SelectURICallBack is None Edouard@2334: self.SelectURICallBack = SelectURICallBack Edouard@2334: Edouard@2334: dvStyle = wx.BORDER_THEME | dv.DV_ROW_LINES Edouard@2334: if self.isManager : Edouard@2334: # no multiple selection in selector mode Edouard@2334: dvStyle |= dv.DV_MULTIPLE Edouard@2334: self.dvc = dv.DataViewCtrl(self, style = dvStyle) Edouard@2334: Edouard@2334: args = lambda *a,**k:(a,k) Edouard@2334: Edouard@2334: ColumnsDesc = [ Edouard@2334: args(_("ID"), 0, width = 100), Edouard@2334: args(_("Last URI"), 1, width = 80), Edouard@2334: args(_("Description"), 2, width = 200, Edouard@2334: mode = dv.DATAVIEW_CELL_EDITABLE Edouard@2334: if self.isManager Edouard@2334: else dv.DATAVIEW_CELL_INERT), Edouard@2334: args(_("Last connection"), 3, width = 100), Edouard@2334: ] Edouard@2334: Edouard@2334: self.model = IDManagerModel( Edouard@2334: os.path.join(str(ctr.ProjectPath), 'psk'), Edouard@2334: len(ColumnsDesc)) Edouard@2334: self.dvc.AssociateModel(self.model) Edouard@2334: Edouard@2334: for a,k in ColumnsDesc: Edouard@2334: self.dvc.AppendTextColumn(*a,**dict(k, flags = colflags)) Edouard@2334: Edouard@2334: # TODO : when select, Edouard@2334: # - update ID field of scheme editor Edouard@2334: # - enable use URI button Edouard@2334: Edouard@2334: self.Sizer = wx.BoxSizer(wx.VERTICAL) Edouard@2334: self.Sizer.Add(self.dvc, 1, wx.EXPAND) Edouard@2334: Edouard@2334: btnbox = wx.BoxSizer(wx.HORIZONTAL) Edouard@2334: if self.isManager : Edouard@2334: Edouard@2334: # deletion of secret and metadata Edouard@2334: deleteButton = wx.Button(self, label=_("Delete ID")) Edouard@2334: self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, deleteButton) Edouard@2334: btnbox.Add(deleteButton, 0, wx.LEFT|wx.RIGHT, 5) Edouard@2334: Edouard@2334: # export all Edouard@2334: exportButton = wx.Button(self, label=_("Export all")) Edouard@2334: self.Bind(wx.EVT_BUTTON, self.OnExportButton, exportButton) Edouard@2334: btnbox.Add(exportButton, 0, wx.LEFT|wx.RIGHT, 5) Edouard@2334: Edouard@2334: # import with a merge -> duplicates are asked for Edouard@2334: importButton = wx.Button(self, label=_("Import")) Edouard@2334: self.Bind(wx.EVT_BUTTON, self.OnImportButton, importButton) Edouard@2334: btnbox.Add(importButton, 0, wx.LEFT|wx.RIGHT, 5) Edouard@2334: Edouard@2334: else : Edouard@2334: # selector mode Edouard@2334: # use last known URI button Edouard@2334: # TODO : disable use URI button until something selected Edouard@2334: selectButton = wx.Button(self, label=_("Use last URI")) Edouard@2334: self.Bind(wx.EVT_BUTTON, self.OnSelectButton, selectButton) Edouard@2334: btnbox.Add(selectButton, 0, wx.LEFT|wx.RIGHT, 5) Edouard@2334: Edouard@2334: self.Sizer.Add(btnbox, 0, wx.TOP|wx.BOTTOM, 5) Edouard@2334: Edouard@2334: def OnDeleteButton(self, evt): Edouard@2334: items = self.dvc.GetSelections() Edouard@2334: rows = [self.model.GetRow(item) for item in items] Edouard@2334: Edouard@2334: # Ask if user really wants to delete Edouard@2334: if wx.MessageBox(_('Are you sure to delete selected IDs?'), Edouard@2334: _('Delete IDs'), Edouard@2334: wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) != wx.YES: Edouard@2334: return Edouard@2334: Edouard@2334: self.model.DeleteRows(rows) Edouard@2334: Edouard@2334: def OnSelectButton(self, evt): Edouard@2334: # TODO : call SetURICallback with URI from curent selection. Edouard@2334: wx.MessageBox(_('?'), Edouard@2334: _('Mhe'), Edouard@2334: wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) Edouard@2334: Edouard@2334: def OnExportButton(self, evt): Edouard@2334: # TODO Edouard@2334: wx.MessageBox(_('?'), Edouard@2334: _('Mhe'), Edouard@2334: wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) Edouard@2334: Edouard@2334: def OnImportButton(self, evt): Edouard@2334: # TODO Edouard@2334: wx.MessageBox(_('?'), Edouard@2334: _('Mhe'), Edouard@2334: wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) Edouard@2334: