controls/IDManager.py
changeset 2334 d1470c052662
child 2335 4262256e1d28
equal deleted inserted replaced
2333:81abf93b4684 2334:d1470c052662
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 # See COPYING file for copyrights details.
       
     5 
       
     6 from __future__ import absolute_import
       
     7 import os
       
     8 import wx
       
     9 import wx.dataview as dv
       
    10 import json
       
    11 
       
    12 def _GetInitialData(psk_path):
       
    13     # [(ID, Desc, LastKnownURI, LastConnect)
       
    14     data = []
       
    15 
       
    16     data_path = os.path.join(psk_path, 'management.json')
       
    17 
       
    18     if os.path.isdir(psk_path):
       
    19         # load known keys metadata
       
    20         # {ID:(Desc, LastKnownURI, LastConnect)}
       
    21         recovered_data = json.loads(open(data_path).read()) \
       
    22                          if os.path.exists(data_path) else {}
       
    23 
       
    24         # go through all secret files available an build data
       
    25         # out of data recoverd from json and list of secret.
       
    26         # this implicitly filters IDs out of metadata who's
       
    27         # secret is missing
       
    28         psk_files = os.listdir(psk_path)
       
    29         for filename in psk_files:
       
    30            if filename.endswith('.secret'):
       
    31                ID = filename[:-7]  # strip filename extension
       
    32                meta = recovered_data.get(ID, 
       
    33                    ['', # default description
       
    34                     None, # last known URI
       
    35                     None])  # last connection date
       
    36                    
       
    37                data.append([ID]+meta)
       
    38     return data
       
    39 
       
    40 def _DeleteID(psk_path, ID):
       
    41     secret_path = os.path.join(psk_path, ID+'.secret')
       
    42     os.remove(secret_path)
       
    43 
       
    44 def _SaveData(psk_path, data):
       
    45     if not os.path.isdir(psk_path):
       
    46         os.mkdir(psk_path)
       
    47     data_path = os.path.join(psk_path, 'management.json')
       
    48     to_store = {row[0]:row[1:] for row in data}
       
    49     with open(data_path, 'w') as f:
       
    50         f.write(json.dumps(to_store))
       
    51 
       
    52 class IDManagerModel(dv.PyDataViewIndexListModel):
       
    53     def __init__(self, psk_path, columncount):
       
    54         self.psk_path = psk_path
       
    55         self.columncount = columncount
       
    56         self.data = _GetInitialData(psk_path)
       
    57         dv.PyDataViewIndexListModel.__init__(self, len(self.data))
       
    58 
       
    59     def _saveData(self):
       
    60         _SaveData(self.psk_path, self.data)
       
    61 
       
    62     def GetColumnType(self, col):
       
    63         return "string"
       
    64 
       
    65     def GetValueByRow(self, row, col):
       
    66         return self.data[row][col]
       
    67 
       
    68     def SetValueByRow(self, value, row, col):
       
    69         self.data[row][col] = value
       
    70         self._saveData()
       
    71 
       
    72     def GetColumnCount(self):
       
    73         return len(self.data[0]) if self.data else self.columncount
       
    74 
       
    75     def GetCount(self):
       
    76         return len(self.data)
       
    77     
       
    78     def GetAttrByRow(self, row, col, attr):
       
    79         if col == 3:
       
    80             attr.SetColour('blue')
       
    81             attr.SetBold(True)
       
    82             return True
       
    83         return False
       
    84 
       
    85 
       
    86     def Compare(self, item1, item2, col, ascending):
       
    87         if not ascending: # swap sort order?
       
    88             item2, item1 = item1, item2
       
    89         row1 = self.GetRow(item1)
       
    90         row2 = self.GetRow(item2)
       
    91         if col == 0:
       
    92             return cmp(int(self.data[row1][col]), int(self.data[row2][col]))
       
    93         else:
       
    94             return cmp(self.data[row1][col], self.data[row2][col])
       
    95 
       
    96     def DeleteRows(self, rows):
       
    97         rows = list(rows)
       
    98         rows.sort(reverse=True)
       
    99         
       
   100         for row in rows:
       
   101             del self.data[row]
       
   102             _DeleteID(self.psk_path, ID)
       
   103             self.RowDeleted(row)
       
   104         self._saveData()
       
   105             
       
   106     def AddRow(self, value):
       
   107         self.data.append(value)
       
   108         self.RowAppended()
       
   109         self._saveData()
       
   110 
       
   111 colflags = dv.DATAVIEW_COL_RESIZABLE|dv.DATAVIEW_COL_SORTABLE
       
   112 
       
   113 class IDManager(wx.Panel):
       
   114     def __init__(self, parent, ctr, SelectURICallBack, *args, **kwargs):
       
   115         wx.Panel.__init__(self, parent, -1, size=(400,200))
       
   116 
       
   117         self.isManager = SelectURICallBack is None 
       
   118         self.SelectURICallBack = SelectURICallBack
       
   119 
       
   120         dvStyle = wx.BORDER_THEME | dv.DV_ROW_LINES
       
   121         if self.isManager :
       
   122             # no multiple selection in selector mode
       
   123             dvStyle |= dv.DV_MULTIPLE
       
   124         self.dvc = dv.DataViewCtrl(self, style = dvStyle)
       
   125                     
       
   126         args = lambda *a,**k:(a,k)
       
   127 
       
   128         ColumnsDesc = [
       
   129             args(_("ID"), 0, width = 100),
       
   130             args(_("Last URI"), 1, width = 80),
       
   131             args(_("Description"), 2, width = 200, 
       
   132                 mode = dv.DATAVIEW_CELL_EDITABLE 
       
   133                        if self.isManager 
       
   134                        else dv.DATAVIEW_CELL_INERT),
       
   135             args(_("Last connection"),  3, width = 100),
       
   136         ]
       
   137 
       
   138         self.model = IDManagerModel(
       
   139             os.path.join(str(ctr.ProjectPath), 'psk'),
       
   140             len(ColumnsDesc))
       
   141         self.dvc.AssociateModel(self.model)
       
   142 
       
   143         for a,k in ColumnsDesc:
       
   144             self.dvc.AppendTextColumn(*a,**dict(k, flags = colflags))
       
   145 
       
   146         # TODO : when select,
       
   147         #  - update ID field of scheme editor
       
   148         #  - enable use URI button
       
   149 
       
   150         self.Sizer = wx.BoxSizer(wx.VERTICAL) 
       
   151         self.Sizer.Add(self.dvc, 1, wx.EXPAND)
       
   152 
       
   153         btnbox = wx.BoxSizer(wx.HORIZONTAL)
       
   154         if self.isManager :
       
   155 
       
   156             # deletion of secret and metadata
       
   157             deleteButton = wx.Button(self, label=_("Delete ID"))
       
   158             self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, deleteButton)
       
   159             btnbox.Add(deleteButton, 0, wx.LEFT|wx.RIGHT, 5)
       
   160 
       
   161             # export all
       
   162             exportButton = wx.Button(self, label=_("Export all"))
       
   163             self.Bind(wx.EVT_BUTTON, self.OnExportButton, exportButton)
       
   164             btnbox.Add(exportButton, 0, wx.LEFT|wx.RIGHT, 5)
       
   165 
       
   166             # import with a merge -> duplicates are asked for
       
   167             importButton = wx.Button(self, label=_("Import"))
       
   168             self.Bind(wx.EVT_BUTTON, self.OnImportButton, importButton)
       
   169             btnbox.Add(importButton, 0, wx.LEFT|wx.RIGHT, 5)
       
   170 
       
   171         else :
       
   172             # selector mode
       
   173             # use last known URI button
       
   174             # TODO : disable use URI button until something selected
       
   175             selectButton = wx.Button(self, label=_("Use last URI"))
       
   176             self.Bind(wx.EVT_BUTTON, self.OnSelectButton, selectButton)
       
   177             btnbox.Add(selectButton, 0, wx.LEFT|wx.RIGHT, 5)
       
   178 
       
   179         self.Sizer.Add(btnbox, 0, wx.TOP|wx.BOTTOM, 5)
       
   180 
       
   181     def OnDeleteButton(self, evt):
       
   182         items = self.dvc.GetSelections()
       
   183         rows = [self.model.GetRow(item) for item in items]
       
   184 
       
   185         # Ask if user really wants to delete
       
   186         if wx.MessageBox(_('Are you sure to delete selected IDs?'),
       
   187                          _('Delete IDs'),
       
   188                              wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) != wx.YES:
       
   189             return
       
   190 
       
   191         self.model.DeleteRows(rows)
       
   192 
       
   193     def OnSelectButton(self, evt):
       
   194         # TODO : call SetURICallback with URI from curent selection.
       
   195         wx.MessageBox(_('?'),
       
   196                       _('Mhe'),
       
   197                       wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT)
       
   198 
       
   199     def OnExportButton(self, evt):
       
   200         # TODO 
       
   201         wx.MessageBox(_('?'),
       
   202                       _('Mhe'),
       
   203                       wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT)
       
   204 
       
   205     def OnImportButton(self, evt):
       
   206         # TODO 
       
   207         wx.MessageBox(_('?'),
       
   208                       _('Mhe'),
       
   209                       wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT)
       
   210