Remove double reboot question after upgrade
[enigma2.git] / lib / python / Plugins / SystemPlugins / SoftwareManager / plugin.py
1 from Plugins.Plugin import PluginDescriptor
2 from Screens.Console import Console
3 from Screens.ChoiceBox import ChoiceBox
4 from Screens.MessageBox import MessageBox
5 from Screens.Screen import Screen
6 from Screens.Ipkg import Ipkg
7 from Components.ActionMap import ActionMap, NumberActionMap
8 from Components.Input import Input
9 from Components.Ipkg import IpkgComponent
10 from Components.Label import Label
11 from Components.MenuList import MenuList
12 from Components.Sources.List import List
13 from Components.Slider import Slider
14 from Components.Harddisk import harddiskmanager
15 from Components.config import config,getConfigListEntry, ConfigSubsection, ConfigText, ConfigLocations
16 from Components.Console import Console
17 from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest
18 from Components.SelectionList import SelectionList
19 from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
20 from Tools.LoadPixmap import LoadPixmap
21 from enigma import eTimer,  loadPNG, quitMainloop, RT_HALIGN_LEFT, RT_VALIGN_CENTER, eListboxPythonMultiContent, eListbox, gFont
22 from cPickle import dump, load
23
24 from os import path as os_path, system as os_system, unlink, stat, mkdir, popen, makedirs, listdir, access, rename, remove, W_OK, R_OK, F_OK
25 from time import time, gmtime, strftime, localtime
26 from stat import ST_MTIME
27 from datetime import date
28
29 from ImageWizard import ImageWizard
30 from BackupRestore import BackupSelection, RestoreMenu, BackupScreen, RestoreScreen, getBackupPath, getBackupFilename
31
32 config.plugins.configurationbackup = ConfigSubsection()
33 config.plugins.configurationbackup.backuplocation = ConfigText(default = '/media/hdd/', visible_width = 50, fixed_size = False)
34 config.plugins.configurationbackup.backupdirs = ConfigLocations(default=['/etc/enigma2/', '/etc/network/interfaces', '/etc/wpa_supplicant.conf'])
35
36
37 def write_cache(cache_file, cache_data):
38         #Does a cPickle dump
39         if not os_path.isdir( os_path.dirname(cache_file) ):
40                 try:
41                         mkdir( os_path.dirname(cache_file) )
42                 except OSError:
43                             print os_path.dirname(cache_file), 'is a file'
44         fd = open(cache_file, 'w')
45         dump(cache_data, fd, -1)
46         fd.close()
47
48 def valid_cache(cache_file, cache_ttl):
49         #See if the cache file exists and is still living
50         try:
51                 mtime = stat(cache_file)[ST_MTIME]
52         except:
53                 return 0
54         curr_time = time()
55         if (curr_time - mtime) > cache_ttl:
56                 return 0
57         else:
58                 return 1
59
60 def load_cache(cache_file):
61         #Does a cPickle load
62         fd = open(cache_file)
63         cache_data = load(fd)
64         fd.close()
65         return cache_data
66
67
68 class UpdatePluginMenu(Screen):
69         skin = """
70                 <screen name="UpdatePluginMenu" position="90,130" size="550,330" title="Softwaremanager..." >
71                         <ePixmap pixmap="skin_default/border_menu.png" position="10,10" zPosition="1" size="250,300" transparent="1" alphatest="on" />
72                         <widget source="menu" render="Listbox" position="20,20" size="230,260" scrollbarMode="showOnDemand">
73                                 <convert type="TemplatedMultiContent">
74                                         {"template": [
75                                                         MultiContentEntryText(pos = (2, 2), size = (230, 22), flags = RT_HALIGN_LEFT, text = 1), # index 0 is the MenuText,
76                                                 ],
77                                         "fonts": [gFont("Regular", 20)],
78                                         "itemHeight": 25
79                                         }
80                                 </convert>
81                         </widget>
82                         <widget source="menu" render="Listbox" position="280,10" size="230,300" scrollbarMode="showNever" selectionDisabled="1">
83                                 <convert type="TemplatedMultiContent">
84                                         {"template": [
85                                                         MultiContentEntryText(pos = (2, 2), size = (230, 300), flags = RT_HALIGN_CENTER|RT_VALIGN_CENTER|RT_WRAP, text = 2), # index 0 is the MenuText,
86                                                 ],
87                                         "fonts": [gFont("Regular", 20)],
88                                         "itemHeight": 230
89                                         }
90                                 </convert>
91                         </widget>
92                 </screen>"""
93                 
94         def __init__(self, session, args = 0):
95                 Screen.__init__(self, session)
96                 self.skin_path = plugin_path
97                 self.menu = args
98                 self.list = []
99                 self.oktext = _("\nPress OK on your remote control to continue.")
100                 self.backupdirs = ' '.join( config.plugins.configurationbackup.backupdirs.value )
101                 if self.menu == 0:
102                         self.list.append(("software-update", _("Software update"), _("\nOnline update of your Dreambox software." ) + self.oktext) )
103                         self.list.append(("software-restore", _("Software restore"), _("\nRestore your Dreambox with a new firmware." ) + self.oktext))
104                         self.list.append(("system-backup", _("Backup system settings"), _("\nBackup your Dreambox settings." ) + self.oktext))
105                         self.list.append(("system-restore",_("Restore system settings"), _("\nRestore your Dreambox settings." ) + self.oktext))
106                         if config.usage.setup_level.index >= 2: # expert+
107                                 self.list.append(("advanced", _("Advanced Options"), _("\nAdvanced options and settings." ) + self.oktext))
108                 elif self.menu == 1:
109                         self.list.append(("ipkg-manager", _("Packet management"),  _("\nView, install and remove available or installed packages." ) + self.oktext))
110                         self.list.append(("ipkg-install", _("Install local IPKG"),  _("\nScan for local packages and install them." ) + self.oktext))
111                         self.list.append(("advancedrestore", _("Advanced restore"), _("\nRestore your backups by date." ) + self.oktext))
112                         self.list.append(("backuplocation", _("Choose backup location"),  _("\nSelect your backup device.\nCurrent device: " ) + config.plugins.configurationbackup.backuplocation.value + self.oktext ))
113                         self.list.append(("backupfiles", _("Choose backup files"),  _("Select files for backup. Currently selected:\n" ) + self.backupdirs + self.oktext))
114                         self.list.append(("ipkg-source",_("Choose upgrade source"), _("\nEdit the upgrade source address." ) + self.oktext))
115
116                 self["menu"] = List(self.list)
117                                 
118                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
119                 {
120                         "ok": self.go,
121                         "back": self.close,
122                         "red": self.close,
123                 }, -1)
124
125                 self.onLayoutFinish.append(self.layoutFinished)
126                 self.backuppath = getBackupPath()
127                 self.backupfile = getBackupFilename()
128                 self.fullbackupfilename = self.backuppath + "/" + self.backupfile
129                 self.onShown.append(self.setWindowTitle)
130                 
131         def layoutFinished(self):
132                 idx = 0
133                 self["menu"].index = idx
134                 
135         def setWindowTitle(self):
136                 self.setTitle(_("Software manager..."))
137                 
138         def go(self):
139                 current = self["menu"].getCurrent()[0]
140                 if self.menu == 0:
141                         if (current == "software-restore"):
142                                 self.session.open(ImageWizard)
143                         elif (current == "software-update"):
144                                 self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to update your Dreambox?")+"\n"+_("\nAfter pressing OK, please wait!"))
145                         elif (current == "advanced"):
146                                 self.session.open(UpdatePluginMenu, 1)
147                         elif (current == "system-backup"):
148                                 self.session.openWithCallback(self.backupDone,BackupScreen, runBackup = True)
149                         elif (current == "system-restore"):
150                                 if os_path.exists(self.fullbackupfilename):
151                                         self.session.openWithCallback(self.startRestore, MessageBox, _("Are you sure you want to restore your Enigma2 backup?\nEnigma2 will restart after the restore"))
152                                 else:   
153                                         self.session.open(MessageBox, _("Sorry no backups found!"), MessageBox.TYPE_INFO)
154                 if self.menu == 1:
155                         if (current == "ipkg-manager"):
156                                 self.session.open(PacketManager, self.skin_path)
157                         elif (current == "ipkg-source"):
158                                 self.session.open(IPKGSource)
159                         elif (current == "ipkg-install"):
160                                 try:
161                                         from Plugins.Extensions.MediaScanner.plugin import main
162                                         main(self.session)
163                                 except:
164                                         self.session.open(MessageBox, _("Sorry MediaScanner is not installed!"), MessageBox.TYPE_INFO)
165                         elif (current == "backuplocation"):
166                                 parts = [ (r.description, r.mountpoint, self.session) for r in harddiskmanager.getMountedPartitions(onlyhotplug = False)]
167                                 for x in parts:
168                                         if not access(x[1], F_OK|R_OK|W_OK) or x[1] == '/':
169                                                 parts.remove(x)
170                                 for x in parts:
171                                         if x[1].startswith('/autofs/'):
172                                                 parts.remove(x)                                 
173                                 if len(parts):
174                                         self.session.openWithCallback(self.backuplocation_choosen, ChoiceBox, title = _("Please select medium to use as backup location"), list = parts)
175                         elif (current == "backupfiles"):
176                                 self.session.openWithCallback(self.backupfiles_choosen,BackupSelection)
177                         elif (current == "advancedrestore"):
178                                 self.session.open(RestoreMenu, self.skin_path)                  
179
180         def backupfiles_choosen(self, ret):
181                 self.backupdirs = ' '.join( config.plugins.configurationbackup.backupdirs.value )
182
183         def backuplocation_choosen(self, option):
184                 if option is not None:
185                         config.plugins.configurationbackup.backuplocation.value = str(option[1])
186                 config.plugins.configurationbackup.backuplocation.save()
187                 config.plugins.configurationbackup.save()
188                 config.save()
189                 self.createBackupfolders()
190         
191         def runUpgrade(self, result):
192                 if result:
193                         self.session.open(UpdatePlugin, self.skin_path)
194
195         """def runFinished(self):
196                 self.session.openWithCallback(self.reboot, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
197                 
198         def reboot(self, result):
199                 if result is None:
200                         return
201                 if result:
202                         quitMainloop(3)"""
203
204         def createBackupfolders(self):
205                 print "Creating backup folder if not already there..."
206                 self.backuppath = getBackupPath()
207                 try:
208                         if (os_path.exists(self.backuppath) == False):
209                                 makedirs(self.backuppath)
210                 except OSError:
211                         self.session.open(MessageBox, _("Sorry, your backup destination is not writeable.\n\nPlease choose another one."), MessageBox.TYPE_INFO)
212
213         def backupDone(self,retval = None):
214                 if retval is True:
215                         self.session.open(MessageBox, _("Backup done."), MessageBox.TYPE_INFO)
216                 else:
217                         self.session.open(MessageBox, _("Backup failed."), MessageBox.TYPE_INFO)
218
219         def startRestore(self, ret = False):
220                 if (ret == True):
221                         self.exe = True
222                         self.session.open(RestoreScreen, runRestore = True)
223
224
225 class IPKGSource(Screen):
226         skin = """
227                 <screen position="100,100" size="550,60" title="IPKG source" >
228                         <widget name="text" position="0,0" size="550,25" font="Regular;20" backgroundColor="background" foregroundColor="#cccccc" />
229                 </screen>"""
230                 
231         def __init__(self, session, args = None):
232                 Screen.__init__(self, session)
233                 self.session = session
234                 
235                 fp = file('/etc/ipkg/official-feed.conf', 'r')
236                 sources = fp.readlines()
237                 fp.close()
238                 
239                 self["text"] = Input(sources[0], maxSize=False, type=Input.TEXT)
240                                 
241                 self["actions"] = NumberActionMap(["WizardActions", "InputActions", "TextEntryActions", "KeyboardInputActions"], 
242                 {
243                         "ok": self.go,
244                         "back": self.close,
245                         "left": self.keyLeft,
246                         "right": self.keyRight,
247                         "home": self.keyHome,
248                         "end": self.keyEnd,
249                         "deleteForward": self.keyDeleteForward,
250                         "deleteBackward": self.keyDeleteBackward,
251                         "1": self.keyNumberGlobal,
252                         "2": self.keyNumberGlobal,
253                         "3": self.keyNumberGlobal,
254                         "4": self.keyNumberGlobal,
255                         "5": self.keyNumberGlobal,
256                         "6": self.keyNumberGlobal,
257                         "7": self.keyNumberGlobal,
258                         "8": self.keyNumberGlobal,
259                         "9": self.keyNumberGlobal,
260                         "0": self.keyNumberGlobal
261                 }, -1)
262                 
263         def go(self):
264                 fp = file('/etc/ipkg/official-feed.conf', 'w')
265                 fp.write(self["text"].getText())
266                 fp.close()
267                 self.close()
268                 
269         def keyLeft(self):
270                 self["text"].left()
271         
272         def keyRight(self):
273                 self["text"].right()
274         
275         def keyHome(self):
276                 self["text"].home()
277         
278         def keyEnd(self):
279                 self["text"].end()
280         
281         def keyDeleteForward(self):
282                 self["text"].delete()
283         
284         def keyDeleteBackward(self):
285                 self["text"].deleteBackward()
286         
287         def keyNumberGlobal(self, number):
288                 print "pressed", number
289                 self["text"].number(number)
290
291
292 class PacketList(MenuList):
293         def __init__(self, list, enableWrapAround=True):
294                 MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent)
295                 self.l.setFont(0, gFont("Regular", 22))
296                 self.l.setFont(1, gFont("Regular", 14))
297                 self.l.setItemHeight(52)
298
299 class PacketManager(Screen):
300         skin = """
301                 <screen position="90,80" size="530,420" title="IPKG upgrade..." >
302                         <widget name="list" position="5,10" size="520,365" zPosition="1" scrollbarMode="showOnDemand" />
303                         <widget name="status" position="30,160" size="530,40" zPosition="4" font="Regular;22" halign="left" transparent="1" />
304                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
305                         <widget name="closetext" position="20,390" size="140,21" zPosition="10" font="Regular;21" transparent="1" />
306                         <ePixmap pixmap="skin_default/buttons/green.png" position="160,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
307                         <widget name="reloadtext" position="170,390" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
308                 </screen>"""
309                 
310         def __init__(self, session, plugin_path, args = None):
311                 Screen.__init__(self, session)
312                 self.session = session
313                 self.skin_path = plugin_path
314
315                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"], 
316                 {
317                         "ok": self.go,
318                         "back": self.close,
319                         "red": self.close,
320                         "green": self.reload,
321                 }, -1)
322                 
323                 self.list = []
324                 self["list"] = PacketList(self.list)
325                 self.status = Label()
326                 self["closetext"] = Label(_("Close"))
327                 self["reloadtext"] = Label(_("Reload"))
328                 self["status"] = self.status                            
329
330                 self.list_updating = True
331                 self.packetlist = []
332                 self.installed_packetlist = {}
333                 self.Console = Console()
334                 self.cmdList = []
335                 self.cachelist = []
336                 self.cache_ttl = 86400  #600 is default, 0 disables, Seconds cache is considered valid (24h should be ok for caching ipkgs)
337                 self.cache_file = '/usr/lib/enigma2/python/Plugins/SystemPlugins/SoftwareManager/packetmanager.cache' #Path to cache directory   
338                 self.oktext = _("\nAfter pressing OK, please wait!")
339
340                 self.ipkg = IpkgComponent()
341                 self.ipkg.addCallback(self.ipkgCallback)
342                 self.onShown.append(self.setWindowTitle)
343                 self.onLayoutFinish.append(self.rebuildList)
344                 self.onClose.append(self.cleanup)
345                 
346         def cleanup(self):
347                 self.ipkg.stop()
348                 if self.Console is not None:
349                         del self.Console
350
351         def reload(self):
352                 if (os_path.exists(self.cache_file) == True):
353                         remove(self.cache_file)
354                         self.list_updating = True
355                         self.rebuildList()
356                         
357         def setWindowTitle(self):
358                 self.setTitle(_("Packet manager"))
359
360         def rebuildList(self):
361                 self["list"].instance.hide()
362                 self.status.setText(_("Package list update"))
363                 self.status.show()
364                 self.inv_cache = 0
365                 self.vc = valid_cache(self.cache_file, self.cache_ttl)
366                 if self.cache_ttl > 0 and self.vc != 0:
367                         try:
368                                 self.buildPacketList()
369                         except:
370                                 self.inv_cache = 1
371                 if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
372                         self.run = 0
373                         self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
374
375         def go(self, returnValue = None):
376                 returnValue = self['list'].l.getCurrentSelection()[0]
377                 self.cmdList = []
378                 if returnValue[3] == 'installed':
379                         self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": returnValue[0] }))
380                         if len(self.cmdList):
381                                 self.session.openWithCallback(self.runRemove, MessageBox, _("Do you want to remove the package:\n" + returnValue[0] + "\n" + self.oktext))
382                 elif returnValue[3] == 'upgradeable':
383                         self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": returnValue[0] }))
384                         if len(self.cmdList):
385                                 self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to upgrade the package:\n" + returnValue[0] + "\n" + self.oktext))
386                 else:
387                         self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": returnValue[0] }))
388                         if len(self.cmdList):
389                                 self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to install the package:\n" + returnValue[0] + "\n" + self.oktext))
390
391         def runRemove(self, result):
392                 if result:
393                         self.session.openWithCallback(self.runRemoveFinished, Ipkg, cmdList = self.cmdList)
394
395         def runRemoveFinished(self):
396                 self.session.openWithCallback(self.RemoveReboot, MessageBox, _("Remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
397
398         def RemoveReboot(self, result):
399                 if result is None:
400                         return
401                 if result is False:
402                         entry = self['list'].l.getCurrentSelection()[0]
403                         item = self['list'].l.getCurrentSelectionIndex()
404                         self.list[item] = PacketEntryComponent([entry[0], entry[1], entry[2], 'installable'])
405                         self.cachelist[item] = [entry[0], entry[1], entry[2], 'installable']
406                         self['list'].l.setList(self.list)
407                         write_cache(self.cache_file, self.cachelist)
408                 if result:
409                         quitMainloop(3)
410
411         def runUpgrade(self, result):
412                 if result:
413                         self.session.openWithCallback(self.runUpgradeFinished, Ipkg, cmdList = self.cmdList)
414
415         def runUpgradeFinished(self):
416                 self.session.openWithCallback(self.UpgradeReboot, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
417                 
418         def UpgradeReboot(self, result):
419                 if result is None:
420                         return
421                 if result is False:
422                         entry = self['list'].l.getCurrentSelection()[0]
423                         item = self['list'].l.getCurrentSelectionIndex()
424                         self.list[item] = PacketEntryComponent([entry[0], entry[1], entry[2], 'installed'])
425                         self.cachelist[item] = [entry[0], entry[1], entry[2], 'installed']
426                         self['list'].l.setList(self.list)
427                         write_cache(self.cache_file, self.cachelist)
428                 if result:
429                         quitMainloop(3)
430
431         def ipkgCallback(self, event, param):
432                 if event == IpkgComponent.EVENT_ERROR:
433                         self.list_updating = False
434                         self.status.setText(_("An error occured!"))
435                 elif event == IpkgComponent.EVENT_DONE:
436                         if self.list_updating:
437                                 self.list_updating = False
438                                 if not self.Console:
439                                         self.Console = Console()
440                                 cmd = "ipkg list"
441                                 self.Console.ePopen(cmd, self.IpkgList_Finished)
442                 #print event, "-", param
443                 pass
444
445         def IpkgList_Finished(self, result, retval, extra_args = None):
446                 if len(result):
447                         self.packetlist = []
448                         for x in result.splitlines():
449                                 split = x.split(' - ')
450                                 self.packetlist.append([split[0].strip(), split[1].strip(),split[2].strip()])
451                 cmd = "ipkg list_installed"
452                 self.Console.ePopen(cmd, self.IpkgListInstalled_Finished)
453
454         def IpkgListInstalled_Finished(self, result, retval, extra_args = None):
455                 if len(result):
456                         self.installed_packetlist = {}
457                         for x in result.splitlines():
458                                 split = x.split(' - ')
459                                 self.installed_packetlist[split[0].strip()] = split[1].strip()
460                 self.buildPacketList()
461
462         def PacketEntryComponent(self,entry):
463                 res = [ entry ]
464                 res.append(MultiContentEntryText(pos=(5, 1), size=(440, 28), font=0, text= entry[0]))
465                 res.append(MultiContentEntryText(pos=(5, 26), size=(440, 20), font=1, text=entry[2]))
466                 res.append(MultiContentEntryPixmapAlphaTest(pos=(445, 2), size=(48, 48), png = entry[4]))
467                 res.append(MultiContentEntryPixmapAlphaTest(pos=(5, 50), size=(510, 2), png = entry[5]))
468                 
469                 return res
470
471
472         def buildPacketList(self):
473                 self.list = []
474                 self.cachelist = []
475                 installedpng = loadPNG(resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installed.png"))
476                 upgradeablepng = loadPNG(resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/upgradeable.png"))
477                 installablepng = loadPNG(resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installable.png"))
478                 divpng = loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
479
480                 if self.cache_ttl > 0 and self.vc != 0:
481                         print 'Loading packagelist cache from ',self.cache_file
482                         try:
483                                 self.cachelist = load_cache(self.cache_file)
484                                 if len(self.cachelist) > 0:
485                                         for x in self.cachelist:
486                                                 if x[3] == 'installed':
487                                                         self.list.append(self.PacketEntryComponent([x[0], x[1], x[2], x[3],installedpng,divpng]))
488                                                 elif x[3] == 'upgradeable':
489                                                         self.list.append(self.PacketEntryComponent([x[0], x[1], x[2], x[3],upgradeablepng,divpng]))
490                                                 else:
491                                                         self.list.append(self.PacketEntryComponent([x[0], x[1], x[2], x[3],installablepng,divpng]))
492                                         self['list'].l.setList(self.list)
493                                         self["list"].instance.show()
494                                         self.status.hide()
495                         except:
496                                 self.inv_cache = 1
497
498                 if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
499                         print 'rebuilding fresh package list'
500                         for x in self.packetlist:
501                                 status = ""
502                                 if self.installed_packetlist.has_key(x[0].strip()):
503                                         if self.installed_packetlist[x[0].strip()] == x[1].strip():
504                                                 status = "installed"
505                                                 self.list.append(self.PacketEntryComponent([x[0].strip(), x[1].strip(), x[2].strip(), status,installedpng,divpng]))
506                                         else:
507                                                 status = "upgradeable"
508                                                 self.list.append(self.PacketEntryComponent([x[0].strip(), x[1].strip(), x[2].strip(), status,upgradeablepng,divpng]))
509                                 else:
510                                         status = "installable"
511                                         self.list.append(self.PacketEntryComponent([x[0].strip(), x[1].strip(), x[2].strip(), status,installablepng,divpng]))
512                                 self.cachelist.append([x[0].strip(), x[1].strip(), x[2].strip(), status])       
513                         write_cache(self.cache_file, self.cachelist)
514                         self['list'].l.setList(self.list)
515                         self["list"].instance.show()
516                         self.status.hide()
517
518
519 class UpdatePlugin(Screen):
520         skin = """
521                 <screen position="100,100" size="550,200" title="Software Update..." >
522                         <widget name="activityslider" position="0,0" size="550,5"  />
523                         <widget name="slider" position="0,100" size="550,30"  />
524                         <widget name="package" position="10,30" size="540,20" font="Regular;18"/>
525                         <widget name="status" position="10,60" size="540,45" font="Regular;18"/>
526                 </screen>"""
527                 
528         def __init__(self, session, args = None):
529                 self.skin = UpdatePlugin.skin
530                 Screen.__init__(self, session)
531                 
532                 self.sliderPackages = { "dreambox-dvb-modules": 1, "enigma2": 2, "tuxbox-image-info": 3 }
533                 
534                 self.slider = Slider(0, 4)
535                 self["slider"] = self.slider
536                 self.activityslider = Slider(0, 100)
537                 self["activityslider"] = self.activityslider
538                 self.status = Label(_("Upgrading Dreambox... Please wait"))
539                 self["status"] = self.status
540                 self.package = Label()
541                 self["package"] = self.package
542                 
543                 self.packages = 0
544                 self.error = 0
545                 
546                 self.activity = 0
547                 self.activityTimer = eTimer()
548                 self.activityTimer.callback.append(self.doActivityTimer)
549                 self.activityTimer.start(100, False)
550                                 
551                 self.ipkg = IpkgComponent()
552                 self.ipkg.addCallback(self.ipkgCallback)
553                 
554                 self.updating = True
555                 self.package.setText(_("Package list update"))
556                 self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
557                         
558                 self["actions"] = ActionMap(["WizardActions"], 
559                 {
560                         "ok": self.exit,
561                         "back": self.exit
562                 }, -1)
563                 
564         def doActivityTimer(self):
565                 self.activity += 1
566                 if self.activity == 100:
567                         self.activity = 0
568                 self.activityslider.setValue(self.activity)
569                 
570         def ipkgCallback(self, event, param):
571                 if event == IpkgComponent.EVENT_DOWNLOAD:
572                         self.status.setText(_("Downloading"))
573                 elif event == IpkgComponent.EVENT_UPGRADE:
574                         if self.sliderPackages.has_key(param):
575                                 self.slider.setValue(self.sliderPackages[param])
576                         self.package.setText(param)
577                         self.status.setText(_("Upgrading"))
578                         self.packages += 1
579                 elif event == IpkgComponent.EVENT_INSTALL:
580                         self.package.setText(param)
581                         self.status.setText(_("Installing"))
582                         self.packages += 1
583                 elif event == IpkgComponent.EVENT_CONFIGURING:
584                         self.package.setText(param)
585                         self.status.setText(_("Configuring"))
586                 elif event == IpkgComponent.EVENT_MODIFIED:
587                         self.session.openWithCallback(
588                                 self.modificationCallback,
589                                 MessageBox,
590                                 _("A configuration file (%s) was modified since Installation.\nDo you want to keep your version?") % (param)
591                         )
592                 elif event == IpkgComponent.EVENT_ERROR:
593                         self.error += 1
594                 elif event == IpkgComponent.EVENT_DONE:
595                         if self.updating:
596                                 self.updating = False
597                                 self.ipkg.startCmd(IpkgComponent.CMD_UPGRADE, args = {'test_only': False})
598                         elif self.error == 0:
599                                 self.slider.setValue(4)
600                                 
601                                 self.activityTimer.stop()
602                                 self.activityslider.setValue(0)
603                                 
604                                 self.package.setText("")
605                                 self.status.setText(_("Done - Installed or upgraded %d packages") % self.packages)
606                         else:
607                                 self.activityTimer.stop()
608                                 self.activityslider.setValue(0)
609                                 error = _("your dreambox might be unusable now. Please consult the manual for further assistance before rebooting your dreambox.")
610                                 if self.packages == 0:
611                                         error = _("No packages were upgraded yet. So you can check your network and try again.")
612                                 if self.updating:
613                                         error = _("Your dreambox isn't connected to the internet properly. Please check it and try again.")
614                                 self.status.setText(_("Error") +  " - " + error)
615                 #print event, "-", param
616                 pass
617
618         def modificationCallback(self, res):
619                 self.ipkg.write(res and "N" or "Y")
620
621         def exit(self):
622                 if not self.ipkg.isRunning():
623                         if self.packages != 0 and self.error == 0:
624                                 self.session.openWithCallback(self.exitAnswer, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"))
625                         else:
626                                 self.close()
627                         
628         def exitAnswer(self, result):
629                 if result is not None and result:
630                         quitMainloop(2)
631                 self.close()
632
633
634
635 class IpkgInstaller(Screen):
636         skin = """
637                 <screen position="100,100" size="550,400" title="..." >
638                         <widget name="red" halign="center" valign="center" position="0,0" size="140,60" backgroundColor="red" font="Regular;21" />
639                         <widget name="green" halign="center" valign="center" position="140,0" text="Install selected" size="140,60" backgroundColor="green" font="Regular;21" />
640                         <widget name="yellow" halign="center" valign="center" position="280,0" size="140,60" backgroundColor="yellow" font="Regular;21" />
641                         <widget name="blue" halign="center" valign="center" position="420,0" size="140,60" backgroundColor="blue" font="Regular;21" />
642                         <widget name="list" position="0,60" size="550,360" />
643                 </screen>
644                 """
645         
646         def __init__(self, session, list):
647                 self.skin = IpkgInstaller.skin
648                 Screen.__init__(self, session)
649
650                 self.list = SelectionList()
651                 self["list"] = self.list
652                 for listindex in range(len(list)):
653                         self.list.addSelection(list[listindex], list[listindex], listindex, True)
654
655                 self["red"] = Label()
656                 self["green"] = Label()
657                 self["yellow"] = Label()
658                 self["blue"] = Label()
659                 
660                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions"], 
661                 {
662                         "ok": self.list.toggleSelection, 
663                         "cancel": self.close, 
664                         "green": self.install
665                 }, -1)
666                 
667         def install(self):
668                 list = self.list.getSelectionsList()
669                 cmdList = []
670                 for item in list:
671                         cmdList.append((IpkgComponent.CMD_INSTALL, { "package": item[1] }))
672                 self.session.open(Ipkg, cmdList = cmdList)
673
674 def filescan_open(list, session, **kwargs):
675         filelist = [x.path for x in list]
676         session.open(IpkgInstaller, filelist) # list
677
678 def filescan(**kwargs):
679         from Components.Scanner import Scanner, ScanPath
680         return \
681                 Scanner(mimetypes = ["application/x-debian-package"], 
682                         paths_to_scan = 
683                                 [
684                                         ScanPath(path = "ipk", with_subdirs = True), 
685                                         ScanPath(path = "", with_subdirs = False), 
686                                 ], 
687                         name = "Ipkg", 
688                         description = "Install software updates...", 
689                         openfnc = filescan_open, )
690
691 def UpgradeMain(session, **kwargs):
692         session.open(UpdatePluginMenu)
693
694 def startSetup(menuid):
695         if menuid != "setup": 
696                 return [ ]
697         return [(_("Software manager") + "...", UpgradeMain, "software_manager", 50)]
698
699 def Plugins(path, **kwargs):
700         global plugin_path
701         plugin_path = path
702         return [
703                 PluginDescriptor(name=_("Software manager"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_MENU, fnc=startSetup), 
704                 #PluginDescriptor(name=_("Software manager"), description=_("Manage your receiver's software"), icon="update.png", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=UpgradeMain),
705                 PluginDescriptor(name=_("Ipkg"), where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)
706         ]